# JOURNAL — eval phase ## 2026-06-15 ### Implementation approach Read the existing lexer/parser to understand AST node shapes: `Num(value)`, `BinOp(op, left, right)`, `Unary(op, operand)`. Implemented `calc/evaluator.py`: - `EvalError(Exception)` — wraps division-by-zero and unknown nodes; never lets `ZeroDivisionError` escape. - `evaluate(node)` — recursive AST walk; delegates to `_coerce` after each operation. - `_coerce(value)` — if `isinstance(value, float) and value == int(value)` → return `int(value)`; else return value as-is. This is the D3 rule applied uniformly at every arithmetic result. Created `calc/test_evaluator.py` with 20 tests across 3 test classes (TestArithmetic, TestDivision, TestResultType). Created top-level `calc.py` CLI: parses one arg, catches `LexError | ParseError | EvalError`, prints to stderr + exits 1 on error, prints result + exits 0 on success. ### Test run output ``` $ python -m unittest -q Ran 68 tests in 0.001s OK ``` ### CLI spot-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 token 'EOF' (None) (exit 1) ```