Skip to content

Instantly share code, notes, and snippets.

@boncheff
Last active April 21, 2016 08:37
Show Gist options
  • Save boncheff/37f451b2ae82cd0ef122895d6b856655 to your computer and use it in GitHub Desktop.
Save boncheff/37f451b2ae82cd0ef122895d6b856655 to your computer and use it in GitHub Desktop.
Python grammar parser using pyparsing module
from __future__ import division
import re
from pyparsing import (
Word,
alphas,
ParseException,
Literal,
CaselessLiteral,
Combine,
Optional,
nums,
Forward,
ZeroOrMore,
StringEnd,
alphanums)
exprStack = []
varStack = []
def pushFirst(str, loc, toks):
exprStack.append(toks[0])
def assignVar(str, loc, toks):
varStack.append(toks[0])
# define grammar
point = Literal('.')
e = CaselessLiteral('E')
plusorminus = Literal('+') | Literal('-')
number = Word(nums)
integer = Combine(Optional(plusorminus) + number)
floatnumber = Combine(integer +
Optional(point + Optional(number)) +
Optional(e + integer))
ident = Word(alphas, alphanums + '_')
plus = Literal("+")
minus = Literal("-")
mult = Literal("*")
div = Literal("/")
lpar = Literal("(").suppress()
rpar = Literal(")").suppress()
addop = plus | minus
multop = mult | div
expop = Literal("^")
assign = Literal("=")
expr = Forward()
atom = ((e | floatnumber | integer | ident).setParseAction(pushFirst) |
(lpar + expr.suppress() + rpar)
)
factor = Forward()
factor << atom + ZeroOrMore((expop + factor).setParseAction(pushFirst))
term = factor + ZeroOrMore((multop + factor).setParseAction(pushFirst))
expr << term + ZeroOrMore((addop + term).setParseAction(pushFirst))
bnf = Optional((ident + assign).setParseAction(assignVar)) + expr
pattern = bnf + StringEnd()
# map operator symbols to corresponding arithmetic operations
opn = {"+": (lambda a, b: a + b),
"-": (lambda a, b: a - b),
"*": (lambda a, b: a * b),
"/": (lambda a, b: a / b),
"^": (lambda a, b: a ** b)}
# Recursive function that evaluates the stack
def evaluateStack(s, variables):
op = s.pop()
if op in "+-*/^":
op2 = evaluateStack(s, variables)
op1 = evaluateStack(s, variables)
return opn[op](op1, op2)
elif re.search('^[a-zA-Z][a-zA-Z0-9_]*$', op):
if op in variables:
return variables[op]
# If a variable cannot be located, its value defaults to 0
else:
return 0
elif re.search('^[-+]?[0-9]+$', op):
return long(op)
else:
return float(op)
def do_parse(expr, variables):
""" expr is the conversion expression to evaluate
variables is a dictionary containing all the variables to replace
"""
try:
tokens = pattern.parseString(expr)
except ParseException, err:
tokens = ['Parse Failure', expr]
# show result of parsing the expr string
if len(tokens) == 0 or tokens[0] != 'Parse Failure':
result = evaluateStack(exprStack, variables)
else:
result = 'Parse Failure %s - %s' % (err.line, err)
return result
if __name__ == '__main__':
print do_parse('100 * cpu_user_ticks / (cpu_user_ticks + '
'cpu_system_ticks + cpu_idle_ticks)',
{'cpu_user_ticks': 25, 'cpu_system_ticks': 43,
'cpu_idle_ticks': 400})
@boncheff
Copy link
Author

To run, pass a conversion expression and a dictionary containing the variables to replace and their values

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment