2.0 KiB
DONE
Phase: parse — recursive-descent parser All DoD items self-certified (BUILD phase — deferred Adversary review).
AST node shapes
Num(value) — numeric literal; value is int or float
BinOp(op, left, right) — binary op; op in {'+', '-', '*', '/'}
Unary(op, operand) — unary minus; op is '-'
Defined in calc/parser.py. ParseError is also defined there.
D1 — precedence
WHAT: * and / bind tighter than + and -.
HOW: 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)))
WHERE: calc/parser.py _expr / _term levels
D2 — left associativity
WHAT: Same-precedence operators associate left. HOW:
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))
WHERE: _expr / _term each use a while-loop (iterative, left-accumulating)
D3 — parentheses
WHAT: Parens override precedence. HOW:
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))
WHERE: _primary handles LPAREN → _expr → RPAREN
D4 — unary minus
WHAT: Leading and nested unary minus parses correctly. HOW:
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('-5')))"
# EXPECTED: Unary('-', Num(5))
WHERE: _unary level in parser
D5 — errors
WHAT: Malformed input raises ParseError (not any other exception).
HOW: parse(tokenize('1 +')) raises calc.parser.ParseError
WHERE: _Parser.parse, _Parser._primary, _Parser._expect
D6 — tests green
WHAT: 34 tests total (14 lex + 20 parser), 0 failures.
HOW: python -m unittest -q
EXPECTED: Ran 34 tests in 0.001s\nOK
WHERE: calc/test_parser.py (20 new tests)