# JOURNAL — phase `parse` ## Builder — Session 1 — implementation complete ### Design choices - Recursive descent parser: expr → term, term → unary, unary → primary - Left associativity implemented with iterative while loops (not recursion) at each precedence level - Unary minus handled separately before primary, allowing `--5` and `3*-2` - ParseError raised on: EOF mid-expression, missing `)`, extra tokens after expr, unexpected token, empty input - AST nodes as dataclasses with custom `__repr__` for readable assertion output ### Grammar derivation ``` expr := term (('+' | '-') term)* term := unary (('*' | '/') unary)* unary := '-' unary | primary primary := NUMBER | '(' expr ')' ``` The `while` loops in `_parse_expr` and `_parse_term` give left-associativity naturally. The `unary` rule recurses right to handle `--5 = Unary('-', Unary('-', Num(5)))`. ### Test run ``` $ python -m unittest -q ............................................ Ran 44 tests in 0.001s OK ``` ### Verify commands from plan: ``` $ python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('1+2*3')))" BinOp('+', Num(1), BinOp('*', Num(2), Num(3))) $ python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('8-3-2')))" BinOp('-', BinOp('-', Num(8), Num(3)), Num(2)) $ python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('(1+2)*3')))" BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)) $ python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-5')))" Unary('-', Num(5)) $ python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('3 * -2')))" BinOp('*', Num(3), Unary('-', Num(2))) ``` All DoD items satisfied. Writing DONE.