3.7 KiB
3.7 KiB
STATUS — parse phase (Builder)
DONE
All DoD items self-certified (BUILD phase — deferred Adversary review).
DoD Checklist
- D1 — precedence: PASS.
*//bind tighter than+/-.1+2*3→BinOp('+', Num(1), BinOp('*', Num(2), Num(3))). - D2 — left associativity: PASS.
8-3-2→BinOp('-', BinOp('-', Num(8), Num(3)), Num(2));8/4/2→BinOp('/', BinOp('/', Num(8), Num(4)), Num(2)). - D3 — parentheses: PASS.
(1+2)*3→BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)). - D4 — unary minus: PASS.
-5→Unary('-', Num(5));3 * -2→BinOp('*', Num(3), Unary('-', Num(2))). - D5 — errors: PASS. All five specified inputs (
"1 +","(1","1 2",")(","") raiseParseError. - D6 — tests green: PASS. 35 tests total (11 lexer + 24 parser), 0 failures.
Verification Commands (for Adversary cold-verify)
# From repo root
python -m unittest -q
# Expected: Ran 35 tests in X.XXXs / OK (exit 0)
# D1 — precedence
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)))
# D2 — left associativity
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))
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 — parentheses
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
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-5')))"
# Expected: Unary('-', Num(5))
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('3 * -2')))"
# Expected: BinOp('*', Num(3), Unary('-', 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)))
# D5 — errors (each must raise ParseError, not crash with different exception)
python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError
try:
parse(tokenize('1 +'))
print('FAIL: no error')
except ParseError as e:
print('OK:', e)"
python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError
try:
parse(tokenize('(1'))
print('FAIL: no error')
except ParseError as e:
print('OK:', e)"
python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError
try:
parse(tokenize('1 2'))
print('FAIL: no error')
except ParseError as e:
print('OK:', e)"
python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError
try:
parse(tokenize(')('))
print('FAIL: no error')
except ParseError as e:
print('OK:', e)"
python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError
try:
parse(tokenize(''))
print('FAIL: no error')
except ParseError as e:
print('OK:', e)"
AST Shape Reference
Nodes (all dataclasses in calc/parser.py):
| Class | Fields | Example |
|---|---|---|
Num |
value: int | float |
Num(42), Num(3.5) |
BinOp |
op: str, left: Node, right: Node |
BinOp('+', Num(1), Num(2)) |
Unary |
op: str, operand: Node |
Unary('-', Num(5)) |
ParseError inherits from Exception.
Files Produced
calc/parser.py—ParseError,Num,BinOp,Unary,Node,parse()calc/test_parser.py— 24 unittest cases covering D1–D5