Skip to content

Commit 2a21c22

Browse files
author
dnolen
committed
Same as Clojure 080121d
1 parent 103aa6e commit 2a21c22

File tree

2 files changed

+93
-26
lines changed

2 files changed

+93
-26
lines changed

src/main/cljs/cljs/spec.cljc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,10 @@
234234
(defmacro conformer
235235
"takes a predicate function with the semantics of conform i.e. it should return either a
236236
(possibly converted) value or :clojure.spec/invalid, and returns a
237-
spec that uses it as a predicate/conformer"
238-
[f]
239-
`(cljs.spec/spec-impl '~f ~f nil true))
237+
spec that uses it as a predicate/conformer. Optionally takes a
238+
second fn that does unform of result of first"
239+
([f] `(cljs.spec/spec-impl '~f ~f nil true))
240+
([f unf] `(cljs.spec/spec-impl '~f ~f nil true ~unf)))
240241

241242
(defmacro fspec
242243
"takes :args :ret and (optional) :fn kwargs whose values are preds

src/main/cljs/cljs/spec.cljs

Lines changed: 89 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
(defprotocol Spec
3838
(conform* [spec x])
39+
(unform* [spec y])
3940
(explain* [spec path via in x])
4041
(gen* [spec overrides path rmap])
4142
(with-gen* [spec gfn])
@@ -107,6 +108,13 @@
107108
[spec x]
108109
(conform* (specize spec) x))
109110

111+
(defn unform
112+
"Given a spec and a value created by or compliant with a call to
113+
'conform' with the same spec, returns a value with all conform
114+
destructuring undone."
115+
[spec x]
116+
(unform* (specize spec) x))
117+
110118
(defn form
111119
"returns the spec as data"
112120
[spec]
@@ -405,6 +413,17 @@
405413
(recur ret ks))
406414
ret)))
407415
::invalid))
416+
(unform* [_ m]
417+
(let [reg (registry)]
418+
(loop [ret m, [k & ks :as keys] (c/keys m)]
419+
(if keys
420+
(if (contains? reg (keys->specs k))
421+
(let [cv (get m k)
422+
v (unform (keys->specs k) cv)]
423+
(recur (if (identical? cv v) ret (assoc ret k v))
424+
ks))
425+
(recur ret ks))
426+
ret))))
408427
(explain* [_ path via in x]
409428
(if-not (map? x)
410429
{path {:pred 'map? :val x :via via :in in}}
@@ -452,25 +471,31 @@
452471

453472
(defn ^:skip-wiki spec-impl
454473
"Do not call this directly, use 'spec'"
455-
[form pred gfn cpred?]
456-
(cond
457-
(spec? pred) (cond-> pred gfn (with-gen gfn))
458-
(regex? pred) (regex-spec-impl pred gfn)
459-
(named? pred) (cond-> (the-spec pred) gfn (with-gen gfn))
460-
:else
461-
(reify
462-
IFn
463-
(-invoke [this x] (valid? this x))
464-
Spec
465-
(conform* [_ x] (dt pred x form cpred?))
466-
(explain* [_ path via in x]
467-
(when (= ::invalid (dt pred x form cpred?))
468-
{path {:pred (abbrev form) :val x :via via :in in}}))
469-
(gen* [_ _ _ _] (if gfn
470-
(gfn)
471-
(gen/gen-for-pred pred)))
472-
(with-gen* [_ gfn] (spec-impl form pred gfn cpred?))
473-
(describe* [_] form))))
474+
([form pred gfn cpred?] (spec-impl form pred gfn cpred? nil))
475+
([form pred gfn cpred? unc]
476+
(cond
477+
(spec? pred) (cond-> pred gfn (with-gen gfn))
478+
(regex? pred) (regex-spec-impl pred gfn)
479+
(named? pred) (cond-> (the-spec pred) gfn (with-gen gfn))
480+
:else
481+
(reify
482+
IFn
483+
(-invoke [this x] (valid? this x))
484+
Spec
485+
(conform* [_ x] (dt pred x form cpred?))
486+
(unform* [_ x] (if cpred?
487+
(if unc
488+
(unc x)
489+
(throw (js/Error. "no unform fn for conformer")))
490+
x))
491+
(explain* [_ path via in x]
492+
(when (= ::invalid (dt pred x form cpred?))
493+
{path {:pred (abbrev form) :val x :via via :in in}}))
494+
(gen* [_ _ _ _] (if gfn
495+
(gfn)
496+
(gen/gen-for-pred pred)))
497+
(with-gen* [_ gfn] (spec-impl form pred gfn cpred?))
498+
(describe* [_] form)))))
474499

475500
(defn ^:skip-wiki multi-spec-impl
476501
"Do not call this directly, use 'multi-spec'"
@@ -492,6 +517,9 @@
492517
(conform* [_ x] (if-let [pred (predx x)]
493518
(dt pred x form)
494519
::invalid))
520+
(unform* [_ x] (if-let [pred (predx x)]
521+
(unform pred x)
522+
(throw (js/Error. (str "No method of: " form " for dispatch value: " (dval x))))))
495523
(explain* [_ path via in x]
496524
(let [dv (dval x)
497525
path (conj path dv)]
@@ -539,6 +567,16 @@
539567
::invalid
540568
(recur (if (identical? cv v) ret (assoc ret i cv))
541569
(inc i))))))))
570+
(unform* [_ x]
571+
(assert (c/and (vector? x)
572+
(= (count x) (count preds))))
573+
(loop [ret x, i 0]
574+
(if (= i (count x))
575+
ret
576+
(let [cv (x i)
577+
v (unform (preds i) cv)]
578+
(recur (if (identical? cv v) ret (assoc ret i v))
579+
(inc i))))))
542580
(explain* [_ path via in x]
543581
(cond
544582
(not (vector? x))
@@ -570,6 +608,7 @@
570608
"Do not call this directly, use 'or'"
571609
[keys forms preds gfn]
572610
(let [id (random-uuid)
611+
kps (zipmap keys preds)
573612
cform (fn [x]
574613
(loop [i 0]
575614
(if (< i (count preds))
@@ -584,6 +623,7 @@
584623
(-invoke [this x] (valid? this x))
585624
Spec
586625
(conform* [_ x] (cform x))
626+
(unform* [_ [k x]] (unform (kps k) x))
587627
(explain* [this path via in x]
588628
(when-not (valid? this x)
589629
(apply merge
@@ -636,6 +676,7 @@
636676
(-invoke [this x] (valid? this x))
637677
Spec
638678
(conform* [_ x] (and-preds x preds forms))
679+
(unform* [_ x] (reduce #(unform %2 %1) x (reverse preds)))
639680
(explain* [_ path via in x] (explain-pred-list forms preds path via in x))
640681
(gen* [_ overrides path rmap] (if gfn (gfn) (gensub (first preds) overrides path rmap (first forms))))
641682
(with-gen* [_ gfn] (and-spec-impl forms preds gfn))
@@ -720,7 +761,7 @@
720761

721762
(defn ^:skip-wiki maybe-impl
722763
"Do not call this directly, use '?'"
723-
[p form] (alt* [p (accept ::nil)] nil [form ::nil]))
764+
[p form] (assoc (alt* [p (accept ::nil)] nil [form ::nil]) :maybe form))
724765

725766
(defn- noret? [p1 pret]
726767
(c/or (= pret ::nil)
@@ -762,6 +803,27 @@
762803
r (if (nil? p0) ::nil (preturn p0))]
763804
(if k0 [k0 r] r)))))
764805

806+
(defn- op-unform [p x]
807+
;;(prn {:p p :x x})
808+
(let [{[p0 & pr :as ps] :ps, [k :as ks] :ks, :keys [::op p1 ret forms rep+ maybe] :as p} (reg-resolve p)
809+
kps (zipmap ks ps)]
810+
(case op
811+
::accept [ret]
812+
nil [(unform p x)]
813+
::amp (let [px (reduce #(unform %2 %1) x (reverse ps))]
814+
(op-unform p1 px))
815+
::rep (mapcat #(op-unform p1 %) x)
816+
::pcat (if rep+
817+
(mapcat #(op-unform p0 %) x)
818+
(mapcat (fn [k]
819+
(when (contains? x k)
820+
(op-unform (kps k) (get x k))))
821+
ks))
822+
::alt (if maybe
823+
[(unform p0 x)]
824+
(let [[k v] x]
825+
(op-unform (kps k) v))))))
826+
765827
(defn- add-ret [p r k]
766828
(let [{:keys [::op ps splice] :as p} (reg-resolve p)
767829
prop #(let [ret (preturn p)]
@@ -792,7 +854,7 @@
792854
(when (accept-nil? p1) (deriv (rep* p2 p2 (add-ret p1 ret nil) splice forms) x)))))))
793855

794856
(defn- op-describe [p]
795-
(let [{:keys [::op ps ks forms splice p1 rep+] :as p} (reg-resolve p)]
857+
(let [{:keys [::op ps ks forms splice p1 rep+ maybe] :as p} (reg-resolve p)]
796858
;;(prn {:op op :ks ks :forms forms :p p})
797859
(when p
798860
(case op
@@ -802,7 +864,9 @@
802864
::pcat (if rep+
803865
(list `+ rep+)
804866
(cons `cat (mapcat vector (c/or (seq ks) (repeat :_)) (c/or (seq forms) (repeat nil)))))
805-
::alt (cons `alt (mapcat vector ks forms))
867+
::alt (if maybe
868+
(list `? maybe)
869+
(cons `alt (mapcat vector ks forms)))
806870
::rep (list (if splice `+ `*) forms)))))
807871

