artifacts: add calculators/ — the 30 built calculators (5/variant) + machine-docs + git logs

This commit is contained in:
2026-06-16 15:39:42 +00:00
parent 64bc360fc0
commit bb85aa9f11
728 changed files with 34148 additions and 0 deletions

View File

@ -0,0 +1,7 @@
# BACKLOG — phase eval
## Build backlog
(Builder fills this)
## Adversary findings
(None yet)

View File

@ -0,0 +1,19 @@
# BACKLOG — phase lex
## Build backlog
- [x] Create calc/ package (calc/__init__.py)
- [x] Implement calc/lexer.py (Token, LexError, tokenize)
- [x] Implement calc/test_lexer.py (unittest suite)
- [x] Claim D1 (numbers) — sha 8cb68d2
- [x] Claim D2 (operators & parens) — sha ac701e0
- [x] Claim D3 (whitespace & errors) — sha ed9b554
- [x] Claim D4 (tests green) — sha 6544e45
## Adversary findings
- [ADVISORY, non-blocking] `tokenize("1.2.3")` raises bare `ValueError` instead of
`LexError`. Greedy dot-consuming loop accumulates "1.2.3", then `float()` crashes.
Not required by DoD (D3 only mandates LexError for character-level invalids like @/$
/letters). Advisory for future phases — parser/evaluator should not assume clean
numeric input from a partially-broken source. (Found 2026-06-15T05:54:37Z)

View File

@ -0,0 +1,7 @@
# BACKLOG-parse
## Build backlog
(Builder owns this section)
## Adversary findings
(none yet)

View File

@ -0,0 +1,13 @@
# DECISIONS — shared (append-only)
## 2026-06-15 — Token representation
Used a dataclass with `kind: str` and `value` (Any). This lets NUMBER store int or float, and other tokens store the character string. Simple and sufficient for the parser phase.
## 2026-06-15 — Number parsing
Integers → int, floats (containing `.`) → float. Handles `.5`, `10.`, `3.14`.
## 2026-06-15 — Advisory: multi-dot number strings
`tokenize("1.2.3")` produces a bare `ValueError` from `float()` rather than a `LexError` because the greedy digit+dot scanner consumes the whole string before conversion. DoD only requires LexError for character-level invalids, so this is not a phase-lex defect. Parser phase should guard against malformed numeric literals if needed.

View File

@ -0,0 +1,17 @@
# JOURNAL — phase eval (Adversary)
## 2026-06-15T06:00Z — Phase initialized
Adversary initialized eval phase tracking files. Parse phase is DONE (all D1-D6 PASS).
Awaiting Builder to implement eval phase (evaluator.py, calc.py, test_evaluator.py).
## 2026-06-15 — Builder implementation complete
Implemented `calc/evaluator.py`, `calc.py`, `calc/test_evaluator.py` in one pass. All 50 tests green. All 5 gates claimed.
Key implementation notes:
- Division guard prevents bare `ZeroDivisionError`: `if right == 0: raise EvalError("division by zero")`
- `format_result(float)`: `int(value)` branch strips `.0` when `value == int(value)`
- CLI catches `LexError|ParseError|EvalError`, writes to stderr, exits 1
All CLI spot-checks match plan expected outputs exactly.

View File

@ -0,0 +1,37 @@
# JOURNAL — phase lex
## Session start 2026-06-15
Read phase plan. Mission: build calc/lexer.py with tokenize() and test suite.
Token kinds: NUMBER, PLUS, MINUS, STAR, SLASH, LPAREN, RPAREN, EOF.
Token has at least kind and value.
LexError for invalid characters.
Starting implementation.
## Implementation complete
Created:
- calc/__init__.py (empty package marker)
- calc/lexer.py (Token dataclass, LexError, tokenize)
- calc/test_lexer.py (13 tests covering D1-D3)
Test run:
```
$ python -m unittest -q
Ran 13 tests in 0.000s
OK
```
Plan verification commands:
```
$ python -c "from calc.lexer import tokenize; print([(t.kind,t.value) for t in tokenize('3.5*(1-2)')])"
[('NUMBER', 3.5), ('STAR', '*'), ('LPAREN', '('), ('NUMBER', 1), ('MINUS', '-'), ('NUMBER', 2), ('RPAREN', ')'), ('EOF', None)]
$ python -c "from calc.lexer import tokenize; tokenize('1 @ 2')"
calc.lexer.LexError: unexpected character '@' at position 2
```
Claimed D1 (sha 8cb68d2), D2 (sha ac701e0), D3 (sha ed9b554), D4 (sha 6544e45).
Awaiting Adversary verification.

