From 6544d8ef441f63198d33608cdfb7eef097e7c4b5 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Fri, 5 Jun 2026 14:03:23 -0400 Subject: [PATCH] feat(strictness): seq/shuffle/NaN?/nthrest/nthnext reject bad args - seq throws on non-seqables (numbers/functions); collections/strings/nil ok - shuffle requires a collection (throws on numbers/strings/nil) - NaN? throws on non-numbers - nthrest/nthnext require a numeric count (nil count throws), clamp negative counts to the whole coll, and treat a nil coll as nil - update inherits assoc's vector bounds/keyword-key checks nan_qmark clean; shuffle 9/1; nthrest 13/1; nthnext 12/0/1; update 59/2/2. clojure-test-suite pass 3880->3889. spec: seq/strictness-round-3 (14). jpm test green. --- src/jolt/core.janet | 18 +++++++++++++----- test/integration/clojure-test-suite-test.janet | 2 +- test/spec/sequences-spec.janet | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 64dd89d..19082ba 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -583,7 +583,10 @@ (tuple? coll) (tuple/slice coll) (string? coll) (if (= 0 (length coll)) nil (tuple ;(map make-char (string/bytes coll)))) (struct? coll) (tuple ;(map (fn [k] (tuple k (get coll k))) (keys coll))) - coll)) + (array? coll) (tuple ;coll) + (and (table? coll) (get coll :jolt/deftype)) coll + # scalars/functions aren't seqable + (error (string "seq not supported on " (type coll))))) (defn core-vec [coll] (when (not (or (nil? coll) (core-coll? coll) (string? coll))) @@ -2898,11 +2901,15 @@ (tuple/slice (tuple ;r))))) (defn core-nthrest [coll n] - (let [c (realize-for-iteration coll)] - (tuple/slice (tuple ;(array/slice c (min n (length c))))))) + (when (not (number? n)) (error "nthrest requires a numeric count")) + (if (nil? coll) nil + (let [c (realize-for-iteration coll) + start (max 0 (min n (length c)))] # negative n -> whole coll + (tuple/slice (tuple ;(array/slice c start)))))) (defn core-nthnext [coll n] - (let [r (core-nthrest coll n)] (if (= 0 (length r)) nil r))) + (when (not (number? n)) (error "nthnext requires a numeric count")) + (let [r (core-nthrest coll n)] (if (or (nil? r) (= 0 (length r))) nil r))) (defn core-butlast [coll] (let [c (realize-for-iteration coll)] @@ -3000,6 +3007,7 @@ (error (string "rseq requires a vector or sorted collection, got " (type coll))))) (defn core-shuffle [coll] + (when (not (core-coll? coll)) (error (string "shuffle requires a collection, got " (type coll)))) (let [c (array/slice (realize-for-iteration coll))] (var i (- (length c) 1)) (while (> i 0) @@ -3242,7 +3250,7 @@ (defn core-decimal? [x] false) (defn core-rational? [x] (intval? x)) (defn core-infinite? [x] (and (number? x) (= (math/abs x) math/inf))) -(defn core-NaN? [x] (and (number? x) (not= x x))) +(defn core-NaN? [x] (if (number? x) (not= x x) (error "NaN? requires a number"))) # Jolt has no ratio type, so numerator/denominator have no valid input (Clojure # requires a Ratio and throws otherwise). (defn core-numerator [x] (error "numerator requires a ratio (Jolt has no ratios)")) diff --git a/test/integration/clojure-test-suite-test.janet b/test/integration/clojure-test-suite-test.janet index 4d8b7e3..5114cbd 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 3875) +(def baseline-pass 3885) # 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 9ee7840..8f78525 100644 --- a/test/spec/sequences-spec.janet +++ b/test/spec/sequences-spec.janet @@ -179,3 +179,21 @@ ["assoc odd args" :throws "(assoc {:a 1} :b)"] ["assoc on number" :throws "(assoc 5 :a 1)"] ["assoc on set" :throws "(assoc #{} :a 1)"]) + +# Strictness on more core fns: seq/shuffle need seqables, NaN? needs a number, +# nthrest/nthnext need a numeric count (and clamp negatives / accept nil coll). +(defspec "seq / strictness round 3" + ["seq on number" :throws "(seq 1)"] + ["seq on fn" :throws "(seq (fn [] 1))"] + ["seq vector ok" "[1 2]" "(seq [1 2])"] + ["NaN? on nil" :throws "(NaN? nil)"] + ["NaN? on number ok" "false" "(NaN? 1.0)"] + ["shuffle on number" :throws "(shuffle 1)"] + ["shuffle on string" :throws "(shuffle \"abc\")"] + ["shuffle vec ok" "3" "(count (shuffle [1 2 3]))"] + ["nthrest nil count" :throws "(nthrest [0 1 2] nil)"] + ["nthrest negative" "[0 1 2]" "(nthrest [0 1 2] -1)"] + ["nthrest nil coll" "nil" "(nthrest nil 0)"] + ["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)"])