synvert-core 1.0.5 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 tHAS tCOMMA
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 tOPEN_ARRAY tCLOSE_ARRAY tOPEN_SELECTOR tCLOSE_SELECTOR
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
- : selector tCHILD expression { Compiler::Expression.new(selector: val[0], rest: val[2], relationship: :child) }
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 tHAS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Selector.new(node_type: val[0], attribute_list: val[1], has_expression: val[4]) }
24
- | tNODE_TYPE tHAS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Selector.new(node_type: val[0], has_expression: val[3]) }
25
- | attribute_list tHAS tOPEN_SELECTOR expression tCLOSE_SELECTOR { Compiler::Selector.new(attribute_list: val[0], has_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 tCOMMA array_value { Compiler::Array.new(value: val[0], rest: val[2]) }
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
@@ -19,6 +19,7 @@
19
19
  #
20
20
  # It also supports some custom selectors:
21
21
  #
22
+ # * not_has: +.class:not_has(.def)+, it's same as +:not(:has())+ in css, just to make implementation easy.
22
23
  # * nested selector: +.send[arguments = [size = 2][first = .sym][last = .hash]]+
23
24
  # * array value: +.send[arguments = (a, b)]+
24
25
  # * IN operator: +.send[message IN (try, try!)]+
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Synvert
4
4
  module Core
5
- VERSION = '1.0.5'
5
+ VERSION = '1.2.0'
6
6
  end
7
7
  end
@@ -76,11 +76,6 @@ describe Parser::AST::Node do
76
76
  expect(node.name).to eq :@@foo
77
77
  end
78
78
 
79
- it 'gets for mlhs node' do
80
- node = parse('var.each { |(param1, param2)| }')
81
- expect(node.arguments.first.name).to eq node.arguments.first
82
- end
83
-
84
79
  it 'gets for restarg node' do
85
80
  node = parse('object.each { |*entry| }')
86
81
  expect(node.arguments.first.name).to eq :entry
@@ -116,16 +111,6 @@ describe Parser::AST::Node do
116
111
  node = parse('user&.update(name: name)')
117
112
  expect(node.message).to eq :update
118
113
  end
119
-
120
- it 'gets for super node' do
121
- node = parse('super(params)')
122
- expect(node.message).to eq :super
123
- end
124
-
125
- it 'gets for zuper node' do
126
- node = parse('super do; end')
127
- expect(node.caller.message).to eq :super
128
- end
129
114
  end
130
115
 
131
116
  describe '#parent_const' do
@@ -1042,4 +1027,45 @@ describe Parser::AST::Node do
1042
1027
  end
1043
1028
  end
1044
1029
  end
1030
+
1031
+ describe '#to_hash' do
1032
+ it 'gets hash' do
1033
+ node = parse(<<~EOS)
1034
+ class Synvert
1035
+ def foobar(foo, bar)
1036
+ foo + bar
1037
+ end
1038
+ end
1039
+ EOS
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: :send,
1060
+ receiver: { name: :foo, type: :lvar },
1061
+ message: :+,
1062
+ arguments: [{ name: :bar, type: :lvar }]
1063
+ }
1064
+ ]
1065
+ }
1066
+ ]
1067
+ }
1068
+ )
1069
+ end
1070
+ end
1045
1071
  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, build)]'
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, build)]'
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, "]"]
@@ -560,7 +597,7 @@ module Synvert::Core::NodeQuery
560
597
  source = '.class:has(> .def)'
561
598
  expected_tokens = [
562
599
  [:tNODE_TYPE, "class"],
563
- [:tHAS, "has"],
600
+ [:tPSEUDO_CLASS, "has"],
564
601
  [:tOPEN_SELECTOR, "("],
565
602
  [:tCHILD, ">"],
566
603
  [:tNODE_TYPE, "def"],
@@ -569,5 +606,35 @@ module Synvert::Core::NodeQuery
569
606
  assert_tokens source, expected_tokens
570
607
  end
571
608
  end
609
+
610
+ context ':not_has' do
611
+ it 'matches' do
612
+ source = '.class:not_has(> .def)'
613
+ expected_tokens = [
614
+ [:tNODE_TYPE, "class"],
615
+ [:tPSEUDO_CLASS, "not_has"],
616
+ [:tOPEN_SELECTOR, "("],
617
+ [:tCHILD, ">"],
618
+ [:tNODE_TYPE, "def"],
619
+ [:tCLOSE_SELECTOR, ")"]
620
+ ]
621
+ assert_tokens source, expected_tokens
622
+ end
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
572
639
  end
573
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,7 +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)'
58
+ assert_parser(source)
59
+ end
60
+
61
+ it 'parses :not_has selector' do
62
+ source = '.class :not_has(> .def)'
63
+ assert_parser(source)
64
+ end
65
+
66
+ it 'parses root :has selector' do
67
+ source = ':has(.def)'
53
68
  assert_parser(source)
54
69
  end
55
70
 
@@ -94,12 +109,12 @@ module Synvert::Core::NodeQuery
94
109
  end
95
110
 
96
111
  it 'parses in operator' do
97
- source = '.def[name in (foo, bar)]'
112
+ source = '.def[name in (foo bar)]'
98
113
  assert_parser(source)
99
114
  end
100
115
 
101
116
  it 'parses not_in operator' do
102
- source = '.def[name not in (foo, bar)]'
117
+ source = '.def[name not in (foo bar)]'
103
118
  assert_parser(source)
104
119
  end
105
120
 
@@ -113,6 +128,16 @@ module Synvert::Core::NodeQuery
113
128
  assert_parser(source)
