Files
agent-orchestrator-benchmark/calculators/builder-adversary-lean/run-05/machine-docs/STATUS-parse.md

5.0 KiB
Raw Blame History

STATUS — parse phase (Builder)

DONE

All DoD gates (D1D6) verified PASS by Adversary @2026-06-15T06:13:00Z. No veto. Phase complete.

Current state

Implementation complete. calc/parser.py and calc/test_parser.py created. 48 tests pass (24 lex + 24 parser). All gates claimed and Adversary-verified.

AST Shape Reference

Num(value)              — value is int or float
BinOp(op, left, right)  — op in ('+', '-', '*', '/')
Unary(op, operand)      — op is '-'

repr() of each node class is canonical (used in test assertions and verification commands).


Gates

D1 — precedence: CLAIMED, awaiting Adversary

WHAT: * and / bind tighter than + and -. 1+2*3BinOp('+', Num(1), BinOp('*', Num(2), Num(3))) (not BinOp('*', BinOp('+', ...), ...)).

HOW to verify:

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+1')))"
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('5-2*3')))"

EXPECTED:

BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))
BinOp('+', BinOp('*', Num(2), Num(3)), Num(1))
BinOp('-', Num(5), BinOp('*', Num(2), Num(3)))

WHERE: calc/parser.py_expr() calls _term() which handles *//; _expr() handles +/- at lower priority.


D2 — left associativity: CLAIMED, awaiting Adversary

WHAT: Same-precedence operators associate left. 8-3-2BinOp('-', BinOp('-', Num(8), Num(3)), Num(2)). 8/4/2BinOp('/', BinOp('/', Num(8), Num(4)), Num(2)).

HOW to verify:

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_expr() and _term() use while loops, wrapping the accumulating left side.


D3 — parentheses: CLAIMED, awaiting Adversary

WHAT: Parens override precedence. (1+2)*3BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)).

HOW to verify:

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('1*(2+3)')))"

EXPECTED:

BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))
BinOp('*', Num(1), BinOp('+', Num(2), Num(3)))

WHERE: calc/parser.py_primary() handles LPAREN … RPAREN by calling _expr() recursively.


D4 — unary minus: CLAIMED, awaiting Adversary

WHAT: Leading and nested unary minus parses correctly. -5Unary('-', Num(5)). -(1+2)Unary('-', BinOp('+', Num(1), Num(2))). 3 * -2BinOp('*', Num(3), Unary('-', Num(2))).

HOW to verify:

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('-', Num(5))
Unary('-', BinOp('+', Num(1), Num(2)))
BinOp('*', Num(3), Unary('-', Num(2)))

WHERE: calc/parser.py_factor() detects MINUS and recurses, returning Unary('-', operand).


D5 — errors: CLAIMED, awaiting Adversary

WHAT: Malformed input raises ParseError (not LexError, IndexError, etc.). Five required cases: "1 +", "(1", "1 2", ")(", "".

HOW to verify:

python -c "
from calc.lexer import tokenize
from calc.parser import parse, ParseError
errors = ['1 +', '(1', '1 2', ')(', '']
for src in errors:
    try:
        parse(tokenize(src))
        print(f'FAIL: {src!r} did not raise')
    except ParseError as e:
        print(f'OK: {src!r} -> ParseError: {e}')
    except Exception as e:
        print(f'FAIL: {src!r} raised wrong exception: {type(e).__name__}: {e}')
"

EXPECTED:

OK: '1 +' -> ParseError: unexpected token 'EOF'
OK: '(1' -> ParseError: unclosed parenthesis, got 'EOF'
OK: '1 2' -> ParseError: unexpected token 'NUMBER'
OK: ')(' -> ParseError: unexpected token 'RPAREN'
OK: '' -> ParseError: empty input

WHERE: calc/parser.pyparse(), _primary(), _primary() RPAREN check.


D6 — tests green: CLAIMED, awaiting Adversary

WHAT: calc/test_parser.py + calc/test_lexer.py combined: 48 tests, 0 failures under python -m unittest.

HOW to verify:

python -m unittest -q

EXPECTED:

----------------------------------------------------------------------
Ran 48 tests in 0.002s

OK

WHERE: calc/test_parser.py (24 tests covering D1D5), calc/test_lexer.py (24 tests from lex phase).