synvert-core 1.0.5 → 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.
@@ -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