808872
(defn- op-explain [form p path via in input]
@@ -943,6 +1007,7 @@
9431007
(if (c/or (nil? x) (coll? x))
9441008
(re-conform re (seq x))
9451009
::invalid))
1010+
(unform* [_ x] (op-unform re x))
9461011
(explain* [_ path via in x]
9471012
(if (c/or (nil? x) (coll? x))
9481013
(re-explain path via in re (seq x))
@@ -989,6 +1054,7 @@
9891054
(conform* [_ f] (if (fn? f)
9901055
(if (identical? f (validate-fn f specs *fspec-iterations*)) f ::invalid)
9911056
::invalid))
1057+
(unform* [_ f] f)
9921058
(explain* [_ path via in f]
9931059
(if (fn? f)
9941060
(let [args (validate-fn f specs 100)]
@@ -1018,7 +1084,7 @@
10181084

10191085
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; non-primitives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
10201086
(cljs.spec/def ::any (cljs.spec/spec (constantly true) :gen gen/any))
1021-
(cljs.spec/def ::kvs->map (cljs.spec/conformer #(zipmap (map ::k %) (map ::v %))))
1087+
(cljs.spec/def ::kvs->map (cljs.spec/conformer #(zipmap (map ::k %) (map ::v %)) #(map (fn [[k v]] {::k k ::v v}) %)))
10221088

10231089
(defn exercise
10241090
"generates a number (default 10) of values compatible with spec and maps conform over them,

0 commit comments

Comments
 (0)