1.5 KiB
1.5 KiB
JOURNAL — Phase parse (Builder)
2026-06-15 — Start
Reading phase plan. Lex phase is DONE. Building parser on top of calc/lexer.py.
Design
Grammar (recursive descent):
expr → term (('+' | '-') term)*
term → unary (('*' | '/') unary)*
unary → '-' unary | primary
primary → NUMBER | '(' expr ')'
expr/termloop = left-associative by constructiontermdeeper thanexpr=*//higher precedence than+/-unaryrecursive = right-associative unary (--5parses as-(-5))
AST nodes:
Num(value)— leafBinOp(op, left, right)— binary operatorUnary(op, operand)— unary operator
ParseError raised for: empty string, trailing operator, unclosed paren, consecutive numbers, mismatched parens.
2026-06-15 — Implementation + tests run
$ python -m unittest -q
Ran 35 tests in 0.001s
OK
(15 from test_lexer + 20 from test_parser)
Plan verify commands:
$ python -c "...parse(tokenize('1+2*3'))"
BinOp('+', Num(1), BinOp('*', Num(2), Num(3))) # D1 ✓
$ python -c "...parse(tokenize('(1+2)*3'))"
BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)) # D3 ✓
$ python -c "...parse(tokenize('8-3-2'))"
BinOp('-', BinOp('-', Num(8), Num(3)), Num(2)) # D2 ✓
$ python -c "...parse(tokenize('-5'))"
Unary('-', Num(5)) # D4 ✓
$ python -c "...parse(tokenize('3 * -2'))"
BinOp('*', Num(3), Unary('-', Num(2))) # D4 ✓
$ python -c "...parse(tokenize('1 +'))"
calc.parser.ParseError: unexpected end of expression # D5 ✓