# STATUS — parse phase ## DONE All DoD gates D1–D6 implemented and self-certified (BUILD phase — DEFERRED review protocol). --- ## What was built - `calc/parser.py` — `ParseError`, `Num`, `BinOp`, `Unary`, `parse(tokens) -> Node` - `calc/test_parser.py` — 24 unittest cases covering D1–D5 (D6 = all pass) --- ## AST shape (stable contract for the evaluator) ``` Num(value) .value — int or float BinOp(op, left, right) .op — one of '+', '-', '*', '/' .left — any Node .right — any Node Unary(op, operand) .op — '-' .operand — any Node ``` All nodes implement `__repr__` and `__eq__`. --- ## D1 — Precedence ✓ `1+2*3` parses as `BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))` — `*` tighter than `+`. Verify: ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('1+2*3')))" # Expected: BinOp('+', Num(1), BinOp('*', Num(2), Num(3))) ``` ## D2 — Left associativity ✓ `8-3-2` → `BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))` `8/4/2` → `BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))` Verify: ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('8-3-2')))" # Expected: BinOp('-', BinOp('-', Num(8), Num(3)), Num(2)) python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('8/4/2')))" # Expected: BinOp('/', BinOp('/', Num(8), Num(4)), Num(2)) ``` ## D3 — Parentheses ✓ `(1+2)*3` → `BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))` — `+` is child of `*`. Verify: ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('(1+2)*3')))" # Expected: BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)) ``` ## D4 — Unary minus ✓ ``` -5 → Unary('-', Num(5)) -(1+2) → Unary('-', BinOp('+', Num(1), Num(2))) 3 * -2 → BinOp('*', Num(3), Unary('-', Num(2))) ``` Verify: ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-5')))" # Expected: Unary('-', Num(5)) python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-(1+2)')))" # Expected: Unary('-', BinOp('+', Num(1), Num(2))) python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('3 * -2')))" # Expected: BinOp('*', Num(3), Unary('-', Num(2))) ``` ## D5 — Errors ✓ Each of the following raises `ParseError` (not any other exception): | Input | ParseError message | |----------|--------------------| | `'1 +'` | `Unexpected end of input` | | `'(1'` | `Expected RPAREN, got 'EOF' (None)` | | `'1 2'` | `Unexpected token after expression: 'NUMBER' (2)` | | `')('` | `Unexpected token 'RPAREN' (')')` | | `''` | `Empty input` | Verify: ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize('1 +'))" # Expected: raises ParseError: Unexpected end of input python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize('(1'))" # Expected: raises ParseError: Expected RPAREN... python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize('1 2'))" # Expected: raises ParseError: Unexpected token after expression... python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize(')('))" # Expected: raises ParseError: Unexpected token 'RPAREN'... python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize(''))" # Expected: raises ParseError: Empty input ``` ## D6 — Tests green ✓ 44 total tests (20 lex + 24 parser), 0 failures. Verify: ```bash python -m unittest -q # Expected: Ran 44 tests in 0.00xs / OK ``` --- ## Plan verify commands (from parse.md) ```bash python -m unittest -q # → Ran 44 tests in 0.001s / OK 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; parse(tokenize('1 +'))" # → ParseError: Unexpected end of input ```