forked from replikativ/datalog-parser
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser_test.cljc
121 lines (96 loc) · 9.07 KB
/
parser_test.cljc
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
(ns datalog.parser-test
(:require #?(:cljs [cljs.test :refer-macros [are deftest]]
:clj [clojure.test :refer [are deftest]])
[datalog.parser :as parser]
[datalog.parser.test.util]))
(deftest validation
(are [q result] (= result (parser/parse q))
'[:find ?e
:in $ ?fname ?lname
:keys foo
:where [?e :user/firstName ?fname]
[?e :user/lastName ?lname]]
'#datalog.parser.type.Query{:qfind #datalog.parser.type.FindRel{:elements [#datalog.parser.type.Variable{:symbol ?e}]}, :qwith nil, :qin [#datalog.parser.type.BindScalar{:variable #datalog.parser.type.SrcVar{:symbol $}} #datalog.parser.type.BindScalar{:variable #datalog.parser.type.Variable{:symbol ?fname}} #datalog.parser.type.BindScalar{:variable #datalog.parser.type.Variable{:symbol ?lname}}], :qwhere [#datalog.parser.type.Pattern{:source #datalog.parser.type.DefaultSrc{}, :pattern [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Constant{:value :user/firstName} #datalog.parser.type.Variable{:symbol ?fname}]} #datalog.parser.type.Pattern{:source #datalog.parser.type.DefaultSrc{}, :pattern [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Constant{:value :user/lastName} #datalog.parser.type.Variable{:symbol ?lname}]}], :qlimit nil, :qoffset nil, :qreturnmaps #datalog.parser.type.ReturnMaps{:mapping-type :keys, :mapping-keys (#datalog.parser.type.MappingKey{:mapping-key foo})}}
'[:find ?e
:in $ ?fname ?lname
:strs foo
:where [?e :user/firstName ?fname]
[?e :user/lastName ?lname]]
'#datalog.parser.type.Query{:qfind #datalog.parser.type.FindRel{:elements [#datalog.parser.type.Variable{:symbol ?e}]}, :qwith nil, :qin [#datalog.parser.type.BindScalar{:variable #datalog.parser.type.SrcVar{:symbol $}} #datalog.parser.type.BindScalar{:variable #datalog.parser.type.Variable{:symbol ?fname}} #datalog.parser.type.BindScalar{:variable #datalog.parser.type.Variable{:symbol ?lname}}], :qwhere [#datalog.parser.type.Pattern{:source #datalog.parser.type.DefaultSrc{}, :pattern [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Constant{:value :user/firstName} #datalog.parser.type.Variable{:symbol ?fname}]} #datalog.parser.type.Pattern{:source #datalog.parser.type.DefaultSrc{}, :pattern [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Constant{:value :user/lastName} #datalog.parser.type.Variable{:symbol ?lname}]}], :qlimit nil, :qoffset nil, :qreturnmaps #datalog.parser.type.ReturnMaps{:mapping-type :strs, :mapping-keys (#datalog.parser.type.MappingKey{:mapping-key foo})}}
'[:find ?e
:in $ ?fname ?lname
:syms foo
:where [?e :user/firstName ?fname]
[?e :user/lastName ?lname]]
'#datalog.parser.type.Query{:qfind #datalog.parser.type.FindRel{:elements [#datalog.parser.type.Variable{:symbol ?e}]}, :qwith nil, :qin [#datalog.parser.type.BindScalar{:variable #datalog.parser.type.SrcVar{:symbol $}} #datalog.parser.type.BindScalar{:variable #datalog.parser.type.Variable{:symbol ?fname}} #datalog.parser.type.BindScalar{:variable #datalog.parser.type.Variable{:symbol ?lname}}], :qwhere [#datalog.parser.type.Pattern{:source #datalog.parser.type.DefaultSrc{}, :pattern [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Constant{:value :user/firstName} #datalog.parser.type.Variable{:symbol ?fname}]} #datalog.parser.type.Pattern{:source #datalog.parser.type.DefaultSrc{}, :pattern [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Constant{:value :user/lastName} #datalog.parser.type.Variable{:symbol ?lname}]}], :qlimit nil, :qoffset nil, :qreturnmaps #datalog.parser.type.ReturnMaps{:mapping-type :syms, :mapping-keys (#datalog.parser.type.MappingKey{:mapping-key foo})}}
'{:find [?e]
:in [$ ?fname ?lname]
:keys [foo]
:where [[?e :user/firstName ?fname]
[?e :user/lastName ?lname]]}
'#datalog.parser.type.Query{:qfind #datalog.parser.type.FindRel{:elements [#datalog.parser.type.Variable{:symbol ?e}]}, :qwith nil, :qin [#datalog.parser.type.BindScalar{:variable #datalog.parser.type.SrcVar{:symbol $}} #datalog.parser.type.BindScalar{:variable #datalog.parser.type.Variable{:symbol ?fname}} #datalog.parser.type.BindScalar{:variable #datalog.parser.type.Variable{:symbol ?lname}}], :qwhere [#datalog.parser.type.Pattern{:source #datalog.parser.type.DefaultSrc{}, :pattern [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Constant{:value :user/firstName} #datalog.parser.type.Variable{:symbol ?fname}]} #datalog.parser.type.Pattern{:source #datalog.parser.type.DefaultSrc{}, :pattern [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Constant{:value :user/lastName} #datalog.parser.type.Variable{:symbol ?lname}]}], :qlimit nil, :qoffset nil, :qreturnmaps #datalog.parser.type.ReturnMaps{:mapping-type :keys, :mapping-keys (#datalog.parser.type.MappingKey{:mapping-key foo})}}
'{:find [[?e ?fname]]
:keys [foo]
:in [$ ?fname ?lname]
:where [[?e :user/firstName ?fname]
[?e :user/lastName ?lname]]}
#datalog.parser.type.Query{:qfind #datalog.parser.type.FindTuple{:elements [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Variable{:symbol ?fname}]}, :qwith nil, :qin [#datalog.parser.type.BindScalar{:variable #datalog.parser.type.SrcVar{:symbol $}} #datalog.parser.type.BindScalar{:variable #datalog.parser.type.Variable{:symbol ?fname}} #datalog.parser.type.BindScalar{:variable #datalog.parser.type.Variable{:symbol ?lname}}], :qwhere [#datalog.parser.type.Pattern{:source #datalog.parser.type.DefaultSrc{}, :pattern [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Constant{:value :user/firstName} #datalog.parser.type.Variable{:symbol ?fname}]} #datalog.parser.type.Pattern{:source #datalog.parser.type.DefaultSrc{}, :pattern [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Constant{:value :user/lastName} #datalog.parser.type.Variable{:symbol ?lname}]}], :qlimit nil, :qoffset nil, :qreturnmaps #datalog.parser.type.ReturnMaps{:mapping-type :keys, :mapping-keys (#datalog.parser.type.MappingKey{:mapping-key foo})}}
))
(deftest validation-fails
(are [q msg] (thrown-msg? msg (parser/parse q))
'[:find ?e :where [?x]]
"Query for unknown vars: [?e]"
'[:find ?e :with ?f :where [?e]]
"Query for unknown vars: [?f]"
'[:find ?e ?x ?t :in ?x :where [?e]]
"Query for unknown vars: [?t]"
'[:find ?x ?e :with ?y ?e :where [?x ?e ?y]]
":find and :with should not use same variables: [?e]"
'[:find ?e :in $ $ ?x :where [?e]]
"Vars used in :in should be distinct"
'[:find ?e :in ?x $ ?x :where [?e]]
"Vars used in :in should be distinct"
'[:find ?e :in $ % ?x % :where [?e]]
"Vars used in :in should be distinct"
'[:find ?n :with ?e ?f ?e :where [?e ?f ?n]]
"Vars used in :with should be distinct"
'[:find ?x :where [$1 ?x]]
"Where uses unknown source vars: [$1]"
'[:find ?x :in $1 :where [$2 ?x]]
"Where uses unknown source vars: [$2]"
'[:find ?e :where (rule ?e)]
"Missing rules var '%' in :in"
'[:find ?e :where [?e] :limit [42]]
"Cannot parse :limit, expected java.lang.Long"
'[:find ?e :where [?e] :offset [666]]
"Cannot parse :offset, expected java.lang.Long"
'[:find ?e :keys foo bar :where [?e] :offset 666]
"Count of :keys/:strs/:syms must match count of :find"
'[:find ?e ?f :keys foo :where [?e ?f] :offset 666]
"Count of :keys/:strs/:syms must match count of :find"
'[:find [?e ?f] :keys foo bar :where [?e ?f] :offset 666]
"Count of :keys/:strs/:syms must match count of :find"
'[:find ?e :strs '(foo bar) :keys '("foo" "bar") :where [?e] :offset 666]
"Only one of these three options is allowed: :keys :strs :syms"
'[:find ?e :in $ ?src :where [?e :attr/val ?v] [?e :attr/src ?src]]
"Potentially expensive ordering of clauses! Clause introduces two or more previously unseen variables: [?e :attr/val ?v]"))
(comment
(let [parse-res (parser/parse '[:find ?e :in $ ?src :where [?e :attr/val ?v] [?e :attr/src ?src]])
clauses (:qwhere parse-res)
input-vars (datalog.parser.impl/collect-type Variable (:qin parse-res) #{})]
(loop [seen-vars input-vars
clause (first clauses)
clauses (rest clauses)
clause->new-vars []]
(if (nil? clause)
clause->new-vars
(let [pattern (:pattern clause)
_ (clojure.pprint/pprint pattern)
new-vars (clojure.set/difference (datalog.parser.impl/collect-type Variable pattern #{})
seen-vars)]
(recur
(into seen-vars new-vars)
(first clauses)
(rest clauses)
(conj clause->new-vars [clause new-vars]))))))
)