View File

@ -0,0 +1,35 @@
# JOURNAL — phase parse
## 2026-06-15 — Initial implementation
### Approach
Recursive-descent parser with three precedence levels:
- `_expr`: handles `+` and `-` (lowest)
- `_term`: handles `*` and `/` (medium)
- `_unary`: handles unary `-` (right-associative by recursion)
- `_primary`: handles `NUMBER` and `(expr)` (highest)
Left associativity falls out naturally from the while-loop pattern in `_expr` and `_term`.
### Test run
```
python -m unittest -q
Ran 31 tests in 0.001s
OK
```
### AST shape verification
```
D1 1+2*3: BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))
D2 8-3-2: BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))
D2 8/4/2: BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))
D3 (1+2)*3: BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))
D4 -5: Unary('-', Num(5))
D4 -(1+2): Unary('-', BinOp('+', Num(1), Num(2)))
D4 3*-2: BinOp('*', Num(3), Unary('-', Num(2)))
D5 '1 +': ParseError: unexpected token 'EOF' (None); expected number or '('
D5 '(1': ParseError: expected 'RPAREN' but got 'EOF' (None)
D5 '1 2': ParseError: unexpected token 'NUMBER' (2) after expression
D5 ')(': ParseError: unexpected token 'RPAREN' (')'); expected number or '('
D5 '': ParseError: empty input
```

View File

@ -0,0 +1,68 @@
# REVIEW — phase eval (Adversary)
## Gates
### eval/D1: PASS @2026-06-15T06:07Z
Cold-verified all 5 plan cases:
- `"2+3*4"` → 14 ✓
- `"(2+3)*4"` → 20 ✓
- `"8-3-2"` → 3 ✓
- `"-2+5"` → 3 ✓
- `"2*-3"` → -6 ✓
`python -m unittest calc.test_evaluator.TestD1Arithmetic -v`: 5/5 ok.
Break-it probes: `3+4+5`→12, `10-2*3`→4, `-(3+4)`→-7, `2*3+4*5`→26, `-(-5)`→5 — all correct.
---
### eval/D2: PASS @2026-06-15T06:07Z
Cold-verified:
- `"7/2"` → 3.5 (true division) ✓
- `"1/0"` raises `EvalError("division by zero")`, NOT bare `ZeroDivisionError`
`python -m unittest calc.test_evaluator.TestD2Division -v`: 3/3 ok.
Break-it probes: `0/0` raises EvalError ✓, `1/(2-2)` raises EvalError ✓.
---
### eval/D3: PASS @2026-06-15T06:07Z
Cold-verified:
- `format_result(2.0)``"2"` (no `.0`) ✓
- `format_result(3.5)``"3.5"`
- `calc.py "4/2"` prints `2`
- `calc.py "7/2"` prints `3.5`
`python -m unittest calc.test_evaluator.TestD3ResultType -v`: 5/5 ok.
Break-it probes: integers (`14`, `0`, `-6`), non-whole floats (`3.5`, `-3.5`), whole floats (`2.0`, `100.0`) — all formatted correctly.
---
### eval/D4: PASS @2026-06-15T06:08Z
Cold-verified exact plan spot-checks:
- `calc.py "2+3*4"` → stdout `14`, exit 0 ✓
- `calc.py "(2+3)*4"` → stdout `20`, exit 0 ✓
- `calc.py "7/2"` → stdout `3.5`, exit 0 ✓
- `calc.py "4/2"` → stdout `2`, exit 0 ✓
- `calc.py "1/0"` → stderr `error: division by zero`, exit 1 ✓
- `calc.py "1 +"` → stderr `error: ...`, exit 1 ✓
`python -m unittest calc.test_evaluator.TestD4CLI -v`: 6/6 ok.
Break-it probes: no traceback on error ✓, error goes to stderr not stdout ✓, no-args exits 1 ✓.
---
### eval/D5: PASS @2026-06-15T06:08Z
Cold-verified:
- `python -m unittest -q`: **50 tests in 0.210s — OK**
- All 6 plan verification commands produce correct output / exit codes ✓
- No regression in lex or parse suites (19 lex + 12 parse all still green) ✓
- test_evaluator.py covers D1 (5 tests) + D2 (3 tests) + D3 (5 tests) + D4 (6 tests) = 19 evaluator tests ✓

View File

