From d06b3fe636a3749afcdf0fc57fb96092c34bf2c5 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Wed, 10 Jun 2026 17:52:30 -0400 Subject: [PATCH] spec: rows for every untested var (131 -> 0); the probes found five real bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test/spec/untested-vars-spec.janet adds 143 rows asserting jolt's documented behavior for the whole implemented-untested category — primed arithmetic, the array/aset/coercion stubs, unchecked-*, the chunk family, JVM-shape stubs (class/bean/proxy/memfn as resolve-only or :throws), ns/REPL machinery, and the misc seqs. tools/spec_coverage.py now checks each var as a whole TOKEN in the test sources (call-position-only matching missed *1, +', ., .., /, and bare transducer refs like cat). Writing rows from probed truth surfaced five real bugs, all fixed: - comp with a jolt-IFn stage silently returned nil ((comp seq :content)) — raw Janet keyword application is not jolt invoke. comp is the canonical overlay defn now (fixed-arity composed fn, so the hot 1-arg path is two direct calls); the seed keeps a private td-comp only for the transducer machinery. hof bench +9% vs native, the price of correct IFn dispatch. - extend (the fn) was a nil-expanding stub MACRO shadowing any definition; it's a real fn over register-method now, and extends? (a constant-false stub) is real over extenders - (.. x f g) hit the 'ClassName.' constructor branch (a name ending in a dot) and died; .. is the canonical threading macro now - aclone errored on pvecs; ns-interns/ns-imports returned live host tables that count/seq reject (now structs) Thread/sleep + Thread/yield land as Thread statics beside Math/: sleep parks the WORKER's own event loop (each future thread has one), which makes timed deref provably fire — futures-spec gains the timeout-fires, sleep-in-body, and timed-out-future-still-completes rows. The futures impl itself already ran on real OS threads (ev/spawn-thread + marshalled results); jolt-ejx was stale. Dashboard: implemented+tested 433 -> 564 of 694; implemented-untested and missing-portable are both EMPTY. Gate: jpm exit 0, all tests passed. --- docs/spec/coverage.md | 268 +++++++++++++-------------- jolt-core/clojure/core/20-coll.clj | 17 ++ jolt-core/clojure/core/30-macros.clj | 39 +++- src/jolt/core.janet | 18 +- src/jolt/evaluator.janet | 26 ++- test/spec/futures-spec.janet | 8 +- test/spec/untested-vars-spec.janet | 177 ++++++++++++++++++ tools/spec_coverage.py | 12 +- 8 files changed, 414 insertions(+), 151 deletions(-) create mode 100644 test/spec/untested-vars-spec.janet diff --git a/docs/spec/coverage.md b/docs/spec/coverage.md index 7783481..93cedaa 100644 --- a/docs/spec/coverage.md +++ b/docs/spec/coverage.md @@ -7,9 +7,9 @@ community examples). jolt interns 564 of them. | Status | Count | Meaning | |---|---|---| -| implemented+tested | 433 | in jolt and exercised by spec/conformance | -| implemented-untested | 131 | in jolt, no direct test — spec entries will add them | -| resolvable-not-interned | 2 | works in code but invisible to ns introspection (conformance finding) | +| implemented+tested | 564 | in jolt and exercised by spec/conformance | +| implemented-untested | 0 | in jolt, no direct test — spec entries will add them | +| resolvable-not-interned | 1 | works in code but invisible to ns introspection (conformance finding) | | missing-portable | 0 | portable semantics, jolt lacks it — implementation gap | | special-form | 15 | specified in §3, not a library var | | dynamic-var | 29 | classification needed: portable default vs host-dependent | @@ -26,28 +26,28 @@ UNVERIFIED field; that column will be added as entries land. | Var | Status | ClojureDocs examples | |---|---|---| | `*` | implemented+tested | ✓ | -| `*'` | implemented-untested | ✓ | -| `*1` | implemented-untested | ✓ | -| `*2` | implemented-untested | ✓ | -| `*3` | implemented-untested | ✓ | +| `*'` | implemented+tested | ✓ | +| `*1` | implemented+tested | ✓ | +| `*2` | implemented+tested | ✓ | +| `*3` | implemented+tested | ✓ | | `*agent*` | dynamic-var | ✓ | | `*allow-unresolved-vars*` | dynamic-var | ✓ | | `*assert*` | dynamic-var | ✓ | -| `*clojure-version*` | implemented-untested | ✓ | +| `*clojure-version*` | implemented+tested | ✓ | | `*command-line-args*` | dynamic-var | ✓ | | `*compile-files*` | dynamic-var | ✓ | | `*compile-path*` | dynamic-var | ✓ | | `*compiler-options*` | dynamic-var | ✓ | | `*data-readers*` | dynamic-var | ✓ | | `*default-data-reader-fn*` | dynamic-var | ✓ | -| `*e` | implemented-untested | ✓ | +| `*e` | implemented+tested | ✓ | | `*err*` | dynamic-var | ✓ | | `*file*` | dynamic-var | ✓ | | `*flush-on-newline*` | dynamic-var | | | `*fn-loader*` | dynamic-var | | -| `*in*` | implemented-untested | | +| `*in*` | implemented+tested | | | `*math-context*` | dynamic-var | | -| `*ns*` | implemented-untested | ✓ | +| `*ns*` | implemented+tested | ✓ | | `*out*` | dynamic-var | ✓ | | `*print-dup*` | dynamic-var | ✓ | | `*print-length*` | dynamic-var | ✓ | @@ -60,26 +60,26 @@ UNVERIFIED field; that column will be added as entries land. | `*repl*` | dynamic-var | | | `*source-path*` | dynamic-var | ✓ | | `*suppress-read*` | dynamic-var | | -| `*unchecked-math*` | implemented-untested | ✓ | +| `*unchecked-math*` | implemented+tested | ✓ | | `*use-context-classloader*` | dynamic-var | ✓ | | `*verbose-defrecords*` | dynamic-var | | | `*warn-on-reflection*` | dynamic-var | ✓ | | `+` | implemented+tested | ✓ | -| `+'` | implemented-untested | ✓ | +| `+'` | implemented+tested | ✓ | | `-` | implemented+tested | ✓ | -| `-'` | implemented-untested | ✓ | +| `-'` | implemented+tested | ✓ | | `->` | implemented+tested | ✓ | | `->>` | implemented+tested | ✓ | | `->ArrayChunk` | jvm-specific | | -| `->Eduction` | implemented-untested | | +| `->Eduction` | implemented+tested | | | `->Vec` | jvm-specific | | | `->VecNode` | jvm-specific | | | `->VecSeq` | jvm-specific | | | `-cache-protocol-fn` | jvm-specific | | | `-reset-methods` | jvm-specific | | -| `.` | implemented-untested | ✓ | -| `..` | implemented-untested | ✓ | -| `/` | implemented-untested | ✓ | +| `.` | implemented+tested | ✓ | +| `..` | implemented+tested | ✓ | +| `/` | implemented+tested | ✓ | | `<` | implemented+tested | ✓ | | `<=` | implemented+tested | ✓ | | `=` | implemented+tested | ✓ | @@ -94,7 +94,7 @@ UNVERIFIED field; that column will be added as entries land. | `Throwable->map` | jvm-specific | ✓ | | `abs` | implemented+tested | ✓ | | `accessor` | jvm-specific | ✓ | -| `aclone` | implemented-untested | ✓ | +| `aclone` | implemented+tested | ✓ | | `add-classpath` | jvm-specific | ✓ | | `add-tap` | agents-taps | ✓ | | `add-watch` | implemented+tested | ✓ | @@ -104,7 +104,7 @@ UNVERIFIED field; that column will be added as entries land. | `aget` | implemented+tested | ✓ | | `alength` | implemented+tested | ✓ | | `alias` | implemented+tested | ✓ | -| `all-ns` | implemented-untested | ✓ | +| `all-ns` | implemented+tested | ✓ | | `alter` | stm-refs | ✓ | | `alter-meta!` | implemented+tested | ✓ | | `alter-var-root` | implemented+tested | ✓ | @@ -117,14 +117,14 @@ UNVERIFIED field; that column will be added as entries land. | `array-map` | implemented+tested | ✓ | | `as->` | implemented+tested | ✓ | | `aset` | implemented+tested | ✓ | -| `aset-boolean` | implemented-untested | ✓ | -| `aset-byte` | implemented-untested | ✓ | -| `aset-char` | implemented-untested | ✓ | -| `aset-double` | implemented-untested | ✓ | -| `aset-float` | implemented-untested | ✓ | -| `aset-int` | implemented-untested | ✓ | -| `aset-long` | implemented-untested | ✓ | -| `aset-short` | implemented-untested | ✓ | +| `aset-boolean` | implemented+tested | ✓ | +| `aset-byte` | implemented+tested | ✓ | +| `aset-char` | implemented+tested | ✓ | +| `aset-double` | implemented+tested | ✓ | +| `aset-float` | implemented+tested | ✓ | +| `aset-int` | implemented+tested | ✓ | +| `aset-long` | implemented+tested | ✓ | +| `aset-short` | implemented+tested | ✓ | | `assert` | implemented+tested | ✓ | | `assoc` | implemented+tested | ✓ | | `assoc!` | implemented+tested | ✓ | @@ -135,16 +135,16 @@ UNVERIFIED field; that column will be added as entries land. | `await-for` | agents-taps | ✓ | | `await1` | agents-taps | | | `bases` | jvm-specific | ✓ | -| `bean` | implemented-untested | ✓ | +| `bean` | implemented+tested | ✓ | | `bigdec` | implemented+tested | ✓ | | `bigint` | implemented+tested | ✓ | -| `biginteger` | implemented-untested | ✓ | +| `biginteger` | implemented+tested | ✓ | | `binding` | implemented+tested | ✓ | | `bit-and` | implemented+tested | ✓ | -| `bit-and-not` | implemented-untested | ✓ | +| `bit-and-not` | implemented+tested | ✓ | | `bit-clear` | implemented+tested | ✓ | -| `bit-flip` | implemented-untested | ✓ | -| `bit-not` | implemented-untested | ✓ | +| `bit-flip` | implemented+tested | ✓ | +| `bit-not` | implemented+tested | ✓ | | `bit-or` | implemented+tested | ✓ | | `bit-set` | implemented+tested | ✓ | | `bit-shift-left` | implemented+tested | ✓ | @@ -152,37 +152,37 @@ UNVERIFIED field; that column will be added as entries land. | `bit-test` | implemented+tested | ✓ | | `bit-xor` | implemented+tested | ✓ | | `boolean` | implemented+tested | ✓ | -| `boolean-array` | implemented-untested | ✓ | +| `boolean-array` | implemented+tested | ✓ | | `boolean?` | implemented+tested | ✓ | -| `booleans` | implemented-untested | ✓ | -| `bound-fn` | implemented-untested | ✓ | +| `booleans` | implemented+tested | ✓ | +| `bound-fn` | implemented+tested | ✓ | | `bound-fn*` | implemented+tested | ✓ | | `bound?` | implemented+tested | ✓ | | `bounded-count` | implemented+tested | ✓ | | `butlast` | implemented+tested | ✓ | -| `byte` | implemented-untested | ✓ | -| `byte-array` | implemented-untested | ✓ | -| `bytes` | implemented-untested | ✓ | -| `bytes?` | implemented-untested | ✓ | +| `byte` | implemented+tested | ✓ | +| `byte-array` | implemented+tested | ✓ | +| `bytes` | implemented+tested | ✓ | +| `bytes?` | implemented+tested | ✓ | | `case` | implemented+tested | ✓ | | `cast` | jvm-specific | ✓ | -| `cat` | implemented-untested | ✓ | +| `cat` | implemented+tested | ✓ | | `catch` | special-form | ✓ | | `char` | implemented+tested | ✓ | -| `char-array` | implemented-untested | ✓ | +| `char-array` | implemented+tested | ✓ | | `char-escape-string` | implemented+tested | ✓ | | `char-name-string` | implemented+tested | ✓ | | `char?` | implemented+tested | ✓ | -| `chars` | implemented-untested | ✓ | -| `chunk` | implemented-untested | ✓ | -| `chunk-append` | implemented-untested | ✓ | -| `chunk-buffer` | implemented-untested | ✓ | -| `chunk-cons` | implemented-untested | ✓ | -| `chunk-first` | implemented-untested | ✓ | -| `chunk-next` | implemented-untested | ✓ | -| `chunk-rest` | implemented-untested | ✓ | +| `chars` | implemented+tested | ✓ | +| `chunk` | implemented+tested | ✓ | +| `chunk-append` | implemented+tested | ✓ | +| `chunk-buffer` | implemented+tested | ✓ | +| `chunk-cons` | implemented+tested | ✓ | +| `chunk-first` | implemented+tested | ✓ | +| `chunk-next` | implemented+tested | ✓ | +| `chunk-rest` | implemented+tested | ✓ | | `chunked-seq?` | implemented+tested | ✓ | -| `class` | implemented-untested | ✓ | +| `class` | implemented+tested | ✓ | | `class?` | jvm-specific | ✓ | | `clear-agent-errors` | agents-taps | | | `clojure-version` | implemented+tested | ✓ | @@ -205,7 +205,7 @@ UNVERIFIED field; that column will be added as entries land. | `conj!` | implemented+tested | ✓ | | `cons` | implemented+tested | ✓ | | `constantly` | implemented+tested | ✓ | -| `construct-proxy` | implemented-untested | ✓ | +| `construct-proxy` | implemented+tested | ✓ | | `contains?` | implemented+tested | ✓ | | `count` | implemented+tested | ✓ | | `counted?` | implemented+tested | ✓ | @@ -213,14 +213,14 @@ UNVERIFIED field; that column will be added as entries land. | `create-struct` | jvm-specific | ✓ | | `cycle` | implemented+tested | ✓ | | `dec` | implemented+tested | ✓ | -| `dec'` | implemented-untested | ✓ | +| `dec'` | implemented+tested | ✓ | | `decimal?` | implemented+tested | ✓ | | `declare` | implemented+tested | ✓ | | `dedupe` | implemented+tested | ✓ | | `def` | special-form | ✓ | | `default-data-readers` | jvm-specific | ✓ | | `definline` | jvm-specific | | -| `definterface` | implemented-untested | ✓ | +| `definterface` | implemented+tested | ✓ | | `defmacro` | special-form | ✓ | | `defmethod` | implemented+tested | ✓ | | `defmulti` | implemented+tested | ✓ | @@ -232,13 +232,13 @@ UNVERIFIED field; that column will be added as entries land. | `defstruct` | jvm-specific | ✓ | | `deftype` | implemented+tested | ✓ | | `delay` | implemented+tested | ✓ | -| `delay?` | implemented-untested | ✓ | +| `delay?` | implemented+tested | ✓ | | `deliver` | implemented+tested | ✓ | | `denominator` | implemented+tested | ✓ | | `deref` | implemented+tested | ✓ | | `derive` | implemented+tested | ✓ | | `descendants` | implemented+tested | ✓ | -| `destructure` | implemented-untested | ✓ | +| `destructure` | implemented+tested | ✓ | | `disj` | implemented+tested | ✓ | | `disj!` | implemented+tested | ✓ | | `dissoc` | implemented+tested | ✓ | @@ -252,10 +252,10 @@ UNVERIFIED field; that column will be added as entries land. | `dosync` | stm-refs | ✓ | | `dotimes` | implemented+tested | ✓ | | `doto` | implemented+tested | ✓ | -| `double` | implemented-untested | ✓ | -| `double-array` | implemented-untested | ✓ | +| `double` | implemented+tested | ✓ | +| `double-array` | implemented+tested | ✓ | | `double?` | implemented+tested | ✓ | -| `doubles` | implemented-untested | ✓ | +| `doubles` | implemented+tested | ✓ | | `drop` | implemented+tested | ✓ | | `drop-last` | implemented+tested | ✓ | | `drop-while` | implemented+tested | ✓ | @@ -263,8 +263,8 @@ UNVERIFIED field; that column will be added as entries land. | `empty` | implemented+tested | ✓ | | `empty?` | implemented+tested | ✓ | | `ensure` | stm-refs | ✓ | -| `ensure-reduced` | implemented-untested | ✓ | -| `enumeration-seq` | implemented-untested | ✓ | +| `ensure-reduced` | implemented+tested | ✓ | +| `enumeration-seq` | implemented+tested | ✓ | | `error-handler` | agents-taps | ✓ | | `error-mode` | agents-taps | ✓ | | `eval` | implemented+tested | ✓ | @@ -275,11 +275,11 @@ UNVERIFIED field; that column will be added as entries land. | `ex-data` | implemented+tested | ✓ | | `ex-info` | implemented+tested | ✓ | | `ex-message` | implemented+tested | ✓ | -| `extend` | implemented-untested | ✓ | +| `extend` | implemented+tested | ✓ | | `extend-protocol` | implemented+tested | ✓ | | `extend-type` | implemented+tested | ✓ | | `extenders` | implemented+tested | ✓ | -| `extends?` | implemented-untested | ✓ | +| `extends?` | implemented+tested | ✓ | | `false?` | implemented+tested | ✓ | | `ffirst` | implemented+tested | ✓ | | `file-seq` | implemented+tested | ✓ | @@ -294,10 +294,10 @@ UNVERIFIED field; that column will be added as entries land. | `find-var` | implemented+tested | ✓ | | `first` | implemented+tested | ✓ | | `flatten` | implemented+tested | ✓ | -| `float` | implemented-untested | ✓ | -| `float-array` | implemented-untested | ✓ | +| `float` | implemented+tested | ✓ | +| `float-array` | implemented+tested | ✓ | | `float?` | implemented+tested | ✓ | -| `floats` | implemented-untested | ✓ | +| `floats` | implemented+tested | ✓ | | `flush` | implemented+tested | ✓ | | `fn` | implemented+tested | ✓ | | `fn?` | implemented+tested | ✓ | @@ -308,7 +308,7 @@ UNVERIFIED field; that column will be added as entries land. | `format` | implemented+tested | ✓ | | `frequencies` | implemented+tested | ✓ | | `future` | implemented+tested | ✓ | -| `future-call` | implemented-untested | ✓ | +| `future-call` | implemented+tested | ✓ | | `future-cancel` | implemented+tested | ✓ | | `future-cancelled?` | implemented+tested | ✓ | | `future-done?` | implemented+tested | ✓ | @@ -319,17 +319,17 @@ UNVERIFIED field; that column will be added as entries land. | `get` | implemented+tested | ✓ | | `get-in` | implemented+tested | ✓ | | `get-method` | implemented+tested | ✓ | -| `get-proxy-class` | implemented-untested | ✓ | +| `get-proxy-class` | implemented+tested | ✓ | | `get-thread-bindings` | implemented+tested | ✓ | | `get-validator` | implemented+tested | ✓ | | `group-by` | implemented+tested | ✓ | -| `halt-when` | implemented-untested | ✓ | -| `hash` | implemented-untested | ✓ | -| `hash-combine` | implemented-untested | ✓ | +| `halt-when` | implemented+tested | ✓ | +| `hash` | implemented+tested | ✓ | +| `hash-combine` | implemented+tested | ✓ | | `hash-map` | implemented+tested | ✓ | -| `hash-ordered-coll` | implemented-untested | ✓ | +| `hash-ordered-coll` | implemented+tested | ✓ | | `hash-set` | implemented+tested | ✓ | -| `hash-unordered-coll` | implemented-untested | ✓ | +| `hash-unordered-coll` | implemented+tested | ✓ | | `ident?` | implemented+tested | ✓ | | `identical?` | implemented+tested | ✓ | | `identity` | implemented+tested | ✓ | @@ -341,29 +341,29 @@ UNVERIFIED field; that column will be added as entries land. | `import` | implemented+tested | ✓ | | `in-ns` | implemented+tested | ✓ | | `inc` | implemented+tested | ✓ | -| `inc'` | implemented-untested | ✓ | +| `inc'` | implemented+tested | ✓ | | `indexed?` | implemented+tested | ✓ | | `infinite?` | implemented+tested | ✓ | -| `init-proxy` | implemented-untested | ✓ | +| `init-proxy` | implemented+tested | ✓ | | `inst-ms` | implemented+tested | ✓ | | `inst-ms*` | implemented+tested | | | `inst?` | implemented+tested | ✓ | | `instance?` | implemented+tested | ✓ | | `int` | implemented+tested | ✓ | -| `int-array` | implemented-untested | ✓ | +| `int-array` | implemented+tested | ✓ | | `int?` | implemented+tested | ✓ | | `integer?` | implemented+tested | ✓ | | `interleave` | implemented+tested | ✓ | | `intern` | implemented+tested | ✓ | | `interpose` | implemented+tested | ✓ | | `into` | implemented+tested | ✓ | -| `into-array` | implemented-untested | ✓ | -| `ints` | implemented-untested | ✓ | +| `into-array` | implemented+tested | ✓ | +| `ints` | implemented+tested | ✓ | | `io!` | stm-refs | ✓ | | `isa?` | implemented+tested | ✓ | | `iterate` | implemented+tested | ✓ | | `iteration` | jvm-specific | ✓ | -| `iterator-seq` | implemented-untested | ✓ | +| `iterator-seq` | implemented+tested | ✓ | | `juxt` | implemented+tested | ✓ | | `keep` | implemented+tested | ✓ | | `keep-indexed` | implemented+tested | ✓ | @@ -386,13 +386,13 @@ UNVERIFIED field; that column will be added as entries land. | `load-string` | implemented+tested | ✓ | | `loaded-libs` | jvm-specific | ✓ | | `locking` | implemented+tested | ✓ | -| `long` | implemented-untested | ✓ | -| `long-array` | implemented-untested | ✓ | -| `longs` | implemented-untested | ✓ | +| `long` | implemented+tested | ✓ | +| `long-array` | implemented+tested | ✓ | +| `longs` | implemented+tested | ✓ | | `loop` | implemented+tested | ✓ | | `macroexpand` | implemented+tested | ✓ | | `macroexpand-1` | implemented+tested | ✓ | -| `make-array` | implemented-untested | ✓ | +| `make-array` | implemented+tested | ✓ | | `make-hierarchy` | implemented+tested | ✓ | | `map` | implemented+tested | ✓ | | `map-entry?` | implemented+tested | ✓ | @@ -402,7 +402,7 @@ UNVERIFIED field; that column will be added as entries land. | `mapv` | implemented+tested | ✓ | | `max` | implemented+tested | ✓ | | `max-key` | implemented+tested | ✓ | -| `memfn` | implemented-untested | ✓ | +| `memfn` | implemented+tested | ✓ | | `memoize` | implemented+tested | ✓ | | `merge` | implemented+tested | ✓ | | `merge-with` | implemented+tested | ✓ | @@ -425,7 +425,7 @@ UNVERIFIED field; that column will be added as entries land. | `new` | special-form | ✓ | | `newline` | implemented+tested | ✓ | | `next` | implemented+tested | ✓ | -| `nfirst` | implemented-untested | ✓ | +| `nfirst` | implemented+tested | ✓ | | `nil?` | implemented+tested | ✓ | | `nnext` | implemented+tested | ✓ | | `not` | implemented+tested | ✓ | @@ -435,8 +435,8 @@ UNVERIFIED field; that column will be added as entries land. | `not=` | implemented+tested | ✓ | | `ns` | implemented+tested | ✓ | | `ns-aliases` | implemented+tested | ✓ | -| `ns-imports` | implemented-untested | ✓ | -| `ns-interns` | implemented-untested | ✓ | +| `ns-imports` | implemented+tested | ✓ | +| `ns-interns` | implemented+tested | ✓ | | `ns-map` | implemented+tested | ✓ | | `ns-name` | implemented+tested | ✓ | | `ns-publics` | implemented+tested | ✓ | @@ -470,18 +470,18 @@ UNVERIFIED field; that column will be added as entries land. | `pmap` | jvm-specific | ✓ | | `pop` | implemented+tested | ✓ | | `pop!` | implemented+tested | ✓ | -| `pop-thread-bindings` | implemented-untested | | +| `pop-thread-bindings` | implemented+tested | | | `pos-int?` | implemented+tested | ✓ | | `pos?` | implemented+tested | ✓ | | `pr` | implemented+tested | ✓ | | `pr-str` | implemented+tested | ✓ | | `prefer-method` | implemented+tested | ✓ | -| `prefers` | implemented-untested | ✓ | +| `prefers` | implemented+tested | ✓ | | `primitives-classnames` | jvm-specific | ✓ | | `print` | implemented+tested | ✓ | | `print-ctor` | jvm-specific | ✓ | -| `print-dup` | implemented-untested | ✓ | -| `print-method` | implemented-untested | ✓ | +| `print-dup` | implemented+tested | ✓ | +| `print-method` | implemented+tested | ✓ | | `print-simple` | jvm-specific | ✓ | | `print-str` | implemented+tested | ✓ | | `printf` | implemented+tested | ✓ | @@ -490,12 +490,12 @@ UNVERIFIED field; that column will be added as entries land. | `prn` | implemented+tested | ✓ | | `prn-str` | implemented+tested | ✓ | | `promise` | implemented+tested | ✓ | -| `proxy` | implemented-untested | ✓ | -| `proxy-call-with-super` | implemented-untested | | -| `proxy-mappings` | implemented-untested | ✓ | +| `proxy` | implemented+tested | ✓ | +| `proxy-call-with-super` | implemented+tested | | +| `proxy-mappings` | implemented+tested | ✓ | | `proxy-name` | jvm-specific | | -| `proxy-super` | implemented-untested | ✓ | -| `push-thread-bindings` | implemented-untested | | +| `proxy-super` | implemented+tested | ✓ | +| `push-thread-bindings` | implemented+tested | | | `pvalues` | jvm-specific | ✓ | | `qualified-ident?` | implemented+tested | ✓ | | `qualified-keyword?` | implemented+tested | ✓ | @@ -505,15 +505,15 @@ UNVERIFIED field; that column will be added as entries land. | `rand` | implemented+tested | ✓ | | `rand-int` | implemented+tested | ✓ | | `rand-nth` | implemented+tested | ✓ | -| `random-sample` | implemented-untested | ✓ | +| `random-sample` | implemented+tested | ✓ | | `random-uuid` | implemented+tested | ✓ | | `range` | implemented+tested | ✓ | | `ratio?` | implemented+tested | ✓ | | `rational?` | implemented+tested | ✓ | | `rationalize` | implemented+tested | ✓ | | `re-find` | implemented+tested | ✓ | -| `re-groups` | implemented-untested | ✓ | -| `re-matcher` | implemented-untested | ✓ | +| `re-groups` | implemented+tested | ✓ | +| `re-matcher` | implemented+tested | ✓ | | `re-matches` | implemented+tested | ✓ | | `re-pattern` | implemented+tested | ✓ | | `re-seq` | implemented+tested | ✓ | @@ -521,7 +521,7 @@ UNVERIFIED field; that column will be added as entries land. | `read+string` | implemented+tested | ✓ | | `read-line` | implemented+tested | ✓ | | `read-string` | implemented+tested | ✓ | -| `reader-conditional` | implemented-untested | ✓ | +| `reader-conditional` | implemented+tested | ✓ | | `reader-conditional?` | implemented+tested | ✓ | | `realized?` | implemented+tested | ✓ | | `record?` | implemented+tested | ✓ | @@ -537,7 +537,7 @@ UNVERIFIED field; that column will be added as entries land. | `ref-min-history` | stm-refs | ✓ | | `ref-set` | stm-refs | ✓ | | `refer` | implemented+tested | ✓ | -| `refer-clojure` | implemented-untested | ✓ | +| `refer-clojure` | implemented+tested | ✓ | | `reify` | implemented+tested | ✓ | | `release-pending-sends` | agents-taps | ✓ | | `rem` | implemented+tested | ✓ | @@ -554,7 +554,7 @@ UNVERIFIED field; that column will be added as entries land. | `require` | implemented+tested | ✓ | | `requiring-resolve` | jvm-specific | ✓ | | `reset!` | implemented+tested | ✓ | -| `reset-meta!` | implemented-untested | ✓ | +| `reset-meta!` | implemented+tested | ✓ | | `reset-vals!` | implemented+tested | ✓ | | `resolve` | implemented+tested | ✓ | | `rest` | implemented+tested | ✓ | @@ -572,10 +572,10 @@ UNVERIFIED field; that column will be added as entries land. | `send-off` | agents-taps | ✓ | | `send-via` | agents-taps | ✓ | | `seq` | implemented+tested | ✓ | -| `seq-to-map-for-destructuring` | implemented-untested | ✓ | +| `seq-to-map-for-destructuring` | implemented+tested | ✓ | | `seq?` | implemented+tested | ✓ | | `seqable?` | implemented+tested | ✓ | -| `seque` | implemented-untested | ✓ | +| `seque` | implemented+tested | ✓ | | `sequence` | implemented+tested | ✓ | | `sequential?` | implemented+tested | ✓ | | `set` | implemented+tested | ✓ | @@ -586,9 +586,9 @@ UNVERIFIED field; that column will be added as entries land. | `set-error-mode!` | agents-taps | ✓ | | `set-validator!` | implemented+tested | ✓ | | `set?` | implemented+tested | ✓ | -| `short` | implemented-untested | ✓ | -| `short-array` | implemented-untested | ✓ | -| `shorts` | implemented-untested | ✓ | +| `short` | implemented+tested | ✓ | +| `short-array` | implemented+tested | ✓ | +| `shorts` | implemented+tested | ✓ | | `shuffle` | implemented+tested | ✓ | | `shutdown-agents` | agents-taps | ✓ | | `simple-ident?` | implemented+tested | ✓ | @@ -607,7 +607,7 @@ UNVERIFIED field; that column will be added as entries land. | `sorted-set` | implemented+tested | ✓ | | `sorted-set-by` | implemented+tested | ✓ | | `sorted?` | implemented+tested | ✓ | -| `special-symbol?` | implemented-untested | ✓ | +| `special-symbol?` | implemented+tested | ✓ | | `spit` | implemented+tested | ✓ | | `split-at` | implemented+tested | ✓ | | `split-with` | implemented+tested | ✓ | @@ -641,7 +641,7 @@ UNVERIFIED field; that column will be added as entries land. | `thread-bound?` | implemented+tested | ✓ | | `throw` | special-form | ✓ | | `time` | implemented+tested | ✓ | -| `to-array` | implemented-untested | ✓ | +| `to-array` | implemented+tested | ✓ | | `to-array-2d` | implemented+tested | ✓ | | `trampoline` | implemented+tested | ✓ | | `transduce` | implemented+tested | ✓ | @@ -650,38 +650,38 @@ UNVERIFIED field; that column will be added as entries land. | `true?` | implemented+tested | ✓ | | `try` | special-form | ✓ | | `type` | implemented+tested | ✓ | -| `unchecked-add` | implemented-untested | ✓ | -| `unchecked-add-int` | implemented-untested | ✓ | -| `unchecked-byte` | implemented-untested | ✓ | -| `unchecked-char` | implemented-untested | | -| `unchecked-dec` | implemented-untested | ✓ | -| `unchecked-dec-int` | implemented-untested | ✓ | -| `unchecked-divide-int` | implemented-untested | ✓ | -| `unchecked-double` | implemented-untested | ✓ | -| `unchecked-float` | implemented-untested | ✓ | -| `unchecked-inc` | implemented-untested | ✓ | -| `unchecked-inc-int` | implemented-untested | ✓ | -| `unchecked-int` | implemented-untested | ✓ | -| `unchecked-long` | implemented-untested | ✓ | -| `unchecked-multiply` | implemented-untested | ✓ | -| `unchecked-multiply-int` | implemented-untested | | -| `unchecked-negate` | implemented-untested | ✓ | -| `unchecked-negate-int` | implemented-untested | ✓ | -| `unchecked-remainder-int` | implemented-untested | | -| `unchecked-short` | implemented-untested | ✓ | -| `unchecked-subtract` | implemented-untested | ✓ | -| `unchecked-subtract-int` | implemented-untested | ✓ | +| `unchecked-add` | implemented+tested | ✓ | +| `unchecked-add-int` | implemented+tested | ✓ | +| `unchecked-byte` | implemented+tested | ✓ | +| `unchecked-char` | implemented+tested | | +| `unchecked-dec` | implemented+tested | ✓ | +| `unchecked-dec-int` | implemented+tested | ✓ | +| `unchecked-divide-int` | implemented+tested | ✓ | +| `unchecked-double` | implemented+tested | ✓ | +| `unchecked-float` | implemented+tested | ✓ | +| `unchecked-inc` | implemented+tested | ✓ | +| `unchecked-inc-int` | implemented+tested | ✓ | +| `unchecked-int` | implemented+tested | ✓ | +| `unchecked-long` | implemented+tested | ✓ | +| `unchecked-multiply` | implemented+tested | ✓ | +| `unchecked-multiply-int` | implemented+tested | | +| `unchecked-negate` | implemented+tested | ✓ | +| `unchecked-negate-int` | implemented+tested | ✓ | +| `unchecked-remainder-int` | implemented+tested | | +| `unchecked-short` | implemented+tested | ✓ | +| `unchecked-subtract` | implemented+tested | ✓ | +| `unchecked-subtract-int` | implemented+tested | ✓ | | `underive` | implemented+tested | ✓ | | `unquote` | jvm-specific | ✓ | | `unquote-splicing` | jvm-specific | ✓ | | `unreduced` | implemented+tested | ✓ | -| `unsigned-bit-shift-right` | implemented-untested | ✓ | +| `unsigned-bit-shift-right` | implemented+tested | ✓ | | `update` | implemented+tested | ✓ | | `update-in` | implemented+tested | ✓ | | `update-keys` | implemented+tested | ✓ | -| `update-proxy` | implemented-untested | ✓ | +| `update-proxy` | implemented+tested | ✓ | | `update-vals` | implemented+tested | ✓ | -| `uri?` | implemented-untested | ✓ | +| `uri?` | implemented+tested | ✓ | | `use` | implemented+tested | ✓ | | `uuid?` | implemented+tested | ✓ | | `val` | implemented+tested | ✓ | @@ -716,6 +716,6 @@ UNVERIFIED field; that column will be added as entries land. | `with-precision` | implemented+tested | ✓ | | `with-redefs` | implemented+tested | ✓ | | `with-redefs-fn` | implemented+tested | ✓ | -| `xml-seq` | implemented-untested | ✓ | +| `xml-seq` | implemented+tested | ✓ | | `zero?` | implemented+tested | ✓ | | `zipmap` | implemented+tested | ✓ | diff --git a/jolt-core/clojure/core/20-coll.clj b/jolt-core/clojure/core/20-coll.clj index ef03133..d92bfc4 100644 --- a/jolt-core/clojure/core/20-coll.clj +++ b/jolt-core/clojure/core/20-coll.clj @@ -821,3 +821,20 @@ ;; The raw Inst protocol method; jolt insts have one representation, so it is ;; inst-ms itself. (defn inst-ms* [i] (inst-ms i)) + +;; Canonical comp — here rather than the seed so each stage is invoked with +;; jolt call semantics: (comp seq :content) works because the keyword stage +;; goes through IFn dispatch (raw Janet keyword application does not). +(defn comp + ([] identity) + ([f] f) + ([f g] + ;; fixed arities first (Clojure's own shape): the 1-arg path — every + ;; map/filter stage — is two direct calls, no rest-seq, no apply. + (fn + ([] (f (g))) + ([x] (f (g x))) + ([x y] (f (g x y))) + ([x y z] (f (g x y z))) + ([x y z & args] (f (apply g x y z args))))) + ([f g & fs] (reduce comp (comp f g) fs))) diff --git a/jolt-core/clojure/core/30-macros.clj b/jolt-core/clojure/core/30-macros.clj index 9d4364e..9c20ba1 100644 --- a/jolt-core/clojure/core/30-macros.clj +++ b/jolt-core/clojure/core/30-macros.clj @@ -295,6 +295,43 @@ (protocol-dispatch ~(name pname) ~(name (first sig)) this# rest#)))) sigs)))) +;; Member threading: (.. x f g) => (. (. x f) g); a parenthesized member +;; carries args. Canonical Clojure shape, single-arity defmacro. +(defmacro .. [x form & more] + (let [step (if (seq? form) + `(. ~x ~(first form) ~@(rest form)) + `(. ~x ~form))] + (if (seq more) + `(.. ~step ~@more) + step))) + +;; True when atype's methods were registered for this protocol (via extend / +;; extend-type). Tags are canonical host names or ns-qualified record names, +;; so a bare record name also matches its "ns.Name" tag. +(defn extends? [protocol atype] + (let [want (name atype) + dotted (str "." want) + dlen (count dotted)] + (boolean (some (fn [t] + (let [tn (name t)] + (or (= tn want) + (and (> (count tn) dlen) + (= (subs tn (- (count tn) dlen)) dotted))))) + (extenders protocol))))) + +;; extend, the FUNCTION (extend-type's runtime sibling): protocol + method-map +;; pairs, methods registered under the type's (canonicalized) name — so +;; (extend 'String P {:m (fn [x] ...)}) dispatches exactly like extend-type. +(defn extend [atype & proto+mmaps] + (loop [s (seq proto+mmaps)] + (when s + (let [proto (first s) + mmap (second s) + pname (name (get proto :name))] + (doseq [[k f] mmap] + (register-method (name atype) pname (name k) f))) + (recur (nnext s))))) + (defmacro extend-type [tsym psym & impls] ;; register-method is a fn (clojure.core); pass type/protocol/method NAMES as ;; strings (not the symbols) so the call compiles as a plain invoke. @@ -308,7 +345,7 @@ (group-by-head type-impls)))) ;; extend (the fn form) is not supported — stub to nil, as before. -(defmacro extend [& args] nil) +;; extend is a real FUNCTION now — defined above extend-type. ;; JVM proxies are unsupported. (defmacro proxy [& args] nil) ;; definterface is JVM-only; bind the name to an empty marker. diff --git a/src/jolt/core.janet b/src/jolt/core.janet index 50aac6b..cbd135b 100644 --- a/src/jolt/core.janet +++ b/src/jolt/core.janet @@ -1402,7 +1402,12 @@ # every-pred now lives in the Clojure collection tier (core/20-coll.clj). -(def core-comp +# Public comp lives in the overlay now (20-coll) — its stages can be any jolt +# IFn (keyword/map/set/vector), which raw Janet calls mishandle ((comp seq +# :content) returned nil: janet keyword-apply is not jolt invoke). This +# private composer remains ONLY for the transducer machinery below, where the +# stages are always real fns. +(def- td-comp (fn [& fs] (case (length fs) 0 identity @@ -1852,7 +1857,10 @@ # below stay native (they build the mutable backing). (defn core-aclone [arr] - (if (buffer? arr) (buffer/slice arr) (array/slice arr))) + (cond + (buffer? arr) (buffer/slice arr) + (pvec? arr) (array ;(pv->array arr)) + (array/slice arr))) # Numeric / object arrays: (T-array size) | (T-array size init) | (T-array seq) (defn- make-num-array [a rest init] @@ -2284,7 +2292,7 @@ (def core-satisfies? (fn [proto-sym obj] false)) -(def core-extends? (fn [& args] false)) +# extends? is a real overlay fn now (30-macros, over extenders). (def core-implements? (fn [& args] false)) (def core-type->str (fn [& args] "")) @@ -2419,7 +2427,7 @@ (let [n (length args) coll (in args (- n 1)) xforms (array/slice args 0 (- n 1)) - xform (if (= 0 (length xforms)) (fn [rf] rf) (apply core-comp xforms))] + xform (if (= 0 (length xforms)) (fn [rf] rf) (apply td-comp xforms))] (core-into (make-vec @[]) xform coll))) (defn core->Eduction [xform coll] (core-into (make-vec @[]) xform coll)) (defn core-proxy-super [& args] (error "proxy-super: JVM proxies are not supported in Jolt")) @@ -2800,7 +2808,6 @@ "range" core-range "identity" core-identity "constantly" core-constantly - "comp" core-comp "vector" core-vector "hash-map" core-hash-map "array-map" core-array-map @@ -2948,7 +2955,6 @@ "Object" core-Object "make-protocol" core-make-protocol "satisfies?" core-satisfies? - "extends?" core-extends? "implements?" core-implements? "type->str" core-type->str "volatile!" core-volatile! diff --git a/src/jolt/evaluator.janet b/src/jolt/evaluator.janet index 8f7c43b..db0150e 100644 --- a/src/jolt/evaluator.janet +++ b/src/jolt/evaluator.janet @@ -417,12 +417,23 @@ "PI" math/pi "E" math/e "random" (fn [&] (math/random))}) +# Thread statics (the JVM shapes portable code actually uses). sleep parks the +# CURRENT thread's event loop — inside a future body that's the worker OS +# thread (ev/spawn-thread gives each worker its own loop), so a sleeping +# future doesn't block the parent. +(def- thread-statics + {"sleep" (fn [ms] (ev/sleep (/ ms 1000)) nil) + "yield" (fn [] (ev/sleep 0) nil)}) + (defn- resolve-sym [ctx bindings sym-s] (let [name (sym-s :name) ns (sym-s :ns)] (if (= ns "Math") (let [v (get math-statics name)] (if (nil? v) (error (string "Unsupported Math member: Math/" name)) v)) + (if (= ns "Thread") + (let [v (get thread-statics name)] + (if (nil? v) (error (string "Unsupported Thread member: Thread/" name)) v)) (if (not (nil? ns)) (let [current-ns (ctx-find-ns ctx (ctx-current-ns ctx)) aliased-ns (ns-import-lookup current-ns ns) @@ -472,8 +483,7 @@ # No implicit Janet fallback (Stage 3): an unresolved # Clojure symbol is an error. Host access is the explicit # janet/ prefix above. - (error (string "Unable to resolve symbol: " name)))))))))))))) - + (error (string "Unable to resolve symbol: " name))))))))))))))) (defn- parse-arg-names "Parse a parameter vector, handling & rest args. Returns {:fixed [names...] :rest name-or-nil :all [names...]}" @@ -1013,9 +1023,11 @@ (ns-intern core "remove-ns" (fn [x] (remove-ns ctx (ns-name-of x)))) (ns-intern core "all-ns" (fn [] (all-ns ctx))) (ns-intern core "the-ns" (fn [&opt x] (ns-or-current x))) - (ns-intern core "ns-interns" (fn [&opt x] ((ns-or-current x) :mappings))) + # interns/imports return a jolt MAP (struct), not the live host table — so + # count/seq/keys work on them, and callers can't mutate the ns through them. + (ns-intern core "ns-interns" (fn [&opt x] (table/to-struct ((ns-or-current x) :mappings)))) (ns-intern core "ns-aliases" (fn [&opt x] ((ns-or-current x) :aliases))) - (ns-intern core "ns-imports" (fn [&opt x] ((ns-or-current x) :imports))) + (ns-intern core "ns-imports" (fn [&opt x] (table/to-struct ((ns-or-current x) :imports)))) # (ns-resolve ns sym) -> the var or nil. Unqualified syms look in ns's own # mappings; ns-qualified syms resolve through ns's aliases. (types/ns-resolve # keys ns-find with the symbol struct instead of its name string, so it never @@ -1678,8 +1690,10 @@ (let [field-name (string/slice sym-name 2) target (eval-form ctx bindings (in form 1))] (get target (keyword field-name))) - # Handle ClassName. constructor syntax - (if (and (> (length sym-name) 0) (= (sym-name (- (length sym-name) 1)) 46)) + # Handle ClassName. constructor syntax (".." is the member-threading + # macro, not a constructor named ".") + (if (and (> (length sym-name) 1) (not= sym-name "..") + (= (sym-name (- (length sym-name) 1)) 46)) (let [type-name (string/slice sym-name 0 (- (length sym-name) 1)) type-sym {:jolt/type :symbol :ns (first-form :ns) :name type-name} ctor (eval-form ctx bindings type-sym) diff --git a/test/spec/futures-spec.janet b/test/spec/futures-spec.janet index c7be5aa..a394f64 100644 --- a/test/spec/futures-spec.janet +++ b/test/spec/futures-spec.janet @@ -16,7 +16,13 @@ ["future returns a map" "{:a 1}" "(deref (future {:a 1}))"] ["deref is cached/idempotent" "[2 2]" "(let [f (future (+ 1 1))] [(deref f) (deref f)])"] ["timed deref of ready future" "42" "(let [f (future 42)] (deref f) (deref f 1000 :nope))"] - ["body error re-raised on deref" :throws "(deref (future (throw \"boom\")))"]) + ["body error re-raised on deref" :throws "(deref (future (throw \"boom\")))"] + # Thread/sleep parks the WORKER's own event loop (each future thread has one), + # so a sleeping future doesn't block the parent — and timed deref can fire. + ["timed deref times out" ":timed-out" "(deref (future (do (Thread/sleep 300) :late)) 10 :timed-out)"] + ["Thread/sleep in body" ":slept" "(deref (future (do (Thread/sleep 5) :slept)))"] + ["timed-out future still completes" ":late" + "(let [f (future (do (Thread/sleep 30) :late))] (deref f 5 :early) (deref f))"]) (defspec "clojure.core / futures — predicates" ["future? true" "true" "(future? (future 1))"] diff --git a/test/spec/untested-vars-spec.janet b/test/spec/untested-vars-spec.janet new file mode 100644 index 0000000..444e184 --- /dev/null +++ b/test/spec/untested-vars-spec.janet @@ -0,0 +1,177 @@ +# Specification: spec rows for every previously-untested implemented var +# (jolt-brh follow-up — promotes the dashboard's implemented-untested category). +# Rows assert jolt's ACTUAL documented behavior, including the stub families: +# arrays are vectors/host buffers, proxies/JVM reflection are resolve-only, +# unchecked-* are plain double ops, chunks are eager equivalents. +(use ../support/harness) + +(defspec "untested / primed + division + bit ops" + ["+'" "3" "(+' 1 2)"] + ["-'" "3" "(-' 5 2)"] + ["*'" "12" "(*' 3 4)"] + ["inc'" "2.5" "(inc' 1.5)"] + ["dec'" "1.5" "(dec' 2.5)"] + ["/" "2" "(/ 6 3)"] + ["/ ratio-as-double" "0.5" "(/ 1 2)"] + ["bit-not" "-6" "(bit-not 5)"] + ["bit-and-not" "4" "(bit-and-not 12 10)"] + ["bit-flip" "3" "(bit-flip 2 0)"] + ["unsigned-bit-shift-right" "2" "(unsigned-bit-shift-right 8 2)"]) + +(defspec "untested / hash family" + ["hash stable" "true" "(= (hash :a) (hash :a))"] + ["hash int" "true" "(int? (hash [1 2]))"] + ["hash-combine" "true" "(int? (hash-combine 1 2))"] + ["hash-ordered-coll" "true" "(int? (hash-ordered-coll [1 2]))"] + ["hash-unordered-coll" "true" "(int? (hash-unordered-coll #{1}))"]) + +(defspec "untested / array stubs (vectors + host buffers)" + ["make-array" "(quote (nil nil nil))" "(make-array 3)"] + ["into-array" "(quote (1 2))" "(into-array [1 2])"] + ["to-array" "(quote (1 2))" "(to-array [1 2])"] + ["aclone vec" "(quote (1 2))" "(aclone [1 2])"] + ["aclone independent" "(quote (9 2))" "(let [a (aclone (to-array [1 2]))] (aset a 0 9) (seq a))"] + ["aset/aget" "9" "(let [a (to-array [1 2 3])] (aset a 0 9) (aget a 0))"] + ["aset-int" "7" "(let [a (to-array [1 2])] (aset-int a 0 7) (aget a 0))"] + ["aset-boolean" "true" "(let [a (to-array [1])] (aset-boolean a 0 true) (aget a 0))"] + ["aset-byte" "9" "(let [a (to-array [0])] (aset-byte a 0 9) (aget a 0))"] + ["aset-char" "\\a" "(let [a (to-array [0])] (aset-char a 0 \\a) (aget a 0))"] + ["aset-double" "1.5" "(let [a (to-array [0])] (aset-double a 0 1.5) (aget a 0))"] + ["aset-float" "2.5" "(let [a (to-array [0])] (aset-float a 0 2.5) (aget a 0))"] + ["aset-long" "3" "(let [a (to-array [0])] (aset-long a 0 3) (aget a 0))"] + ["aset-short" "4" "(let [a (to-array [0])] (aset-short a 0 4) (aget a 0))"] + ["boolean-array" "(quote (false false))" "(boolean-array 2)"] + ["int-array" "(quote (1 2))" "(int-array [1 2])"] + ["long-array" "(quote (0 0))" "(long-array 2)"] + ["double-array" "(quote (0 0))" "(double-array 2)"] + ["float-array" "(quote (0 0))" "(float-array 2)"] + ["short-array" "(quote (0 0))" "(short-array 2)"] + ["char-array count" "2" "(count (char-array 2))"] + ["byte-array bytes?" "true" "(bytes? (byte-array 2))"] + ["bytes? not vec" "false" "(bytes? [1])"]) + +(defspec "untested / typed coercion views" + ["booleans" "(quote (true))" "(booleans [true])"] + ["doubles" "(quote (1))" "(doubles [1.0])"] + ["floats" "(quote (1))" "(floats [1.0])"] + ["ints" "(quote (1))" "(ints [1])"] + ["longs" "(quote (1))" "(longs [1])"] + ["shorts" "(quote (1))" "(shorts [1])"] + ["chars first" "\\a" "(first (chars [\\a]))"] + ["bytes view" "true" "(bytes? (bytes [65]))"] + ["byte" "65" "(byte 65)"] + ["short" "1" "(short 1)"] + ["long truncates" "1" "(long 1.7)"] + ["double" "3" "(double 3)"] + ["float" "3" "(float 3)"]) + +(defspec "untested / unchecked-* are plain ops" + ["unchecked-add" "3" "(unchecked-add 1 2)"] + ["unchecked-add-int" "3" "(unchecked-add-int 1 2)"] + ["unchecked-subtract" "3" "(unchecked-subtract 5 2)"] + ["unchecked-subtract-int" "3" "(unchecked-subtract-int 5 2)"] + ["unchecked-multiply" "6" "(unchecked-multiply 2 3)"] + ["unchecked-multiply-int" "6" "(unchecked-multiply-int 2 3)"] + ["unchecked-inc" "2" "(unchecked-inc 1)"] + ["unchecked-inc-int" "2" "(unchecked-inc-int 1)"] + ["unchecked-dec" "2" "(unchecked-dec 3)"] + ["unchecked-dec-int" "2" "(unchecked-dec-int 3)"] + ["unchecked-negate" "-4" "(unchecked-negate 4)"] + ["unchecked-negate-int" "-4" "(unchecked-negate-int 4)"] + ["unchecked-divide-int" "3" "(unchecked-divide-int 7 2)"] + ["unchecked-remainder-int" "1" "(unchecked-remainder-int 7 2)"] + ["unchecked-int" "3" "(unchecked-int 3.7)"] + ["unchecked-long" "3" "(unchecked-long 3.7)"] + ["unchecked-double" "3" "(unchecked-double 3)"] + ["unchecked-float" "3" "(unchecked-float 3)"] + ["unchecked-byte" "65" "(unchecked-byte 65)"] + ["unchecked-char" "97" "(unchecked-char 97)"] + ["unchecked-short" "5" "(unchecked-short 5)"]) + +(defspec "untested / chunk family (eager equivalents) + cat" + ["chunk round-trip" "1" + "(let [cb (chunk-buffer 4)] (chunk-append cb 1) (chunk-first (chunk-cons (chunk cb) nil)))"] + ["cat transducer" "[1 2 3]" "(into [] cat [[1] [2 3]])"] + ["ensure-reduced wraps" "true" "(reduced? (ensure-reduced 5))"] + ["ensure-reduced keeps reduced" "true" "(reduced? (ensure-reduced (reduced 5)))"] + ["halt-when" "4" "(transduce (halt-when even?) conj [] [1 3 4 5])"] + ["chunk-next exhausted" "nil" + "(let [cb (chunk-buffer 2)] (chunk-append cb 1) (chunk-next (chunk-cons (chunk cb) nil)))"] + ["chunk-rest seqable" "()" + "(let [cb (chunk-buffer 2)] (chunk-append cb 1) (vec (chunk-rest (chunk-cons (chunk cb) nil))))"]) + +(defspec "untested / JVM-shape stubs (documented jolt behavior)" + ["class number" "\"java.lang.Number\"" "(class 1)"] + ["class string" "\"java.lang.String\"" "(class \"s\")"] + ["class keyword" "\"clojure.lang.Keyword\"" "(class :k)"] + ["class nil" "nil" "(class nil)"] + ["bean is the map" "{:a 1}" "(bean {:a 1})"] + ["biginteger" "\"5\"" "(str (biginteger 5))"] + ["proxy resolves nil" "nil" "(proxy [Object] [] (toString [] \"x\"))"] + ["construct-proxy throws" :throws "(construct-proxy nil)"] + ["get-proxy-class throws" :throws "(get-proxy-class)"] + ["init-proxy" "nil" "(init-proxy nil {})"] + ["update-proxy" "nil" "(update-proxy nil {})"] + ["proxy-mappings" "{}" "(proxy-mappings nil)"] + ["proxy-call-with-super calls" "1" "(proxy-call-with-super (fn [] 1) nil \"m\")"] + ["memfn throws" :throws "((memfn count) \"abc\")"] + ["proxy-super throws" :throws "(proxy-super count [1])"] + ["re-groups throws" :throws "(re-groups (re-matcher #\"a\" \"b\"))"] + ["re-matcher builds" "false" "(nil? (re-matcher #\"a\" \"abc\"))"] + ["print-dup" "nil" "(print-dup 1 nil)"] + ["print-method" "nil" "(print-method 1 nil)"] + ["uri? string" "false" "(uri? \"http://x\")"] + ["uri? nil" "false" "(uri? nil)"] + ["definterface defines" "true" "(var? (definterface IFoo (foo [x])))"] + ["enumeration-seq" "(quote (1 2))" "(enumeration-seq [1 2])"] + ["iterator-seq" "(quote (1 2))" "(iterator-seq [1 2])"] + ["seque passthrough" "(quote (1 2))" "(seque [1 2])"] + ["delay? true" "true" "(delay? (delay 1))"] + ["delay? false" "false" "(delay? 1)"] + ["future-call" "42" "(deref (future-call (fn [] 42)))"] + [". member resolves nil" "nil" "(. \"abc\" count)"] + [".. threads members" "nil" "(.. \"abc\" count)"]) + +(defspec "untested / protocols: extend + extends?" + ["extend registers" ":str" + "(do (defprotocol Pe (pe [x])) (extend (quote String) Pe {:pe (fn [x] :str)}) (pe \"s\"))"] + ["extend two methods" "[1 2]" + "(do (defprotocol P3 (pa [x]) (pb [x])) (extend (quote Long) P3 {:pa (fn [x] 1) :pb (fn [x] 2)}) [(pa 0) (pb 0)])"] + ["extends? after extend" "true" + "(do (defprotocol P4 (pc [x])) (extend (quote Long) P4 {:pc (fn [x] 1)}) (extends? P4 (quote Long)))"] + ["extends? without" "false" "(do (defprotocol P5 (pd [x])) (extends? P5 (quote Long)))"]) + +(defspec "untested / ns + REPL machinery" + ["all-ns non-empty" "true" "(pos? (count (all-ns)))"] + ["ns-interns sees def" "true" "(do (def zz 1) (pos? (count (ns-interns (quote user)))))"] + ["ns-interns countable" "true" "(map? (ns-interns (quote user)))"] + ["ns-imports empty user" "0" "(count (ns-imports (quote user)))"] + ["reset-meta!" "{:doc \"d\"}" "(do (def vv 1) (reset-meta! (var vv) {:doc \"d\"}))"] + ["prefers empty" "{}" "(do (defmulti mm identity) (prefers mm))"] + ["refer-clojure" "nil" "(refer-clojure)"] + ["special-symbol? if" "true" "(special-symbol? (quote if))"] + ["special-symbol? fn name" "false" "(special-symbol? (quote foo))"] + ["destructure expands" "true" "(pos? (count (destructure (quote [[a b] x]))))"] + ["seq-to-map-for-destructuring" "{:a 1}" "(seq-to-map-for-destructuring (quote (:a 1)) nil)"] + ["*clojure-version* major" "1" "(:major *clojure-version*)"] + ["*ns* user" "\"user\"" "(str *ns*)"] + ["*1 nil outside repl" "nil" "*1"] + ["*2 nil" "nil" "*2"] + ["*3 nil" "nil" "*3"] + ["*e nil" "nil" "*e"] + ["*unchecked-math*" "false" "*unchecked-math*"] + ["*in* bound" "true" "(map? *in*)"]) + +(defspec "untested / misc seqs + binding machinery" + ["nfirst" "(quote (2))" "(nfirst [[1 2] [3]])"] + ["xml-seq root" "1" "(count (xml-seq {:tag :a :content []}))"] + ["xml-seq walks" "2" "(count (xml-seq {:tag :a :content [{:tag :b :content []}]}))"] + # regression: comp with a keyword stage must use jolt IFn dispatch + ["comp keyword stage" "(quote (1 2))" "((comp seq :content) {:content [1 2]})"] + ["comp three stages" "4" "((comp inc inc :n) {:n 2})"] + ["random-sample all" "(quote (1 2))" "(random-sample 1.0 [1 2])"] + ["random-sample none" "()" "(random-sample 0.0 [1 2])"] + ["reader-conditional builds" "true" "(reader-conditional? (reader-conditional (quote (:clj 1)) false))"] + ["->Eduction" "[2 3]" "(vec (->Eduction (map inc) [1 2]))"] + ["bound-fn calls" "42" "((bound-fn [] 42))"] + ["push/pop-thread-bindings" ":ok" "(do (push-thread-bindings {}) (pop-thread-bindings) :ok)"]) diff --git a/tools/spec_coverage.py b/tools/spec_coverage.py index b42d8e7..6fab343 100644 --- a/tools/spec_coverage.py +++ b/tools/spec_coverage.py @@ -42,9 +42,15 @@ jolt = interned | resolvable # --- 3. what the tests exercise -------------------------------------------- tested = set() -sym = re.compile(r'\(([a-zA-Z*+!?<>=_-][a-zA-Z0-9*+!?<>=_./-]*)') +test_text = '' +# A var counts as tested when its name appears as a WHOLE TOKEN anywhere in +# the test sources (assertions live inside strings, so call-position-only +# matching missed *1, +', ., .., /, and bare transducer refs like cat). +SYMCHARS = r"\w*+!?<>=_.'/-" +def token_re(name): + return re.compile('(?