On Apr 17, 10:43 am, John Machin <sjmac...@lexicon.net> wrote: > > I don't see how it can handle the chained relop in the last two > testcases e. g. '0.00 LE A LE 4.00' -- unless relops are chained by > default in your parser. >
John - First of all, to respect precedence of operations, higher level precedences are parsed and grouped first. If you left off the parse actions and just printed out the parse tree created by the example (using asList()), for "A + B * C" you would get ['A', '+', [ 'B', '*', 'C' ]]. If you expand that test case to "A + B * C + D", you would get ['A', '+', [ 'B', '*', 'C' ], '+', 'D' ]. This is counter to the conventional infix parser that would create [['A', '+', [ 'B', '*', 'C' ]], '+', 'D' ], in which binary operators typicaly return 'operand' 'operator' 'operand' triples, and either operand might be a nested parse tree. As it happens, when using pyparsing's operatorPrecedence helper, *all* binary operators at the same precedence level are actually parsed in a single chain. This is why you see this logic in EvalAddOp.eval: def eval(self): sum = self.value[0].eval() for op,val in operatorOperands(self.value[1:]): if op == '+': sum += val.eval() if op == '-': sum -= val.eval() return sum operatorOperands is a little generator that returns operator-operand pairs, beginning at the second (that is, the "1th") token in the list. You can't just do the simple evaluation of "operand1 operator operand2", you have to build up the sum by first evaluating operand1, and then iterating over the operator-operand pairs in the rest of the list. Same thing for the muliplication operators. For the comparison operators, things are a little more involved. "operand1 operator1 operand2 operator2 operand3" (as in "0.00 LE A LE 4.00") has to evaluate as op1 operator1 op2 AND op2 operator2 op3 So EvalComparisonOp's eval method looks like: def eval(self): val1 = self.value[0].eval() ret = True for op,val in operatorOperands(self.value[1:]): fn = EvalComparisonOp.opMap[op] val2 = val.eval() ret = ret and fn(val1,val2) val1 = val2 return ret The first term is evaluated and stored in val1. Then each comparisonop-operand pair is extracted, the operand is eval()'ed and stored in val2, and the comparison method that is mapped to the comparisonop is called using val1 and val2. Then, to move on to do the next comparison, val2 is stored into val1, and the we iterate to the next comparison-operand pair. In fact, not only does this handle "0.00 LE A LE 4.00", but it could also evaluate "0.00 LE A LE 4.00 LE E > D". (I see that I should actually do some short-circuiting here - if ret is false after calling fn(val1,val2), I should just break out at that point. I'll have that fixed in the online version shortly.) -- Paul -- http://mail.python.org/mailman/listinfo/python-list