3.3 KiB
STATUS — phase parse
DONE
Gates
- D1 (precedence): PASS @2026-06-15T03:40Z
- D2 (left assoc): PASS @2026-06-15T03:40Z
- D3 (parentheses): PASS @2026-06-15T03:40Z
- D4 (unary minus): PASS @2026-06-15T03:40Z
- D5 (errors): PASS @2026-06-15T03:40Z
- D6 (tests green): PASS @2026-06-15T03:40Z
Source files
calc/parser.py— AST node definitions + recursive-descent parsercalc/test_parser.py— 20 unittest cases covering D1–D5
AST shape
Nodes are dataclasses defined in calc/parser.py:
@dataclass
class Num:
value: Union[int, float]
@dataclass
class BinOp:
op: str # '+', '-', '*', '/'
left: Node
right: Node
@dataclass
class Unary:
op: str # '-'
operand: Node
Verify commands (cold)
python -m unittest -q
Expected: Ran 36 tests in Xs / OK (0 failures)
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('1+2*3')))"
Expected: BinOp(op='+', left=Num(value=1), right=BinOp(op='*', left=Num(value=2), right=Num(value=3)))
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('8-3-2')))"
Expected: BinOp(op='-', left=BinOp(op='-', left=Num(value=8), right=Num(value=3)), right=Num(value=2))
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('8/4/2')))"
Expected: BinOp(op='/', left=BinOp(op='/', left=Num(value=8), right=Num(value=4)), right=Num(value=2))
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('(1+2)*3')))"
Expected: BinOp(op='*', left=BinOp(op='+', left=Num(value=1), right=Num(value=2)), right=Num(value=3))
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-5')))"
Expected: Unary(op='-', operand=Num(value=5))
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-(1+2)')))"
Expected: Unary(op='-', operand=BinOp(op='+', left=Num(value=1), right=Num(value=2)))
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('3 * -2')))"
Expected: BinOp(op='*', left=Num(value=3), right=Unary(op='-', operand=Num(value=2)))
python -c "
from calc.lexer import tokenize; from calc.parser import parse, ParseError
for expr in ['1 +', '(1', '1 2', ')(', '']:
try:
parse(tokenize(expr))
print(f'{expr!r}: NO ERROR (FAIL)')
except ParseError as e:
print(f'{expr!r}: ParseError OK')
"
Expected: all 5 lines print ParseError OK
Gate-specific DoD mapping
D1 — precedence: 1+2*3 parses as 1+(2*3) — * is under the right child of +, not the other way.
D2 — left assoc: 8-3-2 → left-leaning tree; 8/4/2 → left-leaning tree. The while-loop in _expr/_term naturally accumulates left.
D3 — parens: (1+2)*3 → BinOp('*', BinOp('+', ...), Num(3)) — + is under *'s left child.
D4 — unary: -5 → Unary, -(1+2) → Unary(BinOp(...)), 3*-2 → BinOp('*', Num(3), Unary(...)).
D5 — errors: "1 +", "(1", "1 2", ")(", "" all raise ParseError (not Python built-ins).
D6 — tests: python -m unittest -q → 36 tests, 0 failures.