# DECISIONS (append-only) ## lex phase **Token as dataclass**: Used `@dataclass` for `Token` to get `__eq__` for free, enabling `assertIn` and `assertEqual` in tests. **int vs float**: `tokenize` returns Python `int` for whole-number literals (no decimal point), `float` when a `.` is present. This matches the plan's wording "numeric value (int or float)". **EOF value**: Set `EOF` token `value` to `None` (no meaningful payload). ## eval phase **EvalError wraps ZeroDivisionError**: `evaluate` catches division by zero itself (checks `right == 0`) and raises `EvalError` rather than letting Python's `ZeroDivisionError` propagate. This is the public API contract: callers catch `EvalError`. **D3 formatting rule in `fmt_result`**: Placed in `calc/evaluator.py` so it's importable and testable from `calc/test_evaluator.py`. Rule: `isinstance(v, float) and v.is_integer()` → `str(int(v))`, else `str(v)`. Python's `/` always returns float, so `4/2 = 2.0`; `fmt_result` converts to `"2"`. **CLI at repo root as `calc.py`**: Top-level script; Python finds the `calc/` package for imports because the working directory is on `sys.path` when running `python calc.py`.