896 B
896 B
JOURNAL — phase parse
Build notes
Implemented a classic recursive-descent parser.
Grammar (precedence lowest → highest):
expr → term (('+' | '-') term)*
term → unary (('*' | '/') unary)*
unary → '-' unary | primary
primary→ NUMBER | '(' expr ')'
The while loops in _expr and _term produce left-associative trees naturally: each iteration wraps the accumulated left subtree as the new node, so 8-3-2 folds as ((8-3)-2).
Unary minus recurses right (_unary calls itself) to allow --5 to parse as Unary('-', Unary('-', Num(5))).
Empty input is caught by checking for EOF before entering _expr. Trailing tokens (e.g. 1 2) are caught by the EOF check after _expr returns. Unclosed parens are caught in _primary when expecting RPAREN but finding EOF.
36 tests: 3 precision, 3 left-assoc, 3 paren, 4 unary, 5 error, plus 18 from lexer phase.