From fb1ec25c69190303df9ce18b5d24da82c5b59e87 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Mon, 15 Jun 2026 10:16:47 -0400 Subject: [PATCH] Fix clojure.walk to descend lists and seqs (jolt-khk) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit walk only handled vector?/map? and fell through :else for everything else, so postwalk over a quoted list (a plist) never touched its elements — postwalk-replace with symbol keys silently no-op'd, which broke clojure.template/apply-template (found during reitit work). Add list? (rebuild as a list) and seq? (map over it) branches after the vector/map ones so concrete collections stay authoritative. Adds walk-spec covering list/seq walking plus a vector/keywordize regression guard and the apply-template trigger. --- src/jolt/clojure/walk.clj | 8 +++++++- test/spec/walk-spec.janet | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 test/spec/walk-spec.janet diff --git a/src/jolt/clojure/walk.clj b/src/jolt/clojure/walk.clj index f2ba5e2..8d73b2b 100644 --- a/src/jolt/clojure/walk.clj +++ b/src/jolt/clojure/walk.clj @@ -1,12 +1,18 @@ ; Jolt Standard Library: clojure.walk ; Tree walking for Clojure data structures. -; Simplified: uses vector? and map? predicates (no list? or seq?). (defn walk [inner outer form] (cond + ; vectors/maps first so seq? can't swallow them (a vector is not seq? on + ; jolt, but keep the concrete branches authoritative) (vector? form) (outer (vec (map inner form))) (map? form) (outer (into (empty form) (map inner form))) + ; lists rebuild as lists, other seqs (incl. macro/template output: cons/ + ; concat/lazy-seq) walk too — without this, postwalk-replace silently no-op'd + ; a quoted list, breaking clojure.template/apply-template (jolt-khk) + (list? form) (outer (apply list (map inner form))) + (seq? form) (outer (map inner form)) :else (outer form))) (defn postwalk diff --git a/test/spec/walk-spec.janet b/test/spec/walk-spec.janet new file mode 100644 index 0000000..90a5aae --- /dev/null +++ b/test/spec/walk-spec.janet @@ -0,0 +1,23 @@ +# Specification: clojure.walk must descend lists and seqs, not just vectors/maps +# (jolt-khk). postwalk-replace with symbol keys over a quoted list silently +# no-op'd because walk only handled vector?/map? and fell through for list?/seq? +# — which broke clojure.template/apply-template (found during reitit work). +(use ../support/harness) + +(defspec "clojure.walk / lists + seqs" + ["postwalk-replace symbol keys in a list" "(quote (+ 2 2))" + "(do (require (quote [clojure.walk :as w])) (w/postwalk-replace {(quote x) 2} (quote (+ x x))))"] + ["postwalk descends a list" "(quote (:a :a))" + "(do (require (quote [clojure.walk :as w])) (w/postwalk (fn [n] (if (symbol? n) :a n)) (quote (x y))))"] + ["prewalk-replace in a list" "(quote (* 3 3))" + "(do (require (quote [clojure.walk :as w])) (w/prewalk-replace {(quote *) (quote *) (quote y) 3} (quote (* y y))))"] + ["nested list + vector" "(quote (1 [2 1]))" + "(do (require (quote [clojure.walk :as w])) (w/postwalk-replace {:a 1 :b 2} (quote (:a [:b :a]))))"] + # vectors/maps still work (regression guard for the existing behavior) + ["postwalk-replace in a vector" "[:one 2 :one]" + "(do (require (quote [clojure.walk :as w])) (w/postwalk-replace {1 :one} [1 2 1]))"] + ["keywordize-keys still works" "{:a 1}" + "(do (require (quote [clojure.walk :as w])) (w/keywordize-keys {\"a\" 1}))"] + # clojure.template/apply-template (the real-world trigger) substitutes now + ["apply-template substitutes" "(quote (+ 1 2))" + "(do (require (quote [clojure.template :as t])) (t/apply-template (quote [x y]) (quote (+ x y)) (quote (1 2))))"])