synvert-core 1.1.1 → 1.2.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 +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
|