node_query 1.0.0 → 1.3.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.
- 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
|
|