feat: distinguish map entries from vectors; min-key NaN ordering; subvec float coercion

- A map entry is a 2-element tuple (Jolt produces tuples only from map iteration;
  vector literals are pvecs, lists are arrays). key/val/map-entry? now accept a
  2-tuple and reject a plain vector, matching Clojure's MapEntry-vs-vector
  distinction — no metadata needed, the representations already differ.
- min-key/max-key reproduce Clojure's NaN-aware folding (2-arg strict </>, then
  <=/>=) and require numeric keys (NaN allowed, strings throw).
- subvec coerces float/NaN indices like (int ...) (truncate, NaN->0) then
  bounds-checks, instead of throwing on non-integers.

min_key 35/14 -> 49/0 (clean); key/val recover the 2-vector cases; subvec floats
fixed. clojure-test-suite pass 3898->3921. Updated conformance-test (key/val now
needs a real entry). spec: map/map-entry-&-key-ordering (14). jpm test green.
This commit is contained in:
Yogthos 2026-06-05 14:22:06 -04:00
parent ace1dcd124
commit 360b23c8af
4 changed files with 60 additions and 18 deletions

View file

@ -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 3895)
(def baseline-pass 3915)
# 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;

View file

@ -117,7 +117,7 @@
["filter over map" "true" "(= [[:b 2]] (filterv (fn [[k v]] (> v 1)) {:a 1 :b 2}))"]
["doall realizes" "(quote (2 3 4))" "(doall (map inc [1 2 3]))"]
["tree-seq" "(quote (1 2 3))" "(map (fn [x] x) (filter (complement coll?) (tree-seq coll? seq [1 [2 [3]]])))"]
["key/val" "true" "(let [e [:k 9]] (and (= :k (key e)) (= 9 (val e))))"]
["key/val" "true" "(let [e (first {:k 9})] (and (= :k (key e)) (= 9 (val e))))"]
["nat-int?" "true" "(and (nat-int? 0) (nat-int? 5) (not (nat-int? -1)))"]
["list* prepend" "(quote (1 2 3 4))" "(list* 1 2 [3 4])"]
["cycle" "(quote (1 2 3 1 2 3 1))" "(take 7 (cycle [1 2 3]))"]

View file

@ -96,3 +96,22 @@
["merge atomic arg" :throws "(merge {} :foo)"]
["merge [k v] ok" "{:foo 1}" "(merge {} [:foo 1])"]
["merge maps ok" "{:a 1, :b 2}" "(merge {:a 1} {:b 2})"])
# Map entries are distinct from plain vectors (key/val/map-entry? reject a
# vector); min-key/max-key follow Clojure's NaN-aware ordering; subvec coerces
# float/NaN indices like (int ...).
(defspec "map / map-entry & key ordering"
["key of entry" ":a" "(key (first {:a 1}))"]
["val of entry" "1" "(val (first {:a 1}))"]
["key rejects vector" :throws "(key [:a 1])"]
["val rejects vector" :throws "(val [:a 1])"]
["map-entry? entry" "true" "(map-entry? (first {:a 1}))"]
["map-entry? vector" "false" "(map-entry? [:a 1])"]
["min-key NaN first" "1" "(min-key identity ##NaN 1)"]
["min-key NaN last" "true" "(NaN? (min-key identity 1 ##NaN))"]
["min-key NaN three" "true" "(infinite? (min-key identity ##NaN ##-Inf 1))"]
["min-key keys nonnum" :throws "(min-key identity \"x\" \"y\")"]
["max-key picks max" "[1 2 3]" "(max-key count [1] [1 2 3] [1 2])"]
["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)"])