synvert-core 1.1.1 → 1.2.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 +7 -0
- data/lib/synvert/core/array_ext.rb +9 -2
- data/lib/synvert/core/node_query/compiler/array.rb +1 -1
- data/lib/synvert/core/node_query/compiler/expression.rb +69 -31
- data/lib/synvert/core/node_query/compiler/selector.rb +17 -14
- data/lib/synvert/core/node_query/lexer.rex +8 -1
- data/lib/synvert/core/node_query/lexer.rex.rb +20 -2
- data/lib/synvert/core/node_query/parser.racc.rb +221 -247
- data/lib/synvert/core/node_query/parser.y +9 -12
- data/lib/synvert/core/version.rb +1 -1
- data/spec/synvert/core/node_query/lexer_spec.rb +30 -4
- data/spec/synvert/core/node_query/parser_spec.rb +54 -19
- metadata +2 -2
@@ -1,28 +1,25 @@
|
|
1
1
|
class Synvert::Core::NodeQuery::Parser
|
2
2
|
options no_result_var
|
3
|
-
token tNODE_TYPE tATTRIBUTE tKEY tIDENTIFIER tIDENTIFIER_VALUE tINDEX tPSEUDO_CLASS
|
3
|
+
token tNODE_TYPE tATTRIBUTE tKEY tIDENTIFIER tIDENTIFIER_VALUE tINDEX tPSEUDO_CLASS
|
4
4
|
tCHILD tSUBSEQUENT_SIBLING tNEXT_SIBLING
|
5
|
-
tOPEN_ATTRIBUTE tCLOSE_ATTRIBUTE tOPEN_DYNAMIC_ATTRIBUTE tCLOSE_DYNAMIC_ATTRIBUTE
|
5
|
+
tOPEN_ATTRIBUTE tCLOSE_ATTRIBUTE tOPEN_DYNAMIC_ATTRIBUTE tCLOSE_DYNAMIC_ATTRIBUTE
|
6
|
+
tOPEN_ARRAY tCLOSE_ARRAY tOPEN_SELECTOR tCLOSE_SELECTOR tOPEN_GOTO_SCOPE tCLOSE_GOTO_SCOPE
|
6
7
|
tEQUAL tNOT_EQUAL tMATCH tNOT_MATCH tGREATER_THAN tGREATER_THAN_OR_EQUAL tLESS_THAN tLESS_THAN_OR_EQUAL tIN tNOT_IN tINCLUDES
|
7
8
|
tARRAY_VALUE tDYNAMIC_ATTRIBUTE tBOOLEAN tFLOAT tINTEGER tNIL tREGEXP tSTRING tSYMBOL
|
8
9
|
rule
|
9
10
|
expression
|
10
|
-
:
|
11
|
-
| selector tSUBSEQUENT_SIBLING expression { Compiler::Expression.new(selector: val[0], rest: val[2], relationship: :subsequent_sibling) }
|
12
|
-
| selector tNEXT_SIBLING expression { Compiler::Expression.new(selector: val[0], rest: val[2], relationship: :next_sibling) }
|
13
|
-
| selector expression { Compiler::Expression.new(selector: val[0], rest: val[1], relationship: :descendant) }
|
14
|
-
| selector { Compiler::Expression.new(selector: val[0]) }
|
15
|
-
| tCHILD expression { Compiler::Expression.new(rest: val[1], relationship: :child) }
|
11
|
+
: tCHILD expression { Compiler::Expression.new(rest: val[1], relationship: :child) }
|
16
12
|
| tSUBSEQUENT_SIBLING expression { Compiler::Expression.new(rest: val[1], relationship: :subsequent_sibling) }
|
17
13
|
| tNEXT_SIBLING expression { Compiler::Expression.new(rest: val[1], relationship: :next_sibling) }
|
14
|
+
| tOPEN_GOTO_SCOPE tIDENTIFIER tCLOSE_GOTO_SCOPE expression { Compiler::Expression.new(goto_scope: val[1], rest: val[3]) }
|
15
|
+
| tPSEUDO_CLASS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Expression.new(relationship: val[0].to_sym, rest: val[2]) }
|
16
|
+
| selector expression { Compiler::Expression.new(selector: val[0], rest: val[1]) }
|
17
|
+
| selector { Compiler::Expression.new(selector: val[0]) }
|
18
18
|
|
19
19
|
selector
|
20
20
|
: tNODE_TYPE attribute_list tINDEX { Compiler::Selector.new(node_type: val[0], attribute_list: val[1], index: val[2]) }
|
21
21
|
| tNODE_TYPE tINDEX { Compiler::Selector.new(node_type: val[0], index: val[1]) }
|
22
22
|
| attribute_list tINDEX { Compiler::Selector.new(attribute_list: val[0], index: val[1]) }
|
23
|
-
| tNODE_TYPE attribute_list tPSEUDO_CLASS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Selector.new(node_type: val[0], attribute_list: val[1], pseudo_class: val[2], pseudo_expression: val[4]) }
|
24
|
-
| tNODE_TYPE tPSEUDO_CLASS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Selector.new(node_type: val[0], pseudo_class: val[1], pseudo_expression: val[3]) }
|
25
|
-
| attribute_list tPSEUDO_CLASS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Selector.new(attribute_list: val[0], pseudo_class: val[1], pseudo_expression: val[3]) }
|
26
23
|
| tNODE_TYPE attribute_list { Compiler::Selector.new(node_type: val[0], attribute_list: val[1]) }
|
27
24
|
| tNODE_TYPE { Compiler::Selector.new(node_type: val[0]) }
|
28
25
|
| attribute_list { Compiler::Selector.new(attribute_list: val[0]) }
|
@@ -53,7 +50,7 @@ rule
|
|
53
50
|
| tKEY tIN tOPEN_ARRAY array_value tCLOSE_ARRAY { Compiler::Attribute.new(key: val[0], value: val[3], operator: :in) }
|
54
51
|
|
55
52
|
array_value
|
56
|
-
: value
|
53
|
+
: value array_value { Compiler::Array.new(value: val[0], rest: val[1]) }
|
57
54
|
| value { Compiler::Array.new(value: val[0]) }
|
58
55
|
|
59
56
|
value
|
data/lib/synvert/core/version.rb
CHANGED
@@ -226,6 +226,19 @@ module Synvert::Core::NodeQuery
|
|
226
226
|
assert_tokens source, expected_tokens
|
227
227
|
end
|
228
228
|
|
229
|
+
it 'matches nil?' do
|
230
|
+
source = ".send[message=nil?]"
|
231
|
+
expected_tokens = [
|
232
|
+
[:tNODE_TYPE, "send"],
|
233
|
+
[:tOPEN_ATTRIBUTE, "["],
|
234
|
+
[:tKEY, "message"],
|
235
|
+
[:tEQUAL, "="],
|
236
|
+
[:tIDENTIFIER_VALUE, "nil?"],
|
237
|
+
[:tCLOSE_ATTRIBUTE, "]"]
|
238
|
+
]
|
239
|
+
assert_tokens source, expected_tokens
|
240
|
+
end
|
241
|
+
|
229
242
|
it 'matches attribute value' do
|
230
243
|
source = '.pair[key={{value}}]'
|
231
244
|
expected_tokens = [
|
@@ -429,7 +442,7 @@ module Synvert::Core::NodeQuery
|
|
429
442
|
end
|
430
443
|
|
431
444
|
it 'matches IN' do
|
432
|
-
source = '.send[message IN (create
|
445
|
+
source = '.send[message IN (create build)]'
|
433
446
|
expected_tokens = [
|
434
447
|
[:tNODE_TYPE, "send"],
|
435
448
|
[:tOPEN_ATTRIBUTE, "["],
|
@@ -437,7 +450,6 @@ module Synvert::Core::NodeQuery
|
|
437
450
|
[:tIN, "IN"],
|
438
451
|
[:tOPEN_ARRAY, "("],
|
439
452
|
[:tIDENTIFIER_VALUE, "create"],
|
440
|
-
[:tCOMMA, ","],
|
441
453
|
[:tIDENTIFIER_VALUE, "build"],
|
442
454
|
[:tCLOSE_ARRAY, ")"],
|
443
455
|
[:tCLOSE_ATTRIBUTE, "]"]
|
@@ -446,7 +458,7 @@ module Synvert::Core::NodeQuery
|
|
446
458
|
end
|
447
459
|
|
448
460
|
it 'matches NOT IN' do
|
449
|
-
source = '.send[message NOT IN (create
|
461
|
+
source = '.send[message NOT IN (create build)]'
|
450
462
|
expected_tokens = [
|
451
463
|
[:tNODE_TYPE, "send"],
|
452
464
|
[:tOPEN_ATTRIBUTE, "["],
|
@@ -454,7 +466,6 @@ module Synvert::Core::NodeQuery
|
|
454
466
|
[:tNOT_IN, "NOT IN"],
|
455
467
|
[:tOPEN_ARRAY, "("],
|
456
468
|
[:tIDENTIFIER_VALUE, "create"],
|
457
|
-
[:tCOMMA, ","],
|
458
469
|
[:tIDENTIFIER_VALUE, "build"],
|
459
470
|
[:tCLOSE_ARRAY, ")"],
|
460
471
|
[:tCLOSE_ATTRIBUTE, "]"]
|
@@ -610,5 +621,20 @@ module Synvert::Core::NodeQuery
|
|
610
621
|
assert_tokens source, expected_tokens
|
611
622
|
end
|
612
623
|
end
|
624
|
+
|
625
|
+
context 'goto_scope' do
|
626
|
+
it 'matches' do
|
627
|
+
source = '.block <body> > .def'
|
628
|
+
expected_tokens = [
|
629
|
+
[:tNODE_TYPE, "block"],
|
630
|
+
[:tOPEN_GOTO_SCOPE, "<"],
|
631
|
+
[:tIDENTIFIER, "body"],
|
632
|
+
[:tCLOSE_GOTO_SCOPE, ">"],
|
633
|
+
[:tCHILD, ">"],
|
634
|
+
[:tNODE_TYPE, "def"]
|
635
|
+
]
|
636
|
+
assert_tokens source, expected_tokens
|
637
|
+
end
|
638
|
+
end
|
613
639
|
end
|
614
640
|
end
|
@@ -28,6 +28,11 @@ module Synvert::Core::NodeQuery
|
|
28
28
|
assert_parser(source)
|
29
29
|
end
|
30
30
|
|
31
|
+
it 'parses scope' do
|
32
|
+
source = '.block <body> > .send'
|
33
|
+
assert_parser(source)
|
34
|
+
end
|
35
|
+
|
31
36
|
it 'parses :first-child' do
|
32
37
|
source = '.class .def:first-child'
|
33
38
|
assert_parser(source)
|
@@ -49,12 +54,17 @@ module Synvert::Core::NodeQuery
|
|
49
54
|
end
|
50
55
|
|
51
56
|
it 'parses :has selector' do
|
52
|
-
source = '.class:has(> .def)'
|
57
|
+
source = '.class :has(> .def)'
|
53
58
|
assert_parser(source)
|
54
59
|
end
|
55
60
|
|
56
61
|
it 'parses :not_has selector' do
|
57
|
-
source = '.class:not_has(> .def)'
|
62
|
+
source = '.class :not_has(> .def)'
|
63
|
+
assert_parser(source)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'parses root :has selector' do
|
67
|
+
source = ':has(.def)'
|
58
68
|
assert_parser(source)
|
59
69
|
end
|
60
70
|
|
@@ -99,12 +109,12 @@ module Synvert::Core::NodeQuery
|
|
99
109
|
end
|
100
110
|
|
101
111
|
it 'parses in operator' do
|
102
|
-
source = '.def[name in (foo
|
112
|
+
source = '.def[name in (foo bar)]'
|
103
113
|
assert_parser(source)
|
104
114
|
end
|
105
115
|
|
106
116
|
it 'parses not_in operator' do
|
107
|
-
source = '.def[name not in (foo
|
117
|
+
source = '.def[name not in (foo bar)]'
|
108
118
|
assert_parser(source)
|
109
119
|
end
|
110
120
|
|
@@ -142,15 +152,22 @@ module Synvert::Core::NodeQuery
|
|
142
152
|
|
143
153
|
def foobar(a, b)
|
144
154
|
{ a: a, b: b }
|
145
|
-
foo.merge(bar)
|
146
155
|
arr[index]
|
147
156
|
arr[index] = value
|
157
|
+
nil?
|
148
158
|
call('')
|
149
159
|
end
|
150
160
|
end
|
151
161
|
EOS
|
152
162
|
}
|
153
163
|
|
164
|
+
let(:test_node) {
|
165
|
+
parse(<<~EOS)
|
166
|
+
RSpec.describe Synvert do
|
167
|
+
end
|
168
|
+
EOS
|
169
|
+
}
|
170
|
+
|
154
171
|
it 'matches class node' do
|
155
172
|
expression = parser.parse('.class[name=Synvert]')
|
156
173
|
expect(expression.query_nodes(node)).to eq [node]
|
@@ -192,12 +209,12 @@ module Synvert::Core::NodeQuery
|
|
192
209
|
end
|
193
210
|
|
194
211
|
it 'matches in' do
|
195
|
-
expression = parser.parse('.def[name IN (foo
|
212
|
+
expression = parser.parse('.def[name IN (foo bar)]')
|
196
213
|
expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
|
197
214
|
end
|
198
215
|
|
199
216
|
it 'matches not in' do
|
200
|
-
expression = parser.parse('.def[name NOT IN (foo
|
217
|
+
expression = parser.parse('.def[name NOT IN (foo bar)]')
|
201
218
|
expect(expression.query_nodes(node)).to eq [node.body.last]
|
202
219
|
end
|
203
220
|
|
@@ -207,18 +224,18 @@ module Synvert::Core::NodeQuery
|
|
207
224
|
end
|
208
225
|
|
209
226
|
it 'matches equal array' do
|
210
|
-
expression = parser.parse('.def[arguments=(a
|
227
|
+
expression = parser.parse('.def[arguments=(a b)]')
|
211
228
|
expect(expression.query_nodes(node)).to eq [node.body.last]
|
212
229
|
|
213
|
-
expression = parser.parse('.def[arguments=(b
|
230
|
+
expression = parser.parse('.def[arguments=(b a)]')
|
214
231
|
expect(expression.query_nodes(node)).to eq []
|
215
232
|
end
|
216
233
|
|
217
234
|
it 'matches not equal array' do
|
218
|
-
expression = parser.parse('.def[arguments!=(a
|
235
|
+
expression = parser.parse('.def[arguments!=(a b)]')
|
219
236
|
expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
|
220
237
|
|
221
|
-
expression = parser.parse('.def[arguments!=(b
|
238
|
+
expression = parser.parse('.def[arguments!=(b a)]')
|
222
239
|
expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second, node.body.last]
|
223
240
|
end
|
224
241
|
|
@@ -247,6 +264,19 @@ module Synvert::Core::NodeQuery
|
|
247
264
|
expect(expression.query_nodes(node)).to eq [node.body.last]
|
248
265
|
end
|
249
266
|
|
267
|
+
it 'matches goto scope' do
|
268
|
+
expression = parser.parse('.def <body> > .send[message=:create]')
|
269
|
+
expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
|
270
|
+
|
271
|
+
expression = parser.parse('.def <body> .send[message=:create]')
|
272
|
+
expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'matches multiple goto scope' do
|
276
|
+
expression = parser.parse('.block <caller.arguments> .const[name=Synvert]')
|
277
|
+
expect(expression.query_nodes(test_node)).to eq [test_node.caller.arguments.first]
|
278
|
+
end
|
279
|
+
|
250
280
|
it 'matches has selector' do
|
251
281
|
expression = parser.parse('.def:has(> .send[receiver=FactoryBot])')
|
252
282
|
expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
|
@@ -257,12 +287,17 @@ module Synvert::Core::NodeQuery
|
|
257
287
|
expect(expression.query_nodes(node)).to eq [node.body.last]
|
258
288
|
end
|
259
289
|
|
290
|
+
it 'matches root has selector' do
|
291
|
+
expression = parser.parse(':has(.def[name=foobar])')
|
292
|
+
expect(expression.query_nodes(node)).to eq [node]
|
293
|
+
end
|
294
|
+
|
260
295
|
it 'matches arguments.size' do
|
261
296
|
expression = parser.parse('.def .send[arguments.size=2]')
|
262
297
|
expect(expression.query_nodes(node)).to eq [
|
263
298
|
node.body.first.body.last,
|
264
299
|
node.body.second.body.last,
|
265
|
-
node.body.third.body.
|
300
|
+
node.body.third.body.third
|
266
301
|
]
|
267
302
|
expression = parser.parse('.def .send[arguments.size>2]')
|
268
303
|
expect(expression.query_nodes(node)).to eq []
|
@@ -270,7 +305,7 @@ module Synvert::Core::NodeQuery
|
|
270
305
|
expect(expression.query_nodes(node)).to eq [
|
271
306
|
node.body.first.body.last,
|
272
307
|
node.body.second.body.last,
|
273
|
-
node.body.third.body.
|
308
|
+
node.body.third.body.third
|
274
309
|
]
|
275
310
|
end
|
276
311
|
|
@@ -291,18 +326,18 @@ module Synvert::Core::NodeQuery
|
|
291
326
|
expect(expression.query_nodes(node)).to eq node.body.last.body.first.children
|
292
327
|
end
|
293
328
|
|
294
|
-
it 'matches identifier' do
|
295
|
-
expression = parser.parse('.send[receiver=foo][message=merge]')
|
296
|
-
expect(expression.query_nodes(node)).to eq [node.body.last.body.second]
|
297
|
-
end
|
298
|
-
|
299
329
|
it 'matches []' do
|
300
330
|
expression = parser.parse('.send[message=[]]')
|
301
|
-
expect(expression.query_nodes(node)).to eq [node.body.last.body.
|
331
|
+
expect(expression.query_nodes(node)).to eq [node.body.last.body.second]
|
302
332
|
end
|
303
333
|
|
304
334
|
it 'matches []=' do
|
305
335
|
expression = parser.parse('.send[message=:[]=]')
|
336
|
+
expect(expression.query_nodes(node)).to eq [node.body.last.body.third]
|
337
|
+
end
|
338
|
+
|
339
|
+
it 'matches nil and nil?' do
|
340
|
+
expression = parser.parse('.send[receiver=nil][message=nil?]')
|
306
341
|
expect(expression.query_nodes(node)).to eq [node.body.last.body.fourth]
|
307
342
|
end
|
308
343
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: synvert-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-04-
|
11
|
+
date: 2022-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|