artifacts: add calculators/ — the 30 built calculators (5/variant) + machine-docs + git logs
This commit is contained in:
@ -0,0 +1,138 @@
|
||||
# STATUS — parse phase
|
||||
|
||||
## DONE
|
||||
|
||||
All DoD gates D1–D6 implemented and self-certified (BUILD phase — DEFERRED review protocol).
|
||||
|
||||
---
|
||||
|
||||
## What was built
|
||||
|
||||
- `calc/parser.py` — `ParseError`, `Num`, `BinOp`, `Unary`, `parse(tokens) -> Node`
|
||||
- `calc/test_parser.py` — 24 unittest cases covering D1–D5 (D6 = all pass)
|
||||
|
||||
---
|
||||
|
||||
## AST shape (stable contract for the evaluator)
|
||||
|
||||
```
|
||||
Num(value) .value — int or float
|
||||
BinOp(op, left, right) .op — one of '+', '-', '*', '/'
|
||||
.left — any Node
|
||||
.right — any Node
|
||||
Unary(op, operand) .op — '-'
|
||||
.operand — any Node
|
||||
```
|
||||
|
||||
All nodes implement `__repr__` and `__eq__`.
|
||||
|
||||
---
|
||||
|
||||
## D1 — Precedence ✓
|
||||
|
||||
`1+2*3` parses as `BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))` — `*` tighter than `+`.
|
||||
|
||||
Verify:
|
||||
```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 ✓
|
||||
|
||||
`8-3-2` → `BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))`
|
||||
`8/4/2` → `BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))`
|
||||
|
||||
Verify:
|
||||
```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 ✓
|
||||
|
||||
`(1+2)*3` → `BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))` — `+` is child of `*`.
|
||||
|
||||
Verify:
|
||||
```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 ✓
|
||||
|
||||
```
|
||||
-5 → Unary('-', Num(5))
|
||||
-(1+2) → Unary('-', BinOp('+', Num(1), Num(2)))
|
||||
3 * -2 → BinOp('*', Num(3), Unary('-', Num(2)))
|
||||
```
|
||||
|
||||
Verify:
|
||||
```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 ✓
|
||||
|
||||
Each of the following raises `ParseError` (not any other exception):
|
||||
|
||||
| Input | ParseError message |
|
||||
|----------|--------------------|
|
||||
| `'1 +'` | `Unexpected end of input` |
|
||||
| `'(1'` | `Expected RPAREN, got 'EOF' (None)` |
|
||||
| `'1 2'` | `Unexpected token after expression: 'NUMBER' (2)` |
|
||||
| `')('` | `Unexpected token 'RPAREN' (')')` |
|
||||
| `''` | `Empty input` |
|
||||
|
||||
Verify:
|
||||
```bash
|
||||
python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize('1 +'))"
|
||||
# Expected: raises ParseError: Unexpected end of input
|
||||
|
||||
python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize('(1'))"
|
||||
# Expected: raises ParseError: Expected RPAREN...
|
||||
|
||||
python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize('1 2'))"
|
||||
# Expected: raises ParseError: Unexpected token after expression...
|
||||
|
||||
python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize(')('))"
|
||||
# Expected: raises ParseError: Unexpected token 'RPAREN'...
|
||||
|
||||
python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize(''))"
|
||||
# Expected: raises ParseError: Empty input
|
||||
```
|
||||
|
||||
## D6 — Tests green ✓
|
||||
|
||||
44 total tests (20 lex + 24 parser), 0 failures.
|
||||
|
||||
Verify:
|
||||
```bash
|
||||
python -m unittest -q
|
||||
# Expected: Ran 44 tests in 0.00xs / OK
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Plan verify commands (from parse.md)
|
||||
|
||||
```bash
|
||||
python -m unittest -q
|
||||
# → Ran 44 tests in 0.001s / OK
|
||||
|
||||
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(parse(tokenize('1+2*3')))"
|
||||
# → BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))
|
||||
|
||||
python -c "from calc.lexer import tokenize; from calc.parser import parse; parse(tokenize('1 +'))"
|
||||
# → ParseError: Unexpected end of input
|
||||
```
|
||||
Reference in New Issue
Block a user