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:
@@ -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)
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user