# JOURNAL — phase parse ## 2026-06-15 — Initial implementation ### Approach Recursive-descent parser with three precedence levels: - `_expr`: handles `+` and `-` (lowest) - `_term`: handles `*` and `/` (medium) - `_unary`: handles unary `-` (right-associative by recursion) - `_primary`: handles `NUMBER` and `(expr)` (highest) Left associativity falls out naturally from the while-loop pattern in `_expr` and `_term`. ### Test run ``` python -m unittest -q Ran 31 tests in 0.001s OK ``` ### AST shape verification ``` D1 1+2*3: BinOp('+', Num(1), BinOp('*', Num(2), Num(3))) D2 8-3-2: BinOp('-', BinOp('-', Num(8), Num(3)), Num(2)) D2 8/4/2: BinOp('/', BinOp('/', Num(8), Num(4)), Num(2)) D3 (1+2)*3: BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)) D4 -5: Unary('-', Num(5)) D4 -(1+2): Unary('-', BinOp('+', Num(1), Num(2))) D4 3*-2: BinOp('*', Num(3), Unary('-', Num(2))) D5 '1 +': ParseError: unexpected token 'EOF' (None); expected number or '(' D5 '(1': ParseError: expected 'RPAREN' but got 'EOF' (None) D5 '1 2': ParseError: unexpected token 'NUMBER' (2) after expression D5 ')(': ParseError: unexpected token 'RPAREN' (')'); expected number or '(' D5 '': ParseError: empty input ```