-
Notifications
You must be signed in to change notification settings - Fork 0
/
core.clj
94 lines (76 loc) · 2.72 KB
/
core.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
(ns shape-shifter.core
(:require [clojure.edn :as edn]
[clojure.spec.alpha :as s]
[clojure.string :as string]
[parcera.core :as parcera]
[shape-shifter.utils :as utils]))
(def ^:dynamic *wildcards*
{"$" `any?
"$fn" `(s/cat :function #(= 'fn %)
:args (s/coll-of symbol? :kind vector?)
:body any?)
"$macro-keyword" `qualified-keyword?
"$symbol" `symbol?
"$string" `string?
"$set" `set?
"$char" `char?
"$keyword" `keyword?
"$map" `map?
"$number" `number?
"$list" `list?
"$vector" `vector?
"$regex" `s/regex?})
(defn ^:private any-number-of-wildcard [symbol]
(if-let [wildcard (some-> #"\$\w*&"
(re-matches symbol)
(string/replace "&" ""))]
`(s/* ~(get *wildcards* wildcard))))
(defmulti ^:private transform (fn [[key _]] key))
(defn ^:private recursively-transform [coll-of-specs value]
(let [random-key-name (utils/random-keyword 5)
transformed-value (transform value)]
(if transformed-value
(conj coll-of-specs random-key-name transformed-value)
coll-of-specs)))
(defn ^:private pattern->collection [patterns kind]
(->> patterns
(reduce recursively-transform [])
(cons `s/cat)
list
(concat `(s/and ~kind))
list
(cons `s/spec)))
(defn ^:private ->boolean [value]
(when (or (= "true" value)
(= "false" value))
#{(boolean value)}))
(defmethod ^:private transform :list [[_ & values]]
(pattern->collection values `list?))
(defmethod ^:private transform :vector [[_ & values]]
(pattern->collection values `vector?))
(defmethod ^:private transform :set [[_ & values]]
(pattern->collection values `set?))
(defmethod ^:private transform :map [[_ & _]]
nil)
(defmethod ^:private transform :symbol [[_ value]]
(or (any-number-of-wildcard value)
(->boolean value)
(get *wildcards* value)
`#{(symbol ~value)}))
(defmethod ^:private transform :number [[_ value]]
`#{(edn/read-string ~value)})
(defmethod ^:private transform :string [[_ value]]
`#{~(string/replace value #"\"" "")})
(defmethod ^:private transform :keyword [[_ value]]
`#{~(keyword (string/replace value ":" ""))})
(defmethod ^:private transform :regex [[_ value]]
`#{~(format "#%s" value)})
(defmethod ^:private transform :whitespace [[_ _]] nil)
(defn pattern->spec
[pattern]
(let [ast (parcera/ast pattern :unhide :literals)]
(-> ast rest first transform eval)))
(comment
(-> "[$keyword& $string&]"
pattern->spec
(s/valid? [:jose :maria "banana" "uva"])))