artifacts: add calculators/ — the 30 built calculators (5/variant) + machine-docs + git logs
This commit is contained in:
@ -0,0 +1,9 @@
|
||||
# BACKLOG-eval.md
|
||||
|
||||
## Build backlog
|
||||
|
||||
All items complete. Gates D1–D5 claimed.
|
||||
|
||||
## Adversary findings
|
||||
|
||||
(none yet)
|
||||
@ -0,0 +1,14 @@
|
||||
# BACKLOG-lex.md
|
||||
|
||||
## Build backlog
|
||||
|
||||
- [x] D1 — Numbers: integers and floats tokenize correctly
|
||||
- [x] D2 — Operators & parens: all six symbols tokenize correctly
|
||||
- [x] D3 — Whitespace skipped; invalid chars raise LexError
|
||||
- [x] D4 — 20 unit tests passing under python -m unittest
|
||||
|
||||
All items complete. Awaiting Adversary PASS verdicts.
|
||||
|
||||
## Adversary findings
|
||||
|
||||
_(Adversary writes here)_
|
||||
@ -0,0 +1,16 @@
|
||||
# BACKLOG-parse.md — Phase `parse`
|
||||
|
||||
## Build backlog
|
||||
|
||||
- [x] Write calc/parser.py (ParseError, Num, BinOp, Unary, parse())
|
||||
- [x] Write calc/test_parser.py (unittest, D1-D5 coverage)
|
||||
- [x] Claim D1 (precedence)
|
||||
- [x] Claim D2 (left associativity)
|
||||
- [x] Claim D3 (parentheses)
|
||||
- [x] Claim D4 (unary minus)
|
||||
- [x] Claim D5 (errors)
|
||||
- [x] Claim D6 (tests green)
|
||||
- [ ] Await Adversary verdict on D1–D6
|
||||
|
||||
## Adversary findings
|
||||
*(None yet — awaiting gate claims)*
|
||||
@ -0,0 +1,5 @@
|
||||
# DECISIONS.md — Shared (append-only)
|
||||
|
||||
## 2026-06-15T04:30:28Z — Adversary initialized
|
||||
|
||||
Adversary loop started for phase `lex`. No decisions yet.
|
||||
@ -0,0 +1,35 @@
|
||||
# JOURNAL-eval.md
|
||||
|
||||
## Session 1
|
||||
|
||||
### Implementation
|
||||
|
||||
Created three files:
|
||||
- `calc/evaluator.py` — `evaluate(node)` walks Num/BinOp/Unary nodes. Division uses Python `/` (true division). After division, normalizes whole-valued floats to int via `int(result)`.
|
||||
- `calc/test_evaluator.py` — 16 tests covering D1 arithmetic (all 5 DoD cases + extras), D2 division/EvalError, D3 type normalization.
|
||||
- `calc.py` — CLI: parses argv[1], calls tokenize→parse→evaluate, prints result; catches LexError/ParseError/EvalError, prints to stderr, exits 1.
|
||||
|
||||
### Verification run
|
||||
|
||||
```
|
||||
$ python -m unittest -q
|
||||
Ran 65 tests in 0.001s
|
||||
OK
|
||||
|
||||
$ 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" 2>&1; echo "exit: $?"
|
||||
error: division by zero
|
||||
exit: 1
|
||||
$ python calc.py "1 +" 2>&1; echo "exit: $?"
|
||||
error: unexpected end of input
|
||||
exit: 1
|
||||
```
|
||||
|
||||
All outputs match DoD specs. 65 tests (lex + parse + eval) pass, zero failures.
|
||||
@ -0,0 +1,41 @@
|
||||
# JOURNAL-lex.md — Adversary Journal
|
||||
|
||||
## 2026-06-15T04:30:28Z — Initialized
|
||||
|
||||
Adversary started. Phase plan read. No Builder work present yet.
|
||||
Watching for gate claims in machine-docs/STATUS-lex.md.
|
||||
|
||||
---
|
||||
|
||||
# JOURNAL-lex.md — Builder Entries
|
||||
|
||||
## 2026-06-15 — Implementation complete
|
||||
|
||||
Built `calc/lexer.py` and `calc/test_lexer.py` in a single session.
|
||||
|
||||
**Design choices:**
|
||||
- `Token` uses `__slots__` for memory efficiency; `__eq__` for test assertions.
|
||||
- Number parsing: scan ahead consuming digits and at most one `.`. A leading dot (`.5`) and trailing dot (`10.`) both work because the while loop checks `src[j] == "."` only when `not has_dot`.
|
||||
- Integer values stored as `int`, floats as `float` — allows parser to distinguish.
|
||||
- `LexError` extends `Exception` directly; message includes the character repr and its position.
|
||||
|
||||
**Test run:**
|
||||
```
|
||||
$ python -m unittest -q
|
||||
....................
|
||||
----------------------------------------------------------------------
|
||||
Ran 20 tests in 0.000s
|
||||
|
||||
OK
|
||||
```
|
||||
|
||||
**Verification commands run locally:**
|
||||
```
|
||||
$ 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')"
|
||||
Traceback:
|
||||
...
|
||||
calc.lexer.LexError: unexpected character '@' at position 2
|
||||
```
|
||||
@ -0,0 +1,32 @@
|
||||
# JOURNAL-parse.md
|
||||
|
||||
## 2026-06-15 — Build + verify
|
||||
|
||||
Lex phase DONE. Token kinds: NUMBER, PLUS, MINUS, STAR, SLASH, LPAREN, RPAREN, EOF.
|
||||
|
||||
Grammar chosen (iterative at each level → left associativity by construction):
|
||||
```
|
||||
expr → term (('+' | '-') term)*
|
||||
term → unary (('*' | '/') unary)*
|
||||
unary → '-' unary | primary
|
||||
primary → NUMBER | '(' expr ')'
|
||||
```
|
||||
|
||||
### Manual verification output
|
||||
|
||||
```
|
||||
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 end of input
|
||||
D5 '(1' -> ParseError: unclosed parenthesis
|
||||
D5 '1 2' -> ParseError: unexpected token 'NUMBER' (2) after expression
|
||||
D5 ')(' -> ParseError: unexpected token 'RPAREN' (')')
|
||||
D5 '' -> ParseError: empty input
|
||||
```
|
||||
|
||||
Test run: `Ran 48 tests in 0.001s — OK` (21 lex + 27 parser)
|
||||
@ -0,0 +1,60 @@
|
||||
# REVIEW-eval.md — Adversary verdicts
|
||||
|
||||
## eval/D1: PASS @2026-06-15T04:45:33Z
|
||||
|
||||
**Evidence (cold run, commit 3b507ee):**
|
||||
```
|
||||
2+3*4 → 14 ✓
|
||||
(2+3)*4 → 20 ✓
|
||||
8-3-2 → 3 ✓
|
||||
-2+5 → 3 ✓
|
||||
2*-3 → -6 ✓
|
||||
```
|
||||
Extra probes: `--5`→5, `-(3+4)`→-7, `1+2+3+4+5`→15 ✓
|
||||
|
||||
---
|
||||
|
||||
## eval/D2: PASS @2026-06-15T04:45:33Z
|
||||
|
||||
**Evidence:**
|
||||
- `7/2` → `3.5` (true division, not integer) ✓
|
||||
- `1/0` → `EvalError: division by zero` (not bare ZeroDivisionError) ✓
|
||||
- `5/(2-2)` → `EvalError` ✓
|
||||
|
||||
---
|
||||
|
||||
## eval/D3: PASS @2026-06-15T04:45:33Z
|
||||
|
||||
**Evidence:**
|
||||
```
|
||||
4/2 → int 2 ✓
|
||||
7/2 → float 3.5 ✓
|
||||
3*2 → int 6 ✓
|
||||
-4/2 → int -2 ✓
|
||||
1/3 → float 0.333... ✓
|
||||
2.5*2 → int 5 ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## eval/D4: PASS @2026-06-15T04:45:33Z
|
||||
|
||||
**Evidence:**
|
||||
```
|
||||
python calc.py "2+3*4" → stdout: 14, exit 0 ✓
|
||||
python calc.py "1/0" → stderr: "error: division by zero", exit 1, empty stdout ✓
|
||||
python calc.py "1 +" → stderr: "error: unexpected end of input", exit 1 ✓
|
||||
python calc.py → exit 1 (usage error) ✓
|
||||
python calc.py "1+1" "extra" → exit 1 (too many args) ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## eval/D5: PASS @2026-06-15T04:45:33Z
|
||||
|
||||
**Evidence:**
|
||||
```
|
||||
Ran 65 tests in 0.001s OK (21 lex + 27 parser + 17 evaluator) ✓
|
||||
```
|
||||
- `test_evaluator.py` covers D1 (arithmetic), D2 (division/EvalError), D3 (result type) ✓
|
||||
- No regressions in lex or parse suites ✓
|
||||
@ -0,0 +1,69 @@
|
||||
# REVIEW-lex.md — Adversary Verdicts
|
||||
|
||||
## Gates
|
||||
|
||||
- D1: PASS @2026-06-15T04:34:35Z
|
||||
- D2: PASS @2026-06-15T04:34:35Z
|
||||
- D3: PASS @2026-06-15T04:40:18Z (re-verified after AF-1 fix)
|
||||
- D4: PASS @2026-06-15T04:40:18Z (21 tests, 0 failures)
|
||||
|
||||
All DoD items verified. VETO lifted.
|
||||
|
||||
---
|
||||
|
||||
## D1 — Numbers: PASS @2026-06-15T04:34:35Z
|
||||
|
||||
```
|
||||
NUMBER 42 int ✓
|
||||
NUMBER 3.14 float ✓
|
||||
NUMBER 0.5 ✓ (.5 leading dot)
|
||||
NUMBER 10.0 float ✓ (10. trailing dot)
|
||||
[('NUMBER', 42), ('EOF', None)] ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## D2 — Operators & Parens: PASS @2026-06-15T04:34:35Z
|
||||
|
||||
```
|
||||
['NUMBER', 'PLUS', 'NUMBER', 'STAR', 'NUMBER', 'EOF'] ✓ (1+2*3)
|
||||
[('PLUS', '+'), ('MINUS', '-'), ('STAR', '*'), ('SLASH', '/'), ('LPAREN', '('), ('RPAREN', ')'), ('EOF', None)] ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## D3 — Whitespace & Errors: PASS @2026-06-15T04:40:18Z
|
||||
|
||||
**First verdict (FAIL @2026-06-15T04:34:35Z):** bare `.` raised `ValueError` not `LexError` (AF-1).
|
||||
|
||||
**Re-verified after fix commit f40a364:**
|
||||
|
||||
```
|
||||
Ran 21 tests in 0.000s — OK ✓
|
||||
['NUMBER', 'PLUS', 'NUMBER', 'EOF'] (whitespace skipped) ✓
|
||||
tokenize('.') → LexError: invalid number literal '.' at position 0 ✓
|
||||
tokenize('1 @ 2') → LexError: unexpected character '@' at position 2 ✓
|
||||
```
|
||||
|
||||
Valid floats unaffected by fix: `.5` → `0.5`, `10.` → `10.0`, `3.5*(1-2)` correct.
|
||||
|
||||
---
|
||||
|
||||
## D4 — Tests Green: PASS @2026-06-15T04:40:18Z
|
||||
|
||||
```
|
||||
Ran 21 tests in 0.000s
|
||||
OK
|
||||
```
|
||||
|
||||
21 tests (was 20; `test_bare_dot_raises_lex_error` added). All three required DoD examples covered and passing.
|
||||
|
||||
---
|
||||
|
||||
## Adversary findings
|
||||
|
||||
### AF-1 (CLOSED @2026-06-15T04:40:18Z): Bare `.` leaked `ValueError` instead of `LexError`
|
||||
|
||||
**Fix:** `lexer.py` now wraps `float(raw)` / `int(raw)` in try/except, re-raises as `LexError`. Test added.
|
||||
Re-tested: `tokenize('.')` now raises `LexError: invalid number literal '.' at position 0`. ✓
|
||||
_Closed by Adversary after re-test._
|
||||
@ -0,0 +1,67 @@
|
||||
# REVIEW-parse.md — Adversary verdicts for phase `parse`
|
||||
|
||||
Phase plan SSOT: /home/loops/project-orchestrator/projects/agent-orchestrator-benchmark/plans/calc/parse.md
|
||||
|
||||
Gates: D1 (precedence), D2 (left associativity), D3 (parentheses), D4 (unary minus), D5 (errors), D6 (tests green)
|
||||
|
||||
## Verdicts
|
||||
|
||||
- D1: PASS @2026-06-15T04:41:47Z
|
||||
- D2: PASS @2026-06-15T04:41:47Z
|
||||
- D3: PASS @2026-06-15T04:41:47Z
|
||||
- D4: PASS @2026-06-15T04:41:47Z
|
||||
- D5: PASS @2026-06-15T04:41:47Z
|
||||
- D6: PASS @2026-06-15T04:41:47Z
|
||||
|
||||
All DoD gates PASS. No VETO. Phase `parse` complete — Builder may write ## DONE to STATUS-parse.md.
|
||||
|
||||
---
|
||||
|
||||
## Evidence
|
||||
|
||||
**D1 — Precedence**
|
||||
```
|
||||
1+2*3 → BinOp('+', Num(1), BinOp('*', Num(2), Num(3))) ✓ (* binds tighter)
|
||||
2*3+4 → BinOp('+', BinOp('*', Num(2), Num(3)), Num(4)) ✓
|
||||
1+2*3-4/2 → BinOp('-', BinOp('+', Num(1), BinOp('*', Num(2), Num(3))), BinOp('/', Num(4), Num(2))) ✓
|
||||
```
|
||||
|
||||
**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)) ✓
|
||||
1-2+3 → BinOp('+', BinOp('-', Num(1), Num(2)), Num(3)) ✓
|
||||
2/3*4 → BinOp('*', BinOp('/', Num(2), Num(3)), Num(4)) ✓
|
||||
```
|
||||
|
||||
**D3 — Parentheses**
|
||||
```
|
||||
(1+2)*3 → BinOp('*', BinOp('+', Num(1), Num(2)), Num(3)) ✓
|
||||
((((1)))) → Num(1) ✓
|
||||
```
|
||||
|
||||
**D4 — Unary Minus**
|
||||
```
|
||||
-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))) ✓
|
||||
-2*3 → BinOp('*', Unary('-', Num(2)), Num(3)) ✓
|
||||
2+-3 → BinOp('+', Num(2), Unary('-', Num(3))) ✓
|
||||
-2*-3 → BinOp('*', Unary('-', Num(2)), Unary('-', Num(3))) ✓
|
||||
```
|
||||
|
||||
**D5 — Errors (all raise ParseError, no wrong exception types)**
|
||||
```
|
||||
'1 +' → ParseError: unexpected end of input ✓
|
||||
'(1' → ParseError: unclosed parenthesis ✓
|
||||
'1 2' → ParseError: unexpected token 'NUMBER'… ✓
|
||||
')(' → ParseError: unexpected token 'RPAREN' ✓
|
||||
'' → ParseError: empty input ✓
|
||||
Extra probes: '+1', '1++2', '1*', '((1+2)', '1+2)', '*', '()', '1 + + 2' — all ParseError ✓
|
||||
```
|
||||
|
||||
**D6 — Tests Green**
|
||||
```
|
||||
Ran 48 tests in 0.001s OK (21 lex + 27 parser) ✓
|
||||
```
|
||||
@ -0,0 +1,46 @@
|
||||
# STATUS-eval.md
|
||||
|
||||
## DONE
|
||||
|
||||
All eval gates D1–D5 Adversary-verified PASS. Phase complete.
|
||||
|
||||
---
|
||||
|
||||
## Gate: D1-D5 CLAIMED → ALL PASS
|
||||
|
||||
All gates are implemented and self-verified. Claiming D1–D5 together.
|
||||
|
||||
### WHAT is claimed
|
||||
- D1 — arithmetic: evaluate() correct for +, -, *, /, precedence, parens, unary minus
|
||||
- D2 — division: true division, EvalError on divide-by-zero (no bare ZeroDivisionError)
|
||||
- D3 — result type: whole-valued → int, non-whole → float
|
||||
- D4 — CLI: `python calc.py <expr>` prints result and exits 0; errors to stderr, non-zero exit
|
||||
- D5 — tests green + no regression
|
||||
|
||||
### HOW to verify (cold, from fresh clone)
|
||||
|
||||
```bash
|
||||
cd <repo-root>
|
||||
python -m unittest -q # must be 0 failures (65 tests)
|
||||
python calc.py "2+3*4" # expect: 14
|
||||
python calc.py "(2+3)*4" # expect: 20
|
||||
python calc.py "7/2" # expect: 3.5
|
||||
python calc.py "4/2" # expect: 2
|
||||
python calc.py "1/0" # expect: non-zero exit, error on stderr
|
||||
python calc.py "1 +" # expect: non-zero exit, error on stderr
|
||||
```
|
||||
|
||||
### EXPECTED outcomes
|
||||
- unittest: `Ran 65 tests in ~0.001s` — `OK`
|
||||
- `2+3*4` → `14`
|
||||
- `(2+3)*4` → `20`
|
||||
- `7/2` → `3.5`
|
||||
- `4/2` → `2`
|
||||
- `1/0` → exit 1, stderr: `error: division by zero`
|
||||
- `1 +` → exit 1, stderr: `error: unexpected end of input`
|
||||
|
||||
### WHERE inputs live
|
||||
- `calc/evaluator.py` — evaluator + EvalError
|
||||
- `calc/test_evaluator.py` — unittest suite covering D1–D3
|
||||
- `calc.py` — CLI entry point
|
||||
- Commit: (see git log — latest commit on main)
|
||||
@ -0,0 +1,98 @@
|
||||
# STATUS-lex.md
|
||||
|
||||
## DONE
|
||||
|
||||
All DoD gates Adversary-verified PASS. Phase `lex` complete.
|
||||
|
||||
- D1: PASS @2026-06-15T04:34:35Z
|
||||
- D2: PASS @2026-06-15T04:34:35Z
|
||||
- D3: PASS @2026-06-15T04:40:18Z (AF-1 fixed: bare `.` now raises LexError)
|
||||
- D4: PASS @2026-06-15T04:40:18Z (21 tests, 0 failures)
|
||||
|
||||
Fix commit: f40a364
|
||||
|
||||
---
|
||||
|
||||
## Gate D1 — Numbers CLAIMED
|
||||
|
||||
**WHAT:** `tokenize()` converts integers and floats to `NUMBER` tokens with numeric values.
|
||||
|
||||
**HOW to verify:**
|
||||
```bash
|
||||
python -c "from calc.lexer import tokenize; r=tokenize('42'); print(r[0].kind, r[0].value, type(r[0].value).__name__)"
|
||||
python -c "from calc.lexer import tokenize; r=tokenize('3.14'); print(r[0].kind, r[0].value, type(r[0].value).__name__)"
|
||||
python -c "from calc.lexer import tokenize; r=tokenize('.5'); print(r[0].kind, r[0].value)"
|
||||
python -c "from calc.lexer import tokenize; r=tokenize('10.'); print(r[0].kind, r[0].value, type(r[0].value).__name__)"
|
||||
python -c "from calc.lexer import tokenize; print([(t.kind,t.value) for t in tokenize('42')])"
|
||||
```
|
||||
|
||||
**EXPECTED:**
|
||||
- `NUMBER 42 int`
|
||||
- `NUMBER 3.14 float`
|
||||
- `NUMBER 0.5`
|
||||
- `NUMBER 10.0 float`
|
||||
- `[('NUMBER', 42), ('EOF', None)]`
|
||||
|
||||
**WHERE:** `calc/lexer.py`, commit fb03159
|
||||
|
||||
---
|
||||
|
||||
## Gate D2 — Operators & Parens CLAIMED
|
||||
|
||||
**WHAT:** `+ - * / ( )` tokenize to correct kinds; `"1+2*3"` → NUMBER PLUS NUMBER STAR NUMBER EOF.
|
||||
|
||||
**HOW to verify:**
|
||||
```bash
|
||||
python -c "from calc.lexer import tokenize; print([t.kind for t in tokenize('1+2*3')])"
|
||||
python -c "from calc.lexer import tokenize; print([(t.kind,t.value) for t in tokenize('+-*/()')] )"
|
||||
```
|
||||
|
||||
**EXPECTED:**
|
||||
- `['NUMBER', 'PLUS', 'NUMBER', 'STAR', 'NUMBER', 'EOF']`
|
||||
- `[('PLUS', '+'), ('MINUS', '-'), ('STAR', '*'), ('SLASH', '/'), ('LPAREN', '('), ('RPAREN', ')'), ('EOF', None)]`
|
||||
|
||||
**WHERE:** `calc/lexer.py`, commit fb03159
|
||||
|
||||
---
|
||||
|
||||
## Gate D3 — Whitespace & Errors RE-CLAIMED (AF-1 fixed)
|
||||
|
||||
**WHAT:** Spaces/tabs skipped; ALL invalid inputs raise `LexError` (including bare `.`).
|
||||
|
||||
**Fix applied:** Wrapped `float(raw)` / `int(raw)` conversion in try/except; raises `LexError` on bad numeric literals. Added `test_bare_dot_raises_lex_error` to test suite.
|
||||
|
||||
**HOW to verify:**
|
||||
```bash
|
||||
python -m unittest -q
|
||||
python -c "from calc.lexer import tokenize; print([t.kind for t in tokenize(' 12 + 3 ')])"
|
||||
python -c "from calc.lexer import tokenize; tokenize('1 @ 2')"
|
||||
python -c "from calc.lexer import tokenize; tokenize('.')"
|
||||
```
|
||||
|
||||
**EXPECTED:**
|
||||
- `Ran 21 tests in 0.000s\nOK`
|
||||
- `['NUMBER', 'PLUS', 'NUMBER', 'EOF']`
|
||||
- Raises `calc.lexer.LexError: unexpected character '@' at position 2`
|
||||
- Raises `calc.lexer.LexError: invalid number literal '.' at position 0`
|
||||
|
||||
**WHERE:** `calc/lexer.py`, `calc/test_lexer.py` (see fix commit)
|
||||
|
||||
---
|
||||
|
||||
## Gate D4 — Tests Green CLAIMED
|
||||
|
||||
**WHAT:** `calc/test_lexer.py` passes under `python -m unittest` with 0 failures.
|
||||
|
||||
**HOW to verify:**
|
||||
```bash
|
||||
python -m unittest -q
|
||||
python -c "from calc.lexer import tokenize; print([(t.kind,t.value) for t in tokenize('3.5*(1-2)')])"
|
||||
python -c "from calc.lexer import tokenize; tokenize('1 @ 2')"
|
||||
```
|
||||
|
||||
**EXPECTED:**
|
||||
- `Ran 20 tests in 0.000s\nOK`
|
||||
- `[('NUMBER', 3.5), ('STAR', '*'), ('LPAREN', '('), ('NUMBER', 1), ('MINUS', '-'), ('NUMBER', 2), ('RPAREN', ')'), ('EOF', None)]`
|
||||
- Raises `LexError`
|
||||
|
||||
**WHERE:** `calc/test_lexer.py`, commit fb03159
|
||||
@ -0,0 +1,126 @@
|
||||
# STATUS-parse.md
|
||||
|
||||
## DONE
|
||||
|
||||
All DoD gates Adversary-verified PASS. Phase `parse` complete.
|
||||
|
||||
- D1: PASS @2026-06-15T04:41:47Z
|
||||
- D2: PASS @2026-06-15T04:41:47Z
|
||||
- D3: PASS @2026-06-15T04:41:47Z
|
||||
- D4: PASS @2026-06-15T04:41:47Z
|
||||
- D5: PASS @2026-06-15T04:41:47Z
|
||||
- D6: PASS @2026-06-15T04:41:47Z
|
||||
|
||||
---
|
||||
|
||||
## Gate D1–D6 CLAIMED (archived)
|
||||
|
||||
---
|
||||
|
||||
## AST Shape
|
||||
|
||||
Nodes defined in `calc/parser.py`:
|
||||
|
||||
```python
|
||||
Num(value) # numeric leaf; value is int or float
|
||||
BinOp(op, left, right) # binary: op in {'+','-','*','/'}; left/right are nodes
|
||||
Unary(op, operand) # unary: op == '-'; operand is a node
|
||||
```
|
||||
|
||||
All nodes implement `__repr__` and `__eq__`.
|
||||
|
||||
---
|
||||
|
||||
## D1 — Precedence
|
||||
|
||||
**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'))))"
|
||||
python -c "from calc.lexer import tokenize; from calc.parser import parse; print(repr(parse(tokenize('2*3+4'))))"
|
||||
```
|
||||
|
||||
**EXPECTED:**
|
||||
- `BinOp('+', Num(1), BinOp('*', Num(2), Num(3)))`
|
||||
- `BinOp('+', BinOp('*', Num(2), Num(3)), Num(4))`
|
||||
|
||||
---
|
||||
|
||||
## D2 — Left Associativity
|
||||
|
||||
**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
|
||||
|
||||
**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
|
||||
|
||||
**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
|
||||
|
||||
**WHAT:** Malformed input raises `ParseError`.
|
||||
|
||||
**HOW:**
|
||||
```bash
|
||||
python -c "from calc.lexer import tokenize; from calc.parser import parse, ParseError
|
||||
for s in ['1 +','(1','1 2',')(','']:
|
||||
try: parse(tokenize(s)); print('NO ERROR for', repr(s))
|
||||
except ParseError as e: print('OK ParseError:', repr(s), '->', e)
|
||||
"
|
||||
```
|
||||
|
||||
**EXPECTED:** All five raise `ParseError` (not any other exception type).
|
||||
|
||||
---
|
||||
|
||||
## D6 — Tests Green
|
||||
|
||||
**WHAT:** `python -m unittest -q` passes with 0 failures; covers D1–D5.
|
||||
|
||||
**HOW:**
|
||||
```bash
|
||||
python -m unittest -q
|
||||
```
|
||||
|
||||
**EXPECTED:** `Ran 48 tests in ...s\n\nOK` (21 lex + 27 parser)
|
||||
|
||||
**WHERE:** `calc/parser.py`, `calc/test_parser.py`
|
||||
Reference in New Issue
Block a user