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 — eval phase
## Build backlog
_(Builder's section — read-only for Adversary)_
## Adversary findings
_(none yet)_

View File

@ -0,0 +1,13 @@
# BACKLOG-lex
## Build backlog
- [x] Create calc/__init__.py
- [x] Create calc/lexer.py with Token, LexError, tokenize()
- [x] Create calc/test_lexer.py covering D1D4 (21 tests)
- [x] Run tests: 21 passed, 0 failed
- [ ] Claim D1 + D2 + D3 + D4 (all gates, single claim)
- [ ] Await Adversary verification
## Adversary findings
(none yet)

View File

@ -0,0 +1,20 @@
# BACKLOG — Phase parse
## Build backlog
- [x] Create calc/parser.py (ParseError, Num, BinOp, Unary, recursive-descent parse())
- [x] Create calc/test_parser.py (24 unittest tests, D1D5 coverage)
- [x] Verified 45 tests pass (21 lexer + 24 parser), 0 failures
- [x] CLAIM D1D6 (all gates claimed together)
## Adversary findings
(none yet)
## Break-it probes planned
- Precedence weak test: ensure `1+2*3` really builds `BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))` not `BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))`
- Associativity weak test: ensure `8-3-2` builds `BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))` not `BinOp('-', Num(8), BinOp('-', Num(3), Num(2)))`
- Nested unary: `--5`, `-(-(1+2))` should work
- ParseError specificity: check it's ParseError (not generic Exception) for all 5 error cases in D5
- Empty input edge case

View File

@ -0,0 +1,12 @@
# DECISIONS — shared append-only log
## 2026-06-15T00:12Z — Adversary initialized
Adversary started for phase lex. Watching for Builder claims via git.
## 2026-06-15 — Token representation (Builder)
Using a dataclass for Token with `kind: str` and `value`. The `kind` is a plain string constant
(e.g. "NUMBER"), not an enum, keeping pure stdlib with minimal boilerplate.
## 2026-06-15 — NUMBER value type (Builder)
`value` for NUMBER tokens is `int` if no decimal point, else `float`. Matches Python's natural
numeric types; convenient for the evaluator phase.

View File

@ -0,0 +1,43 @@
# JOURNAL — eval phase
## 2026-06-15 — Implementation
Built evaluator, CLI, and tests in one go.
### evaluator.py
`evaluate(node)` walks the AST recursively:
- `Num` → return `node.value` directly (already int or float from lexer)
- `Unary('-', operand)` → negate result
- `BinOp(op, left, right)` → evaluate both sides, apply op
Division: uses Python `left / right` (always returns float). If `right == 0`, raises `EvalError("Division by zero")`.
D3 result type rule: after computing a float result in BinOp, if `result == int(result)` we cast to int. This ensures `4/2 → 2` (int) and `7/2 → 3.5` (float). Integer arithmetic returns int naturally.
### calc.py CLI
`main()` accepts exactly one argv argument (the expression string).
Catches `LexError`, `ParseError`, `EvalError` → prints `Error: <msg>` to stderr, exits 1.
On success prints `result` (which is already int or float with correct type per D3 rule).
### Test run output
```
$ python -m unittest -q
Ran 62 tests in 0.001s
OK
```
(45 prior tests from lex+parse phases, 17 new evaluator tests)
### CLI checks
```
$ 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: Division by zero (exit 1)
$ python calc.py "1 +" → Error: Unexpected end of input (exit 1)
```

View File

@ -0,0 +1,48 @@
# JOURNAL-lex — Adversary
## 2026-06-15T00:12Z — Cold start
Read phase plan lex.md. Builder has not pushed any code yet (only seed commit 13c0db5).
Initialized REVIEW, BACKLOG, JOURNAL files. Waiting for Builder to push work.
DoD gates to verify:
- D1: numbers (integers and floats)
- D2: operators & parens
- D3: whitespace & errors (LexError)
- D4: tests green (python -m unittest -q)
## 2026-06-15T00:20Z — Verification complete
Builder pushed claim(D1-D4). Pulled, ran cold verification, ran break-it probes.
All four gates PASS. No vetoes. Wrote verdicts to REVIEW-lex.md.
Adversary probes: empty string, lone dot, whitespace-only, double-dot, 1.2.3, position accuracy — all held.
---
# JOURNAL-lex — Builder
## 2026-06-15 — Implementation
Read phase plan. Built calc/lexer.py and calc/test_lexer.py from scratch.
### Implementation choices
- Token is a dataclass with `kind: str` and `value: int | float | str | None`
- NUMBER value is int (no dot) or float (dot present)
- LexError message includes repr of the offending char and its 0-based position
- Leading dot (`.5`) and trailing dot (`10.`) are valid floats (scanned by loop: stops when second dot seen)
- Single lone dot is an error (raw == '.')
### Test run output
```
python -m unittest -q
----------------------------------------------------------------------
Ran 21 tests in 0.000s
OK
```
### Verification commands output
```
$ 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
```

View File

@ -0,0 +1,44 @@
# JOURNAL — Phase parse (Adversary)
## 2026-06-15T00:18Z — Adversary initialized
- Phase parse kicked off. Lex phase confirmed ## DONE.
- Initialized REVIEW-parse.md, STATUS-parse.md, BACKLOG-parse.md, JOURNAL-parse.md.
- Watching for Builder to claim gates D1D6.
- Planned break-it probes logged in BACKLOG.
## 2026-06-15T00:22Z — Cold verification complete, all gates PASS
- Watchdog pinged: Builder claimed D1D6 in commit 7768832.
- Read parser.py and test_parser.py cold (no prior state).
- Ran `python -m unittest -q` → 45 tests, 0 failures.
- Ran all AST shape checks from STATUS-parse.md — every output matched expected.
- Ran full break-it probe suite: right-assoc trap, triple unary, deep nesting, float, extra error cases.
- All held. No defects found. PASS recorded in REVIEW-parse.md for D1D6.
---
# JOURNAL — Phase parse (Builder)
## 2026-06-15T00:19Z — Implementation complete
Design: recursive-descent with grammar:
```
expr := term (('+' | '-') term)*
term := unary (('*' | '/') unary)*
unary := '-' unary | primary
primary := NUMBER | '(' expr ')'
```
Left-associativity emerges from iterative loops (not recursion) in expr/term.
Ran `python -m unittest -q``Ran 45 tests in 0.001s OK`
Key outputs verified:
- `1+2*3``BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))`
- `8-3-2``BinOp('-', BinOp('-', Num(8), Num(3)), Num(2))`
- `8/4/2``BinOp('/', BinOp('/', Num(8), Num(4)), Num(2))`
- `(1+2)*3``BinOp('*', BinOp('+', Num(1), Num(2)), Num(3))`
- `-5``Unary('-', Num(5))`
- `-(1+2)``Unary('-', BinOp('+', Num(1), Num(2)))`
- `3 * -2``BinOp('*', Num(3), Unary('-', Num(2)))`
- All D5 error cases → `ParseError`
Claiming all gates D1D6.

View File

@ -0,0 +1,96 @@
# REVIEW — eval phase (Adversary)
_Adversary cold-verification log. Each verdict is recorded here._
## Status summary
- D1 arithmetic: PASS
- D2 division + EvalError: PASS
- D3 result type: PASS
- D4 CLI: PASS
- D5 tests green + end-to-end: PASS
## Verdicts
### D1 — arithmetic: PASS @2026-06-15T00:27:02Z
Cold run, commit 165c7cc.
Commands run and results:
```
python calc.py "2+3*4" → 14 ✓
python calc.py "(2+3)*4" → 20 ✓
python calc.py "8-3-2" → 3 ✓
python calc.py "-2+5" → 3 ✓
python calc.py "2*-3" → -6 ✓
python calc.py "--5" → 5 ✓ (double unary)
python calc.py "10-3-2" → 5 ✓ (left-associativity)
python calc.py "-(2+3)" → -5 ✓ (unary on paren)
```
### D2 — division + EvalError: PASS @2026-06-15T00:27:02Z
```
python calc.py "7/2" → 3.5 ✓ (true division)
python calc.py "1/0" → "Error: Division by zero" on stderr, exit 1 ✓
python calc.py "5/(3-3)" → "Error: Division by zero" on stderr, exit 1 ✓
```
Verified `EvalError` is raised (not bare `ZeroDivisionError`) via:
- `calc/evaluator.py:37-38` explicitly checks `right == 0` and raises `EvalError`
- `calc.py:24` catches `EvalError` — ZeroDivisionError would escape if not caught; confirmed not raised
`EvalError` is a proper subclass of `Exception`: confirmed True.
### D3 — result type: PASS @2026-06-15T00:27:02Z
```
evaluate(parse(tokenize("4/2"))) → int 2 ✓
evaluate(parse(tokenize("7/2"))) → float 3.5 ✓
evaluate(parse(tokenize("2+3"))) → int 5 ✓ (integer arithmetic stays int)
evaluate(parse(tokenize("-6/2"))) → int -3 ✓ (negative whole result is int)
evaluate(parse(tokenize("1000*1000"))) → int 1000000 ✓
```
Rule documented in `calc/evaluator.py` docstring. `print(4/2)` outputs `2` (not `2.0`). ✓
### D4 — CLI: PASS @2026-06-15T00:27:02Z
```
python calc.py "2+3*4" → prints 14, exit 0 ✓
python calc.py "1 +" → "Error: Unexpected end of input" on stderr, exit 1, no traceback ✓
python calc.py "1/0" → "Error: Division by zero" on stderr, exit 1, no traceback ✓
python calc.py "" → "Error: Empty input" on stderr, exit 1 ✓
python calc.py → usage message on stderr, exit 1 ✓
python calc.py "((1+2)" → error on stderr, exit 1, no traceback ✓
```
No tracebacks in any error path. ✓
### D5 — tests green + end-to-end: PASS @2026-06-15T00:27:02Z
```
python -m unittest -q → Ran 62 tests in 0.001s OK
```
62 tests = lex + parse + evaluator suites. 0 failures, 0 errors. No regression. ✓
## Adversary findings
_(none — all gates pass, no defects found)_
## Break-it probes run
- Traceback check on all error paths: no traceback in any case ✓
- No-args invocation: graceful usage error ✓
- Empty string input: graceful error ✓
- Double unary minus `--5` → 5 ✓
- Left-associativity `10-3-2` → 5 ✓
- Unary in division `-8/2` → -4 ✓
- Negative whole result type `-6/2` → int -3 ✓
- Large numbers `1000*1000` → int 1000000 ✓
- Division by zero via expression `5/(3-3)` → EvalError ✓
- Unclosed paren `((1+2)` → parse error, no crash ✓
---
Initialized: 2026-06-15T00:24:45Z
Verdicts filed: 2026-06-15T00:27:02Z

View File

@ -0,0 +1,67 @@
# REVIEW-lex — Adversary verdicts
Phase: lex
Adversary cold-started: 2026-06-15
## Status
All gates verified PASS. No vetoes.
## Verdicts
### D1: PASS @2026-06-15T00:20Z
**Evidence (cold run from work-adv):**
```
tokenize("42") → NUMBER(42 int), EOF ✓
tokenize("3.14") → NUMBER(3.14 float) ✓
tokenize(".5") → NUMBER(0.5 float) ✓
tokenize("10.") → NUMBER(10.0 float) ✓
```
Plan spec: "tokenize('42') → [NUMBER(42), EOF]" — confirmed exactly.
### D2: PASS @2026-06-15T00:20Z
**Evidence:**
```
kinds("1+2*3") → ['NUMBER', 'PLUS', 'NUMBER', 'STAR', 'NUMBER', 'EOF'] ✓
all operators +-*/() → PLUS MINUS STAR SLASH LPAREN RPAREN ✓
tokens("3.5*(1-2)") → [('NUMBER', 3.5), ('STAR','*'), ('LPAREN','('), ('NUMBER', 1),
('MINUS','-'), ('NUMBER', 2), ('RPAREN',')'), ('EOF', None)] ✓
```
### D3: PASS @2026-06-15T00:20Z
**Evidence:**
```
tokenize(" 12 + 3 ") → ['NUMBER', 'PLUS', 'NUMBER', 'EOF'] ✓ (whitespace skipped)
tokenize("1 @ 2") → LexError: Unexpected character '@' at position 2 ✓
tokenize("$") → LexError: ... '$' ... ✓
tokenize("x") → LexError: ... 'x' at position 0 ✓
tokenize(".") → LexError: ... '.' at position 0 ✓
LexError message includes offending char AND position ✓
```
### D4: PASS @2026-06-15T00:20Z
**Evidence:**
```
$ python -m unittest -q
----------------------------------------------------------------------
Ran 21 tests in 0.000s
OK
```
Exit code: 0. 21 tests, 0 failures.
Required cases covered: " 12 + 3 " ✓, "3.5*(1-2)" ✓, "1 @ 2" → LexError ✓.
## Adversarial probes (break-it attempts — all held)
- Empty string → `[EOF]` (correct)
- Whitespace-only → `[EOF]` (correct)
- Lone dot → `LexError: Unexpected character '.' at position 0` (correct)
- `..` → LexError on lone dot (correct)
- `1.2.3``NUMBER(1.2) NUMBER(0.3) EOF` (valid lexer behavior; parser rejects)
- `12 @ 5` → LexError at position 3 (position accuracy confirmed)
- All operators in sequence `+-*/()` → correct kinds
- Newline treated as whitespace (conservative, robust)
No defects found. Implementation is correct per the phase plan.

View File

@ -0,0 +1,91 @@
# REVIEW — Phase parse (Adversary)
## Status
All gates PASS. Ready to approve ## DONE.
## Gate verdicts
| Gate | Verdict | Timestamp | Evidence |
|------|---------|-----------|----------|
| D1 — precedence | PASS | 2026-06-15T00:22:39Z | See below |
| D2 — left associativity | PASS | 2026-06-15T00:22:39Z | See below |
| D3 — parentheses | PASS | 2026-06-15T00:22:39Z | See below |
| D4 — unary minus | PASS | 2026-06-15T00:22:39Z | See below |
| D5 — errors | PASS | 2026-06-15T00:22:39Z | See below |
| D6 — tests green | PASS | 2026-06-15T00:22:39Z | Ran 45 tests in 0.001s OK |
## Cold-verification evidence
### D6 — tests green
```
python -m unittest -q
Ran 45 tests in 0.001s
OK
```
21 lexer + 24 parser tests, 0 failures.
### D1 — precedence (cold AST shape check)
```
1+2*3 → BinOp('+', Num(1), BinOp('*', Num(2), Num(3))) ✓ (* binds tighter)
2*3+4 → BinOp('+', BinOp('*', Num(2), Num(3)), Num(4)) ✓
10-6/2 → BinOp('-', Num(10), BinOp('/', Num(6), Num(2))) ✓
```
Independently derived: `1+2*3` must have `+` at root with `*` in right child — confirmed.
### D2 — left associativity (cold AST shape check)
```
8-3-2 → BinOp('-', BinOp('-', Num(8), Num(3)), Num(2)) ✓ (left-assoc)
8/4/2 → BinOp('/', BinOp('/', Num(8), Num(4)), Num(2)) ✓
1+2+3 → BinOp('+', BinOp('+', Num(1), Num(2)), Num(3)) ✓
2*3*4 → BinOp('*', BinOp('*', Num(2), Num(3)), Num(4)) ✓
1-2-3 → BinOp('-', BinOp('-', Num(1), Num(2)), Num(3)) ✓ (break-it: not right-assoc)
```
Iterative while-loop in `_expr`/`_term` enforces left-assoc by construction.
### D3 — parentheses (cold AST shape check)
```
(1+2)*3 → BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)) ✓
3*(1+2) → BinOp('*', Num(3), BinOp('+', Num(1), Num(2))) ✓
((4)) → Num(4) ✓
8-(3-2) → BinOp('-', Num(8), BinOp('-', Num(3), Num(2))) ✓
((((1+2)))) → BinOp('+', Num(1), Num(2)) ✓ (deep nesting)
```
### D4 — unary minus (cold AST shape check)
```
-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))) ✓
1 + -2 → BinOp('+', Num(1), Unary('-', Num(2))) ✓
---5 → Unary('-', Unary('-', Unary('-', Num(5)))) ✓ (break-it: triple unary)
6 / -2 → BinOp('/', Num(6), Unary('-', Num(2))) ✓ (break-it: unary in denom)
```
`_unary` is right-recursive: `'-' _unary | _primary` — correct for unary.
### D5 — errors (cold: all five plan cases + extras)
```
'1 +' → OK ParseError
'(1' → OK ParseError
'1 2' → OK ParseError
')(' → OK ParseError
'' → OK ParseError
'*' → OK ParseError (break-it)
')' → OK ParseError (break-it)
'1+2)' → OK ParseError (break-it)
'((1+2)' → OK ParseError (break-it)
```
All raise `ParseError` specifically, not a generic exception.
## Break-it probes run
- Right-assoc trap (`1-2-3`, `8-3-2`, `8/4/2`): held — correctly left-assoc
- Deep nesting `((((1+2))))`: held
- Triple unary `---5`: held
- Unary in denominator `6/-2`: held
- Float literal `3.14 → Num(3.14)`: parsed fine
- Trailing `)` after valid expr `1+2)`: ParseError ✓
- Solo operator `*`: ParseError ✓
- Double-unclosed paren `((1+2)`: ParseError ✓
## Adversary findings
None. No defects found. All probes held.

View File

@ -0,0 +1,23 @@
# STATUS — Phase eval
## DONE
All DoD gates Adversary-verified PASS. Phase eval is complete. This is the final phase — the calculator is end-to-end: string → tokens → AST → number.
| Gate | Status | Verified |
|------|--------|----------|
| D1 — arithmetic | PASS | 2026-06-15T00:27:02Z |
| D2 — division + EvalError | PASS | 2026-06-15T00:27:02Z |
| D3 — result type | PASS | 2026-06-15T00:27:02Z |
| D4 — CLI | PASS | 2026-06-15T00:27:02Z |
| D5 — tests green + end-to-end | PASS | 2026-06-15T00:27:02Z |
Adversary ran 62 tests (exit 0), cold-verified all D1D5 gates, and ran full break-it probe suite
(double unary, left-assoc, negative whole result type, division by zero via expression, unclosed
paren, empty string, no-args) — all held. No defects found.
## Artifacts
- `calc/evaluator.py``EvalError`, `evaluate()`
- `calc/test_evaluator.py` — 17 unittest tests (D1D3 coverage, type assertions)
- `calc.py` — top-level CLI (D4 + end-to-end check)

View File

@ -0,0 +1,20 @@
# STATUS — Phase lex
## DONE
All DoD gates Adversary-verified PASS. Phase lex is complete.
| Gate | Status | Verified |
|------|--------|----------|
| D1 — numbers | PASS | 2026-06-15T00:20Z |
| D2 — operators & parens | PASS | 2026-06-15T00:20Z |
| D3 — whitespace & errors | PASS | 2026-06-15T00:20Z |
| D4 — tests green | PASS | 2026-06-15T00:20Z |
Adversary ran 21 tests (exit 0), cold-verified all token outputs, and ran break-it probes — all held.
## Artifacts
- `calc/lexer.py` — Token dataclass, LexError, tokenize()
- `calc/test_lexer.py` — 21 unittest tests
- `calc/__init__.py` — package marker

View File

@ -0,0 +1,22 @@
# STATUS — Phase parse
## DONE
All DoD gates Adversary-verified PASS. Phase parse is complete.
| Gate | Status | Verified |
|------|--------|----------|
| D1 — precedence | PASS | 2026-06-15T00:22:39Z |
| D2 — left associativity | PASS | 2026-06-15T00:22:39Z |
| D3 — parentheses | PASS | 2026-06-15T00:22:39Z |
| D4 — unary minus | PASS | 2026-06-15T00:22:39Z |
| D5 — errors | PASS | 2026-06-15T00:22:39Z |
| D6 — tests green | PASS | 2026-06-15T00:22:39Z |
Adversary ran 45 tests (exit 0), cold-verified all AST shapes, and ran full break-it probe suite
(right-assoc trap, triple unary, deep nesting, float, extra error cases) — all held. No defects found.
## Artifacts
- `calc/parser.py` — ParseError, Num, BinOp, Unary, parse()
- `calc/test_parser.py` — 24 unittest tests (D1D5 coverage, tree-structure assertions)