# # 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