Fix import resolution: correct library paths + hook type mismatch

Root causes of server [http-load] errors:
1. _import_hook passed pre-computed string key to library_loaded_p
   which calls library_name_key(string) → sx_to_list(string) → crash.
   Fix: pass original list spec, not the string key.
2. resolve_library_path didn't check web/lib/ for (sx dom), (sx browser),
   (web boot-helpers). These libraries use namespace prefixes that don't
   match their file locations.

Server startup errors: 190 → 0.
2683/2684 tests pass (1 known: define-library import clause — spec gap).
New test file: spec/tests/test-import-bind.sx

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 23:44:22 +00:00
parent 191981a22b
commit 6e216038ba
3 changed files with 95 additions and 10 deletions

View File

@@ -291,15 +291,20 @@ let resolve_library_path lib_spec =
let parts = match lib_spec with List l | ListRef { contents = l } -> l | _ -> [] in
match List.map (fun v -> match v with Symbol s -> s | String s -> s | _ -> "") parts with
| ["sx"; name] ->
(* Check spec/ first, then lib/ *)
(* Check spec/ first, then lib/, then web/lib/ (dom.sx, browser.sx live there) *)
let spec_path = Filename.concat !_spec_base (name ^ ".sx") in
let lib_path = Filename.concat !_lib_base (name ^ ".sx") in
let web_lib_path = Filename.concat (Filename.concat !_web_base "lib") (name ^ ".sx") in
if Sys.file_exists spec_path then Some spec_path
else if Sys.file_exists lib_path then Some lib_path
else if Sys.file_exists web_lib_path then Some web_lib_path
else None
| ["web"; name] ->
let path = Filename.concat !_web_base (name ^ ".sx") in
if Sys.file_exists path then Some path else None
let lib_path = Filename.concat (Filename.concat !_web_base "lib") (name ^ ".sx") in
if Sys.file_exists path then Some path
else if Sys.file_exists lib_path then Some lib_path
else None
| [prefix; name] ->
(* Generic: try prefix/name.sx *)
let path = Filename.concat prefix (name ^ ".sx") in
@@ -311,9 +316,14 @@ let resolve_library_path lib_spec =
let _import_env : env option ref = ref None
let load_library_file path =
(* Use eval_expr which has the cek_run import patch — handles nested imports *)
let env = match !_import_env with Some e -> e | None -> Sx_types.make_env () in
let exprs = Sx_parser.parse_file path in
List.iter (fun expr -> ignore (Sx_ref.eval_expr expr (Env env))) exprs
List.iter (fun expr ->
try ignore (Sx_ref.eval_expr expr (Env env))
with Eval_error msg ->
Printf.eprintf "[load-library] %s: %s\n%!" (Filename.basename path) msg
) exprs
(** IO-aware CEK run — handles suspension by dispatching IO requests.
Import requests are handled locally (load .sx file).
@@ -787,11 +797,11 @@ let () =
Loads the .sx file for the library, registers it, and returns true. *)
let () =
Sx_types._import_hook := Some (fun lib_spec ->
let key = Sx_ref.library_name_key lib_spec in
if Sx_types.sx_truthy (Sx_ref.library_loaded_p key) then true
if Sx_types.sx_truthy (Sx_ref.library_loaded_p lib_spec) then true
else match resolve_library_path lib_spec with
| Some path ->
(try load_library_file path; true
(try load_library_file path;
true
with _ -> false)
| None -> false)
@@ -2492,6 +2502,8 @@ let http_mode port =
ignore (env_bind env "_spec-dir" (String spec_base));
ignore (env_bind env "_lib-dir" (String lib_base));
ignore (env_bind env "_web-dir" (String web_base));
(* Set import env so load_library_file (called by _import_hook) uses the main env *)
_import_env := Some env;
let t0 = Unix.gettimeofday () in
(* Core spec + adapters.
Skip: primitives.sx (declarative metadata — all prims native in OCaml),

View File

@@ -495,8 +495,7 @@ and cek_run state =
let op = match request with Dict d -> (match Hashtbl.find_opt d "op" with Some (String s) -> s | _ -> "") | _ -> "" in
if op = "import" then
let lib_spec = match request with Dict d -> (match Hashtbl.find_opt d "library" with Some v -> v | _ -> Nil) | _ -> Nil in
let key = library_name_key lib_spec in
let resolved = sx_truthy (library_loaded_p key) ||
let resolved = sx_truthy (library_loaded_p lib_spec) ||
(match !_import_hook with Some hook -> hook lib_spec | None -> false) in
if resolved then run (cek_resume final Nil)
else raise (Eval_error "IO suspension in non-IO context")
@@ -834,8 +833,7 @@ let cek_run_iterative state =
let op = match request with Dict d -> (match Hashtbl.find_opt d "op" with Some (String s) -> s | _ -> "") | _ -> "" in
if op = "import" then begin
let lib_spec = match request with Dict d -> (match Hashtbl.find_opt d "library" with Some v -> v | _ -> Nil) | _ -> Nil in
let key = library_name_key lib_spec in
let resolved = sx_truthy (library_loaded_p key) ||
let resolved = sx_truthy (library_loaded_p lib_spec) ||
(match !_import_hook with Some hook -> hook lib_spec | None -> false) in
if resolved then begin
s := cek_resume !s Nil;