# STATUS — phase `parse` ## DONE All DoD gates self-certified (BUILD phase — deferred review). ## Gates | Gate | Status | |------|--------| | D1 — precedence | PASS (self-certified) | | D2 — left associativity | PASS (self-certified) | | D3 — parentheses | PASS (self-certified) | | D4 — unary minus | PASS (self-certified) | | D5 — errors | PASS (self-certified) | | D6 — tests green | PASS (self-certified) | ## Verify commands (Adversary cold-verify) ```bash # D6 — all tests green (19 lex + 25 parser = 44 total) python -m unittest -q # Expected: Ran 44 tests in 0.001s OK # D1 — precedence: 1+2*3 must parse as 1+(2*3) 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: 8-3-2 must parse as (8-3)-2 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)) # D2 — left associativity: 8/4/2 must parse as (8/4)/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 — parens override: (1+2)*3 has + under * 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: -5 python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-5')))" # Expected: Unary('-', Num(5)) # D4 — unary in multiply: 3 * -2 python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('3 * -2')))" # Expected: BinOp('*', Num(3), Unary('-', Num(2))) # D4 — unary with paren: -(1+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 — error: 1 + (EOF after operator) python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize('1 +'))" 2>&1 # Expected: calc.parser.ParseError raised # D5 — error: (1 (unclosed paren) python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize('(1'))" 2>&1 # Expected: calc.parser.ParseError raised # D5 — error: 1 2 (two consecutive numbers) python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize('1 2'))" 2>&1 # Expected: calc.parser.ParseError raised # D5 — error: )( (wrong-order parens) python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize(')('))" 2>&1 # Expected: calc.parser.ParseError raised # D5 — error: empty string python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize(''))" 2>&1 # Expected: calc.parser.ParseError raised ``` ## AST shape (for Adversary to re-derive) Nodes are Python dataclasses with custom `__repr__`: - `Num(value)` — leaf node, value is int or float - `BinOp(op, left, right)` — binary operator; op is the literal char `'+'`, `'-'`, `'*'`, `'/'` - `Unary(op, operand)` — unary operator; op is `'-'` ## WHAT is claimed - `calc/parser.py` — `parse(tokens) -> Node` using recursive descent - Grammar: `expr → term ((+|-) term)*`, `term → unary ((*|/) unary)*`, `unary → - unary | primary`, `primary → NUMBER | ( expr )` - `ParseError` defined in `calc.parser` (plain Exception subclass) - All 44 tests pass (`python -m unittest -q`) - Parser asserts on tree structure (not evaluation) in tests ## Artifacts - `calc/parser.py` — ParseError, Num, BinOp, Unary, parse() - `calc/test_parser.py` — 25 unittest cases covering D1–D5