# JOURNAL — phase parse ## 2026-06-15 ### Implementation approach Built a standard recursive-descent parser with two levels of precedence: ``` expr : term (('+' | '-') term)* # low precedence, left-assoc term : factor (('*' | '/') factor)* # high precedence, left-assoc factor : NUMBER | '-' factor # unary minus (right-recursive) | '(' expr ')' ``` The left-associativity is inherent in the `while` loop pattern: each iteration wraps the current `left` in a new BinOp, so `8-3-2` naturally produces `BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))`. Unary minus in `factor` uses right-recursion so `--5` gives `Unary('-', Unary('-', Num(5)))` and `3 * -2` gives `BinOp('*', Num(3), Unary('-', Num(2)))` — the unary binds only to what follows it, not to the whole expression. ### Verification commands run ``` $ python -m unittest -q Ran 50 tests in 0.001s OK $ python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('1+2*3')))" BinOp('+', Num(1), BinOp('*', Num(2), Num(3))) $ python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize('1 +'))" # ParseError: unexpected token 'EOF' (None) [raised, not crash] ``` All five mandatory error cases (`1 +`, `(1`, `1 2`, `)(`, `""`) raise `ParseError` — not `IndexError`, `KeyError`, or any other exception. ### Gate timeline - feat commit `c78a0d7` — parser + tests + STATUS - D1 claimed `49beb26`, pushed - D2 claimed `73f747d`, pushed - D3 claimed `3c97bfc`, pushed - D4 claimed `686695b`, pushed - D5 claimed `66d75f1`, pushed - D6 claimed `272fbac`, pushed - Awaiting Adversary verdict