114
129
  end
115
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
+
116
141
  describe '#query_nodes' do
117
142
  let(:node) {
118
143
  parse(<<~EOS)
@@ -127,13 +152,22 @@ module Synvert::Core::NodeQuery
127
152
 
128
153
  def foobar(a, b)
129
154
  { a: a, b: b }
130
- foo.merge(bar)
131
155
  arr[index]
156
+ arr[index] = value
157
+ nil?
158
+ call('')
132
159
  end
133
160
  end
134
161
  EOS
135
162
  }
136
163
 
164
+ let(:test_node) {
165
+ parse(<<~EOS)
166
+ RSpec.describe Synvert do
167
+ end
168
+ EOS
169
+ }
170
+
137
171
  it 'matches class node' do
138
172
  expression = parser.parse('.class[name=Synvert]')
139
173
  expect(expression.query_nodes(node)).to eq [node]
@@ -175,12 +209,12 @@ module Synvert::Core::NodeQuery
175
209
  end
176
210
 
177
211
  it 'matches in' do
178
- expression = parser.parse('.def[name IN (foo, bar)]')
212
+ expression = parser.parse('.def[name IN (foo bar)]')
179
213
  expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
180
214
  end
181
215
 
182
216
  it 'matches not in' do
183
- expression = parser.parse('.def[name NOT IN (foo, bar)]')
217
+ expression = parser.parse('.def[name NOT IN (foo bar)]')
184
218
  expect(expression.query_nodes(node)).to eq [node.body.last]
185
219
  end
186
220
 
@@ -190,34 +224,34 @@ module Synvert::Core::NodeQuery
190
224
  end
191
225
 
192
226
  it 'matches equal array' do
193
- expression = parser.parse('.def[arguments=(a, b)]')
227
+ expression = parser.parse('.def[arguments=(a b)]')
194
228
  expect(expression.query_nodes(node)).to eq [node.body.last]
195
229
 
196
- expression = parser.parse('.def[arguments=(b, a)]')
230
+ expression = parser.parse('.def[arguments=(b a)]')
197
231
  expect(expression.query_nodes(node)).to eq []
198
232
  end
199
233
 
200
234
  it 'matches not equal array' do
201
- expression = parser.parse('.def[arguments!=(a, b)]')
235
+ expression = parser.parse('.def[arguments!=(a b)]')
202
236
  expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
203
237
 
204
- expression = parser.parse('.def[arguments!=(b, a)]')
238
+ expression = parser.parse('.def[arguments!=(b a)]')
205
239
  expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second, node.body.last]
206
240
  end
207
241
 
208
242
  it 'matches descendant node' do
209
243
  expression = parser.parse('.class .send[message=:create]')
210
- expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
244
+ expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
211
245
  end
212
246
 
213
247
  it 'matches three level descendant node' do
214
248
  expression = parser.parse('.class .def .send[message=:create]')
215
- expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
249
+ expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
216
250
  end
217
251
 
218
252
  it 'matches child node' do
219
253
  expression = parser.parse('.def > .send[message=:create]')
220
- expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
254
+ expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
221
255
  end
222
256
 
223
257
  it 'matches next sibling node' do
@@ -230,23 +264,54 @@ module Synvert::Core::NodeQuery
230
264
  expect(expression.query_nodes(node)).to eq [node.body.last]
231
265
  end
232
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
+
233
280
  it 'matches has selector' do
234
281
  expression = parser.parse('.def:has(> .send[receiver=FactoryBot])')
235
282
  expect(expression.query_nodes(node)).to eq [node.body.first, node.body.second]
236
283
  end
237
284
 
285
+ it 'matches not_has selector' do
286
+ expression = parser.parse('.def:not_has(> .send[receiver=FactoryBot])')
287
+ expect(expression.query_nodes(node)).to eq [node.body.last]
288
+ end
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
+
238
295
  it 'matches arguments.size' do
239
- expression = parser.parse('.send[arguments.size=2]')
240
- expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
241
- expression = parser.parse('.send[arguments.size>2]')
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]')
242
303
  expect(expression.query_nodes(node)).to eq []
243
- expression = parser.parse('.send[arguments.size>=2]')
244
- expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
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
+ ]
245
310
  end
246
311
 
247
312
  it 'matches arguments' do
248
313
  expression = parser.parse('.send[arguments=[size=2][first=.sym][last=.hash]]')
249
- expect(expression.query_nodes(node)).to eq [node.body.first.children.last, node.body.second.children.last]
314
+ expect(expression.query_nodes(node)).to eq [node.body.first.body.last, node.body.second.body.last]
250
315
  end
251
316
 
252
317
  it 'matches regexp value' do
@@ -261,15 +326,25 @@ module Synvert::Core::NodeQuery
261
326
  expect(expression.query_nodes(node)).to eq node.body.last.body.first.children
262
327
  end
263
328
 
264
- it 'matches identifier' do
265
- expression = parser.parse('.send[receiver=foo][message=merge]')
329
+ it 'matches []' do
330
+ expression = parser.parse('.send[message=[]]')
266
331
  expect(expression.query_nodes(node)).to eq [node.body.last.body.second]
267
332
  end
268
333
 
269
- it 'matches []' do
270
- expression = parser.parse('.send[message="[]"]')
334
+ it 'matches []=' do
335
+ expression = parser.parse('.send[message=:[]=]')
271
336
  expect(expression.query_nodes(node)).to eq [node.body.last.body.third]
272
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
273
348
  end
274
349
  end
275
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.0.5
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-25 00:00:00.000000000 Z
11
+ date: 2022-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport