Fix defhandler: native special form in make_server_env
defhandler was only available via web-forms.sx which had load dependencies that failed. Now registered as a native special form in make_server_env, works in both coroutine and HTTP modes. Key fix: custom special forms receive [List args; Env eval_env], not flat args. The handler is now bound in the eval env, not the make_server_env closure env. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -692,6 +692,37 @@ let make_server_env () =
|
||||
setup_io_bridges env;
|
||||
setup_html_tags env;
|
||||
setup_io_env env;
|
||||
(* defhandler — native special form. Called by CEK as (handler [raw-args; Env eval-env]).
|
||||
Registers handler as handler:name in the eval env. *)
|
||||
ignore (Sx_ref.register_special_form (String "defhandler") (NativeFn ("defhandler", fun sf_args ->
|
||||
(* Custom special forms receive [List args; Env eval_env] *)
|
||||
let raw_args, eval_env = match sf_args with
|
||||
| [List a; Env e] | [ListRef { contents = a }; Env e] -> (a, e)
|
||||
| _ -> ([], env) in
|
||||
match raw_args with
|
||||
| name_sym :: rest ->
|
||||
let name = match name_sym with Symbol s -> s | String s -> s | _ -> Sx_types.inspect name_sym in
|
||||
let rec parse_opts acc = function
|
||||
| Keyword k :: v :: rest -> Hashtbl.replace acc k v; parse_opts acc rest
|
||||
| rest -> (acc, rest) in
|
||||
let opts = Hashtbl.create 4 in
|
||||
let (_, remaining) = parse_opts opts rest in
|
||||
let params, body = match remaining with
|
||||
| List p :: b :: _ -> (p, b) | List p :: [] -> (p, Nil) | _ -> ([], Nil) in
|
||||
let hdef = Hashtbl.create 8 in
|
||||
Hashtbl.replace hdef "__type" (String "handler");
|
||||
Hashtbl.replace hdef "name" (String name);
|
||||
Hashtbl.replace hdef "params" (List params);
|
||||
Hashtbl.replace hdef "body" body;
|
||||
Hashtbl.replace hdef "closure" (Env eval_env);
|
||||
Hashtbl.replace hdef "method" (match Hashtbl.find_opt opts "method" with
|
||||
| Some (Keyword m) -> String m | Some v -> v | None -> String "get");
|
||||
Hashtbl.replace hdef "path" (match Hashtbl.find_opt opts "path" with Some v -> v | None -> Nil);
|
||||
Hashtbl.replace hdef "csrf" (match Hashtbl.find_opt opts "csrf" with Some v -> v | None -> Bool true);
|
||||
Hashtbl.replace hdef "returns" (match Hashtbl.find_opt opts "returns" with Some v -> v | None -> String "element");
|
||||
ignore (env_bind eval_env ("handler:" ^ name) (Dict hdef));
|
||||
Dict hdef
|
||||
| _ -> Nil)));
|
||||
(* Initialize trampoline ref so HO primitives (map, filter, etc.)
|
||||
can call SX lambdas. Must be done here because Sx_ref is only
|
||||
available at the binary level, not in the library. *)
|
||||
@@ -1840,7 +1871,48 @@ let http_setup_declarative_stubs env =
|
||||
noop "define-primitive";
|
||||
noop "deftype";
|
||||
noop "defeffect";
|
||||
noop "define-page-helper"
|
||||
noop "define-page-helper";
|
||||
(* defhandler — register as native special form so it works without web-forms.sx.
|
||||
Parses the handler args and stores as handler:name in the env. *)
|
||||
ignore (Sx_ref.register_special_form (String "defhandler") (NativeFn ("defhandler", fun args ->
|
||||
match args with
|
||||
| name_sym :: rest ->
|
||||
let name = match name_sym with Symbol s -> s | String s -> s | _ -> Sx_types.inspect name_sym in
|
||||
(* Parse keyword opts and find params/body *)
|
||||
let rec parse_opts acc = function
|
||||
| Keyword k :: v :: rest -> Hashtbl.replace acc k v; parse_opts acc rest
|
||||
| rest -> (acc, rest)
|
||||
in
|
||||
let opts = Hashtbl.create 4 in
|
||||
let (_, remaining) = parse_opts opts rest in
|
||||
let params, body = match remaining with
|
||||
| List p :: b :: _ -> (p, b)
|
||||
| List p :: [] -> (p, Nil)
|
||||
| _ -> ([], Nil)
|
||||
in
|
||||
let hdef = Hashtbl.create 8 in
|
||||
Hashtbl.replace hdef "__type" (String "handler");
|
||||
Hashtbl.replace hdef "name" (String name);
|
||||
Hashtbl.replace hdef "params" (List params);
|
||||
Hashtbl.replace hdef "body" body;
|
||||
Hashtbl.replace hdef "closure" (Env env);
|
||||
Hashtbl.replace hdef "method" (match Hashtbl.find_opt opts "method" with
|
||||
| Some (Keyword m) -> String m | Some v -> v | None -> String "get");
|
||||
Hashtbl.replace hdef "path" (match Hashtbl.find_opt opts "path" with
|
||||
| Some v -> v | None -> Nil);
|
||||
Hashtbl.replace hdef "csrf" (match Hashtbl.find_opt opts "csrf" with
|
||||
| Some v -> v | None -> Bool true);
|
||||
Hashtbl.replace hdef "returns" (match Hashtbl.find_opt opts "returns" with
|
||||
| Some v -> v | None -> String "element");
|
||||
let handler_key = "handler:" ^ name in
|
||||
ignore (env_bind env handler_key (Dict hdef));
|
||||
Dict hdef
|
||||
| _ -> Nil)));
|
||||
(* Also stub defquery/defaction/defrelation/defstyle as no-ops *)
|
||||
noop "defquery";
|
||||
noop "defaction";
|
||||
noop "defrelation";
|
||||
noop "defstyle"
|
||||
|
||||
let http_setup_platform_constructors env =
|
||||
(* Platform constructor functions expected by evaluator.sx.
|
||||
|
||||
Reference in New Issue
Block a user