diff --git a/hosts/ocaml/bin/sx_server.ml b/hosts/ocaml/bin/sx_server.ml index ff90249..cc5b1b3 100644 --- a/hosts/ocaml/bin/sx_server.ml +++ b/hosts/ocaml/bin/sx_server.ml @@ -711,6 +711,18 @@ let register_jit_hook env = (* ====================================================================== *) +(* Re-assert host-provided extension points after loading .sx files. + evaluator.sx defines *custom-special-forms* and register-special-form! + which shadow the native bindings from setup_evaluator_bridge. *) +let rebind_host_extensions env = + Hashtbl.replace env.bindings "register-special-form!" + (NativeFn ("register-special-form!", fun args -> + match args with + | [String name; handler] -> + ignore (Sx_ref.register_special_form (String name) handler); Nil + | _ -> raise (Eval_error "register-special-form!: expected (name handler)"))); + ignore (env_bind env "*custom-special-forms*" Sx_ref.custom_special_forms) + (* Command dispatch *) (* ====================================================================== *) @@ -727,6 +739,9 @@ let rec dispatch env cmd = ignore (Sx_ref.eval_expr expr (Env env)); incr count ) exprs; + (* Rebind host extension points after .sx load — evaluator.sx + defines *custom-special-forms* which shadows the native dict *) + rebind_host_extensions env; send_ok_value (Number (float_of_int !count)) with | Eval_error msg -> send_error msg @@ -1142,7 +1157,9 @@ let cli_load_files env files = ignore (Sx_ref.eval_expr expr (Env env)) ) exprs end - ) files + ) files; + (* Rebind after load in case .sx files shadowed host extension points *) + rebind_host_extensions env let cli_mode mode = let env = make_server_env () in diff --git a/run-tests.sh b/run-tests.sh index b2c0889..ebba452 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -101,6 +101,16 @@ check('defhandler via eval', '(has-key? (defhandler test-h (&key x) x) \"__type\ check('definition-form-extensions populated', '(> (len *definition-form-extensions*) 0)', 'true') check('RENDER_HTML_FORMS has defstyle', '(contains? RENDER_HTML_FORMS \"defstyle\")', 'true') +# Env-shadowing regression: custom forms survive evaluator.sx load +bridge2 = OcamlSync() +bridge2.eval('(register-special-form! \"shadow-test\" (fn (args env) 42))') +bridge2.load('spec/evaluator.sx') +check('custom form survives evaluator.sx load', + bridge2.eval('(has-key? *custom-special-forms* \"shadow-test\")'), 'true') +bridge2.eval('(register-special-form! \"post-load\" (fn (args env) 99))') +check('custom form callable after evaluator.sx load', + bridge2.eval('(post-load 1)'), '99') + print(f'\\nResults: {ok} passed, {fail} failed') import sys; sys.exit(1 if fail > 0 else 0) "