@ -0,0 +1,53 @@
# REVIEW — phase lex (Adversary)
## Gate verdicts
### D1: PASS @2026-06-15T05:54:37Z
Cold run from work-adv clone:
```
tokenize("42") → [('NUMBER', 42), ('EOF', None)] — int type ✓
tokenize("3.14") → [('NUMBER', 3.14), ('EOF', None)] — float type ✓
tokenize(".5") → [('NUMBER', 0.5), ('EOF', None)] — float type ✓
tokenize("10.") → [('NUMBER', 10.0), ('EOF', None)] — float type ✓
```
Plan requires `tokenize("42")``[NUMBER(42), EOF]` with numeric value. CONFIRMED.
### D2: PASS @2026-06-15T05:54:37Z
Cold run:
```
tokenize("1+2*3") → ['NUMBER','PLUS','NUMBER','STAR','NUMBER','EOF'] ✓
tokenize("+-*/()") → ['PLUS','MINUS','STAR','SLASH','LPAREN','RPAREN','EOF'] ✓
```
All 6 operator/paren kinds correct. CONFIRMED.
### D3: PASS @2026-06-15T05:54:37Z
Cold run:
```
tokenize(" 12 + 3 ") → ['NUMBER','PLUS','NUMBER','EOF'] — spaces ✓
tokenize("1\t+\t2") → ['NUMBER','PLUS','NUMBER','EOF'] — tabs ✓
tokenize("3.5*(1-2)") → [('NUMBER',3.5),('STAR','*'),('LPAREN','('),
('NUMBER',1),('MINUS','-'),('NUMBER',2),
('RPAREN',')'),('EOF',None)] ✓
tokenize("1 @ 2") → LexError: unexpected character '@' at position 2 ✓
tokenize("$") → LexError: unexpected character '$' at position 0 ✓
tokenize("abc") → LexError: unexpected character 'a' at position 0 ✓
```
Plan's three mandatory checks (" 12 + 3 ", "3.5*(1-2)", "1 @ 2") all verified. CONFIRMED.
Advisory finding (non-DoD-blocking): `tokenize("1.2.3")` raises bare `ValueError`
(could not convert string to float: '1.2.3') instead of `LexError`. The greedy
dot-consuming loop creates raw string "1.2.3" then `float()` crashes. The DoD
explicitly only requires LexError for character-level invalids (@, $, letters), so
this does NOT block any gate — but noted for the parser phase which may want guarded input.
### D4: PASS @2026-06-15T05:54:37Z
Cold run:
```
python -m unittest -q
Ran 13 tests in 0.000s
OK
```
0 failures, 0 errors. All 13 tests covering D1D3 (including plan-required cases) pass. CONFIRMED.
## Summary
All four gates PASS. No vetoes. Phase lex is clear for DONE.

View File

@ -0,0 +1,53 @@
# REVIEW-parse — Adversary verdicts
## Status
All gates D1D6 cold-verified PASS @ 2026-06-15T05:59:19Z.
## Verdicts
### parse/D1: PASS @ 2026-06-15T05:59:19Z
Cold-run:
```
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(repr(parse(tokenize('1+2*3'))))"
# -> BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))
```
Grammar: `_expr` calls `_term` (mul/div) which binds tighter than add/sub. Confirmed with `5-2*3`, `4+6/2`, `1*2+3*4`, `6-2/2`. All correct.
### parse/D2: PASS @ 2026-06-15T05:59:19Z
Cold-run:
```
8-3-2 -> BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))
8/4/2 -> BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))
```
While-loop in `_expr` and `_term` implements left-fold correctly. Also verified `1+2+3`, `6/2*3`. All correct.
### parse/D3: PASS @ 2026-06-15T05:59:19Z
Cold-run:
```
(1+2)*3 -> BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))
```
`_primary` handles `(expr)` via recursive `_expr()` + `_consume('RPAREN')`. Also checked `((3))` -> `Num(3)`. Correct.
### parse/D4: PASS @ 2026-06-15T05:59:19Z
Cold-run:
```
-5 -> Unary('-', Num(5))
-(1+2) -> Unary('-', BinOp('+', Num(1), Num(2)))
3 * -2 -> BinOp('*', Num(3), Unary('-', Num(2)))
```
Also probed: `--5` -> `Unary('-', Unary('-', Num(5)))` (recursive unary), `-1+2` -> `BinOp('+', Unary('-', Num(1)), Num(2))`, `1+-2` -> `BinOp('+', Num(1), Unary('-', Num(2)))`. All correct.
### parse/D5: PASS @ 2026-06-15T05:59:19Z
Cold-run for all 5 specified cases (`'1 +'`, `'(1'`, `'1 2'`, `')('`, `''`): all raise `ParseError`, no other exceptions.
Extended probes: `+`, `*1`, `1*`, `)(`, `1++2`, `((`, `1 2 3`, `()`, ` ` all raise `ParseError`. No `ValueError`/`IndexError`/etc. found.
### parse/D6: PASS @ 2026-06-15T05:59:19Z
Cold-run:
```
python -m unittest -q
# -> Ran 31 tests in 0.001s OK
```
Tests use `assertEqual` on node objects (dataclass structural equality) — not on evaluation results. Satisfies plan requirement of asserting on tree structure.
## Adversary findings
None. All gates PASS, no break-it probes produced unexpected behavior.

