# STATUS — parse phase (Builder) ## DONE All DoD items self-certified (BUILD phase — deferred Adversary review). ## DoD Checklist - **D1 — precedence:** PASS. `*`/`/` bind tighter than `+`/`-`. `1+2*3` → `BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))`. - **D2 — left associativity:** PASS. `8-3-2` → `BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))`; `8/4/2` → `BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))`. - **D3 — parentheses:** PASS. `(1+2)*3` → `BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))`. - **D4 — unary minus:** PASS. `-5` → `Unary('-', Num(5))`; `3 * -2` → `BinOp('*', Num(3), Unary('-', Num(2)))`. - **D5 — errors:** PASS. All five specified inputs (`"1 +"`, `"(1"`, `"1 2"`, `")("`, `""`) raise `ParseError`. - **D6 — tests green:** PASS. 35 tests total (11 lexer + 24 parser), 0 failures. ## Verification Commands (for Adversary cold-verify) ```bash # From repo root python -m unittest -q # Expected: Ran 35 tests in X.XXXs / OK (exit 0) # D1 — precedence 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 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 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 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('3 * -2')))" # Expected: BinOp('*', Num(3), Unary('-', Num(2))) python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-(1+2)')))" # Expected: Unary('-', BinOp('+', Num(1), Num(2))) # D5 — errors (each must raise ParseError, not crash with different exception) python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError try: parse(tokenize('1 +')) print('FAIL: no error') except ParseError as e: print('OK:', e)" python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError try: parse(tokenize('(1')) print('FAIL: no error') except ParseError as e: print('OK:', e)" python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError try: parse(tokenize('1 2')) print('FAIL: no error') except ParseError as e: print('OK:', e)" python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError try: parse(tokenize(')(')) print('FAIL: no error') except ParseError as e: print('OK:', e)" python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError try: parse(tokenize('')) print('FAIL: no error') except ParseError as e: print('OK:', e)" ``` ## AST Shape Reference Nodes (all `dataclass`es in `calc/parser.py`): | Class | Fields | Example | |-------|--------|---------| | `Num` | `value: int \| float` | `Num(42)`, `Num(3.5)` | | `BinOp` | `op: str, left: Node, right: Node` | `BinOp('+', Num(1), Num(2))` | | `Unary` | `op: str, operand: Node` | `Unary('-', Num(5))` | `ParseError` inherits from `Exception`. ## Files Produced - `calc/parser.py` — `ParseError`, `Num`, `BinOp`, `Unary`, `Node`, `parse()` - `calc/test_parser.py` — 24 unittest cases covering D1–D5