|
7 | 7 | ; You must not remove this notice, or any other, from this software.
|
8 | 8 |
|
9 | 9 | (ns cljs.spec
|
10 |
| - (:refer-clojure :exclude [+ * and or cat def keys]) |
11 |
| - (:require [cljs.core :as c] |
| 10 | + (:refer-clojure :exclude [+ * and or cat def keys resolve]) |
| 11 | + (:require [cljs.analyzer.api :refer [resolve]] |
12 | 12 | [clojure.walk :as walk]
|
13 | 13 | [cljs.spec.gen :as gen]
|
14 | 14 | [clojure.string :as str]))
|
15 | 15 |
|
| 16 | +(alias 'c 'clojure.core) |
| 17 | + |
16 | 18 | (defn- ->sym
|
17 | 19 | "Returns a symbol from a symbol or var"
|
18 | 20 | [x]
|
|
30 | 32 | (conj (walk/postwalk-replace {s '%} form) '[%] 'fn))
|
31 | 33 | expr))
|
32 | 34 |
|
33 |
| -(defn- res [form] |
| 35 | +(defn- res [env form] |
34 | 36 | (cond
|
35 | 37 | (keyword? form) form
|
36 |
| - (symbol? form) (c/or (-> form resolve ->sym) form) |
37 |
| - (sequential? form) (walk/postwalk #(if (symbol? %) (res %) %) (unfn form)) |
| 38 | + (symbol? form) (c/or (->> form (resolve env) ->sym) form) |
| 39 | + (sequential? form) (walk/postwalk #(if (symbol? %) (res env %) %) (unfn form)) |
38 | 40 | :else form))
|
39 | 41 |
|
40 | 42 | (defmacro def
|
41 | 43 | "Given a namespace-qualified keyword or symbol k, and a spec, spec-name, predicate or regex-op
|
42 | 44 | makes an entry in the registry mapping k to the spec"
|
43 | 45 | [k spec-form]
|
44 |
| - `(cljs.spec/def-impl ~k '~(res spec-form) ~spec-form)) |
| 46 | + `(cljs.spec/def-impl ~k '~(res &env spec-form) ~spec-form)) |
45 | 47 |
|
46 | 48 | (defmacro spec
|
47 | 49 | "Takes a single predicate form, e.g. can be the name of a predicate,
|
|
59 | 61 |
|
60 | 62 | Returns a spec."
|
61 | 63 | [form & {:keys [gen]}]
|
62 |
| - `(cljs.spec/spec-impl '~(res form) ~form ~gen nil)) |
| 64 | + `(cljs.spec/spec-impl '~(res &env form) ~form ~gen nil)) |
63 | 65 |
|
64 | 66 | (defmacro multi-spec
|
65 | 67 | "Takes the name of a spec/predicate-returning multimethod and a
|
|
88 | 90 | though those values are not evident in the spec.
|
89 | 91 | "
|
90 | 92 | [mm retag]
|
91 |
| - `(cljs.spec/multi-spec-impl '~(res mm) (var ~mm) ~retag)) |
| 93 | + `(cljs.spec/multi-spec-impl '~(res &env mm) (var ~mm) ~retag)) |
92 | 94 |
|
93 | 95 | (defmacro keys
|
94 | 96 | "Creates and returns a map validating spec. :req and :opt are both
|
|
174 | 176 | Returns a spec that returns the conformed value. Successive
|
175 | 177 | conformed values propagate through rest of predicates."
|
176 | 178 | [& pred-forms]
|
177 |
| - `(cljs.spec/and-spec-impl '~(mapv res pred-forms) ~(vec pred-forms) nil)) |
| 179 | + `(cljs.spec/and-spec-impl '~(mapv #(res &env %) pred-forms) ~(vec pred-forms) nil)) |
178 | 180 |
|
179 | 181 | (defmacro *
|
180 | 182 | "Returns a regex op that matches zero or more values matching
|
181 | 183 | pred. Produces a vector of matches iff there is at least one match"
|
182 | 184 | [pred-form]
|
183 |
| - `(cljs.spec/rep-impl '~(res pred-form) ~pred-form)) |
| 185 | + `(cljs.spec/rep-impl '~(res &env pred-form) ~pred-form)) |
184 | 186 |
|
185 | 187 | (defmacro +
|
186 | 188 | "Returns a regex op that matches one or more values matching
|
187 | 189 | pred. Produces a vector of matches"
|
188 | 190 | [pred-form]
|
189 |
| - `(cljs.spec/rep+impl '~(res pred-form) ~pred-form)) |
| 191 | + `(cljs.spec/rep+impl '~(res &env pred-form) ~pred-form)) |
190 | 192 |
|
191 | 193 | (defmacro ?
|
192 | 194 | "Returns a regex op that matches zero or one value matching
|
|
248 | 250 | Optionally takes :gen generator-fn, which must be a fn of no args
|
249 | 251 | that returns a test.check generator."
|
250 | 252 | [& {:keys [args ret fn gen]}]
|
251 |
| - `(cljs.spec/fspec-impl ~args '~(res args) ~ret '~(res ret) ~fn '~(res fn) ~gen)) |
| 253 | + (let [env &env] |
| 254 | + `(cljs.spec/fspec-impl ~args '~(res env args) ~ret '~(res env ret) ~fn '~(res env fn) ~gen))) |
252 | 255 |
|
253 | 256 | (defmacro tuple
|
254 | 257 | "takes one or more preds and returns a spec for a tuple, a vector
|
255 | 258 | where each element conforms to the corresponding pred. Each element
|
256 | 259 | will be referred to in paths using its ordinal."
|
257 | 260 | [& preds]
|
258 | 261 | (assert (not (empty? preds)))
|
259 |
| - `(cljs.spec/tuple-impl '~(mapv res preds) ~(vec preds))) |
| 262 | + `(cljs.spec/tuple-impl '~(mapv #(res &env %) preds) ~(vec preds))) |
260 | 263 |
|
261 | 264 | (defn- ns-qualify
|
262 | 265 | "Qualify symbol s by resolving it or using the current *ns*."
|
263 |
| - [s] |
264 |
| - (if-let [resolved (resolve s)] |
| 266 | + [env s] |
| 267 | + (if-let [resolved (resolve env s)] |
265 | 268 | (->sym resolved)
|
266 | 269 | (if (namespace s)
|
267 | 270 | s
|
268 | 271 | (symbol (str (.name *ns*)) (str s)))))
|
269 | 272 |
|
270 | 273 | (defn- fn-spec-sym
|
271 |
| - [sym role] |
272 |
| - (symbol (str (ns-qualify sym) "$" (name role)))) |
| 274 | + [env sym role] |
| 275 | + (symbol (str (ns-qualify env sym) "$" (name role)))) |
273 | 276 |
|
274 | 277 | (defmacro fdef
|
275 | 278 | "Takes a symbol naming a function, and one or more of the following:
|
|
304 | 307 | :sym symbol?)
|
305 | 308 | :ret symbol?)"
|
306 | 309 | [fn-sym & {:keys [args ret fn] :as m}]
|
307 |
| - (let [qn (ns-qualify fn-sym)] |
| 310 | + (let [env &env |
| 311 | + qn (ns-qualify env fn-sym)] |
308 | 312 | `(do ~@(reduce
|
309 | 313 | (c/fn [defns role]
|
310 | 314 | (if (contains? m role)
|
311 |
| - (let [s (fn-spec-sym qn (name role))] |
| 315 | + (let [s (fn-spec-sym env qn (name role))] |
312 | 316 | (conj defns `(cljs.spec/def '~s ~(get m role))))
|
313 | 317 | defns))
|
314 | 318 | [] [:args :ret :fn])
|
|
0 commit comments