core: move if-let/if-some/when-some/while/dotimes macros to overlay
Phase 3 batch 2 (jolt-1j0), 5 user-facing macros as defmacro + syntax-quote. The conditional-binding macros use the auto-gensym temp# idiom so the name binds only in the taken branch (the else/empty branch sees the outer scope, carrying forward the earlier scope fix). when-let stays in Janet for now — 20-coll uses it, so it must remain available before the macro tier loads. Note: gensym in a macro body resolves to Janet's 0-arity builtin, so explicit (gensym "prefix") fails — use template auto-gensym (foo#) instead. conformance 228/228 x3, full suite green (incl. the if-let scope regression spec, now exercising the overlay macro), clojure-test-suite 3930, bench A/B flat.
This commit is contained in:
parent
330a3a23d9
commit
e611034550
3 changed files with 38 additions and 68 deletions
|
|
@ -16,3 +16,30 @@
|
|||
;; via rest-destructuring.
|
||||
(defmacro if-not [test then & [else]]
|
||||
`(if (not ~test) ~then ~else))
|
||||
|
||||
;; Conditional binding macros: the name is bound ONLY in the taken branch (the
|
||||
;; auto-gensym temp# tests the value; the else/empty branch sees the surrounding
|
||||
;; scope). temp# is a single template-local gensym — referenced twice, same symbol.
|
||||
(defmacro if-let [bindings then & [else]]
|
||||
(let [form (bindings 0) tst (bindings 1)]
|
||||
`(let [temp# ~tst]
|
||||
(if temp# (let [~form temp#] ~then) ~else))))
|
||||
|
||||
(defmacro if-some [bindings then & [else]]
|
||||
(let [form (bindings 0) tst (bindings 1)]
|
||||
`(let [temp# ~tst]
|
||||
(if (some? temp#) (let [~form temp#] ~then) ~else))))
|
||||
|
||||
(defmacro when-some [bindings & body]
|
||||
(let [form (bindings 0) tst (bindings 1)]
|
||||
`(let [temp# ~tst]
|
||||
(if (some? temp#) (let [~form temp#] ~@body) nil))))
|
||||
|
||||
(defmacro while [test & body]
|
||||
`(loop [] (when ~test ~@body (recur))))
|
||||
|
||||
(defmacro dotimes [bindings & body]
|
||||
(let [i (bindings 0) n (bindings 1)]
|
||||
`(let [n# ~n]
|
||||
(loop [~i 0]
|
||||
(when (< ~i n#) ~@body (recur (inc ~i)))))))
|
||||
|
|
|
|||
|
|
@ -2142,17 +2142,6 @@
|
|||
# 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-if-let
|
||||
"Macro: (if-let [binding val-expr] then else?)"
|
||||
[bindings then-form & else-forms]
|
||||
(def form-sym (in bindings 0))
|
||||
(def val-form (in bindings 1))
|
||||
(def temp (gensym "if_let__"))
|
||||
@[(sym* "let*") @[temp val-form]
|
||||
@[(sym* "if") temp
|
||||
@[(sym* "let*") @[form-sym temp] then-form]
|
||||
;else-forms]])
|
||||
|
||||
(defn core-when-let
|
||||
"Macro: (when-let [binding val-expr] & body)"
|
||||
[bindings & body]
|
||||
|
|
@ -2164,28 +2153,6 @@
|
|||
@[(sym* "let*") @[form-sym temp] @[(sym* "do") ;body]]
|
||||
nil]])
|
||||
|
||||
(defn core-if-some
|
||||
"Macro: (if-some [binding val-expr] then else?)"
|
||||
[bindings then-form & else-forms]
|
||||
(def form-sym (in bindings 0))
|
||||
(def val-form (in bindings 1))
|
||||
(def temp (gensym "if_some__"))
|
||||
@[(sym* "let*") @[temp val-form]
|
||||
@[(sym* "if") @[(sym* "some?") temp]
|
||||
@[(sym* "let*") @[form-sym temp] then-form]
|
||||
;else-forms]])
|
||||
|
||||
(defn core-when-some
|
||||
"Macro: (when-some [binding val-expr] & body)"
|
||||
[bindings & body]
|
||||
(def form-sym (in bindings 0))
|
||||
(def val-form (in bindings 1))
|
||||
(def temp (gensym "when_some__"))
|
||||
@[(sym* "let*") @[temp val-form]
|
||||
@[(sym* "if") @[(sym* "some?") temp]
|
||||
@[(sym* "let*") @[form-sym temp] @[(sym* "do") ;body]]
|
||||
nil]])
|
||||
|
||||
(defn core-doto
|
||||
"Macro: (doto obj (method args)...) → let obj, call methods, return obj"
|
||||
[obj & forms]
|
||||
|
|
@ -2229,34 +2196,6 @@
|
|||
(build (tuple/slice cls 2))]))))
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"} @[g expr] (build clauses)])
|
||||
|
||||
(defn core-dotimes
|
||||
"Macro: (dotimes [sym n] & body) -> loop from 0 to n-1"
|
||||
[bindings & body]
|
||||
(def sym (in bindings 0))
|
||||
(def n-form (in bindings 1))
|
||||
(def i (gensym))
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"}
|
||||
@[i n-form]
|
||||
@[{:jolt/type :symbol :ns nil :name "loop*"}
|
||||
@[sym 0]
|
||||
@[{:jolt/type :symbol :ns nil :name "if"}
|
||||
@[{:jolt/type :symbol :ns nil :name "<"} sym i]
|
||||
@[{:jolt/type :symbol :ns nil :name "do"}
|
||||
;body
|
||||
@[{:jolt/type :symbol :ns nil :name "recur"}
|
||||
@[{:jolt/type :symbol :ns nil :name "inc"} sym]]]
|
||||
nil]]])
|
||||
|
||||
(defn core-while
|
||||
"Macro: (while test & body) -> loop while test is truthy"
|
||||
[test & body]
|
||||
@[{:jolt/type :symbol :ns nil :name "loop*"}
|
||||
@[]
|
||||
@[{:jolt/type :symbol :ns nil :name "when"}
|
||||
test
|
||||
@[{:jolt/type :symbol :ns nil :name "do"} ;body]
|
||||
@[{:jolt/type :symbol :ns nil :name "recur"}]]])
|
||||
|
||||
(defn core-for
|
||||
"Macro: (for [binding-form coll :when test :let [bindings]] body)
|
||||
List comprehension. Basic support for :when and :let."
|
||||
|
|
@ -3836,14 +3775,9 @@
|
|||
"when" core-when
|
||||
"when-not" core-when-not
|
||||
"when-first" core-when-first
|
||||
"if-let" core-if-let
|
||||
"when-let" core-when-let
|
||||
"if-some" core-if-some
|
||||
"when-some" core-when-some
|
||||
"doto" core-doto
|
||||
"condp" core-condp
|
||||
"dotimes" core-dotimes
|
||||
"while" core-while
|
||||
"->" core-thread-first
|
||||
"->>" core-thread-last
|
||||
"some->" core-some->
|
||||
|
|
@ -3932,7 +3866,7 @@
|
|||
(defn core-macro-names
|
||||
"Set of core binding names that are macros."
|
||||
[]
|
||||
@{"and" true "or" true "cond" true "case" true "for" true "when" true "when-not" true "if-let" true "when-let" true "if-some" true "when-some" true "doto" true "defn" true "defn-" true "declare" 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 "binding" true "lazy-seq" true "lazy-cat" true "when-first" true "condp" true "dotimes" true "while" true "some->" true "some->>" true "cond->" true "cond->>" true "as->" true "->" true "->>" true "letfn" true "doseq" true "delay" true "assert" true "future" true})
|
||||
@{"and" true "or" true "cond" true "case" true "for" true "when" true "when-not" true "when-let" true "doto" true "defn" true "defn-" true "declare" 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 "binding" true "lazy-seq" true "lazy-cat" true "when-first" true "condp" true "some->" true "some->>" true "cond->" true "cond->>" true "as->" true "->" true "->>" true "letfn" true "doseq" true "delay" true "assert" true "future" true})
|
||||
|
||||
(def init-core!
|
||||
(fn [& args]
|
||||
|
|
|
|||
|
|
@ -34,4 +34,13 @@
|
|||
["if-not no else" "nil" "(if-not true :then)"]
|
||||
["if-not no else hit" ":then" "(if-not false :then)"]
|
||||
["comment -> nil" "nil" "(comment a b c)"]
|
||||
["comment in do" "42" "(do (comment ignored) 42)"])
|
||||
["comment in do" "42" "(do (comment ignored) 42)"]
|
||||
["if-let then" "6" "(if-let [x 5] (inc x) :none)"]
|
||||
["if-let else" ":none" "(if-let [x nil] (inc x) :none)"]
|
||||
["if-let else scope" "9" "(let [x 9] (if-let [x nil] :t x))"]
|
||||
["if-some zero" "1" "(if-some [x 0] (inc x) :none)"]
|
||||
["if-some nil" ":none" "(if-some [x nil] x :none)"]
|
||||
["when-some multi" "14" "(when-some [x 7] (inc x) (* x 2))"]
|
||||
["when-some nil" "nil" "(when-some [x nil] x)"]
|
||||
["while loop" "3" "(let [a (atom 0)] (while (< @a 3) (swap! a inc)) @a)"]
|
||||
["dotimes sum" "10" "(let [a (atom 0)] (dotimes [i 5] (swap! a + i)) @a)"])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue