Lazy module loading: compiler loads on demand, playground page
- load-library! native: islands can declare module dependencies at hydration time, triggering on-demand .sxbc loading - JIT compiler lazy-load: compiler.sxbc loads via setTimeout after boot, eliminating "JIT: compiler not loaded" errors - _import_hook on sx_types: infrastructure for hosts to resolve import suspensions inside eval_expr (server wiring deferred to Step 8) - Playground page (/sx/(tools.(playground))): REPL island that lazy-loads the compiler module when navigated to — demonstrates the full lazy loading pipeline Known remaining issues: - SPA navigation broken for pages using let-match (orchestration.sx, router.sx) — bytecode compiler doesn't handle let-match special form - Server-side "IO suspension in non-IO context" during http_load_files — needs cek_run import handling (deferred to Step 8) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,9 +19,16 @@
|
|||||||
function boot(K) {
|
function boot(K) {
|
||||||
|
|
||||||
// ================================================================
|
// ================================================================
|
||||||
// 8 FFI Host Primitives
|
// FFI Host Primitives
|
||||||
// ================================================================
|
// ================================================================
|
||||||
|
|
||||||
|
// Lazy module loading — islands/components call this to declare dependencies
|
||||||
|
K.registerNative("load-library!", function(args) {
|
||||||
|
var name = args[0];
|
||||||
|
if (!name) return false;
|
||||||
|
return __sxLoadLibrary(name) || false;
|
||||||
|
});
|
||||||
|
|
||||||
K.registerNative("host-global", function(args) {
|
K.registerNative("host-global", function(args) {
|
||||||
var name = args[0];
|
var name = args[0];
|
||||||
if (typeof globalThis !== "undefined" && name in globalThis) return globalThis[name];
|
if (typeof globalThis !== "undefined" && name in globalThis) return globalThis[name];
|
||||||
@@ -463,13 +470,16 @@
|
|||||||
loadLibrary(info.deps[i], loading);
|
loadLibrary(info.deps[i], loading);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark as loaded BEFORE executing — self-imports (define-library re-exports)
|
||||||
|
// will see it as already loaded and skip rather than infinite-looping.
|
||||||
|
_loadedLibs[name] = true;
|
||||||
|
|
||||||
// Load this module
|
// Load this module
|
||||||
var ok = loadBytecodeFile("sx/" + info.file);
|
var ok = loadBytecodeFile("sx/" + info.file);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
var sxFile = info.file.replace(/\.sxbc$/, '.sx');
|
var sxFile = info.file.replace(/\.sxbc$/, '.sx');
|
||||||
ok = loadSxFile("sx/" + sxFile);
|
ok = loadSxFile("sx/" + sxFile);
|
||||||
}
|
}
|
||||||
_loadedLibs[name] = true;
|
|
||||||
return !!ok;
|
return !!ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,8 +624,14 @@
|
|||||||
var _doInit = function() {
|
var _doInit = function() {
|
||||||
loadWebStack();
|
loadWebStack();
|
||||||
Sx.init();
|
Sx.init();
|
||||||
// Enable JIT after all boot code has run
|
// Enable JIT after all boot code has run.
|
||||||
setTimeout(function() { K.eval('(enable-jit!)'); }, 0);
|
// Lazy-load the compiler first — JIT needs it to compile functions.
|
||||||
|
setTimeout(function() {
|
||||||
|
if (K.beginModuleLoad) K.beginModuleLoad();
|
||||||
|
loadLibrary("sx compiler", {});
|
||||||
|
if (K.endModuleLoad) K.endModuleLoad();
|
||||||
|
K.eval('(enable-jit!)');
|
||||||
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (document.readyState === "loading") {
|
if (document.readyState === "loading") {
|
||||||
|
|||||||
@@ -255,6 +255,10 @@ let _env_bind_hook : (env -> string -> value -> unit) option ref = ref None
|
|||||||
Used by browser kernel to sync mutations back to global_env. *)
|
Used by browser kernel to sync mutations back to global_env. *)
|
||||||
let _vm_global_set_hook : (string -> value -> unit) option ref = ref None
|
let _vm_global_set_hook : (string -> value -> unit) option ref = ref None
|
||||||
|
|
||||||
|
(* Optional hook: called by cek_run on import suspension.
|
||||||
|
If set, the hook loads the library and returns true; cek_run then resumes. *)
|
||||||
|
let _import_hook : (value -> bool) option ref = ref None
|
||||||
|
|
||||||
let env_bind env name v =
|
let env_bind env name v =
|
||||||
Hashtbl.replace env.bindings (intern name) v;
|
Hashtbl.replace env.bindings (intern name) v;
|
||||||
(match !_env_bind_hook with Some f -> f env name v | None -> ());
|
(match !_env_bind_hook with Some f -> f env name v | None -> ());
|
||||||
|
|||||||
@@ -19,9 +19,16 @@
|
|||||||
function boot(K) {
|
function boot(K) {
|
||||||
|
|
||||||
// ================================================================
|
// ================================================================
|
||||||
// 8 FFI Host Primitives
|
// FFI Host Primitives
|
||||||
// ================================================================
|
// ================================================================
|
||||||
|
|
||||||
|
// Lazy module loading — islands/components call this to declare dependencies
|
||||||
|
K.registerNative("load-library!", function(args) {
|
||||||
|
var name = args[0];
|
||||||
|
if (!name) return false;
|
||||||
|
return __sxLoadLibrary(name) || false;
|
||||||
|
});
|
||||||
|
|
||||||
K.registerNative("host-global", function(args) {
|
K.registerNative("host-global", function(args) {
|
||||||
var name = args[0];
|
var name = args[0];
|
||||||
if (typeof globalThis !== "undefined" && name in globalThis) return globalThis[name];
|
if (typeof globalThis !== "undefined" && name in globalThis) return globalThis[name];
|
||||||
@@ -617,8 +624,14 @@
|
|||||||
var _doInit = function() {
|
var _doInit = function() {
|
||||||
loadWebStack();
|
loadWebStack();
|
||||||
Sx.init();
|
Sx.init();
|
||||||
// Enable JIT after all boot code has run
|
// Enable JIT after all boot code has run.
|
||||||
setTimeout(function() { K.eval('(enable-jit!)'); }, 0);
|
// Lazy-load the compiler first — JIT needs it to compile functions.
|
||||||
|
setTimeout(function() {
|
||||||
|
if (K.beginModuleLoad) K.beginModuleLoad();
|
||||||
|
loadLibrary("sx compiler", {});
|
||||||
|
if (K.endModuleLoad) K.endModuleLoad();
|
||||||
|
K.eval('(enable-jit!)');
|
||||||
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (document.readyState === "loading") {
|
if (document.readyState === "loading") {
|
||||||
|
|||||||
50
sx/sx/playground.sx
Normal file
50
sx/sx/playground.sx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
;; Playground — interactive REPL with lazy-loaded compiler
|
||||||
|
|
||||||
|
(defcomp ~playground/content (&key (title "Playground"))
|
||||||
|
(~docs/page :title title
|
||||||
|
(h1 "Playground")
|
||||||
|
(p "An interactive SX REPL. The compiler module loads on demand when you navigate here.")
|
||||||
|
(span :data-sx-island "playground/repl"
|
||||||
|
(div (~tw :tokens "mt-6 border border-stone-200 rounded-lg overflow-hidden")
|
||||||
|
(div (~tw :tokens "bg-stone-100 px-4 py-2 text-sm font-mono text-stone-500")
|
||||||
|
"sx> ")
|
||||||
|
(div (~tw :tokens "p-4 font-mono text-sm min-h-[120px] text-stone-400")
|
||||||
|
"Loading compiler...")))))
|
||||||
|
|
||||||
|
(defisland ~playground/repl ()
|
||||||
|
(do
|
||||||
|
(load-library! "sx compiler")
|
||||||
|
(let ((input (signal ""))
|
||||||
|
(output (signal "(ready)"))
|
||||||
|
(history (signal (list))))
|
||||||
|
(let ((eval-input
|
||||||
|
(fn (e)
|
||||||
|
(let ((src (deref input)))
|
||||||
|
(when (not (empty? src))
|
||||||
|
(let ((result (cek-try
|
||||||
|
(fn () (str (cek-eval (first (sx-parse src)))))
|
||||||
|
(fn (err) (str "Error: " err)))))
|
||||||
|
(reset! output result)
|
||||||
|
(swap! history (fn (h) (append h (list (dict :src src :result result)))))
|
||||||
|
(reset! input "")))))))
|
||||||
|
(div (~tw :tokens "border border-stone-200 rounded-lg overflow-hidden")
|
||||||
|
(div (~tw :tokens "bg-stone-800 text-stone-100 p-4 font-mono text-sm min-h-[120px] max-h-[300px] overflow-y-auto")
|
||||||
|
(for-each
|
||||||
|
(fn (entry)
|
||||||
|
(div
|
||||||
|
(div (~tw :tokens "text-emerald-400") (str "sx> " (get entry "src")))
|
||||||
|
(div (~tw :tokens "text-stone-300 mb-2") (get entry "result"))))
|
||||||
|
(deref history))
|
||||||
|
(div (~tw :tokens "text-amber-400") (str "=> " (deref output))))
|
||||||
|
(div (~tw :tokens "flex border-t border-stone-200")
|
||||||
|
(span (~tw :tokens "bg-stone-100 px-3 py-2 text-stone-500 font-mono text-sm") "sx>")
|
||||||
|
(input :type "text"
|
||||||
|
:value (deref input)
|
||||||
|
:on-input (fn (e) (reset! input (element-value (host-get e "target"))))
|
||||||
|
:on-keydown (fn (e) (when (= (host-get e "key") "Enter") (eval-input e)))
|
||||||
|
(~tw :tokens "flex-1 px-3 py-2 font-mono text-sm outline-none")
|
||||||
|
:placeholder "Type an expression..."
|
||||||
|
:autofocus "true")
|
||||||
|
(button :on-click eval-input
|
||||||
|
(~tw :tokens "px-4 py-2 bg-violet-600 text-white font-mono text-sm hover:bg-violet-700")
|
||||||
|
"Eval")))))))
|
||||||
Reference in New Issue
Block a user