|
36 | 36 |
|
37 | 37 | (defprotocol Spec |
38 | 38 | (conform* [spec x]) |
| 39 | + (unform* [spec y]) |
39 | 40 | (explain* [spec path via in x]) |
40 | 41 | (gen* [spec overrides path rmap]) |
41 | 42 | (with-gen* [spec gfn]) |
|
107 | 108 | [spec x] |
108 | 109 | (conform* (specize spec) x)) |
109 | 110 |
|
| 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 | + |
110 | 118 | (defn form |
111 | 119 | "returns the spec as data" |
112 | 120 | [spec] |
|
405 | 413 | (recur ret ks)) |
406 | 414 | ret))) |
407 | 415 | ::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)))) |
408 | 427 | (explain* [_ path via in x] |
409 | 428 | (if-not (map? x) |
410 | 429 | {path {:pred 'map? :val x :via via :in in}} |
|
452 | 471 |
|
453 | 472 | (defn ^:skip-wiki spec-impl |
454 | 473 | "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))))) |
474 | 499 |
|
475 | 500 | (defn ^:skip-wiki multi-spec-impl |
476 | 501 | "Do not call this directly, use 'multi-spec'" |
|
492 | 517 | (conform* [_ x] (if-let [pred (predx x)] |
493 | 518 | (dt pred x form) |
494 | 519 | ::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)))))) |
495 | 523 | (explain* [_ path via in x] |
496 | 524 | (let [dv (dval x) |
497 | 525 | path (conj path dv)] |
|
539 | 567 | ::invalid |
540 | 568 | (recur (if (identical? cv v) ret (assoc ret i cv)) |
541 | 569 | (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)))))) |
542 | 580 | (explain* [_ path via in x] |
543 | 581 | (cond |
544 | 582 | (not (vector? x)) |
|
570 | 608 | "Do not call this directly, use 'or'" |
571 | 609 | [keys forms preds gfn] |
572 | 610 | (let [id (random-uuid) |
| 611 | + kps (zipmap keys preds) |
573 | 612 | cform (fn [x] |
574 | 613 | (loop [i 0] |
575 | 614 | (if (< i (count preds)) |
|
584 | 623 | (-invoke [this x] (valid? this x)) |
585 | 624 | Spec |
586 | 625 | (conform* [_ x] (cform x)) |
| 626 | + (unform* [_ [k x]] (unform (kps k) x)) |
587 | 627 | (explain* [this path via in x] |
588 | 628 | (when-not (valid? this x) |
589 | 629 | (apply merge |
|
636 | 676 | (-invoke [this x] (valid? this x)) |
637 | 677 | Spec |
638 | 678 | (conform* [_ x] (and-preds x preds forms)) |
| 679 | + (unform* [_ x] (reduce #(unform %2 %1) x (reverse preds))) |
639 | 680 | (explain* [_ path via in x] (explain-pred-list forms preds path via in x)) |
640 | 681 | (gen* [_ overrides path rmap] (if gfn (gfn) (gensub (first preds) overrides path rmap (first forms)))) |
641 | 682 | (with-gen* [_ gfn] (and-spec-impl forms preds gfn)) |
|
720 | 761 |
|
721 | 762 | (defn ^:skip-wiki maybe-impl |
722 | 763 | "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)) |
724 | 765 |
|
725 | 766 | (defn- noret? [p1 pret] |
726 | 767 | (c/or (= pret ::nil) |
|
762 | 803 | r (if (nil? p0) ::nil (preturn p0))] |
763 | 804 | (if k0 [k0 r] r))))) |
764 | 805 |
|
| 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 | + |
765 | 827 | (defn- add-ret [p r k] |
766 | 828 | (let [{:keys [::op ps splice] :as p} (reg-resolve p) |
767 | 829 | prop #(let [ret (preturn p)] |
|
792 | 854 | (when (accept-nil? p1) (deriv (rep* p2 p2 (add-ret p1 ret nil) splice forms) x))))))) |
793 | 855 |
|
794 | 856 | (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)] |
796 | 858 | ;;(prn {:op op :ks ks :forms forms :p p}) |
797 | 859 | (when p |
798 | 860 | (case op |
|
802 | 864 | ::pcat (if rep+ |
803 | 865 | (list `+ rep+) |
804 | 866 | (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))) |
806 | 870 | ::rep (list (if splice `+ `*) forms))))) |
807 | 871 |
|
808 | 872 | (defn- op-explain [form p path via in input] |
|
943 | 1007 | (if (c/or (nil? x) (coll? x)) |
944 | 1008 | (re-conform re (seq x)) |
945 | 1009 | ::invalid)) |
| 1010 | + (unform* [_ x] (op-unform re x)) |
946 | 1011 | (explain* [_ path via in x] |
947 | 1012 | (if (c/or (nil? x) (coll? x)) |
948 | 1013 | (re-explain path via in re (seq x)) |
|
989 | 1054 | (conform* [_ f] (if (fn? f) |
990 | 1055 | (if (identical? f (validate-fn f specs *fspec-iterations*)) f ::invalid) |
991 | 1056 | ::invalid)) |
| 1057 | + (unform* [_ f] f) |
992 | 1058 | (explain* [_ path via in f] |
993 | 1059 | (if (fn? f) |
994 | 1060 | (let [args (validate-fn f specs 100)] |
|
1018 | 1084 |
|
1019 | 1085 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; non-primitives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
1020 | 1086 | (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}) %))) |
1022 | 1088 |
|
1023 | 1089 | (defn exercise |
1024 | 1090 | "generates a number (default 10) of values compatible with spec and maps conform over them, |
|
0 commit comments