Compiler research (#10)
adds self-hosted compiler is functionally: - The default compile path is the portable pipeline using jolt.analyzer (Clojure) → host-neutral IR → backend.janet. - The analyzer is itself Clojure, compiled by jolt for true self-hosting. - bootstrap-fixpoint passes (stage1 == stage2 == stage3): rebuilding the compiler on its own output. - clojure.core is now self-hosted in the overlay. - Stateful forms (defmacro/ns/deftype/defmulti/require/in-ns) are interpreted by design.
This commit is contained in:
parent
607779866e
commit
d3194aae59
68 changed files with 6590 additions and 2019 deletions
|
|
@ -44,6 +44,22 @@
|
|||
["if-some zero" "1" "(if-some [x 0] (inc x) :none)"]
|
||||
["when-some nil" "nil" "(when-some [x nil] x)"])
|
||||
|
||||
# Regression: if-let/when-let/if-some/when-some bind the name ONLY in the
|
||||
# then/body branch. The else branch (and a falsy when-let body, which there is
|
||||
# none of) must see the surrounding scope, not the binding — so the else of
|
||||
# (let [x 5] (if-let [x nil] ...)) sees x=5, like Clojure. (Previously the macros
|
||||
# wrapped the whole `if` in the binding's let*, leaking it into the else.)
|
||||
(defspec "control / conditional-binding scope"
|
||||
["if-let else sees outer" "5" "(let [x 5] (if-let [x nil] :then x))"]
|
||||
["if-let then binds" "7" "(let [x 5] (if-let [x 7] x :else))"]
|
||||
["if-some else sees outer" "5" "(let [x 5] (if-some [x nil] :then x))"]
|
||||
["if-some binds false" "false" "(if-some [x false] x :else)"]
|
||||
["when-let else via or" "5" "(let [x 5] (or (when-let [x nil] x) x))"]
|
||||
["when-let multi-form body" "14" "(when-let [x 7] (inc x) (* x 2))"]
|
||||
["if-let in fn param" "9" "((fn [xs] (if-let [xs nil] :then xs)) 9)"]
|
||||
["when-some binds zero" "1" "(when-some [x 0] (inc x))"]
|
||||
["if-let evals test once" "1" "(let [c (atom 0)] (if-let [v (do (swap! c inc) :v)] @c :none))"])
|
||||
|
||||
(defspec "control / iteration"
|
||||
["dotimes side-effect" "5" "(let [a (atom 0)] (dotimes [i 5] (swap! a inc)) @a)"]
|
||||
["while" "5" "(let [a (atom 0)] (while (< @a 5) (swap! a inc)) @a)"]
|
||||
|
|
@ -52,8 +68,15 @@
|
|||
["for :when" "[0 2 4]" "(for [x (range 6) :when (even? x)] x)"]
|
||||
["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)"]
|
||||
["for :let+:when" "[4 6 8]" "(for [x (range 5) :let [y (* x 2)] :when (> y 3)] y)"]
|
||||
["for multi :when" "[[1 :a] [1 :b]]" "(for [x [0 1] :when (odd? x) y [:a :b]] [x y])"]
|
||||
["for destructure" "[3 7]" "(for [[a b] [[1 2] [3 4]]] (+ a b))"]
|
||||
["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))"]
|
||||
|
|
|
|||
|
|
@ -35,7 +35,10 @@
|
|||
[":strs" "7" "(let [{:strs [a]} {\"a\" 7}] a)"]
|
||||
[":syms" "8" "(let [{:syms [a]} {(quote a) 8}] a)"]
|
||||
["namespaced :keys" "3" "(let [{:keys [x/y]} {:x/y 3}] y)"]
|
||||
["namespaced :syms" "4" "(let [{:syms [p/q]} {(quote p/q) 4}] q)"])
|
||||
["namespaced :syms" "4" "(let [{:syms [p/q]} {(quote p/q) 4}] q)"]
|
||||
# :keys also accepts keyword elements ({:keys [:a :b]}), binding bare locals.
|
||||
["keyword :keys" "3" "(let [{:keys [:a :b]} {:a 1 :b 2}] (+ a b))"]
|
||||
["keyword :keys ns" "3" "(let [{:keys [:x/y]} {:x/y 3}] y)"])
|
||||
|
||||
(defspec "destructure / keyword args (& {:keys})"
|
||||
["fn kwargs" "[1 2]" "(do (defn f [& {:keys [a b]}] [a b]) (f :a 1 :b 2))"]
|
||||
|
|
@ -43,6 +46,15 @@
|
|||
["fn kwargs :or" "9" "(do (defn h [& {:keys [a] :or {a 9}}] a) (h))"]
|
||||
["fn kwargs trailing map" "7" "(do (defn k [& {:keys [a]}] a) (k {:a 7}))"])
|
||||
|
||||
(defspec "destructure / fn params & loop"
|
||||
["fn vector param" "7" "((fn [[a b]] (+ a b)) [3 4])"]
|
||||
["fn map param" "30" "((fn [{:keys [x y]}] (* x y)) {:x 5 :y 6})"]
|
||||
["fn :or param" "7" "((fn [{:keys [x] :or {x 7}}] x) {})"]
|
||||
["fn multi-arity destr" "15" "((fn ([[a]] a) ([[a] b] (+ a b))) [10] 5)"]
|
||||
["loop vector binding" "[4 2]" "(loop [[a b] [1 2] n 0] (if (< n 3) (recur [(inc a) b] (inc n)) [a b]))"]
|
||||
["loop map binding" "4" "(loop [{:keys [v]} {:v 1} n 0] (if (< n 2) (recur {:v (* v 2)} (inc n)) v))"]
|
||||
["loop init sees destr" "[1 2 3]" "(loop [[a b] [1 2] c (+ a b)] [a b c])"])
|
||||
|
||||
(defspec "destructure / macro params"
|
||||
["macro & [a & more :as all]"
|
||||
"[1 [2 3] [1 2 3]]"
|
||||
|
|
|
|||
|
|
@ -35,4 +35,7 @@
|
|||
["catch binds thrown value" "42"
|
||||
"(try (throw 42) (catch :default e e))"]
|
||||
["rethrow preserves ex" "\"inner\""
|
||||
"(try (try (throw (ex-info \"inner\" {})) (catch :default e (throw e))) (catch :default e (ex-message e)))"])
|
||||
"(try (try (throw (ex-info \"inner\" {})) (catch :default e (throw e))) (catch :default e (ex-message e)))"]
|
||||
["ex-data on non-ex" "nil" "(ex-data 42)"]
|
||||
["ex-cause on non-ex" "nil" "(ex-cause {:k 1})"]
|
||||
["ex-message of string" "\"hi\"" "(ex-message \"hi\")"])
|
||||
|
|
|
|||
|
|
@ -31,3 +31,10 @@
|
|||
["janet-type string" ":string" "(do (require (quote [jolt.interop :as j])) (j/janet-type \"x\"))"]
|
||||
["janet-type number" ":number" "(do (require (quote [jolt.interop :as j])) (j/janet-type 1))"]
|
||||
["janet-type keyword" ":keyword" "(do (require (quote [jolt.interop :as j])) (j/janet-type :a))"])
|
||||
|
||||
(defspec "interop / arrays (aget/aset/alength)"
|
||||
["alength" "3" "(alength (object-array [1 2 3]))"]
|
||||
["aget" "20" "(aget (object-array [10 20 30]) 1)"]
|
||||
["aset returns val" "9" "(aset (object-array [1 2 3]) 1 9)"]
|
||||
["aset mutates" "[7 2 3]" "(let [a (object-array [1 2 3])] (aset a 0 7) (vec a))"]
|
||||
["aget 2d" "4" "(aget (to-array-2d [[1 2] [3 4]]) 1 1)"])
|
||||
|
|
|
|||
|
|
@ -25,3 +25,50 @@
|
|||
"(= (gensym) (gensym))"]
|
||||
["gensym# in template" "true"
|
||||
"(do (defmacro m [] `(let [x# 1] x#)) (= 1 (m)))"])
|
||||
|
||||
# Core macros ported from Janet to the Clojure overlay (jolt-1j0 phase 3,
|
||||
# jolt-core/clojure/core/30-macros.clj).
|
||||
(defspec "macros / core-overlay"
|
||||
["if-not true branch" ":then" "(if-not false :then :else)"]
|
||||
["if-not else branch" ":else" "(if-not true :then :else)"]
|
||||
["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)"]
|
||||
["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)"]
|
||||
["as-> threads" "12" "(as-> 5 x (+ x 1) (* x 2))"]
|
||||
["as-> no forms" "5" "(as-> 5 x)"]
|
||||
["some-> through" "6" "(some-> {:a {:b 5}} :a :b inc)"]
|
||||
["some-> short-circuit" "nil" "(some-> {:a nil} :a :b)"]
|
||||
["some->> through" "9" "(some->> [1 2 3] (map inc) (reduce +))"]
|
||||
["some->> nil" "nil" "(some->> nil (map inc))"]
|
||||
["doto returns obj" "[1 2]" "(deref (doto (atom []) (swap! conj 1) (swap! conj 2)))"]
|
||||
["when-first" "20" "(when-first [x [10 20 30]] (* x 2))"]
|
||||
["when-first empty" "nil" "(when-first [x []] :body)"]
|
||||
["when-first nil coll" "nil" "(when-first [x nil] :body)"]
|
||||
["when-first range" "0" "(when-first [x (range 5)] x)"]
|
||||
["cond->> threads" "12" "(cond->> 5 true (+ 1) false (* 100) true (* 2))"]
|
||||
["cond->> skip" "10" "(cond->> 10 false (+ 1))"]
|
||||
["assert pass" ":ok" "(do (assert (= 1 1)) :ok)"]
|
||||
["assert throws" ":threw" "(try (assert (= 1 2)) (catch :default e :threw))"]
|
||||
["assert message" "\"nope\"" "(try (assert false \"nope\") (catch :default e (ex-message e)))"]
|
||||
["delay value" "42" "(deref (delay 42))"]
|
||||
["delay forces once" "1" "(let [c (atom 0) d (delay (swap! c inc))] @d @d @c)"]
|
||||
["future deref" "9" "(deref (future (* 3 3)))"]
|
||||
["letfn simple" "25" "(letfn [(sq [x] (* x x))] (sq 5))"]
|
||||
["letfn mutual" "true" "(letfn [(ev? [n] (if (zero? n) true (od? (dec n)))) (od? [n] (if (zero? n) false (ev? (dec n))))] (ev? 8))"]
|
||||
["condp match" ":two" "(condp = 2 1 :one 2 :two 3 :three)"]
|
||||
["condp default" ":else" "(condp = 9 1 :one 2 :two :else)"]
|
||||
["condp :>> form" "\"got 2\"" "(condp some [1 2 3] #{0 9} :>> (fn [x] (str \"got \" x)) #{2 6} :>> (fn [x] (str \"got \" x)))"]
|
||||
["condp no match" ":threw" "(try (condp = 9 1 :one) (catch :default e :threw))"]
|
||||
["binding rebinds" "99" "(do (def ^:dynamic *bx* 10) (binding [*bx* 99] *bx*))"]
|
||||
["binding restores" "10" "(do (def ^:dynamic *by* 10) (binding [*by* 99] *by*) *by*)"]
|
||||
["binding seen by fn" "7" "(do (def ^:dynamic *bz* 0) (defn rdz [] *bz*) (binding [*bz* 7] (rdz)))"])
|
||||
|
|
|
|||
|
|
@ -115,3 +115,32 @@
|
|||
["subvec float trunc" "[0]" "(subvec [0 1 2] 0.5 1.33)"]
|
||||
["subvec NaN start" "[0 1 2]" "(subvec [0 1 2] ##NaN 3)"]
|
||||
["subvec NaN end" "[]" "(subvec [0 1 2] 0 ##NaN)"])
|
||||
|
||||
# A nil value is a PRESENT key in Clojure (distinct from a missing key); Janet
|
||||
# structs drop nil, so jolt builds these maps as a phm. Tested via literals (the
|
||||
# reader path) and the construction/op surface, in every spec mode.
|
||||
(defspec "map / nil values preserved"
|
||||
["literal contains" "true" "(contains? {:b nil} :b)"]
|
||||
["literal not= empty" "false" "(= {:b nil} {})"]
|
||||
["literal get nil" "nil" "(get {:b nil} :b :x)"]
|
||||
["literal keys incl nil" "true" "(= #{:a :b} (set (keys {:a nil :b 1})))"]
|
||||
["literal count" "2" "(count {:a nil :b 1})"]
|
||||
["literal vals incl nil" "2" "(count (vals {:a nil :b 1}))"]
|
||||
["eval values w/ nil" "3" "(:a {:a (+ 1 2) :b nil})"]
|
||||
["nil key present" "true" "(contains? {nil :v} nil)"]
|
||||
["assoc nil present" "true" "(contains? (assoc {:a 1} :b nil) :b)"]
|
||||
["assoc nil get" "nil" "(get (assoc {:a 1} :b nil) :b :x)"]
|
||||
["assoc overwrite nil" "nil" "(get (assoc {:a 1} :a nil) :a :x)"]
|
||||
["hash-map nil" "true" "(contains? (hash-map :b nil) :b)"]
|
||||
["merge new nil" "true" "(contains? (merge {:a 1} {:b nil}) :b)"]
|
||||
["merge overwrite nil" "nil" "(get (merge {:a 1} {:a nil}) :a :x)"]
|
||||
["merge-with present nil" "true" "(= [nil 1] (get (merge-with (fn [a b] [a b]) {:a nil} {:a 1}) :a))"]
|
||||
["into nil val" "true" "(contains? (into {} [[:a nil]]) :a)"]
|
||||
["conj map nil" "true" "(contains? (conj {:x 1} {:a nil}) :a)"]
|
||||
["zipmap nil" "true" "(contains? (zipmap [:a] [nil]) :a)"]
|
||||
["select-keys nil" "true" "(contains? (select-keys {:a nil} [:a]) :a)"]
|
||||
["get-in present nil" "nil" "(get-in {:a nil} [:a] :x)"]
|
||||
["get-in through nil" ":x" "(get-in {:a nil} [:a :b] :x)"]
|
||||
["dissoc keeps nil" "true" "(contains? (dissoc {:a nil :b 1} :b) :a)"]
|
||||
["reduce-kv sees nil" "true" "(= #{:a :b} (reduce-kv (fn [acc k v] (conj acc k)) #{} {:a nil :b 2}))"]
|
||||
["nil-free stays fast" "true" "(= {:a 1 :b 2} {:b 2 :a 1})"])
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@
|
|||
["with-meta preserves value" "true" "(= [1 2 3] (with-meta [1 2 3] {:a 1}))"]
|
||||
["with-meta on map" "{:doc \"x\"}" "(meta (with-meta {:k 1} {:doc \"x\"}))"]
|
||||
["vary-meta" "{:a 2}" "(meta (vary-meta (with-meta [1] {:a 1}) update :a inc))"]
|
||||
["vary-meta extra args" "{:a 1 :b 2}" "(meta (vary-meta (with-meta [1] {:a 1}) assoc :b 2))"]
|
||||
["meta reader ^" "{:tag :int}" "(meta ^{:tag :int} [1 2])"]
|
||||
["with-meta on fn ok" "true" "(fn? (with-meta inc {:a 1}))"])
|
||||
["with-meta on fn ok" "true" "(fn? (with-meta inc {:a 1}))"]
|
||||
["with-meta nil clears" "nil" "(meta (with-meta [1 2 3] nil))"])
|
||||
|
||||
(defspec "metadata / type hints"
|
||||
# ^Type / ^:kw / ^"str" on a symbol attach as metadata and are otherwise inert:
|
||||
|
|
|
|||
|
|
@ -59,6 +59,50 @@
|
|||
["symbol constructor" "(quote x)" "(symbol \"x\")"]
|
||||
["name of string" "\"s\"" "(name \"s\")"])
|
||||
|
||||
# Predicates moved from Janet to the Clojure overlay (jolt-1j0). Jolt has no
|
||||
# ratio/bigdecimal types (so ratio?/decimal? are always false, rational?=int?),
|
||||
# and no distinct host object/undefined types (object?/undefined? always false).
|
||||
(defspec "predicates / overlay-migrated"
|
||||
["not-any? true" "true" "(not-any? even? [1 3 5])"]
|
||||
["not-any? false" "false" "(not-any? even? [1 2 3])"]
|
||||
["not-every? true" "true" "(not-every? even? [2 4 5])"]
|
||||
["not-every? false" "false" "(not-every? even? [2 4 6])"]
|
||||
["ident? number" "false" "(ident? 1)"]
|
||||
["qualified-ident?" "true" "(qualified-ident? :a/b)"]
|
||||
["qualified-ident? no" "false" "(qualified-ident? :a)"]
|
||||
["simple-ident?" "true" "(simple-ident? :a)"]
|
||||
["ratio?" "false" "(ratio? 3)"]
|
||||
["decimal?" "false" "(decimal? 3)"]
|
||||
["rational? int" "true" "(rational? 3)"]
|
||||
["rational? float" "false" "(rational? 3.5)"]
|
||||
["nat-int? zero" "true" "(nat-int? 0)"]
|
||||
["nat-int? neg" "false" "(nat-int? -1)"]
|
||||
["pos-int?" "true" "(pos-int? 5)"]
|
||||
["neg-int?" "true" "(neg-int? -3)"]
|
||||
["NaN? on nan" "true" "(NaN? (/ 0.0 0.0))"]
|
||||
["NaN? on number" "false" "(NaN? 5)"]
|
||||
["abs negative" "3" "(abs -3)"]
|
||||
["abs positive" "2.5" "(abs 2.5)"]
|
||||
["object?" "false" "(object? 1)"]
|
||||
["undefined?" "false" "(undefined? 1)"]
|
||||
["keyword-identical?" "true" "(keyword-identical? :a :a)"]
|
||||
["keyword-identical? no" "false" "(keyword-identical? :a :b)"])
|
||||
|
||||
# Tagged-value predicates moved to the overlay in Phase 4 (read the value's
|
||||
# :jolt/type via get). The constructors stay native.
|
||||
(defspec "predicates / tagged-value (Phase 4)"
|
||||
["atom? yes" "true" "(atom? (atom 1))"]
|
||||
["atom? no" "false" "(atom? 1)"]
|
||||
["volatile? yes" "true" "(volatile? (volatile! 1))"]
|
||||
["volatile? no" "false" "(volatile? (atom 1))"]
|
||||
["record? yes" "true" "(do (defrecord Rp [a]) (record? (->Rp 1)))"]
|
||||
["record? no map" "false" "(record? {:a 1})"]
|
||||
["record? no nil" "false" "(record? nil)"]
|
||||
["tagged-literal? yes" "true" "(tagged-literal? (tagged-literal (quote inst) \"2020\"))"]
|
||||
["tagged-literal? no" "false" "(tagged-literal? 1)"]
|
||||
["reader-conditional? no" "false" "(reader-conditional? 1)"]
|
||||
["chunked-seq? always false" "false" "(chunked-seq? (seq [1 2 3]))"])
|
||||
|
||||
(defspec "predicates / equality & identity"
|
||||
["= same" "true" "(= 1 1)"]
|
||||
["= vectors" "true" "(= [1 2] [1 2])"]
|
||||
|
|
|
|||
|
|
@ -43,6 +43,14 @@
|
|||
["map" "[2 3 4]" "(map inc [1 2 3])"]
|
||||
["map two colls" "[5 7 9]" "(map + [1 2 3] [4 5 6])"]
|
||||
["map stops at shortest" "[5 7]" "(map + [1 2] [4 5 6])"]
|
||||
# nil elements are values, not end-of-seq: multi-coll map must not truncate.
|
||||
["map keeps nil elements" "[[1 :a] [nil :b] [3 nil]]" "(map vector [1 nil 3] [:a :b nil])"]
|
||||
["map 3 colls" "[12 15 18]" "(map + [1 2 3] [4 5 6] [7 8 9])"]
|
||||
["map 3 colls shortest" "[12 15]" "(map + [1 2] [4 5 6] [7 8 9])"]
|
||||
["map 4 colls" "[16 20]" "(map + [1 2] [3 4] [5 6] [7 8])"]
|
||||
["map 3 colls nils" "[[1 :a 10] [nil :b 20] [3 nil 30]]" "(map vector [1 nil 3] [:a :b nil] [10 20 30])"]
|
||||
["map empty coll" "()" "(map + [] [1 2 3] [4 5 6])"]
|
||||
["map lazy+concrete" "[11 22 33]" "(map + (map identity [1 2 3]) [10 20 30])"]
|
||||
["map-indexed" "[[0 :a] [1 :b]]" "(map-indexed vector [:a :b])"]
|
||||
["mapv" "[2 3 4]" "(mapv inc [1 2 3])"]
|
||||
["filter" "[2 4]" "(filter even? [1 2 3 4])"]
|
||||
|
|
@ -54,8 +62,15 @@
|
|||
["reduce single no init" "5" "(reduce + [5])"]
|
||||
["reduced short-circuits" "3" "(reduce (fn [a x] (if (> a 2) (reduced a) (+ a x))) 0 [1 2 3 4 5])"]
|
||||
["reduce-kv" "6" "(reduce-kv (fn [a k v] (+ a v)) 0 {:a 1 :b 2 :c 3})"]
|
||||
["reduce-kv on vector" "[[0 :a] [1 :b]]" "(reduce-kv (fn [a i v] (conj a [i v])) [] [:a :b])"]
|
||||
["reduce-kv honors reduced" "[:a]" "(reduce-kv (fn [a i v] (if (= i 1) (reduced a) (conj a v))) [] [:a :b :c])"]
|
||||
["reduce-kv on nil" "0" "(reduce-kv (fn [a k v] (+ a v)) 0 nil)"]
|
||||
["reductions" "[1 3 6]" "(reductions + [1 2 3])"]
|
||||
["mapcat" "[1 1 2 2]" "(mapcat (fn [x] [x x]) [1 2])"]
|
||||
["mapcat two colls" "[1 3 2 4]" "(mapcat vector [1 2] [3 4])"]
|
||||
["mapcat three colls" "[1 2 3]" "(mapcat vector [1] [2] [3])"]
|
||||
["mapcat empty coll" "()" "(mapcat vector [] [1 2] [3 4])"]
|
||||
["mapcat seqs" "[1 2 3 4]" "(mapcat identity [[1 2] [3 4]])"]
|
||||
["keep" "[1 3]" "(keep (fn [x] (if (odd? x) x nil)) [1 2 3 4])"]
|
||||
["some truthy" "true" "(some even? [1 2 3])"]
|
||||
["some nil" "nil" "(some even? [1 3 5])"]
|
||||
|
|
@ -197,3 +212,52 @@
|
|||
["nthnext nil count" :throws "(nthnext [0 1 2] nil)"]
|
||||
["update vec oob" :throws "(update [] 1 identity)"]
|
||||
["update vec kw key" :throws "(update [1 2 3] :k identity)"])
|
||||
|
||||
# Regression cases for clojure.core fns moved from Janet to the Clojure overlay
|
||||
# (jolt-1j0), plus two bugs fixed in the process: nthrest returns () (not nil)
|
||||
# for an exhausted n>0 walk, and distinct? compares by VALUE (equal collections
|
||||
# are not distinct).
|
||||
(defspec "seq / overlay-migrated fns"
|
||||
["nthrest exhausted -> ()" "()" "(nthrest nil 100)"]
|
||||
["nthrest vec exhausted" "()" "(nthrest [1 2 3] 100)"]
|
||||
["nthrest n<=0 keeps coll" "[1 2 3]" "(nthrest [1 2 3] 0)"]
|
||||
["nthrest drops n" "[3 4 5]" "(nthrest [1 2 3 4 5] 2)"]
|
||||
["nthnext exhausted -> nil" "nil" "(nthnext [1 2] 5)"]
|
||||
["nthnext surprising nil" "nil" "(nthnext nil nil)"]
|
||||
["nthnext drops n" "[3 4]" "(nthnext [1 2 3 4] 2)"]
|
||||
["distinct? distinct" "true" "(distinct? 1 2 3)"]
|
||||
["distinct? dup" "false" "(distinct? 1 2 1)"]
|
||||
["distinct? equal colls" "false" "(distinct? [1 2] [1 2])"]
|
||||
["distinct? single" "true" "(distinct? 5)"]
|
||||
["replace maps elements" "[:a 2 :c 2]" "(replace {1 :a 3 :c} [1 2 3 2])"]
|
||||
["replace preserves nil val" "[1 nil 3]" "(replace {2 nil} [1 2 3])"]
|
||||
["take-last" "[3 4]" "(take-last 2 [1 2 3 4])"]
|
||||
["take-last empty -> nil" "nil" "(take-last 2 [])"]
|
||||
["take-last n>len" "[1 2]" "(take-last 9 [1 2])"]
|
||||
["drop-last default 1" "[1 2]" "(drop-last [1 2 3])"]
|
||||
["drop-last n" "[1 2]" "(drop-last 2 [1 2 3 4])"]
|
||||
["split-with" "[[2 4] [5 6]]" "(split-with even? [2 4 5 6])"]
|
||||
["replicate" "[:x :x :x]" "(replicate 3 :x)"]
|
||||
["bounded-count" "3" "(bounded-count 3 [1 2 3 4 5])"]
|
||||
["run! side effects" "6" "(let [a (atom 0)] (run! (fn [x] (swap! a + x)) [1 2 3]) @a)"]
|
||||
["completing wraps rf" "3" "((completing +) 1 2)"]
|
||||
["comparator <" "[1 2 3]" "(sort (comparator <) [3 1 2])"]
|
||||
["comparator >" "[3 2 1]" "(sort (comparator >) [3 1 2])"]
|
||||
["reductions" "[1 3 6 10]" "(reductions + [1 2 3 4])"]
|
||||
["reductions with init" "[10 11 13 16]" "(reductions + 10 [1 2 3])"]
|
||||
["reductions empty calls f" "[0]" "(reductions + [])"]
|
||||
["reductions empty + init" "[5]" "(reductions + 5 [])"]
|
||||
["tree-seq pre-order" "[[1 [2] 3] 1 [2] 2 3]" "(tree-seq sequential? seq [1 [2] 3])"]
|
||||
["some found" "true" "(some even? [1 3 4])"]
|
||||
["some none -> nil" "nil" "(some even? [1 3 5])"]
|
||||
["some keyword pred" "7" "(some :a [{:b 1} {:a 7}])"]
|
||||
["some returns value" "4" "(some (fn [x] (when (even? x) x)) [1 3 4 5])"]
|
||||
["flatten nested" "[1 2 3 4 5]" "(flatten [1 [2 [3 4]] 5])"]
|
||||
["flatten lists too" "[1 2 3]" "(flatten [1 (list 2 3)])"]
|
||||
["flatten scalar -> empty" "[]" "(flatten 5)"]
|
||||
["interleave" "[1 :a 2 :b]" "(interleave [1 2 3] [:a :b])"]
|
||||
["interleave empty" "[]" "(interleave)"]
|
||||
["rationalize identity" "5" "(rationalize 5)"]
|
||||
["dedupe consecutive" "[1 2 3 1]" "(dedupe [1 1 2 2 3 1 1])"]
|
||||
["dedupe empty" "[]" "(dedupe [])"]
|
||||
["dedupe no dups" "[1 2 3]" "(dedupe [1 2 3])"])
|
||||
|
|
|
|||
|
|
@ -48,3 +48,8 @@
|
|||
["subs end past len" :throws "(subs \"abcde\" 1 6)"]
|
||||
["subs nil start" :throws "(subs \"abcde\" nil 2)"]
|
||||
["subs on nil" :throws "(subs nil 1 2)"])
|
||||
|
||||
(defspec "string / namespace-munge"
|
||||
["hyphens to underscores" "\"a_b_c\"" "(namespace-munge \"a-b-c\")"]
|
||||
["from a symbol" "\"foo_bar\"" "(namespace-munge (quote foo-bar))"]
|
||||
["no hyphens unchanged" "\"ok\"" "(namespace-munge \"ok\")"])
|
||||
|
|
|
|||
|
|
@ -24,7 +24,12 @@
|
|||
["count" "3" "(count [1 2 3])"]
|
||||
["contains? index" "true" "(contains? [:a :b] 1)"]
|
||||
["contains? past end" "false" "(contains? [:a] 3)"]
|
||||
["vector as fn" ":b" "([:a :b :c] 1)"])
|
||||
["vector as fn" ":b" "([:a :b :c] 1)"]
|
||||
# An IFn collection held in a binding (not just a literal) must dispatch as IFn,
|
||||
# not as a host call: applies to vectors, keywords, and meta-bearing vectors.
|
||||
["vector-in-local as fn" "20" "(let [v [10 20 30]] (v 1))"]
|
||||
["keyword-in-local as fn" "7" "(let [k :a] (k {:a 7}))"]
|
||||
["meta vector as fn" "10" "((with-meta [10 20] {:k 1}) 0)"])
|
||||
|
||||
(defspec "vector / update (persistent)"
|
||||
["conj appends" "[1 2 3]" "(conj [1 2] 3)"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue