From 3dafa60e658ef4bd2aadff782d3a4b05bc29e503 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Sun, 7 Jun 2026 01:13:22 -0400 Subject: [PATCH] core: move binding macro to overlay Phase 3 batch 6 (jolt-1j0). binding installs an array-map var->value thread frame and restores it on exit via try/finally. Dynamic rebinding is seen by called fns. This completes the cleanly-portable safe macros (19 total). conformance 228/228 x3, full suite green. --- jolt-core/clojure/core/30-macros.clj | 9 +++++++++ src/jolt/core.janet | 28 +--------------------------- test/spec/macros-spec.janet | 5 ++++- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/jolt-core/clojure/core/30-macros.clj b/jolt-core/clojure/core/30-macros.clj index 3c2a8bd..34b9427 100644 --- a/jolt-core/clojure/core/30-macros.clj +++ b/jolt-core/clojure/core/30-macros.clj @@ -107,6 +107,15 @@ [] fnspecs)] `(let* [~@binds] ~@body))) +;; Dynamic binding: install a thread-binding frame of var->value (array-map keeps +;; var-get happy, unlike a phm), restore on exit. +(defmacro binding [bindings & body] + (let [pairs (reduce (fn [acc p] (conj (conj acc `(var ~(first p))) (second p))) + [] (partition 2 bindings))] + `(let* [frame# (array-map ~@pairs)] + (push-thread-bindings frame#) + (try (do ~@body) (finally (pop-thread-bindings)))))) + ;; condp: clauses are test-expr result-expr, or test-expr :>> result-fn (calls ;; result-fn on the truthy (pred test-expr value)); a lone trailing expr is the ;; default. The recursive emit builds a nested if chain. diff --git a/src/jolt/core.janet b/src/jolt/core.janet index e834c57..dad353b 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -2264,31 +2264,6 @@ (defn core-intern [ns-name sym-name val] val) -(defn core-binding - "Macro: (binding [var val ...] body...) - Uses array-map (plain struct) to store binding frame - to avoid PHM get() incompatibility with var-get." - [bindings & body] - (def frame-pairs @[]) - (var i 0) - (let [n (length bindings)] - (while (< i n) - (array/push frame-pairs - @[{:jolt/type :symbol :ns nil :name "var"} (in bindings i)]) - (array/push frame-pairs (in bindings (+ i 1))) - (+= i 2))) - (def hm-form (array/insert frame-pairs 0 - {:jolt/type :symbol :ns nil :name "array-map"})) - @[{:jolt/type :symbol :ns nil :name "let*"} - [{:jolt/type :symbol :ns nil :name "frame"} hm-form] - @[{:jolt/type :symbol :ns nil :name "push-thread-bindings"} - {:jolt/type :symbol :ns nil :name "frame"}] - @[{:jolt/type :symbol :ns nil :name "try"} - @[{:jolt/type :symbol :ns nil :name "do"} ;body] - @[{:jolt/type :symbol :ns nil :name "finally"} - @[{:jolt/type :symbol :ns nil :name "pop-thread-bindings"}]]]]) - - (defn- defn->def "Shared expansion for defn/defn-: (name doc-string? attr-map? params body...) or (name doc-string? attr-map? ([params] body)... attr-map?) -> (def name (fn* ...))." @@ -3695,7 +3670,6 @@ "alter-meta!" core-alter-meta! "reset-meta!" core-reset-meta! "intern" core-intern - "binding" core-binding "push-thread-bindings" core-push-thread-bindings "pop-thread-bindings" core-pop-thread-bindings # Dynamic vars — stubs for SCI bootstrap @@ -3710,7 +3684,7 @@ (defn core-macro-names "Set of core binding names that are macros." [] - @{"and" true "or" true "cond" true "case" true "for" true "when" true "when-not" true "when-let" true "defn" true "defn-" true "declare" true "fn" true "let" true "loop" true "defrecord" true "defprotocol" true "extend-type" true "extend-protocol" true "extend" true "reify" true "proxy" true "definterface" true "binding" true "lazy-seq" true "lazy-cat" true "cond->" true "->" true "->>" true "doseq" true}) + @{"and" true "or" true "cond" true "case" true "for" true "when" true "when-not" true "when-let" true "defn" true "defn-" true "declare" true "fn" true "let" true "loop" true "defrecord" true "defprotocol" true "extend-type" true "extend-protocol" true "extend" true "reify" true "proxy" true "definterface" true "lazy-seq" true "lazy-cat" true "cond->" true "->" true "->>" true "doseq" true}) (def init-core! (fn [& args] diff --git a/test/spec/macros-spec.janet b/test/spec/macros-spec.janet index 505d1e7..e21f5c0 100644 --- a/test/spec/macros-spec.janet +++ b/test/spec/macros-spec.janet @@ -68,4 +68,7 @@ ["condp match" ":two" "(condp = 2 1 :one 2 :two 3 :three)"] ["condp default" ":else" "(condp = 9 1 :one 2 :two :else)"] ["condp :>> form" "\"got 2\"" "(condp some [1 2 3] #{0 9} :>> (fn [x] (str \"got \" x)) #{2 6} :>> (fn [x] (str \"got \" x)))"] - ["condp no match" ":threw" "(try (condp = 9 1 :one) (catch :default e :threw))"]) + ["condp no match" ":threw" "(try (condp = 9 1 :one) (catch :default e :threw))"] + ["binding rebinds" "99" "(do (def ^:dynamic *bx* 10) (binding [*bx* 99] *bx*))"] + ["binding restores" "10" "(do (def ^:dynamic *by* 10) (binding [*by* 99] *by*) *by*)"] + ["binding seen by fn" "7" "(do (def ^:dynamic *bz* 0) (defn rdz [] *bz*) (binding [*bz* 7] (rdz)))"])