## DONE # STATUS — phase `parse` ## Claimed gates: D1 D2 D3 D4 D5 D6 ## Files - `calc/parser.py` — parser implementation - `calc/test_parser.py` — unittest suite ## AST node shapes ``` Num(value) — leaf numeric literal; value is int or float BinOp(op, left, right) — binary op; op is one of '+' '-' '*' '/' Unary(op, operand) — unary op; op is '-' ``` All nodes implement `__repr__` and `__eq__`. ## How to verify ```bash # D6 — all tests green python -m unittest -q # D1 — * binds tighter than + 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 — parens override precedence 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('-(1+2)')))" # expected: Unary('-', BinOp('+', Num(1), Num(2))) python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('3 * -2')))" # expected: BinOp('*', Num(3), Unary('-', Num(2))) # D5 — ParseError for malformed input python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError for s in ['1 +', '(1', '1 2', ')(' , '']: try: parse(tokenize(s)) print('FAIL', s) except ParseError: print('OK', repr(s)) " # expected: all 5 lines print OK # D1/D3 anti-confusion check — structure differs: # 1+2*3 => BinOp('+', Num(1), BinOp('*', Num(2), Num(3))) [* is the right child of +] # (1+2)*3 => BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)) [+ is the left child of *] ``` ## Commit `a05812d` — claim(D1-D6): add recursive-descent parser with full gate coverage