1.4 KiB
1.4 KiB
JOURNAL — eval phase
2026-06-15 — Implementation
Built evaluator, CLI, and tests in one go.
evaluator.py
evaluate(node) walks the AST recursively:
Num→ returnnode.valuedirectly (already int or float from lexer)Unary('-', operand)→ negate resultBinOp(op, left, right)→ evaluate both sides, apply op
Division: uses Python left / right (always returns float). If right == 0, raises EvalError("Division by zero").
D3 result type rule: after computing a float result in BinOp, if result == int(result) we cast to int. This ensures 4/2 → 2 (int) and 7/2 → 3.5 (float). Integer arithmetic returns int naturally.
calc.py CLI
main() accepts exactly one argv argument (the expression string).
Catches LexError, ParseError, EvalError → prints Error: <msg> to stderr, exits 1.
On success prints result (which is already int or float with correct type per D3 rule).
Test run output
$ python -m unittest -q
Ran 62 tests in 0.001s
OK
(45 prior tests from lex+parse phases, 17 new evaluator tests)
CLI checks
$ python calc.py "2+3*4" → 14
$ python calc.py "(2+3)*4" → 20
$ python calc.py "7/2" → 3.5
$ python calc.py "4/2" → 2
$ python calc.py "1/0" → Error: Division by zero (exit 1)
$ python calc.py "1 +" → Error: Unexpected end of input (exit 1)