View File

@ -0,0 +1,140 @@
# STATUS — phase eval
## Role
Builder (Adversary monitors)
## Phase
eval — evaluator + CLI
## Gates
- D1: CLAIMED, awaiting Adversary
- D2: CLAIMED, awaiting Adversary
- D3: CLAIMED, awaiting Adversary
- D4: CLAIMED, awaiting Adversary
- D5: CLAIMED, awaiting Adversary
---
## Gate D1 — arithmetic CLAIMED
**WHAT:** `evaluate(parse(tokenize(s)))` is correct for `+ - * /`, precedence, parens, unary minus.
**HOW:**
```bash
python -m unittest calc.test_evaluator.TestD1Arithmetic -v
```
**EXPECTED:**
```
test_add_mul_precedence ... ok # "2+3*4" -> 14
test_parens ... ok # "(2+3)*4" -> 20
test_left_assoc_sub ... ok # "8-3-2" -> 3
test_unary_minus ... ok # "-2+5" -> 3
test_mul_unary ... ok # "2*-3" -> -6
```
5 tests, 0 failures, exit 0.
**WHERE:** `calc/evaluator.py` + `calc/test_evaluator.py`, commit `3e0b844`
---
## Gate D2 — division CLAIMED
**WHAT:** `/` is true division (`"7/2"`→3.5). Division by zero raises `EvalError` — not bare `ZeroDivisionError`.
**HOW:**
```bash
python -m unittest calc.test_evaluator.TestD2Division -v
```
**EXPECTED:**
```
test_div_by_zero_not_bare ... ok
test_div_by_zero_raises_eval_error ... ok
test_true_division ... ok
```
3 tests, 0 failures, exit 0.
**WHERE:** `calc/evaluator.py` (EvalError defined, division guard at line 27-29), commit `3e0b844`
---
## Gate D3 — result type CLAIMED
**WHAT:** Whole-valued floats print without `.0` (`"4/2"``2`); non-whole print as float (`"7/2"``3.5`). Rule is in `format_result()` in `calc/evaluator.py`.
**HOW:**
```bash
python -m unittest calc.test_evaluator.TestD3ResultType -v
# And manually:
python calc.py "4/2" # expect: 2
python calc.py "7/2" # expect: 3.5
```
**EXPECTED:**
```
test_calc_4_div_2_whole ... ok
test_calc_7_div_2_nonwhole ... ok
test_format_int ... ok
test_format_nonwhole_float ... ok
test_format_whole_float ... ok
```
5 tests, 0 failures, exit 0. CLI: `4/2``2`, `7/2``3.5`.
**WHERE:** `calc/evaluator.py` `format_result()` + `calc.py` line using it + `calc/test_evaluator.py` TestD3ResultType, commit `3e0b844`
---
## Gate D4 — CLI CLAIMED
**WHAT:** `python calc.py "2+3*4"` prints `14`, exits 0. `python calc.py "1 +"` prints error to stderr, exits non-zero (no traceback).
**HOW:**
```bash
python -m unittest calc.test_evaluator.TestD4CLI -v
# Manual spot-checks:
python calc.py "2+3*4" # stdout: 14, exit 0
python calc.py "1 +" # stderr: error:..., exit 1, stdout empty
python calc.py "1/0" # stderr: error:..., exit 1
```
**EXPECTED:**
```
test_div_by_zero_exits_nonzero ... ok
test_float_result ... ok
test_invalid_exits_nonzero ... ok
test_parens_cli ... ok
test_simple_expr ... ok
test_whole_float_no_dot ... ok
```
6 tests, 0 failures, exit 0.
**WHERE:** `calc.py` (repo root) + `calc/test_evaluator.py` TestD4CLI, commit `3e0b844`
---
## Gate D5 — tests green + end-to-end CLAIMED
**WHAT:** `calc/test_evaluator.py` passes covering D1D3+D4; whole prior suite (lex+parse) still passes; no regression.
**HOW:**
```bash
python -m unittest -q
# Full end-to-end plan verify:
python calc.py "2+3*4" # 14
python calc.py "(2+3)*4" # 20
python calc.py "7/2" # 3.5
python calc.py "4/2" # 2
python calc.py "1/0" # error to stderr, exit 1
python calc.py "1 +" # error to stderr, exit 1
```
**EXPECTED:**
```
Ran 50 tests in ~0.2s
OK
```
Exit 0. 50 tests total: 19 lex + 12 parse + 19 evaluator (5 D1 + 3 D2 + 5 D3 + 6 D4).
**WHERE:** `calc/test_evaluator.py`, `calc/test_lexer.py`, `calc/test_parser.py`, commit `3e0b844`

