From d8ffe386e6acde3b19952822309f44ef6c0d889e Mon Sep 17 00:00:00 2001 From: Yogthos Date: Wed, 10 Jun 2026 12:25:45 -0400 Subject: [PATCH] edn: fix strict-map? regression in edn->value; apply EDN built-in tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI caught what the local per-change gate missed (clojure-stdlib-suite-test wasn't in the habitual gate list): strict map? (PR #28) correctly stopped treating tagged structs as maps, which silently disabled edn->value's (and (map? x) (= :jolt/set ...)) set-form branch — edn/read-string returned raw reader forms for #{...}. Reader FORMS are now detected by :jolt/type directly, never via map?. While here, EDN's built-in tagged elements are applied properly: a :jolt/tagged form routes through the registered data reader (__read-tagged, a ctx-capturing core fn over :data-readers), so (edn/read-string "#uuid ...") yields a real UUID value and #inst an instant — per the EDN spec. The read_string battery goes 47 -> 50 (above the old 49 baseline: the uuid asserts pass now); baseline raised to 50. --- src/jolt/clojure/edn.clj | 13 +++++++++---- src/jolt/evaluator.janet | 9 +++++++++ test/integration/clojure-stdlib-suite-test.janet | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/jolt/clojure/edn.clj b/src/jolt/clojure/edn.clj index ea271b3..5181e99 100644 --- a/src/jolt/clojure/edn.clj +++ b/src/jolt/clojure/edn.clj @@ -10,10 +10,15 @@ ;; maps/vectors/lists. (Lists stay lists — EDN never evaluates them as code.) (defn- edn->value [x] (cond - (and (map? x) (= :jolt/set (get x :jolt/type))) (set (map edn->value (get x :value))) - ;; Only untagged structs are real maps; symbols/chars/tagged literals are also - ;; struct? (=> map?) but carry a :jolt/type and must pass through unchanged. - (and (map? x) (nil? (get x :jolt/type))) + ;; Reader FORMS are detected by :jolt/type tag, never by map? — strict map? + ;; (correctly) excludes tagged structs, so the old (and (map? x) ...) guard + ;; would skip them. + (= :jolt/set (get x :jolt/type)) (set (map edn->value (get x :value))) + ;; EDN built-in tagged elements (#uuid/#inst, plus registered readers): + ;; apply the data reader to the read form (no evaluation involved). + (= :jolt/tagged (get x :jolt/type)) + (__read-tagged (get x :tag) (edn->value (get x :form))) + (map? x) (into {} (map (fn [e] [(edn->value (key e)) (edn->value (val e))]) x)) (vector? x) (mapv edn->value x) (seq? x) (map edn->value x) diff --git a/src/jolt/evaluator.janet b/src/jolt/evaluator.janet index 6d743de..a860cb0 100644 --- a/src/jolt/evaluator.janet +++ b/src/jolt/evaluator.janet @@ -1112,6 +1112,15 @@ the-form)) the-form))) (ns-intern core "macroexpand-1" expand-1) + # Apply a registered data reader to an already-read form (EDN built-in tags + # #uuid/#inst and any registered reader). Throws on an unknown tag. + (ns-intern core "__read-tagged" + (fn [tag form] + (def data-readers (get (ctx :env) :data-readers)) + (def reader-fn (if data-readers (get data-readers tag))) + (if reader-fn + (reader-fn form) + (error (string "No reader function for tag " tag))))) # macroexpand: expand repeatedly until the head is no longer a macro (the # form's SUBFORMS are not expanded, matching Clojure). (ns-intern core "macroexpand" diff --git a/test/integration/clojure-stdlib-suite-test.janet b/test/integration/clojure-stdlib-suite-test.janet index 1b4417e..c98f8bc 100644 --- a/test/integration/clojure-stdlib-suite-test.janet +++ b/test/integration/clojure-stdlib-suite-test.janet @@ -15,7 +15,7 @@ # clojure.edn reads via clojure.core/read-string (opts/:eof + nil/blank) and # constructs set/nested values. Only #uuid remains (no real uuid type) — # jolt-b7y. Guard the passing subset. - ["clojure/edn_test/read_string.cljc" 49 false]]) + ["clojure/edn_test/read_string.cljc" 50 false]]) (def root "test/clojure-stdlib") (def per-file-timeout 6)