diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 3068f00..0f04153 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -342,6 +342,13 @@ result))))))))))) (defn core-assoc [m & kvs] + (when (odd? (length kvs)) + (error "assoc expects an even number of key/value arguments")) + # assoc is defined on maps, vectors and nil; reject other shapes + (when (or (number? m) (string? m) (buffer? m) (keyword? m) (boolean? m) + (plist? m) (set? m) (core-transient? m) + (and (struct? m) (get m :jolt/type))) + (error (string "assoc requires a map or vector, got " (type m)))) (cond (phm? m) (do (var result m) (var i 0) (while (< i (length kvs)) (set result (phm-assoc result (kvs i) (kvs (+ i 1)))) (+= i 2)) result) @@ -527,9 +534,14 @@ # maps and sets: first of their seq (an entry / element) (phm? coll) (let [e (phm-entries coll)] (if (= 0 (length e)) nil (in e 0))) (set? coll) (let [s (phs-seq coll)] (if (= 0 (length s)) nil (in s 0))) - (struct? coll) (let [ks (keys coll)] (if (= 0 (length ks)) nil (tuple (in ks 0) (get coll (in ks 0))))) - (or (nil? coll) (= 0 (length coll))) nil - (string? coll) (make-char (in coll 0)) + (and (struct? coll) (nil? (get coll :jolt/type))) + (let [ks (keys coll)] (if (= 0 (length ks)) nil (tuple (in ks 0) (get coll (in ks 0))))) + (nil? coll) nil + (string? coll) (if (= 0 (length coll)) nil (make-char (in coll 0))) + # scalars aren't seqable + (or (number? coll) (boolean? coll) (keyword? coll) (and (struct? coll) (get coll :jolt/type))) + (error (string "first not supported on " (type coll))) + (= 0 (length coll)) nil (in coll 0))) (defn core-rest [coll] @@ -2972,8 +2984,13 @@ (defn core-not-empty [coll] (if (or (nil? coll) (= 0 (core-count coll))) nil coll)) +# rseq is defined only on vectors and sorted collections (Reversible). (defn core-rseq [coll] - (let [c (realize-for-iteration coll)] (tuple/slice (tuple ;(reverse c))))) + (cond + (pvec? coll) (tuple/slice (tuple ;(reverse (pv->array coll)))) + (core-sorted-map? coll) (tuple/slice (tuple ;(reverse (sorted-map-entries coll)))) + (core-sorted-set? coll) (tuple/slice (tuple ;(reverse (coll :items)))) + (error (string "rseq requires a vector or sorted collection, got " (type coll))))) (defn core-shuffle [coll] (let [c (array/slice (realize-for-iteration coll))] diff --git a/test/integration/clojure-test-suite-test.janet b/test/integration/clojure-test-suite-test.janet index f0488fd..1c79296 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 3860) +(def baseline-pass 3870) # 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 4a3bc10..9ee7840 100644 --- a/test/spec/sequences-spec.janet +++ b/test/spec/sequences-spec.janet @@ -165,3 +165,17 @@ ["val on number" :throws "(val 0)"] ["key of entry" ":a" "(key (first {:a 1}))"] ["val of entry" "1" "(val (first {:a 1}))"]) + +# More strictness: first/rseq on the right shapes, assoc even-arg requirement. +(defspec "seq / more strictness" + ["first on number" :throws "(first 42)"] + ["first on keyword" :throws "(first :a)"] + ["first ok vec" "1" "(first [1 2])"] + ["first ok nil" "nil" "(first nil)"] + ["rseq vector" "[3 2 1]" "(rseq [1 2 3])"] + ["rseq on string" :throws "(rseq \"ab\")"] + ["rseq on map" :throws "(rseq {:a 1})"] + ["rseq on number" :throws "(rseq 0)"] + ["assoc odd args" :throws "(assoc {:a 1} :b)"] + ["assoc on number" :throws "(assoc 5 :a 1)"] + ["assoc on set" :throws "(assoc #{} :a 1)"])