2.0 KiB
2.0 KiB
JOURNAL-parse.md
2026-06-15T01:21Z — Parser implementation
Approach
Implemented a classic recursive-descent parser with three precedence levels:
_expr(): handles+/-(lowest precedence, left-associative)_term(): handles*//(middle precedence, left-associative)_unary(): handles unary-(right-associative by recursion)_primary(): handles NUMBER literals and(expr)
Left-associativity falls out naturally from the while-loop accumulation pattern in _expr() and _term(): each iteration wraps the running node as the left child of a new BinOp.
Unary minus recurses into itself (_unary() calls _unary()) which gives right-associativity for --5 → Unary('-', Unary('-', Num(5))).
Verification run
$ python -m unittest -q
Ran 43 tests in 0.001s
OK
Manual shape checks:
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 end of input ✓
D5 '(1': ParseError: expected 'RPAREN' but got 'EOF' ✓
D5 '1 2': ParseError: unexpected token after expression ✓
D5 ')(': ParseError: unexpected token 'RPAREN' ✓
D5 '': ParseError: empty expression ✓
Error handling design
- Empty token list (just EOF): caught in
parse()before entering_expr() - Trailing operator (
1 +):_primary()sees EOF, raises ParseError - Unclosed paren (
(1):_consume('RPAREN')fails with ParseError - Extra number (
1 2):parse()checkspeek() != EOFafter_expr()returns )before(:_primary()sees RPAREN, not a valid primary, raises ParseError