New sx_playwright mode="sandbox" — injects the WASM kernel into about:blank
with full FFI, IO suspension tracing, and real DOM. No server needed.
Predefined stacks: core (kernel only), web (full web stack), hs (+ hyperscript),
test (+ test framework). Custom files and setup expressions supported.
Reproduces the host-callback IO suspension bug: direct callFn chains 6/6
suspensions correctly, but host-callback → addEventListener → _driveAsync
only completes 1/6. Bug is in the _driveAsync resume chain context.
Also: debug.sx mock DOM harness, test_hs_repeat.js Node.js reproduction.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Runtime visibility fix:
- eval-hs now injects runtime helpers (hs-add, hs-falsy?, hs-strict-eq,
hs-type-check, hs-matches?, hs-contains?, hs-coerce) via outer let
binding so the tree-walker evaluator can resolve them
Parser fixes:
- null/undefined: return (null-literal) AST node instead of bare nil
(nil was indistinguishable from "no parse result" sentinel)
- === / !== tokenized as single 3-char operators
- mod operator: emit (modulo) instead of (%) — modulo is a real primitive
Compiler fixes:
- null-literal → nil
- % → modulo
- contains? → hs-contains? (avoids tree-walker primitive arity conflict)
Runtime additions:
- hs-contains?: wraps list membership + string containment
Tokenizer:
- Added keywords: a, an (removed — broke all tokenization), exist
- Triple operators: === and !== now tokenized correctly
Scorecard: 54/112 test groups passing, +23 from baseline.
Unlocked: really-equals, english comparisons, is-in, null is empty,
null exists, type checks, strict equality, mod.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract pure expression tests from the official _hyperscript test suite
and implement parser/compiler/runtime extensions to pass them.
Test infrastructure:
- 222 fixtures extracted from evalHyperScript calls (no DOM dependency)
- SX data format with eval-hs bridge and run-hs-fixture runner
- 24 suites covering expressions, comparisons, coercion, logic, etc.
Parser extensions (parser.sx):
- mod as infix arithmetic operator
- English comparison phrases (is less than, is greater than or equal to)
- is a/an Type typecheck syntax
- === / !== strict equality operators
- I as me synonym, am as is for comparisons
- does not exist/match/contain postfix
- some/every ... with quantifier expressions
- undefined keyword → nil
Compiler updates (compiler.sx):
- + emits hs-add (type-dispatching: string concat or numeric add)
- no emits hs-falsy? (HS truthiness: empty string is falsy)
- matches? emits hs-matches? (string regex in non-DOM context)
- New cases: not-in?, in?, type-check, strict-eq, some, every
Runtime additions (runtime.sx):
- hs-coerce: Int/Integer truncation via floor
- hs-add: string concat when either operand is string
- hs-falsy?: HS-compatible truthiness (nil, false, "" are falsy)
- hs-matches?: string pattern matching
- hs-type-check/hs-type-check!: lenient/strict type checking
- hs-strict-eq: type + value equality
Tokenizer (tokenizer.sx):
- Added keywords: I, am, does, some, mod, equal, equals, really,
include, includes, contain, undefined, exist
Scorecard: 47/112 test groups passing. 0 non-HS regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two hyperscript extensions beyond stock:
render ~component :key val [into|before|after target]
Tokenizer: ~ + ident → component token type
Parser: render command with kwargs and optional position
Compiler: emits (render-to-html ~comp :key val) or
(hs-put! (render-to-html ...) pos target)
Bridges hyperscript flow to SX component rendering
eval (sx-expression) — SX escape hatch
Inside eval (...), content is SX syntax (not hyperscript)
Parser: collect-sx-source extracts balanced parens from raw source
Compiler: sx-parse at compile time, inlines AST directly
Result: SX runs in handler scope — hyperscript variables visible!
Also supports string form: eval '(+ 1 2)' for backward compat
set name to "Giles"
set greeting to eval (str "Hello " name) -- name is visible!
16 new tests (parser + compiler + integration).
3127/3127 full build, zero regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tokenizer:
* and % now emit as operators (were silently swallowed)
Added keywords: install, measure, behavior, called
5 new arithmetic operator tests
Parser — expression layer:
Arithmetic (+, -, *, /, %) via parse-arith
Unary not, no, unary minus
the X of Y possessive (parse-the-expr)
as Type conversion, X in Y membership, array literals [...]
fetch URL parsing fixed — no longer consumes "as" meant for fetch
Parser — 8 new commands:
return, throw, append...to, tell...end, for...in...end,
make a Type, install Behavior, measure
Parser — 2 new features:
def name(params)...end, behavior Name(params)...end
Parser — enhanced:
wait for event [from target], on every event modifier
33 new parser tests (16 suites), 5 tokenizer tests.
3043/3043 full build, zero regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>