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

5.0 KiB
Raw Blame History

STATUS — phase parse

Role: Builder owns this file.

DONE

All gates PASSED by Adversary (2026-06-15T05:14:00Z). Advisory F1 corrected post-PASS.

Gate Status
D1 PASS (Adversary verified 2026-06-15T05:12:00Z)
D2 PASS (Adversary verified 2026-06-15T05:12:30Z)
D3 PASS (Adversary verified 2026-06-15T05:13:00Z)
D4 PASS (Adversary verified 2026-06-15T05:13:30Z)
D5 PASS (Adversary verified 2026-06-15T05:13:45Z)
D6 PASS (Adversary verified 2026-06-15T05:14:00Z)

Post-DONE fix: Advisory F1 resolved — corrected test count from "50 (25+25)" to "48 (23+25)" in D6 gate entry.


AST node shapes (stable interface)

calc/parser.py exports three node types and one exception:

@dataclass
class Num:
    value: Union[int, float]
    # repr: Num(42) or Num(3.14)

@dataclass
class BinOp:
    op: str        # one of '+', '-', '*', '/'
    left: Node
    right: Node
    # repr: BinOp('+', Num(1), Num(2))

@dataclass
class Unary:
    op: str        # '-'
    operand: Node
    # repr: Unary('-', Num(5))

class ParseError(Exception): ...

parse(tokens) -> Node consumes a token list from calc.lexer.tokenize().


Gate D1 — Precedence

WHAT: * and / bind tighter than + and -. 1+2*3 parses as 1+(2*3), not (1+2)*3.

HOW to verify:

python -c "
from calc.lexer import tokenize; from calc.parser import parse
r = repr(parse(tokenize('1+2*3')))
assert r == \"BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))\", r
print('D1 OK:', r)
"
python -c "
from calc.lexer import tokenize; from calc.parser import parse
r = repr(parse(tokenize('2*3+1')))
assert r == \"BinOp('+', BinOp('*', Num(2), Num(3)), Num(1))\", r
print('D1b OK:', r)
"

EXPECTED:

D1 OK: BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))
D1b OK: BinOp('+', BinOp('*', Num(2), Num(3)), Num(1))

WHERE: calc/parser.py (current HEAD)


Gate D2 — Left Associativity

WHAT: Same-precedence operators associate left. 8-3-2(8-3)-2; 8/4/2(8/4)/2.

HOW to verify:

python -c "
from calc.lexer import tokenize; from calc.parser import parse
r = repr(parse(tokenize('8-3-2')))
assert r == \"BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))\", r
print('D2 sub OK:', r)
"
python -c "
from calc.lexer import tokenize; from calc.parser import parse
r = repr(parse(tokenize('8/4/2')))
assert r == \"BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))\", r
print('D2 div OK:', r)
"

EXPECTED:

D2 sub OK: BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))
D2 div OK: BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))

WHERE: calc/parser.py (current HEAD)


Gate D3 — Parentheses

WHAT: Parens override precedence. (1+2)*3 parses with + under *.

HOW to verify:

python -c "
from calc.lexer import tokenize; from calc.parser import parse
r = repr(parse(tokenize('(1+2)*3')))
assert r == \"BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))\", r
print('D3 OK:', r)
"
python -c "
from calc.lexer import tokenize; from calc.parser import parse
r = repr(parse(tokenize('8/(2+2)')))
assert r == \"BinOp('/', Num(8), BinOp('+', Num(2), Num(2)))\", r
print('D3b OK:', r)
"

EXPECTED:

D3 OK: BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))
D3b OK: BinOp('/', Num(8), BinOp('+', Num(2), Num(2)))

WHERE: calc/parser.py (current HEAD)


Gate D4 — Unary Minus

WHAT: Leading and nested unary minus parses correctly.

HOW to verify:

python -c "
from calc.lexer import tokenize; from calc.parser import parse
r = repr(parse(tokenize('-5')))
assert r == \"Unary('-', Num(5))\", r
print('D4a OK:', r)
r = repr(parse(tokenize('-(1+2)')))
assert r == \"Unary('-', BinOp('+', Num(1), Num(2)))\", r
print('D4b OK:', r)
r = repr(parse(tokenize('3 * -2')))
assert r == \"BinOp('*', Num(3), Unary('-', Num(2)))\", r
print('D4c OK:', r)
"

EXPECTED:

D4a OK: Unary('-', Num(5))
D4b OK: Unary('-', BinOp('+', Num(1), Num(2)))
D4c OK: BinOp('*', Num(3), Unary('-', Num(2)))

WHERE: calc/parser.py (current HEAD)


Gate D5 — Errors

WHAT: Malformed inputs raise ParseError. Mandated cases: "1 +", "(1", "1 2", ")(", "".

HOW to verify:

python -c "
from calc.lexer import tokenize
from calc.parser import parse, ParseError
bad_cases = ['1 +', '(1', '1 2', ')(', '']
for src in bad_cases:
    try:
        parse(tokenize(src))
        print('FAIL: no exception for', repr(src))
    except ParseError as e:
        print('OK ParseError for', repr(src), ':', e)
    except Exception as e:
        print('FAIL: wrong exception', type(e).__name__, 'for', repr(src), ':', e)
"

EXPECTED: Five lines all starting with OK ParseError for.

WHERE: calc/parser.py (current HEAD)


Gate D6 — Tests Green

WHAT: calc/test_parser.py (unittest) passes under python -m unittest, 0 failures, covering D1D5.

HOW to verify:

python -m unittest -q

EXPECTED: Ran 48 tests in 0.00Xs OK (23 lexer + 25 parser)

WHERE: calc/test_parser.py and calc/parser.py (current HEAD)