sends operators and operands entered in the GUI to an embedded instance of the
Evaluator class.
The Evaluator class
Manages two stacks. One stack records pending operators (e.g., +), and one records
pending operands (e.g., 3.141). Temporary results are computed as new operators
are sent from CalcGui and pushed onto the operands stack.
As you can see from this, the magic of expression evaluation boils down to juggling the
operator and operand stacks. In a sense, the calculator implements a little stack-based
language, to evaluate the expressions being entered. While scanning expression strings
from left to right as they are entered, operands are pushed along the way, but operators
delimit operands and may trigger temporary results before they are pushed. Because it
records states and performs transitions, some might use the term state machine to de-
scribe this calculator language implementation.
Here’s the general scenario:
- When a new operator is seen (i.e., when an operator button or key is pressed), the
prior operand in the entry field is pushed onto the operands stack. - The operator is then added to the operators stack, but only after all pending op-
erators of higher precedence have been popped and applied to pending operands
(e.g., pressing + makes any pending * operators on the stack fire). - When “eval” is pressed, all remaining operators are popped and applied to all
remaining operands, and the result is the last remaining value on the operands
stack.
In the end, the last value on the operands stack is displayed in the calculator’s entry
field, ready for use in another operation. This evaluation algorithm is probably best
described by working through examples. Let’s step through the entry of a few expres-
sions and watch the evaluation stacks grow.
PyCalc stack tracing is enabled with the debugme flag in the module; if true, the operator
and operand stacks are displayed on stdout each time the Evaluator class is about to
apply an operator and reduce (pop) the stacks. Run PyCalc with a console window to
see the traces. A tuple holding the stack lists (operators , operands) is printed on each
stack reduction; tops of stacks are at the ends of the lists. For instance, here is the
console output after typing and evaluating a simple string:
1) Entered keys: "5 * 3 + 4 <eval>" [result = 19]
(['*'], ['5', '3']) [on '+' press: displays "15"]
(['+'], ['15', '4']) [on 'eval' press: displays "19"]
Note that the pending (stacked) subexpression is evaluated when the + is pressed:
operators bind tighter than +, so the code is evaluated immediately before the + operator
is pushed. When the + button is pressed, the entry field contains 3; we push 3 onto the
operands stack, reduce the subexpression (5 3), push its result onto operands, push
1466 | Chapter 19: Text and Language