#
# simpleArith.py
#
# Example of defining an arithmetic expression parser using
# the operatorGrammar helper method in pyparsing.
#
# Copyright 2006, by Paul McGuire
#
from pyparsing import *
ParserElement.enablePackrat() #comment this out once for comparison
# To use the operatorGrammar helper:
# 1. Define the "atom" operand term of the grammar.
# For this simple grammar, the smallest operand is either
# and integer or a variable. This will be the first argument
# to the operatorGrammar method.
# 2. Define a list of tuples for each level of operator
# precedence. Each tuple is of the form
# (opExpr, numTerms, rightLeftAssoc, parseAction), where
# - opExpr is the pyparsing expression for the operator;
# may also be a string, which will be converted to a Literal
# - numTerms is the number of terms for this operator (must
# be 1 or 2)
# - rightLeftAssoc is the indicator whether the operator is
# right or left associative, using the pyparsing-defined
# constants opAssoc.RIGHT and opAssoc.LEFT.
# - parseAction is the parse action to be associated with
# expressions matching this operator expression (the
# parse action tuple member may be omitted)
# 3. Call operatorGrammar passing the operand expression and
# the operator precedence list, and save the returned value
# as the generated pyparsing expression. You can then use
# this expression to parse input strings, or incorporate it
# into a larger, more complex grammar.
#
#dummy parse actions
def action_op_prefix(s, loc, toks):
# print "pre: ", toks
return
def action_op_infix(s, loc, toks):
# print "in : ", toks
return
def action_op_infix_left(s, loc, toks):
# print "inL: ", toks
return
#The leaf nodes of the parse tree: integer numbers and variable names
integer = Word(nums).setParseAction(lambda t:int(t[0]))
variable = (NotAny(Keyword('not') | Keyword('and') | Keyword('or')) +
Word(alphas,exact=1))
#Let parentheses work with power and unary '-' too
expression = Forward()
primary = integer | variable | Suppress('(') + expression + Suppress(')')
#Power and unary operations are intertwined to get correct operator precedence:
# -a**-b == -(a ** (-b))
power, u_expr = Forward(), Forward()
#Exponentiation: a**b;
#Strongest binding on left side, weaker than unary operations (-a) on right side.
power1 = Group(primary + '**' + u_expr) .setParseAction(action_op_infix)
power << (power1 | primary)
#Unary arithmetic operations: -a; +a
u_expr1 = Group(oneOf('- +') + u_expr) .setParseAction(action_op_prefix)
u_expr << (u_expr1 | power)
#All other operators are defined here. Those with the strongest binding come first.
expression << operatorPrecedence( u_expr,
[(oneOf('* /'), 2, opAssoc.LEFT, action_op_infix_left),
(oneOf('+ -'), 2, opAssoc.LEFT, action_op_infix_left),
(oneOf('< > <= >= == !='), 2, opAssoc.LEFT, action_op_infix_left),
(Keyword('not'), 1, opAssoc.RIGHT, action_op_prefix),
(Keyword('and'), 2, opAssoc.LEFT, action_op_infix_left),
(Keyword('or'), 2, opAssoc.LEFT, action_op_infix_left),
])
test = [
"1**2**3**4",
"-2**-3 == -(2**(-3))", #why does this expression take so long?
"3----++-2",
"1 and 2 or not a and b",
"not a <= a and c > d",
"9 + 2 - 3 + 4",
"9 + 2 * 4",
"(9 + 2) * 3",
"(9 + -2) * 3**-4**5",
"M*X + B",
"M*(X + B)",
# "2+3*4-2+3*4-2+3*4**-2**+3*4-2+3*4 and -2+3*4-2+3*4 or -2+3*4**-2**+3*4**-2**+3*4\
# -2+3*4-2+3*4-2+3*4-2+3*4-2+3*4-2 or 3*4-2 and not 3*4*2+3*4-2+3*4-2+3*4**-2**3*4\
# -2+3*4-2+3*(4-2+3)*4*-2+3*4**-2**+3**4**-2**+3**(4-(2+3)*4-2+3*4-2+3*4-2)+3*4-+3\
# *4-2+3*4-2+3*4*2+3*4-2+3*4-2+3*4**-2**+3**4**-2 < +3*4-2 or 3**4-2+3*4-2+3*4**-2\
# **+3**-4**-2*+3*4-2+3*4-2==+3*4 or -2+3*4<-2+3*4-2+3*4-2+3*4-2+3*4+1-2-3-------4"
]
#test the parser
print
for t in test:
print t
print expression.parseString(t)
# print power.parseString(t)
print