From 4eb2cf5c46e281303e373e25c2254b8ba3a2d79c Mon Sep 17 00:00:00 2001 From: Yogthos Date: Sun, 7 Jun 2026 17:29:39 -0400 Subject: [PATCH] core: move let and loop to the syntax tier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit let -> let* with destructuring pre-expanded via destructure (now exposed as a clojure.core fn, which it is in Clojure too) so the compiler sees plain bindings — analyze-bindings rejects patterns as uncompilable. loop -> loop* with raw bindings, matching the prior Janet macro: loop can't pre-destructure without breaking recur arity, so the interpreter handles pattern loops and the compiler falls back. conformance 228x3, fixpoint, clojure-test-suite 3930. --- jolt-core/clojure/core/00-syntax.clj | 16 ++++++++++++++++ src/jolt/core.janet | 24 ++---------------------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/jolt-core/clojure/core/00-syntax.clj b/jolt-core/clojure/core/00-syntax.clj index 101241f..e558ea3 100644 --- a/jolt-core/clojure/core/00-syntax.clj +++ b/jolt-core/clojure/core/00-syntax.clj @@ -64,6 +64,22 @@ ;; multi-arity); fn is just the public spelling. (defmacro fn [& args] `(fn* ~@args)) +;; let desugars destructuring patterns to plain bindings (via destructure) so the +;; COMPILER sees only plain symbols — analyze-bindings rejects patterns as +;; uncompilable, relying on this macro to have expanded them. (The interpreter +;; could destructure let* directly, but the compiler can't.) let* is sequential, so +;; a later init can reference an earlier destructured name. destructure is a +;; clojure.core fn; calling it at expansion time is fine — it's interned at init. +(defmacro let [bindings & body] + `(let* ~(destructure bindings) ~@body)) + +;; loop -> loop* with raw bindings (matching the prior Janet macro). loop can't +;; pre-destructure like let: that would change the binding count and break recur's +;; arity. The interpreter destructures loop* directly; for destructuring loops the +;; compiler falls back (a pre-existing limitation). +(defmacro loop [bindings & body] + `(loop* ~bindings ~@body)) + ;; A fresh jolt symbol inside a macro body (a bare (gensym) returns a Janet symbol ;; the destructurer rejects). This defn compiles fine: by the time a tier triggers ;; the analyzer build the kernel is in place (the build is gated until then). diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 50522c0..00e6a81 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -2433,25 +2433,6 @@ (while (< i n) (d-process (in bindings i) (in bindings (+ i 1)) out) (+= i 2)) (tuple/slice out)) -(defn core-let - "Macro: (let [bindings] body) → (let* [plain-bindings] body), expanding - destructuring patterns so the compiler/interpreter see only plain symbols." - [bindings & body] - (def result @[]) - (array/push result {:jolt/type :symbol :ns nil :name "let*"}) - (array/push result (core-destructure bindings)) - (each b body (array/push result b)) - result) - -(defn core-loop - "Macro: (loop [bindings] body) → (loop* [bindings] body)" - [bindings & body] - (def result @[]) - (array/push result {:jolt/type :symbol :ns nil :name "loop*"}) - (array/push result bindings) - (each b body (array/push result b)) - result) - # Protocol implementation — methods dispatch via type registry (defn core-defprotocol [protocol-name & sigs] (def result @[]) @@ -3424,8 +3405,7 @@ "remove-all-methods" core-remove-all-methods "prefer-method" core-prefer-method "Object" core-Object - "let" core-let - "loop" core-loop + "destructure" core-destructure "defprotocol" core-defprotocol "extend-type" core-extend-type "extend-protocol" core-extend-protocol @@ -3487,7 +3467,7 @@ (defn core-macro-names "Set of core binding names that are macros." [] - @{"when-let" true "defn" true "defn-" 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}) + @{"when-let" true "defn" true "defn-" 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]