node_query 1.0.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/Gemfile.lock +9 -7
- data/README.md +437 -3
- data/lib/node_query/adapter.rb +6 -0
- data/lib/node_query/compiler/attribute.rb +1 -1
- data/lib/node_query/compiler/basic_selector.rb +1 -1
- data/lib/node_query/compiler/comparable.rb +26 -11
- data/lib/node_query/compiler/expression.rb +4 -10
- data/lib/node_query/compiler/expression_list.rb +11 -3
- data/lib/node_query/compiler/identifier.rb +2 -2
- data/lib/node_query/compiler/regexp.rb +7 -10
- data/lib/node_query/compiler/selector.rb +13 -4
- data/lib/node_query/compiler/string.rb +19 -0
- data/lib/node_query/compiler.rb +0 -1
- data/lib/node_query/helper.rb +47 -24
- data/lib/node_query/node_rules.rb +174 -0
- data/lib/node_query/parser_adapter.rb +6 -2
- data/lib/node_query/version.rb +1 -1
- data/lib/node_query.rb +37 -10
- data/lib/node_query_lexer.rex +2 -5
- data/lib/node_query_lexer.rex.rb +287 -0
- data/lib/node_query_parser.racc.rb +302 -0
- data/lib/node_query_parser.y +3 -4
- data/node_query.gemspec +3 -3
- data/sig/node_query/adapter.rbs +11 -0
- data/sig/node_query.rbs +6 -4
- metadata +11 -8
- data/lib/node_query/compiler/evaluated_value.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c7363cd71167f293f046d95014611ceb1f695c6639036a3cc0e202ab0ae5b2b
|
4
|
+
data.tar.gz: 161c5784daae011699a1300b0cedc2f18bdc7235f71b7e89e1b1dce41caef71a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 356e94651bf61cb5c244399bbfe0aa353aafe3a3506fc7e6b1efba9cc4c853a0b963850c9d64e9b9de26dd3986f10967b2752a3dc7ec627dfd7466ddcf9a70f8
|
7
|
+
data.tar.gz: 0fdacf2a86292f60968a02054ce43e02a131d3a1da7c528d1e6df2e8279afd5de19afe714a80bd1b9ab17143c433b4e219c7be2cf8b55e3f28e45ff82a25d274
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 1.3.0 (2022-09-13)
|
4
|
+
|
5
|
+
* Rename `NodeQuery#parse` to `NodeQuery#query_nodes`
|
6
|
+
* `NodeQuery#query_ndoes` accepts `including_self` argument
|
7
|
+
* `NodeQuery#query_ndoes` supports both nql and rules
|
8
|
+
* Add `NodeQuery#match_node?`
|
9
|
+
* Add `NdoeRules`
|
10
|
+
* Drop `EvaluatedValue`, use `String` instead
|
11
|
+
* Write better test cases
|
12
|
+
|
13
|
+
## 1.2.0 (2022-07-01)
|
14
|
+
|
15
|
+
* Rename `NodeQuery.get_adapter` to `NodeQuery.adapter`
|
16
|
+
* Use generic type in rbs
|
17
|
+
* Fix `Compiler::Array` to `Compiler::ArrayValue`
|
18
|
+
|
19
|
+
## 1.1.0 (2022-06-27)
|
20
|
+
|
21
|
+
* Support `*` in attribute key
|
22
|
+
* Add new Adapter method `is_node?`
|
23
|
+
|
3
24
|
## 1.0.0 (2022-06-26)
|
4
25
|
|
5
26
|
* Abstract from synvert-core
|
data/Gemfile.lock
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
node_query (1.
|
5
|
-
activesupport
|
4
|
+
node_query (1.3.0)
|
5
|
+
activesupport (< 7.0.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activesupport (
|
10
|
+
activesupport (6.1.7)
|
11
11
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
12
12
|
i18n (>= 1.6, < 2)
|
13
13
|
minitest (>= 5.1)
|
14
14
|
tzinfo (~> 2.0)
|
15
|
+
zeitwerk (~> 2.3)
|
15
16
|
ast (2.4.2)
|
16
17
|
coderay (1.1.3)
|
17
18
|
concurrent-ruby (1.1.10)
|
@@ -35,14 +36,14 @@ GEM
|
|
35
36
|
guard (~> 2.1)
|
36
37
|
guard-compat (~> 1.1)
|
37
38
|
rspec (>= 2.99.0, < 4.0)
|
38
|
-
i18n (1.
|
39
|
+
i18n (1.12.0)
|
39
40
|
concurrent-ruby (~> 1.0)
|
40
41
|
listen (3.7.1)
|
41
42
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
42
43
|
rb-inotify (~> 0.9, >= 0.9.10)
|
43
44
|
lumberjack (1.2.8)
|
44
45
|
method_source (1.0.0)
|
45
|
-
minitest (5.16.
|
46
|
+
minitest (5.16.3)
|
46
47
|
nenv (0.3.0)
|
47
48
|
notiffany (0.1.3)
|
48
49
|
nenv (~> 0.1)
|
@@ -50,7 +51,7 @@ GEM
|
|
50
51
|
oedipus_lex (2.6.0)
|
51
52
|
parser (3.1.2.0)
|
52
53
|
ast (~> 2.4.1)
|
53
|
-
parser_node_ext (0.
|
54
|
+
parser_node_ext (0.4.0)
|
54
55
|
parser
|
55
56
|
pry (0.14.1)
|
56
57
|
coderay (~> 1.1)
|
@@ -75,8 +76,9 @@ GEM
|
|
75
76
|
rspec-support (3.11.0)
|
76
77
|
shellany (0.0.1)
|
77
78
|
thor (1.2.1)
|
78
|
-
tzinfo (2.0.
|
79
|
+
tzinfo (2.0.5)
|
79
80
|
concurrent-ruby (~> 1.0)
|
81
|
+
zeitwerk (2.6.0)
|
80
82
|
|
81
83
|
PLATFORMS
|
82
84
|
x86_64-darwin-21
|
data/README.md
CHANGED
@@ -1,6 +1,44 @@
|
|
1
1
|
# NodeQuery
|
2
2
|
|
3
|
-
NodeQuery defines an AST node query language, which is a css like syntax for matching nodes
|
3
|
+
NodeQuery defines an AST node query language, which is a css like syntax for matching nodes,
|
4
|
+
it supports other ast parser if it implements `NodeQuery::Adapter`.
|
5
|
+
|
6
|
+
## Table of Contents
|
7
|
+
|
8
|
+
- [NodeQuery](#nodequery)
|
9
|
+
- [Table of Contents](#table-of-contents)
|
10
|
+
- [Installation](#installation)
|
11
|
+
- [Usage](#usage)
|
12
|
+
- [Node Query Language](#node-query-language)
|
13
|
+
- [nql matches node type](#nql-matches-node-type)
|
14
|
+
- [nql matches attribute](#nql-matches-attribute)
|
15
|
+
- [nql matches nested attribute](#nql-matches-nested-attribute)
|
16
|
+
- [nql matches evaluated value](#nql-matches-evaluated-value)
|
17
|
+
- [nql matches nested selector](#nql-matches-nested-selector)
|
18
|
+
- [nql matches method result](#nql-matches-method-result)
|
19
|
+
- [nql matches operators](#nql-matches-operators)
|
20
|
+
- [nql matches array node attribute](#nql-matches-array-node-attribute)
|
21
|
+
- [nql matches * in attribute key](#nql-matches--in-attribute-key)
|
22
|
+
- [nql matches multiple selectors](#nql-matches-multiple-selectors)
|
23
|
+
- [Descendant combinator](#descendant-combinator)
|
24
|
+
- [Child combinator](#child-combinator)
|
25
|
+
- [Adjacent sibling combinator](#adjacent-sibling-combinator)
|
26
|
+
- [General sibling combinator](#general-sibling-combinator)
|
27
|
+
- [nql matches goto scope](#nql-matches-goto-scope)
|
28
|
+
- [nql matches pseudo selector](#nql-matches-pseudo-selector)
|
29
|
+
- [nql matches multiple expressions](#nql-matches-multiple-expressions)
|
30
|
+
- [Node Rules](#node-rules)
|
31
|
+
- [rules matches node type](#rules-matches-node-type)
|
32
|
+
- [rules matches attribute](#rules-matches-attribute)
|
33
|
+
- [rules matches nested attribute](#rules-matches-nested-attribute)
|
34
|
+
- [rules matches evaluated value](#rules-matches-evaluated-value)
|
35
|
+
- [rules matches nested selector](#rules-matches-nested-selector)
|
36
|
+
- [rules matches method result](#rules-matches-method-result)
|
37
|
+
- [rules matches operators](#rules-matches-operators)
|
38
|
+
- [rules matches array nodes attribute](#rules-matches-array-nodes-attribute)
|
39
|
+
- [Write Adapter](#write-adapter)
|
40
|
+
- [Development](#development)
|
41
|
+
- [Contributing](#contributing)
|
4
42
|
|
5
43
|
## Installation
|
6
44
|
|
@@ -20,12 +58,408 @@ Or install it yourself as:
|
|
20
58
|
|
21
59
|
## Usage
|
22
60
|
|
23
|
-
It provides
|
61
|
+
It provides two apis: `query_nodes` and `match_node?`
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
node_query = NodeQuery.new(nqlOrRules: String | Hash) # Initialize NodeQuery
|
65
|
+
node_query.query_nodes(node: Node, including_self = true): Node[] # Get the matching nodes.
|
66
|
+
node_query.match_node?(node: Node): boolean # Check if the node matches nql or rules.
|
67
|
+
```
|
68
|
+
|
69
|
+
Here is an example for parser ast node.
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
source = `
|
73
|
+
class User
|
74
|
+
def initialize(id, name)
|
75
|
+
@id = id
|
76
|
+
@name = name
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
user = User.new(1, "Murphy")
|
81
|
+
`
|
82
|
+
node = Parser::CurrentRuby.parse(source)
|
83
|
+
|
84
|
+
# It will get the node of initialize.
|
85
|
+
NodeQuery.new('.def[name=initialize]').query_nodes(node)
|
86
|
+
NodeQuery.new({ nodeType: 'def', name: 'initialize' }).query_nodes(node)
|
87
|
+
```
|
88
|
+
|
89
|
+
## Node Query Language
|
90
|
+
|
91
|
+
### nql matches node type
|
92
|
+
|
93
|
+
```
|
94
|
+
.class
|
95
|
+
```
|
96
|
+
|
97
|
+
It matches class node
|
98
|
+
|
99
|
+
### nql matches attribute
|
100
|
+
|
101
|
+
```
|
102
|
+
.class[name=User]
|
103
|
+
```
|
104
|
+
|
105
|
+
It matches class node whose name is User
|
106
|
+
|
107
|
+
### nql matches nested attribute
|
108
|
+
|
109
|
+
```
|
110
|
+
.class[parent_class.name=Base]
|
111
|
+
```
|
112
|
+
|
113
|
+
It matches class node whose parent class name is Base
|
114
|
+
|
115
|
+
### nql matches evaluated value
|
116
|
+
|
117
|
+
```
|
118
|
+
.ivasgn[left_value="@{{right_value}}"]
|
119
|
+
```
|
120
|
+
|
121
|
+
It matches ivasgn node whose left value equals '@' plus the evaluated value of right value.
|
122
|
+
|
123
|
+
### nql matches nested selector
|
124
|
+
|
125
|
+
```
|
126
|
+
.def[body.0=.ivasgn]
|
127
|
+
```
|
128
|
+
|
129
|
+
It matches def node whose first child node is an ivasgn node.
|
130
|
+
|
131
|
+
### nql matches method result
|
132
|
+
|
133
|
+
```
|
134
|
+
.def[arguments.size=2]
|
135
|
+
```
|
136
|
+
|
137
|
+
It matches def node whose arguments size is 2.
|
138
|
+
|
139
|
+
### nql matches operators
|
140
|
+
|
141
|
+
```
|
142
|
+
.class[name=User]
|
143
|
+
```
|
144
|
+
|
145
|
+
Value of name is equal to User
|
146
|
+
|
147
|
+
```
|
148
|
+
.class[name^=User]
|
149
|
+
```
|
150
|
+
|
151
|
+
Value of name starts with User
|
152
|
+
|
153
|
+
```
|
154
|
+
.class[name$=User]
|
155
|
+
```
|
156
|
+
|
157
|
+
Value of name ends with User
|
158
|
+
|
159
|
+
```
|
160
|
+
.class[name*=User]
|
161
|
+
```
|
162
|
+
|
163
|
+
Value of name contains User
|
164
|
+
|
165
|
+
```
|
166
|
+
.def[arguments.size!=2]
|
167
|
+
```
|
168
|
+
|
169
|
+
Size of arguments is not equal to 2
|
170
|
+
|
171
|
+
```
|
172
|
+
.def[arguments.size>=2]
|
173
|
+
```
|
174
|
+
|
175
|
+
Size of arguments is greater than or equal to 2
|
176
|
+
|
177
|
+
```
|
178
|
+
.def[arguments.size>2]
|
179
|
+
```
|
180
|
+
|
181
|
+
Size of arguments is greater than 2
|
182
|
+
|
183
|
+
```
|
184
|
+
.def[arguments.size<=2]
|
185
|
+
```
|
186
|
+
|
187
|
+
Size of arguments is less than or equal to 2
|
188
|
+
|
189
|
+
```
|
190
|
+
.def[arguments.size<2]
|
191
|
+
```
|
192
|
+
|
193
|
+
Size of arguments is less than 2
|
194
|
+
|
195
|
+
```
|
196
|
+
.class[name IN (User Account)]
|
197
|
+
```
|
198
|
+
|
199
|
+
Value of name is either User or Account
|
200
|
+
|
201
|
+
```
|
202
|
+
.class[name NOT IN (User Account)]
|
203
|
+
```
|
204
|
+
|
205
|
+
Value of name is neither User nor Account
|
206
|
+
|
207
|
+
```
|
208
|
+
.def[arguments INCLUDES id]
|
209
|
+
```
|
210
|
+
|
211
|
+
Value of arguments includes id
|
212
|
+
|
213
|
+
```
|
214
|
+
.class[name=~/User/]
|
215
|
+
```
|
216
|
+
|
217
|
+
Value of name matches User
|
218
|
+
|
219
|
+
```
|
220
|
+
.class[name!~/User/]
|
221
|
+
```
|
222
|
+
|
223
|
+
Value of name does not match User
|
224
|
+
|
225
|
+
```
|
226
|
+
.class[name IN (/User/ /Account/)]
|
227
|
+
```
|
228
|
+
|
229
|
+
Value of name matches either /User/ or /Account/
|
230
|
+
|
231
|
+
### nql matches array node attribute
|
232
|
+
|
233
|
+
```
|
234
|
+
.def[arguments=(id name)]
|
235
|
+
```
|
236
|
+
|
237
|
+
It matches def node whose arguments are id and name.
|
238
|
+
|
239
|
+
### nql matches * in attribute key
|
240
|
+
|
241
|
+
```
|
242
|
+
.def[arguments.*.name IN (id name)]
|
243
|
+
```
|
244
|
+
|
245
|
+
It matches def node whose arguments are either id or name.
|
246
|
+
|
247
|
+
### nql matches multiple selectors
|
248
|
+
|
249
|
+
#### Descendant combinator
|
250
|
+
|
251
|
+
```
|
252
|
+
.class .send
|
253
|
+
```
|
254
|
+
|
255
|
+
It matches send node whose ansestor is class node.
|
256
|
+
|
257
|
+
#### Child combinator
|
258
|
+
|
259
|
+
```
|
260
|
+
.def > .send
|
261
|
+
```
|
262
|
+
|
263
|
+
It matches send node whose parent is def node.
|
264
|
+
|
265
|
+
#### Adjacent sibling combinator
|
266
|
+
|
267
|
+
```
|
268
|
+
.send[left_value=@id] + .send
|
269
|
+
```
|
270
|
+
|
271
|
+
It matches send node only if it is immediately follows the send node whose left value is @id.
|
272
|
+
|
273
|
+
#### General sibling combinator
|
274
|
+
|
275
|
+
```
|
276
|
+
.send[left_value=@id] ~ .send
|
277
|
+
```
|
278
|
+
|
279
|
+
It matches send node only if it is follows the send node whose left value is @id.
|
280
|
+
|
281
|
+
### nql matches goto scope
|
282
|
+
|
283
|
+
```
|
284
|
+
.def body .send
|
285
|
+
```
|
286
|
+
|
287
|
+
It matches send node who is in the body of def node.
|
288
|
+
|
289
|
+
### nql matches pseudo selector
|
290
|
+
|
291
|
+
```
|
292
|
+
.class:has(.def[name=initialize])
|
293
|
+
```
|
294
|
+
|
295
|
+
It matches class node who has an initialize def node.
|
296
|
+
|
297
|
+
```
|
298
|
+
.class:not_has(.def[name=initialize])
|
299
|
+
```
|
300
|
+
|
301
|
+
It matches class node who does not have an initialize def node.
|
302
|
+
|
303
|
+
### nql matches multiple expressions
|
304
|
+
|
305
|
+
```
|
306
|
+
.ivasgn[left_value=@id], .ivasgn[left_value=@name]
|
307
|
+
```
|
308
|
+
|
309
|
+
It matches ivasgn node whose left value is either @id or @name.
|
310
|
+
|
311
|
+
## Node Rules
|
312
|
+
|
313
|
+
### rules matches node type
|
314
|
+
|
315
|
+
```
|
316
|
+
{ nodeType: 'class' }
|
317
|
+
```
|
318
|
+
|
319
|
+
It matches class node
|
320
|
+
|
321
|
+
### rules matches attribute
|
322
|
+
|
323
|
+
```
|
324
|
+
{ nodeType: 'def', name: 'initialize' }
|
325
|
+
```
|
326
|
+
|
327
|
+
It matches def node whose name is initialize
|
328
|
+
|
329
|
+
```
|
330
|
+
{ nodeType: 'def', arguments: { "0": 1, "1": "Murphy" } }
|
331
|
+
```
|
332
|
+
|
333
|
+
It matches def node whose arguments are 1 and Murphy.
|
334
|
+
|
335
|
+
### rules matches nested attribute
|
336
|
+
|
337
|
+
```
|
338
|
+
{ nodeType: 'class', parent_class: { name: 'Base' } }
|
339
|
+
```
|
340
|
+
|
341
|
+
It matches class node whose parent class name is Base
|
342
|
+
|
343
|
+
### rules matches evaluated value
|
344
|
+
|
345
|
+
```
|
346
|
+
{ nodeType: 'ivasgn', left_value: '@{{right_value}}' }
|
347
|
+
```
|
348
|
+
|
349
|
+
It matches ivasgn node whose left value equals '@' plus the evaluated value of right value.
|
350
|
+
|
351
|
+
### rules matches nested selector
|
352
|
+
|
353
|
+
```
|
354
|
+
{ nodeType: 'def', body: { "0": { nodeType: 'ivasgn' } } }
|
355
|
+
```
|
356
|
+
|
357
|
+
It matches def node whose first child node is an ivasgn node.
|
358
|
+
|
359
|
+
### rules matches method result
|
360
|
+
|
361
|
+
```
|
362
|
+
{ nodeType: 'def', arguments: { size: 2 } }
|
363
|
+
```
|
364
|
+
|
365
|
+
It matches def node whose arguments size is 2.
|
366
|
+
|
367
|
+
### rules matches operators
|
368
|
+
|
369
|
+
```
|
370
|
+
{ nodeType: 'class', name: 'User' }
|
371
|
+
```
|
372
|
+
|
373
|
+
Value of name is equal to User
|
374
|
+
|
375
|
+
```
|
376
|
+
{ nodeType: 'def', arguments: { size { not: 2 } }
|
377
|
+
```
|
378
|
+
|
379
|
+
Size of arguments is not equal to 2
|
380
|
+
|
381
|
+
```
|
382
|
+
{ nodeType: 'def', arguments: { size { gte: 2 } }
|
383
|
+
```
|
384
|
+
|
385
|
+
Size of arguments is greater than or equal to 2
|
386
|
+
|
387
|
+
```
|
388
|
+
{ nodeType: 'def', arguments: { size { gt: 2 } }
|
389
|
+
```
|
390
|
+
|
391
|
+
Size of arguments is greater than 2
|
392
|
+
|
393
|
+
```
|
394
|
+
{ nodeType: 'def', arguments: { size { lte: 2 } }
|
395
|
+
```
|
396
|
+
|
397
|
+
Size of arguments is less than or equal to 2
|
398
|
+
|
399
|
+
```
|
400
|
+
{ nodeType: 'def', arguments: { size { lt: 2 } }
|
401
|
+
```
|
402
|
+
|
403
|
+
Size of arguments is less than 2
|
404
|
+
|
405
|
+
```
|
406
|
+
{ nodeType: 'class', name: { in: ['User', 'Account'] } }
|
407
|
+
```
|
408
|
+
|
409
|
+
Value of name is either User or Account
|
410
|
+
|
411
|
+
```
|
412
|
+
{ nodeType: 'class', name: { not_in: ['User', 'Account'] } }
|
413
|
+
```
|
414
|
+
|
415
|
+
Value of name is neither User nor Account
|
416
|
+
|
417
|
+
```
|
418
|
+
{ nodeType: 'def', arguments: { includes: 'id' } }
|
419
|
+
```
|
420
|
+
|
421
|
+
Value of arguments includes id
|
422
|
+
|
423
|
+
```
|
424
|
+
{ nodeType: 'class', name: /User/ }
|
425
|
+
```
|
426
|
+
|
427
|
+
Value of name matches User
|
428
|
+
|
429
|
+
```
|
430
|
+
{ nodeType: 'class', name: { not: /User/ } }
|
431
|
+
```
|
432
|
+
|
433
|
+
Value of name does not match User
|
434
|
+
|
435
|
+
```
|
436
|
+
{ nodeType: 'class', name: { in: [/User/, /Account/] } }
|
437
|
+
```
|
438
|
+
|
439
|
+
Value of name matches either /User/ or /Account/
|
440
|
+
|
441
|
+
### rules matches array nodes attribute
|
442
|
+
|
443
|
+
```
|
444
|
+
{ nodeType: 'def', arguments: ['id', 'name'] }
|
445
|
+
```
|
446
|
+
|
447
|
+
It matches def node whose arguments are id and name.
|
448
|
+
|
449
|
+
## Write Adapter
|
450
|
+
|
451
|
+
Different parser, like parser, will generate different AST nodes, to make NodeQuery work for them all,
|
452
|
+
we define an [Adapter](https://github.com/xinminlabs/node-query-ruby/blob/main/lib/node_query/adapter.rb) interface,
|
453
|
+
if you implement the Adapter interface, you can set it as NodeQuery's adapter.
|
24
454
|
|
25
455
|
```ruby
|
26
|
-
NodeQuery.
|
456
|
+
NodeQuery.configure(adapter: ParserAdapter.new)
|
27
457
|
```
|
28
458
|
|
459
|
+
Here is the ParserAdapter implementation:
|
460
|
+
|
461
|
+
[ParserAdapter](https://github.com/xinminlabs/node-query-ruby/blob/main/lib/node_query/parser_adapter.rb)
|
462
|
+
|
29
463
|
## Development
|
30
464
|
|
31
465
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/node_query/adapter.rb
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
# Abstract Adapter class
|
4
4
|
class NodeQuery::Adapter
|
5
|
+
# Check if it is a node
|
6
|
+
# @return [Boolean]
|
7
|
+
def is_node?(node)
|
8
|
+
raise NotImplementedError, 'get_node_type is not implemented'
|
9
|
+
end
|
10
|
+
|
5
11
|
# Get the type of node
|
6
12
|
# @param node [Node] ast node
|
7
13
|
# @return [Symbol] node type
|
@@ -17,7 +17,7 @@ module NodeQuery::Compiler
|
|
17
17
|
# @param node [Node] the node
|
18
18
|
# @return [Boolean]
|
19
19
|
def match?(node)
|
20
|
-
@value.base_node = node if @value.is_a?(
|
20
|
+
@value.base_node = node if @value.is_a?(String)
|
21
21
|
node && @value.match?(NodeQuery::Helper.get_target_node(node, @key), @operator)
|
22
22
|
end
|
23
23
|
|
@@ -17,7 +17,7 @@ module NodeQuery::Compiler
|
|
17
17
|
def match?(node, _operator = '==')
|
18
18
|
return false unless node
|
19
19
|
|
20
|
-
@node_type.to_sym == NodeQuery.
|
20
|
+
@node_type.to_sym == NodeQuery.adapter.get_node_type(node) && (!@attribute_list || @attribute_list.match?(node))
|
21
21
|
end
|
22
22
|
|
23
23
|
def to_s
|
@@ -25,7 +25,7 @@ module NodeQuery::Compiler
|
|
25
25
|
!actual.is_a?(::Array) || actual.size != expected_value.size ||
|
26
26
|
actual.zip(expected_value).any? { |actual_node, expected_node| expected_node.match?(actual_node, '!=') }
|
27
27
|
else
|
28
|
-
|
28
|
+
!is_equal?(node)
|
29
29
|
end
|
30
30
|
when '=~'
|
31
31
|
actual_value(node) =~ expected_value
|
@@ -46,9 +46,17 @@ module NodeQuery::Compiler
|
|
46
46
|
when '<='
|
47
47
|
actual_value(node) <= expected_value
|
48
48
|
when 'in'
|
49
|
-
|
49
|
+
if node.is_a?(Array)
|
50
|
+
node.all? { |child| expected_value.any? { |expected| expected.match?(child, '==') } }
|
51
|
+
else
|
52
|
+
expected_value.any? { |expected| expected.match?(node, '==') }
|
53
|
+
end
|
50
54
|
when 'not_in'
|
51
|
-
|
55
|
+
if node.is_a?(Array)
|
56
|
+
node.all? { |child| expected_value.all? { |expected| expected.match?(child, '!=') } }
|
57
|
+
else
|
58
|
+
expected_value.all? { |expected| expected.match?(node, '!=') }
|
59
|
+
end
|
52
60
|
when 'includes'
|
53
61
|
actual_value(node).any? { |actual| actual == expected_value }
|
54
62
|
else
|
@@ -57,19 +65,26 @@ module NodeQuery::Compiler
|
|
57
65
|
actual.is_a?(::Array) && actual.size == expected_value.size &&
|
58
66
|
actual.zip(expected_value).all? { |actual_node, expected_node| expected_node.match?(actual_node, '==') }
|
59
67
|
else
|
60
|
-
|
68
|
+
is_equal?(node)
|
61
69
|
end
|
62
70
|
end
|
63
71
|
end
|
64
72
|
|
73
|
+
# Check if the actual value equals the node value.
|
74
|
+
# @param node [Node] the node
|
75
|
+
# @return [Boolean] true if the actual value equals the node value.
|
76
|
+
def is_equal?(node)
|
77
|
+
actual_value(node) == expected_value
|
78
|
+
end
|
79
|
+
|
65
80
|
# Get the actual value from ast node.
|
66
81
|
# @param node [Node] ast node
|
67
82
|
# @return the node value, could be integer, float, string, boolean, nil, range, and etc.
|
68
83
|
def actual_value(node)
|
69
|
-
if
|
70
|
-
case NodeQuery.
|
84
|
+
if NodeQuery.adapter.is_node?(node)
|
85
|
+
case NodeQuery.adapter.get_node_type(node)
|
71
86
|
when :int, :float, :str, :sym
|
72
|
-
NodeQuery.
|
87
|
+
NodeQuery.adapter.get_children(node).last
|
73
88
|
when :true
|
74
89
|
true
|
75
90
|
when :false
|
@@ -77,13 +92,13 @@ module NodeQuery::Compiler
|
|
77
92
|
when :nil
|
78
93
|
nil
|
79
94
|
when :array
|
80
|
-
NodeQuery.
|
95
|
+
NodeQuery.adapter.get_children(node).map { |child_node| actual_value(child_node) }
|
81
96
|
when :irange
|
82
|
-
actual_value(NodeQuery.
|
97
|
+
actual_value(NodeQuery.adapter.get_children(node).first)..actual_value(NodeQuery.adapter.get_children(node).last)
|
83
98
|
when :erange
|
84
|
-
actual_value(NodeQuery.
|
99
|
+
actual_value(NodeQuery.adapter.get_children(node).first)...actual_value(NodeQuery.adapter.get_children(node).last)
|
85
100
|
when :begin
|
86
|
-
actual_value(NodeQuery.
|
101
|
+
actual_value(NodeQuery.adapter.get_children(node).first)
|
87
102
|
else
|
88
103
|
node
|
89
104
|
end
|
@@ -11,22 +11,16 @@ module NodeQuery::Compiler
|
|
11
11
|
@rest = rest
|
12
12
|
end
|
13
13
|
|
14
|
-
# Check if the node matches the expression.
|
15
|
-
# @param node [Node] the node
|
16
|
-
# @return [Boolean]
|
17
|
-
def match?(node)
|
18
|
-
!query_nodes(node).empty?
|
19
|
-
end
|
20
|
-
|
21
14
|
# Query nodes by the selector and the rest expression.
|
22
15
|
# @param node [Node] node to match
|
16
|
+
# @params including_self [boolean] if query the current node.
|
23
17
|
# @return [Array<Node>] matching nodes.
|
24
|
-
def query_nodes(node)
|
25
|
-
matching_nodes = @selector.query_nodes(node)
|
18
|
+
def query_nodes(node, including_self = true)
|
19
|
+
matching_nodes = @selector.query_nodes(node, including_self)
|
26
20
|
return matching_nodes if @rest.nil?
|
27
21
|
|
28
22
|
matching_nodes.flat_map do |matching_node|
|
29
|
-
@rest.query_nodes(matching_node)
|
23
|
+
@rest.query_nodes(matching_node, including_self)
|
30
24
|
end
|
31
25
|
end
|
32
26
|
|