2.1 KiB
STATUS-parse
Phase: parse
Files: calc/parser.py, calc/test_parser.py
AST Shape
Num(value) — leaf; value is int or float
BinOp(op, left, right) — op ∈ {'+', '-', '*', '/'}; left and right are Nodes
Unary(op, operand) — op is '-'; operand is a Node
All nodes are @dataclass with custom __repr__.
parse(tokens) -> Node accepts the list returned by calc.lexer.tokenize.
ParseError(Exception) is raised for all malformed input.
Gate Verification
D1 — Precedence
Command: 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)))
Observed: BinOp('+', Num(1), BinOp('*', Num(2), Num(3))) ✓ PASS
D2 — Left Associativity
Command: parse 8-3-2 and 8/4/2
Expected: BinOp('-', BinOp('-', Num(8), Num(3)), Num(2)) and BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))
Observed: Both matched exactly ✓ PASS
D3 — Parentheses
Command: parse (1+2)*3
Expected: BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))
Observed: BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)) ✓ PASS
D4 — Unary Minus
Commands: parse -5, -(1+2), 3 * -2
Expected: Unary('-', Num(5)), Unary('-', BinOp('+', Num(1), Num(2))), BinOp('*', Num(3), Unary('-', Num(2)))
Observed: All three matched exactly ✓ PASS
D5 — Errors
Inputs tested: "1 +", "(1", "1 2", ")(", ""
Expected: Each raises ParseError
Observed:
"1 +"→ParseError: unexpected end of input✓"(1"→ParseError: unclosed '(' — expected ')'✓"1 2"→ParseError: unexpected token Token(kind='NUMBER', value=2) after expression✓")("→ParseError: unexpected token 'RPAREN' (')')✓""→ParseError: unexpected end of input✓ PASS
D6 — Tests Green
Command: python -m unittest -q
Expected: 0 failures
Observed: Ran 35 tests in 0.001s — OK ✓ PASS