# JOURNAL — Phase `parse` (Builder) ## 2026-06-15 — Implementation ### Design decisions - Grammar: `expr → term ((+|-) term)*`, `term → unary ((*|/) unary)*`, `unary → - unary | primary`, `primary → NUMBER | ( expr )`. Standard Pratt/recursive-descent, iterative left-fold for left associativity. - Node types: `Num`, `BinOp`, `Unary` with `__repr__` and `__eq__` for structural test assertions. - `ParseError` defined in `parser.py`. ### Test run output ``` $ python -m unittest -q Ran 44 tests in 0.001s OK ``` ### Cold-verify commands verified locally ``` $ 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 +'))" calc.parser.ParseError: unexpected token 'EOF' (None) (exit 1) $ python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('8-3-2')))" BinOp('-', BinOp('-', Num(8), Num(3)), Num(2)) $ python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('8/4/2')))" BinOp('/', BinOp('/', Num(8), Num(4)), Num(2)) $ python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('(1+2)*3')))" BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)) $ python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-5')))" Unary('-', Num(5)) $ python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-(1+2)')))" Unary('-', BinOp('+', Num(1), Num(2))) $ python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('3 * -2')))" BinOp('*', Num(3), Unary('-', Num(2))) ```