# STATUS-parse ## DONE ## Claimed gates: D1, D2, D3, D4, D5, D6 **Commit:** (see git log — latest push on main) **Files:** `calc/parser.py`, `calc/test_parser.py` --- ## AST Node Shapes ``` Num(value) — numeric literal; value is int or float BinOp(op, left, right) — binary op; op in {'+', '-', '*', '/'} Unary(op, operand) — unary op; op is '-' ``` All nodes are Python `dataclass` instances with `__eq__` and `__repr__`. --- ## Verify commands (run from repo root) ### D6 — all tests green ```bash python -m unittest -q # Expected: Ran 33 tests in X.XXXs OK (14 lex + 19 parser) ``` ### D1 — `*` binds tighter than `+` ```bash 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 ```bash 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 — parentheses override precedence ```bash 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 ```bash 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 — errors raise ParseError ```bash python -c " from calc.lexer import tokenize from calc.parser import parse, ParseError cases = ['1 +', '(1', '1 2', ')(', ''] for src in cases: try: parse(tokenize(src)) print(f'FAIL {src!r}: no error raised') except ParseError as e: print(f'OK {src!r}: ParseError') except Exception as e: print(f'FAIL {src!r}: wrong exception {type(e).__name__}') " # Expected: OK for all 5 cases ``` --- ## DoD mapping | Gate | DoD item | How verified | |------|----------|-------------| | D1 | `*`/`/` bind tighter than `+`/`-` | `1+2*3` → `BinOp('+', Num(1), BinOp('*', ...))` | | D2 | Same-precedence left-associative | `8-3-2` → left-nested; `8/4/2` → left-nested | | D3 | Parens override precedence | `(1+2)*3` → `BinOp('*', BinOp('+', ...), ...)` | | D4 | Unary minus (leading, nested, in mul) | `-5`, `-(1+2)`, `3 * -2` all parsed as Unary | | D5 | Malformed input raises ParseError | All 5 cases raise ParseError, not other exceptions | | D6 | 0 failures in `python -m unittest` | 33 tests OK (14 lex + 19 parser) |