Merge pull request #47 from jolt-lang/leaf-shrink-batch

core: Stage 3 — leaf batch: nine more seed fns to the overlay
This commit is contained in:
Dmitri Sotnikov 2026-06-10 14:57:10 -04:00 committed by GitHub
commit af34d94870
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
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))]
(jolt.host/ref-put! target (nth idxs+val (- n 2)) 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,
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)
>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)))
(defn core-complement [f]
(fn [& args] (not (apply f args))))
# complement now lives in the Clojure collection tier (core/20-coll.clj).
# Jolt has no inst/uri/uuid host types, so these are always false; inst-ms has
# nothing valid to read.
@ -2034,7 +2033,7 @@
# 64-bit integers (Janet int/s64 — C-backed)
(defn core-bigint [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.
(defn core-chunk-buffer [capacity] @[])
@ -2060,19 +2059,15 @@
# semantics) now live in the Clojure sorted tier (core/25-sorted.clj).
(defn core-array-seq [arr & _] (core-seq arr))
(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]
(cond
(nil? x) nil (number? x) "java.lang.Number" (string? x) "java.lang.String"
(boolean? x) "java.lang.Boolean" (keyword? x) "clojure.lang.Keyword"
(function? x) "clojure.lang.IFn" (buffer? x) "[B"
(string (type x))))
(defn core-clojure-version [] "1.11.0-jolt")
(defn core-munge [s]
(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)))
# clojure-version / munge / test now live in the Clojure collection tier
# (core/20-coll.clj).
# ============================================================
@ -2375,15 +2370,8 @@
(let [sub (core-get m k)]
(core-assoc m k (apply core-update-in (if (nil? sub) {} sub) (ks-rest ks) f args))))))
(defn core-fnil [f & defaults]
(fn [& args]
(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)))
# fnil now lives in the Clojure collection tier (core/20-coll.clj), with
# Clojure's canonical 2/3/4-arity (patch the first 1-3 args only).
# copy-var stubs for sci.impl.copy-vars (used by sci.impl.namespaces)
(defn core-copy-core-var [sym] nil)
@ -2637,8 +2625,8 @@
# 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
# requires a Ratio and throws otherwise).
(defn core-numerator [x] (error "numerator requires a ratio (Jolt has no ratios)"))
(defn core-denominator [x] (error "denominator requires a ratio (Jolt has no ratios)"))
# numerator / denominator now live in the Clojure collection tier (Jolt has
# no ratios; they throw, as on a non-ratio in Clojure).
(def- special-syms
{"if" true "do" true "let*" true "fn*" true "quote" true "var" true "def" true
@ -2914,8 +2902,6 @@
"key" core-key
"val" core-val
"map-entry?" core-map-entry?
"numerator" core-numerator
"denominator" core-denominator
"special-symbol?" core-special-symbol?
"promise" core-promise
"deliver" core-deliver
@ -3005,7 +2991,6 @@
"range" core-range
"identity" core-identity
"constantly" core-constantly
"complement" core-complement
"comp" core-comp
"partial" core-partial
"memoize" core-memoize
@ -3085,7 +3070,6 @@
"unchecked-double" core-unchecked-double
"bigint" core-bigint
"biginteger" core-biginteger
"bigdec" core-bigdec
"chunk-buffer" core-chunk-buffer
"chunk-append" core-chunk-append
"chunk" core-chunk
@ -3099,11 +3083,7 @@
"reader-conditional" core-reader-conditional
"array-seq" core-array-seq
"seque" core-seque
"supers" core-supers
"class" core-class
"clojure-version" core-clojure-version
"munge" core-munge
"test" core-test
"enumeration-seq" core-enumeration-seq
"iterator-seq" core-iterator-seq
"re-matcher" core-re-matcher
@ -3173,7 +3153,6 @@
"resolve" core-resolve
"update-in" core-update-in
"assoc-in" core-assoc-in
"fnil" core-fnil
"copy-core-var" core-copy-core-var
"copy-var" core-copy-var
"macrofy" core-macrofy

View file

@ -35,3 +35,22 @@
["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)"]
["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))"])