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_io_bridges env;
|
||||||
setup_html_tags env;
|
setup_html_tags env;
|
||||||
setup_io_env 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.)
|
(* Initialize trampoline ref so HO primitives (map, filter, etc.)
|
||||||
can call SX lambdas. Must be done here because Sx_ref is only
|
can call SX lambdas. Must be done here because Sx_ref is only
|
||||||
available at the binary level, not in the library. *)
|
available at the binary level, not in the library. *)
|
||||||
@@ -1840,7 +1871,48 @@ let http_setup_declarative_stubs env =
|
|||||||
noop "define-primitive";
|
noop "define-primitive";
|
||||||
noop "deftype";
|
noop "deftype";
|
||||||
noop "defeffect";
|
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 =
|
let http_setup_platform_constructors env =
|
||||||
(* Platform constructor functions expected by evaluator.sx.
|
(* Platform constructor functions expected by evaluator.sx.
|
||||||
|
|||||||
Reference in New Issue
Block a user