/
parse_by_transform.clj
76 lines (67 loc) · 3.17 KB
/
parse_by_transform.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
(ns fn2api-core.signatures-spec.parse-by-transform
(:require [fn2api-core.helper.defn-args]
[fn2api-core.helper.spec :refer [transform]]
[fn2api-core.signatures :refer [fn->type]]
[clojure.spec.alpha :as s]
[spec-tools.core :as st]))
;; spec for fspecs for transformation via conform+unform
(s/def ::spec (s/or :spec s/spec?
:kw keyword? ;; TODO this should be namespaced
:fn (and symbol? #(fn? (var-get (resolve %))))
;;TODO implement compound
:other any? ;; for debugging purposes
))
(s/def ::name+spec (s/cat :name keyword? :spec ::spec))
(s/def ::name+spec* (s/cat :spec ::spec))
(s/def ::argv #{'clojure.core/sequential?})
(s/def ::spec-trivial
(s/cat :cat #{'clojure.spec.alpha/cat}
:args (s/* ::name+spec)))
(s/def ::spec-trivial*
(s/and (s/conformer #() #(zipmap [:args] %))
(s/cat :args (s/and (s/conformer #() vec)
(s/* ::name+spec*)))))
(s/def ::spec-with-variadic-argv
(s/cat :cat #{'clojure.spec.alpha/cat}
:args (s/* ::name+spec)
:argv (s/cat :name keyword? :spec ::argv)))
(s/def ::spec-with-variadic-argv*
(s/and (s/conformer #() #(zipmap [:args :argv] %))
(s/cat :args (s/and (s/conformer #() vec)
(s/* ::name+spec*))
:argv (s/cat :spec ::argv))))
(s/def ::spec-with-map+argv
(s/cat :cat #{'clojure.spec.alpha/cat}
:args (s/* ::name+spec)
:kwargs ::name+spec ;; TODO not necessarily at the last position
:argv (s/cat :name keyword? :spec ::argv))) ;; TODO should be optional
(s/def ::spec-with-map+argv*
(s/and (s/conformer #() #(zipmap [:args :kwargs :argv] %))
(s/cat :args (s/and (s/conformer #() vec)
(s/* ::name+spec*))
:kwargs (s/and (s/conformer #() first)
::name+spec*)
:argv (s/cat :spec ::argv))))
(s/def ::spec-with-explicit-map
(s/cat :cat #{'clojure.spec.alpha/cat}
:args (s/* ::name+spec)
:kwargs ::name+spec))
(s/def ::spec-with-explicit-map*
(s/and (s/conformer #() #(zipmap [:args :kwargs] %))
(s/cat :args (s/and (s/conformer #() vec)
(s/* ::name+spec*))
:kwargs (s/and (s/conformer #() first)
::name+spec*))))
(defn fn->specs
"get specs of fn:var by tranformation via conform+unform"
[fn:var]
(let [type->s_conform+s_unform {:trivial [::spec-trivial ::spec-trivial*]
:with-variadic-argv [::spec-with-variadic-argv ::spec-with-variadic-argv*]
:with-map+argv [::spec-with-map+argv ::spec-with-map+argv*]
:with-explicit-map [::spec-with-explicit-map ::spec-with-explicit-map*]}
fn-type (fn->type fn:var)
[s_conform s_unform] (get type->s_conform+s_unform fn-type)]
(assert (and s_conform s_unform)
(str "fn->spec not defined for " fn-type))
(transform s_conform s_unform
(s/form (:args (s/get-spec fn:var))))))