4.5 KiB
STATUS — Phase parse (Builder)
DONE
All gates D1–D6 Adversary-verified PASS @2026-06-15T06:32:30Z. Phase parse complete.
Current State
Gates D1 through D6 implemented, claimed, and Adversary-verified. Phase complete.
Gate D1 — precedence — CLAIMED, awaiting Adversary
WHAT: * and / bind tighter than + and -. 1+2*3 parses as BinOp('+', Num(1), BinOp('*', Num(2), Num(3))), not BinOp('+', BinOp('+',…)).
HOW:
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('1+2*3')))"
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('2*3+4')))"
EXPECTED:
BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))
BinOp('+', BinOp('*', Num(2), Num(3)), Num(4))
WHERE: calc/parser.py — commit 64d0252
Gate D2 — left associativity — CLAIMED, awaiting Adversary
WHAT: Same-precedence operators associate left. 8-3-2 → (8-3)-2; 8/4/2 → (8/4)/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('-', BinOp('-', Num(8), Num(3)), Num(2))
BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))
WHERE: calc/parser.py — commit 64d0252
Gate D3 — parentheses — CLAIMED, awaiting Adversary
WHAT: Parens override precedence. (1+2)*3 parses with + under *.
HOW:
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('(1+2)*3')))"
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('((2+3))')))"
EXPECTED:
BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))
BinOp('+', Num(2), Num(3))
WHERE: calc/parser.py _primary() method — commit 64d0252
Gate D4 — unary minus — CLAIMED, awaiting Adversary
WHAT: Leading and nested unary minus parses correctly: -5, -(1+2), 3 * -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')))"
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('--5')))"
EXPECTED:
Unary('-', Num(5))
Unary('-', BinOp('+', Num(1), Num(2)))
BinOp('*', Num(3), Unary('-', Num(2)))
Unary('-', Unary('-', Num(5)))
WHERE: calc/parser.py _unary() method — commit 64d0252
Gate D5 — errors — CLAIMED, awaiting Adversary
WHAT: Malformed input raises ParseError (not any other exception) for all five plan cases.
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(f'FAIL: {src!r} did not raise')
except ParseError as e:
print(f'OK ParseError: {src!r} -> {e}')
except Exception as e:
print(f'FAIL wrong exc: {src!r} -> {type(e).__name__}: {e}')
"
EXPECTED:
OK ParseError: '1 +' -> unexpected end of expression
OK ParseError: '(1' -> expected ')' but got 'EOF' ('')
OK ParseError: '1 2' -> unexpected token 'NUMBER' (2)
OK ParseError: ')(' -> unexpected token 'RPAREN' (')')
OK ParseError: '' -> empty expression
WHERE: calc/parser.py parse(), _primary(), _expr() — commit 64d0252
Gate D6 — tests green — CLAIMED, awaiting Adversary
WHAT: calc/test_parser.py (unittest) passes with 0 failures, covering D1–D5. Total 35 tests pass (15 lexer + 20 parser).
HOW:
python -m unittest -q
EXPECTED:
Ran 35 tests in 0.00Xs
OK
Test coverage in calc/test_parser.py:
TestPrecedence(4 tests): D1 —1+2*3,2*3+4,6-2*3,1+6/2TestAssociativity(4 tests): D2 —8-3-2,8/4/2,1+2+3,2*3*4TestParentheses(3 tests): D3 —(1+2)*3,((2+3)),3*(2+1)TestUnaryMinus(4 tests): D4 —-5,-(1+2),3 * -2,--5TestErrors(5 tests): D5 —1 +,(1,1 2,)(,""
All tests assert on tree structure via assertEqual on dataclass instances (not on evaluation).
WHERE: calc/test_parser.py, calc/parser.py — commit 64d0252