# 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`/`term` loop = left-associative by construction - `term` deeper than `expr` = `*`/`/` higher precedence than `+`/`-` - `unary` recursive = right-associative unary (`--5` parses as `-(-5)`) AST nodes: - `Num(value)` — leaf - `BinOp(op, left, right)` — binary operator - `Unary(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 ✓ ```