# STATUS-parse Phase: `parse` Files: `calc/parser.py`, `calc/test_parser.py` ## AST Shape ``` Num(value) — leaf; value is int or float BinOp(op, left, right) — op ∈ {'+', '-', '*', '/'}; left and right are Nodes Unary(op, operand) — op is '-'; operand is a Node ``` All nodes are `@dataclass` with custom `__repr__`. `parse(tokens) -> Node` accepts the list returned by `calc.lexer.tokenize`. `ParseError(Exception)` is raised for all malformed input. ## Gate Verification ### D1 — Precedence **Command:** `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)))` **Observed:** `BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))` ✓ **PASS** ### D2 — Left Associativity **Command:** parse `8-3-2` and `8/4/2` **Expected:** `BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))` and `BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))` **Observed:** Both matched exactly ✓ **PASS** ### D3 — Parentheses **Command:** parse `(1+2)*3` **Expected:** `BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))` **Observed:** `BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))` ✓ **PASS** ### D4 — Unary Minus **Commands:** parse `-5`, `-(1+2)`, `3 * -2` **Expected:** `Unary('-', Num(5))`, `Unary('-', BinOp('+', Num(1), Num(2)))`, `BinOp('*', Num(3), Unary('-', Num(2)))` **Observed:** All three matched exactly ✓ **PASS** ### D5 — Errors **Inputs tested:** `"1 +"`, `"(1"`, `"1 2"`, `")("`, `""` **Expected:** Each raises `ParseError` **Observed:** - `"1 +"` → `ParseError: unexpected end of input` ✓ - `"(1"` → `ParseError: unclosed '(' — expected ')'` ✓ - `"1 2"` → `ParseError: unexpected token Token(kind='NUMBER', value=2) after expression` ✓ - `")("` → `ParseError: unexpected token 'RPAREN' (')')` ✓ - `""` → `ParseError: unexpected end of input` ✓ **PASS** ### D6 — Tests Green **Command:** `python -m unittest -q` **Expected:** 0 failures **Observed:** `Ran 35 tests in 0.001s — OK` ✓ **PASS** ## DONE