Make clojure.spec.alpha load and run
Four general gaps, shaken out by loading clojure.spec.alpha: - Special forms were shadowable by a same-named macro. analyze-list macroexpanded before checking special forms, so a ns that redefs def/and/or (spec excludes them via :refer-clojure :exclude) made a bare def resolve to the macro instead of the special form, breaking every defn after. Now a head in the special-form set is never macroexpanded, matching the reference macroexpand1 isSpecial check. - reify dropped all but the last arity of a multi-arity protocol method (spec reifies (specize* [s]) and (specize* [s _])). The macro keyed methods by name and overwrote; now it groups arities into one multi-arity fn. - reify instances did not implement IObj: with-meta threw and (instance? clojure.lang.IObj r) was false. Every Clojure reify carries metadata. with-meta now copies the reify to a fresh identity (shared method table) and keys its meta; instance? IObj/IMeta is true for any reify. This was the registry bug — spec's with-name returned nil for specs, so get-spec missed. - (set! (. Class field) val) was rejected. spec toggles clojure.lang.RT/checkSpecAsserts this way; the analyzer now lowers it to a jolt.host/set-static-field! call over a mutable-statics table, and a plain Class/field read consults that table. Also: .name/.getName on a Namespace and .ns/.sym on a Var (spec's ns-qualify / ->sym). analyzer + reify are seed sources (re-minted). spec.alpha now does valid?/conform/cat/keys/explain-str/check-asserts. tick.alpha.interval-test still needs time-literals data readers (separate).
This commit is contained in:
parent
f417516148
commit
7a343351d6
11 changed files with 598 additions and 532 deletions
|
|
@ -448,15 +448,22 @@
|
|||
;; a reify can implement SEVERAL protocols; collect them all (each bare symbol
|
||||
;; switches the current protocol, like extend-type) and pass every protocol name
|
||||
;; to make-reified so (instance? Proto r)/satisfies? recognise all of them.
|
||||
(loop [items (seq forms) protos [] methods {}]
|
||||
;; Several bodies for the same method name are distinct arities (clojure.spec
|
||||
;; reifies (specize* [s]) and (specize* [s _])): group them into one multi-arity
|
||||
;; fn so dispatch picks the clause by arg count.
|
||||
(loop [items (seq forms) protos [] methods {} order []]
|
||||
(if (empty? items)
|
||||
`(make-reified ~methods ~@(vec (map name protos)))
|
||||
`(make-reified
|
||||
~(reduce (fn [m k] (assoc m k `(fn ~@(get methods k)))) {} order)
|
||||
~@(vec (map name protos)))
|
||||
(let [x (first items)]
|
||||
(if (symbol? x)
|
||||
(recur (rest items) (conj protos x) methods)
|
||||
(recur (rest items) protos
|
||||
(assoc methods (keyword (name (first x)))
|
||||
`(fn ~(nth x 1) ~@(drop 2 x)))))))))
|
||||
(recur (rest items) (conj protos x) methods order)
|
||||
(let [k (keyword (name (first x)))
|
||||
clause `(~(nth x 1) ~@(drop 2 x))]
|
||||
(recur (rest items) protos
|
||||
(assoc methods k (conj (get methods k []) clause))
|
||||
(if (contains? methods k) order (conj order k)))))))))
|
||||
|
||||
(defmacro defrecord [name-sym fields & body]
|
||||
(let [tn (name name-sym)
|
||||
|
|
|
|||
|
|
@ -331,6 +331,14 @@
|
|||
(and thead (field-head? thead))
|
||||
{:op :set-field :obj (analyze ctx (nth ti 1) env)
|
||||
:field (subs thead 2) :val val-node}
|
||||
;; (set! (. Class member) val) — a host static-field set. clojure.spec.alpha
|
||||
;; toggles clojure.lang.RT/checkSpecAsserts this way. Lowered to a runtime
|
||||
;; jolt.host/set-static-field! call (the read side is a :host-static ref).
|
||||
(and (= thead ".") (>= (count ti) 3) (form-sym? (nth ti 1)) (form-sym? (nth ti 2))
|
||||
(= :class (:kind (resolve-global ctx (nth ti 1)))))
|
||||
(invoke (var-ref "jolt.host" "set-static-field!")
|
||||
[(const (:name (resolve-global ctx (nth ti 1))))
|
||||
(const (form-sym-name (nth ti 2))) val-node])
|
||||
(form-sym? target)
|
||||
(do (when (local? env (form-sym-name target)) (uncompilable "set! of a local"))
|
||||
(let [r (resolve-global ctx target)]
|
||||
|
|
@ -557,11 +565,12 @@
|
|||
shadowed (and hname (local? env hname))]
|
||||
(cond
|
||||
;; Canonical order (Clojure/CLJS analyze-seq): macroexpand FIRST, then
|
||||
;; dispatch special forms / interop / invoke. Expanding before the
|
||||
;; special-form check means a head that is a macro always expands — even
|
||||
;; one whose name is also in the special-form set — matching reference
|
||||
;; read -> macroexpand -> analyze. A local shadows both.
|
||||
(and (form-sym? head) (not shadowed) (form-macro? ctx head))
|
||||
;; dispatch special forms / interop / invoke. A local shadows the macro.
|
||||
;; A true special form is NOT shadowable by a same-named macro, matching
|
||||
;; the reference macroexpand1's isSpecial check — so a ns that redefs a
|
||||
;; macro `def`/`and`/`or` (clojure.spec.alpha) keeps the special form `def`.
|
||||
(and (form-sym? head) (not shadowed)
|
||||
(not (contains? handled hname)) (form-macro? ctx head))
|
||||
(analyze ctx (form-expand-1 ctx form) env)
|
||||
;; jolt.ffi/__cfn — the foreign-function special form (always emitted
|
||||
;; fully-qualified by the jolt.ffi/foreign-fn macro, so aliases resolve).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue