Wire adapter-html.sx into OCaml server, replacing hand-written renderer
sx_server.ml: sx_render_to_html() calls the SX adapter-html.sx render-to-html via CEK eval, falling back to Sx_render.render_to_html if adapter not loaded. CLI --render mode now loads render.sx + adapter-html.sx. sx_primitives.ml: Added ~25 primitives needed by adapter-html.sx: scope-push!/pop!/peek/emit!, emitted, provide-push!/pop! (hashtable stack), lambda?/island?/component?/macro?, component-closure/name/params/body/ has-children?, lambda-closure/params/body, is-else-clause?, for-each-indexed, empty-dict?, make-raw-html, raw-html-content run_tests.ml: Loads render.sx + adapter-html.sx for test-render-html.sx. Registers trampoline, eval-expr, scope stubs, expand-macro, cond-scheme?. Status: 1105/1114 OCaml tests pass. 8 remaining failures are env-merge edge cases in render-lambda-html/component-children/island rendering — same adapter code works in JS (143/143). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -700,6 +700,113 @@ let () =
|
||||
match args with
|
||||
| [String name] -> Bool (Hashtbl.mem primitives name)
|
||||
| _ -> Bool false);
|
||||
|
||||
(* ---- Scope stack primitives (for adapter-html.sx tree-walk rendering) ---- *)
|
||||
let scope_stacks : (string, (value * value list) list) Hashtbl.t = Hashtbl.create 8 in
|
||||
register "scope-push!" (fun args ->
|
||||
match args with
|
||||
| [String name; value] ->
|
||||
let stack = try Hashtbl.find scope_stacks name with Not_found -> [] in
|
||||
Hashtbl.replace scope_stacks name ((value, []) :: stack); Nil
|
||||
| [String name] ->
|
||||
let stack = try Hashtbl.find scope_stacks name with Not_found -> [] in
|
||||
Hashtbl.replace scope_stacks name ((Nil, []) :: stack); Nil
|
||||
| _ -> Nil);
|
||||
register "scope-pop!" (fun args ->
|
||||
match args with
|
||||
| [String name] ->
|
||||
let stack = try Hashtbl.find scope_stacks name with Not_found -> [] in
|
||||
(match stack with _ :: rest -> Hashtbl.replace scope_stacks name rest | [] -> ()); Nil
|
||||
| _ -> Nil);
|
||||
register "scope-peek" (fun args ->
|
||||
match args with
|
||||
| [String name] ->
|
||||
(match Hashtbl.find_opt scope_stacks name with
|
||||
| Some ((v, _) :: _) -> v
|
||||
| _ -> Nil)
|
||||
| _ -> Nil);
|
||||
register "scope-emit!" (fun args ->
|
||||
match args with
|
||||
| [String name; value] ->
|
||||
(match Hashtbl.find_opt scope_stacks name with
|
||||
| Some ((v, emitted) :: rest) ->
|
||||
Hashtbl.replace scope_stacks name ((v, emitted @ [value]) :: rest)
|
||||
| _ -> ()); Nil
|
||||
| _ -> Nil);
|
||||
register "emitted" (fun args ->
|
||||
match args with
|
||||
| [String name] ->
|
||||
(match Hashtbl.find_opt scope_stacks name with
|
||||
| Some ((_, emitted) :: _) -> List emitted
|
||||
| _ -> List [])
|
||||
| _ -> List []);
|
||||
register "provide-push!" (fun args ->
|
||||
Hashtbl.find primitives "scope-push!" args);
|
||||
register "provide-pop!" (fun args ->
|
||||
Hashtbl.find primitives "scope-pop!" args);
|
||||
|
||||
(* ---- Predicates needed by adapter-html.sx ---- *)
|
||||
register "lambda?" (fun args ->
|
||||
match args with [Lambda _] -> Bool true | _ -> Bool false);
|
||||
register "island?" (fun args ->
|
||||
match args with [Island _] -> Bool true | _ -> Bool false);
|
||||
register "is-else-clause?" (fun args ->
|
||||
match args with
|
||||
| [Keyword "else"] -> Bool true
|
||||
| [Bool true] -> Bool true
|
||||
| _ -> Bool false);
|
||||
register "component?" (fun args ->
|
||||
match args with [Component _] -> Bool true | [Island _] -> Bool true | _ -> Bool false);
|
||||
register "lambda-closure" (fun args ->
|
||||
match args with [Lambda l] -> Env l.l_closure | _ -> Nil);
|
||||
register "component-closure" (fun args ->
|
||||
match args with
|
||||
| [Component c] -> Env c.c_closure
|
||||
| [Island i] -> Env i.i_closure
|
||||
| _ -> Nil);
|
||||
register "component-has-children?" (fun args ->
|
||||
match args with
|
||||
| [Component c] -> Bool (List.mem "children" c.c_params)
|
||||
| [Island i] -> Bool (List.mem "children" i.i_params)
|
||||
| _ -> Bool false);
|
||||
register "component-name" (fun args ->
|
||||
match args with
|
||||
| [Component c] -> String c.c_name
|
||||
| [Island i] -> String i.i_name
|
||||
| _ -> Nil);
|
||||
register "component-params" (fun args ->
|
||||
match args with
|
||||
| [Component c] -> List (List.map (fun s -> String s) c.c_params)
|
||||
| [Island i] -> List (List.map (fun s -> String s) i.i_params)
|
||||
| _ -> List []);
|
||||
register "component-body" (fun args ->
|
||||
match args with
|
||||
| [Component c] -> c.c_body
|
||||
| [Island i] -> i.i_body
|
||||
| _ -> Nil);
|
||||
register "macro?" (fun args ->
|
||||
match args with [Macro _] -> Bool true | _ -> Bool false);
|
||||
register "for-each-indexed" (fun args ->
|
||||
match args with
|
||||
| [f; (List items | ListRef { contents = items })] ->
|
||||
List.iteri (fun i x -> ignore (call_any f [Number (float_of_int i); x])) items; Nil
|
||||
| _ -> raise (Eval_error "for-each-indexed: expected (fn list)"));
|
||||
register "lambda-params" (fun args ->
|
||||
match args with
|
||||
| [Lambda l] -> List (List.map (fun s -> String s) l.l_params)
|
||||
| _ -> List []);
|
||||
register "lambda-body" (fun args ->
|
||||
match args with [Lambda l] -> l.l_body | _ -> Nil);
|
||||
(* expand-macro is registered later by run_tests.ml / sx_server.ml
|
||||
because it needs eval_expr which creates a dependency cycle *);
|
||||
register "empty-dict?" (fun args ->
|
||||
match args with
|
||||
| [Dict d] -> Bool (Hashtbl.length d = 0)
|
||||
| _ -> Bool true);
|
||||
register "make-raw-html" (fun args ->
|
||||
match args with [String s] -> RawHTML s | _ -> Nil);
|
||||
register "raw-html-content" (fun args ->
|
||||
match args with [RawHTML s] -> String s | _ -> String "");
|
||||
register "get-primitive" (fun args ->
|
||||
match args with
|
||||
| [String name] ->
|
||||
|
||||
Reference in New Issue
Block a user