Move stdlib out of spec — clean spec/library boundary

spec/ now contains only the language definition (5 files):
  evaluator.sx, parser.sx, primitives.sx, render.sx, special-forms.sx

lib/ contains code written IN the language (8 files):
  stdlib.sx, types.sx, freeze.sx, content.sx,
  bytecode.sx, compiler.sx, vm.sx, callcc.sx

Test files follow source: spec/tests/ for core language tests,
lib/tests/ for library tests (continuations, freeze, types, vm).

Updated all consumers:
- JS/Python/OCaml bootstrappers: added lib/ to source search paths
- OCaml bridge: spec_dir for parser/render, lib_dir for compiler/freeze
- JS test runner: scans spec/tests/ (always) + lib/tests/ (--full)
- OCaml test runner: scans spec/tests/, lib tests via explicit request
- Docker dev mounts: added ./lib:/app/lib:ro

Tests: 1041 JS standard, 1322 JS full, 1101 OCaml — all pass

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 23:18:30 +00:00
parent 50871780a3
commit f3f70cc00b
26 changed files with 109 additions and 64 deletions

View File

@@ -31,8 +31,9 @@ services:
- ./sx/sx:/app/sx - ./sx/sx:/app/sx
- ./sx/path_setup.py:/app/path_setup.py - ./sx/path_setup.py:/app/path_setup.py
- ./sx/entrypoint.sh:/usr/local/bin/entrypoint.sh - ./sx/entrypoint.sh:/usr/local/bin/entrypoint.sh
# Spec + web SX files (loaded by OCaml kernel for aser, parser, render) # Spec + lib + web SX files (loaded by OCaml kernel)
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
# OCaml SX kernel binary (built with: cd hosts/ocaml && eval $(opam env) && dune build) # OCaml SX kernel binary (built with: cd hosts/ocaml && eval $(opam env) && dune build)
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro

View File

@@ -48,6 +48,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./blog/alembic.ini:/app/blog/alembic.ini:ro - ./blog/alembic.ini:/app/blog/alembic.ini:ro
- ./blog/alembic:/app/blog/alembic:ro - ./blog/alembic:/app/blog/alembic:ro
@@ -90,6 +91,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./market/alembic.ini:/app/market/alembic.ini:ro - ./market/alembic.ini:/app/market/alembic.ini:ro
- ./market/alembic:/app/market/alembic:ro - ./market/alembic:/app/market/alembic:ro
@@ -131,6 +133,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./cart/alembic.ini:/app/cart/alembic.ini:ro - ./cart/alembic.ini:/app/cart/alembic.ini:ro
- ./cart/alembic:/app/cart/alembic:ro - ./cart/alembic:/app/cart/alembic:ro
@@ -172,6 +175,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./events/alembic.ini:/app/events/alembic.ini:ro - ./events/alembic.ini:/app/events/alembic.ini:ro
- ./events/alembic:/app/events/alembic:ro - ./events/alembic:/app/events/alembic:ro
@@ -213,6 +217,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./federation/alembic.ini:/app/federation/alembic.ini:ro - ./federation/alembic.ini:/app/federation/alembic.ini:ro
- ./federation/alembic:/app/federation/alembic:ro - ./federation/alembic:/app/federation/alembic:ro
@@ -254,6 +259,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./account/alembic.ini:/app/account/alembic.ini:ro - ./account/alembic.ini:/app/account/alembic.ini:ro
- ./account/alembic:/app/account/alembic:ro - ./account/alembic:/app/account/alembic:ro
@@ -295,6 +301,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./relations/alembic.ini:/app/relations/alembic.ini:ro - ./relations/alembic.ini:/app/relations/alembic.ini:ro
- ./relations/alembic:/app/relations/alembic:ro - ./relations/alembic:/app/relations/alembic:ro
@@ -329,6 +336,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./likes/alembic.ini:/app/likes/alembic.ini:ro - ./likes/alembic.ini:/app/likes/alembic.ini:ro
- ./likes/alembic:/app/likes/alembic:ro - ./likes/alembic:/app/likes/alembic:ro
@@ -363,6 +371,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./orders/alembic.ini:/app/orders/alembic.ini:ro - ./orders/alembic.ini:/app/orders/alembic.ini:ro
- ./orders/alembic:/app/orders/alembic:ro - ./orders/alembic:/app/orders/alembic:ro
@@ -400,6 +409,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./test/app.py:/app/app.py - ./test/app.py:/app/app.py
- ./test/sx:/app/sx - ./test/sx:/app/sx
@@ -431,6 +441,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./sx/app.py:/app/app.py - ./sx/app.py:/app/app.py
- ./sx/sxc:/app/sxc - ./sx/sxc:/app/sxc
@@ -469,6 +480,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./artdag/core:/app/artdag/core - ./artdag/core:/app/artdag/core
- ./artdag/l1/tests:/app/artdag/l1/tests - ./artdag/l1/tests:/app/artdag/l1/tests
@@ -497,6 +509,7 @@ services:
- ./shared:/app/shared - ./shared:/app/shared
- ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro - ./hosts/ocaml/_build/default/bin/sx_server.exe:/app/bin/sx_server:ro
- ./spec:/app/spec:ro - ./spec:/app/spec:ro
- ./lib:/app/lib:ro
- ./web:/app/web:ro - ./web:/app/web:ro
- ./artdag:/app/artdag - ./artdag:/app/artdag
profiles: profiles:

View File

@@ -70,10 +70,11 @@ def compile_ref_to_js(
""" """
from datetime import datetime, timezone from datetime import datetime, timezone
# Source directories: core spec and web framework # Source directories: core spec, standard library, web framework
_source_dirs = [ _source_dirs = [
os.path.join(_PROJECT, "spec"), # Core spec os.path.join(_PROJECT, "spec"), # Core language spec
os.path.join(_PROJECT, "web"), # Web framework os.path.join(_PROJECT, "lib"), # Standard library (stdlib, compiler, vm, ...)
os.path.join(_PROJECT, "web"), # Web framework
] ]
bridge = _get_bridge() bridge = _get_bridge()

View File

@@ -270,6 +270,7 @@ env["pop-suite"] = function() {
// 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");
const libTests = path.join(projectDir, "lib", "tests");
const webTests = path.join(projectDir, "web", "tests"); const webTests = path.join(projectDir, "web", "tests");
const frameworkSrc = fs.readFileSync(path.join(specTests, "test-framework.sx"), "utf8"); const frameworkSrc = fs.readFileSync(path.join(specTests, "test-framework.sx"), "utf8");
@@ -278,17 +279,17 @@ for (const expr of frameworkExprs) {
Sx.eval(expr, env); Sx.eval(expr, env);
} }
// Load compiler + VM spec when running full tests // Load compiler + VM from lib/ when running full tests
if (fullBuild) { if (fullBuild) {
const specDir = path.join(projectDir, "spec"); const libDir = path.join(projectDir, "lib");
for (const specFile of ["bytecode.sx", "compiler.sx", "vm.sx"]) { for (const libFile of ["bytecode.sx", "compiler.sx", "vm.sx"]) {
const specPath = path.join(specDir, specFile); const libPath = path.join(libDir, libFile);
if (fs.existsSync(specPath)) { if (fs.existsSync(libPath)) {
const src = fs.readFileSync(specPath, "utf8"); const src = fs.readFileSync(libPath, "utf8");
const exprs = Sx.parse(src); const exprs = Sx.parse(src);
for (const expr of exprs) { for (const expr of exprs) {
try { Sx.eval(expr, env); } catch (e) { try { Sx.eval(expr, env); } catch (e) {
console.error(`Error loading ${specFile}: ${e.message}`); console.error(`Error loading ${libFile}: ${e.message}`);
} }
} }
} }
@@ -300,28 +301,32 @@ const args = process.argv.slice(2).filter(a => !a.startsWith("--"));
let testFiles = []; let testFiles = [];
if (args.length > 0) { if (args.length > 0) {
// Specific test files // Specific test files — search spec, lib, and web test dirs
for (const arg of args) { for (const arg of args) {
const name = arg.endsWith(".sx") ? arg : `${arg}.sx`; const name = arg.endsWith(".sx") ? arg : `${arg}.sx`;
const specPath = path.join(specTests, name); const specPath = path.join(specTests, name);
const libPath = path.join(libTests, name);
const webPath = path.join(webTests, name); const webPath = path.join(webTests, name);
if (fs.existsSync(specPath)) testFiles.push(specPath); if (fs.existsSync(specPath)) testFiles.push(specPath);
else if (fs.existsSync(libPath)) testFiles.push(libPath);
else if (fs.existsSync(webPath)) testFiles.push(webPath); else if (fs.existsSync(webPath)) testFiles.push(webPath);
else console.error(`Test file not found: ${name}`); else console.error(`Test file not found: ${name}`);
} }
} else { } else {
// Tests requiring optional modules (only run with --full) // All spec tests (core language — always run)
const requiresFull = new Set(["test-continuations.sx", "test-continuations-advanced.sx", "test-types.sx", "test-freeze.sx", "test-vm.sx"]);
// All spec tests
for (const f of fs.readdirSync(specTests).sort()) { for (const f of fs.readdirSync(specTests).sort()) {
if (f.startsWith("test-") && f.endsWith(".sx") && f !== "test-framework.sx") { if (f.startsWith("test-") && f.endsWith(".sx") && f !== "test-framework.sx") {
if (!fullBuild && requiresFull.has(f)) {
console.log(`Skipping ${f} (requires --full)`);
continue;
}
testFiles.push(path.join(specTests, f)); testFiles.push(path.join(specTests, f));
} }
} }
// Library tests (only with --full — require compiler, vm, signals, etc.)
if (fullBuild) {
for (const f of fs.readdirSync(libTests).sort()) {
if (f.startsWith("test-") && f.endsWith(".sx")) {
testFiles.push(path.join(libTests, f));
}
}
}
} }
// Run tests // Run tests

View File

@@ -386,12 +386,13 @@ let () =
in in
let root = find_root (Sys.getcwd ()) in let root = find_root (Sys.getcwd ()) in
let spec p = Filename.concat (Filename.concat root "spec") p in let spec p = Filename.concat (Filename.concat root "spec") p in
let lib p = Filename.concat (Filename.concat root "lib") p in
let web p = Filename.concat (Filename.concat root "web") p in let web p = Filename.concat (Filename.concat root "web") p in
let env = make_integration_env () in let env = make_integration_env () in
(* Load spec + adapters *) (* Load spec + lib + adapters *)
Printf.printf "Loading spec + adapters...\n%!"; Printf.printf "Loading spec + lib + adapters...\n%!";
let load path = let load path =
if Sys.file_exists path then begin if Sys.file_exists path then begin
let exprs = Sx_parser.parse_file path in let exprs = Sx_parser.parse_file path in
@@ -405,6 +406,7 @@ let () =
load (web "signals.sx"); load (web "signals.sx");
load (web "adapter-html.sx"); load (web "adapter-html.sx");
load (web "adapter-sx.sx"); load (web "adapter-sx.sx");
ignore lib; (* available for future library loading *)
(* Helper: render SX source string to HTML *) (* Helper: render SX source string to HTML *)
let render_html src = let render_html src =

View File

@@ -734,10 +734,11 @@ let run_spec_tests env test_files =
Printf.printf "\nLoading test framework...\n%!"; Printf.printf "\nLoading test framework...\n%!";
load_and_eval framework_path; load_and_eval framework_path;
(* Load spec modules needed by tests *) (* Load modules needed by tests *)
let spec_dir = Filename.concat project_dir "spec" in let spec_dir = Filename.concat project_dir "spec" in
let lib_dir = Filename.concat project_dir "lib" in
let web_dir = Filename.concat project_dir "web" in let web_dir = Filename.concat project_dir "web" in
let load_spec name dir = let load_module name dir =
let path = Filename.concat dir name in let path = Filename.concat dir name in
if Sys.file_exists path then begin if Sys.file_exists path then begin
Printf.printf "Loading %s...\n%!" name; Printf.printf "Loading %s...\n%!" name;
@@ -746,36 +747,47 @@ let run_spec_tests env test_files =
end end
in in
(* Render adapter for test-render-html.sx *) (* Render adapter for test-render-html.sx *)
load_spec "render.sx" spec_dir; load_module "render.sx" spec_dir;
load_spec "adapter-html.sx" web_dir; load_module "adapter-html.sx" web_dir;
(* Compiler + VM for test-vm.sx *) (* Library modules for lib/tests/ *)
load_spec "bytecode.sx" spec_dir; load_module "bytecode.sx" lib_dir;
load_spec "compiler.sx" spec_dir; load_module "compiler.sx" lib_dir;
load_spec "vm.sx" spec_dir; load_module "vm.sx" lib_dir;
load_module "signals.sx" web_dir;
load_module "freeze.sx" lib_dir;
load_module "content.sx" lib_dir;
load_module "types.sx" lib_dir;
(* Determine test files *) (* Determine test files — scan spec/tests/ and lib/tests/ *)
let lib_tests_dir = Filename.concat project_dir "lib/tests" in
let files = if test_files = [] then begin let files = if test_files = [] then begin
let entries = Sys.readdir spec_tests_dir in (* Spec tests (core language — always run) *)
Array.sort String.compare entries; let spec_entries = Sys.readdir spec_tests_dir in
let requires_full = ["test-continuations.sx"; "test-types.sx"; "test-freeze.sx"; Array.sort String.compare spec_entries;
"test-continuations-advanced.sx"; "test-signals-advanced.sx"; let spec_files = Array.to_list spec_entries
"test-vm.sx"] in |> List.filter (fun f ->
Array.to_list entries String.length f > 5 &&
|> List.filter (fun f -> String.sub f 0 5 = "test-" &&
String.length f > 5 && Filename.check_suffix f ".sx" &&
String.sub f 0 5 = "test-" && f <> "test-framework.sx")
Filename.check_suffix f ".sx" && |> List.map (fun f -> Filename.concat spec_tests_dir f)
f <> "test-framework.sx" && in
not (List.mem f requires_full)) spec_files
end else end else
(* Specific test files — search all test dirs *)
List.map (fun name -> List.map (fun name ->
if Filename.check_suffix name ".sx" then name let name = if Filename.check_suffix name ".sx" then name else name ^ ".sx" in
else name ^ ".sx") test_files let spec_path = Filename.concat spec_tests_dir name in
let lib_path = Filename.concat lib_tests_dir name in
if Sys.file_exists spec_path then spec_path
else if Sys.file_exists lib_path then lib_path
else Filename.concat spec_tests_dir name (* will fail with "not found" *)
) test_files
in in
List.iter (fun name -> List.iter (fun path ->
let path = Filename.concat spec_tests_dir name in
if Sys.file_exists path then begin if Sys.file_exists path then begin
let name = Filename.basename path in
Printf.printf "\n%s\n" (String.make 60 '='); Printf.printf "\n%s\n" (String.make 60 '=');
Printf.printf "Running %s\n" name; Printf.printf "Running %s\n" name;
Printf.printf "%s\n%!" (String.make 60 '='); Printf.printf "%s\n%!" (String.make 60 '=');

View File

@@ -1174,18 +1174,20 @@ let cli_load_files env files =
let cli_mode mode = let cli_mode mode =
let env = make_server_env () in let env = make_server_env () in
(* Load spec + adapter files for aser modes *) (* Load spec + adapter files for aser modes *)
let base = try Sys.getenv "SX_SPEC_DIR" with Not_found -> "spec" in let spec_base = try Sys.getenv "SX_SPEC_DIR" with Not_found -> "spec" in
let lib_base = try Sys.getenv "SX_LIB_DIR" with Not_found -> "lib" in
let web_base = try Sys.getenv "SX_WEB_DIR" with Not_found -> "web" in let web_base = try Sys.getenv "SX_WEB_DIR" with Not_found -> "web" in
let spec_files = [ let render_files = [
Filename.concat base "parser.sx"; Filename.concat spec_base "parser.sx";
Filename.concat base "render.sx"; Filename.concat spec_base "render.sx";
Filename.concat web_base "adapter-html.sx"; Filename.concat web_base "adapter-html.sx";
Filename.concat web_base "adapter-sx.sx"; Filename.concat web_base "adapter-sx.sx";
Filename.concat web_base "web-forms.sx"; Filename.concat web_base "web-forms.sx";
] in ] in
(* Load spec files for all CLI modes that need rendering *) (* Load spec + adapter files for rendering CLI modes *)
(if mode = "aser" || mode = "aser-slot" || mode = "render" then (if mode = "aser" || mode = "aser-slot" || mode = "render" then
cli_load_files env spec_files); cli_load_files env render_files);
ignore lib_base; (* available for --load paths *)
(* Load any files passed via --load *) (* Load any files passed via --load *)
let load_files = ref [] in let load_files = ref [] in
let args = Array.to_list Sys.argv in let args = Array.to_list Sys.argv in
@@ -1256,13 +1258,14 @@ let cli_mode mode =
let test_mode () = let test_mode () =
let env = make_server_env () in let env = make_server_env () in
(* Load full spec + adapter stack *) (* Load spec + lib + adapter stack *)
let base = try Sys.getenv "SX_SPEC_DIR" with Not_found -> "spec" in let spec_base = try Sys.getenv "SX_SPEC_DIR" with Not_found -> "spec" in
let lib_base = try Sys.getenv "SX_LIB_DIR" with Not_found -> "lib" in
let web_base = try Sys.getenv "SX_WEB_DIR" with Not_found -> "web" in let web_base = try Sys.getenv "SX_WEB_DIR" with Not_found -> "web" in
let files = [ let files = [
Filename.concat base "parser.sx"; Filename.concat spec_base "parser.sx";
Filename.concat base "render.sx"; Filename.concat spec_base "render.sx";
Filename.concat base "compiler.sx"; Filename.concat lib_base "compiler.sx";
Filename.concat web_base "signals.sx"; Filename.concat web_base "signals.sx";
Filename.concat web_base "adapter-html.sx"; Filename.concat web_base "adapter-html.sx";
Filename.concat web_base "adapter-sx.sx"; Filename.concat web_base "adapter-sx.sx";

View File

@@ -1448,6 +1448,7 @@ def compile_ref_to_py(
_project = os.path.abspath(os.path.join(ref_dir, "..", "..", "..")) _project = os.path.abspath(os.path.join(ref_dir, "..", "..", ".."))
_source_dirs = [ _source_dirs = [
os.path.join(_project, "spec"), os.path.join(_project, "spec"),
os.path.join(_project, "lib"),
os.path.join(_project, "web"), os.path.join(_project, "web"),
ref_dir, ref_dir,
] ]

View File

@@ -14,7 +14,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-03-24T22:31:01Z"; var SX_VERSION = "2026-03-24T22:56:21Z";
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); }

View File

@@ -367,17 +367,24 @@ class OcamlBridge:
# Collect files to load # Collect files to load
all_files: list[str] = [] all_files: list[str] = []
# Spec files needed by aser + bytecode compiler # Core spec files
spec_dir = os.path.join(os.path.dirname(__file__), "../../spec") spec_dir = os.path.join(os.path.dirname(__file__), "../../spec")
for spec_file in ["parser.sx", "render.sx", "bytecode.sx", "compiler.sx"]: for spec_file in ["parser.sx", "render.sx"]:
path = os.path.normpath(os.path.join(spec_dir, spec_file)) path = os.path.normpath(os.path.join(spec_dir, spec_file))
if os.path.isfile(path): if os.path.isfile(path):
all_files.append(path) all_files.append(path)
# Library files (compiler, vm, freeze — written in the language)
lib_dir = os.path.join(os.path.dirname(__file__), "../../lib")
for lib_file in ["bytecode.sx", "compiler.sx"]:
path = os.path.normpath(os.path.join(lib_dir, lib_file))
if os.path.isfile(path):
all_files.append(path)
# All directories loaded into the Python env # All directories loaded into the Python env
all_dirs = list(set(_watched_dirs) | _dirs_from_cache) all_dirs = list(set(_watched_dirs) | _dirs_from_cache)
# Isomorphic libraries: signals, rendering, freeze scopes, web forms # Isomorphic libraries: signals, rendering, web forms
web_dir = os.path.join(os.path.dirname(__file__), "../../web") web_dir = os.path.join(os.path.dirname(__file__), "../../web")
if os.path.isdir(web_dir): if os.path.isdir(web_dir):
for web_file in ["signals.sx", "adapter-html.sx", "adapter-sx.sx", for web_file in ["signals.sx", "adapter-html.sx", "adapter-sx.sx",
@@ -385,9 +392,9 @@ class OcamlBridge:
path = os.path.normpath(os.path.join(web_dir, web_file)) path = os.path.normpath(os.path.join(web_dir, web_file))
if os.path.isfile(path): if os.path.isfile(path):
all_files.append(path) all_files.append(path)
# Spec library files (loaded after adapters) # Library files loaded after adapters (depend on scope primitives)
for spec_lib in ["freeze.sx"]: for lib_file in ["freeze.sx"]:
path = os.path.normpath(os.path.join(spec_dir, spec_lib)) path = os.path.normpath(os.path.join(lib_dir, lib_file))
if os.path.isfile(path): if os.path.isfile(path):
all_files.append(path) all_files.append(path)