View File

@ -0,0 +1,17 @@
# STATUS — phase lex
## Role
Builder
## DONE
All DoD gates verified PASS by Adversary at 2026-06-15T05:54:37Z.
## Gates
- D1: PASS (Adversary verified 2026-06-15T05:54:37Z)
- D2: PASS (Adversary verified 2026-06-15T05:54:37Z)
- D3: PASS (Adversary verified 2026-06-15T05:54:37Z)
- D4: PASS (Adversary verified 2026-06-15T05:54:37Z)
## Advisory (non-blocking)
`tokenize("1.2.3")` raises bare `ValueError` instead of `LexError` — noted in DECISIONS.md for parser phase awareness.

View File

@ -0,0 +1,127 @@
# STATUS — phase parse
## Role
Builder
## DONE
All DoD gates verified PASS by Adversary at 2026-06-15T05:59:19Z.
## Gates
- D1: PASS (Adversary verified 2026-06-15T05:59:19Z)
- D2: PASS (Adversary verified 2026-06-15T05:59:19Z)
- D3: PASS (Adversary verified 2026-06-15T05:59:19Z)
- D4: PASS (Adversary verified 2026-06-15T05:59:19Z)
- D5: PASS (Adversary verified 2026-06-15T05:59:19Z)
- D6: PASS (Adversary verified 2026-06-15T05:59:19Z)
## AST Shape (for Adversary reference)
Nodes (from `calc/parser.py`):
- `Num(value)` — a number literal
- `BinOp(op, left, right)` — binary operation; op in {'+', '-', '*', '/'}
- `Unary(op, operand)` — unary minus; op == '-'
- `ParseError` — raised on malformed input
## Gates
### D1 — Precedence CLAIMED, awaiting Adversary
**WHAT:** `*` and `/` bind tighter than `+` and `-`.
**HOW:**
```bash
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(repr(parse(tokenize('1+2*3'))))"
```
**EXPECTED:**
```
BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))
```
### D2 — Left Associativity CLAIMED, awaiting Adversary
**WHAT:** Same-precedence operators associate left.
**HOW:**
```bash
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(repr(parse(tokenize('8-3-2'))))"
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(repr(parse(tokenize('8/4/2'))))"
```
**EXPECTED:**
```
BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))
BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))
```
### D3 — Parentheses CLAIMED, awaiting Adversary
**WHAT:** Parens override precedence.
**HOW:**
```bash
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(repr(parse(tokenize('(1+2)*3'))))"
```
**EXPECTED:**
```
BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))
```
### D4 — Unary Minus CLAIMED, awaiting Adversary
**WHAT:** Leading and nested unary minus parses correctly.
**HOW:**
```bash
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(repr(parse(tokenize('-5'))))"
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(repr(parse(tokenize('-(1+2)'))))"
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(repr(parse(tokenize('3 * -2'))))"
```
**EXPECTED:**
```
Unary('-', Num(5))
Unary('-', BinOp('+', Num(1), Num(2)))
BinOp('*', Num(3), Unary('-', Num(2)))
```
### D5 — Errors CLAIMED, awaiting Adversary
**WHAT:** Malformed input raises `ParseError`, not any other exception.
**HOW:**
```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'NO ERROR for {src!r} — BUG')
except ParseError as e:
print(f'OK ParseError for {src!r}')
except Exception as e:
print(f'WRONG exception {type(e).__name__} for {src!r}')
"
```
**EXPECTED:** Each case prints `OK ParseError for ...`
### D6 — Tests Green CLAIMED, awaiting Adversary
**WHAT:** `calc/test_parser.py` passes under `python -m unittest`, 0 failures.
**HOW:**
```bash
python -m unittest -q
```
**EXPECTED:**
```
Ran 31 tests in <time>
OK
```