# JOURNAL-eval — Builder ## 2026-06-15 — Implementation ### What was built - `calc/evaluator.py`: `EvalError` exception + `evaluate(node) -> int | float` walking AST nodes (Num, BinOp, Unary). Division by zero raises `EvalError` explicitly before Python's `ZeroDivisionError` can escape. - `calc.py` (root): CLI entry point. Calls `tokenize → parse → evaluate`. `_fmt()` converts whole-valued floats to int display. - `calc/test_evaluator.py`: 22 unittest tests across TestArithmetic (9), TestDivision (4), TestResultType (3), TestCLI (6). ### Test run ``` $ python -m unittest -q Ran 68 tests in 0.224s 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) ``` All match DoD expected values. ### Design notes - `evaluate` always returns `int` for integer operations and `float` for true division. The `_fmt` function in `calc.py` handles D3 display: floats that are whole become int strings. - `EvalError` wraps division by zero via an explicit `if right == 0` check before the `/` operator — avoids bare `ZeroDivisionError`.