Phase 6 — deftype: named type aliases, unions, records, and parameterized
types. Type definitions stored as plain dicts in *type-registry*. Includes
resolve-type for named type resolution, substitute-type-vars for
parameterized instantiation, subtype-resolved? for structural record
subtyping, and infer-type extension for record field type inference via get.
Phase 7 — defeffect: static effect annotations. Effects stored in
*effect-registry* and *effect-annotations*. Supports :effects keyword on
defcomp and define. Gradual: unannotated = all effects, empty list = pure.
check-body-walk validates effect containment at call sites.
Standard types defined: (maybe a), type-def, diagnostic, prim-param-sig.
Standard effects declared: io, mutation, render.
84/84 type system tests pass. Both Python and JS bootstrappers succeed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements subtype checking, type inference, type narrowing, and
component call-site checking. All type logic is in types.sx (spec),
bootstrapped to every host. Adds test-types.sx with full coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two bugs fixed:
1. process-bindings used merge(env) which returns {} for Env objects
(Env is not a dict subclass). Changed to env-extend in render.sx
and adapter-async.sx. This caused "Undefined symbol: theme" etc.
2. async-aser-eval-call passed evaled-args list to async-invoke(&rest),
double-wrapping it. Changed to inline apply + coroutine check.
Also: bootstrap define-async into sx_ref.py (Phase 6), replace ~1000 LOC
hand-written async_eval_ref.py with 24-line thin re-export shim.
Test runner now uses Env (not flat dict) for render envs to catch scope bugs.
8 new regression tests (4 scope chain, 2 native callable arity, 2 render).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add page-helpers-demo page with defisland ~demo-client-runner (pure SX,
zero JS files) showing spec functions running on both server and client
- Fix _aser_component children serialization: flatten list results from map
instead of serialize(list) which wraps in parens creating ((div ...) ...)
that re-parses as invalid function call. Fixed in adapter-async.sx spec
and async_eval_ref.py
- Switch _eval_slot to use async_eval_ref.py when SX_USE_REF=1 (was
hardcoded to async_eval.py)
- Add Island type support to async_eval_ref.py: import, SSR rendering,
aser dispatch, thread-first, defisland in _ASER_FORMS
- Add server affinity check: components with :affinity :server expand
even when _expand_components is False
- Add diagnostic _aser_stack context to EvalError messages
- New spec files: adapter-async.sx, page-helpers.sx, platform_js.py
- Bootstrappers: page-helpers module support, performance.now() timing
- 0-arity lambda event handler fix in adapter-dom.sx
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EvalError moved to types.py. All 27 files updated to import eval_expr,
trampoline, call_lambda, etc. directly from shared.sx.ref.sx_ref instead
of through the evaluator.py indirection layer. 320/320 spec tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- evaluator.py: replace 1200 lines of hand-written eval with thin shim
that re-exports from bootstrapped sx_ref.py
- bootstrap_py.py: emit all fn-bodied defines as `def` (not `lambda`),
flatten tail-position if/cond/case/when to if/elif with returns,
fix &rest handling in _emit_define_as_def
- platform_py.py: EvalError imports from evaluator.py so catches work
- __init__.py: remove SX_USE_REF conditional, always use bootstrapped
- tests/run.py: reset render_active after render tests for isolation
- Removes setrecursionlimit(5000) hack — no longer needed with flat code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix qq-expand in eval.sx: use concat+list instead of append to prevent
nested lists from being flattened during quasiquote expansion
- Update append primitive to match spec ("if x is list, concatenate")
- Rebuild sx_ref.py with quasiquote fix
- Make relations.py self-contained: parse defrelation AST directly
without depending on the evaluator (25/25 tests pass)
- Replace hand-written JSEmitter with js.sx self-hosting bootstrapper
- Guard server-only tests in test-eval.sx with runtime check
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
eval-list only dispatches to the render adapter when render-active? is true.
render-to-html and aser set render-active! on entry. Pure evaluate() calls
no longer stringify component results through the render adapter.
Fixes component children parity: (defcomp ~wrap (&key &rest children) children)
now returns [1,2,3] in eval mode, renders to "123" only in render mode.
Parity: 112/116 pass (remaining 4 are hand-written evaluator.py bugs).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bootstrap_py.py (G0) and run_py_sx.py (G1) now both import static
platform sections from platform_py.py instead of duplicating them.
bootstrap_py.py shrinks from 2287 to 1176 lines — only the PyEmitter
transpiler and build orchestration remain.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>