core: Stage 3 — leaf batch: complement/fnil/clojure-version/bigdec/numerator/denominator/supers/munge/test to the overlay

Nine more seed leaves move to 20-coll.clj (verified leaf-by-leaf: defn +
core-bindings entry only, no internal callers). fnil is upgraded to Clojure's
canonical 2/3/4-arity — it patches only the first 1-3 arguments; the old
kernel fn patched every position it had a default for, which Clojure does
not. The rest carry their kernel semantics over unchanged (bigdec is a
double, numerator/denominator throw, supers is #{}, munge rewrites dashes).

16 new spec rows incl. the fnil arity-contract cases. Gate green:
conformance 326x3, suite 4577, full jpm test (2:18 — first full run with the
ctx image cache on main).
This commit is contained in:
Yogthos 2026-06-10 14:56:50 -04:00
parent ee6447adae
commit 3faee14271
4 changed files with 77 additions and 30 deletions

View file

@ -592,3 +592,41 @@
target (reduce (fn [t k] (nth t k)) arr (take (- n 2) idxs+val))] target (reduce (fn [t k] (nth t k)) arr (take (- n 2) idxs+val))]
(jolt.host/ref-put! target (nth idxs+val (- n 2)) val) (jolt.host/ref-put! target (nth idxs+val (- n 2)) val)
val)) val))
;; --- Phase 2 leaf batch (jolt-ded): fn combinators + host-free stubs ---------
(defn complement
"Takes a fn f and returns a fn that takes the same arguments as f, has the
same effects, if any, and returns the opposite truth value."
[f]
(fn [& args] (not (apply f args))))
;; Canonical Clojure fnil: patches only the FIRST 1-3 arguments (the old Janet
;; kernel patched every position it had a default for, which Clojure does not).
(defn fnil
([f x]
(fn [a & args] (apply f (if (nil? a) x a) args)))
([f x y]
(fn [a b & args] (apply f (if (nil? a) x a) (if (nil? b) y b) args)))
([f x y z]
(fn [a b c & args]
(apply f (if (nil? a) x a) (if (nil? b) y b) (if (nil? c) z c) args))))
(defn clojure-version [] "1.11.0-jolt")
;; Jolt numbers are doubles; no BigDecimal, no ratios.
(defn bigdec [x] (* 1.0 x))
(defn numerator [x] (throw (ex-info "numerator requires a ratio (Jolt has no ratios)" {})))
(defn denominator [x] (throw (ex-info "denominator requires a ratio (Jolt has no ratios)" {})))
;; No class hierarchy on the Janet host.
(defn supers [x] #{})
;; The kernel's munge only rewrote dashes; kept as-is for parity.
(defn munge [s] (str-replace-all "-" "_" (str s)))
(defn test
"Calls the :test fn from v's metadata; :ok if it runs, :no-test if absent."
[v]
(let [t (:test (meta v))]
(if t (do (t) :ok) :no-test)))

View file

@ -69,6 +69,17 @@ test, rather than preserving the bug.
would recurse). GOTCHA for future attached-ops ports: inside the overlay, would recurse). GOTCHA for future attached-ops ports: inside the overlay,
NEVER read your own wrapper's fields with `get`. NEVER read your own wrapper's fields with `get`.
- **Leaf batch (jolt-ded)** — complement, fnil (canonical 2/3/4-arity: patch
the first 1-3 args only, unlike the old patch-everything kernel fn),
clojure-version, bigdec, numerator, denominator, supers, munge, test moved
to 20-coll.clj.
- ***in* reader family (jolt-0d9)** — *in*, read-line, read, with-in-str,
line-seq in a new `50-io.clj` tier over two seed seams (__stdin-read-line,
__parse-next). GOTCHAS: a map LITERAL with :jolt/type as a key parses as a
tagged form (don't tag overlay value maps); a leftover seed stub holding the
same name breaks direct-linked overlay self-recursion (line-seq bound to the
stub's root and truncated after one element) — delete the stub first.
## MOVABLE candidates (Phase 2 worklist, 193) ## MOVABLE candidates (Phase 2 worklist, 193)
>Eduction NaN? abs aclone alength ancestors array-map array-seq assoc! associative? bean bigdec bigint biginteger boolean boolean? booleans byte bytes bytes? cat char char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class clojure-version comparator compare-and-set! completing conj! counted? decimal? deliver denominator derive descendants destructure disj disj! dissoc! distinct? doall dorun double? doubles drop-last eduction empty ensure-reduced enumeration-seq ex-cause ex-data ex-info ex-info? ex-message find float? floats force halt-when hash-combine hash-map hash-ordered-coll hash-set hash-unordered-coll ident? ifn? indexed? infinite? inst-ms inst? integer? ints isa? iterator-seq key keyword keyword-identical? list* list? longs macrofy map-entry? memfn munge nat-int? neg-int? not-any? not-every? nthnext nthrest numerator numeric= object? parents persistent! pop pop! pos-int? pr prefers println-str prn-str promise qualified-ident? qualified-keyword? qualified-symbol? rand rand-nth random-sample ratio? rational? rationalize re-groups re-matcher record? reduce-kv reduced reduced? reductions replace replicate resolve reversible? rseq rsubseq run! seq-to-map-for-destructuring seque set set? short shorts shuffle simple-ident? simple-keyword? simple-symbol? some-search sort sort-by sorted-map sorted-map-by sorted-map? sorted-set sorted-set-by sorted-set? special-symbol? split-at split-with str-join str-replace-all str-replace-first str-split subseq supers symbol tagged-literal tagged-literal? take-last test transduce unchecked-add unchecked-byte unchecked-char unchecked-dec unchecked-divide-int unchecked-double unchecked-float unchecked-inc unchecked-int unchecked-multiply unchecked-negate unchecked-remainder-int unchecked-short unchecked-subtract undefined? underive uri? uuid? val vector volatile! volatile? xml-seq >Eduction NaN? abs aclone alength ancestors array-map array-seq assoc! associative? bean bigdec bigint biginteger boolean boolean? booleans byte bytes bytes? cat char char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class clojure-version comparator compare-and-set! completing conj! counted? decimal? deliver denominator derive descendants destructure disj disj! dissoc! distinct? doall dorun double? doubles drop-last eduction empty ensure-reduced enumeration-seq ex-cause ex-data ex-info ex-info? ex-message find float? floats force halt-when hash-combine hash-map hash-ordered-coll hash-set hash-unordered-coll ident? ifn? indexed? infinite? inst-ms inst? integer? ints isa? iterator-seq key keyword keyword-identical? list* list? longs macrofy map-entry? memfn munge nat-int? neg-int? not-any? not-every? nthnext nthrest numerator numeric= object? parents persistent! pop pop! pos-int? pr prefers println-str prn-str promise qualified-ident? qualified-keyword? qualified-symbol? rand rand-nth random-sample ratio? rational? rationalize re-groups re-matcher record? reduce-kv reduced reduced? reductions replace replicate resolve reversible? rseq rsubseq run! seq-to-map-for-destructuring seque set set? short shorts shuffle simple-ident? simple-keyword? simple-symbol? some-search sort sort-by sorted-map sorted-map-by sorted-map? sorted-set sorted-set-by sorted-set? special-symbol? split-at split-with str-join str-replace-all str-replace-first str-split subseq supers symbol tagged-literal tagged-literal? take-last test transduce unchecked-add unchecked-byte unchecked-char unchecked-dec unchecked-divide-int unchecked-double unchecked-float unchecked-inc unchecked-int unchecked-multiply unchecked-negate unchecked-remainder-int unchecked-short unchecked-subtract undefined? underive uri? uuid? val vector volatile! volatile? xml-seq

View file

@ -1485,8 +1485,7 @@
(def core-constantly (fn [x] (fn [& _] x))) (def core-constantly (fn [x] (fn [& _] x)))
(defn core-complement [f] # complement now lives in the Clojure collection tier (core/20-coll.clj).
(fn [& args] (not (apply f args))))
# Jolt has no inst/uri/uuid host types, so these are always false; inst-ms has # Jolt has no inst/uri/uuid host types, so these are always false; inst-ms has
# nothing valid to read. # nothing valid to read.
@ -2034,7 +2033,7 @@
# 64-bit integers (Janet int/s64 — C-backed) # 64-bit integers (Janet int/s64 — C-backed)
(defn core-bigint [x] (int/s64 x)) (defn core-bigint [x] (int/s64 x))
(defn core-biginteger [x] (int/s64 x)) (defn core-biginteger [x] (int/s64 x))
(defn core-bigdec [x] (* 1.0 x)) # no BigDecimal; use a double # bigdec now lives in the Clojure collection tier (no BigDecimal: a double).
# Chunked seqs — Jolt does not chunk, so these are simple eager equivalents. # Chunked seqs — Jolt does not chunk, so these are simple eager equivalents.
(defn core-chunk-buffer [capacity] @[]) (defn core-chunk-buffer [capacity] @[])
@ -2060,19 +2059,15 @@
# semantics) now live in the Clojure sorted tier (core/25-sorted.clj). # semantics) now live in the Clojure sorted tier (core/25-sorted.clj).
(defn core-array-seq [arr & _] (core-seq arr)) (defn core-array-seq [arr & _] (core-seq arr))
(defn core-seque [& args] (in args (- (length args) 1))) (defn core-seque [& args] (in args (- (length args) 1)))
(defn core-supers [x] (make-phs)) # supers now lives in the Clojure collection tier (no class hierarchy: #{}).
(defn core-class [x] (defn core-class [x]
(cond (cond
(nil? x) nil (number? x) "java.lang.Number" (string? x) "java.lang.String" (nil? x) nil (number? x) "java.lang.Number" (string? x) "java.lang.String"
(boolean? x) "java.lang.Boolean" (keyword? x) "clojure.lang.Keyword" (boolean? x) "java.lang.Boolean" (keyword? x) "clojure.lang.Keyword"
(function? x) "clojure.lang.IFn" (buffer? x) "[B" (function? x) "clojure.lang.IFn" (buffer? x) "[B"
(string (type x)))) (string (type x))))
(defn core-clojure-version [] "1.11.0-jolt") # clojure-version / munge / test now live in the Clojure collection tier
(defn core-munge [s] # (core/20-coll.clj).
(string/replace-all "-" "_" (string s)))
(defn core-test [v]
(let [t (and (core-meta v) (get (core-meta v) :test))]
(if t (do (t) :ok) :no-test)))
# ============================================================ # ============================================================
@ -2375,15 +2370,8 @@
(let [sub (core-get m k)] (let [sub (core-get m k)]
(core-assoc m k (apply core-update-in (if (nil? sub) {} sub) (ks-rest ks) f args)))))) (core-assoc m k (apply core-update-in (if (nil? sub) {} sub) (ks-rest ks) f args))))))
(defn core-fnil [f & defaults] # fnil now lives in the Clojure collection tier (core/20-coll.clj), with
(fn [& args] # Clojure's canonical 2/3/4-arity (patch the first 1-3 args only).
(def new-args (array/slice args))
(var i 0)
(each d defaults
(when (and (< i (length new-args)) (nil? (in new-args i)))
(put new-args i d))
(++ i))
(apply f new-args)))
# copy-var stubs for sci.impl.copy-vars (used by sci.impl.namespaces) # copy-var stubs for sci.impl.copy-vars (used by sci.impl.namespaces)
(defn core-copy-core-var [sym] nil) (defn core-copy-core-var [sym] nil)
@ -2637,8 +2625,8 @@
# ratio?/decimal?/rational? live in the Clojure collection tier (core/20-coll.clj). # ratio?/decimal?/rational? live in the Clojure collection tier (core/20-coll.clj).
# Jolt has no ratio type, so numerator/denominator have no valid input (Clojure # Jolt has no ratio type, so numerator/denominator have no valid input (Clojure
# requires a Ratio and throws otherwise). # requires a Ratio and throws otherwise).
(defn core-numerator [x] (error "numerator requires a ratio (Jolt has no ratios)")) # numerator / denominator now live in the Clojure collection tier (Jolt has
(defn core-denominator [x] (error "denominator requires a ratio (Jolt has no ratios)")) # no ratios; they throw, as on a non-ratio in Clojure).
(def- special-syms (def- special-syms
{"if" true "do" true "let*" true "fn*" true "quote" true "var" true "def" true {"if" true "do" true "let*" true "fn*" true "quote" true "var" true "def" true
@ -2914,8 +2902,6 @@
"key" core-key "key" core-key
"val" core-val "val" core-val
"map-entry?" core-map-entry? "map-entry?" core-map-entry?
"numerator" core-numerator
"denominator" core-denominator
"special-symbol?" core-special-symbol? "special-symbol?" core-special-symbol?
"promise" core-promise "promise" core-promise
"deliver" core-deliver "deliver" core-deliver
@ -3005,7 +2991,6 @@
"range" core-range "range" core-range
"identity" core-identity "identity" core-identity
"constantly" core-constantly "constantly" core-constantly
"complement" core-complement
"comp" core-comp "comp" core-comp
"partial" core-partial "partial" core-partial
"memoize" core-memoize "memoize" core-memoize
@ -3085,7 +3070,6 @@
"unchecked-double" core-unchecked-double "unchecked-double" core-unchecked-double
"bigint" core-bigint "bigint" core-bigint
"biginteger" core-biginteger "biginteger" core-biginteger
"bigdec" core-bigdec
"chunk-buffer" core-chunk-buffer "chunk-buffer" core-chunk-buffer
"chunk-append" core-chunk-append "chunk-append" core-chunk-append
"chunk" core-chunk "chunk" core-chunk
@ -3099,11 +3083,7 @@
"reader-conditional" core-reader-conditional "reader-conditional" core-reader-conditional
"array-seq" core-array-seq "array-seq" core-array-seq
"seque" core-seque "seque" core-seque
"supers" core-supers
"class" core-class "class" core-class
"clojure-version" core-clojure-version
"munge" core-munge
"test" core-test
"enumeration-seq" core-enumeration-seq "enumeration-seq" core-enumeration-seq
"iterator-seq" core-iterator-seq "iterator-seq" core-iterator-seq
"re-matcher" core-re-matcher "re-matcher" core-re-matcher
@ -3173,7 +3153,6 @@
"resolve" core-resolve "resolve" core-resolve
"update-in" core-update-in "update-in" core-update-in
"assoc-in" core-assoc-in "assoc-in" core-assoc-in
"fnil" core-fnil
"copy-core-var" core-copy-core-var "copy-core-var" core-copy-core-var
"copy-var" core-copy-var "copy-var" core-copy-var
"macrofy" core-macrofy "macrofy" core-macrofy

View file

@ -35,3 +35,22 @@
["some-fn" "true" "((some-fn even? neg?) 3 4)"] ["some-fn" "true" "((some-fn even? neg?) 3 4)"]
["memoize" "2" "(do (def c (atom 0)) (def f (memoize (fn [x] (swap! c inc) x))) (f 1) (f 1) (f 2) @c)"] ["memoize" "2" "(do (def c (atom 0)) (def f (memoize (fn [x] (swap! c inc) x))) (f 1) (f 1) (f 2) @c)"]
["trampoline" "10" "(trampoline (fn f [n acc] (if (zero? n) acc (fn [] (f (dec n) (+ acc 2))))) 5 0)"]) ["trampoline" "10" "(trampoline (fn f [n acc] (if (zero? n) acc (fn [] (f (dec n) (+ acc 2))))) 5 0)"])
# Phase 2 leaf batch (jolt-ded): moved from the Janet seed to 20-coll.clj.
(defspec "clojure.core / leaf batch (complement fnil munge etc.)"
["complement true" "true" "((complement pos?) -1)"]
["complement false" "false" "((complement pos?) 1)"]
["complement multi" "true" "((complement <) 3 2)"]
["fnil patches nil" "1" "((fnil inc 0) nil)"]
["fnil passes non-nil" "6" "((fnil inc 0) 5)"]
["fnil two defaults" "8" "((fnil + 1 2) nil nil 5)"]
["fnil only first 3" "[:a :b :c nil]" "((fnil vector :a :b :c) nil nil nil nil)"]
["fnil in update" "{:k 1}" "(update {} :k (fnil inc 0))"]
["clojure-version" "true" "(string? (clojure-version))"]
["bigdec" "3" "(bigdec 3)"]
["numerator throws" :throws "(numerator 1)"]
["denominator throws" :throws "(denominator 1)"]
["supers empty set" "#{}" "(supers 1)"]
["munge dashes" "\"a_b\"" "(munge \"a-b\")"]
["munge symbol" "\"x_y\"" "(munge (quote x-y))"]
["test no-test" ":no-test" "(test (quote foo))"])