4.1 KiB
STATUS — phase parse
DONE
All gates D1–D6 Adversary-verified PASS @2026-06-15T04:22:33Z. No vetoes. Phase complete.
Gates: D1–D6 CLAIMED, awaiting Adversary
All six gates implemented and locally verified. Claiming all simultaneously.
AST node shapes (stable contract for evaluator)
Num(value)— leaf;valueisintorfloatBinOp(op, left, right)— binary op;opis'PLUS'|'MINUS'|'STAR'|'SLASH'Unary(op, operand)— unary minus;opis'MINUS'
All nodes are frozen @dataclasss with __repr__ and __eq__ derived from fields.
Defined in calc/parser.py.
D1 — precedence (CLAIMED)
WHAT: *// bind tighter than +/-: 1+2*3 parses as BinOp('PLUS', Num(1), BinOp('STAR', Num(2), Num(3))).
HOW:
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('1+2*3')))"
EXPECTED:
BinOp('PLUS', Num(1), BinOp('STAR', Num(2), Num(3)))
WHERE: calc/parser.py — expr() loops over +/-, term() loops over *//.
D2 — left associativity (CLAIMED)
WHAT: Same-precedence operators associate left.
8-3-2→BinOp('MINUS', BinOp('MINUS', Num(8), Num(3)), Num(2))8/4/2→BinOp('SLASH', BinOp('SLASH', Num(8), Num(4)), Num(2))
HOW:
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('MINUS', BinOp('MINUS', Num(8), Num(3)), Num(2))
BinOp('SLASH', BinOp('SLASH', Num(8), Num(4)), Num(2))
WHERE: calc/parser.py — while loops in expr() and term() fold left.
D3 — parentheses (CLAIMED)
WHAT: Parens override precedence: (1+2)*3 → BinOp('STAR', BinOp('PLUS', Num(1), Num(2)), Num(3)).
HOW:
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('(1+2)*3')))"
EXPECTED:
BinOp('STAR', BinOp('PLUS', Num(1), Num(2)), Num(3))
WHERE: calc/parser.py — primary() handles LPAREN … RPAREN.
D4 — unary minus (CLAIMED)
WHAT: Leading and nested unary minus works.
-5→Unary('MINUS', Num(5))-(1+2)→Unary('MINUS', BinOp('PLUS', Num(1), Num(2)))3 * -2→BinOp('STAR', Num(3), Unary('MINUS', Num(2)))
HOW:
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('MINUS', Num(5))
Unary('MINUS', BinOp('PLUS', Num(1), Num(2)))
BinOp('STAR', Num(3), Unary('MINUS', Num(2)))
WHERE: calc/parser.py — unary() intercepts MINUS before primary().
D5 — errors (CLAIMED)
WHAT: Each malformed input raises ParseError (not any other exception).
HOW:
python -c "
from calc.lexer import tokenize
from calc.parser import parse, ParseError
cases = ['1 +', '(1', '1 2', ')(', '']
for src in cases:
try:
parse(tokenize(src))
print('FAIL — no error for', repr(src))
except ParseError as e:
print('OK', repr(src), '->', e)
"
EXPECTED (all OK lines):
OK '1 +' -> unexpected token 'EOF'
OK '(1' -> expected ')', got 'EOF'
OK '1 2' -> unexpected token 'NUMBER' after expression
OK ')(' -> unexpected token 'RPAREN'
OK '' -> unexpected token 'EOF'
WHERE: calc/parser.py — primary() raises on bad token; trailing-token check after expr().
D6 — tests green (CLAIMED)
WHAT: python -m unittest -q runs 39 tests (17 lex + 22 parser), 0 failures.
HOW:
python -m unittest -q
EXPECTED:
----------------------------------------------------------------------
Ran 39 tests in ...s
OK
WHERE: calc/test_parser.py — 22 tests across 5 classes (TestPrecedence, TestLeftAssociativity, TestParentheses, TestUnaryMinus, TestErrors).