# STATUS — phase `parse` ## DONE ## Gates - D1 (precedence): PASS @2026-06-15T03:40Z - D2 (left assoc): PASS @2026-06-15T03:40Z - D3 (parentheses): PASS @2026-06-15T03:40Z - D4 (unary minus): PASS @2026-06-15T03:40Z - D5 (errors): PASS @2026-06-15T03:40Z - D6 (tests green): PASS @2026-06-15T03:40Z ## Source files - `calc/parser.py` — AST node definitions + recursive-descent parser - `calc/test_parser.py` — 20 unittest cases covering D1–D5 ## AST shape Nodes are dataclasses defined in `calc/parser.py`: ```python @dataclass class Num: value: Union[int, float] @dataclass class BinOp: op: str # '+', '-', '*', '/' left: Node right: Node @dataclass class Unary: op: str # '-' operand: Node ``` ## Verify commands (cold) ```bash python -m unittest -q ``` Expected: `Ran 36 tests in Xs` / `OK` (0 failures) ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('1+2*3')))" ``` Expected: `BinOp(op='+', left=Num(value=1), right=BinOp(op='*', left=Num(value=2), right=Num(value=3)))` ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('8-3-2')))" ``` Expected: `BinOp(op='-', left=BinOp(op='-', left=Num(value=8), right=Num(value=3)), right=Num(value=2))` ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('8/4/2')))" ``` Expected: `BinOp(op='/', left=BinOp(op='/', left=Num(value=8), right=Num(value=4)), right=Num(value=2))` ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('(1+2)*3')))" ``` Expected: `BinOp(op='*', left=BinOp(op='+', left=Num(value=1), right=Num(value=2)), right=Num(value=3))` ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-5')))" ``` Expected: `Unary(op='-', operand=Num(value=5))` ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-(1+2)')))" ``` Expected: `Unary(op='-', operand=BinOp(op='+', left=Num(value=1), right=Num(value=2)))` ```bash python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('3 * -2')))" ``` Expected: `BinOp(op='*', left=Num(value=3), right=Unary(op='-', operand=Num(value=2)))` ```bash python -c " from calc.lexer import tokenize; from calc.parser import parse, ParseError for expr in ['1 +', '(1', '1 2', ')(', '']: try: parse(tokenize(expr)) print(f'{expr!r}: NO ERROR (FAIL)') except ParseError as e: print(f'{expr!r}: ParseError OK') " ``` Expected: all 5 lines print `ParseError OK` ## Gate-specific DoD mapping **D1 — precedence:** `1+2*3` parses as `1+(2*3)` — `*` is under the right child of `+`, not the other way. **D2 — left assoc:** `8-3-2` → left-leaning tree; `8/4/2` → left-leaning tree. The while-loop in `_expr`/`_term` naturally accumulates left. **D3 — parens:** `(1+2)*3` → `BinOp('*', BinOp('+', ...), Num(3))` — `+` is under `*`'s left child. **D4 — unary:** `-5` → `Unary`, `-(1+2)` → `Unary(BinOp(...))`, `3*-2` → `BinOp('*', Num(3), Unary(...))`. **D5 — errors:** `"1 +"`, `"(1"`, `"1 2"`, `")("`, `""` all raise `ParseError` (not Python built-ins). **D6 — tests:** `python -m unittest -q` → 36 tests, 0 failures.