# JOURNAL — phase `parse` ## 2026-06-15 — Initial implementation Plan: recursive-descent parser with two precedence levels. - `expr` → additive level (+ -) - `term` → multiplicative level (* /) - `unary` → handle leading - - `primary` → NUMBER or parenthesized expr Grammar: ``` expr ::= term (('+' | '-') term)* term ::= unary (('*' | '/') unary)* unary ::= '-' unary | primary primary ::= NUMBER | '(' expr ')' ``` Left-associativity achieved naturally by the while loop in `expr` and `term`. ## Test run output ``` Ran 36 tests in 0.001s OK ``` ## AST shape verification ``` 1+2*3: BinOp(op='+', left=Num(value=1), right=BinOp(op='*', left=Num(value=2), right=Num(value=3))) 8-3-2: BinOp(op='-', left=BinOp(op='-', left=Num(value=8), right=Num(value=3)), right=Num(value=2)) 8/4/2: BinOp(op='/', left=BinOp(op='/', left=Num(value=8), right=Num(value=4)), right=Num(value=2)) (1+2)*3: BinOp(op='*', left=BinOp(op='+', left=Num(value=1), right=Num(value=2)), right=Num(value=3)) -5: Unary(op='-', operand=Num(value=5)) -(1+2): Unary(op='-', operand=BinOp(op='+', left=Num(value=1), right=Num(value=2))) 3*-2: BinOp(op='*', left=Num(value=3), right=Unary(op='-', operand=Num(value=2))) ``` ## Error cases ``` '1 +': ParseError: unexpected end of input '(1': ParseError: expected RPAREN, got 'EOF' (None) '1 2': ParseError: unexpected token 'NUMBER' (2) after expression ')(': ParseError: unexpected token 'RPAREN' (')') '': ParseError: empty expression ```