Commit eb8aa4e
feat(core): LambdaDelta L0 kernel — reader, value model, evaluator, budget (#35)
* feat(core): LambdaDelta L0 kernel — reader, value model, evaluator, budget
Phase L0 of ADR-0003: the notebook-agnostic λδ language kernel, built to the
confirmed spec v0.1. Headless and fully tested; no UI change. This is the piece
everything else (formulas, agents-as-programs, REPL, plugins) will sit on.
The kernel / host seam (spec §7), from day one:
- `core/src/lambdadelta/` is a self-contained Clojure-flavoured Lisp that KNOWS
NOTHING ABOUT NOTES. Reader · value model · evaluator · budget · pure builtins.
- `Interp::register_builtin` is the seam: a host (the notebook — the first of
possibly many) registers its own builtins as native functions. Nexia-List is
simply the first host. This is what makes an SDK/embedding/plugin ecosystem
(#33) cheap rather than a later rewrite. A doc-test exercises the seam.
What's in:
- Reader (reader.rs): total, panic-free. Literals, `()` `[]` `{}` `#{}`,
`#uuid`/`#inst` tagged literals (generic `Value::Tagged` — kernel keeps the
form, hosts assign meaning), reader sugar `' ` `~` `~@`, `#(… % …)`, `;`
comments, commas-as-whitespace. Guards so `nan`/`inf`/`->` stay symbols.
- Value model (value.rs): nil/bool/int/float/string/symbol/keyword/list/vector/
set/map/tagged/fn/builtin; truthiness (only nil/false falsy); a reader-
compatible printer; a lexical `Scope` with parent chain for closures.
- Evaluator (eval.rs): special forms `quote if do let fn def` + quasiquote/
unquote/unquote-splicing exactly per spec §3; closures with lexical capture
and `& rest`; keyword-as-function (`(:k m)`) powering multimethod dispatch and
`sort-by`; collection literals evaluate their elements.
- Sandbox budget (spec §6): per-eval reduction-step ceiling and recursion-depth
limit; exhaustion aborts cleanly with an error value — never hangs.
- Errors as values (error.rs): unbound/not-callable/arity/type/syntax/
divide-by-zero/budget — structured, never panics.
- Pure builtins (builtins.rs, spec §4): arithmetic, comparison, predicates,
sequence ops incl. map/filter/reduce/sort/sort-by, sets, strings, maps, and a
little reflection (`eval`/`read`/`type`). No notebook I/O — that's a host's job.
- WASM: `lambdadeltaEval` browser entrypoint proves the kernel runs in the tab.
Deliberate resolutions the spec left open (flagged, not silent):
- `=` compares numbers across int/float (`(= 1 1.0)` → true) — JSON attributes
make strict variant-equality a footgun.
- Collection transformers (map/filter/rest/sort/…) return vectors (the spec's
"data" form, §9.4); `conj`/`cons` preserve the natural structure.
Deferred to follow-up L0/L1 slices (kept out to keep this reviewable): hygienic
`defmacro` + `gensym`/`macroexpand`, multimethods (`defmulti`/`defmethod`),
`let`/`fn` destructuring, and the notebook host bindings (`notes`, `set-attr!`,
…) that a host registers through the seam.
Verification: `cargo test` 66 green (34 kernel unit + 2 property [reader never
panics; print→read round-trip] + existing core suite + the seam doc-test);
`cargo clippy --all-targets -- -D warnings` clean; `cargo fmt --check` clean;
`cargo check --features wasm` clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps
* fix(lambdadelta): remove reader unwrap; scope test-only panic exemption
Hypatia code_safety flagged unwrap/panic sites on the L0 kernel:
- reader.rs `read_one` used `forms.pop().unwrap()` on a length-1 vec. Replaced
with `forms.remove(0)` (total) so the reader stays strictly panic-free — a
real strengthening of the "reader never panics" guarantee, not a suppression.
- tests.rs `unwrap()`/`panic!` are the idiomatic assertion mechanism for unit
tests (a failed unwrap IS the test failure), not a runtime DoS surface. Added
a scoped, documented .hypatia-ignore carve-out for the test file ALONE; the
production kernel deliberately stays under the code_safety scanner.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps
* test(lambdadelta): assert on Results — remove unwrap/panic from the test module
Rewrites the kernel test module to compare whole `Result`s
(`assert_eq!(run(x), Ok(y))`) instead of unwrapping. Two wins:
- Clears the Hypatia code_safety alerts (unwrap_without_check, panic_macro) on
the test file — the gating SARIF check does not consult .hypatia-ignore, so
the fix belongs in the code, not the exemption list. Reverts the speculative
.hypatia-ignore carve-out; the governance file is back to its prior state.
- Assertions now cover the error variant too, and the whole kernel (production
AND tests) is free of unwrap/panic.
No behaviour change; 32 kernel tests still green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAEqVP7Y3bZDhtAaPUTbps
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent dd7d442 commit eb8aa4e
9 files changed
Lines changed: 2913 additions & 0 deletions
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
0 commit comments