synvert-core 1.1.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/lib/synvert/core/array_ext.rb +9 -2
- data/lib/synvert/core/node_ext.rb +14 -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/compiler/string.rb +0 -11
- data/lib/synvert/core/node_query/lexer.rex +12 -1
- data/lib/synvert/core/node_query/lexer.rex.rb +28 -2
- data/lib/synvert/core/node_query/parser.racc.rb +219 -253
- data/lib/synvert/core/node_query/parser.y +9 -14
- data/lib/synvert/core/node_query.rb +7 -7
- data/lib/synvert/core/version.rb +1 -1
- data/spec/synvert/core/node_ext_spec.rb +32 -24
- data/spec/synvert/core/node_query/lexer_spec.rb +56 -4
- data/spec/synvert/core/node_query/parser_spec.rb +90 -25
- metadata +2 -2
@@ -1,31 +1,26 @@
|
|
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
|
-
| 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
22
|
| tNODE_TYPE attribute_list { Compiler::Selector.new(node_type: val[0], attribute_list: val[1]) }
|
27
23
|
| tNODE_TYPE { Compiler::Selector.new(node_type: val[0]) }
|
28
|
-
| attribute_list { Compiler::Selector.new(attribute_list: val[0]) }
|
29
24
|
;
|
30
25
|
|
31
26
|
attribute_list
|
@@ -53,7 +48,7 @@ rule
|
|
53
48
|
| tKEY tIN tOPEN_ARRAY array_value tCLOSE_ARRAY { Compiler::Attribute.new(key: val[0], value: val[3], operator: :in) }
|
54
49
|
|
55
50
|
array_value
|
56
|
-
: value
|
51
|
+
: value array_value { Compiler::Array.new(value: val[0], rest: val[1]) }
|
57
52
|
| value { Compiler::Array.new(value: val[0]) }
|
58
53
|
|
59
54
|
value
|
@@ -5,10 +5,10 @@
|
|
5
5
|
# It supports the following selectors:
|
6
6
|
#
|
7
7
|
# * AST node type: +.class+, +.send+
|
8
|
-
# * attribute value:
|
9
|
-
# * attribute regex: <code
|
10
|
-
# * attribute conditions:
|
11
|
-
# * nested attribute:
|
8
|
+
# * attribute value: +.send[receiver = nil]+, +.send[message = create]+
|
9
|
+
# * attribute regex: <code>.send[key=~/\A:([^'"]+)\z/]</code>, <code>.send[key!~/\A:([^'"]+)\z/]</code>
|
10
|
+
# * attribute conditions: +.send[message != nil]+, +.send[value > 1]+, +.send[value >= 1]+, +.send[value < 1]+, +.send[value <= 1]+
|
11
|
+
# * nested attribute: +.send[caller.message = map]+, +.send[arguments.size = 2]+
|
12
12
|
# * first or last child: +.def:first-child+, +.send:last-child+
|
13
13
|
# * nth-child or nth-last-child: +.def:nth-child(2)+, +.send:nth-last-child(2)+
|
14
14
|
# * descendant: +.class .send+
|
@@ -21,9 +21,9 @@
|
|
21
21
|
#
|
22
22
|
# * not_has: +.class:not_has(.def)+, it's same as +:not(:has())+ in css, just to make implementation easy.
|
23
23
|
# * nested selector: +.send[arguments = [size = 2][first = .sym][last = .hash]]+
|
24
|
-
# * array value: +.send[arguments = (a
|
25
|
-
# * IN operator: +.send[message IN (try
|
26
|
-
# * NOT IN operator: +.send[message NOT IN (create
|
24
|
+
# * array value: +.send[arguments = (a b)]+
|
25
|
+
# * IN operator: +.send[message IN (try try!)]+
|
26
|
+
# * NOT IN operator: +.send[message NOT IN (create build)]+
|
27
27
|
# * INCLUDES operator: +.send[arguments INCLUDES &block]+
|
28
28
|
# * dynamic attribute value: +.hash > .pair[key={{value}}]+
|
29
29
|
#
|
data/lib/synvert/core/version.rb
CHANGED
@@ -1033,33 +1033,41 @@ describe Parser::AST::Node do
|
|
1033
1033
|
node = parse(<<~EOS)
|
1034
1034
|
class Synvert
|
1035
1035
|
def foobar(foo, bar)
|
1036
|
-
foo
|
1036
|
+
{ foo => bar }
|
1037
1037
|
end
|
1038
1038
|
end
|
1039
1039
|
EOS
|
1040
|
-
expect(node.to_hash).to eq(
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1040
|
+
expect(node.to_hash).to eq(
|
1041
|
+
{
|
1042
|
+
type: :class,
|
1043
|
+
parent_class: nil,
|
1044
|
+
name: {
|
1045
|
+
type: :const,
|
1046
|
+
parent_const: nil,
|
1047
|
+
name: :Synvert
|
1048
|
+
},
|
1049
|
+
body: [
|
1050
|
+
{
|
1051
|
+
type: :def,
|
1052
|
+
name: :foobar,
|
1053
|
+
arguments: [
|
1054
|
+
{ type: :arg, name: :foo },
|
1055
|
+
{ type: :arg, name: :bar }
|
1056
|
+
],
|
1057
|
+
body: [
|
1058
|
+
{
|
1059
|
+
type: :hash,
|
1060
|
+
pairs: {
|
1061
|
+
type: :pair,
|
1062
|
+
key: { type: :lvar, name: :foo },
|
1063
|
+
value: { type: :lvar, name: :bar }
|
1064
|
+
}
|
1065
|
+
}
|
1066
|
+
]
|
1067
|
+
}
|
1068
|
+
]
|
1069
|
+
}
|
1070
|
+
)
|
1063
1071
|
end
|
1064
1072
|
end
|
1065
1073
|
end
|
@@ -200,6 +200,45 @@ module Synvert::Core::NodeQuery
|
|
200
200
|
assert_tokens source, expected_tokens
|
201
201
|
end
|
202
202
|
|
203
|
+
it 'matches :[] message' do
|
204
|
+
source = ".send[message=[]]"
|
205
|
+
expected_tokens = [
|
206
|
+
[:tNODE_TYPE, "send"],
|
207
|
+
[:tOPEN_ATTRIBUTE, "["],
|
208
|
+
[:tKEY, "message"],
|
209
|
+
[:tEQUAL, "="],
|
210
|
+
[:tIDENTIFIER_VALUE, "[]"],
|
211
|
+
[:tCLOSE_ATTRIBUTE, "]"]
|
212
|
+
]
|
213
|
+
assert_tokens source, expected_tokens
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'matches :[] message' do
|
217
|
+
source = ".send[message=:[]=]"
|
218
|
+
expected_tokens = [
|
219
|
+
[:tNODE_TYPE, "send"],
|
220
|
+
[:tOPEN_ATTRIBUTE, "["],
|
221
|
+
[:tKEY, "message"],
|
222
|
+
[:tEQUAL, "="],
|
223
|
+
[:tSYMBOL, :[]=],
|
224
|
+
[:tCLOSE_ATTRIBUTE, "]"]
|
225
|
+
]
|
226
|
+
assert_tokens source, expected_tokens
|
227
|
+
end
|
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
|
+
|
203
242
|
it 'matches attribute value' do
|
204
243
|
source = '.pair[key={{value}}]'
|
205
244
|
expected_tokens = [
|
@@ -403,7 +442,7 @@ module Synvert::Core::NodeQuery
|
|
403
442
|
end
|
404
443
|
|
405
444
|
it 'matches IN' do
|
406
|
-
source = '.send[message IN (create
|
445
|
+
source = '.send[message IN (create build)]'
|
407
446
|
expected_tokens = [
|
408
447
|
[:tNODE_TYPE, "send"],
|
409
448
|
[:tOPEN_ATTRIBUTE, "["],
|
@@ -411,7 +450,6 @@ module Synvert::Core::NodeQuery
|
|
411
450
|
[:tIN, "IN"],
|
412
451
|
[:tOPEN_ARRAY, "("],
|
413
452
|
[:tIDENTIFIER_VALUE, "create"],
|
414
|
-
[:tCOMMA, ","],
|
415
453
|
[:tIDENTIFIER_VALUE, "build"],
|
416
454
|
[:tCLOSE_ARRAY, ")"],
|
417
455
|
[:tCLOSE_ATTRIBUTE, "]"]
|
@@ -420,7 +458,7 @@ module Synvert::Core::NodeQuery
|
|
420
458
|
end
|
421
459
|
|
422
460
|
it 'matches NOT IN' do
|
423
|
-
source = '.send[message NOT IN (create
|
461
|
+
source = '.send[message NOT IN (create build)]'
|
424
462
|
expected_tokens = [
|
425
463
|
[:tNODE_TYPE, "send"],
|
426
464
|
[:tOPEN_ATTRIBUTE, "["],
|
@@ -428,7 +466,6 @@ module Synvert::Core::NodeQuery
|
|
428
466
|
[:tNOT_IN, "NOT IN"],
|
429
467
|
[:tOPEN_ARRAY, "("],
|
430
468
|
[:tIDENTIFIER_VALUE, "create"],
|
431
|
-
[:tCOMMA, ","],
|
432
469
|
[:tIDENTIFIER_VALUE, "build"],
|
433
470
|
[:tCLOSE_ARRAY, ")"],
|
434
471
|
[:tCLOSE_ATTRIBUTE, "]"]
|
@@ -584,5 +621,20 @@ module Synvert::Core::NodeQuery
|
|
584
621
|
assert_tokens source, expected_tokens
|
585
622
|
end
|
586
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
|
587
639
|
end
|
588
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
|
|
@@ -118,6 +128,16 @@ module Synvert::Core::NodeQuery
|
|
118
128
|
assert_parser(source)
|
119
129
|
end
|
120
130
|
|
131
|
+
it 'parses []=' do
|
132
|
+
source = '.send[message=[]=]'
|
133
|
+
assert_parser(source)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'parses :[]' do
|
137
|
+
source = '.send[message=:[]]'
|
138
|
+
assert_parser(source)
|
139
|
+
end
|
140
|
+
|
121
141
|
describe '#query_nodes' do
|
122
142
|
let(:node) {
|
123
143
|
parse(<<~EOS)
|
@@ -132,13 +152,22 @@ module Synvert::Core::NodeQuery
|
|
132
152
|
|
133
153
|
def foobar(a, b)
|
134
154
|
{ a: a, b: b }
|
135
|
-
foo.merge(bar)
|
136
155
|
arr[index]
|
156
|
+
arr[index] = value
|
157
|
+
nil?
|
158
|
+
call('')
|
137
159
|
end
|
138
160
|
end
|
139
161
|
EOS
|
140
162
|
}
|
141
163
|
|
164
|
+
let(:test_node) {
|
165
|
+
parse(<<~EOS)
|
166
|
+
RSpec.describe Synvert do
|
167
|
+
end
|
168
|
+
EOS
|
169
|
+
}
|
170
|
+
|
142
171
|
it 'matches class node' do
|
143
172
|
expression = parser.parse('.class[name=Synvert]')
|
144
173
|
expect(expression.query_nodes(node)).to eq [node]
|
@@ -180,12 +209,12 @@ module Synvert::Core::NodeQuery
|
|
180
209
|
end
|
181
210
|
|
182
211
|
it 'matches in' do
|
183
|
-
expression = parser.parse('.def[name IN (foo
|
212
|
+
expression = parser.parse('.def[name IN (foo bar)]')
|
184
213
|
expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
|
185
214
|
end
|
186
215
|
|
187
216
|
it 'matches not in' do
|
188
|
-
expression = parser.parse('.def[name NOT IN (foo
|
217
|
+
expression = parser.parse('.def[name NOT IN (foo bar)]')
|
189
218
|
expect(expression.query_nodes(node)).to eq [node.body.last]
|
190
219
|
end
|
191
220
|
|
@@ -195,34 +224,34 @@ module Synvert::Core::NodeQuery
|
|
195
224
|
end
|
196
225
|
|
197
226
|
it 'matches equal array' do
|
198
|
-
expression = parser.parse('.def[arguments=(a
|
227
|
+
expression = parser.parse('.def[arguments=(a b)]')
|
199
228
|
expect(expression.query_nodes(node)).to eq [node.body.last]
|
200
229
|
|
201
|
-
expression = parser.parse('.def[arguments=(b
|
230
|
+
expression = parser.parse('.def[arguments=(b a)]')
|
202
231
|
expect(expression.query_nodes(node)).to eq []
|
203
232
|
end
|
204
233
|
|
205
234
|
it 'matches not equal array' do
|
206
|
-
expression = parser.parse('.def[arguments!=(a
|
235
|
+
expression = parser.parse('.def[arguments!=(a b)]')
|
207
236
|
expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
|
208
237
|
|
209
|
-
expression = parser.parse('.def[arguments!=(b
|
238
|
+
expression = parser.parse('.def[arguments!=(b a)]')
|
210
239
|
expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second, node.body.last]
|
211
240
|
end
|
212
241
|
|
213
242
|
it 'matches descendant node' do
|
214
243
|
expression = parser.parse('.class .send[message=:create]')
|
215
|
-
expect(expression.query_nodes(node)).to eq [node.body.first.
|
244
|
+
expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
|
216
245
|
end
|
217
246
|
|
218
247
|
it 'matches three level descendant node' do
|
219
248
|
expression = parser.parse('.class .def .send[message=:create]')
|
220
|
-
expect(expression.query_nodes(node)).to eq [node.body.first.
|
249
|
+
expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
|
221
250
|
end
|
222
251
|
|
223
252
|
it 'matches child node' do
|
224
253
|
expression = parser.parse('.def > .send[message=:create]')
|
225
|
-
expect(expression.query_nodes(node)).to eq [node.body.first.
|
254
|
+
expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
|
226
255
|
end
|
227
256
|
|
228
257
|
it 'matches next sibling node' do
|
@@ -235,6 +264,19 @@ module Synvert::Core::NodeQuery
|
|
235
264
|
expect(expression.query_nodes(node)).to eq [node.body.last]
|
236
265
|
end
|
237
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
|
+
|
238
280
|
it 'matches has selector' do
|
239
281
|
expression = parser.parse('.def:has(> .send[receiver=FactoryBot])')
|
240
282
|
expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
|
@@ -245,18 +287,31 @@ module Synvert::Core::NodeQuery
|
|
245
287
|
expect(expression.query_nodes(node)).to eq [node.body.last]
|
246
288
|
end
|
247
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
|
+
|
248
295
|
it 'matches arguments.size' do
|
249
|
-
expression = parser.parse('.send[arguments.size=2]')
|
250
|
-
expect(expression.query_nodes(node)).to eq [
|
251
|
-
|
296
|
+
expression = parser.parse('.def .send[arguments.size=2]')
|
297
|
+
expect(expression.query_nodes(node)).to eq [
|
298
|
+
node.body.first.body.last,
|
299
|
+
node.body.second.body.last,
|
300
|
+
node.body.third.body.third
|
301
|
+
]
|
302
|
+
expression = parser.parse('.def .send[arguments.size>2]')
|
252
303
|
expect(expression.query_nodes(node)).to eq []
|
253
|
-
expression = parser.parse('.send[arguments.size>=2]')
|
254
|
-
expect(expression.query_nodes(node)).to eq [
|
304
|
+
expression = parser.parse('.def .send[arguments.size>=2]')
|
305
|
+
expect(expression.query_nodes(node)).to eq [
|
306
|
+
node.body.first.body.last,
|
307
|
+
node.body.second.body.last,
|
308
|
+
node.body.third.body.third
|
309
|
+
]
|
255
310
|
end
|
256
311
|
|
257
312
|
it 'matches arguments' do
|
258
|
-
expression = parser.parse('.send[arguments
|
259
|
-
expect(expression.query_nodes(node)).to eq [node.body.first.
|
313
|
+
expression = parser.parse('.send[arguments.size=2][arguments.first=.sym][arguments.last=.hash]')
|
314
|
+
expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
|
260
315
|
end
|
261
316
|
|
262
317
|
it 'matches regexp value' do
|
@@ -271,15 +326,25 @@ module Synvert::Core::NodeQuery
|
|
271
326
|
expect(expression.query_nodes(node)).to eq node.body.last.body.first.children
|
272
327
|
end
|
273
328
|
|
274
|
-
it 'matches
|
275
|
-
expression = parser.parse('.send[
|
329
|
+
it 'matches []' do
|
330
|
+
expression = parser.parse('.send[message=[]]')
|
276
331
|
expect(expression.query_nodes(node)).to eq [node.body.last.body.second]
|
277
332
|
end
|
278
333
|
|
279
|
-
it 'matches []' do
|
280
|
-
expression = parser.parse('.send[message
|
334
|
+
it 'matches []=' do
|
335
|
+
expression = parser.parse('.send[message=:[]=]')
|
281
336
|
expect(expression.query_nodes(node)).to eq [node.body.last.body.third]
|
282
337
|
end
|
338
|
+
|
339
|
+
it 'matches nil and nil?' do
|
340
|
+
expression = parser.parse('.send[receiver=nil][message=nil?]')
|
341
|
+
expect(expression.query_nodes(node)).to eq [node.body.last.body.fourth]
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'matches empty string' do
|
345
|
+
expression = parser.parse('.send[message=call][arguments.first=""]')
|
346
|
+
expect(expression.query_nodes(node)).to eq [node.body.last.body.last]
|
347
|
+
end
|
283
348
|
end
|
284
349
|
end
|
285
350
|
end
|
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.1
|
4
|
+
version: 1.2.1
|
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-
|
11
|
+
date: 2022-05-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|