js: fix lambda binding (index-of on lists), add vectors + R7RS platform stubs

- Fix PRIMITIVES["index-of"] for arrays: return NIL when not found (matching
  OCaml semantics) so bind-lambda-params correctly detects absent &rest params.
  Previously String(array).indexOf() returned -1, which passed number? check
  and mis-fired the &rest branch, leaving non-&rest params unbound.
- Declare var _lastErrorKont_ and var hostError in IIFE scope (strict mode fix)
- Add PRIMITIVES["host-error"], ["try-catch"], ["without-io-hook"]
- Add env["test-allowed?"] stub in run_tests.js
- Add spec/tests/test-vectors.sx: 42 tests for all vector primitives
- Rebuild sx-browser.js: 1847 standard / 2362 full tests pass (up from 5)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 10:02:23 +00:00
parent 5a332fa430
commit 1d85e3a79c
4 changed files with 777 additions and 117 deletions

View File

@@ -842,6 +842,13 @@ PREAMBLE = '''\
if (a === b) return true; if (a === b) return true;
if (a && b && a._sym && b._sym) return a.name === b.name; if (a && b && a._sym && b._sym) return a.name === b.name;
if (a && b && a._kw && b._kw) return a.name === b.name; if (a && b && a._kw && b._kw) return a.name === b.name;
if (a && b && a._vector && b._vector) {
if (a.arr.length !== b.arr.length) return false;
for (var _i = 0; _i < a.arr.length; _i++) {
if (!sxEq(a.arr[_i], b.arr[_i])) return false;
}
return true;
}
return false; return false;
} }
@@ -908,6 +915,44 @@ PREAMBLE = '''\
function SxSpread(attrs) { this.attrs = attrs || {}; } function SxSpread(attrs) { this.attrs = attrs || {}; }
SxSpread.prototype._spread = true; SxSpread.prototype._spread = true;
function SxVector(arr) { this.arr = arr || []; }
SxVector.prototype._vector = true;
var _paramUidCounter = 0;
function SxParameter(defaultVal, converter) {
this._uid = ++_paramUidCounter;
this._default = defaultVal;
this._converter = converter || null;
}
SxParameter.prototype._parameter = true;
function parameter_p(x) { return x != null && x._parameter === true; }
function parameterUid(p) { return p._uid; }
function parameterDefault(p) { return p._default; }
function SxCallccContinuation(capturedKont) { this._captured = capturedKont; }
SxCallccContinuation.prototype._callcc = true;
function makeCallccContinuation(kont) { return new SxCallccContinuation(kont); }
function callccContinuation_p(x) { return x != null && x._callcc === true; }
function callccContinuationData(x) { return x._captured; }
function evalError_p(v) {
return v != null && typeof v === "object" && v["__eval_error__"] === true;
}
function sxApplyCek(f, args) {
try {
return typeof f === "function" ? f.apply(null, args) : f;
} catch (e) {
if (e && e._perform_request) throw e;
if (e && e._cek_suspend) throw e;
return {"__eval_error__": true, "message": e && e.message ? e.message : String(e)};
}
}
var _JIT_SKIP_SENTINEL = {"__jit_skip": true};
function jitTryCall(f, args) { return _JIT_SKIP_SENTINEL; }
function jitSkip_p(v) { return v === _JIT_SKIP_SENTINEL || (v != null && v["__jit_skip"] === true); }
var _scopeStacks = {}; var _scopeStacks = {};
function isSym(x) { return x != null && x._sym === true; } function isSym(x) { return x != null && x._sym === true; }
@@ -1004,7 +1049,20 @@ PRIMITIVES_JS_MODULES: dict[str, str] = {
PRIMITIVES["split"] = function(s, sep) { return String(s).split(sep || " "); }; PRIMITIVES["split"] = function(s, sep) { return String(s).split(sep || " "); };
PRIMITIVES["join"] = function(sep, coll) { return coll.join(sep); }; PRIMITIVES["join"] = function(sep, coll) { return coll.join(sep); };
PRIMITIVES["replace"] = function(s, old, nw) { return s.split(old).join(nw); }; PRIMITIVES["replace"] = function(s, old, nw) { return s.split(old).join(nw); };
PRIMITIVES["index-of"] = function(s, needle, from) { return String(s).indexOf(needle, from || 0); }; PRIMITIVES["index-of"] = function(s, needle, from) {
if (Array.isArray(s)) {
var _start = from || 0;
for (var _i = _start; _i < s.length; _i++) {
var _a = s[_i];
if (_a === needle) return _i;
if (_a != null && needle != null && typeof _a === "object" && typeof needle === "object") {
if ((_a._sym && needle._sym || _a._kw && needle._kw) && _a.name === needle.name) return _i;
}
}
return NIL;
}
return String(s).indexOf(needle, from || 0);
};
PRIMITIVES["starts-with?"] = function(s, p) { return String(s).indexOf(p) === 0; }; PRIMITIVES["starts-with?"] = function(s, p) { return String(s).indexOf(p) === 0; };
PRIMITIVES["ends-with?"] = function(s, p) { var str = String(s); return str.indexOf(p, str.length - p.length) !== -1; }; PRIMITIVES["ends-with?"] = function(s, p) { var str = String(s); return str.indexOf(p, str.length - p.length) !== -1; };
PRIMITIVES["slice"] = function(c, a, b) { if (!c || typeof c.slice !== "function") { console.error("[sx-debug] slice called on non-sliceable:", typeof c, c, "a=", a, "b=", b, new Error().stack); return []; } return b !== undefined ? c.slice(a, b) : c.slice(a); }; PRIMITIVES["slice"] = function(c, a, b) { if (!c || typeof c.slice !== "function") { console.error("[sx-debug] slice called on non-sliceable:", typeof c, c, "a=", a, "b=", b, new Error().stack); return []; } return b !== undefined ? c.slice(a, b) : c.slice(a); };
@@ -1086,6 +1144,39 @@ PRIMITIVES_JS_MODULES: dict[str, str] = {
}; };
''', ''',
"core.vectors": '''
// core.vectors — R7RS mutable fixed-size arrays
PRIMITIVES["make-vector"] = function(n, fill) {
var arr = new Array(n);
var f = (fill !== undefined) ? fill : NIL;
for (var i = 0; i < n; i++) arr[i] = f;
return new SxVector(arr);
};
PRIMITIVES["vector"] = function() {
return new SxVector(Array.prototype.slice.call(arguments));
};
PRIMITIVES["vector?"] = function(x) { return x != null && x._vector === true; };
PRIMITIVES["vector-length"] = function(v) { return v.arr.length; };
PRIMITIVES["vector-ref"] = function(v, i) {
if (i < 0 || i >= v.arr.length) throw new Error("vector-ref: index " + i + " out of bounds (length " + v.arr.length + ")");
return v.arr[i];
};
PRIMITIVES["vector-set!"] = function(v, i, val) {
if (i < 0 || i >= v.arr.length) throw new Error("vector-set!: index " + i + " out of bounds (length " + v.arr.length + ")");
v.arr[i] = val; return NIL;
};
PRIMITIVES["vector->list"] = function(v) { return v.arr.slice(); };
PRIMITIVES["list->vector"] = function(l) { return new SxVector(l.slice()); };
PRIMITIVES["vector-fill!"] = function(v, val) {
for (var i = 0; i < v.arr.length; i++) v.arr[i] = val; return NIL;
};
PRIMITIVES["vector-copy"] = function(v, start, end) {
var s = (start !== undefined) ? start : 0;
var e = (end !== undefined) ? Math.min(end, v.arr.length) : v.arr.length;
return new SxVector(v.arr.slice(s, e));
};
''',
"stdlib.format": ''' "stdlib.format": '''
// stdlib.format // stdlib.format
PRIMITIVES["format-decimal"] = function(v, p) { return Number(v).toFixed(p || 2); }; PRIMITIVES["format-decimal"] = function(v, p) { return Number(v).toFixed(p || 2); };
@@ -1234,6 +1325,7 @@ PLATFORM_JS_PRE = '''
if (x._macro) return "macro"; if (x._macro) return "macro";
if (x._raw) return "raw-html"; if (x._raw) return "raw-html";
if (x._sx_expr) return "sx-expr"; if (x._sx_expr) return "sx-expr";
if (x._vector) return "vector";
if (typeof Node !== "undefined" && x instanceof Node) return "dom-node"; if (typeof Node !== "undefined" && x instanceof Node) return "dom-node";
if (Array.isArray(x)) return "list"; if (Array.isArray(x)) return "list";
if (typeof x === "object") return "dict"; if (typeof x === "object") return "dict";
@@ -1400,6 +1492,12 @@ PLATFORM_JS_PRE = '''
// Placeholder — overridden by transpiled version from render.sx // Placeholder — overridden by transpiled version from render.sx
function isRenderExpr(expr) { return false; } function isRenderExpr(expr) { return false; }
// Last error continuation — saved when a raise goes unhandled, for post-mortem inspection.
var _lastErrorKont_ = null;
// hostError — throw a host-level error that propagates out of cekRun.
function hostError(msg) { throw new Error(typeof msg === "string" ? msg : inspect(msg)); }
// Render dispatch — call the active adapter's render function. // Render dispatch — call the active adapter's render function.
// Set by each adapter when loaded; defaults to identity (no rendering). // Set by each adapter when loaded; defaults to identity (no rendering).
var _renderExprFn = null; var _renderExprFn = null;
@@ -1743,6 +1841,13 @@ CEK_FIXUPS_JS = '''
PRIMITIVES["lambda-name"] = lambdaName; PRIMITIVES["lambda-name"] = lambdaName;
PRIMITIVES["component?"] = isComponent; PRIMITIVES["component?"] = isComponent;
PRIMITIVES["island?"] = isIsland; PRIMITIVES["island?"] = isIsland;
PRIMITIVES["parameter?"] = parameter_p;
PRIMITIVES["parameter-uid"] = parameterUid;
PRIMITIVES["parameter-default"] = parameterDefault;
PRIMITIVES["make-parameter"] = function(defaultVal, converter) {
var p = new SxParameter(defaultVal, converter || null);
return p;
};
PRIMITIVES["make-symbol"] = function(n) { return new Symbol(n); }; PRIMITIVES["make-symbol"] = function(n) { return new Symbol(n); };
PRIMITIVES["is-html-tag?"] = function(n) { return HTML_TAGS.indexOf(n) >= 0; }; PRIMITIVES["is-html-tag?"] = function(n) { return HTML_TAGS.indexOf(n) >= 0; };
function makeEnv() { return merge(componentEnv, PRIMITIVES); } function makeEnv() { return merge(componentEnv, PRIMITIVES); }
@@ -2031,7 +2136,7 @@ PLATFORM_DOM_JS = """
} }
function domDispatch(el, name, detail) { function domDispatch(el, name, detail) {
if (!_hasDom || !el) return false; if (!_hasDom || !el || typeof el.dispatchEvent !== "function") return false;
var evt = new CustomEvent(name, { bubbles: true, cancelable: true, detail: detail || {} }); var evt = new CustomEvent(name, { bubbles: true, cancelable: true, detail: detail || {} });
return el.dispatchEvent(evt); return el.dispatchEvent(evt);
} }
@@ -2157,6 +2262,14 @@ PLATFORM_ORCHESTRATION_JS = """
// Platform interface — Orchestration (browser-only) // Platform interface — Orchestration (browser-only)
// ========================================================================= // =========================================================================
// --- Stubs for define-library functions not transpiled by extract_defines ---
// These are defined in orchestration.sx's define-library and called from
// boot.sx top-level defines. The JS bootstrapper only transpiles top-level
// defines, so we provide stubs here for functions that need a JS identity.
function flushCollectedStyles() { return NIL; }
function processElements(root) { return NIL; }
// --- Browser/Network --- // --- Browser/Network ---
function browserNavigate(url) { function browserNavigate(url) {
@@ -2642,6 +2755,10 @@ PLATFORM_ORCHESTRATION_JS = """
return el && el.closest ? el.closest(sel) : null; return el && el.closest ? el.closest(sel) : null;
} }
function domDocument() {
return _hasDom ? document : null;
}
function domBody() { function domBody() {
return _hasDom ? document.body : null; return _hasDom ? document.body : null;
} }
@@ -3085,6 +3202,8 @@ PLATFORM_BOOT_JS = """
// Platform interface — Boot (mount, hydrate, scripts, cookies) // Platform interface — Boot (mount, hydrate, scripts, cookies)
// ========================================================================= // =========================================================================
function preloadIslandDefs() { return NIL; }
function resolveMountTarget(target) { function resolveMountTarget(target) {
if (typeof target === "string") return _hasDom ? document.querySelector(target) : null; if (typeof target === "string") return _hasDom ? document.querySelector(target) : null;
return target; return target;
@@ -3237,6 +3356,18 @@ 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) // Core primitives that require native JS (cannot be expressed via FFI)
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
PRIMITIVES["error"] = function(msg) { throw new Error(msg); }; PRIMITIVES["error"] = function(msg) { throw new Error(msg); };
PRIMITIVES["host-error"] = function(msg) { throw new Error(typeof msg === "string" ? msg : inspect(msg)); };
PRIMITIVES["try-catch"] = function(tryFn, catchFn) {
try {
return cekRun(continueWithCall(tryFn, [], makeEnv(), [], []));
} catch(e) {
var msg = e && e.message ? e.message : String(e);
return cekRun(continueWithCall(catchFn, [msg], makeEnv(), [msg], []));
}
};
PRIMITIVES["without-io-hook"] = function(thunk) {
return cekRun(continueWithCall(thunk, [], makeEnv(), [], []));
};
PRIMITIVES["sort"] = function(lst) { PRIMITIVES["sort"] = function(lst) {
if (!Array.isArray(lst)) return lst; if (!Array.isArray(lst)) return lst;
return lst.slice().sort(function(a, b) { return lst.slice().sort(function(a, b) {
@@ -3304,7 +3435,7 @@ def fixups_js(has_html, has_sx, has_dom, has_signals=False, has_deps=False, has_
PRIMITIVES["dom-tag-name"] = domTagName; PRIMITIVES["dom-tag-name"] = domTagName;
PRIMITIVES["dom-get-prop"] = domGetProp; PRIMITIVES["dom-get-prop"] = domGetProp;
PRIMITIVES["dom-set-prop"] = domSetProp; PRIMITIVES["dom-set-prop"] = domSetProp;
PRIMITIVES["reactive-text"] = reactiveText; if (typeof reactiveText === "function") PRIMITIVES["reactive-text"] = reactiveText;
PRIMITIVES["set-interval"] = setInterval_; PRIMITIVES["set-interval"] = setInterval_;
PRIMITIVES["clear-interval"] = clearInterval_; PRIMITIVES["clear-interval"] = clearInterval_;
PRIMITIVES["promise-then"] = promiseThen; PRIMITIVES["promise-then"] = promiseThen;
@@ -3493,35 +3624,35 @@ def public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_boot, has
elif has_orch: elif has_orch:
api_lines.append(' init: typeof engineInit === "function" ? engineInit : null,') api_lines.append(' init: typeof engineInit === "function" ? engineInit : null,')
if has_deps: if has_deps:
api_lines.append(' scanRefs: scanRefs,') api_lines.append(' scanRefs: typeof scanRefs === "function" ? scanRefs : null,')
api_lines.append(' scanComponentsFromSource: scanComponentsFromSource,') api_lines.append(' scanComponentsFromSource: typeof scanComponentsFromSource === "function" ? scanComponentsFromSource : null,')
api_lines.append(' transitiveDeps: transitiveDeps,') api_lines.append(' transitiveDeps: typeof transitiveDeps === "function" ? transitiveDeps : null,')
api_lines.append(' computeAllDeps: computeAllDeps,') api_lines.append(' computeAllDeps: typeof computeAllDeps === "function" ? computeAllDeps : null,')
api_lines.append(' componentsNeeded: componentsNeeded,') api_lines.append(' componentsNeeded: typeof componentsNeeded === "function" ? componentsNeeded : null,')
api_lines.append(' pageComponentBundle: pageComponentBundle,') api_lines.append(' pageComponentBundle: typeof pageComponentBundle === "function" ? pageComponentBundle : null,')
api_lines.append(' pageCssClasses: pageCssClasses,') api_lines.append(' pageCssClasses: typeof pageCssClasses === "function" ? pageCssClasses : null,')
api_lines.append(' scanIoRefs: scanIoRefs,') api_lines.append(' scanIoRefs: typeof scanIoRefs === "function" ? scanIoRefs : null,')
api_lines.append(' transitiveIoRefs: transitiveIoRefs,') api_lines.append(' transitiveIoRefs: typeof transitiveIoRefs === "function" ? transitiveIoRefs : null,')
api_lines.append(' computeAllIoRefs: computeAllIoRefs,') api_lines.append(' computeAllIoRefs: typeof computeAllIoRefs === "function" ? computeAllIoRefs : null,')
api_lines.append(' componentPure_p: componentPure_p,') api_lines.append(' componentPure_p: typeof componentPure_p === "function" ? componentPure_p : null,')
if has_page_helpers: if has_page_helpers:
api_lines.append(' categorizeSpecialForms: categorizeSpecialForms,') api_lines.append(' categorizeSpecialForms: typeof categorizeSpecialForms === "function" ? categorizeSpecialForms : null,')
api_lines.append(' buildReferenceData: buildReferenceData,') api_lines.append(' buildReferenceData: typeof buildReferenceData === "function" ? buildReferenceData : null,')
api_lines.append(' buildAttrDetail: buildAttrDetail,') api_lines.append(' buildAttrDetail: typeof buildAttrDetail === "function" ? buildAttrDetail : null,')
api_lines.append(' buildHeaderDetail: buildHeaderDetail,') api_lines.append(' buildHeaderDetail: typeof buildHeaderDetail === "function" ? buildHeaderDetail : null,')
api_lines.append(' buildEventDetail: buildEventDetail,') api_lines.append(' buildEventDetail: typeof buildEventDetail === "function" ? buildEventDetail : null,')
api_lines.append(' buildComponentSource: buildComponentSource,') api_lines.append(' buildComponentSource: typeof buildComponentSource === "function" ? buildComponentSource : null,')
api_lines.append(' buildBundleAnalysis: buildBundleAnalysis,') api_lines.append(' buildBundleAnalysis: typeof buildBundleAnalysis === "function" ? buildBundleAnalysis : null,')
api_lines.append(' buildRoutingAnalysis: buildRoutingAnalysis,') api_lines.append(' buildRoutingAnalysis: typeof buildRoutingAnalysis === "function" ? buildRoutingAnalysis : null,')
api_lines.append(' buildAffinityAnalysis: buildAffinityAnalysis,') api_lines.append(' buildAffinityAnalysis: typeof buildAffinityAnalysis === "function" ? buildAffinityAnalysis : null,')
if has_router: if has_router:
api_lines.append(' splitPathSegments: splitPathSegments,') api_lines.append(' splitPathSegments: typeof splitPathSegments === "function" ? splitPathSegments : null,')
api_lines.append(' parseRoutePattern: parseRoutePattern,') api_lines.append(' parseRoutePattern: typeof parseRoutePattern === "function" ? parseRoutePattern : null,')
api_lines.append(' matchRoute: matchRoute,') api_lines.append(' matchRoute: typeof matchRoute === "function" ? matchRoute : null,')
api_lines.append(' findMatchingRoute: findMatchingRoute,') api_lines.append(' findMatchingRoute: typeof findMatchingRoute === "function" ? findMatchingRoute : null,')
api_lines.append(' urlToExpr: urlToExpr,') api_lines.append(' urlToExpr: typeof urlToExpr === "function" ? urlToExpr : null,')
api_lines.append(' autoQuoteUnknowns: autoQuoteUnknowns,') api_lines.append(' autoQuoteUnknowns: typeof autoQuoteUnknowns === "function" ? autoQuoteUnknowns : null,')
api_lines.append(' prepareUrlExpr: prepareUrlExpr,') api_lines.append(' prepareUrlExpr: typeof prepareUrlExpr === "function" ? prepareUrlExpr : null,')
if has_dom: if has_dom:
api_lines.append(' registerIo: typeof registerIoPrimitive === "function" ? registerIoPrimitive : null,') api_lines.append(' registerIo: typeof registerIoPrimitive === "function" ? registerIoPrimitive : null,')
@@ -3529,21 +3660,21 @@ def public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_boot, has
api_lines.append(' asyncRender: typeof asyncSxRenderWithEnv === "function" ? asyncSxRenderWithEnv : null,') api_lines.append(' asyncRender: typeof asyncSxRenderWithEnv === "function" ? asyncSxRenderWithEnv : null,')
api_lines.append(' asyncRenderToDom: typeof asyncRenderToDom === "function" ? asyncRenderToDom : null,') api_lines.append(' asyncRenderToDom: typeof asyncRenderToDom === "function" ? asyncRenderToDom : null,')
if has_signals: if has_signals:
api_lines.append(' signal: signal,') api_lines.append(' signal: typeof signal === "function" ? signal : null,')
api_lines.append(' deref: deref,') api_lines.append(' deref: typeof deref === "function" ? deref : null,')
api_lines.append(' reset: reset_b,') api_lines.append(' reset: typeof reset_b === "function" ? reset_b : null,')
api_lines.append(' swap: swap_b,') api_lines.append(' swap: typeof swap_b === "function" ? swap_b : null,')
api_lines.append(' computed: computed,') api_lines.append(' computed: typeof computed === "function" ? computed : null,')
api_lines.append(' effect: effect,') api_lines.append(' effect: typeof effect === "function" ? effect : null,')
api_lines.append(' batch: batch,') api_lines.append(' batch: typeof batch === "function" ? batch : null,')
api_lines.append(' isSignal: isSignal,') api_lines.append(' isSignal: typeof isSignal === "function" ? isSignal : null,')
api_lines.append(' makeSignal: makeSignal,') api_lines.append(' makeSignal: typeof makeSignal === "function" ? makeSignal : null,')
api_lines.append(' defStore: defStore,') api_lines.append(' defStore: typeof defStore === "function" ? defStore : null,')
api_lines.append(' useStore: useStore,') api_lines.append(' useStore: typeof useStore === "function" ? useStore : null,')
api_lines.append(' clearStores: clearStores,') api_lines.append(' clearStores: typeof clearStores === "function" ? clearStores : null,')
api_lines.append(' emitEvent: emitEvent,') api_lines.append(' emitEvent: typeof emitEvent === "function" ? emitEvent : null,')
api_lines.append(' onEvent: onEvent,') api_lines.append(' onEvent: typeof onEvent === "function" ? onEvent : null,')
api_lines.append(' bridgeEvent: bridgeEvent,') api_lines.append(' bridgeEvent: typeof bridgeEvent === "function" ? bridgeEvent : null,')
api_lines.append(' makeSpread: makeSpread,') api_lines.append(' makeSpread: makeSpread,')
api_lines.append(' isSpread: isSpread,') api_lines.append(' isSpread: isSpread,')
api_lines.append(' spreadAttrs: spreadAttrs,') api_lines.append(' spreadAttrs: spreadAttrs,')

View File

@@ -293,6 +293,8 @@ env["pop-suite"] = function() {
return null; return null;
}; };
env["test-allowed?"] = function(name) { return true; };
// Load test framework // Load test framework
const projectDir = path.join(__dirname, "..", ".."); const projectDir = path.join(__dirname, "..", "..");
const specTests = path.join(projectDir, "spec", "tests"); const specTests = path.join(projectDir, "spec", "tests");

View File

@@ -16,6 +16,13 @@
if (a === b) return true; if (a === b) return true;
if (a && b && a._sym && b._sym) return a.name === b.name; if (a && b && a._sym && b._sym) return a.name === b.name;
if (a && b && a._kw && b._kw) return a.name === b.name; if (a && b && a._kw && b._kw) return a.name === b.name;
if (a && b && a._vector && b._vector) {
if (a.arr.length !== b.arr.length) return false;
for (var _i = 0; _i < a.arr.length; _i++) {
if (!sxEq(a.arr[_i], b.arr[_i])) return false;
}
return true;
}
return false; return false;
} }
@@ -24,7 +31,7 @@
// ========================================================================= // =========================================================================
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } }); var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
var SX_VERSION = "2026-04-05T11:01:51Z"; var SX_VERSION = "2026-04-26T10:01:22Z";
function isNil(x) { return x === NIL || x === null || x === undefined; } function isNil(x) { return x === NIL || x === null || x === undefined; }
function isSxTruthy(x) { return x !== false && !isNil(x); } function isSxTruthy(x) { return x !== false && !isNil(x); }
@@ -82,6 +89,44 @@
function SxSpread(attrs) { this.attrs = attrs || {}; } function SxSpread(attrs) { this.attrs = attrs || {}; }
SxSpread.prototype._spread = true; SxSpread.prototype._spread = true;
function SxVector(arr) { this.arr = arr || []; }
SxVector.prototype._vector = true;
var _paramUidCounter = 0;
function SxParameter(defaultVal, converter) {
this._uid = ++_paramUidCounter;
this._default = defaultVal;
this._converter = converter || null;
}
SxParameter.prototype._parameter = true;
function parameter_p(x) { return x != null && x._parameter === true; }
function parameterUid(p) { return p._uid; }
function parameterDefault(p) { return p._default; }
function SxCallccContinuation(capturedKont) { this._captured = capturedKont; }
SxCallccContinuation.prototype._callcc = true;
function makeCallccContinuation(kont) { return new SxCallccContinuation(kont); }
function callccContinuation_p(x) { return x != null && x._callcc === true; }
function callccContinuationData(x) { return x._captured; }
function evalError_p(v) {
return v != null && typeof v === "object" && v["__eval_error__"] === true;
}
function sxApplyCek(f, args) {
try {
return typeof f === "function" ? f.apply(null, args) : f;
} catch (e) {
if (e && e._perform_request) throw e;
if (e && e._cek_suspend) throw e;
return {"__eval_error__": true, "message": e && e.message ? e.message : String(e)};
}
}
var _JIT_SKIP_SENTINEL = {"__jit_skip": true};
function jitTryCall(f, args) { return _JIT_SKIP_SENTINEL; }
function jitSkip_p(v) { return v === _JIT_SKIP_SENTINEL || (v != null && v["__jit_skip"] === true); }
var _scopeStacks = {}; var _scopeStacks = {};
function isSym(x) { return x != null && x._sym === true; } function isSym(x) { return x != null && x._sym === true; }
@@ -122,6 +167,7 @@
if (x._macro) return "macro"; if (x._macro) return "macro";
if (x._raw) return "raw-html"; if (x._raw) return "raw-html";
if (x._sx_expr) return "sx-expr"; if (x._sx_expr) return "sx-expr";
if (x._vector) return "vector";
if (typeof Node !== "undefined" && x instanceof Node) return "dom-node"; if (typeof Node !== "undefined" && x instanceof Node) return "dom-node";
if (Array.isArray(x)) return "list"; if (Array.isArray(x)) return "list";
if (typeof x === "object") return "dict"; if (typeof x === "object") return "dict";
@@ -288,6 +334,12 @@
// Placeholder — overridden by transpiled version from render.sx // Placeholder — overridden by transpiled version from render.sx
function isRenderExpr(expr) { return false; } function isRenderExpr(expr) { return false; }
// Last error continuation — saved when a raise goes unhandled, for post-mortem inspection.
var _lastErrorKont_ = null;
// hostError — throw a host-level error that propagates out of cekRun.
function hostError(msg) { throw new Error(typeof msg === "string" ? msg : inspect(msg)); }
// Render dispatch — call the active adapter's render function. // Render dispatch — call the active adapter's render function.
// Set by each adapter when loaded; defaults to identity (no rendering). // Set by each adapter when loaded; defaults to identity (no rendering).
var _renderExprFn = null; var _renderExprFn = null;
@@ -390,7 +442,20 @@
PRIMITIVES["split"] = function(s, sep) { return String(s).split(sep || " "); }; PRIMITIVES["split"] = function(s, sep) { return String(s).split(sep || " "); };
PRIMITIVES["join"] = function(sep, coll) { return coll.join(sep); }; PRIMITIVES["join"] = function(sep, coll) { return coll.join(sep); };
PRIMITIVES["replace"] = function(s, old, nw) { return s.split(old).join(nw); }; PRIMITIVES["replace"] = function(s, old, nw) { return s.split(old).join(nw); };
PRIMITIVES["index-of"] = function(s, needle, from) { return String(s).indexOf(needle, from || 0); }; PRIMITIVES["index-of"] = function(s, needle, from) {
if (Array.isArray(s)) {
var _start = from || 0;
for (var _i = _start; _i < s.length; _i++) {
var _a = s[_i];
if (_a === needle) return _i;
if (_a != null && needle != null && typeof _a === "object" && typeof needle === "object") {
if ((_a._sym && needle._sym || _a._kw && needle._kw) && _a.name === needle.name) return _i;
}
}
return NIL;
}
return String(s).indexOf(needle, from || 0);
};
PRIMITIVES["starts-with?"] = function(s, p) { return String(s).indexOf(p) === 0; }; PRIMITIVES["starts-with?"] = function(s, p) { return String(s).indexOf(p) === 0; };
PRIMITIVES["ends-with?"] = function(s, p) { var str = String(s); return str.indexOf(p, str.length - p.length) !== -1; }; PRIMITIVES["ends-with?"] = function(s, p) { var str = String(s); return str.indexOf(p, str.length - p.length) !== -1; };
PRIMITIVES["slice"] = function(c, a, b) { if (!c || typeof c.slice !== "function") { console.error("[sx-debug] slice called on non-sliceable:", typeof c, c, "a=", a, "b=", b, new Error().stack); return []; } return b !== undefined ? c.slice(a, b) : c.slice(a); }; PRIMITIVES["slice"] = function(c, a, b) { if (!c || typeof c.slice !== "function") { console.error("[sx-debug] slice called on non-sliceable:", typeof c, c, "a=", a, "b=", b, new Error().stack); return []; } return b !== undefined ? c.slice(a, b) : c.slice(a); };
@@ -470,6 +535,38 @@
}; };
// core.vectors — R7RS mutable fixed-size arrays
PRIMITIVES["make-vector"] = function(n, fill) {
var arr = new Array(n);
var f = (fill !== undefined) ? fill : NIL;
for (var i = 0; i < n; i++) arr[i] = f;
return new SxVector(arr);
};
PRIMITIVES["vector"] = function() {
return new SxVector(Array.prototype.slice.call(arguments));
};
PRIMITIVES["vector?"] = function(x) { return x != null && x._vector === true; };
PRIMITIVES["vector-length"] = function(v) { return v.arr.length; };
PRIMITIVES["vector-ref"] = function(v, i) {
if (i < 0 || i >= v.arr.length) throw new Error("vector-ref: index " + i + " out of bounds (length " + v.arr.length + ")");
return v.arr[i];
};
PRIMITIVES["vector-set!"] = function(v, i, val) {
if (i < 0 || i >= v.arr.length) throw new Error("vector-set!: index " + i + " out of bounds (length " + v.arr.length + ")");
v.arr[i] = val; return NIL;
};
PRIMITIVES["vector->list"] = function(v) { return v.arr.slice(); };
PRIMITIVES["list->vector"] = function(l) { return new SxVector(l.slice()); };
PRIMITIVES["vector-fill!"] = function(v, val) {
for (var i = 0; i < v.arr.length; i++) v.arr[i] = val; return NIL;
};
PRIMITIVES["vector-copy"] = function(v, start, end) {
var s = (start !== undefined) ? start : 0;
var e = (end !== undefined) ? Math.min(end, v.arr.length) : v.arr.length;
return new SxVector(v.arr.slice(s, e));
};
// stdlib.format // stdlib.format
PRIMITIVES["format-decimal"] = function(v, p) { return Number(v).toFixed(p || 2); }; PRIMITIVES["format-decimal"] = function(v, p) { return Number(v).toFixed(p || 2); };
PRIMITIVES["parse-int"] = function(v, d) { var n = parseInt(v, 10); return isNaN(n) ? (d || 0) : n; }; PRIMITIVES["parse-int"] = function(v, d) { var n = parseInt(v, 10); return isNaN(n) ? (d || 0) : n; };
@@ -1029,6 +1126,10 @@ PRIMITIVES["make-let-frame"] = makeLetFrame;
var makeDefineFrame = function(name, env, hasEffects, effectList) { return {"env": env, "effect-list": effectList, "has-effects": hasEffects, "type": "define", "name": name}; }; var makeDefineFrame = function(name, env, hasEffects, effectList) { return {"env": env, "effect-list": effectList, "has-effects": hasEffects, "type": "define", "name": name}; };
PRIMITIVES["make-define-frame"] = makeDefineFrame; PRIMITIVES["make-define-frame"] = makeDefineFrame;
// make-define-foreign-frame
var makeDefineForeignFrame = function(name, spec, env) { return {"spec": spec, "env": env, "type": "define-foreign", "name": name}; };
PRIMITIVES["make-define-foreign-frame"] = makeDefineForeignFrame;
// make-set-frame // make-set-frame
var makeSetFrame = function(name, env) { return {"env": env, "type": "set", "name": name}; }; var makeSetFrame = function(name, env) { return {"env": env, "type": "set", "name": name}; };
PRIMITIVES["make-set-frame"] = makeSetFrame; PRIMITIVES["make-set-frame"] = makeSetFrame;
@@ -1321,6 +1422,18 @@ PRIMITIVES["*render-fn*"] = _renderFn;
var _bindTracking_ = NIL; var _bindTracking_ = NIL;
PRIMITIVES["*bind-tracking*"] = _bindTracking_; PRIMITIVES["*bind-tracking*"] = _bindTracking_;
// *provide-batch-depth*
var _provideBatchDepth_ = 0;
PRIMITIVES["*provide-batch-depth*"] = _provideBatchDepth_;
// *provide-batch-queue*
var _provideBatchQueue_ = [];
PRIMITIVES["*provide-batch-queue*"] = _provideBatchQueue_;
// *provide-subscribers*
var _provideSubscribers_ = {};
PRIMITIVES["*provide-subscribers*"] = _provideSubscribers_;
// *library-registry* // *library-registry*
var _libraryRegistry_ = {}; var _libraryRegistry_ = {};
PRIMITIVES["*library-registry*"] = _libraryRegistry_; PRIMITIVES["*library-registry*"] = _libraryRegistry_;
@@ -1361,6 +1474,132 @@ PRIMITIVES["io-lookup"] = ioLookup;
var ioNames = function() { return keys(_ioRegistry_); }; var ioNames = function() { return keys(_ioRegistry_); };
PRIMITIVES["io-names"] = ioNames; PRIMITIVES["io-names"] = ioNames;
// *foreign-registry*
var _foreignRegistry_ = {};
PRIMITIVES["*foreign-registry*"] = _foreignRegistry_;
// foreign-register!
var foreignRegister_b = function(name, spec) { return dictSet(_foreignRegistry_, name, spec); };
PRIMITIVES["foreign-register!"] = foreignRegister_b;
// foreign-registered?
var foreignRegistered_p = function(name) { return dictHas(_foreignRegistry_, name); };
PRIMITIVES["foreign-registered?"] = foreignRegistered_p;
// foreign-lookup
var foreignLookup = function(name) { return get(_foreignRegistry_, name); };
PRIMITIVES["foreign-lookup"] = foreignLookup;
// foreign-names
var foreignNames = function() { return keys(_foreignRegistry_); };
PRIMITIVES["foreign-names"] = foreignNames;
// foreign-parse-params
var foreignParseParams = function(paramList) { return (function() {
var result = [];
var i = 0;
var items = (isSxTruthy(isList(paramList)) ? paramList : []);
return foreignParseParamsLoop(items, result);
})(); };
PRIMITIVES["foreign-parse-params"] = foreignParseParams;
// foreign-parse-kwargs!
var foreignParseKwargs_b = function(spec, remaining) { return (isSxTruthy((isSxTruthy(!isSxTruthy(isEmpty(remaining))) && isSxTruthy((len(remaining) >= 2)) && keyword_p(first(remaining)))) ? (dictSet(spec, keywordName(first(remaining)), (function() {
var v = nth(remaining, 1);
return (isSxTruthy(keyword_p(v)) ? keywordName(v) : v);
})()), foreignParseKwargs_b(spec, rest(rest(remaining)))) : NIL); };
PRIMITIVES["foreign-parse-kwargs!"] = foreignParseKwargs_b;
// foreign-resolve-binding
var foreignResolveBinding = function(bindingStr) { return (function() {
var parts = split(bindingStr, ".");
return (isSxTruthy((len(parts) <= 1)) ? {"method": bindingStr, "object": NIL} : (function() {
var method = last(parts);
var obj = join(".", reverse(rest(reverse(parts))));
return {"method": method, "object": obj};
})());
})(); };
PRIMITIVES["foreign-resolve-binding"] = foreignResolveBinding;
// foreign-check-args
var foreignCheckArgs = function(name, params, args) { if (isSxTruthy((isSxTruthy(!isSxTruthy(isEmpty(params))) && (len(args) < len(params))))) {
error((String("foreign ") + String(name) + String(": expected ") + String(len(params)) + String(" args, got ") + String(len(args))));
}
return forEach(function(i) { return (function() {
var spec = nth(params, i);
var val = nth(args, i);
var expected = get(spec, "type");
return (isSxTruthy((isSxTruthy(!isSxTruthy(sxEq(expected, "any"))) && !isSxTruthy(valueMatchesType_p(val, expected)))) ? error((String("foreign ") + String(name) + String(": arg '") + String(get(spec, "name")) + String("' expected ") + String(expected) + String(", got ") + String(typeOf(val)))) : NIL);
})(); }, range(0, min(len(params), len(args)))); };
PRIMITIVES["foreign-check-args"] = foreignCheckArgs;
// foreign-build-lambda
var foreignBuildLambda = function(spec) { return (function() {
var name = get(spec, "name");
var mode = (isSxTruthy(dictHas(spec, "returns")) ? (function() {
var r = get(spec, "returns");
return (isSxTruthy(sxEq(r, "promise")) ? "async" : "sync");
})() : "sync");
return (isSxTruthy(sxEq(mode, "async")) ? [new Symbol("fn"), [new Symbol("&rest"), new Symbol("__ffi-args__")], [new Symbol("perform"), [new Symbol("foreign-dispatch"), [new Symbol("quote"), name], new Symbol("__ffi-args__")]]] : [new Symbol("fn"), [new Symbol("&rest"), new Symbol("__ffi-args__")], [new Symbol("foreign-dispatch"), [new Symbol("quote"), name], new Symbol("__ffi-args__")]]);
})(); };
PRIMITIVES["foreign-build-lambda"] = foreignBuildLambda;
// sf-define-foreign
var sfDefineForeign = function(args, env) { return (function() {
var name = (isSxTruthy(symbol_p(first(args))) ? symbolName(first(args)) : first(args));
var paramList = nth(args, 1);
var spec = {};
spec["name"] = name;
spec["params"] = foreignParseParams(paramList);
foreignParseKwargs_b(spec, rest(rest(args)));
foreignRegister_b(name, spec);
return spec;
})(); };
PRIMITIVES["sf-define-foreign"] = sfDefineForeign;
// step-sf-define-foreign
var stepSfDefineForeign = function(args, env, kont) { return (function() {
var spec = sfDefineForeign(args, env);
var name = (isSxTruthy(symbol_p(first(args))) ? symbolName(first(args)) : first(args));
var lambdaExpr = foreignBuildLambda(spec);
return makeCekState(lambdaExpr, env, kontPush(makeDefineForeignFrame(name, spec, env), kont));
})(); };
PRIMITIVES["step-sf-define-foreign"] = stepSfDefineForeign;
// foreign-dispatch
var foreignDispatch = function(name, args) { return (function() {
var spec = foreignLookup(name);
if (isSxTruthy(isNil(spec))) {
error((String("foreign-dispatch: unknown foreign function '") + String(name) + String("'")));
}
return (function() {
var params = get(spec, "params");
var binding = get(spec, "js");
foreignCheckArgs(name, (isSxTruthy(isNil(params)) ? [] : params), args);
return (isSxTruthy(isNil(binding)) ? error((String("foreign ") + String(name) + String(": no binding for current platform"))) : (function() {
var resolved = foreignResolveBinding(binding);
var objName = get(resolved, "object");
var method = get(resolved, "method");
return (isSxTruthy(isPrimitive("host-call")) ? (isSxTruthy(isNil(objName)) ? apply(getPrimitive("host-call"), concat([NIL, method], args)) : (function() {
var obj = (getPrimitive("host-global"))(objName);
return apply(getPrimitive("host-call"), concat([obj, method], args));
})()) : error((String("foreign ") + String(name) + String(": host-call not available on this platform"))));
})());
})();
})(); };
PRIMITIVES["foreign-dispatch"] = foreignDispatch;
// foreign-parse-params-loop
var foreignParseParamsLoop = function(items, acc) { return (isSxTruthy(isEmpty(items)) ? acc : (function() {
var item = first(items);
var restItems = rest(items);
return (isSxTruthy((isSxTruthy(!isSxTruthy(isEmpty(restItems))) && isSxTruthy(keyword_p(first(restItems))) && isSxTruthy(sxEq(keywordName(first(restItems)), "as")) && (len(restItems) >= 2))) ? foreignParseParamsLoop(rest(rest(restItems)), append(acc, [{"type": (function() {
var t = nth(restItems, 1);
return (isSxTruthy(keyword_p(t)) ? keywordName(t) : (String(t)));
})(), "name": (isSxTruthy(symbol_p(item)) ? symbolName(item) : (String(item)))}])) : foreignParseParamsLoop(restItems, append(acc, [{"type": "any", "name": (isSxTruthy(symbol_p(item)) ? symbolName(item) : (String(item)))}])));
})()); };
PRIMITIVES["foreign-parse-params-loop"] = foreignParseParamsLoop;
// step-sf-io // step-sf-io
var stepSfIo = function(args, env, kont) { return (function() { var stepSfIo = function(args, env, kont) { return (function() {
var name = first(args); var name = first(args);
@@ -1839,7 +2078,7 @@ PRIMITIVES["step-sf-let-match"] = stepSfLetMatch;
var args = rest(expr); var args = rest(expr);
return (isSxTruthy(!isSxTruthy(sxOr(sxEq(typeOf(head), "symbol"), sxEq(typeOf(head), "lambda"), sxEq(typeOf(head), "list")))) ? (isSxTruthy(isEmpty(expr)) ? makeCekValue([], env, kont) : makeCekState(first(expr), env, kontPush(makeMapFrame(NIL, rest(expr), [], env), kont))) : (isSxTruthy(sxEq(typeOf(head), "symbol")) ? (function() { return (isSxTruthy(!isSxTruthy(sxOr(sxEq(typeOf(head), "symbol"), sxEq(typeOf(head), "lambda"), sxEq(typeOf(head), "list")))) ? (isSxTruthy(isEmpty(expr)) ? makeCekValue([], env, kont) : makeCekState(first(expr), env, kontPush(makeMapFrame(NIL, rest(expr), [], env), kont))) : (isSxTruthy(sxEq(typeOf(head), "symbol")) ? (function() {
var name = symbolName(head); var name = symbolName(head);
return (function() { var _m = name; if (_m == "if") return stepSfIf(args, env, kont); if (_m == "when") return stepSfWhen(args, env, kont); if (_m == "cond") return stepSfCond(args, env, kont); if (_m == "case") return stepSfCase(args, env, kont); if (_m == "and") return stepSfAnd(args, env, kont); if (_m == "or") return stepSfOr(args, env, kont); if (_m == "let") return stepSfLet(args, env, kont); if (_m == "let*") return stepSfLet(args, env, kont); if (_m == "lambda") return stepSfLambda(args, env, kont); if (_m == "fn") return stepSfLambda(args, env, kont); if (_m == "define") return stepSfDefine(args, env, kont); if (_m == "defcomp") return makeCekValue(sfDefcomp(args, env), env, kont); if (_m == "defisland") return makeCekValue(sfDefisland(args, env), env, kont); if (_m == "defmacro") return makeCekValue(sfDefmacro(args, env), env, kont); if (_m == "defio") return makeCekValue(sfDefio(args, env), env, kont); if (_m == "io") return stepSfIo(args, env, kont); if (_m == "begin") return stepSfBegin(args, env, kont); if (_m == "do") return (isSxTruthy((isSxTruthy(!isSxTruthy(isEmpty(args))) && isSxTruthy(isList(first(args))) && isSxTruthy(!isSxTruthy(isEmpty(first(args)))) && isList(first(first(args))))) ? (function() { return (function() { var _m = name; if (_m == "if") return stepSfIf(args, env, kont); if (_m == "when") return stepSfWhen(args, env, kont); if (_m == "cond") return stepSfCond(args, env, kont); if (_m == "case") return stepSfCase(args, env, kont); if (_m == "and") return stepSfAnd(args, env, kont); if (_m == "or") return stepSfOr(args, env, kont); if (_m == "let") return stepSfLet(args, env, kont); if (_m == "let*") return stepSfLet(args, env, kont); if (_m == "lambda") return stepSfLambda(args, env, kont); if (_m == "fn") return stepSfLambda(args, env, kont); if (_m == "define") return stepSfDefine(args, env, kont); if (_m == "defcomp") return makeCekValue(sfDefcomp(args, env), env, kont); if (_m == "defisland") return makeCekValue(sfDefisland(args, env), env, kont); if (_m == "defmacro") return makeCekValue(sfDefmacro(args, env), env, kont); if (_m == "defio") return makeCekValue(sfDefio(args, env), env, kont); if (_m == "define-foreign") return stepSfDefineForeign(args, env, kont); if (_m == "io") return stepSfIo(args, env, kont); if (_m == "begin") return stepSfBegin(args, env, kont); if (_m == "do") return (isSxTruthy((isSxTruthy(!isSxTruthy(isEmpty(args))) && isSxTruthy(isList(first(args))) && isSxTruthy(!isSxTruthy(isEmpty(first(args)))) && isList(first(first(args))))) ? (function() {
var bindings = first(args); var bindings = first(args);
var testClause = nth(args, 1); var testClause = nth(args, 1);
var body = rest(rest(args)); var body = rest(rest(args));
@@ -1849,10 +2088,10 @@ PRIMITIVES["step-sf-let-match"] = stepSfLetMatch;
var test = first(testClause); var test = first(testClause);
var result = rest(testClause); var result = rest(testClause);
return stepEvalList(cons(new Symbol("let"), cons(new Symbol("__do-loop"), cons(map(function(b) { return [first(b), nth(b, 1)]; }, bindings), [cons(new Symbol("if"), cons(test, cons((isSxTruthy(isEmpty(result)) ? NIL : cons(new Symbol("begin"), result)), [cons(new Symbol("begin"), append(body, [cons(new Symbol("__do-loop"), steps)]))])))]))), env, kont); return stepEvalList(cons(new Symbol("let"), cons(new Symbol("__do-loop"), cons(map(function(b) { return [first(b), nth(b, 1)]; }, bindings), [cons(new Symbol("if"), cons(test, cons((isSxTruthy(isEmpty(result)) ? NIL : cons(new Symbol("begin"), result)), [cons(new Symbol("begin"), append(body, [cons(new Symbol("__do-loop"), steps)]))])))]))), env, kont);
})() : stepSfBegin(args, env, kont)); if (_m == "guard") return stepSfGuard(args, env, kont); if (_m == "quote") return makeCekValue((isSxTruthy(isEmpty(args)) ? NIL : first(args)), env, kont); if (_m == "quasiquote") return makeCekValue(qqExpand(first(args), env), env, kont); if (_m == "->") return stepSfThreadFirst(args, env, kont); if (_m == "->>") return stepSfThreadLast(args, env, kont); if (_m == "|>") return stepSfThreadLast(args, env, kont); if (_m == "as->") return stepSfThreadAs(args, env, kont); if (_m == "set!") return stepSfSet(args, env, kont); if (_m == "letrec") return stepSfLetrec(args, env, kont); if (_m == "reset") return stepSfReset(args, env, kont); if (_m == "shift") return stepSfShift(args, env, kont); if (_m == "deref") return stepSfDeref(args, env, kont); if (_m == "scope") return stepSfScope(args, env, kont); if (_m == "provide") return stepSfProvide(args, env, kont); if (_m == "peek") return stepSfPeek(args, env, kont); if (_m == "provide!") return stepSfProvide_b(args, env, kont); if (_m == "context") return stepSfContext(args, env, kont); if (_m == "bind") return stepSfBind(args, env, kont); if (_m == "emit!") return stepSfEmit(args, env, kont); if (_m == "emitted") return stepSfEmitted(args, env, kont); if (_m == "handler-bind") return stepSfHandlerBind(args, env, kont); if (_m == "restart-case") return stepSfRestartCase(args, env, kont); if (_m == "signal-condition") return stepSfSignal(args, env, kont); if (_m == "invoke-restart") return stepSfInvokeRestart(args, env, kont); if (_m == "match") return stepSfMatch(args, env, kont); if (_m == "let-match") return stepSfLetMatch(args, env, kont); if (_m == "dynamic-wind") return makeCekValue(sfDynamicWind(args, env), env, kont); if (_m == "map") return stepHoMap(args, env, kont); if (_m == "map-indexed") return stepHoMapIndexed(args, env, kont); if (_m == "filter") return stepHoFilter(args, env, kont); if (_m == "reduce") return stepHoReduce(args, env, kont); if (_m == "some") return stepHoSome(args, env, kont); if (_m == "every?") return stepHoEvery(args, env, kont); if (_m == "for-each") return stepHoForEach(args, env, kont); if (_m == "raise") return stepSfRaise(args, env, kont); if (_m == "raise-continuable") return makeCekState(first(args), env, kontPush(makeRaiseEvalFrame(env, true), kont)); if (_m == "call/cc") return stepSfCallcc(args, env, kont); if (_m == "call-with-current-continuation") return stepSfCallcc(args, env, kont); if (_m == "perform") return stepSfPerform(args, env, kont); if (_m == "define-library") return stepSfDefineLibrary(args, env, kont); if (_m == "import") return stepSfImport(args, env, kont); if (_m == "define-record-type") return makeCekValue(sfDefineRecordType(args, env), env, kont); if (_m == "define-protocol") return makeCekValue(sfDefineProtocol(args, env), env, kont); if (_m == "implement") return makeCekValue(sfImplement(args, env), env, kont); if (_m == "parameterize") return stepSfParameterize(args, env, kont); if (_m == "syntax-rules") return makeCekValue(sfSyntaxRules(args, env), env, kont); if (_m == "define-syntax") return stepSfDefine(args, env, kont); return (isSxTruthy(dictHas(_customSpecialForms, name)) ? makeCekValue((get(_customSpecialForms, name))(args, env), env, kont) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? (function() { })() : stepSfBegin(args, env, kont)); if (_m == "guard") return stepSfGuard(args, env, kont); if (_m == "quote") return makeCekValue((isSxTruthy(isEmpty(args)) ? NIL : first(args)), env, kont); if (_m == "quasiquote") return makeCekValue(qqExpand(first(args), env), env, kont); if (_m == "->") return stepSfThreadFirst(args, env, kont); if (_m == "->>") return stepSfThreadLast(args, env, kont); if (_m == "|>") return stepSfThreadLast(args, env, kont); if (_m == "as->") return stepSfThreadAs(args, env, kont); if (_m == "set!") return stepSfSet(args, env, kont); if (_m == "letrec") return stepSfLetrec(args, env, kont); if (_m == "reset") return stepSfReset(args, env, kont); if (_m == "shift") return stepSfShift(args, env, kont); if (_m == "deref") return stepSfDeref(args, env, kont); if (_m == "scope") return stepSfScope(args, env, kont); if (_m == "provide") return stepSfProvide(args, env, kont); if (_m == "peek") return stepSfPeek(args, env, kont); if (_m == "provide!") return stepSfProvide_b(args, env, kont); if (_m == "context") return stepSfContext(args, env, kont); if (_m == "bind") return stepSfBind(args, env, kont); if (_m == "emit!") return stepSfEmit(args, env, kont); if (_m == "emitted") return stepSfEmitted(args, env, kont); if (_m == "handler-bind") return stepSfHandlerBind(args, env, kont); if (_m == "restart-case") return stepSfRestartCase(args, env, kont); if (_m == "signal-condition") return stepSfSignal(args, env, kont); if (_m == "invoke-restart") return stepSfInvokeRestart(args, env, kont); if (_m == "match") return stepSfMatch(args, env, kont); if (_m == "let-match") return stepSfLetMatch(args, env, kont); if (_m == "dynamic-wind") return makeCekValue(sfDynamicWind(args, env), env, kont); if (_m == "map") return stepHoMap(args, env, kont); if (_m == "map-indexed") return stepHoMapIndexed(args, env, kont); if (_m == "filter") return stepHoFilter(args, env, kont); if (_m == "reduce") return stepHoReduce(args, env, kont); if (_m == "some") return stepHoSome(args, env, kont); if (_m == "every?") return stepHoEvery(args, env, kont); if (_m == "for-each") return stepHoForEach(args, env, kont); if (_m == "raise") return stepSfRaise(args, env, kont); if (_m == "raise-continuable") return makeCekState(first(args), env, kontPush(makeRaiseEvalFrame(env, true), kont)); if (_m == "call/cc") return stepSfCallcc(args, env, kont); if (_m == "call-with-current-continuation") return stepSfCallcc(args, env, kont); if (_m == "perform") return stepSfPerform(args, env, kont); if (_m == "define-library") return stepSfDefineLibrary(args, env, kont); if (_m == "import") return stepSfImport(args, env, kont); if (_m == "define-record-type") return makeCekValue(sfDefineRecordType(args, env), env, kont); if (_m == "define-protocol") return makeCekValue(sfDefineProtocol(args, env), env, kont); if (_m == "implement") return makeCekValue(sfImplement(args, env), env, kont); if (_m == "parameterize") return stepSfParameterize(args, env, kont); if (_m == "syntax-rules") return makeCekValue(sfSyntaxRules(args, env), env, kont); if (_m == "define-syntax") return stepSfDefine(args, env, kont); return (isSxTruthy((isSxTruthy(dictHas(_customSpecialForms, name)) && !isSxTruthy(envHas(env, name)))) ? makeCekValue((get(_customSpecialForms, name))(args, env), env, kont) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? (function() {
var mac = envGet(env, name); var mac = envGet(env, name);
return makeCekState(expandMacro(mac, args, env), env, kont); return makeCekState(expandMacro(mac, args, env), env, kont);
})() : (isSxTruthy((isSxTruthy(_renderCheck) && _renderCheck(expr, env))) ? makeCekValue(_renderFn(expr, env), env, kont) : stepEvalCall(head, args, env, kont)))); })(); })() : (isSxTruthy((isSxTruthy(_renderCheck) && isSxTruthy(!isSxTruthy(envHas(env, name))) && _renderCheck(expr, env))) ? makeCekValue(_renderFn(expr, env), env, kont) : stepEvalCall(head, args, env, kont)))); })();
})() : stepEvalCall(head, args, env, kont))); })() : stepEvalCall(head, args, env, kont)));
})(); }; })(); };
PRIMITIVES["step-eval-list"] = stepEvalList; PRIMITIVES["step-eval-list"] = stepEvalList;
@@ -1868,10 +2107,30 @@ PRIMITIVES["kont-extract-provides"] = kontExtractProvides;
// fire-provide-subscribers // fire-provide-subscribers
var fireProvideSubscribers = function(frame, kont) { return (function() { var fireProvideSubscribers = function(frame, kont) { return (function() {
var subs = get(frame, "subscribers"); var subs = get(frame, "subscribers");
return (isSxTruthy(!isSxTruthy(isEmpty(subs))) ? forEach(function(sub) { return cekCall(sub, [kont]); }, subs) : NIL); return (isSxTruthy(!isSxTruthy(isEmpty(subs))) ? (isSxTruthy((_provideBatchDepth_ > 0)) ? forEach(function(sub) { return (isSxTruthy(!isSxTruthy(contains(_provideBatchQueue_, sub))) ? append_b(_provideBatchQueue_, sub) : NIL); }, subs) : forEach(function(sub) { return cekCall(sub, [kont]); }, subs)) : NIL);
})(); }; })(); };
PRIMITIVES["fire-provide-subscribers"] = fireProvideSubscribers; PRIMITIVES["fire-provide-subscribers"] = fireProvideSubscribers;
// fire-provide-subscribers
var fireProvideSubscribers = function(name) { return (function() {
var subs = get(_provideSubscribers_, name);
return (isSxTruthy((isSxTruthy(subs) && !isSxTruthy(isEmpty(subs)))) ? (isSxTruthy((_provideBatchDepth_ > 0)) ? forEach(function(sub) { return (isSxTruthy(!isSxTruthy(contains(_provideBatchQueue_, sub))) ? append_b(_provideBatchQueue_, sub) : NIL); }, subs) : forEach(function(sub) { return cekCall(sub, [NIL]); }, subs)) : NIL);
})(); };
PRIMITIVES["fire-provide-subscribers"] = fireProvideSubscribers;
// batch-begin!
var batchBegin_b = function() { return (_provideBatchDepth_ = (_provideBatchDepth_ + 1)); };
PRIMITIVES["batch-begin!"] = batchBegin_b;
// batch-end!
var batchEnd_b = function() { _provideBatchDepth_ = (_provideBatchDepth_ - 1);
return (isSxTruthy(sxEq(_provideBatchDepth_, 0)) ? (function() {
var queue = _provideBatchQueue_;
_provideBatchQueue_ = [];
return forEach(function(sub) { return cekCall(sub, [NIL]); }, queue);
})() : NIL); };
PRIMITIVES["batch-end!"] = batchEnd_b;
// step-sf-bind // step-sf-bind
var stepSfBind = function(args, env, kont) { return (function() { var stepSfBind = function(args, env, kont) { return (function() {
var body = first(args); var body = first(args);
@@ -2011,7 +2270,7 @@ PRIMITIVES["sf-syntax-rules"] = sfSyntaxRules;
{ var _c = decls; for (var _i = 0; _i < _c.length; _i++) { var decl = _c[_i]; if (isSxTruthy((isSxTruthy(isList(decl)) && isSxTruthy(!isSxTruthy(isEmpty(decl))) && symbol_p(first(decl))))) { { var _c = decls; for (var _i = 0; _i < _c.length; _i++) { var decl = _c[_i]; if (isSxTruthy((isSxTruthy(isList(decl)) && isSxTruthy(!isSxTruthy(isEmpty(decl))) && symbol_p(first(decl))))) {
(function() { (function() {
var kind = symbolName(first(decl)); var kind = symbolName(first(decl));
return (isSxTruthy(sxEq(kind, "export")) ? (exports = append(exports, map(function(s) { return (isSxTruthy(symbol_p(s)) ? symbolName(s) : (String(s))); }, rest(decl)))) : (isSxTruthy(sxEq(kind, "begin")) ? (bodyForms = append(bodyForms, rest(decl))) : NIL)); return (isSxTruthy(sxEq(kind, "export")) ? (exports = append(exports, map(function(s) { return (isSxTruthy(symbol_p(s)) ? symbolName(s) : (String(s))); }, rest(decl)))) : (isSxTruthy(sxEq(kind, "import")) ? forEach(function(importSet) { return bindImportSet(importSet, libEnv); }, rest(decl)) : (isSxTruthy(sxEq(kind, "begin")) ? (bodyForms = append(bodyForms, rest(decl))) : NIL)));
})(); })();
} } } } } }
{ var _c = bodyForms; for (var _i = 0; _i < _c.length; _i++) { var form = _c[_i]; evalExpr(form, libEnv); } } { var _c = bodyForms; for (var _i = 0; _i < _c.length; _i++) { var form = _c[_i]; evalExpr(form, libEnv); } }
@@ -2407,10 +2666,10 @@ PRIMITIVES["step-sf-provide"] = stepSfProvide;
_bindTracking_.push(name); _bindTracking_.push(name);
} }
} }
return makeCekValue((isSxTruthy(frame) ? get(frame, "value") : (function() { return makeCekValue((function() {
var sv = scopePeek(name); var sv = scopePeek(name);
return (isSxTruthy(isNil(sv)) ? defaultVal : sv); return (isSxTruthy(isNil(sv)) ? (isSxTruthy(frame) ? get(frame, "value") : defaultVal) : sv);
})()), env, kont); })(), env, kont);
})(); }; })(); };
PRIMITIVES["step-sf-context"] = stepSfContext; PRIMITIVES["step-sf-context"] = stepSfContext;
@@ -2649,6 +2908,14 @@ PRIMITIVES["step-ho-for-each"] = stepHoForEach;
})(); })();
} }
return makeCekValue(value, fenv, restK); return makeCekValue(value, fenv, restK);
})(); if (_m == "define-foreign") return (function() {
var name = get(frame, "name");
var fenv = get(frame, "env");
if (isSxTruthy((isSxTruthy(isLambda(value)) && isNil(lambdaName(value))))) {
value.name = name;
}
envBind(fenv, name, value);
return makeCekValue(value, fenv, restK);
})(); if (_m == "set") return (function() { })(); if (_m == "set") return (function() {
var name = get(frame, "name"); var name = get(frame, "name");
var fenv = get(frame, "env"); var fenv = get(frame, "env");
@@ -2780,8 +3047,8 @@ PRIMITIVES["step-ho-for-each"] = stepHoForEach;
(function() { (function() {
var subscriber = function(fireKont) { return cekRun(makeCekState(body, fenv, [])); }; var subscriber = function(fireKont) { return cekRun(makeCekState(body, fenv, [])); };
return forEach(function(name) { return (function() { return forEach(function(name) { return (function() {
var pf = kontFindProvide(restK, name); var existing = get(_provideSubscribers_, name);
return (isSxTruthy(pf) ? dictSet(pf, "subscribers", append(get(pf, "subscribers"), [subscriber])) : NIL); return dictSet(_provideSubscribers_, name, append((isSxTruthy(existing) ? existing : []), [subscriber]));
})(); }, tracked); })(); }, tracked);
})(); })();
return makeCekValue(value, fenv, restK); return makeCekValue(value, fenv, restK);
@@ -2789,16 +3056,18 @@ PRIMITIVES["step-ho-for-each"] = stepHoForEach;
var name = get(frame, "name"); var name = get(frame, "name");
var fenv = get(frame, "env"); var fenv = get(frame, "env");
var target = kontFindProvide(restK, name); var target = kontFindProvide(restK, name);
return (isSxTruthy(target) ? (function() { return (function() {
var oldVal = get(target, "value"); var oldVal = (isSxTruthy(target) ? get(target, "value") : scopePeek(name));
if (isSxTruthy(target)) {
target["value"] = value; target["value"] = value;
}
scopePop(name); scopePop(name);
scopePush(name, value); scopePush(name, value);
if (isSxTruthy(!isSxTruthy(sxEq(oldVal, value)))) { if (isSxTruthy(!isSxTruthy(sxEq(oldVal, value)))) {
fireProvideSubscribers(target, restK); fireProvideSubscribers(name);
} }
return makeCekValue(value, fenv, restK); return makeCekValue(value, fenv, restK);
})() : (isSxTruthy(envHas(fenv, "provide-set!")) ? (apply(envGet(fenv, "provide-set!"), [name, value]), makeCekValue(value, fenv, restK)) : makeCekValue(NIL, fenv, restK))); })();
})(); if (_m == "scope-acc") return (function() { })(); if (_m == "scope-acc") return (function() {
var remaining = get(frame, "remaining"); var remaining = get(frame, "remaining");
var fenv = get(frame, "env"); var fenv = get(frame, "env");
@@ -2936,7 +3205,10 @@ PRIMITIVES["step-continue"] = stepContinue;
return makeCekValue(result, env, kont); return makeCekValue(result, env, kont);
})(); })();
})(); })();
})() : (isSxTruthy((isSxTruthy(isCallable(f)) && isSxTruthy(!isSxTruthy(isLambda(f))) && isSxTruthy(!isSxTruthy(isComponent(f))) && !isSxTruthy(isIsland(f)))) ? makeCekValue(apply(f, args), env, kont) : (isSxTruthy(isLambda(f)) ? (function() { })() : (isSxTruthy((isSxTruthy(isCallable(f)) && isSxTruthy(!isSxTruthy(isLambda(f))) && isSxTruthy(!isSxTruthy(isComponent(f))) && !isSxTruthy(isIsland(f)))) ? (function() {
var result = sxApplyCek(f, args);
return (isSxTruthy(evalError_p(result)) ? makeCekValue(get(result, "message"), env, kontPush(makeRaiseEvalFrame(env, false), kont)) : (isSxTruthy((isSxTruthy(isDict(result)) && get(result, "__vm_suspended"))) ? makeCekSuspended(get(result, "request"), env, kontPush(makeVmResumeFrame(get(result, "resume"), env), kont)) : makeCekValue(result, env, kont)));
})() : (isSxTruthy(isLambda(f)) ? (function() {
var params = lambdaParams(f); var params = lambdaParams(f);
var local = envMerge(lambdaClosure(f), env); var local = envMerge(lambdaClosure(f), env);
if (isSxTruthy(!isSxTruthy(bindLambdaParams(params, args, local)))) { if (isSxTruthy(!isSxTruthy(bindLambdaParams(params, args, local)))) {
@@ -2948,7 +3220,7 @@ PRIMITIVES["step-continue"] = stepContinue;
} }
return (function() { return (function() {
var jitResult = jitTryCall(f, args); var jitResult = jitTryCall(f, args);
return (isSxTruthy(isNil(jitResult)) ? makeCekState(lambdaBody(f), local, kont) : (isSxTruthy((isSxTruthy(isDict(jitResult)) && get(jitResult, "__vm_suspended"))) ? makeCekSuspended(get(jitResult, "request"), env, kontPush(makeVmResumeFrame(get(jitResult, "resume"), env), kont)) : makeCekValue(jitResult, local, kont))); return (isSxTruthy(jitSkip_p(jitResult)) ? makeCekState(lambdaBody(f), local, kont) : (isSxTruthy((isSxTruthy(isDict(jitResult)) && get(jitResult, "__vm_suspended"))) ? makeCekSuspended(get(jitResult, "request"), env, kontPush(makeVmResumeFrame(get(jitResult, "resume"), env), kont)) : makeCekValue(jitResult, local, kont)));
})(); })();
})() : (isSxTruthy(sxOr(isComponent(f), isIsland(f))) ? (function() { })() : (isSxTruthy(sxOr(isComponent(f), isIsland(f))) ? (function() {
var parsed = parseKeywordArgs(rawArgs, env); var parsed = parseKeywordArgs(rawArgs, env);
@@ -3350,10 +3622,16 @@ PRIMITIVES["serialize"] = serialize;
// === Transpiled from lib/dom (DOM library) === // === Transpiled from lib/dom (DOM library) ===
// dom-visible?
var domVisible_p = function(el) { return (isSxTruthy(el) ? !isSxTruthy(sxEq(hostGet(hostGet(el, "style"), "display"), "none")) : false); };
PRIMITIVES["dom-visible?"] = domVisible_p;
// === Transpiled from lib/browser (browser API library) === // === Transpiled from lib/browser (browser API library) ===
// json-stringify
var jsonStringify = function(v) { return hostCall(hostGlobal("JSON"), "stringify", v); };
PRIMITIVES["json-stringify"] = jsonStringify;
// === Transpiled from adapter-dom === // === Transpiled from adapter-dom ===
@@ -3524,6 +3802,7 @@ PRIMITIVES["process-page-scripts"] = processPageScripts;
// sx-hydrate-islands // sx-hydrate-islands
var sxHydrateIslands = function(root) { return (function() { var sxHydrateIslands = function(root) { return (function() {
var els = domQueryAll(sxOr(root, domBody()), "[data-sx-island]"); var els = domQueryAll(sxOr(root, domBody()), "[data-sx-island]");
preloadIslandDefs();
logInfo((String("sx-hydrate-islands: ") + String(len(els)) + String(" island(s) in ") + String((isSxTruthy(root) ? "subtree" : "document")))); logInfo((String("sx-hydrate-islands: ") + String(len(els)) + String(" island(s) in ") + String((isSxTruthy(root) ? "subtree" : "document"))));
return forEach(function(el) { return (isSxTruthy(isProcessed(el, "island-hydrated")) ? logInfo((String(" skip (already hydrated): ") + String(domGetAttr(el, "data-sx-island")))) : (logInfo((String(" hydrating: ") + String(domGetAttr(el, "data-sx-island")))), markProcessed(el, "island-hydrated"), hydrateIsland(el))); }, els); return forEach(function(el) { return (isSxTruthy(isProcessed(el, "island-hydrated")) ? logInfo((String(" skip (already hydrated): ") + String(domGetAttr(el, "data-sx-island")))) : (logInfo((String(" hydrating: ") + String(domGetAttr(el, "data-sx-island")))), markProcessed(el, "island-hydrated"), hydrateIsland(el))); }, els);
})(); }; })(); };
@@ -3537,26 +3816,34 @@ PRIMITIVES["sx-hydrate-islands"] = sxHydrateIslands;
var compName = (String("~") + String(name)); var compName = (String("~") + String(name));
var env = getRenderEnv(NIL); var env = getRenderEnv(NIL);
return (function() { return (function() {
var comp = envGet(env, compName); var comp = envGet(globalEnv(), compName);
return (isSxTruthy(!isSxTruthy(sxOr(isComponent(comp), isIsland(comp)))) ? logWarn((String("hydrate-island: unknown island ") + String(compName))) : (function() { return (isSxTruthy(!isSxTruthy(sxOr(isComponent(comp), isIsland(comp)))) ? logWarn((String("hydrate-island: unknown island ") + String(compName))) : (function() {
var kwargs = sxOr(first(sxParse(stateSx)), {}); var kwargs = sxOr(first(sxParse(stateSx)), {});
var disposers = []; var disposers = [];
var local = envMerge(componentClosure(comp), env); var local = envMerge(componentClosure(comp), env);
{ var _c = componentParams(comp); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; envBind(local, p, (isSxTruthy(dictHas(kwargs, p)) ? dictGet(kwargs, p) : NIL)); } } { var _c = componentParams(comp); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; envBind(local, p, (isSxTruthy(dictHas(kwargs, p)) ? dictGet(kwargs, p) : NIL)); } }
return (function() { return (function() {
var bodyDom = cekTry(function() { return withIslandScope(function(disposable) { return append_b(disposers, disposable); }, function() { return renderToDom(componentBody(comp), local, NIL); }); }, function(err) { logWarn((String("hydrate-island FAILED: ") + String(compName) + String(" — ") + String(err))); var cursor = {["parent"]: el, ["index"]: 0};
hostCall(el, "replaceChildren");
scopePush("sx-hydrating", NIL);
cekTry(function() { return withIslandScope(function(disposable) { return append_b(disposers, disposable); }, function() { return (function() {
var bodyDom = renderToDom(componentBody(comp), local, NIL);
return (isSxTruthy(bodyDom) ? domAppend(el, bodyDom) : NIL);
})(); }); }, function(err) { scopePop("sx-hydrating");
logWarn((String("hydrate fallback: ") + String(compName) + String(" — ") + String(err)));
return (function() { return (function() {
var errorEl = domCreateElement("div", NIL); var fallback = cekTry(function() { return withIslandScope(function(d) { return append_b(disposers, d); }, function() { return renderToDom(componentBody(comp), local, NIL); }); }, function(err2) { return (function() {
domSetAttr(errorEl, "class", "sx-island-error"); var e = domCreateElement("div", NIL);
domSetAttr(errorEl, "style", "padding:8px;margin:4px 0;border:1px solid #ef4444;border-radius:4px;background:#fef2f2;color:#b91c1c;font-family:monospace;font-size:12px;white-space:pre-wrap"); domSetTextContent(e, (String("Island error: ") + String(compName) + String("\n") + String(err2)));
domSetTextContent(errorEl, (String("Island error: ") + String(compName) + String("\n") + String(err))); return e;
return errorEl;
})(); }); })(); });
domSetTextContent(el, ""); hostCall(el, "replaceChildren", fallback);
domAppend(el, bodyDom); return NIL;
})(); });
scopePop("sx-hydrating");
domSetData(el, "sx-disposers", disposers); domSetData(el, "sx-disposers", disposers);
setTimeout_(function() { return processElements(el); }, 0); setTimeout_(function() { return processElements(el); }, 0);
return logInfo((String("hydrated island: ") + String(compName) + String(" (") + String(len(disposers)) + String(" disposers)"))); return logInfo((String("hydrated island: ~") + String(compName) + String(" (") + String(len(disposers)) + String(" disposers)")));
})(); })();
})()); })());
})(); })();
@@ -3656,6 +3943,18 @@ PRIMITIVES["boot-init"] = bootInit;
// Core primitives that require native JS (cannot be expressed via FFI) // Core primitives that require native JS (cannot be expressed via FFI)
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
PRIMITIVES["error"] = function(msg) { throw new Error(msg); }; PRIMITIVES["error"] = function(msg) { throw new Error(msg); };
PRIMITIVES["host-error"] = function(msg) { throw new Error(typeof msg === "string" ? msg : inspect(msg)); };
PRIMITIVES["try-catch"] = function(tryFn, catchFn) {
try {
return cekRun(continueWithCall(tryFn, [], makeEnv(), [], []));
} catch(e) {
var msg = e && e.message ? e.message : String(e);
return cekRun(continueWithCall(catchFn, [msg], makeEnv(), [msg], []));
}
};
PRIMITIVES["without-io-hook"] = function(thunk) {
return cekRun(continueWithCall(thunk, [], makeEnv(), [], []));
};
PRIMITIVES["sort"] = function(lst) { PRIMITIVES["sort"] = function(lst) {
if (!Array.isArray(lst)) return lst; if (!Array.isArray(lst)) return lst;
return lst.slice().sort(function(a, b) { return lst.slice().sort(function(a, b) {
@@ -3723,7 +4022,7 @@ PRIMITIVES["boot-init"] = bootInit;
PRIMITIVES["dom-tag-name"] = domTagName; PRIMITIVES["dom-tag-name"] = domTagName;
PRIMITIVES["dom-get-prop"] = domGetProp; PRIMITIVES["dom-get-prop"] = domGetProp;
PRIMITIVES["dom-set-prop"] = domSetProp; PRIMITIVES["dom-set-prop"] = domSetProp;
PRIMITIVES["reactive-text"] = reactiveText; if (typeof reactiveText === "function") PRIMITIVES["reactive-text"] = reactiveText;
PRIMITIVES["set-interval"] = setInterval_; PRIMITIVES["set-interval"] = setInterval_;
PRIMITIVES["clear-interval"] = clearInterval_; PRIMITIVES["clear-interval"] = clearInterval_;
PRIMITIVES["promise-then"] = promiseThen; PRIMITIVES["promise-then"] = promiseThen;
@@ -3807,6 +4106,13 @@ PRIMITIVES["boot-init"] = bootInit;
PRIMITIVES["lambda-name"] = lambdaName; PRIMITIVES["lambda-name"] = lambdaName;
PRIMITIVES["component?"] = isComponent; PRIMITIVES["component?"] = isComponent;
PRIMITIVES["island?"] = isIsland; PRIMITIVES["island?"] = isIsland;
PRIMITIVES["parameter?"] = parameter_p;
PRIMITIVES["parameter-uid"] = parameterUid;
PRIMITIVES["parameter-default"] = parameterDefault;
PRIMITIVES["make-parameter"] = function(defaultVal, converter) {
var p = new SxParameter(defaultVal, converter || null);
return p;
};
PRIMITIVES["make-symbol"] = function(n) { return new Symbol(n); }; PRIMITIVES["make-symbol"] = function(n) { return new Symbol(n); };
PRIMITIVES["is-html-tag?"] = function(n) { return HTML_TAGS.indexOf(n) >= 0; }; PRIMITIVES["is-html-tag?"] = function(n) { return HTML_TAGS.indexOf(n) >= 0; };
function makeEnv() { return merge(componentEnv, PRIMITIVES); } function makeEnv() { return merge(componentEnv, PRIMITIVES); }
@@ -3997,7 +4303,7 @@ PRIMITIVES["boot-init"] = bootInit;
} }
function domDispatch(el, name, detail) { function domDispatch(el, name, detail) {
if (!_hasDom || !el) return false; if (!_hasDom || !el || typeof el.dispatchEvent !== "function") return false;
var evt = new CustomEvent(name, { bubbles: true, cancelable: true, detail: detail || {} }); var evt = new CustomEvent(name, { bubbles: true, cancelable: true, detail: detail || {} });
return el.dispatchEvent(evt); return el.dispatchEvent(evt);
} }
@@ -4119,6 +4425,14 @@ PRIMITIVES["boot-init"] = bootInit;
// Platform interface — Orchestration (browser-only) // Platform interface — Orchestration (browser-only)
// ========================================================================= // =========================================================================
// --- Stubs for define-library functions not transpiled by extract_defines ---
// These are defined in orchestration.sx's define-library and called from
// boot.sx top-level defines. The JS bootstrapper only transpiles top-level
// defines, so we provide stubs here for functions that need a JS identity.
function flushCollectedStyles() { return NIL; }
function processElements(root) { return NIL; }
// --- Browser/Network --- // --- Browser/Network ---
function browserNavigate(url) { function browserNavigate(url) {
@@ -4604,6 +4918,10 @@ PRIMITIVES["boot-init"] = bootInit;
return el && el.closest ? el.closest(sel) : null; return el && el.closest ? el.closest(sel) : null;
} }
function domDocument() {
return _hasDom ? document : null;
}
function domBody() { function domBody() {
return _hasDom ? document.body : null; return _hasDom ? document.body : null;
} }
@@ -5045,6 +5363,8 @@ PRIMITIVES["boot-init"] = bootInit;
// Platform interface — Boot (mount, hydrate, scripts, cookies) // Platform interface — Boot (mount, hydrate, scripts, cookies)
// ========================================================================= // =========================================================================
function preloadIslandDefs() { return NIL; }
function resolveMountTarget(target) { function resolveMountTarget(target) {
if (typeof target === "string") return _hasDom ? document.querySelector(target) : null; if (typeof target === "string") return _hasDom ? document.querySelector(target) : null;
return target; return target;
@@ -5920,52 +6240,52 @@ PRIMITIVES["boot-init"] = bootInit;
hydrateIslands: typeof sxHydrateIslands === "function" ? sxHydrateIslands : null, hydrateIslands: typeof sxHydrateIslands === "function" ? sxHydrateIslands : null,
disposeIsland: typeof disposeIsland === "function" ? disposeIsland : null, disposeIsland: typeof disposeIsland === "function" ? disposeIsland : null,
init: typeof bootInit === "function" ? bootInit : null, init: typeof bootInit === "function" ? bootInit : null,
scanRefs: scanRefs, scanRefs: typeof scanRefs === "function" ? scanRefs : null,
scanComponentsFromSource: scanComponentsFromSource, scanComponentsFromSource: typeof scanComponentsFromSource === "function" ? scanComponentsFromSource : null,
transitiveDeps: transitiveDeps, transitiveDeps: typeof transitiveDeps === "function" ? transitiveDeps : null,
computeAllDeps: computeAllDeps, computeAllDeps: typeof computeAllDeps === "function" ? computeAllDeps : null,
componentsNeeded: componentsNeeded, componentsNeeded: typeof componentsNeeded === "function" ? componentsNeeded : null,
pageComponentBundle: pageComponentBundle, pageComponentBundle: typeof pageComponentBundle === "function" ? pageComponentBundle : null,
pageCssClasses: pageCssClasses, pageCssClasses: typeof pageCssClasses === "function" ? pageCssClasses : null,
scanIoRefs: scanIoRefs, scanIoRefs: typeof scanIoRefs === "function" ? scanIoRefs : null,
transitiveIoRefs: transitiveIoRefs, transitiveIoRefs: typeof transitiveIoRefs === "function" ? transitiveIoRefs : null,
computeAllIoRefs: computeAllIoRefs, computeAllIoRefs: typeof computeAllIoRefs === "function" ? computeAllIoRefs : null,
componentPure_p: componentPure_p, componentPure_p: typeof componentPure_p === "function" ? componentPure_p : null,
categorizeSpecialForms: categorizeSpecialForms, categorizeSpecialForms: typeof categorizeSpecialForms === "function" ? categorizeSpecialForms : null,
buildReferenceData: buildReferenceData, buildReferenceData: typeof buildReferenceData === "function" ? buildReferenceData : null,
buildAttrDetail: buildAttrDetail, buildAttrDetail: typeof buildAttrDetail === "function" ? buildAttrDetail : null,
buildHeaderDetail: buildHeaderDetail, buildHeaderDetail: typeof buildHeaderDetail === "function" ? buildHeaderDetail : null,
buildEventDetail: buildEventDetail, buildEventDetail: typeof buildEventDetail === "function" ? buildEventDetail : null,
buildComponentSource: buildComponentSource, buildComponentSource: typeof buildComponentSource === "function" ? buildComponentSource : null,
buildBundleAnalysis: buildBundleAnalysis, buildBundleAnalysis: typeof buildBundleAnalysis === "function" ? buildBundleAnalysis : null,
buildRoutingAnalysis: buildRoutingAnalysis, buildRoutingAnalysis: typeof buildRoutingAnalysis === "function" ? buildRoutingAnalysis : null,
buildAffinityAnalysis: buildAffinityAnalysis, buildAffinityAnalysis: typeof buildAffinityAnalysis === "function" ? buildAffinityAnalysis : null,
splitPathSegments: splitPathSegments, splitPathSegments: typeof splitPathSegments === "function" ? splitPathSegments : null,
parseRoutePattern: parseRoutePattern, parseRoutePattern: typeof parseRoutePattern === "function" ? parseRoutePattern : null,
matchRoute: matchRoute, matchRoute: typeof matchRoute === "function" ? matchRoute : null,
findMatchingRoute: findMatchingRoute, findMatchingRoute: typeof findMatchingRoute === "function" ? findMatchingRoute : null,
urlToExpr: urlToExpr, urlToExpr: typeof urlToExpr === "function" ? urlToExpr : null,
autoQuoteUnknowns: autoQuoteUnknowns, autoQuoteUnknowns: typeof autoQuoteUnknowns === "function" ? autoQuoteUnknowns : null,
prepareUrlExpr: prepareUrlExpr, prepareUrlExpr: typeof prepareUrlExpr === "function" ? prepareUrlExpr : null,
registerIo: typeof registerIoPrimitive === "function" ? registerIoPrimitive : null, registerIo: typeof registerIoPrimitive === "function" ? registerIoPrimitive : null,
registerIoDeps: typeof registerIoDeps === "function" ? registerIoDeps : null, registerIoDeps: typeof registerIoDeps === "function" ? registerIoDeps : null,
asyncRender: typeof asyncSxRenderWithEnv === "function" ? asyncSxRenderWithEnv : null, asyncRender: typeof asyncSxRenderWithEnv === "function" ? asyncSxRenderWithEnv : null,
asyncRenderToDom: typeof asyncRenderToDom === "function" ? asyncRenderToDom : null, asyncRenderToDom: typeof asyncRenderToDom === "function" ? asyncRenderToDom : null,
signal: signal, signal: typeof signal === "function" ? signal : null,
deref: deref, deref: typeof deref === "function" ? deref : null,
reset: reset_b, reset: typeof reset_b === "function" ? reset_b : null,
swap: swap_b, swap: typeof swap_b === "function" ? swap_b : null,
computed: computed, computed: typeof computed === "function" ? computed : null,
effect: effect, effect: typeof effect === "function" ? effect : null,
batch: batch, batch: typeof batch === "function" ? batch : null,
isSignal: isSignal, isSignal: typeof isSignal === "function" ? isSignal : null,
makeSignal: makeSignal, makeSignal: typeof makeSignal === "function" ? makeSignal : null,
defStore: defStore, defStore: typeof defStore === "function" ? defStore : null,
useStore: useStore, useStore: typeof useStore === "function" ? useStore : null,
clearStores: clearStores, clearStores: typeof clearStores === "function" ? clearStores : null,
emitEvent: emitEvent, emitEvent: typeof emitEvent === "function" ? emitEvent : null,
onEvent: onEvent, onEvent: typeof onEvent === "function" ? onEvent : null,
bridgeEvent: bridgeEvent, bridgeEvent: typeof bridgeEvent === "function" ? bridgeEvent : null,
makeSpread: makeSpread, makeSpread: makeSpread,
isSpread: isSpread, isSpread: isSpread,
spreadAttrs: spreadAttrs, spreadAttrs: spreadAttrs,

207
spec/tests/test-vectors.sx Normal file
View File

@@ -0,0 +1,207 @@
;; test-vectors.sx — Tests for vector primitives
(defsuite
"vectors"
(deftest
"make-vector default fill is nil"
(let
((v (make-vector 3)))
(assert (vector? v))
(assert-equal 3 (vector-length v))
(assert-equal nil (vector-ref v 0))
(assert-equal nil (vector-ref v 1))
(assert-equal nil (vector-ref v 2))))
(deftest
"make-vector with fill value"
(let
((v (make-vector 4 99)))
(assert-equal 4 (vector-length v))
(assert-equal 99 (vector-ref v 0))
(assert-equal 99 (vector-ref v 1))
(assert-equal 99 (vector-ref v 2))
(assert-equal 99 (vector-ref v 3))))
(deftest
"make-vector size zero"
(let ((v (make-vector 0))) (assert-equal 0 (vector-length v))))
(deftest
"make-vector size one"
(let
((v (make-vector 1 "x")))
(assert-equal 1 (vector-length v))
(assert-equal "x" (vector-ref v 0))))
(deftest
"vector constructor no args"
(let ((v (vector))) (assert-equal 0 (vector-length v))))
(deftest
"vector constructor with args"
(let
((v (vector 10 20 30)))
(assert-equal 3 (vector-length v))
(assert-equal 10 (vector-ref v 0))
(assert-equal 20 (vector-ref v 1))
(assert-equal 30 (vector-ref v 2))))
(deftest
"vector constructor strings"
(let
((v (vector "a" "b" "c")))
(assert-equal "a" (vector-ref v 0))
(assert-equal "b" (vector-ref v 1))
(assert-equal "c" (vector-ref v 2))))
(deftest "vector? true for vector" (assert (vector? (make-vector 3))))
(deftest "vector? false for list" (assert (not (vector? (list 1 2 3)))))
(deftest "vector? false for number" (assert (not (vector? 42))))
(deftest "vector? false for nil" (assert (not (vector? nil))))
(deftest "vector? false for string" (assert (not (vector? "hello"))))
(deftest "vector-length zero" (assert-equal 0 (vector-length (vector))))
(deftest
"vector-length three"
(assert-equal 3 (vector-length (vector 1 2 3))))
(deftest
"vector-length after make-vector"
(assert-equal 7 (vector-length (make-vector 7 0))))
(deftest
"vector-ref first element"
(assert-equal 1 (vector-ref (vector 1 2 3) 0)))
(deftest
"vector-ref last element"
(assert-equal 3 (vector-ref (vector 1 2 3) 2)))
(deftest
"vector-ref middle element"
(assert-equal 2 (vector-ref (vector 1 2 3) 1)))
(deftest
"vector-set! mutates in place"
(let
((v (vector 1 2 3)))
(vector-set! v 1 99)
(assert-equal 99 (vector-ref v 1))
(assert-equal 1 (vector-ref v 0))
(assert-equal 3 (vector-ref v 2))))
(deftest
"vector-set! first slot"
(let
((v (make-vector 3 0)))
(vector-set! v 0 42)
(assert-equal 42 (vector-ref v 0))))
(deftest
"vector-set! last slot"
(let
((v (make-vector 3 0)))
(vector-set! v 2 77)
(assert-equal 77 (vector-ref v 2))))
(deftest
"vector-set! returns nil"
(let ((v (make-vector 3 0))) (assert-equal nil (vector-set! v 0 1))))
(deftest
"vector->list empty"
(assert-equal (list) (vector->list (vector))))
(deftest
"vector->list numbers"
(assert-equal (list 1 2 3) (vector->list (vector 1 2 3))))
(deftest
"vector->list strings"
(assert-equal (list "a" "b") (vector->list (vector "a" "b"))))
(deftest
"list->vector empty"
(let ((v (list->vector (list)))) (assert-equal 0 (vector-length v))))
(deftest
"list->vector numbers"
(let
((v (list->vector (list 10 20 30))))
(assert-equal 3 (vector-length v))
(assert-equal 10 (vector-ref v 0))
(assert-equal 20 (vector-ref v 1))
(assert-equal 30 (vector-ref v 2))))
(deftest
"vector-fill! sets all elements"
(let
((v (vector 1 2 3)))
(vector-fill! v 0)
(assert-equal 0 (vector-ref v 0))
(assert-equal 0 (vector-ref v 1))
(assert-equal 0 (vector-ref v 2))))
(deftest
"vector-fill! returns nil"
(assert-equal nil (vector-fill! (make-vector 2 0) 7)))
(deftest
"vector-fill! string fill"
(let
((v (make-vector 3 "")))
(vector-fill! v "x")
(assert-equal "x" (vector-ref v 0))
(assert-equal "x" (vector-ref v 2))))
(deftest
"vector-copy full copy"
(let
((v1 (vector 1 2 3)) (v2 (vector-copy (vector 1 2 3))))
(assert-equal 3 (vector-length v2))
(assert-equal 1 (vector-ref v2 0))
(assert-equal 2 (vector-ref v2 1))
(assert-equal 3 (vector-ref v2 2))))
(deftest
"vector-copy is independent"
(let
((v1 (vector 1 2 3)))
(let
((v2 (vector-copy v1)))
(vector-set! v1 0 99)
(assert-equal 1 (vector-ref v2 0)))))
(deftest
"vector-copy with start"
(let
((v (vector-copy (vector 10 20 30 40) 1)))
(assert-equal 3 (vector-length v))
(assert-equal 20 (vector-ref v 0))
(assert-equal 30 (vector-ref v 1))
(assert-equal 40 (vector-ref v 2))))
(deftest
"vector-copy with start and end"
(let
((v (vector-copy (vector 10 20 30 40) 1 3)))
(assert-equal 2 (vector-length v))
(assert-equal 20 (vector-ref v 0))
(assert-equal 30 (vector-ref v 1))))
(deftest
"vector-copy empty slice"
(let
((v (vector-copy (vector 1 2 3) 1 1)))
(assert-equal 0 (vector-length v))))
(deftest
"vector-ref out of bounds raises"
(let
((ok false))
(guard (exn (else (set! ok true))) (vector-ref (vector 1 2 3) 5))
(assert ok)))
(deftest
"vector-ref negative index raises"
(let
((ok false))
(guard (exn (else (set! ok true))) (vector-ref (vector 1 2 3) -1))
(assert ok)))
(deftest
"vector-set! out of bounds raises"
(let
((ok false))
(guard
(exn (else (set! ok true)))
(vector-set! (vector 1 2 3) 10 99))
(assert ok)))
(deftest
"vector list round-trip"
(let
((lst (list 5 10 15 20)))
(assert-equal lst (vector->list (list->vector lst)))))
(deftest
"vector mutation does not affect copy"
(let
((v1 (vector 1 2 3)))
(let
((v2 (vector-copy v1)))
(vector-set! v2 0 100)
(assert-equal 1 (vector-ref v1 0))
(assert-equal 100 (vector-ref v2 0)))))
(deftest
"vector-length after fill"
(let
((v (make-vector 5 0)))
(vector-fill! v 1)
(assert-equal 5 (vector-length v)))))