# STATUS — Phase `parse` (Builder) ## DONE All gates D1–D6: Adversary-verified PASS (see REVIEW-parse.md @2026-06-15T04:08Z). Phase complete. ## AST Node Shapes (stable API for evaluator) ``` Num(value) # numeric literal; value is int or float BinOp(op, left, right) # op in {'+', '-', '*', '/'}; left/right are nodes Unary(op, operand) # op is '-'; operand is a node ``` All nodes implement `__repr__` and `__eq__`. ## Gate Claims ### D1 — Precedence **WHAT:** `*` and `/` bind tighter than `+` and `-`; `1+2*3` parses as `1+(2*3)`. **HOW:** ```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)))` **WHERE:** `calc/parser.py` — `_expr` iterates `+/-`, `_term` iterates `*//`, so `*` folds first. ### D2 — Left Associativity **WHAT:** `8-3-2` parses as `(8-3)-2`; `8/4/2` as `(8/4)/2`. **HOW:** ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('8-3-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(3)), Num(2))` - `BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))` **WHERE:** `calc/parser.py` — `_expr` and `_term` use `while` loops (iterative left fold). ### D3 — Parentheses **WHAT:** `(1+2)*3` parses with `+` under `*`. **HOW:** ```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))` **WHERE:** `calc/parser.py` — `_primary` handles `LPAREN expr RPAREN`, returning inner node to be used in `_term`. ### D4 — Unary Minus **WHAT:** `-5`, `-(1+2)`, `3 * -2` each parse correctly. **HOW:** ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-5')))" python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-(1+2)')))" python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('3 * -2')))" ``` **EXPECTED:** - `Unary('-', Num(5))` - `Unary('-', BinOp('+', Num(1), Num(2)))` - `BinOp('*', Num(3), Unary('-', Num(2)))` **WHERE:** `calc/parser.py` — `_unary` handles `MINUS` recursively before `_primary`. ### D5 — Errors **WHAT:** Malformed inputs raise `ParseError` (not any other exception). **HOW:** ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError try: parse(tokenize('1 +')) print('NO ERROR') except ParseError: print('ParseError OK') " # Repeat for each case: '(1', '1 2', ')(', '' ``` **EXPECTED:** `ParseError` raised for all five inputs: `"1 +"`, `"(1"`, `"1 2"`, `")("`, `""`. Shortcut — test suite already covers all five (TestErrors class). **WHERE:** `calc/parser.py` — `_primary` raises on bad token; `parse()` raises on trailing token or empty input; `_expect` raises on mismatched RPAREN. ### D6 — Tests Green **WHAT:** `python -m unittest -q` passes, 0 failures; covers D1–D5 with structural assertions. **HOW:** ```bash python -m unittest -q ``` **EXPECTED:** `Ran 44 tests in ...s\n\nOK` (21 lexer + 23 parser) **WHERE:** `calc/test_parser.py` — classes TestPrecedence, TestLeftAssociativity, TestParentheses, TestUnaryMinus, TestErrors.