core: Stage 3 — sorted collections are pure Clojure (canonical port)
sorted-map/sorted-map-by/sorted-set/sorted-set-by/sorted?/sorted-map?/
sorted-set?/subseq/rsubseq now live in their own overlay tier (25-sorted.clj).
A sorted coll is a tagged host table with a comparator-ordered :entries
vector, a 3-way :cmp, and the tier's op implementations ATTACHED to the value
(:ops map): the seed's conj/assoc/get/seq/count/... branches are each a
one-line call through (coll :ops), so the ops travel with the value — correct
across contexts, forks, and AOT images, no module-level hooks to re-wire.
The host surface grows by three minimal value primitives: jolt.host/
tagged-table, ref-put! (already there), and ref-get — a raw field read,
because plain get on a sorted coll IS the comparator lookup and reading
:entries with it recurses.
This fixes a pile of Clojure-correctness gaps the Janet kernel had:
- lookup/membership now go through the COMPARATOR: (contains? (sorted-set 1)
1.0) was a deep= scan, (conj (sorted-set 1) 1.0) and assoc of a
comparator-equal key now no-op/replace as in Clojure
- equality is representation-agnostic: (= (sorted-map :a 1) {:a 1}) and
(= (sorted-set 1 2) #{1 2}) were false
- iteration was broken: (map inc (sorted-set 3 1 2)) errored
(realize-for-iteration and coll->cells had no sorted branches)
- empty?/empty saw the host wrapper, not the collection: (empty? (sorted-map))
was false, (empty sc) returned a bare table; it now keeps the comparator
- sorted colls canonicalize as map keys; comparator fns may be boolean
predicates or 3-way (Clojure's fn->comparator)
- sorted-map throws on odd kv count; conj nil is a no-op
Also fixes jolt-h86 en passant: into-conj had no branch for sets (or sorted
colls) and silently returned the target unchanged — (into #{} [:a :b]) was
#{}. The fallback now folds conj. Regression rows in sets-spec.
sorted-spec grows to 77 rows (comparator-based membership, equality,
empty/rseq/printing, seq-fn interop, subseq/rsubseq on maps). Gate green:
conformance 326x3, suite 4577 (vs 4566 prior — the battery gained rows),
sorted+sets specs, full jpm test, bench at parity with main back-to-back
(4521ms vs 4619ms TOTAL under identical load).
This commit is contained in:
parent
d6c5552fda
commit
55a3ebf93f
9 changed files with 400 additions and 143 deletions
|
|
@ -9,7 +9,10 @@
|
|||
["set? true" "true" "(set? #{1})"]
|
||||
["set? false on vector" "false" "(set? [1])"]
|
||||
["count dedups" "3" "(count (set [1 1 2 3]))"]
|
||||
["equality order-indep" "true" "(= #{1 2 3} #{3 2 1})"])
|
||||
["equality order-indep" "true" "(= #{1 2 3} #{3 2 1})"]
|
||||
# jolt-h86: into-conj had no set branch and returned the set unchanged
|
||||
["into set" "#{:a :b}" "(into #{} [:a :b])"]
|
||||
["into non-empty set" "#{1 2 3}" "(into #{1} [2 3 2])"])
|
||||
|
||||
(defspec "set / operations"
|
||||
["conj adds" "#{1 2 3}" "(conj #{1 2} 3)"]
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
# Specification: sorted collections (sorted-map / sorted-set, subseq/rsubseq).
|
||||
#
|
||||
# sorted collections are first-class for the core ops (jolt-ti9): get/assoc/dissoc/
|
||||
# conj/contains?/keys/vals/disj all work and preserve sort order, and a sorted coll
|
||||
# is callable as a key-lookup fn. STILL TODO: the by-comparator constructors
|
||||
# (sorted-map-by / sorted-set-by) ignore the supplied comparator (jolt-ti9). (vec
|
||||
# coerces a seq to a vector so expecteds are vector literals, not quoted lists.)
|
||||
# Sorted collections are pure Clojure (stage 3, jolt-0lj): the entries live in
|
||||
# a comparator-ordered vector, all ops are overlay Clojure attached to the
|
||||
# value, and the Janet seed only dispatches to them. Semantics match Clojure:
|
||||
# lookup/membership go through the COMPARATOR ((contains? (sorted-set 1) 1.0)
|
||||
# is true), equality is representation-agnostic ((= (sorted-map :a 1) {:a 1})),
|
||||
# empty?/empty see the collection (not the host wrapper) and (empty sc) keeps
|
||||
# the comparator. (vec coerces a seq to a vector so expecteds are vector
|
||||
# literals, not quoted lists.)
|
||||
(use ../support/harness)
|
||||
|
||||
(defspec "sorted / construction & ordering"
|
||||
|
|
@ -56,4 +59,62 @@
|
|||
["subseq >=" "[3 4 5]" "(vec (subseq (sorted-set 1 2 3 4 5) >= 3))"]
|
||||
["subseq <" "[1 2]" "(vec (subseq (sorted-set 1 2 3 4 5) < 3))"]
|
||||
["subseq range" "[2 3 4]" "(vec (subseq (sorted-set 1 2 3 4 5) > 1 < 5))"]
|
||||
["rsubseq <=" "[3 2 1]" "(vec (rsubseq (sorted-set 1 2 3 4 5) <= 3))"])
|
||||
["rsubseq <=" "[3 2 1]" "(vec (rsubseq (sorted-set 1 2 3 4 5) <= 3))"]
|
||||
["subseq on map" "[[2 :b] [3 :c]]" "(vec (subseq (sorted-map 1 :a 2 :b 3 :c) > 1))"]
|
||||
["subseq empty result" "nil" "(subseq (sorted-set 1 2) > 5)"]
|
||||
["rsubseq on map" "[[2 :b] [1 :a]]" "(vec (rsubseq (sorted-map 1 :a 2 :b 3 :c) < 3))"])
|
||||
|
||||
(defspec "sorted / predicates"
|
||||
["sorted-map? true" "true" "(sorted-map? (sorted-map 1 :a))"]
|
||||
["sorted-map? false" "false" "(sorted-map? {:a 1})"]
|
||||
["sorted-set? true" "true" "(sorted-set? (sorted-set 1))"]
|
||||
["sorted-set? false" "false" "(sorted-set? #{1})"]
|
||||
["map? sorted-map" "true" "(map? (sorted-map 1 :a))"]
|
||||
["coll? sorted-set" "true" "(coll? (sorted-set 1))"])
|
||||
|
||||
(defspec "sorted / lookup + membership use the comparator"
|
||||
["get cross-numeric" ":a" "(get (sorted-map 1 :a) 1.0)"]
|
||||
["contains? cross-numeric" "true" "(contains? (sorted-set 1) 1.0)"]
|
||||
["conj equal elem no-op" "1" "(count (conj (sorted-set 1) 1.0))"]
|
||||
["assoc equal key replaces" "[[1 :z]]" "(vec (seq (assoc (sorted-map 1 :a) 1.0 :z)))"]
|
||||
["first sorted-map" "[1 :a]" "(first (sorted-map 2 :b 1 :a))"]
|
||||
["dissoc missing no-op" "2" "(count (dissoc (sorted-map 1 :a 2 :b) 9))"]
|
||||
["conj map merges" "3" "(count (conj (sorted-map 1 :a) {2 :b 3 :c}))"]
|
||||
["conj nil no-op" "1" "(count (conj (sorted-map 1 :a) nil))"]
|
||||
["into sorted-map" "[[1 :a] [2 :b]]" "(vec (seq (into (sorted-map) [[2 :b] [1 :a]])))"]
|
||||
["source unchanged" "[1 2]" "(let [s (sorted-set 1 2)] (conj s 9) (vec (seq s)))"]
|
||||
["sorted-map odd kvs throws" :throws "(sorted-map 1 :a 2)"])
|
||||
|
||||
(defspec "sorted / equality is representation-agnostic"
|
||||
["sorted-map = literal" "true" "(= (sorted-map :a 1 :b 2) {:a 1 :b 2})"]
|
||||
["literal = sorted-map" "true" "(= {:a 1 :b 2} (sorted-map :a 1 :b 2))"]
|
||||
["sorted-map = hash-map" "true" "(= (sorted-map :a 1) (hash-map :a 1))"]
|
||||
["sorted-map != more keys" "false" "(= (sorted-map :a 1) {:a 1 :b 2})"]
|
||||
["sorted-set = literal" "true" "(= (sorted-set 1 2) #{1 2})"]
|
||||
["literal = sorted-set" "true" "(= #{1 2} (sorted-set 2 1))"]
|
||||
["sorted-set != diff" "false" "(= (sorted-set 1 2) #{1 3})"]
|
||||
["two sorted-maps" "true" "(= (sorted-map 1 :a 2 :b) (sorted-map 2 :b 1 :a))"]
|
||||
["cmp irrelevant to =" "true" "(= (sorted-map-by > 1 :a 2 :b) (sorted-map 1 :a 2 :b))"]
|
||||
["sorted-map as map key" ":hit" "(get {(sorted-map :a 1) :hit} {:a 1})"]
|
||||
["sorted-set as map key" ":hit" "(get {(sorted-set 1 2) :hit} #{2 1})"])
|
||||
|
||||
(defspec "sorted / empty + empty? + rseq + printing"
|
||||
["empty? empty map" "true" "(empty? (sorted-map))"]
|
||||
["empty? non-empty" "false" "(empty? (sorted-map 1 :a))"]
|
||||
["empty? empty set" "true" "(empty? (sorted-set))"]
|
||||
["empty keeps sortedness" "true" "(sorted? (empty (sorted-map 1 :a)))"]
|
||||
["empty keeps cmp" "[3 1]" "(vec (seq (into (empty (sorted-set-by > 1 2)) [1 3])))"]
|
||||
["empty set kind" "true" "(sorted-set? (empty (sorted-set 1)))"]
|
||||
["rseq map" "[[2 :b] [1 :a]]" "(vec (rseq (sorted-map 1 :a 2 :b)))"]
|
||||
["rseq set" "[3 2 1]" "(vec (rseq (sorted-set 1 2 3)))"]
|
||||
["pr-str sorted-map" "\"{1 :a, 2 :b}\"" "(pr-str (sorted-map 2 :b 1 :a))"]
|
||||
["pr-str sorted-set" "\"#{1 2 3}\"" "(pr-str (sorted-set 3 1 2))"])
|
||||
|
||||
(defspec "sorted / seq fn interop"
|
||||
["map over sorted-map" "[1 2 3]" "(vec (map first (sorted-map 2 :b 1 :a 3 :c)))"]
|
||||
["map over sorted-set" "[2 3 4]" "(vec (map inc (sorted-set 3 1 2)))"]
|
||||
["filter entries" "[[2 :b]]" "(vec (filter (fn [[k v]] (even? k)) (sorted-map 1 :a 2 :b)))"]
|
||||
["reduce over set" "6" "(reduce + (sorted-set 1 2 3))"]
|
||||
["vec of sorted-set" "[1 2 3]" "(vec (sorted-set 3 1 2))"]
|
||||
["into vec" "[[1 :a] [2 :b]]" "(into [] (sorted-map 2 :b 1 :a))"]
|
||||
["sorted-map-by 3way cmp" "[3 2 1]" "(vec (keys (sorted-map-by (fn [a b] (- b a)) 1 :a 2 :b 3 :c)))"])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue