core: spec 35-var batch A — 1.11 parsers, map/partition variants, with-redefs, ns fns
Fifteen vars from the spec coverage gap (docs/spec/coverage.md): parse-long/parse-double/parse-boolean (strict validation; scan-number alone accepts 0x10), newline, current-time-ms (host clock for time), update-keys/ update-vals (PHM base, collisions last-wins), partitionv/partitionv-all/ splitv-at (lazy seqs of vectors; splitv-at's tail stays a seq, matching the reference), with-redefs/with-redefs-fn (roots restored on throw), time, macroexpand (expand-1 to fixpoint), alias/ns-unalias (write BOTH alias stores — require :as uses string-keyed :imports while ns-aliases reads :aliases; split filed), ns-publics (symbol-keyed map; publics == interns, no privacy). Three pre-existing bugs fixed along the way: - (partition n step pad coll) misparsed pad as the coll and returned () - (require 'bare.symbol) rejected — only vector specs were accepted - analyze-form leaked the interpreted analyzer's ns on a punt: a throw out of the analyzer left current-ns=jolt.analyzer and :compile-ns set, so the fallback interpretation resolved user vars against the wrong namespace (bit (var user-sym) under compile mode) And one overlay-authoring landmine documented in-code: a 20-coll fn must not use 30-macros macros (with-redefs-fn's dotimes compiled as a forward ref that resolved to the macro fn at runtime) — loop/recur instead. Gate: conformance 316x3 (+14 rows), suite 4324 pass / 78 clean (was 4081/72; parse_*/update_* files now contribute), baselines raised, all specs+unit, fixpoint, self-host, sci, staged. Coverage: missing-portable 35 -> 20.
This commit is contained in:
parent
894af34b4c
commit
eb7a9f1b20
13 changed files with 277 additions and 44 deletions
|
|
@ -3,14 +3,14 @@
|
|||
Generated 2026-06-10 by `tools/spec_coverage.py` — do not edit by hand.
|
||||
|
||||
Surface: **694** clojure.core vars (ClojureDocs export; 648 with
|
||||
community examples). jolt interns 534 of them.
|
||||
community examples). jolt interns 550 of them.
|
||||
|
||||
| Status | Count | Meaning |
|
||||
|---|---|---|
|
||||
| implemented+tested | 380 | in jolt and exercised by spec/conformance |
|
||||
| implemented-untested | 154 | in jolt, no direct test — spec entries will add them |
|
||||
| implemented+tested | 397 | in jolt and exercised by spec/conformance |
|
||||
| implemented-untested | 153 | in jolt, no direct test — spec entries will add them |
|
||||
| resolvable-not-interned | 22 | works in code but invisible to ns introspection (conformance finding) |
|
||||
| missing-portable | 35 | portable semantics, jolt lacks it — implementation gap |
|
||||
| missing-portable | 19 | portable semantics, jolt lacks it — implementation gap |
|
||||
| special-form | 13 | specified in §3, not a library var |
|
||||
| dynamic-var | 29 | classification needed: portable default vs host-dependent |
|
||||
| agents-taps | 22 | out of scope pending concurrency design note |
|
||||
|
|
@ -103,7 +103,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `agent-errors` | agents-taps | |
|
||||
| `aget` | implemented+tested | ✓ |
|
||||
| `alength` | implemented+tested | ✓ |
|
||||
| `alias` | missing-portable | ✓ |
|
||||
| `alias` | implemented+tested | ✓ |
|
||||
| `all-ns` | implemented-untested | ✓ |
|
||||
| `alter` | stm-refs | ✓ |
|
||||
| `alter-meta!` | implemented+tested | ✓ |
|
||||
|
|
@ -390,7 +390,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `long-array` | implemented-untested | ✓ |
|
||||
| `longs` | implemented-untested | ✓ |
|
||||
| `loop` | implemented+tested | ✓ |
|
||||
| `macroexpand` | missing-portable | ✓ |
|
||||
| `macroexpand` | implemented+tested | ✓ |
|
||||
| `macroexpand-1` | implemented+tested | ✓ |
|
||||
| `make-array` | implemented-untested | ✓ |
|
||||
| `make-hierarchy` | implemented+tested | ✓ |
|
||||
|
|
@ -423,7 +423,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `neg-int?` | implemented+tested | ✓ |
|
||||
| `neg?` | implemented+tested | ✓ |
|
||||
| `new` | special-form | ✓ |
|
||||
| `newline` | missing-portable | ✓ |
|
||||
| `newline` | implemented+tested | ✓ |
|
||||
| `next` | implemented+tested | ✓ |
|
||||
| `nfirst` | implemented-untested | ✓ |
|
||||
| `nil?` | implemented+tested | ✓ |
|
||||
|
|
@ -434,15 +434,15 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `not-every?` | implemented+tested | ✓ |
|
||||
| `not=` | implemented+tested | ✓ |
|
||||
| `ns` | implemented+tested | ✓ |
|
||||
| `ns-aliases` | implemented-untested | ✓ |
|
||||
| `ns-aliases` | implemented+tested | ✓ |
|
||||
| `ns-imports` | implemented-untested | ✓ |
|
||||
| `ns-interns` | implemented-untested | ✓ |
|
||||
| `ns-map` | implemented-untested | ✓ |
|
||||
| `ns-name` | implemented+tested | ✓ |
|
||||
| `ns-publics` | missing-portable | ✓ |
|
||||
| `ns-publics` | implemented+tested | ✓ |
|
||||
| `ns-refers` | missing-portable | ✓ |
|
||||
| `ns-resolve` | implemented+tested | ✓ |
|
||||
| `ns-unalias` | missing-portable | ✓ |
|
||||
| `ns-unalias` | implemented+tested | ✓ |
|
||||
| `ns-unmap` | implemented-untested | ✓ |
|
||||
| `nth` | implemented+tested | ✓ |
|
||||
| `nthnext` | implemented+tested | ✓ |
|
||||
|
|
@ -454,16 +454,16 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `odd?` | implemented+tested | ✓ |
|
||||
| `or` | implemented+tested | ✓ |
|
||||
| `parents` | implemented+tested | ✓ |
|
||||
| `parse-boolean` | missing-portable | ✓ |
|
||||
| `parse-double` | missing-portable | ✓ |
|
||||
| `parse-long` | missing-portable | ✓ |
|
||||
| `parse-boolean` | implemented+tested | ✓ |
|
||||
| `parse-double` | implemented+tested | ✓ |
|
||||
| `parse-long` | implemented+tested | ✓ |
|
||||
| `parse-uuid` | implemented+tested | ✓ |
|
||||
| `partial` | implemented+tested | ✓ |
|
||||
| `partition` | implemented+tested | ✓ |
|
||||
| `partition-all` | implemented+tested | ✓ |
|
||||
| `partition-by` | implemented+tested | ✓ |
|
||||
| `partitionv` | missing-portable | |
|
||||
| `partitionv-all` | missing-portable | |
|
||||
| `partitionv` | implemented+tested | |
|
||||
| `partitionv-all` | implemented+tested | |
|
||||
| `pcalls` | jvm-specific | ✓ |
|
||||
| `peek` | implemented+tested | ✓ |
|
||||
| `persistent!` | implemented+tested | ✓ |
|
||||
|
|
@ -611,7 +611,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `spit` | implemented-untested | ✓ |
|
||||
| `split-at` | implemented+tested | ✓ |
|
||||
| `split-with` | implemented+tested | ✓ |
|
||||
| `splitv-at` | missing-portable | |
|
||||
| `splitv-at` | implemented+tested | |
|
||||
| `str` | implemented+tested | ✓ |
|
||||
| `stream-into!` | jvm-specific | |
|
||||
| `stream-reduce!` | jvm-specific | |
|
||||
|
|
@ -640,7 +640,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `the-ns` | implemented+tested | ✓ |
|
||||
| `thread-bound?` | missing-portable | ✓ |
|
||||
| `throw` | special-form | ✓ |
|
||||
| `time` | missing-portable | ✓ |
|
||||
| `time` | implemented+tested | ✓ |
|
||||
| `to-array` | implemented-untested | ✓ |
|
||||
| `to-array-2d` | implemented+tested | ✓ |
|
||||
| `trampoline` | implemented+tested | ✓ |
|
||||
|
|
@ -678,9 +678,9 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `unsigned-bit-shift-right` | implemented-untested | ✓ |
|
||||
| `update` | implemented+tested | ✓ |
|
||||
| `update-in` | implemented+tested | ✓ |
|
||||
| `update-keys` | missing-portable | ✓ |
|
||||
| `update-keys` | implemented+tested | ✓ |
|
||||
| `update-proxy` | implemented-untested | ✓ |
|
||||
| `update-vals` | missing-portable | ✓ |
|
||||
| `update-vals` | implemented+tested | ✓ |
|
||||
| `uri?` | implemented-untested | ✓ |
|
||||
| `use` | implemented+tested | ✓ |
|
||||
| `uuid?` | implemented+tested | ✓ |
|
||||
|
|
@ -714,8 +714,8 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `with-open` | missing-portable | ✓ |
|
||||
| `with-out-str` | implemented+tested | ✓ |
|
||||
| `with-precision` | missing-portable | ✓ |
|
||||
| `with-redefs` | missing-portable | ✓ |
|
||||
| `with-redefs-fn` | missing-portable | ✓ |
|
||||
| `with-redefs` | implemented+tested | ✓ |
|
||||
| `with-redefs-fn` | implemented+tested | ✓ |
|
||||
| `xml-seq` | implemented-untested | ✓ |
|
||||
| `zero?` | implemented+tested | ✓ |
|
||||
| `zipmap` | implemented+tested | ✓ |
|
||||
|
|
|
|||
|
|
@ -287,6 +287,46 @@
|
|||
(defn tagged-literal? [x] (= (get x :jolt/type) :jolt/tagged-literal))
|
||||
(defn record? [x] (some? (get x :jolt/deftype)))
|
||||
(defn uuid? [x] (= (get x :jolt/type) :jolt/uuid))
|
||||
|
||||
;; Clojure 1.11 map transformers. PHM base so transformed keys canonicalize
|
||||
;; (collisions: last entry in seq order wins, matching the reference).
|
||||
(defn update-keys [m f]
|
||||
(reduce-kv (fn [acc k v] (assoc acc (f k) v)) (hash-map) m))
|
||||
|
||||
(defn update-vals [m f]
|
||||
(reduce-kv (fn [acc k v] (assoc acc k (f v))) (hash-map) m))
|
||||
|
||||
;; Vector-returning partition variants (1.11): lazy seqs OF vectors.
|
||||
(defn partitionv
|
||||
([n coll] (map vec (partition n coll)))
|
||||
([n step coll] (map vec (partition n step coll)))
|
||||
([n step pad coll] (map vec (partition n step pad coll))))
|
||||
|
||||
(defn partitionv-all
|
||||
([n coll] (map vec (partition-all n coll)))
|
||||
([n step coll] (map vec (partition-all n step coll))))
|
||||
|
||||
;; First part a vector, rest a seq — matching the reference implementation.
|
||||
(defn splitv-at [n coll]
|
||||
[(vec (take n coll)) (drop n coll)])
|
||||
|
||||
;; with-redefs-fn: temporarily set each var's root to the mapped value, run
|
||||
;; the thunk, restore the saved roots even on throw. The with-redefs macro
|
||||
;; (30-macros) builds the {var val} map from names.
|
||||
(defn with-redefs-fn [binding-map func]
|
||||
(let [vars (vec (keys binding-map))
|
||||
saved (mapv var-get vars)]
|
||||
(doseq [v vars] (var-set v (get binding-map v)))
|
||||
(try
|
||||
(func)
|
||||
(finally
|
||||
;; loop/recur, not dotimes: dotimes is a 30-macros macro and this tier
|
||||
;; compiles before it exists (a forward ref would resolve to the macro
|
||||
;; fn at runtime and mis-apply it).
|
||||
(loop [i 0]
|
||||
(when (< i (count vars))
|
||||
(var-set (nth vars i) (nth saved i))
|
||||
(recur (inc i))))))))
|
||||
;; Jolt has no chunked seqs (Phase 5 territory), so this is always false.
|
||||
(defn chunked-seq? [x] false)
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,22 @@
|
|||
|
||||
;; defonce: define name only if it isn't already bound to a non-nil root;
|
||||
;; returns the existing var untouched otherwise (matching the prior arm).
|
||||
;; time: evaluate expr, print the elapsed wall-clock, return the value.
|
||||
;; current-time-ms is the host's monotonic clock.
|
||||
(defmacro time [expr]
|
||||
`(let [start# (current-time-ms)
|
||||
ret# ~expr]
|
||||
(println (str "Elapsed time: " (- (current-time-ms) start#) " msecs"))
|
||||
ret#))
|
||||
|
||||
;; with-redefs: temporary root rebinding, restored on exit (incl. throw).
|
||||
;; Builds (hash-map (var n1) v1 ...) — a call form, since map-literal forms
|
||||
;; can't carry call forms as keys.
|
||||
(defmacro with-redefs [bindings & body]
|
||||
(let [pairs (reduce (fn [acc p] (conj (conj acc `(var ~(first p))) (second p)))
|
||||
[] (partition 2 bindings))]
|
||||
`(with-redefs-fn (hash-map ~@pairs) (fn [] ~@body))))
|
||||
|
||||
(defmacro defonce [name expr]
|
||||
`(let [v# (resolve (quote ~name))]
|
||||
(if (and v# (some? (var-get v#)))
|
||||
|
|
|
|||
|
|
@ -351,15 +351,24 @@
|
|||
# jolt.analyzer), and the interpreter rebinds current-ns to a fn's defining ns
|
||||
# while it runs — so h/current-ns must read this instead of ctx-current-ns.
|
||||
(put (ctx :env) :compile-ns (ctx-current-ns ctx))
|
||||
(def saved-ns (ctx-current-ns ctx))
|
||||
(def av (ns-find (ctx-find-ns ctx "jolt.analyzer") "analyze"))
|
||||
# Pre-kernel bootstrap: ensure-analyzer is gated until the kernel tier loads
|
||||
# (see api/load-core-overlay!), so a compile request from an earlier tier (e.g.
|
||||
# 00-syntax's destructure defn) finds no analyzer. That fallback is DESIGNED —
|
||||
# route it through the sanctioned punt channel rather than crashing on a nil var.
|
||||
(unless av (error "jolt/uncompilable: analyzer not built (pre-kernel bootstrap)"))
|
||||
(def r ((var-get av) ctx form))
|
||||
(unless av
|
||||
(put (ctx :env) :compile-ns nil)
|
||||
(error "jolt/uncompilable: analyzer not built (pre-kernel bootstrap)"))
|
||||
# The analyzer runs INTERPRETED; the interpreter rebinds current-ns to a fn's
|
||||
# defining ns (jolt.analyzer) while it runs and only restores on normal return.
|
||||
# A punt THROWS out of those frames, leaking jolt.analyzer as current-ns (and
|
||||
# :compile-ns stayed set) — the fallback interpretation then resolves user vars
|
||||
# against the wrong ns. Restore both on every exit.
|
||||
(def r (protect ((var-get av) ctx form)))
|
||||
(put (ctx :env) :compile-ns nil)
|
||||
r)
|
||||
(ctx-set-current-ns ctx saved-ns)
|
||||
(if (r 0) (r 1) (error (r 1))))
|
||||
|
||||
# The analyzer's deliberate punt signal — (uncompilable why) throws the string
|
||||
# "jolt/uncompilable: <why>". Anything else escaping the compile step is an
|
||||
|
|
|
|||
|
|
@ -1409,12 +1409,15 @@
|
|||
# (core/20-coll.clj).
|
||||
|
||||
(defn core-partition
|
||||
"(partition n coll) or (partition n step coll). Only complete partitions of
|
||||
size n are kept (use partition-all to keep the trailing remainder)."
|
||||
"(partition n coll), (partition n step coll), or (partition n step pad coll).
|
||||
Only complete partitions of size n are kept; with pad, the final partial
|
||||
partition is padded from pad (possibly to fewer than n if pad runs out)."
|
||||
[n & rest]
|
||||
(let [has-step (> (length rest) 1)
|
||||
step (if has-step (first rest) n)
|
||||
coll (if has-step (in rest 1) (first rest))]
|
||||
(let [argc (length rest)
|
||||
step (if (>= argc 2) (first rest) n)
|
||||
pad (if (>= argc 3) (in rest 1) nil)
|
||||
has-pad (>= argc 3)
|
||||
coll (case argc 1 (first rest) 2 (in rest 1) 3 (in rest 2))]
|
||||
# Option A: always lazy.
|
||||
(defn pstep [c]
|
||||
(fn []
|
||||
|
|
@ -1425,9 +1428,16 @@
|
|||
(array/push part (core-first cur))
|
||||
(set cur (core-rest cur))
|
||||
(++ i))
|
||||
(if (= i n)
|
||||
(cond
|
||||
(= i n)
|
||||
(let [next-cur (if (= step n) cur (lazy-from (core-drop (- step n) cur)))]
|
||||
@[(tuple/slice (tuple ;part)) (pstep next-cur)])
|
||||
# partial final partition: pad it (last partition, then stop)
|
||||
(and has-pad (> i 0))
|
||||
(do
|
||||
(each x (realize-for-iteration pad)
|
||||
(when (< (length part) n) (array/push part x)))
|
||||
@[(tuple/slice (tuple ;part)) (fn [] nil)])
|
||||
nil)))))
|
||||
(make-lazy-seq (pstep (lazy-from coll)))))
|
||||
|
||||
|
|
@ -1775,6 +1785,44 @@
|
|||
(prin "\n")
|
||||
nil)
|
||||
|
||||
(defn core-newline [] (prin "\n") nil)
|
||||
|
||||
# Clojure 1.11 string->scalar parsers: nil on malformed input, throw on a
|
||||
# non-string. Validation is strict (scan-number alone accepts 0x10 etc.).
|
||||
(defn- parse-arg-str [s who]
|
||||
(if (or (string? s) (buffer? s)) (string s)
|
||||
(error (string who " requires a string, got " (type s)))))
|
||||
|
||||
(defn core-parse-long [s]
|
||||
(def str* (parse-arg-str s "parse-long"))
|
||||
(def n (length str*))
|
||||
(def start (if (and (> n 0) (or (= 43 (in str* 0)) (= 45 (in str* 0)))) 1 0))
|
||||
(if (and (> n start)
|
||||
(do (var ok true)
|
||||
(for i start n (when (or (< (in str* i) 48) (> (in str* i) 57)) (set ok false)))
|
||||
ok))
|
||||
(scan-number str*)
|
||||
nil))
|
||||
|
||||
(defn core-parse-double [s]
|
||||
(def str* (parse-arg-str s "parse-double"))
|
||||
# strict float shape: [+-] digits [. digits] [eE [+-] digits] — at least one
|
||||
# digit overall; "Infinity"/"-Infinity"/"NaN" accepted like the reference.
|
||||
(cond
|
||||
(= str* "Infinity") math/inf
|
||||
(= str* "-Infinity") (- math/inf)
|
||||
(= str* "NaN") math/nan
|
||||
(do
|
||||
(def pat (peg/compile ~(sequence (opt (set "+-")) (choice (sequence (some :d) (opt (sequence "." (any :d)))) (sequence "." (some :d))) (opt (sequence (set "eE") (opt (set "+-")) (some :d))) -1)))
|
||||
(if (peg/match pat str*) (scan-number str*) nil))))
|
||||
|
||||
(defn core-parse-boolean [s]
|
||||
(def str* (parse-arg-str s "parse-boolean"))
|
||||
(case str* "true" true "false" false nil))
|
||||
|
||||
# Host time source for the `time` macro (monotonic, milliseconds).
|
||||
(defn core-current-time-ms [] (* 1000 (os/clock :monotonic)))
|
||||
|
||||
# Capture *out*: run thunk with Janet's :out dynamic bound to a buffer, so all
|
||||
# print/println/pr/prn output (which go through `prin` -> (dyn :out)) is collected
|
||||
# and returned as a string. The with-out-str macro (overlay) wraps a body thunk.
|
||||
|
|
@ -2942,6 +2990,11 @@
|
|||
"hash-unordered-coll" core-hash-unordered-coll
|
||||
"prefers" core-prefers
|
||||
"random-uuid" core-random-uuid
|
||||
"parse-long" core-parse-long
|
||||
"parse-double" core-parse-double
|
||||
"parse-boolean" core-parse-boolean
|
||||
"newline" core-newline
|
||||
"current-time-ms" core-current-time-ms
|
||||
"parse-uuid" core-parse-uuid
|
||||
"interpose" core-interpose
|
||||
"mapcat" core-mapcat
|
||||
|
|
|
|||
|
|
@ -784,13 +784,14 @@
|
|||
(defn require-impl
|
||||
"(require '[ns :as a :refer [...]] ...) — load + alias/refer each spec. A fn, so
|
||||
the args (quoted specs) arrive evaluated. Varargs (Clojure-compatible); each spec
|
||||
is a vector. eval-require does the namespace load + alias/refer into current-ns."
|
||||
is a vector [ns & opts] or a bare ns symbol (treated as [ns])."
|
||||
[ctx & specs]
|
||||
(each spec specs
|
||||
(let [s (if (pvec? spec) (pv->array spec) spec)]
|
||||
(if (and (indexed? s) (> (length s) 0))
|
||||
(eval-require ctx s)
|
||||
(error "require expects a vector spec"))))
|
||||
(cond
|
||||
(and (indexed? s) (> (length s) 0)) (eval-require ctx s)
|
||||
(and (struct? s) (= :symbol (s :jolt/type))) (eval-require ctx @[s])
|
||||
(error "require expects a vector spec or a namespace symbol"))))
|
||||
nil)
|
||||
|
||||
(defn in-ns-impl
|
||||
|
|
@ -1102,15 +1103,47 @@
|
|||
# Reader / expansion as plain fns: read-string parses one form; macroexpand-1
|
||||
# expands a (quoted, already-evaluated) call form once via its macro var.
|
||||
(ns-intern core "read-string" (fn [s] (parse-string s)))
|
||||
(ns-intern core "macroexpand-1"
|
||||
(def expand-1 (fn [the-form]
|
||||
(if (and (array? the-form) (> (length the-form) 0)
|
||||
(struct? (first the-form)) (= :symbol ((first the-form) :jolt/type)))
|
||||
(let [v (resolve-var ctx @{} (first the-form))]
|
||||
(if (and v (var-macro? v))
|
||||
(apply (var-get v) (tuple/slice the-form 1))
|
||||
the-form))
|
||||
the-form)))
|
||||
(ns-intern core "macroexpand-1" expand-1)
|
||||
# macroexpand: expand repeatedly until the head is no longer a macro (the
|
||||
# form's SUBFORMS are not expanded, matching Clojure).
|
||||
(ns-intern core "macroexpand"
|
||||
(fn [the-form]
|
||||
(if (and (array? the-form) (> (length the-form) 0)
|
||||
(struct? (first the-form)) (= :symbol ((first the-form) :jolt/type)))
|
||||
(let [v (resolve-var ctx @{} (first the-form))]
|
||||
(if (and v (var-macro? v))
|
||||
(apply (var-get v) (tuple/slice the-form 1))
|
||||
the-form))
|
||||
the-form)))
|
||||
(var cur the-form)
|
||||
(var nxt (expand-1 cur))
|
||||
(while (not= cur nxt) (set cur nxt) (set nxt (expand-1 cur)))
|
||||
cur))
|
||||
# alias/ns-unalias: alias bookkeeping is currently split (require :as writes
|
||||
# the string-keyed :imports table that resolution reads; :aliases is the
|
||||
# introspection table ns-aliases reads) — write/remove BOTH until unified.
|
||||
(ns-intern core "alias"
|
||||
(fn [alias-sym ns-sym]
|
||||
(def cur (ctx-find-ns ctx (ctx-current-ns ctx)))
|
||||
(ns-import cur (alias-sym :name) (ns-sym :name))
|
||||
(ns-add-alias cur alias-sym (ctx-find-ns ctx (ns-sym :name)))
|
||||
nil))
|
||||
(ns-intern core "ns-unalias"
|
||||
(fn [ns-d alias-sym]
|
||||
(def ns (ns-or-current ns-d))
|
||||
(put (ns :imports) (alias-sym :name) nil)
|
||||
(put (ns :aliases) alias-sym nil)
|
||||
nil))
|
||||
# ns-publics: {symbol -> var} (jolt has no private vars, so publics = interns).
|
||||
# Keys are symbol structs (value-hashed), matching Clojure's symbol keys.
|
||||
(ns-intern core "ns-publics"
|
||||
(fn [&opt ns-d]
|
||||
(def ns (ns-or-current ns-d))
|
||||
(var m (make-phm))
|
||||
(loop [[nm v] :pairs (ns :mappings)]
|
||||
(set m (phm-assoc m {:jolt/type :symbol :ns nil :name nm} v)))
|
||||
m))
|
||||
core)
|
||||
|
||||
# Dispatch a special form by its string name.
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@
|
|||
# Raised 4004 -> 4034 / clean 66 -> 67 porting partition-all + repeatedly to the
|
||||
# overlay, which required fixing two leniencies (a char is not callable; take
|
||||
# validates its count) — correct beyond those fns, so the suite rose broadly.
|
||||
(def baseline-pass 4081)
|
||||
(def baseline-pass 4324)
|
||||
# A file is "clean" when it ran with zero failures AND zero errors.
|
||||
(def baseline-clean-files 72)
|
||||
(def baseline-clean-files 78)
|
||||
# Per-file wall-clock budget (seconds). Normal files finish in well under 1s, so
|
||||
# this normally only fires on genuinely-infinite-sequence hangs. It's an env var
|
||||
# (JOLT_SUITE_TIMEOUT) so CI — whose runners are slower than a dev machine — can
|
||||
|
|
|
|||
|
|
@ -177,6 +177,22 @@
|
|||
["parse-uuid case =" "true" "(= (parse-uuid \"b6883c0a-0342-4007-9966-bc2dfa6b109e\") (parse-uuid \"B6883C0A-0342-4007-9966-BC2DFA6B109E\"))"]
|
||||
["parse-uuid bad nil" "nil" "(parse-uuid \"df0993\")"]
|
||||
["uuid as map key" ":v" "(get {(parse-uuid \"b6883c0a-0342-4007-9966-bc2dfa6b109e\") :v} (parse-uuid \"b6883c0a-0342-4007-9966-bc2dfa6b109e\"))"]
|
||||
|
||||
### ---- 1.11 additions + ns fns (spec 35-var batch A) ----
|
||||
["parse-long" "42" "(parse-long \"42\")"]
|
||||
["parse-long bad" "nil" "(parse-long \"4.2\")"]
|
||||
["parse-double" "1500.0" "(parse-double \"1.5e3\")"]
|
||||
["parse-boolean" "true" "(parse-boolean \"true\")"]
|
||||
["update-keys" "{\"a\" 1}" "(update-keys {:a 1} name)"]
|
||||
["update-vals" "{:a 2}" "(update-vals {:a 1} inc)"]
|
||||
["partitionv pad" "[[1 2] [3 :p]]" "(partitionv 2 2 [:p] [1 2 3])"]
|
||||
["partition pad" "[[0 1 2 3] [4 5 6 7] [8 9 :a]]" "(partition 4 4 [:a] (range 10))"]
|
||||
["splitv-at" "[[1 2] [3 4]]" "(splitv-at 2 [1 2 3 4])"]
|
||||
["with-redefs" "[42 1]" "(do (defn cwr [] 1) [(with-redefs [cwr (fn [] 42)] (cwr)) (cwr)])"]
|
||||
["time returns value" "3" "(time (+ 1 2))"]
|
||||
["macroexpand" "true" "(= (quote if) (first (macroexpand (quote (when-not false 1)))))"]
|
||||
["require bare symbol" "\"a,b\"" "(do (require (quote clojure.string)) (clojure.string/join \",\" [\"a\" \"b\"]))"]
|
||||
["ns-publics lookup" "true" "(do (def cnp 7) (some? (get (ns-publics (quote user)) (quote cnp))))"]
|
||||
["macroexpand-1 when" "2" "(count (rest (macroexpand-1 (quote (when true 1)))))"]
|
||||
|
||||
### ---- HIGH: aliased namespace calls ----
|
||||
|
|
|
|||
|
|
@ -72,3 +72,16 @@
|
|||
["binding rebinds" "99" "(do (def ^:dynamic *bx* 10) (binding [*bx* 99] *bx*))"]
|
||||
["binding restores" "10" "(do (def ^:dynamic *by* 10) (binding [*by* 99] *by*) *by*)"]
|
||||
["binding seen by fn" "7" "(do (def ^:dynamic *bz* 0) (defn rdz [] *bz*) (binding [*bz* 7] (rdz)))"])
|
||||
|
||||
# time returns the body value (the timing line goes to *out*); with-redefs
|
||||
# temporarily rebinds var roots and restores on exit (even on throw);
|
||||
# macroexpand expands repeatedly until the head is no longer a macro.
|
||||
(defspec "macros / time, with-redefs, macroexpand"
|
||||
["time returns value" "3" "(time (+ 1 2))"]
|
||||
["with-redefs rebinds" "42" "(do (defn wr-f [] 1) (with-redefs [wr-f (fn [] 42)] (wr-f)))"]
|
||||
["with-redefs restores" "1" "(do (defn wr-g [] 1) (with-redefs [wr-g (fn [] 42)]) (wr-g))"]
|
||||
["with-redefs restores on throw" "1"
|
||||
"(do (defn wr-h [] 1) (try (with-redefs [wr-h (fn [] 42)] (throw (ex-info \"x\" {}))) (catch :default e nil)) (wr-h))"]
|
||||
["with-redefs-fn" "42" "(do (defn wr-i [] 1) (with-redefs-fn {(var wr-i) (fn [] 42)} (fn [] (wr-i))))"]
|
||||
["macroexpand full" "true" "(let [e (macroexpand (quote (when-not false 1)))] (= (quote if) (first e)))"]
|
||||
["macroexpand non-macro" "[1 2]" "(macroexpand (quote [1 2]))"])
|
||||
|
|
|
|||
|
|
@ -144,3 +144,14 @@
|
|||
["dissoc keeps nil" "true" "(contains? (dissoc {:a nil :b 1} :b) :a)"]
|
||||
["reduce-kv sees nil" "true" "(= #{:a :b} (reduce-kv (fn [acc k v] (conj acc k)) #{} {:a nil :b 2}))"]
|
||||
["nil-free stays fast" "true" "(= {:a 1 :b 2} {:b 2 :a 1})"])
|
||||
|
||||
# Clojure 1.11 map transformers.
|
||||
(defspec "map / update-keys & update-vals (1.11)"
|
||||
["update-keys" "{\"a\" 1, \"b\" 2}" "(update-keys {:a 1 :b 2} name)"]
|
||||
["update-keys empty" "{}" "(update-keys {} inc)"]
|
||||
["update-keys nil" "{}" "(update-keys nil str)"]
|
||||
["update-keys collide last wins" "1" "(count (update-keys {:a 1 :b 2} (fn [_] :k)))"]
|
||||
["update-vals" "{:a 2, :b 3}" "(update-vals {:a 1 :b 2} inc)"]
|
||||
["update-vals empty" "{}" "(update-vals {} inc)"]
|
||||
["update-vals nil" "{}" "(update-vals nil inc)"]
|
||||
["update-vals keeps keys" "[:a :b]" "(sort (keys (update-vals {:a 1 :b 2} inc)))"])
|
||||
|
|
|
|||
|
|
@ -35,3 +35,10 @@
|
|||
["require clojure.walk" "{:a 2}" "(do (require '[clojure.walk :as w]) (w/postwalk (fn [x] (if (number? x) (inc x) x)) {:a 1}))"]
|
||||
["walk keywordize-keys" "{:a 1}" "(do (require '[clojure.walk :as w]) (w/keywordize-keys {\"a\" 1}))"]
|
||||
["walk stringify-keys" "true" "(do (require '[clojure.walk :as w]) (= {\"a\" 1} (w/stringify-keys {:a 1})))"])
|
||||
|
||||
(defspec "namespaces / alias, ns-unalias, ns-publics"
|
||||
["alias + use" "\"1,2\"" "(do (require (quote clojure.string)) (alias (quote st) (quote clojure.string)) (st/join \",\" [1 2]))"]
|
||||
["ns-unalias removes" "true"
|
||||
"(do (require (quote clojure.string)) (alias (quote st2) (quote clojure.string)) (ns-unalias (quote user) (quote st2)) (nil? (get (ns-aliases (quote user)) (quote st2))))"]
|
||||
["ns-publics has var" "true" "(do (def npv 1) (some? (get (ns-publics (quote user)) (quote npv))))"]
|
||||
["newline returns nil" "nil" "(newline)"])
|
||||
|
|
|
|||
|
|
@ -134,3 +134,26 @@
|
|||
["rand n in [0,n)" "true" "(let [r (rand 10)] (and (>= r 0) (< r 10)))"]
|
||||
["rand-nth member" "true" "(contains? #{:a :b :c} (rand-nth [:a :b :c]))"]
|
||||
["rand-nth single" ":x" "(rand-nth [:x])"])
|
||||
|
||||
# Clojure 1.11 string->scalar parsers: nil on malformed, throw on non-string.
|
||||
(defspec "numbers / parse fns (1.11)"
|
||||
["parse-long" "42" "(parse-long \"42\")"]
|
||||
["parse-long negative" "-7" "(parse-long \"-7\")"]
|
||||
["parse-long plus" "7" "(parse-long \"+7\")"]
|
||||
["parse-long float nil" "nil" "(parse-long \"1.5\")"]
|
||||
["parse-long hex nil" "nil" "(parse-long \"0x10\")"]
|
||||
["parse-long empty nil" "nil" "(parse-long \"\")"]
|
||||
["parse-long junk nil" "nil" "(parse-long \"12ab\")"]
|
||||
["parse-long throws" :throws "(parse-long 42)"]
|
||||
["parse-double" "1.5" "(parse-double \"1.5\")"]
|
||||
["parse-double int" "4.0" "(parse-double \"4\")"]
|
||||
["parse-double sci" "1500.0" "(parse-double \"1.5e3\")"]
|
||||
["parse-double neg" "-0.5" "(parse-double \"-0.5\")"]
|
||||
["parse-double junk" "nil" "(parse-double \"abc\")"]
|
||||
["parse-double trail" "nil" "(parse-double \"1.5x\")"]
|
||||
["parse-double throws" :throws "(parse-double :k)"]
|
||||
["parse-boolean true" "true" "(parse-boolean \"true\")"]
|
||||
["parse-boolean false" "false" "(parse-boolean \"false\")"]
|
||||
["parse-boolean case" "nil" "(parse-boolean \"True\")"]
|
||||
["parse-boolean junk" "nil" "(parse-boolean \"yes\")"]
|
||||
["parse-boolean throws" :throws "(parse-boolean true)"])
|
||||
|
|
|
|||
|
|
@ -261,3 +261,15 @@
|
|||
["dedupe consecutive" "[1 2 3 1]" "(dedupe [1 1 2 2 3 1 1])"]
|
||||
["dedupe empty" "[]" "(dedupe [])"]
|
||||
["dedupe no dups" "[1 2 3]" "(dedupe [1 2 3])"])
|
||||
|
||||
# Clojure 1.11 vector-returning partition/split variants.
|
||||
(defspec "seq / partitionv & splitv-at (1.11)"
|
||||
["partitionv" "[[1 2] [3 4]]" "(partitionv 2 [1 2 3 4 5])"]
|
||||
["partitionv elems are vectors" "true" "(every? vector? (partitionv 2 [1 2 3 4]))"]
|
||||
["partitionv step" "[[1 2] [3 4]]" "(partitionv 2 2 [1 2 3 4 5])"]
|
||||
["partitionv pad" "[[1 2] [3 :p]]" "(partitionv 2 2 [:p] [1 2 3])"]
|
||||
["partitionv-all" "[[1 2] [3]]" "(partitionv-all 2 [1 2 3])"]
|
||||
["partitionv-all vectors" "true" "(every? vector? (partitionv-all 2 [1 2 3]))"]
|
||||
["splitv-at" "[[1 2] [3 4]]" "(splitv-at 2 [1 2 3 4])"]
|
||||
["splitv-at first is vector" "true" "(vector? (first (splitv-at 2 [1 2 3])))"]
|
||||
["splitv-at past end" "[[1 2] []]" "(splitv-at 5 [1 2])"])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue