diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 86e6028..cfb25a2 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -283,7 +283,16 @@ # Collections # ============================================================ -(defn core-conj [coll & xs] +# Is x a map value (for conj/merge semantics: conj-ing a map merges its entries)? +(defn- map-value? [x] + (or (phm? x) (and (struct? x) (nil? (get x :jolt/type))))) + +(defn core-conj [& args] + (if (= 0 (length args)) (make-vec @[]) # (conj) -> [] + (let [coll (first args) xs (tuple/slice args 1)] + (if (nil? coll) + # conj onto nil builds a list (prepends): (conj nil 1 2) -> (2 1) + (do (var result nil) (each x xs (set result (pl-cons x result))) result) (if (pvec? coll) (do (var result coll) (each x xs (set result (pv-conj result x))) result) (if (plist? coll) @@ -303,20 +312,20 @@ (if (phm? coll) (do (var result coll) - (var i 0) - (while (< i (length xs)) - (let [pair (xs i)] - (set result (phm-assoc result (vnth pair 0) (vnth pair 1)))) - (++ i)) + (each x xs + (if (map-value? x) + # conj a map -> merge its entries + (each k (if (phm? x) (keys (phm-to-struct x)) (keys x)) + (set result (phm-assoc result k (if (phm? x) (phm-get x k) (in x k))))) + (set result (phm-assoc result (vnth x 0) (vnth x 1))))) result) (do (var result coll) - (var i 0) - (while (< i (length xs)) - (let [pair (xs i)] - (set result (merge result {(vnth pair 0) (vnth pair 1)}))) - (++ i)) - result)))))))) + (each x xs + (if (map-value? x) + (set result (merge result (if (phm? x) (phm-to-struct x) x))) + (set result (merge result {(vnth x 0) (vnth x 1)})))) + result))))))))))) (defn core-assoc [m & kvs] (cond diff --git a/test/integration/clojure-test-suite-test.janet b/test/integration/clojure-test-suite-test.janet index 498a70c..5d6c0e0 100644 --- a/test/integration/clojure-test-suite-test.janet +++ b/test/integration/clojure-test-suite-test.janet @@ -18,7 +18,7 @@ # Baseline: assertions Jolt currently passes across the suite. Raise as Jolt # improves so a regression (previously-passing assertion breaking) is caught. -(def baseline-pass 3680) +(def baseline-pass 3690) # A file is "clean" when it ran with zero failures AND zero errors. (def baseline-clean-files 45) # Per-file wall-clock budget (seconds). Normal files finish in well under 1s; diff --git a/test/spec/sequences-spec.janet b/test/spec/sequences-spec.janet index 9615f7c..4b5ff82 100644 --- a/test/spec/sequences-spec.janet +++ b/test/spec/sequences-spec.janet @@ -118,3 +118,14 @@ ["map a map" "[1 nil 2]" "(map {:a 1 :b 2} [:a :z :b])"] ["take-nth transducer" "[0 2 4 6 8]" "(into [] (take-nth 2) (range 10))"] ["interpose transducer" "[1 :x 2]" "(into [] (interpose :x) [1 2])"]) + +# conj edge cases: 0-arg, conj onto nil (builds a list), conj a map into a map. +(defspec "seq / conj edge cases" + ["conj no args" "[]" "(conj)"] + ["conj nil one" "[3]" "(conj nil 3)"] + ["conj nil many" "[2 1]" "(conj nil 1 2)"] + ["conj vector" "[1 2 3]" "(conj [1 2] 3)"] + ["conj list prepend" "[0 1 2]" "(conj '(1 2) 0)"] + ["conj map + map" "{:a 0, :b 1}" "(conj {:a 0} {:b 1})"] + ["conj map + pair" "{:a 0, :b 1}" "(conj {:a 0} [:b 1])"] + ["conj map merge wins" "{:a 2}" "(conj {:a 0} {:a 1} {:a 2})"])