Fix 73 JS test failures: match transpiler, sxEq, deref frame, signals, stepper lib

Evaluator fixes (from broken match refactor in 8bba02f):
- Deref frame: use CEK state `value`, not `(get frame "value")`
- Deref frame: restore `(context "sx-reactive" nil)` (was undefined `get-tracking-context`)
- Scope-acc frame: restore missing `(get frame "value")` arg to make-scope-acc-frame
- Add missing `thread-insert-arg` helper for thread-first non-HO branch

Transpiler (hosts/javascript/transpiler.sx):
- Add `match` special form handler (IIFE with chained if/return, `_` wildcard)
- Replace `=`/`!=` infix `==` with `sxEq()` function call for proper symbol equality

JS platform (hosts/javascript/platform.py):
- Add `sxEq` for structural symbol/keyword comparison
- Add `componentFile`, `sort`, `defStore`/`useStore`/`clearStores` primitives
- Add `length`/`map`/`for-each`/`reduce` as VM-compatible HOF primitives
- Fix `SYM` → `makeSymbol` references

New files:
- sx/sx/stepper-lib.sx: extracted split-tag, build-code-tokens, steps-to-preview

JS tests: 0 → 1582/1585 passing (3 remaining are VM closure interop)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-31 08:33:27 +00:00
parent 465ce1abcb
commit f828fb023b
112 changed files with 1728 additions and 1813 deletions

View File

@@ -835,6 +835,16 @@ PREAMBLE = '''\
;(function(global) {
"use strict";
// =========================================================================
// Equality — used by transpiled code (= a b) → sxEq(a, b)
// =========================================================================
function sxEq(a, b) {
if (a === b) return true;
if (a && b && a._sym && b._sym) return a.name === b.name;
if (a && b && a._kw && b._kw) return a.name === b.name;
return false;
}
// =========================================================================
// Types
// =========================================================================
@@ -944,8 +954,8 @@ PRIMITIVES_JS_MODULES: dict[str, str] = {
"core.comparison": '''
// core.comparison
PRIMITIVES["="] = function(a, b) { return a === b; };
PRIMITIVES["!="] = function(a, b) { return a !== b; };
PRIMITIVES["="] = sxEq;
PRIMITIVES["!="] = function(a, b) { return !sxEq(a, b); };
PRIMITIVES["<"] = function(a, b) { return a < b; };
PRIMITIVES[">"] = function(a, b) { return a > b; };
PRIMITIVES["<="] = function(a, b) { return a <= b; };
@@ -1306,6 +1316,7 @@ PLATFORM_JS_PRE = '''
function componentClosure(c) { return c.closure; }
function componentHasChildren(c) { return c.hasChildren; }
function componentName(c) { return c.name; }
function componentFile(c) { return (c && c.file) ? c.file : NIL; }
function componentAffinity(c) { return c.affinity || "auto"; }
function componentParamTypes(c) { return (c && c._paramTypes) ? c._paramTypes : NIL; }
function componentSetParamTypes_b(c, t) { if (c) c._paramTypes = t; return NIL; }
@@ -2669,12 +2680,12 @@ PLATFORM_ORCHESTRATION_JS = """
function cekTry(thunkFn, handlerFn) {
try {
var result = _wrapSxFn(thunkFn)();
if (!handlerFn || handlerFn === NIL) return [SYM("ok"), result];
if (!handlerFn || handlerFn === NIL) return [makeSymbol("ok"), result];
return result;
} catch (e) {
var msg = (e && e.message) ? e.message : String(e);
if (handlerFn && handlerFn !== NIL) return _wrapSxFn(handlerFn)(msg);
return [SYM("error"), msg];
return [makeSymbol("error"), msg];
}
}
function errorMessage(e) {
@@ -3223,6 +3234,43 @@ def fixups_js(has_html, has_sx, has_dom, has_signals=False, has_deps=False, has_
// Core primitives that require native JS (cannot be expressed via FFI)
// -----------------------------------------------------------------------
PRIMITIVES["error"] = function(msg) { throw new Error(msg); };
PRIMITIVES["sort"] = function(lst) {
if (!Array.isArray(lst)) return lst;
return lst.slice().sort(function(a, b) {
if (a < b) return -1; if (a > b) return 1; return 0;
});
};
// Aliases for VM bytecode compatibility
PRIMITIVES["length"] = PRIMITIVES["len"];
// VM-compatible HOF primitives — use callPrimFn which handles native, lambda, and VM closures
function callPrimFn(f, args) {
if (typeof f === "function") return f.apply(null, args);
if (f && f._lambda) return cekCall(f, args);
if (f && f["vm-code"]) {
// VM closure — call through call-primitive dispatch
var cp = PRIMITIVES["vm-call-closure"];
if (cp) return cp(f, args);
}
return cekCall(f, args);
}
PRIMITIVES["map"] = function(fn, lst) {
if (Array.isArray(fn)) { var tmp = fn; fn = lst; lst = tmp; }
var result = [];
for (var i = 0; i < lst.length; i++) result.push(callPrimFn(fn, [lst[i]]));
return result;
};
PRIMITIVES["for-each"] = function(fn, lst) {
if (Array.isArray(fn)) { var tmp = fn; fn = lst; lst = tmp; }
for (var i = 0; i < lst.length; i++) callPrimFn(fn, [lst[i]]);
return NIL;
};
PRIMITIVES["reduce"] = function(fn, init, lst) {
if (Array.isArray(fn)) { var tmp = fn; fn = lst; lst = init; init = tmp; }
var acc = init;
for (var i = 0; i < lst.length; i++) acc = callPrimFn(fn, [acc, lst[i]]);
return acc;
};
// FFI library functions — defined in dom.sx/browser.sx but not transpiled.
// Registered here so runtime-evaluated SX code (data-init, islands) can use them.
@@ -3303,14 +3351,30 @@ def fixups_js(has_html, has_sx, has_dom, has_signals=False, has_deps=False, has_
PRIMITIVES["cek-try"] = function(thunkFn, handlerFn) {
try {
var result = _wrapSxFn(thunkFn)();
if (!handlerFn || handlerFn === NIL) return [SYM("ok"), result];
if (!handlerFn || handlerFn === NIL) return [makeSymbol("ok"), result];
return result;
} catch (e) {
var msg = (e && e.message) ? e.message : String(e);
if (handlerFn && handlerFn !== NIL) return _wrapSxFn(handlerFn)(msg);
return [SYM("error"), msg];
return [makeSymbol("error"), msg];
}
};''']
};
// Named stores — global mutable registry (mirrors OCaml sx_primitives.ml)
var _storeRegistry = {};
function defStore(name, initFn) {
if (!_storeRegistry.hasOwnProperty(name)) {
_storeRegistry[name] = _wrapSxFn(initFn)();
}
return _storeRegistry[name];
}
function useStore(name) {
if (!_storeRegistry.hasOwnProperty(name)) throw new Error("Store not found: " + name);
return _storeRegistry[name];
}
function clearStores() { _storeRegistry = {}; return NIL; }
PRIMITIVES["def-store"] = defStore;
PRIMITIVES["use-store"] = useStore;
PRIMITIVES["clear-stores"] = clearStores;''']
if has_deps:
lines.append('''
// Platform deps functions (native JS, not transpiled — need explicit registration)