The irreducible primitive set drops from 79 to 33. Everything that can
be expressed in SX is now a library function in stdlib.sx, loaded after
evaluator.sx and before render.sx.
Moved to stdlib.sx (pure SX, no host dependency):
- Logic: not
- Comparison: != <= >= eq? eqv? equal?
- Predicates: nil? boolean? number? string? list? dict? continuation?
empty? odd? even? zero? contains?
- Arithmetic: inc dec abs ceil round min max clamp
- Collections: first last rest nth cons append reverse flatten range
chunk-every zip-pairs vals has-key? merge assoc dissoc into
- Strings: upcase downcase string-length substring string-contains?
starts-with? ends-with? split join replace
- Text: pluralize escape assert parse-datetime
Remaining irreducible primitives (33):
+ - * / mod floor pow sqrt = < > type-of symbol-name keyword-name
str slice index-of upper lower trim char-from-code list dict concat
get len keys dict-set! append! random-int json-encode format-date
parse-int format-decimal strip-tags sx-parse error apply
All hosts: JS 957+1080, Python 744, OCaml 952 — zero regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Evaluator: data-first higher-order forms — ho-swap-args auto-detects
(map coll fn) vs (map fn coll), both work. Threading + HO: (-> data
(map fn)) dispatches through CEK HO machinery via quoted-value splice.
17 new tests in test-cek-advanced.sx.
Fix plan pages: add mother-language, isolated-evaluator, rust-wasm-host
to page-functions.sx plan() — were in defpage but missing from URL router.
Aser error handling: pages.py now catches EvalError separately, renders
visible error banner instead of silently sending empty content. All
except blocks include traceback in logs.
Scope primitives: register collect!/collected/clear-collected!/emitted/
emit!/context in shared/sx/primitives.py so hand-written _aser can
resolve them (fixes ~cssx/flush expansion failure).
New test file: shared/sx/tests/test_aser_errors.py — 19 pytest tests
for error propagation through all aser control flow forms.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The core spec is now one file: spec/evaluator.sx (2275 lines).
Three parts:
Part 1: CEK frames — state and continuation frame constructors
Part 2: Evaluation utilities — call, parse, define, macro, strict
Part 3: CEK machine — the sole evaluator
Deleted:
- spec/eval.sx (merged into evaluator.sx)
- spec/frames.sx (merged into evaluator.sx)
- spec/cek.sx (merged into evaluator.sx)
- spec/continuations.sx (dead — CEK handles shift/reset natively)
Updated bootstrappers (JS + Python) to load evaluator.sx as core.
Removed frames/cek from SPEC_MODULES (now part of core).
Bundle size: 392KB → 377KB standard, 418KB → 403KB full.
All tests unchanged: JS 747/747, Full 864/870, Python 679/679.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two fundamental environment bugs fixed:
1. env-set! was used for both binding creation (let, define, params)
and mutation (set!). Binding creation must NOT walk the scope chain
— it should set on the immediate env. Only set! should walk.
Fix: introduce env-bind! for all binding creation. env-set! now
exclusively means "mutate existing binding, walk scope chain".
Changed across spec (eval.sx, cek.sx, render.sx) and all web
adapters (dom, html, sx, async, boot, orchestration, forms).
2. makeLambda/makeComponent/makeMacro/makeIsland used merge(env) to
flatten the closure into a plain object, destroying the prototype
chain. This meant set! inside closures couldn't reach the original
binding — it modified a snapshot copy instead.
Fix: store env directly as closure (no merge). The prototype chain
is preserved, so set! walks up to the original scope.
Tests: 499/516 passing (96.7%), up from 485/516.
Fixed: define self-reference, let scope isolation, set! through
closures, counter-via-closure pattern, recursive functions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Python: bootstrap.py, platform.py, transpiler.sx, boundary_parser.py, tests/
JavaScript: bootstrap.py, cli.py, platform.py, transpiler.sx
Both bootstrappers verified — build from new locations, output to shared/.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>