From 40b0f525f3e54ce415f76bd28524a0d7464d8e7d Mon Sep 17 00:00:00 2001 From: Yogthos Date: Sun, 7 Jun 2026 16:40:24 -0400 Subject: [PATCH] core: move doseq to the syntax tier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same shortcut as the prior Janet macro — realize a for comprehension with count for side effects, return nil — so for keeps handling :when/:let/:while and multiple bindings. Lives in 00-syntax because the analyzer uses doseq (analyze-try). Added :when/:while/:let/returns-nil regression cases. conformance 228x3, fixpoint, clojure-test-suite 3930, full suite green. --- jolt-core/clojure/core/00-syntax.clj | 6 ++++++ src/jolt/core.janet | 10 +--------- test/spec/control-flow-spec.janet | 6 +++++- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/jolt-core/clojure/core/00-syntax.clj b/jolt-core/clojure/core/00-syntax.clj index 0303c27..b2bebfd 100644 --- a/jolt-core/clojure/core/00-syntax.clj +++ b/jolt-core/clojure/core/00-syntax.clj @@ -95,3 +95,9 @@ (first cls) `(if ~(mk-test (first cls)) ~(nth cls 1) ~(build (drop 2 cls))))))] `(let* [~g ~expr] ~(build clauses)))) + +;; doseq runs body for side effects across the bindings, returning nil. Same +;; shortcut as the prior Janet macro: realize a `for` comprehension with count +;; (for handles :when/:let/:while and multiple bindings). +(defmacro doseq [bindings & body] + `(do (count (for ~bindings (do ~@body nil))) nil)) diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 1bd8041..91e1ef3 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -2364,13 +2364,6 @@ # doseq — like `for` but eager and returns nil. Reuse `for`, force realization # with `count`, discard the result. -(defn core-doseq [bindings & body] - (def for-body @[{:jolt/type :symbol :ns nil :name "do"} ;body nil]) - @[{:jolt/type :symbol :ns nil :name "do"} - @[{:jolt/type :symbol :ns nil :name "count"} - @[{:jolt/type :symbol :ns nil :name "for"} bindings for-body]] - nil]) - # assert — (assert x) / (assert x message). Throws when x is falsy. # resolve stub — returns nil (symbols not found in Jolt's clojure.core) @@ -3174,7 +3167,6 @@ "pop" core-pop "trampoline" core-trampoline "format" core-format - "doseq" core-doseq "first" core-first "rest" core-rest "next" core-next @@ -3562,7 +3554,7 @@ (defn core-macro-names "Set of core binding names that are macros." [] - @{"for" true "when-let" true "defn" true "defn-" 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 "doseq" true}) + @{"for" true "when-let" true "defn" true "defn-" 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}) (def init-core! (fn [& args] diff --git a/test/spec/control-flow-spec.janet b/test/spec/control-flow-spec.janet index ee12855..eab82a6 100644 --- a/test/spec/control-flow-spec.janet +++ b/test/spec/control-flow-spec.janet @@ -69,7 +69,11 @@ ["for :while" "[0 1 2]" "(for [x (range 10) :while (< x 3)] x)"] ["for :let" "[0 1 4]" "(for [x (range 3) :let [sq (* x x)]] sq)"] ["doseq side-effect" "6" "(let [a (atom 0)] (doseq [x [1 2 3]] (swap! a (fn [v] (+ v x)))) @a)"] - ["doseq nested" "4" "(let [c (atom 0)] (doseq [x [1 2] y [10 20]] (swap! c inc)) @c)"]) + ["doseq nested" "4" "(let [c (atom 0)] (doseq [x [1 2] y [10 20]] (swap! c inc)) @c)"] + ["doseq :when" "[1 3]" "(let [a (atom [])] (doseq [x [1 2 3] :when (odd? x)] (swap! a conj x)) @a)"] + ["doseq :while" "6" "(let [a (atom 0)] (doseq [x (range 10) :while (< x 4)] (swap! a + x)) @a)"] + ["doseq :let" "[0 1 4]" "(let [a (atom [])] (doseq [x (range 3) :let [sq (* x x)]] (swap! a conj sq)) @a)"] + ["doseq returns nil" "nil" "(doseq [x [1 2 3]] x)"]) (defspec "control / threading" ["->" "6" "(-> 1 inc (+ 4))"]