Merge pull request #56 from jolt-lang/missing-core-vars
core: the last seven missing-portable vars — coverage gap closed (jolt-brh)
This commit is contained in:
commit
daab2ab4cc
7 changed files with 167 additions and 26 deletions
|
|
@ -3,16 +3,16 @@
|
|||
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 553 of them.
|
||||
community examples). jolt interns 564 of them.
|
||||
|
||||
| Status | Count | Meaning |
|
||||
|---|---|---|
|
||||
| implemented+tested | 415 | in jolt and exercised by spec/conformance |
|
||||
| implemented-untested | 138 | in jolt, no direct test — spec entries will add them |
|
||||
| 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) |
|
||||
| missing-portable | 10 | portable semantics, jolt lacks it — implementation gap |
|
||||
| missing-portable | 0 | portable semantics, jolt lacks it — implementation gap |
|
||||
| special-form | 15 | specified in §3, not a library var |
|
||||
| dynamic-var | 30 | classification needed: portable default vs host-dependent |
|
||||
| dynamic-var | 29 | classification needed: portable default vs host-dependent |
|
||||
| agents-taps | 22 | out of scope pending concurrency design note |
|
||||
| stm-refs | 11 | out of scope pending concurrency design note |
|
||||
| jvm-specific | 53 | catalogued, not specified |
|
||||
|
|
@ -45,7 +45,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `*file*` | dynamic-var | ✓ |
|
||||
| `*flush-on-newline*` | dynamic-var | |
|
||||
| `*fn-loader*` | dynamic-var | |
|
||||
| `*in*` | dynamic-var | |
|
||||
| `*in*` | implemented-untested | |
|
||||
| `*math-context*` | dynamic-var | |
|
||||
| `*ns*` | implemented-untested | ✓ |
|
||||
| `*out*` | dynamic-var | ✓ |
|
||||
|
|
@ -170,8 +170,8 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `catch` | special-form | ✓ |
|
||||
| `char` | implemented+tested | ✓ |
|
||||
| `char-array` | implemented-untested | ✓ |
|
||||
| `char-escape-string` | implemented-untested | ✓ |
|
||||
| `char-name-string` | implemented-untested | ✓ |
|
||||
| `char-escape-string` | implemented+tested | ✓ |
|
||||
| `char-name-string` | implemented+tested | ✓ |
|
||||
| `char?` | implemented+tested | ✓ |
|
||||
| `chars` | implemented-untested | ✓ |
|
||||
| `chunk` | implemented-untested | ✓ |
|
||||
|
|
@ -185,7 +185,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `class` | implemented-untested | ✓ |
|
||||
| `class?` | jvm-specific | ✓ |
|
||||
| `clear-agent-errors` | agents-taps | |
|
||||
| `clojure-version` | implemented-untested | ✓ |
|
||||
| `clojure-version` | implemented+tested | ✓ |
|
||||
| `coll?` | implemented+tested | ✓ |
|
||||
| `comment` | implemented+tested | ✓ |
|
||||
| `commute` | stm-refs | ✓ |
|
||||
|
|
@ -278,7 +278,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `extend` | implemented-untested | ✓ |
|
||||
| `extend-protocol` | implemented+tested | ✓ |
|
||||
| `extend-type` | implemented+tested | ✓ |
|
||||
| `extenders` | missing-portable | ✓ |
|
||||
| `extenders` | implemented+tested | ✓ |
|
||||
| `extends?` | implemented-untested | ✓ |
|
||||
| `false?` | implemented+tested | ✓ |
|
||||
| `ffirst` | implemented+tested | ✓ |
|
||||
|
|
@ -287,7 +287,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `filterv` | implemented+tested | ✓ |
|
||||
| `finally` | special-form | ✓ |
|
||||
| `find` | implemented+tested | ✓ |
|
||||
| `find-keyword` | missing-portable | ✓ |
|
||||
| `find-keyword` | implemented+tested | ✓ |
|
||||
| `find-ns` | implemented+tested | ✓ |
|
||||
| `find-protocol-impl` | jvm-specific | |
|
||||
| `find-protocol-method` | jvm-specific | |
|
||||
|
|
@ -346,7 +346,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `infinite?` | implemented+tested | ✓ |
|
||||
| `init-proxy` | implemented-untested | ✓ |
|
||||
| `inst-ms` | implemented+tested | ✓ |
|
||||
| `inst-ms*` | missing-portable | |
|
||||
| `inst-ms*` | implemented+tested | |
|
||||
| `inst?` | implemented+tested | ✓ |
|
||||
| `instance?` | implemented+tested | ✓ |
|
||||
| `int` | implemented+tested | ✓ |
|
||||
|
|
@ -376,7 +376,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `lazy-seq` | implemented+tested | ✓ |
|
||||
| `let` | implemented+tested | ✓ |
|
||||
| `letfn` | implemented+tested | ✓ |
|
||||
| `line-seq` | implemented-untested | ✓ |
|
||||
| `line-seq` | implemented+tested | ✓ |
|
||||
| `list` | implemented+tested | ✓ |
|
||||
| `list*` | implemented+tested | ✓ |
|
||||
| `list?` | implemented+tested | ✓ |
|
||||
|
|
@ -415,7 +415,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `mod` | implemented+tested | ✓ |
|
||||
| `monitor-enter` | special-form | |
|
||||
| `monitor-exit` | special-form | |
|
||||
| `munge` | implemented-untested | ✓ |
|
||||
| `munge` | implemented+tested | ✓ |
|
||||
| `name` | implemented+tested | ✓ |
|
||||
| `namespace` | implemented+tested | ✓ |
|
||||
| `namespace-munge` | implemented+tested | ✓ |
|
||||
|
|
@ -517,9 +517,9 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `re-matches` | implemented+tested | ✓ |
|
||||
| `re-pattern` | implemented+tested | ✓ |
|
||||
| `re-seq` | implemented+tested | ✓ |
|
||||
| `read` | missing-portable | ✓ |
|
||||
| `read+string` | missing-portable | ✓ |
|
||||
| `read-line` | missing-portable | ✓ |
|
||||
| `read` | implemented+tested | ✓ |
|
||||
| `read+string` | implemented+tested | ✓ |
|
||||
| `read-line` | implemented+tested | ✓ |
|
||||
| `read-string` | implemented+tested | ✓ |
|
||||
| `reader-conditional` | implemented-untested | ✓ |
|
||||
| `reader-conditional?` | implemented+tested | ✓ |
|
||||
|
|
@ -623,7 +623,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `subs` | implemented+tested | ✓ |
|
||||
| `subseq` | implemented+tested | ✓ |
|
||||
| `subvec` | implemented+tested | ✓ |
|
||||
| `supers` | implemented-untested | ✓ |
|
||||
| `supers` | implemented+tested | ✓ |
|
||||
| `swap!` | implemented+tested | ✓ |
|
||||
| `swap-vals!` | implemented+tested | ✓ |
|
||||
| `symbol` | implemented+tested | ✓ |
|
||||
|
|
@ -636,7 +636,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `take-nth` | implemented+tested | ✓ |
|
||||
| `take-while` | implemented+tested | ✓ |
|
||||
| `tap>` | agents-taps | ✓ |
|
||||
| `test` | implemented-untested | ✓ |
|
||||
| `test` | implemented+tested | ✓ |
|
||||
| `the-ns` | implemented+tested | ✓ |
|
||||
| `thread-bound?` | implemented+tested | ✓ |
|
||||
| `throw` | special-form | ✓ |
|
||||
|
|
@ -671,7 +671,7 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `unchecked-short` | implemented-untested | ✓ |
|
||||
| `unchecked-subtract` | implemented-untested | ✓ |
|
||||
| `unchecked-subtract-int` | implemented-untested | ✓ |
|
||||
| `underive` | implemented-untested | ✓ |
|
||||
| `underive` | implemented+tested | ✓ |
|
||||
| `unquote` | jvm-specific | ✓ |
|
||||
| `unquote-splicing` | jvm-specific | ✓ |
|
||||
| `unreduced` | implemented+tested | ✓ |
|
||||
|
|
@ -707,13 +707,13 @@ UNVERIFIED field; that column will be added as entries land.
|
|||
| `while` | implemented+tested | ✓ |
|
||||
| `with-bindings` | implemented+tested | ✓ |
|
||||
| `with-bindings*` | implemented+tested | ✓ |
|
||||
| `with-in-str` | missing-portable | ✓ |
|
||||
| `with-in-str` | implemented+tested | ✓ |
|
||||
| `with-loading-context` | jvm-specific | |
|
||||
| `with-local-vars` | missing-portable | ✓ |
|
||||
| `with-local-vars` | implemented+tested | ✓ |
|
||||
| `with-meta` | implemented+tested | ✓ |
|
||||
| `with-open` | missing-portable | ✓ |
|
||||
| `with-open` | implemented+tested | ✓ |
|
||||
| `with-out-str` | implemented+tested | ✓ |
|
||||
| `with-precision` | missing-portable | ✓ |
|
||||
| `with-precision` | implemented+tested | ✓ |
|
||||
| `with-redefs` | implemented+tested | ✓ |
|
||||
| `with-redefs-fn` | implemented+tested | ✓ |
|
||||
| `xml-seq` | implemented-untested | ✓ |
|
||||
|
|
|
|||
|
|
@ -809,3 +809,15 @@
|
|||
(assoc m k (up (get m k) ks f args))
|
||||
(assoc m k (apply f (get m k) args)))))]
|
||||
(up m ks f args)))
|
||||
|
||||
;; --- jolt-brh: the last missing-portable vars --------------------------------
|
||||
|
||||
;; jolt keywords have no intern table (any keyword "exists"), so find-keyword
|
||||
;; always finds — babashka makes the same call.
|
||||
(defn find-keyword
|
||||
([nm] (keyword nm))
|
||||
([ns nm] (keyword ns nm)))
|
||||
|
||||
;; The raw Inst protocol method; jolt insts have one representation, so it is
|
||||
;; inst-ms itself.
|
||||
(defn inst-ms* [i] (inst-ms i))
|
||||
|
|
|
|||
|
|
@ -73,6 +73,29 @@
|
|||
[] (partition 2 bindings))]
|
||||
`(with-redefs-fn (hash-map ~@pairs) (fn [] ~@body))))
|
||||
|
||||
;; Fresh free-standing var cells bound as locals; read/write with
|
||||
;; var-get/var-set. The cells come from the host seam __local-var.
|
||||
(defmacro with-local-vars [bindings & body]
|
||||
(let [binds (reduce (fn [acc p] (conj (conj acc (first p)) `(__local-var ~(second p))))
|
||||
[] (partition 2 bindings))]
|
||||
`(let [~@binds] ~@body)))
|
||||
|
||||
;; Canonical recursive expansion; closing goes through the host seam __close
|
||||
;; (a map-like value's :close fn or a host file — no .close interop here).
|
||||
(defmacro with-open [bindings & body]
|
||||
(if (zero? (count bindings))
|
||||
`(do ~@body)
|
||||
`(let [~(first bindings) ~(second bindings)]
|
||||
(try
|
||||
(with-open ~(vec (drop 2 bindings)) ~@body)
|
||||
(finally (__close ~(first bindings)))))))
|
||||
|
||||
;; jolt numbers are doubles — there is no BigDecimal math context, so the
|
||||
;; precision (and optional :rounding mode) is accepted and ignored.
|
||||
(defmacro with-precision [precision & exprs]
|
||||
(let [body (if (= :rounding (first exprs)) (drop 2 exprs) exprs)]
|
||||
`(do ~@body)))
|
||||
|
||||
(defmacro with-bindings [binding-map & body]
|
||||
`(with-bindings* ~binding-map (fn [] ~@body)))
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
"A reader over string s (the with-in-str expansion calls this)."
|
||||
[s]
|
||||
(let [buf (atom s)]
|
||||
{:read-line-fn
|
||||
{:buf buf
|
||||
:fill-fn nil
|
||||
:read-line-fn
|
||||
(fn []
|
||||
(let [cur @buf]
|
||||
(when (pos? (count cur))
|
||||
|
|
@ -40,7 +42,13 @@
|
|||
(def ^:private stdin-buf (atom ""))
|
||||
|
||||
(def ^:dynamic *in*
|
||||
{:read-line-fn
|
||||
{:buf stdin-buf
|
||||
:fill-fn (fn []
|
||||
(let [line (__stdin-read-line)]
|
||||
(if (nil? line)
|
||||
false
|
||||
(do (swap! stdin-buf (fn [b] (str b line "\n"))) true))))
|
||||
:read-line-fn
|
||||
(fn []
|
||||
(let [cur @stdin-buf]
|
||||
(if (pos? (count cur))
|
||||
|
|
@ -87,6 +95,27 @@
|
|||
`(binding [*in* (__string-reader ~s)]
|
||||
~@body))
|
||||
|
||||
;; Like read, and also returns the exact text consumed for the form (leading
|
||||
;; whitespace included). On EOF: throws, or returns [eof-value ""] when
|
||||
;; eof-error? is false.
|
||||
(defn read+string
|
||||
([] (read+string *in*))
|
||||
([stream] (read+string stream true nil))
|
||||
([stream eof-error? eof-value]
|
||||
(let [buf (get stream :buf)
|
||||
fill (get stream :fill-fn)]
|
||||
(loop []
|
||||
(let [s (deref buf)
|
||||
r (__parse-next s)]
|
||||
(if (nil? r)
|
||||
(if (and fill (fill))
|
||||
(recur)
|
||||
(if eof-error?
|
||||
(throw (ex-info "EOF while reading" {}))
|
||||
[eof-value ""]))
|
||||
(do (reset! buf (nth r 1))
|
||||
[(nth r 0) (subs s 0 (- (count s) (count (nth r 1))))])))))))
|
||||
|
||||
(defn line-seq
|
||||
"Returns the lines of text from rdr as a lazy sequence of strings, as by
|
||||
read-line. (Jolt extension kept from the old kernel stub: a plain string
|
||||
|
|
|
|||
|
|
@ -2256,6 +2256,17 @@
|
|||
(defn core-copy-var [sym & args] nil)
|
||||
(defn core-macrofy [sym fn & more] fn)
|
||||
(defn core-new-var [sym & args] nil)
|
||||
# A free-standing var cell (not interned anywhere): with-local-vars binds
|
||||
# these as locals; var-get/var-set work on any cell.
|
||||
(defn core-local-var [&opt val]
|
||||
@{:jolt/type :jolt/var :name "local" :ns nil :root val :gen 0})
|
||||
# with-open's close seam: a map-like value closes via its :close fn, a host
|
||||
# file via file/close. No .close interop on the Janet host.
|
||||
(defn core-close-resource [x]
|
||||
(cond
|
||||
(and (or (table? x) (struct? x)) (function? (get x :close))) ((get x :close))
|
||||
(= :core/file (type x)) (file/close x)
|
||||
(error (string "with-open: don't know how to close " (type x)))))
|
||||
# sci stub: pass the registry map through (it was @{} — a raw host table that
|
||||
# strict map-conj rightly rejects; identity also keeps sci's registry intact).
|
||||
(defn core-avoid-method-too-large [& args] (if (> (length args) 0) (in args 0) {}))
|
||||
|
|
@ -2949,6 +2960,8 @@
|
|||
"copy-var" core-copy-var
|
||||
"macrofy" core-macrofy
|
||||
"new-var" core-new-var
|
||||
"__local-var" core-local-var
|
||||
"__close" core-close-resource
|
||||
"avoid-method-too-large" core-avoid-method-too-large
|
||||
"inst?" core-inst?
|
||||
"inst-ms" core-inst-ms
|
||||
|
|
|
|||
|
|
@ -951,6 +951,17 @@
|
|||
(ns-intern core "protocol-dispatch"
|
||||
(fn [proto-name method-name obj rest-args]
|
||||
(protocol-dispatch-impl ctx proto-name method-name obj rest-args)))
|
||||
(ns-intern core "extenders"
|
||||
(fn [proto]
|
||||
# All type-tags whose registry entry implements this protocol, as symbols
|
||||
# (closest analog to Clojure's class list); nil when none.
|
||||
(let [pname (get (get proto :name) :name)
|
||||
registry (get (ctx :env) :type-registry)
|
||||
out @[]]
|
||||
(each tag (keys registry)
|
||||
(when (get (get registry tag) pname)
|
||||
(array/push out {:jolt/type :symbol :ns nil :name tag})))
|
||||
(if (empty? out) nil (tuple ;out)))))
|
||||
(ns-intern core "register-method"
|
||||
(fn [type-name proto-name method-name f]
|
||||
(register-method-impl ctx type-name proto-name method-name f)))
|
||||
|
|
|
|||
53
test/spec/missing-vars-spec.janet
Normal file
53
test/spec/missing-vars-spec.janet
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Specification: the last missing-portable core vars (jolt-brh):
|
||||
# extenders, find-keyword, inst-ms*, read+string, with-local-vars, with-open,
|
||||
# with-precision. Documented jolt divergences: find-keyword always finds (jolt
|
||||
# keywords have no intern table — babashka does the same); with-precision
|
||||
# evaluates its body with the precision ignored (numbers are doubles, no
|
||||
# BigDecimal context); with-open closes via the value's :close fn or a host
|
||||
# file (no .close interop on the Janet host).
|
||||
(use ../support/harness)
|
||||
|
||||
(defspec "core / find-keyword + inst-ms*"
|
||||
["find-keyword" ":a" "(find-keyword \"a\")"]
|
||||
["find-keyword 2-arity" ":n/a" "(find-keyword \"n\" \"a\")"]
|
||||
["find-keyword = keyword" "true" "(= (find-keyword \"x\") :x)"]
|
||||
["inst-ms*" "true" "(= (inst-ms* #inst \"2020-01-01T00:00:00Z\") (inst-ms #inst \"2020-01-01T00:00:00Z\"))"]
|
||||
["inst-ms* value" "0" "(inst-ms* #inst \"1970-01-01T00:00:00Z\")"])
|
||||
|
||||
(defspec "core / with-local-vars"
|
||||
["var-get initial" "1" "(with-local-vars [x 1] (var-get x))"]
|
||||
["var-set" "2" "(with-local-vars [x 1] (var-set x 2) (var-get x))"]
|
||||
["two vars" "[1 2]" "(with-local-vars [a 1 b 2] [(var-get a) (var-get b)])"]
|
||||
["vars are values" "5" "(with-local-vars [x 0] (let [bump (fn [v] (var-set v (+ 5 (var-get v))))] (bump x) (var-get x)))"]
|
||||
["init sees outer" "3" "(let [y 3] (with-local-vars [x y] (var-get x)))"]
|
||||
["body result" ":done" "(with-local-vars [x 1] :done)"])
|
||||
|
||||
(defspec "core / with-open"
|
||||
["body result" ":r" "(let [log (atom [])] (with-open [c {:close (fn [] (swap! log conj :closed))}] :r))"]
|
||||
["close runs" "[:closed]" "(let [log (atom [])] (with-open [c {:close (fn [] (swap! log conj :closed))}] :r) (deref log))"]
|
||||
["close on throw" "[:closed]" "(let [log (atom [])] (try (with-open [c {:close (fn [] (swap! log conj :closed))}] (throw (ex-info \"boom\" {}))) (catch Exception e nil)) (deref log))"]
|
||||
["nested close order" "[:inner :outer]" "(let [log (atom [])] (with-open [a {:close (fn [] (swap! log conj :outer))} b {:close (fn [] (swap! log conj :inner))}] :r) (deref log))"]
|
||||
["zero bindings" ":r" "(with-open [] :r)"]
|
||||
["binding visible" "5" "(with-open [c {:close (fn [] nil) :v 5}] (:v c))"])
|
||||
|
||||
(defspec "core / with-precision"
|
||||
["body evaluates" "3.14" "(with-precision 3 3.14)"]
|
||||
["multiple body forms" "2" "(with-precision 10 1 2)"]
|
||||
["rounding arg accepted" "1.5" "(with-precision 4 :rounding :half-up 1.5)"]
|
||||
["arithmetic" "2" "(with-precision 5 (+ 1 1))"])
|
||||
|
||||
(defspec "core / read+string"
|
||||
["form and text" "true" "(let [[v s] (with-in-str \"42 rest\" (read+string))] (and (= v 42) (string? s)))"]
|
||||
["form value" "(quote (+ 1 2))" "(first (with-in-str \"(+ 1 2)\" (read+string)))"]
|
||||
["text covers the form" "true" "(let [[v s] (with-in-str \" [1 2] tail\" (read+string))] (and (= v [1 2]) (> (count s) 3)))"]
|
||||
["advances the stream" "[1 2]" "(with-in-str \"1 2\" [(first (read+string)) (first (read+string))])"]
|
||||
["EOF throws" :throws "(with-in-str \"\" (read+string))"]
|
||||
["eof-value arity" ":done" "(first (with-in-str \"\" (read+string *in* false :done)))"])
|
||||
|
||||
(defspec "core / extenders"
|
||||
["lists extended type" "[\"user.Rx\"]"
|
||||
"(do (defprotocol Px (pm [x])) (defrecord Rx [] Px (pm [x] 1)) (mapv str (extenders Px)))"]
|
||||
["nil when none" "nil"
|
||||
"(do (defprotocol Py (pn [x])) (extenders Py))"]
|
||||
["seq of tags" "true"
|
||||
"(do (defprotocol Pz (pz [x])) (defrecord Rz [] Pz (pz [x] 1)) (and (seq (extenders Pz)) (= 1 (count (extenders Pz)))))"])
|
||||
Loading…
Add table
Add a link
Reference in a new issue