Add .sxbc s-expression bytecode format

Bytecode modules are now serialized as s-expressions (.sxbc) in addition
to JSON (.sxbc.json). The .sxbc format is the canonical representation —
content-addressable, parseable by the SX parser, and suitable for CID
referencing. Annotation layers (source maps, variable names, tests, docs)
can reference the bytecode CID without polluting the bytecode itself.

Format: (sxbc version hash (code :arity N :bytecode (...) :constants (...)))

The browser loader tries .sxbc first (via load-sxbc kernel primitive),
falls back to .sxbc.json. Caddy needs .sxbc MIME type to serve the new
format (currently 404s, JSON fallback works).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-27 14:16:22 +00:00
parent 8d3ab040ef
commit e0070041d6
27 changed files with 11379 additions and 65 deletions

View File

@@ -503,6 +503,41 @@ let () =
let d = Hashtbl.create 2 in Hashtbl.replace d "ok" (Bool false); Hashtbl.replace d "error" (String msg); Dict d)
| _ -> raise (Eval_error "try-call: 1 arg"));
(* --- Bytecode loading from s-expression format ---
(sxbc version hash (code :arity N :upvalue-count N :bytecode (...) :constants (...)))
Recursively converts the SX tree into the dict format that loadModule expects. *)
bind "load-sxbc" (fun args ->
match args with
| [List (_ :: _ :: _ :: code_form :: _)] | [List (_ :: _ :: code_form :: _)] ->
let rec convert_code form =
match form with
| List (Symbol "code" :: rest) ->
let d = Hashtbl.create 8 in
let rec parse_kv = function
| Keyword "arity" :: Number n :: rest -> Hashtbl.replace d "arity" (Number n); parse_kv rest
| Keyword "upvalue-count" :: Number n :: rest -> Hashtbl.replace d "upvalue-count" (Number n); parse_kv rest
| Keyword "bytecode" :: List nums :: rest ->
Hashtbl.replace d "bytecode" (List nums); parse_kv rest
| Keyword "constants" :: List consts :: rest ->
Hashtbl.replace d "constants" (List (List.map convert_const consts)); parse_kv rest
| _ :: rest -> parse_kv rest (* skip unknown keywords *)
| [] -> ()
in
parse_kv rest;
Dict d
| _ -> raise (Eval_error ("load-sxbc: expected (code ...), got " ^ type_of form))
and convert_const = function
| List (Symbol "code" :: _) as form -> convert_code form
| List (Symbol "list" :: items) -> List (List.map convert_const items)
| v -> v (* strings, numbers, booleans, nil, symbols, keywords pass through *)
in
let module_val = convert_code code_form in
let code = Sx_vm.code_from_value module_val in
let _result = Sx_vm.execute_module code _vm_globals in
sync_vm_to_env ();
Number (float_of_int (Hashtbl.length _vm_globals))
| _ -> raise (Eval_error "load-sxbc: expected (sxbc version hash (code ...))"));
(* --- List mutation --- *)
bind "append!" (fun args ->
match args with