core: port when-let to the overlay — finishes the Phase 3 macro migration

Last core macro still in Janet. Goes in 00-syntax (not 30-macros with its
if-let/if-some/when-some siblings) because 20-coll's not-empty uses it and
20-coll loads before 30. Via `let` so the binding form may destructure,
matching Clojure — the old Janet version used let* and wouldn't.

Drops core-when-let + the sym* helper + the intern entry, and empties
core-macro-names (no seed binding is a macro anymore).
This commit is contained in:
Yogthos 2026-06-07 19:49:27 -04:00
parent 77e3e3afcf
commit 8bc5bd6f61
3 changed files with 16 additions and 20 deletions

View file

@ -321,3 +321,12 @@
;; (for handles :when/:let/:while and multiple bindings). ;; (for handles :when/:let/:while and multiple bindings).
(defmacro doseq [bindings & body] (defmacro doseq [bindings & body]
`(do (count (for ~bindings (do ~@body nil))) nil)) `(do (count (for ~bindings (do ~@body nil))) nil))
;; when-let must live in this (early) tier, not 30-macros with its if-let/if-some/
;; when-some siblings: 20-coll uses it (not-empty), and 20-coll loads before 30. The
;; name binds only in the taken branch (temp# tests the value); via `let` so the
;; binding form may itself destructure, matching Clojure.
(defmacro when-let [bindings & body]
(let [form (bindings 0) tst (bindings 1)]
`(let [temp# ~tst]
(if temp# (let [~form temp#] ~@body) nil))))

View file

@ -25,6 +25,8 @@
`(let [temp# ~tst] `(let [temp# ~tst]
(if temp# (let [~form temp#] ~then) ~else)))) (if temp# (let [~form temp#] ~then) ~else))))
;; when-let lives in 00-syntax (not here): 20-coll uses it, which loads before this tier.
(defmacro if-some [bindings then & [else]] (defmacro if-some [bindings then & [else]]
(let [form (bindings 0) tst (bindings 1)] (let [form (bindings 0) tst (bindings 1)]
`(let [temp# ~tst] `(let [temp# ~tst]

View file

@ -2048,23 +2048,8 @@
{:jolt/type :symbol :ns nil :name (string prefix-string n)}) {:jolt/type :symbol :ns nil :name (string prefix-string n)})
# if-let / when-let / if-some / when-some bind the name ONLY in the then/body # if-let/when-let/if-some/when-some now live in the Clojure overlay
# branch — the else branch must see the surrounding scope, not the binding (so # (core/30-macros.clj) as defmacros.
# (let [x 5] (if-let [x nil] x x)) returns 5, like Clojure). Achieved with a fresh
# temp around the if; the name is rebound to the temp inside the taken branch only.
(defn- sym* [name] {:jolt/type :symbol :ns nil :name name})
(defn core-when-let
"Macro: (when-let [binding val-expr] & body)"
[bindings & body]
(def form-sym (in bindings 0))
(def val-form (in bindings 1))
(def temp (gensym "when_let__"))
@[(sym* "let*") @[temp val-form]
@[(sym* "if") temp
@[(sym* "let*") @[form-sym temp] @[(sym* "do") ;body]]
nil]])
(defn core-push-thread-bindings [b] (push-thread-bindings b)) (defn core-push-thread-bindings [b] (push-thread-bindings b))
(defn core-pop-thread-bindings [] (pop-thread-bindings)) (defn core-pop-thread-bindings [] (pop-thread-bindings))
@ -3090,7 +3075,6 @@
"add-watch" core-add-watch "add-watch" core-add-watch
"remove-watch" core-remove-watch "remove-watch" core-remove-watch
"not" core-not "not" core-not
"when-let" core-when-let
"derive" core-derive "derive" core-derive
"isa?" core-isa? "isa?" core-isa?
"parents" core-parents "parents" core-parents
@ -3156,9 +3140,10 @@
"*assert" true}) "*assert" true})
(defn core-macro-names (defn core-macro-names
"Set of core binding names that are macros." "Set of core binding names that are macros. Empty now that every core macro
lives in the Clojure overlay (clojure.core.*-syntax / *-macros tiers)."
[] []
@{"when-let" true}) @{})
(def init-core! (def init-core!
(fn [& args] (fn [& args]