From d31304f552ff495e358b73abcc4b2622a6af8e21 Mon Sep 17 00:00:00 2001 From: Reuben Brooks Date: Mon, 24 Aug 2026 09:20:52 -0500 Subject: [PATCH 1/2] feat: expose native extension features to Shen Batteries --- boot.lua | 24 ++++++++++++++++++++++++ test/features_spec.lua | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 test/features_spec.lua diff --git a/boot.lua b/boot.lua index d003e0e..3f82a9e 100644 --- a/boot.lua +++ b/boot.lua @@ -1586,6 +1586,30 @@ local function initialise() local r local fn = P.F["shen.initialise"] if fn then r = fn() end + -- Initialise the Shen Batteries feature registry from capabilities detected + -- by native extension installers. The Batteries module loader asks the + -- port for `(shen.x.features.current)` before loading a module; leaving the + -- registry unbound makes that query fail even though the extension API is + -- otherwise available. Keep this derived from backend globals so + -- SHEN_X_SHA256=pure (and ports without native backends) advertise no host + -- capability while retaining the portable implementation. + if P.F["shen.x.features.current"] then + local detected = {} + local function add_backend(global, feature) + local backend = P.GLOBALS[global] + if backend == R.intern("host") then + detected[#detected + 1] = R.intern(feature) + end + end + add_backend("shen.x.*sha256-backend*", "shen.x/sha256-host") + add_backend("shen.x.*zmq-backend*", "shen.x/zmq-host") + -- The community extension's `features.initialise` additionally hooks the + -- legacy macro registration API (`shen.set-lambda-form-entry`), which is + -- not present in the refreshed 41.2 kernel. Seed its backing global + -- directly; `features.current` is the stable API consumed by Batteries + -- and `features.add` can still extend this list later. + P.GLOBALS["shen.x.features.*features*"] = R.from_table(detected) + end -- Register the lua.* interop entries in Shen's own arity/lambda-form -- tables (needs the *property-vector* the kernel just created). require("lua_interop").post_initialise() diff --git a/test/features_spec.lua b/test/features_spec.lua new file mode 100644 index 0000000..d651fec --- /dev/null +++ b/test/features_spec.lua @@ -0,0 +1,40 @@ +-- Shen Batteries feature bridge (including shen-extensions capabilities). +-- +-- luajit test/features_spec.lua +local shen = require("shen") +shen.boot{ quiet = true } +local R = require("runtime") +local P = shen.prims + +local npass, nfail = 0, 0 +local function check(cond, name) + if cond then npass = npass + 1 + else nfail = nfail + 1; io.write("FAIL: ", name, "\n") end +end + +local function names(xs) + local out = {} + while R.is_cons(xs) do + out[tostring(xs[1])] = true + xs = xs[2] + end + return out +end + +local ok, current = pcall(shen.call, "shen.x.features.current") +check(ok, "features.current is callable after boot") +if ok then + local have = names(current) + local backend = P.GLOBALS["shen.x.*sha256-backend*"] + if backend == R.intern("host") then + check(have["shen.x/sha256-host"] == true, + "host SHA-256 backend is advertised to Batteries") + else + check(have["shen.x/sha256-host"] ~= true, + "non-host SHA-256 backend is not advertised as host") + end + check(not have["shen.x/zmq-host"], "unsupported ZMQ backend is not advertised") +end + +io.write(string.format("features_spec: %d pass, %d fail\n", npass, nfail)) +os.exit(nfail == 0 and 0 or 1) From f1dbd98159050a368379fda120ce632f3403aeb7 Mon Sep 17 00:00:00 2001 From: Reuben Brooks Date: Mon, 24 Aug 2026 09:21:53 -0500 Subject: [PATCH 2/2] test: include feature bridge spec in port suite --- scripts/run-tests.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/run-tests.lua b/scripts/run-tests.lua index 35d4974..887fcfb 100644 --- a/scripts/run-tests.lua +++ b/scripts/run-tests.lua @@ -34,6 +34,7 @@ local specs = { "test/engine_spec.lua", "test/error_robustness_spec.lua", "test/eval_order_spec.lua", + "test/features_spec.lua", "test/interop_spec.lua", "test/io_spec.lua", "test/jit_spec.lua",