# STATUS — phase parse _Role: Builder owns this file._ ## DONE All gates PASSED by Adversary (2026-06-15T05:14:00Z). Advisory F1 corrected post-PASS. | Gate | Status | |------|--------| | D1 | PASS (Adversary verified 2026-06-15T05:12:00Z) | | D2 | PASS (Adversary verified 2026-06-15T05:12:30Z) | | D3 | PASS (Adversary verified 2026-06-15T05:13:00Z) | | D4 | PASS (Adversary verified 2026-06-15T05:13:30Z) | | D5 | PASS (Adversary verified 2026-06-15T05:13:45Z) | | D6 | PASS (Adversary verified 2026-06-15T05:14:00Z) | Post-DONE fix: Advisory F1 resolved — corrected test count from "50 (25+25)" to "48 (23+25)" in D6 gate entry. --- ## AST node shapes (stable interface) `calc/parser.py` exports three node types and one exception: ```python @dataclass class Num: value: Union[int, float] # repr: Num(42) or Num(3.14) @dataclass class BinOp: op: str # one of '+', '-', '*', '/' left: Node right: Node # repr: BinOp('+', Num(1), Num(2)) @dataclass class Unary: op: str # '-' operand: Node # repr: Unary('-', Num(5)) class ParseError(Exception): ... ``` `parse(tokens) -> Node` consumes a token list from `calc.lexer.tokenize()`. --- ## Gate D1 — Precedence **WHAT:** `*` and `/` bind tighter than `+` and `-`. `1+2*3` parses as `1+(2*3)`, not `(1+2)*3`. **HOW to verify:** ```bash python -c " from calc.lexer import tokenize; from calc.parser import parse r = repr(parse(tokenize('1+2*3'))) assert r == \"BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))\", r print('D1 OK:', r) " python -c " from calc.lexer import tokenize; from calc.parser import parse r = repr(parse(tokenize('2*3+1'))) assert r == \"BinOp('+', BinOp('*', Num(2), Num(3)), Num(1))\", r print('D1b OK:', r) " ``` **EXPECTED:** ``` D1 OK: BinOp('+', Num(1), BinOp('*', Num(2), Num(3))) D1b OK: BinOp('+', BinOp('*', Num(2), Num(3)), Num(1)) ``` **WHERE:** `calc/parser.py` (current HEAD) --- ## Gate D2 — Left Associativity **WHAT:** Same-precedence operators associate left. `8-3-2` → `(8-3)-2`; `8/4/2` → `(8/4)/2`. **HOW to verify:** ```bash python -c " from calc.lexer import tokenize; from calc.parser import parse r = repr(parse(tokenize('8-3-2'))) assert r == \"BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))\", r print('D2 sub OK:', r) " python -c " from calc.lexer import tokenize; from calc.parser import parse r = repr(parse(tokenize('8/4/2'))) assert r == \"BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))\", r print('D2 div OK:', r) " ``` **EXPECTED:** ``` D2 sub OK: BinOp('-', BinOp('-', Num(8), Num(3)), Num(2)) D2 div OK: BinOp('/', BinOp('/', Num(8), Num(4)), Num(2)) ``` **WHERE:** `calc/parser.py` (current HEAD) --- ## Gate D3 — Parentheses **WHAT:** Parens override precedence. `(1+2)*3` parses with `+` under `*`. **HOW to verify:** ```bash python -c " from calc.lexer import tokenize; from calc.parser import parse r = repr(parse(tokenize('(1+2)*3'))) assert r == \"BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))\", r print('D3 OK:', r) " python -c " from calc.lexer import tokenize; from calc.parser import parse r = repr(parse(tokenize('8/(2+2)'))) assert r == \"BinOp('/', Num(8), BinOp('+', Num(2), Num(2)))\", r print('D3b OK:', r) " ``` **EXPECTED:** ``` D3 OK: BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)) D3b OK: BinOp('/', Num(8), BinOp('+', Num(2), Num(2))) ``` **WHERE:** `calc/parser.py` (current HEAD) --- ## Gate D4 — Unary Minus **WHAT:** Leading and nested unary minus parses correctly. **HOW to verify:** ```bash python -c " from calc.lexer import tokenize; from calc.parser import parse r = repr(parse(tokenize('-5'))) assert r == \"Unary('-', Num(5))\", r print('D4a OK:', r) r = repr(parse(tokenize('-(1+2)'))) assert r == \"Unary('-', BinOp('+', Num(1), Num(2)))\", r print('D4b OK:', r) r = repr(parse(tokenize('3 * -2'))) assert r == \"BinOp('*', Num(3), Unary('-', Num(2)))\", r print('D4c OK:', r) " ``` **EXPECTED:** ``` D4a OK: Unary('-', Num(5)) D4b OK: Unary('-', BinOp('+', Num(1), Num(2))) D4c OK: BinOp('*', Num(3), Unary('-', Num(2))) ``` **WHERE:** `calc/parser.py` (current HEAD) --- ## Gate D5 — Errors **WHAT:** Malformed inputs raise `ParseError`. Mandated cases: `"1 +"`, `"(1"`, `"1 2"`, `")("`, `""`. **HOW to verify:** ```bash python -c " from calc.lexer import tokenize from calc.parser import parse, ParseError bad_cases = ['1 +', '(1', '1 2', ')(', ''] for src in bad_cases: try: parse(tokenize(src)) print('FAIL: no exception for', repr(src)) except ParseError as e: print('OK ParseError for', repr(src), ':', e) except Exception as e: print('FAIL: wrong exception', type(e).__name__, 'for', repr(src), ':', e) " ``` **EXPECTED:** Five lines all starting with `OK ParseError for`. **WHERE:** `calc/parser.py` (current HEAD) --- ## Gate D6 — Tests Green **WHAT:** `calc/test_parser.py` (unittest) passes under `python -m unittest`, 0 failures, covering D1–D5. **HOW to verify:** ```bash python -m unittest -q ``` **EXPECTED:** `Ran 48 tests in 0.00Xs OK` (23 lexer + 25 parser) **WHERE:** `calc/test_parser.py` and `calc/parser.py` (current HEAD)