human-ql 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ # Copyright (c) 2016 David Kellum
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you
7
+ # may not use this file except in compliance with the License. You may
8
+ # obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15
+ # implied. See the License for the specific language governing
16
+ # permissions and limitations under the License.
17
+ #++
18
+
19
+ require_relative 'setup.rb'
20
+ require 'human-ql/query_generator'
21
+
22
+ class TestQueryGenerator < Minitest::Test
23
+
24
+ TC = HumanQL::QueryParser.new
25
+ QG = HumanQL::QueryGenerator.new( parser: TC )
26
+
27
+ A = 'a'
28
+ B = 'b'
29
+ C = 'c'
30
+ D = 'd'
31
+ E = 'e'
32
+ FOO = 'FOO'
33
+
34
+ def assert_gen( expected, ast )
35
+ out = QG.generate( ast )
36
+ assert_equal( expected, out, ast )
37
+ end
38
+
39
+ def test_empty
40
+ assert_gen( nil, nil )
41
+ end
42
+
43
+ def test_simple
44
+ assert_gen( 'a', A )
45
+ end
46
+
47
+ def test_default
48
+ assert_gen( 'a b', [:and, A, B] )
49
+ end
50
+
51
+ def test_or
52
+ assert_gen( 'a or b', [:or, A, B] )
53
+ end
54
+
55
+ def test_not
56
+ assert_gen( '-a', [:not, A] )
57
+ end
58
+
59
+ def test_not_or
60
+ assert_gen( '-(a or b)', [:not, [:or, A, B]] )
61
+ end
62
+
63
+ def test_not_and
64
+ assert_gen( '-(a b)', [:not, [:and, A, B]] )
65
+ end
66
+
67
+ def test_scope
68
+ assert_gen( 'FOO:a', [FOO, A] )
69
+ assert_gen( 'FOO:(a or b)', [FOO, [:or, A, B]] )
70
+ end
71
+
72
+ def test_complex_1
73
+ assert_gen( 'a or b c or d',
74
+ [:and, [:or, A, B], [:or, C, D]] )
75
+ end
76
+
77
+ def test_complex_2
78
+ assert_gen( '"a b" -(c or d)',
79
+ [:and, [:phrase, A, B], [:not, [:or, C, D]]] )
80
+ end
81
+
82
+ def test_complex_3
83
+ assert_gen( '-(a b) or (c d) or e',
84
+ [:or, [:not, [:and, A, B]], [:and, C, D], E] )
85
+ end
86
+
87
+ end
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ # Copyright (c) 2016 David Kellum
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you
7
+ # may not use this file except in compliance with the License. You may
8
+ # obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15
+ # implied. See the License for the specific language governing
16
+ # permissions and limitations under the License.
17
+ #++
18
+
19
+ require_relative 'setup.rb'
20
+
21
+ require 'human-ql/query_parser'
22
+ require 'human-ql/tree_normalizer'
23
+
24
+ class TestingQueryParser < HumanQL::QueryParser
25
+ def initialize
26
+ super( scopes: [ 'FOO', ignorecase: true ],
27
+ verbose: ARGV.include?( '--verbose' ) )
28
+ end
29
+ end
30
+
31
+ class TestQueryParser < Minitest::Test
32
+ TC = TestingQueryParser.new
33
+ DN = HumanQL::TreeNormalizer.new( not_scope: true )
34
+
35
+ def test_norm_prefix
36
+ assert_equal( '-', TC.norm_prefix( '-' ) )
37
+ assert_equal( '- ', TC.norm_prefix( '- ' ) )
38
+ assert_equal( 'a-b', TC.norm_prefix( 'a-b' ) )
39
+ assert_equal( '- a', TC.norm_prefix( '-a' ) )
40
+ assert_equal( '- ab', TC.norm_prefix( '-ab' ) )
41
+ assert_equal( 'a - b', TC.norm_prefix( 'a -b' ) )
42
+ assert_equal( 'a - bc', TC.norm_prefix( 'a -bc' ) )
43
+ end
44
+
45
+ def test_norm_quote
46
+ assert_equal( ' " ', TC.norm_infix( '"' ) )
47
+ assert_equal( ' " ', TC.norm_infix( '" ' ) )
48
+ assert_equal( 'a " b', TC.norm_infix( 'a"b' ) )
49
+ assert_equal( ' " a', TC.norm_infix( '"a' ) )
50
+ assert_equal( ' " ab " ', TC.norm_infix( '"ab"' ) )
51
+ assert_equal( 'a " b " ', TC.norm_infix( 'a "b"' ) )
52
+ assert_equal( 'a " bc " ', TC.norm_infix( 'a "bc"' ) )
53
+ end
54
+
55
+ def test_norm_parens_split
56
+ assert_equal( ' ( ', TC.norm_infix( '(' ) )
57
+ assert_equal( ' ( ', TC.norm_infix( '( ' ) )
58
+ assert_equal( ' ( a ) ', TC.norm_infix( '(a)' ) )
59
+ assert_equal( ' ( ab ) ', TC.norm_infix( '(ab)' ) )
60
+ assert_equal( 'a ( b ) ', TC.norm_infix( 'a (b)' ) )
61
+ assert_equal( 'a ( bc ) ', TC.norm_infix( 'a (bc)' ) )
62
+ assert_equal( 'a ( b ) ', TC.norm_infix( 'a(b)' ) )
63
+ end
64
+
65
+ def test_norm_space
66
+ assert_equal( 'foo', TC.norm_space( 'foo' ) )
67
+ assert_equal( 'foo', TC.norm_space( ' foo' ) )
68
+ assert_equal( 'foo', TC.norm_space( ' foo ' ) )
69
+ assert_equal( 'foo bar', TC.norm_space( " foo\t bar\r " ) )
70
+ end
71
+
72
+ def test_norm_scope
73
+ assert_equal( 'FOO: ', TC.norm_scope( 'FOO:' ) )
74
+ assert_equal( ' ', TC.norm_scope( ':' ) )
75
+ assert_equal( ' ', TC.norm_scope( ' :' ) )
76
+ assert_equal( ' after', TC.norm_scope( ':after' ) )
77
+ assert_equal( ' after', TC.norm_scope( ' :after' ) )
78
+
79
+ assert_equal( 'FOO: bar', TC.norm_scope( 'FOO:bar' ) )
80
+ assert_equal( 'FOO: bar', TC.norm_scope( 'FOO: bar' ) )
81
+ assert_equal( 'FOO: bar', TC.norm_scope( 'FOO : bar' ) )
82
+
83
+ assert_equal( ' FOO: bar', TC.norm_scope( ' FOO:bar' ) )
84
+ assert_equal( 'a FOO: bar', TC.norm_scope( 'a FOO:bar' ) )
85
+ end
86
+
87
+ def test_norm_scope_undefined
88
+ assert_equal( 'other bar', TC.norm_scope( 'other : bar' ) )
89
+ assert_equal( 'other bar', TC.norm_scope( 'other: bar' ) )
90
+ assert_equal( 'other bar', TC.norm_scope( 'other:bar' ) )
91
+
92
+ assert_equal( ' other bar', TC.norm_scope( ' other: bar' ) )
93
+ assert_equal( 'a other bar', TC.norm_scope( 'a other: bar' ) )
94
+ end
95
+
96
+ def test_normalize
97
+ assert_equal( nil, TC.normalize( nil ) )
98
+ assert_equal( nil, TC.normalize( '' ) )
99
+ assert_equal( nil, TC.normalize( ' ' ) )
100
+ assert_equal( 'a ( bc | d )', TC.normalize( 'a (bc|d)' ) )
101
+ assert_equal( '- ( a | b )', TC.normalize( '-(a|b)' ) )
102
+ end
103
+
104
+ A = 'a'
105
+ B = 'b'
106
+ C = 'c'
107
+ D = 'd'
108
+ E = 'e'
109
+ FOO = 'FOO'
110
+
111
+ def assert_parse( expected_tree, input )
112
+ out = TC.parse( input )
113
+ out = DN.normalize( out )
114
+ assert_equal( expected_tree, out, input )
115
+ end
116
+
117
+ def test_parse_basic_1
118
+ assert_parse( 'a', 'a' )
119
+ end
120
+
121
+ def test_parse_basic_2
122
+ assert_parse( [ :and, A, B ], 'a b' )
123
+ end
124
+
125
+ def test_parse_scope_basic
126
+ assert_parse( [ FOO, A ], 'FOO:a' )
127
+ assert_parse( [ FOO, A ], 'FOO: a' )
128
+ assert_parse( [ FOO, A ], 'FOO : a' )
129
+ assert_parse( [ :and, [ FOO, A ], [ FOO, B ] ], 'FOO : a FOO:b' )
130
+ assert_parse( [ :or, [ FOO, A ], [ FOO, B ] ], 'FOO : a | FOO:b' )
131
+ end
132
+
133
+ def test_parse_scope_complex
134
+ assert_parse( [ FOO, [ :phrase, A, B ] ], 'FOO:"a b"' )
135
+ assert_parse( [ FOO, [ :and, A, B ] ], 'FOO:(a b)' )
136
+ assert_parse( [ FOO, [ :or, A, B ] ], 'FOO:(a|b)' )
137
+ end
138
+
139
+ def test_parse_scope_not
140
+ assert_parse( [ FOO, [ :not, A ] ], 'FOO:-a' )
141
+ end
142
+
143
+ def test_parse_not_scope_paren
144
+ assert_parse( [ :not, [ FOO, A ] ], '-(FOO:a)' )
145
+ end
146
+
147
+ def test_parse_not_scope
148
+ assert_parse( [ FOO, A ], '- FOO:a' )
149
+ assert_parse( [ :and, [ :not, FOO ], A ], '-FOO:a' )
150
+ end
151
+
152
+ def test_parse_phrase
153
+ assert_parse( [ :phrase, A, B ], '"a b"' )
154
+ end
155
+
156
+ def test_parse_empty
157
+ assert_parse( nil, '' )
158
+ end
159
+
160
+ def test_parse_empty_phrase
161
+ assert_parse( nil, '"' )
162
+ assert_parse( nil, '""' )
163
+ end
164
+
165
+ def test_parse_not
166
+ assert_parse( [ :not, A ], '-a' )
167
+ end
168
+
169
+ def test_parse_not_noop
170
+ assert_parse( nil, '-' )
171
+ end
172
+
173
+ def test_parse_or
174
+ assert_parse( [ :or, A, B ], 'a|b' )
175
+ end
176
+
177
+ def test_parse_or_noop_0
178
+ assert_parse( nil, '|' )
179
+ assert_parse( nil, '||' )
180
+ assert_parse( nil, '|&' )
181
+ assert_parse( nil, '&|' )
182
+ assert_parse( nil, '-|' )
183
+ assert_parse( nil, '|-' )
184
+ end
185
+
186
+ def test_noop_trailing_not
187
+ assert_parse( A, 'a -' )
188
+ end
189
+
190
+ def test_noop_trailing_or
191
+ assert_parse( [:and, A, B], 'a b |' )
192
+ end
193
+
194
+ def test_noop_leading_or
195
+ assert_parse( [:and, A, B], '| a b' )
196
+ end
197
+
198
+ def test_parse_or_noop_1
199
+ assert_parse( A, '|a' )
200
+ end
201
+
202
+ def test_parse_or_noop_2
203
+ assert_parse( A, 'a|' )
204
+ end
205
+
206
+ def test_parse_and_noop_1
207
+ assert_parse( A, '&a' )
208
+ end
209
+
210
+ def test_parse_and_noop_2
211
+ assert_parse( A, 'a&' )
212
+ end
213
+
214
+ def test_parse_not_phrase
215
+ assert_parse( [ :not, [ :phrase, A, B ] ], '-"a b"' )
216
+ end
217
+
218
+ def test_parse_parens_empty
219
+ assert_parse( nil, '()' )
220
+ end
221
+
222
+ def test_parse_adj_parens
223
+ assert_parse( [:and, A, B], '(a)b' )
224
+ assert_parse( [:and, [:not, 'a-' ], B], '-(a-)b' )
225
+ assert_parse( [:and, [:not, A], B], '-(a)b' )
226
+ end
227
+
228
+ def test_parse_parens_0
229
+ assert_parse( A, '(a)' )
230
+ end
231
+
232
+ def test_parse_parens_1
233
+ assert_parse( [ :and, A, B ], '(a b)' )
234
+ end
235
+
236
+ def test_parse_parens_2
237
+ assert_parse( [ :and, [ :or, A, B ], C ], '(a|b) c' )
238
+ end
239
+
240
+ def test_parse_parens_3
241
+ assert_parse( [ :and, C, [ :or, A, B ] ], 'c (a|b)' )
242
+ end
243
+
244
+ def test_parse_parens_4
245
+ assert_parse( [ :and, D, [ :or, A, B, C ] ], 'd (a|b|c)' )
246
+ end
247
+
248
+ def test_parse_parens_5
249
+ assert_parse( [ :and, [ :or, A, B ], [ :or, C, D ] ], '(a|b) (c|d)' )
250
+ assert_parse( [ :and, [ :or, A, B ], [ :or, C, D ] ], '((a|b) (c|d))' )
251
+ end
252
+
253
+ def test_parse_parens_6
254
+ assert_parse( [ :or, [ :and, A, B ], [ :and, C, D ] ], '((a&b) | (c&d))' )
255
+ assert_parse( [ :or, [ :and, A, B ], [ :not, [ :and, C, D ] ] ],
256
+ '((a&b) | -(c&d))' )
257
+ assert_parse( [ :or, [ :not, [ :and, A, B ] ], [ :and, C, D ], E ],
258
+ '(-(a&b) | (c&d) | e )' )
259
+ end
260
+
261
+ def test_parens_and_quotes
262
+ assert_parse( [:phrase, 'a' ], '"(a)"' )
263
+ assert_parse( [ :or, [ :and, A, B ], [ :and, C, D ] ], '((a&b) |"(c&d))' )
264
+ assert_parse( [ :or, [ :and, A, B ], [ :and, C, D ] ], '((a&b) |(c&d)")' )
265
+ assert_parse( [ :and, [ :or, [ :and, A, B ], C ], D ], '((a&b) |(c&"d))' )
266
+ assert_parse( [ :or, [ :and, A, B ], [ :and, [:phrase, C], D ] ],
267
+ '((a&b) |("c))"&d))' )
268
+ end
269
+
270
+ def test_parse_precedence_1
271
+ assert_parse( [ :and, [ :or, A, B ], C ], 'a | b c' )
272
+ end
273
+
274
+ def test_parse_precedence_2
275
+ assert_parse( [ :and, A, [ :or, B, C ] ], 'a b | c' )
276
+ end
277
+
278
+ def test_parse_precedence_3
279
+ assert_parse( [ :and, [ :or, A, [ :not, B ] ], C ], 'a | - b c' )
280
+ end
281
+
282
+ def test_parse_precedence_4_explicit
283
+ assert_parse( [ :and, [ :or, [ :not, A ], B ], C ], '-a | b & c' )
284
+ end
285
+
286
+ def test_parse_precedence_4_implied
287
+ assert_parse( [ :and, [ :or, [ :not, A ], B ], C ], '-a | b c' )
288
+ end
289
+
290
+ def test_parse_precedence_5_explicit
291
+ assert_parse( [ :and, [ :or, A, B ], [ :not, C ] ], 'a | b AND -c' )
292
+ end
293
+
294
+ def test_parse_precedence_5_implied
295
+ assert_parse( [ :and, [ :or, A, B ], [ :not, C ] ], 'a | b -c' )
296
+ end
297
+
298
+ def test_parse_precedence_6
299
+ assert_parse( [ :and, [ :or, A, B ], [ :not, C ], D ], 'a | b -c d' )
300
+ end
301
+
302
+ def test_parse_empty_not
303
+ assert_parse( A, 'a -""' )
304
+ end
305
+
306
+ end
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ # Copyright (c) 2016 David Kellum
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License"); you
7
+ # may not use this file except in compliance with the License. You may
8
+ # obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15
+ # implied. See the License for the specific language governing
16
+ # permissions and limitations under the License.
17
+ #++
18
+
19
+ require_relative 'setup.rb'
20
+
21
+ require 'human-ql/tree_normalizer'
22
+
23
+ class TestTreeNormalizer < Minitest::Test
24
+ DN = HumanQL::TreeNormalizer.new
25
+ UN = HumanQL::TreeNormalizer.new( unconstrained_not: false )
26
+ LN = HumanQL::TreeNormalizer.new( unconstrained_not: false,
27
+ scope_at_top_only: true,
28
+ scope_and_only: true )
29
+
30
+ A = 'a'
31
+ B = 'b'
32
+ C = 'c'
33
+ D = 'd'
34
+ E = 'e'
35
+
36
+ S1 = 'S1'
37
+ S2 = 'S2'
38
+
39
+ def assert_norm( normalizer, expected, input )
40
+ output = normalizer.normalize( input )
41
+ assert_equal( expected, output, input )
42
+ end
43
+
44
+ def assert_norm_identity( normalizer, id )
45
+ assert_norm( normalizer, id, id )
46
+ end
47
+
48
+ def test_basic_norm_0
49
+ assert_norm( DN, nil, nil )
50
+ assert_norm( DN, nil, [ :and ] )
51
+ end
52
+
53
+ def test_basic_norm_1
54
+ assert_norm( DN, A, A )
55
+ end
56
+
57
+ def test_basic_norm_2
58
+ assert_norm( DN, A, [ :or, [ :and ], A ] )
59
+ end
60
+
61
+ def test_basic_norm_3
62
+ assert_norm( DN, [ :and, A, B ], [ :and, [ :and, A, B ] ] )
63
+ end
64
+
65
+ def test_not
66
+ assert_norm( DN, [ :not, A ], [ :not, A ] )
67
+ assert_norm( DN, [ :not, A ], [ :not, A, B ] )
68
+ end
69
+
70
+ def test_nested_not
71
+ assert_norm( DN, nil, [ :not, [ :not, A ] ] )
72
+ assert_norm( DN, [ :not, B], [ :not, [ :and, [ :not, A ], B ] ])
73
+ end
74
+
75
+ def test_scope
76
+ assert_norm( DN, [ S1, A ], [ S1, A ] )
77
+ assert_norm( DN, [ S1, A ], [ S1, A, B ] )
78
+ end
79
+
80
+ def test_nested_scope
81
+ assert_norm( DN, [ S2, A ], [ S2, [ :and, A, [ S1, B ] ] ] )
82
+ assert_norm( DN, [ S2, [ :or, A, C ] ], [ S2, [ :or, A, [ S1, B ], C ] ] )
83
+ end
84
+
85
+ def test_nested_same_scope
86
+ assert_norm( DN, [ S2, [ :and, A, B ] ],
87
+ [ S2, [ :and, A, [ S2, B ] ] ] )
88
+ end
89
+
90
+ def test_scope_not
91
+ assert_norm_identity( DN, [ S1, [ :not, A ] ] )
92
+ end
93
+
94
+ def test_not_scope
95
+ # Should be inverted
96
+ assert_norm( DN, [ S1, [ :not, A ] ],
97
+ [ :not, [ S1, A ] ] )
98
+ assert_norm( DN, [ S1, [ :not, A ] ],
99
+ [ :not, [ :and, [ S1, A ] ] ] )
100
+ assert_norm( DN, [ :and, [ S1, [ :not, A ] ], B ],
101
+ [ :and, [ :not, [ S1, A ] ], B ] )
102
+ end
103
+
104
+ def test_not_scope_limited
105
+ assert_norm( LN, [ :and, A, [ S1, [ :not, B ] ] ],
106
+ [ :and, A, [ :not, [ S1, B ] ] ] )
107
+ assert_norm( LN, [ :and, A, [ S1, [ :not, B ] ] ],
108
+ [ :and, A, [ :not, [:and, [ S1, B ] ] ] ] )
109
+ end
110
+
111
+ def test_not_scope_indirect
112
+ assert_norm( DN, [ :not, B ],
113
+ [ :not, [ :or, [ S1, A ], B ] ] )
114
+ end
115
+
116
+ def test_unconstrained_scope_not
117
+ assert_norm( UN, nil, [ S1, [ :not, A ] ] )
118
+ end
119
+
120
+ def test_unconstrained_not_scope
121
+ assert_norm( UN, nil, [ :not, [ S1, A ] ] )
122
+ end
123
+
124
+ def test_unconstrained_not
125
+ assert_norm( UN, nil, [ :not, A ] )
126
+ assert_norm( UN, B, [ :or, [ :not, A ], B ] )
127
+ end
128
+
129
+ def test_unconstrained_not_complex
130
+ assert_norm( UN, nil, [ :and, [ :not, A ], [ :not, A ] ] )
131
+ assert_norm( UN, B, [ :and, [ :not, A ], [ :or, [ :not, A ], B ] ] )
132
+ end
133
+
134
+ def test_constrained_not
135
+ assert_norm_identity( UN, [ :and, [ :not, A ], B ] )
136
+ assert_norm_identity( UN, [ :and, [ :not, A ], [ :phrase, B, C ] ] )
137
+ assert_norm_identity( UN, [ :and, A, [ :or, [ :not, B ], [ :not, C ] ] ] )
138
+ assert_norm_identity( UN, [ :and, [ :or, A, D ],
139
+ [ :or, [ :not, B ], [ :not, C ] ] ] )
140
+ assert_norm_identity( UN, [ :and, [ :not, A ],
141
+ [ :or, [ :and, [ :not, B ], C ], D ] ] )
142
+ end
143
+
144
+ def test_limited_scope
145
+ assert_norm_identity( LN, [ S1, A ] )
146
+ assert_norm_identity( LN, [ :and, [ S1, A ], B ] )
147
+ assert_norm_identity( LN, [ :and, A, [ S1, B ] ] )
148
+ assert_norm( LN, B, [ :or, [ S1, A ], B ] )
149
+ assert_norm( LN, [ :and, B, C ],
150
+ [ :and, [ :or, [ S1, A ], B ], C ] )
151
+ end
152
+
153
+ end