VM: VmClosure value type + iterative run loop + define hoisting + SSR fixes
Core VM changes: - Add VmClosure value variant — inner closures created by OP_CLOSURE are first-class VM values, not NativeFn wrappers around call_closure - Convert `run` from recursive to while-loop — zero OCaml stack growth, true TCO for VmClosure tail calls - vm_call handles VmClosure by pushing frame on current VM (no new VM allocation per call) - Forward ref _vm_call_closure_ref for cross-boundary calls (CEK/primitives) Compiler (spec/compiler.sx): - Define hoisting in compile-begin: pre-allocate local slots for all define forms before compiling any values. Fixes forward references between inner functions (e.g. read-expr referencing skip-ws in sx-parse) - scope-define-local made idempotent (skip if slot already exists) Server (sx_server.ml): - JIT fail-once sentinel: mark l_compiled as failed after first VM runtime error. Eliminates thousands of retry attempts per page render. - HTML tag bindings: register all HTML tags as pass-through NativeFns so eval-expr can handle (div ...) etc. in island component bodies. - Log VM FAIL errors with function name before disabling JIT. SSR fixes: - adapter-html.sx letrec handler: evaluate bindings in proper letrec scope (pre-bind nil, then evaluate), render body with render-to-html instead of eval-expr. Fixes island SSR for components using letrec. - Add `init` primitive to OCaml kernel (all-but-last of list). - VmClosure handling in sx_runtime.ml sx_call dispatch. Tests: 971/971 OCaml (+19 new), 0 failures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
// =========================================================================
|
||||
|
||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||
var SX_VERSION = "2026-03-23T20:57:55Z";
|
||||
var SX_VERSION = "2026-03-23T23:36:04Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
@@ -2500,8 +2500,21 @@ PRIMITIVES["render-list-to-html"] = renderListToHtml;
|
||||
var branch = evalCond(rest(expr), env);
|
||||
return (isSxTruthy(branch) ? renderToHtml(branch, env) : "");
|
||||
})() : (isSxTruthy((name == "case")) ? renderToHtml(trampoline(evalExpr(expr, env)), env) : (isSxTruthy((name == "letrec")) ? (function() {
|
||||
var result = evalExpr(expr, env);
|
||||
return renderValueToHtml(result, env);
|
||||
var bindings = nth(expr, 1);
|
||||
var body = slice(expr, 2);
|
||||
var local = envExtend(env);
|
||||
{ var _c = bindings; for (var _i = 0; _i < _c.length; _i++) { var pair = _c[_i]; (function() {
|
||||
var pname = (isSxTruthy((typeOf(first(pair)) == "symbol")) ? symbolName(first(pair)) : (String(first(pair))));
|
||||
return envBind(local, pname, NIL);
|
||||
})(); } }
|
||||
{ var _c = bindings; for (var _i = 0; _i < _c.length; _i++) { var pair = _c[_i]; (function() {
|
||||
var pname = (isSxTruthy((typeOf(first(pair)) == "symbol")) ? symbolName(first(pair)) : (String(first(pair))));
|
||||
return envSet(local, pname, trampoline(evalExpr(nth(pair, 1), local)));
|
||||
})(); } }
|
||||
if (isSxTruthy((len(body) > 1))) {
|
||||
{ var _c = init(body); for (var _i = 0; _i < _c.length; _i++) { var e = _c[_i]; trampoline(evalExpr(e, local)); } }
|
||||
}
|
||||
return renderToHtml(last(body), local);
|
||||
})() : (isSxTruthy(sxOr((name == "let"), (name == "let*"))) ? (function() {
|
||||
var local = processBindings(nth(expr, 1), env);
|
||||
return (isSxTruthy((len(expr) == 3)) ? renderToHtml(nth(expr, 2), local) : join("", map(function(i) { return renderToHtml(nth(expr, i), local); }, range(2, len(expr)))));
|
||||
|
||||
Reference in New Issue
Block a user