artifacts: add calculators/ — the 30 built calculators (5/variant) + machine-docs + git logs
This commit is contained in:
15
calculators/builder-solo/run-01/machine-docs/JOURNAL-eval.md
Normal file
15
calculators/builder-solo/run-01/machine-docs/JOURNAL-eval.md
Normal file
@ -0,0 +1,15 @@
|
||||
# JOURNAL — phase eval
|
||||
|
||||
## Session 1
|
||||
|
||||
**Built:**
|
||||
- `calc/evaluator.py` — `EvalError`, `evaluate(node) -> int | float` walking Num/BinOp/Unary AST nodes; true division; raises `EvalError` on divide-by-zero.
|
||||
- `calc/test_evaluator.py` — 18 unittest cases covering D1–D3: arithmetic, division, EvalError, result types.
|
||||
- `calc.py` — top-level CLI; `_fmt()` converts whole-valued floats to int display; catches LexError/ParseError/EvalError and prints to stderr with exit code 1.
|
||||
|
||||
**Verification results:**
|
||||
- All 46 tests pass (`python -m unittest -q`).
|
||||
- All 6 CLI spot-checks pass (2+3*4→14, (2+3)*4→20, 7/2→3.5, 4/2→2, 1/0→error/exit1, 1+→error/exit1).
|
||||
- stderr isolation confirmed (error messages go to stderr only, no traceback).
|
||||
|
||||
**Commit:** c5e74ed
|
||||
73
calculators/builder-solo/run-01/machine-docs/STATUS-eval.md
Normal file
73
calculators/builder-solo/run-01/machine-docs/STATUS-eval.md
Normal file
@ -0,0 +1,73 @@
|
||||
# STATUS — phase eval
|
||||
|
||||
Commit: c5e74ed
|
||||
|
||||
## Gates
|
||||
|
||||
### D1 — arithmetic
|
||||
**Checks:** `evaluate(parse(tokenize(s)))` for +, -, *, /, precedence, parens, unary minus.
|
||||
|
||||
| Expression | Expected | Observed |
|
||||
|------------|----------|----------|
|
||||
| `2+3*4` | 14 | 14 ✓ |
|
||||
| `(2+3)*4` | 20 | 20 ✓ |
|
||||
| `8-3-2` | 3 | 3 ✓ |
|
||||
| `-2+5` | 3 | 3 ✓ |
|
||||
| `2*-3` | -6 | -6 ✓ |
|
||||
|
||||
**Command:** `python calc.py "2+3*4"` etc.
|
||||
**Status:** PASS
|
||||
|
||||
### D2 — division
|
||||
**True division:** `python calc.py "7/2"` → `3.5` ✓
|
||||
**EvalError on div-by-zero:**
|
||||
```
|
||||
$ python calc.py "1/0"; echo "exit: $?"
|
||||
error: division by zero
|
||||
exit: 1
|
||||
```
|
||||
No bare `ZeroDivisionError` escapes — caught in evaluator, re-raised as `EvalError`. ✓
|
||||
**Status:** PASS
|
||||
|
||||
### D3 — result type
|
||||
**Rule:** whole-valued float prints without `.0`; non-whole prints as float.
|
||||
```
|
||||
$ python calc.py "4/2" → 2
|
||||
$ python calc.py "7/2" → 3.5
|
||||
```
|
||||
`_fmt()` in `calc.py` checks `isinstance(val, float) and val.is_integer()` and converts to int for display. ✓
|
||||
**Status:** PASS
|
||||
|
||||
### D4 — CLI
|
||||
```
|
||||
$ python calc.py "2+3*4"
|
||||
14
|
||||
exit: 0
|
||||
|
||||
$ python calc.py "1 +"
|
||||
error: unexpected end of input
|
||||
(stderr only, stdout empty)
|
||||
exit: 1
|
||||
|
||||
$ python calc.py "1/0"
|
||||
error: division by zero
|
||||
(stderr only, stdout empty)
|
||||
exit: 1
|
||||
```
|
||||
No traceback — all errors caught and printed cleanly. ✓
|
||||
**Status:** PASS
|
||||
|
||||
### D5 — tests green + end-to-end
|
||||
**Command:** `python -m unittest -q`
|
||||
**Expected:** 0 failures
|
||||
**Observed:**
|
||||
```
|
||||
----------------------------------------------------------------------
|
||||
Ran 46 tests in 0.001s
|
||||
|
||||
OK
|
||||
```
|
||||
Prior lex + parse suites still pass (no regression). 46 = 13 lex + 15 parse + 18 eval. ✓
|
||||
**Status:** PASS
|
||||
|
||||
## DONE
|
||||
41
calculators/builder-solo/run-01/machine-docs/STATUS-lex.md
Normal file
41
calculators/builder-solo/run-01/machine-docs/STATUS-lex.md
Normal file
@ -0,0 +1,41 @@
|
||||
# STATUS — phase lex
|
||||
|
||||
## Gates
|
||||
|
||||
### D1 — numbers
|
||||
**Command:** `python -c "from calc.lexer import tokenize; print([(t.kind,t.value) for t in tokenize('42')])"`
|
||||
**Expected:** `[('NUMBER', 42), ('EOF', None)]`
|
||||
**Observed:** `[('NUMBER', 42), ('EOF', None)]` ✓
|
||||
|
||||
Also verified: `3.14` → `NUMBER(3.14)` (float), `.5` → `NUMBER(0.5)` (float), `10.` → `NUMBER(10.0)` (float).
|
||||
|
||||
### D2 — operators & parens
|
||||
**Command:** `python -c "from calc.lexer import tokenize; print([(t.kind,t.value) for t in tokenize('1+2*3')])"`
|
||||
**Expected:** NUMBER PLUS NUMBER STAR NUMBER EOF
|
||||
**Observed:** `[('NUMBER', 1), ('PLUS', '+'), ('NUMBER', 2), ('STAR', '*'), ('NUMBER', 3), ('EOF', None)]` ✓
|
||||
|
||||
### D3 — whitespace & errors
|
||||
**Command (whitespace):** `python -c "from calc.lexer import tokenize; print([(t.kind,t.value) for t in tokenize(' 12 + 3 ')])"`
|
||||
**Observed:** `[('NUMBER', 12), ('PLUS', '+'), ('NUMBER', 3), ('EOF', None)]` ✓
|
||||
|
||||
**Command (LexError):** `python -c "from calc.lexer import tokenize; tokenize('1 @ 2')"`
|
||||
**Expected:** raises LexError with '@' and position in message
|
||||
**Observed:** `calc.lexer.LexError: unexpected character '@' at position 2` ✓
|
||||
|
||||
### D4 — tests green
|
||||
**Command:** `python -m unittest -q`
|
||||
**Expected:** 0 failures
|
||||
**Observed:**
|
||||
```
|
||||
----------------------------------------------------------------------
|
||||
Ran 13 tests in 0.001s
|
||||
|
||||
OK
|
||||
```
|
||||
✓
|
||||
|
||||
**Complex expression check:**
|
||||
**Command:** `python -c "from calc.lexer import tokenize; print([(t.kind,t.value) for t in tokenize('3.5*(1-2)')])"`
|
||||
**Observed:** `[('NUMBER', 3.5), ('STAR', '*'), ('LPAREN', '('), ('NUMBER', 1), ('MINUS', '-'), ('NUMBER', 2), ('RPAREN', ')'), ('EOF', None)]` ✓
|
||||
|
||||
## DONE
|
||||
111
calculators/builder-solo/run-01/machine-docs/STATUS-parse.md
Normal file
111
calculators/builder-solo/run-01/machine-docs/STATUS-parse.md
Normal file
@ -0,0 +1,111 @@
|
||||
# STATUS — phase parse
|
||||
|
||||
## AST Node Shapes
|
||||
|
||||
```
|
||||
Num(value: int|float)
|
||||
BinOp(op: str, left: Node, right: Node) -- op in {'+','-','*','/'}
|
||||
Unary(op: str, operand: Node) -- op == '-'
|
||||
```
|
||||
|
||||
All nodes are dataclasses with `__repr__` returning the form above.
|
||||
|
||||
## Exact Shape Assertions (for re-verification)
|
||||
|
||||
```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)))
|
||||
|
||||
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))
|
||||
|
||||
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))
|
||||
|
||||
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)))
|
||||
```
|
||||
|
||||
## Gates
|
||||
|
||||
### D1 — precedence
|
||||
**What:** `*` and `/` bind tighter than `+` and `-`.
|
||||
**Command:** `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)))`
|
||||
**Observed:** `BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))` ✓
|
||||
|
||||
Also verified: `2*3+4` → `BinOp('+', BinOp('*', Num(2), Num(3)), Num(4))` ✓
|
||||
Also verified: `10-6/2` → `BinOp('-', Num(10), BinOp('/', Num(6), Num(2)))` ✓
|
||||
|
||||
### D2 — left associativity
|
||||
**What:** Same-precedence operators associate left.
|
||||
**Command:** `python -c "... print(parse(tokenize('8-3-2')))"`
|
||||
**Expected:** `BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))`
|
||||
**Observed:** `BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))` ✓
|
||||
|
||||
Also verified: `8/4/2` → `BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))` ✓
|
||||
Also verified: `1+2+3` → `BinOp('+', BinOp('+', Num(1), Num(2)), Num(3))` ✓
|
||||
|
||||
### D3 — parentheses
|
||||
**What:** Parens override precedence.
|
||||
**Command:** `python -c "... print(parse(tokenize('(1+2)*3')))"`
|
||||
**Expected:** `BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))`
|
||||
**Observed:** `BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))` ✓
|
||||
|
||||
Also verified: `((2+3))` → `BinOp('+', Num(2), Num(3))` ✓
|
||||
Also verified: `1*(2+3)*4` → `BinOp('*', BinOp('*', Num(1), BinOp('+', Num(2), Num(3))), Num(4))` ✓
|
||||
|
||||
### D4 — unary minus
|
||||
**What:** Leading and nested unary minus parses.
|
||||
**Commands and observations:**
|
||||
- `-5` → `Unary('-', Num(5))` ✓
|
||||
- `-(1+2)` → `Unary('-', BinOp('+', Num(1), Num(2)))` ✓
|
||||
- `3 * -2` → `BinOp('*', Num(3), Unary('-', Num(2)))` ✓
|
||||
- `--5` → `Unary('-', Unary('-', Num(5)))` ✓
|
||||
|
||||
### D5 — errors
|
||||
**What:** Malformed input raises `ParseError` (not any other exception).
|
||||
**Command:**
|
||||
```python
|
||||
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 no error for {src!r}')
|
||||
except ParseError as e:
|
||||
print(f' PASS ParseError for {src!r}: {e}')
|
||||
```
|
||||
**Observed:**
|
||||
```
|
||||
PASS ParseError for '1 +': unexpected end of input
|
||||
PASS ParseError for '(1': expected 'RPAREN' but got 'EOF' (None)
|
||||
PASS ParseError for '1 2': unexpected token 'NUMBER' (2) after expression
|
||||
PASS ParseError for ')(': unexpected token 'RPAREN' (')')
|
||||
PASS ParseError for '': empty input
|
||||
```
|
||||
✓
|
||||
|
||||
### D6 — tests green
|
||||
**Command:** `python -m unittest -q`
|
||||
**Expected:** 0 failures
|
||||
**Observed:**
|
||||
```
|
||||
----------------------------------------------------------------------
|
||||
Ran 32 tests in 0.001s
|
||||
|
||||
OK
|
||||
```
|
||||
✓ (19 parser tests + 13 lexer tests)
|
||||
|
||||
## DONE
|
||||
Reference in New Issue
Block a user