|
16 | 16 | [cljs.spec.impl.gen :as gen]
|
17 | 17 | [clojure.string :as str]))
|
18 | 18 |
|
| 19 | +(def ^:const MAX_INT 9007199254740991) |
| 20 | + |
19 | 21 | (def ^:dynamic *recursion-limit*
|
20 | 22 | "A soft limit on how many times a branching spec (or/alt/*/opt-keys/multi-spec)
|
21 | 23 | can be recursed through during generation. After this a
|
|
27 | 29 | 21)
|
28 | 30 |
|
29 | 31 | (def ^:dynamic *coll-check-limit*
|
30 |
| - "The number of items validated in a collection spec'ed with 'coll'" |
31 |
| - 100) |
| 32 | + "The number of items validated in a collection spec'ed with 'every'" |
| 33 | + 101) |
| 34 | + |
| 35 | +(def ^:dynamic *coll-error-limit* |
| 36 | + "The number of errors reported by explain in a collection spec'ed with 'every'" |
| 37 | + 20) |
32 | 38 |
|
33 | 39 | (def ^:private ^:dynamic *instrument-enabled*
|
34 | 40 | "if false, instrumented fns call straight through"
|
|
180 | 186 | ;;(prn {:ed ed})
|
181 | 187 | (doseq [[path {:keys [pred val reason via in] :as prob}] (::problems ed)]
|
182 | 188 | (when-not (empty? in)
|
183 |
| - (print "In:" in "")) |
| 189 | + (print "In:" (pr-str in) "")) |
184 | 190 | (print "val: ")
|
185 | 191 | (pr val)
|
186 | 192 | (print " fails")
|
187 | 193 | (when-not (empty? via)
|
188 |
| - (print " spec:" (last via))) |
| 194 | + (print " spec:" (pr-str (last via)))) |
189 | 195 | (when-not (empty? path)
|
190 |
| - (print " at:" path)) |
| 196 | + (print " at:" (pr-str path))) |
191 | 197 | (print " predicate: ")
|
192 | 198 | (pr pred)
|
193 | 199 | (when reason (print ", " reason))
|
194 | 200 | (doseq [[k v] prob]
|
195 | 201 | (when-not (#{:pred :val :reason :via :in} k)
|
196 |
| - (print "\n\t" k " ") |
| 202 | + (print "\n\t" (pr-str k) " ") |
197 | 203 | (pr v)))
|
198 | 204 | (newline))
|
199 | 205 | (doseq [[k v] ed]
|
200 | 206 | (when-not (#{::problems} k)
|
201 |
| - (print k " ") |
| 207 | + (print (pr-str k) " ") |
202 | 208 | (pr v)
|
203 | 209 | (newline)))))
|
204 | 210 | (println "Success!")))
|
|
667 | 673 | (with-gen* [_ gfn] (and-spec-impl forms preds gfn))
|
668 | 674 | (describe* [_] `(and ~@forms))))
|
669 | 675 |
|
| 676 | +(defn ^:skip-wiki every-impl |
| 677 | + "Do not call this directly, use 'every'" |
| 678 | + ([form pred opts] (every-impl form pred opts nil)) |
| 679 | + ([form pred {:keys [count max-count min-count distinct gen-max gen-into ::kfn] |
| 680 | + :or {gen-max 20, gen-into []} |
| 681 | + :as opts} |
| 682 | + gfn] |
| 683 | + (let [check? #(valid? pred %) |
| 684 | + kfn (c/or kfn (fn [i v] i))] |
| 685 | + (reify |
| 686 | + Spec |
| 687 | + (conform* [_ x] |
| 688 | + (cond |
| 689 | + (c/or (not (seqable? x)) |
| 690 | + (c/and distinct (not (empty? x)) (not (apply distinct? x))) |
| 691 | + (c/and count (not= count (bounded-count (inc count) x))) |
| 692 | + (c/and (c/or min-count max-count) |
| 693 | + (not (<= (c/or min-count 0) |
| 694 | + (bounded-count (if max-count (inc max-count) min-count) x) |
| 695 | + (c/or max-count MAX_INT))))) |
| 696 | + ::invalid |
| 697 | + |
| 698 | + :else |
| 699 | + (if (indexed? x) |
| 700 | + (let [step (max 1 (long (/ (c/count x) *coll-check-limit*)))] |
| 701 | + (loop [i 0] |
| 702 | + (if (>= i (c/count x)) |
| 703 | + x |
| 704 | + (if (check? (nth x i)) |
| 705 | + (recur (c/+ i step)) |
| 706 | + ::invalid)))) |
| 707 | + (c/or (c/and (every? check? (take *coll-check-limit* x)) x) |
| 708 | + ::invalid)))) |
| 709 | + (unform* [_ x] x) |
| 710 | + (explain* [_ path via in x] |
| 711 | + (cond |
| 712 | + (not (seqable? x)) |
| 713 | + {path {:pred 'seqable? :val x :via via :in in}} |
| 714 | + |
| 715 | + (c/and distinct (not (empty? x)) (not (apply distinct? x))) |
| 716 | + {path {:pred 'distinct? :val x :via via :in in}} |
| 717 | + |
| 718 | + (c/and count (not= count (bounded-count count x))) |
| 719 | + {path {:pred `(= ~count (c/count %)) :val x :via via :in in}} |
| 720 | + |
| 721 | + (c/and (c/or min-count max-count) |
| 722 | + (not (<= (c/or min-count 0) |
| 723 | + (bounded-count (if max-count (inc max-count) min-count) x) |
| 724 | + (c/or max-count MAX_INT)))) |
| 725 | + {path {:pred `(<= ~(c/or min-count 0) (c/count %) ~(c/or max-count 'js/Number.MAX_SAFE_INTEGER)) :val x :via via :in in}} |
| 726 | + |
| 727 | + :else |
| 728 | + (apply merge |
| 729 | + (take *coll-error-limit* |
| 730 | + (keep identity |
| 731 | + (map (fn [i v] |
| 732 | + (let [k (kfn i v)] |
| 733 | + (when-not (check? v) |
| 734 | + (let [prob (explain-1 form pred (conj path k) via (conj in k) v)] |
| 735 | + prob)))) |
| 736 | + (range) x)))))) |
| 737 | + (gen* [_ overrides path rmap] |
| 738 | + (if gfn |
| 739 | + (gfn) |
| 740 | + (let [init (empty gen-into) |
| 741 | + pgen (gensub pred overrides path rmap form)] |
| 742 | + (gen/fmap |
| 743 | + #(if (vector? init) % (into init %)) |
| 744 | + (cond |
| 745 | + distinct |
| 746 | + (if count |
| 747 | + (gen/vector-distinct pgen {:num-elements count :max-tries 100}) |
| 748 | + (gen/vector-distinct pgen {:min-elements (c/or min-count 0) |
| 749 | + :max-elements (c/or max-count (max gen-max (c/* 2 (c/or min-count 0)))) |
| 750 | + :max-tries 100})) |
| 751 | + |
| 752 | + count |
| 753 | + (gen/vector pgen count) |
| 754 | + |
| 755 | + (c/or min-count max-count) |
| 756 | + (gen/vector pgen (c/or min-count 0) (c/or max-count (max gen-max (c/* 2 (c/or min-count 0))))) |
| 757 | + |
| 758 | + :else |
| 759 | + (gen/vector pgen 0 gen-max)))))) |
| 760 | + |
| 761 | + (with-gen* [_ gfn] (every-impl form pred opts gfn)) |
| 762 | + (describe* [_] `(every ~form ~@(mapcat identity opts))))))) |
| 763 | + |
670 | 764 | ;;;;;;;;;;;;;;;;;;;;;;; regex ;;;;;;;;;;;;;;;;;;;
|
671 | 765 | ;;See:
|
672 | 766 | ;; http://matt.might.net/articles/implementation-of-regular-expression-matching-in-scheme-with-derivatives/
|
|
0 commit comments