# STATUS-parse ## DONE ## Claim All DoD gates D1–D6 implemented and verified. | Gate | Item | Status | |------|------|--------| | D1 | `*`/`/` bind tighter than `+`/`-` | claimed PASS | | D2 | Left associativity | claimed PASS | | D3 | Parentheses override precedence | claimed PASS | | D4 | Unary minus | claimed PASS | | D5 | ParseError on malformed input | claimed PASS | | D6 | `calc/test_parser.py` — 31 tests, 0 failures | claimed PASS | ## Files - `calc/parser.py` — parser implementation - `calc/test_parser.py` — unittest suite Commit: `00ca873` (claim(D1-D6): feat: add calc parser with AST nodes, recursive descent, full test suite) ## How to verify ```bash # D6 — all tests python -m unittest -q # Expected: OK (31 tests, 0 failures) # D1 — mul binds tighter than add 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 of subtraction 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)) # D2 — left associativity of division 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 with binary mul python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('3 * -2')))" # Expected: BinOp('*', Num(3), Unary('-', Num(2))) # D4 — negation of group 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 — ParseError on "1 +" python -c " from calc.lexer import tokenize from calc.parser import parse, ParseError try: parse(tokenize('1 +')) print('ERROR: no exception raised') except ParseError as e: print('OK ParseError:', e) " # D5 — ParseError on "(1" python -c " from calc.lexer import tokenize from calc.parser import parse, ParseError try: parse(tokenize('(1')) print('ERROR: no exception raised') except ParseError as e: print('OK ParseError:', e) " # D5 — ParseError on "1 2" python -c " from calc.lexer import tokenize from calc.parser import parse, ParseError try: parse(tokenize('1 2')) print('ERROR: no exception raised') except ParseError as e: print('OK ParseError:', e) " # D5 — ParseError on ")(" python -c " from calc.lexer import tokenize from calc.parser import parse, ParseError try: parse(tokenize(')(')) print('ERROR: no exception raised') except ParseError as e: print('OK ParseError:', e) " # D5 — ParseError on empty string python -c " from calc.lexer import tokenize from calc.parser import parse, ParseError try: parse(tokenize('')) print('ERROR: no exception raised') except ParseError as e: print('OK ParseError:', e) " ``` ## AST Node shapes ``` Num(value) — leaf, value is int or float BinOp(op, left, right) — op is one of '+', '-', '*', '/' Unary(op, operand) — op is '-' ``` All are dataclasses with `__repr__` that prints the form above.