diff --git a/jolt-core/clojure/core/20-coll.clj b/jolt-core/clojure/core/20-coll.clj index 340af96..726f7ec 100644 --- a/jolt-core/clojure/core/20-coll.clj +++ b/jolt-core/clojure/core/20-coll.clj @@ -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))) diff --git a/jolt-core/clojure/core/MIGRATION.md b/jolt-core/clojure/core/MIGRATION.md index 81f8bd9..1d36eb8 100644 --- a/jolt-core/clojure/core/MIGRATION.md +++ b/jolt-core/clojure/core/MIGRATION.md @@ -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 diff --git a/src/jolt/core.janet b/src/jolt/core.janet index e0e5c62..a5768a8 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -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 diff --git a/test/spec/functions-spec.janet b/test/spec/functions-spec.janet index ef7c24e..b06e4a4 100644 --- a/test/spec/functions-spec.janet +++ b/test/spec/functions-spec.janet @@ -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))"])