4.2 KiB
4.2 KiB
STATUS-parse — Builder
DONE
All gates D1–D6 verified PASS by Adversary @2026-06-15T00:50Z. See REVIEW-parse.md.
Gate: D1–D6 CLAIMED → PASS
All six gates are implemented and verified locally.
Files
| File | Description |
|---|---|
calc/parser.py |
Recursive-descent parser exposing parse(tokens) -> Node |
calc/test_parser.py |
unittest suite covering D1–D6 |
AST Node Types (stable shape for evaluator)
@dataclass
class Num:
value: Any # int or float from lexer
@dataclass
class BinOp:
op: str # '+', '-', '*', '/'
left: Any # Node
right: Any # Node
@dataclass
class Unary:
op: str # '-'
operand: Any # Node
All three are dataclasses with __repr__ — equality comparison works via ==.
Verification commands (cold-runnable from any clone)
# D6 — all tests green
python -m unittest -q
# D1 — precedence: 1+2*3 => BinOp('+', Num(1), BinOp('*', Num(2), Num(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)))
# D1 — precedence: 2*3+1 => BinOp('+', BinOp('*', Num(2), Num(3)), Num(1))
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('2*3+1')))"
# expected: BinOp('+', BinOp('*', Num(2), Num(3)), Num(1))
# D2 — left assoc subtraction: 8-3-2 => BinOp('-', BinOp('-', Num(8), Num(3)), Num(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 assoc division: 8/4/2 => BinOp('/', BinOp('/', Num(8), Num(4)), 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 — parens override: (1+2)*3 => BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))
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 => Unary('-', Num(5))
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-5')))"
# expected: Unary('-', Num(5))
# D4 — unary in paren: -(1+2) => Unary('-', BinOp('+', Num(1), 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)))
# D4 — unary in mul: 3 * -2 => BinOp('*', Num(3), Unary('-', Num(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)))
# D5 — each raises ParseError (must NOT raise any other exception)
python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError
for s in ['1 +', '(1', '1 2', ')(', '']:
try: parse(tokenize(s)); print(f'FAIL no error for {s!r}')
except ParseError as e: print(f'OK {s!r} => ParseError: {e}')
except Exception as e: print(f'FAIL wrong exc for {s!r}: {type(e).__name__}: {e}')
"
# expected: 5 lines each starting "OK"
Expected outputs (exact)
| Gate | Expression | Expected repr |
|---|---|---|
| D1 | 1+2*3 |
BinOp('+', Num(1), BinOp('*', Num(2), Num(3))) |
| D1 | 2*3+1 |
BinOp('+', BinOp('*', Num(2), Num(3)), Num(1)) |
| D2 | 8-3-2 |
BinOp('-', BinOp('-', Num(8), Num(3)), Num(2)) |
| D2 | 8/4/2 |
BinOp('/', BinOp('/', Num(8), Num(4)), Num(2)) |
| D3 | (1+2)*3 |
BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)) |
| D4 | -5 |
Unary('-', Num(5)) |
| D4 | -(1+2) |
Unary('-', BinOp('+', Num(1), Num(2))) |
| D4 | 3 * -2 |
BinOp('*', Num(3), Unary('-', Num(2))) |
| D5 | 1 + |
ParseError: unexpected token 'EOF' (None) |
| D5 | (1 |
ParseError: expected RPAREN, got 'EOF' (None) |
| D5 | 1 2 |
ParseError: unexpected token 'NUMBER' (2) after expression |
| D5 | )( |
ParseError: unexpected token 'RPAREN' (')') |
| D5 | `` | ParseError: empty expression |
| D6 | python -m unittest -q |
Ran 46 tests in …s OK |