synvert-core 0.64.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +1 -1
  3. data/.gitignore +4 -0
  4. data/CHANGELOG.md +5 -0
  5. data/Guardfile +11 -2
  6. data/README.md +2 -2
  7. data/Rakefile +15 -1
  8. data/lib/synvert/core/array_ext.rb +41 -0
  9. data/lib/synvert/core/engine/erb.rb +9 -8
  10. data/lib/synvert/core/node_ext.rb +47 -44
  11. data/lib/synvert/core/node_query/compiler/array.rb +34 -0
  12. data/lib/synvert/core/node_query/compiler/attribute.rb +51 -0
  13. data/lib/synvert/core/node_query/compiler/attribute_list.rb +24 -0
  14. data/lib/synvert/core/node_query/compiler/boolean.rb +23 -0
  15. data/lib/synvert/core/node_query/compiler/comparable.rb +79 -0
  16. data/lib/synvert/core/node_query/compiler/dynamic_attribute.rb +51 -0
  17. data/lib/synvert/core/node_query/compiler/expression.rb +88 -0
  18. data/lib/synvert/core/node_query/compiler/float.rb +23 -0
  19. data/lib/synvert/core/node_query/compiler/identifier.rb +41 -0
  20. data/lib/synvert/core/node_query/compiler/integer.rb +23 -0
  21. data/lib/synvert/core/node_query/compiler/invalid_operator_error.rb +7 -0
  22. data/lib/synvert/core/node_query/compiler/nil.rb +23 -0
  23. data/lib/synvert/core/node_query/compiler/parse_error.rb +7 -0
  24. data/lib/synvert/core/node_query/compiler/regexp.rb +37 -0
  25. data/lib/synvert/core/node_query/compiler/selector.rb +51 -0
  26. data/lib/synvert/core/node_query/compiler/string.rb +34 -0
  27. data/lib/synvert/core/node_query/compiler/symbol.rb +23 -0
  28. data/lib/synvert/core/node_query/compiler.rb +24 -0
  29. data/lib/synvert/core/node_query/lexer.rex +96 -0
  30. data/lib/synvert/core/node_query/lexer.rex.rb +293 -0
  31. data/lib/synvert/core/node_query/parser.racc.rb +518 -0
  32. data/lib/synvert/core/node_query/parser.y +84 -0
  33. data/lib/synvert/core/node_query.rb +36 -0
  34. data/lib/synvert/core/rewriter/action/delete_action.rb +4 -2
  35. data/lib/synvert/core/rewriter/action/replace_action.rb +4 -2
  36. data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +2 -2
  37. data/lib/synvert/core/rewriter/action/wrap_action.rb +3 -2
  38. data/lib/synvert/core/rewriter/action.rb +2 -1
  39. data/lib/synvert/core/rewriter/helper.rb +5 -2
  40. data/lib/synvert/core/rewriter/instance.rb +25 -13
  41. data/lib/synvert/core/rewriter/scope/query_scope.rb +36 -0
  42. data/lib/synvert/core/rewriter/scope/within_scope.rb +1 -1
  43. data/lib/synvert/core/rewriter.rb +1 -0
  44. data/lib/synvert/core/version.rb +1 -1
  45. data/lib/synvert/core.rb +22 -5
  46. data/spec/synvert/core/engine/erb_spec.rb +2 -2
  47. data/spec/synvert/core/node_ext_spec.rb +36 -5
  48. data/spec/synvert/core/node_query/lexer_spec.rb +512 -0
  49. data/spec/synvert/core/node_query/parser_spec.rb +270 -0
  50. data/spec/synvert/core/rewriter/condition/if_only_exist_condition_spec.rb +1 -6
  51. data/spec/synvert/core/rewriter/helper_spec.rb +4 -1
  52. data/spec/synvert/core/rewriter/instance_spec.rb +24 -3
  53. data/spec/synvert/core/rewriter/scope/query_scope_spec.rb +74 -0
  54. data/spec/synvert/core/rewriter/scope/within_scope_spec.rb +12 -9
  55. data/spec/synvert/core/rewriter_spec.rb +4 -2
  56. data/synvert-core-ruby.gemspec +5 -1
  57. metadata +75 -2
@@ -0,0 +1,512 @@
1
+ require 'spec_helper'
2
+ require 'oedipus_lex'
3
+
4
+ module Synvert::Core::NodeQuery
5
+ RSpec.describe Lexer do
6
+ let(:lexer) { described_class.new }
7
+
8
+ def assert_tokens(source, expected_tokens)
9
+ lexer.parse(source)
10
+ tokens = []
11
+ while token = lexer.next_token
12
+ tokens << token
13
+ end
14
+ expect(tokens).to eq expected_tokens
15
+ end
16
+
17
+ context 'ast node type' do
18
+ it 'matches node type' do
19
+ source = '.send'
20
+ expected_tokens = [[:tNODE_TYPE, "send"]]
21
+ assert_tokens source, expected_tokens
22
+ end
23
+ end
24
+
25
+ context 'attribute value' do
26
+ it 'matches =' do
27
+ source = '.send[message=create]'
28
+ expected_tokens = [
29
+ [:tNODE_TYPE, "send"],
30
+ [:tOPEN_ATTRIBUTE, "["],
31
+ [:tKEY, "message"],
32
+ [:tEQUAL, "="],
33
+ [:tIDENTIFIER_VALUE, "create"],
34
+ [:tCLOSE_ATTRIBUTE, "]"]
35
+ ]
36
+ assert_tokens source, expected_tokens
37
+ end
38
+
39
+ it 'matches nil' do
40
+ source = '.send[receiver=nil]'
41
+ expected_tokens = [
42
+ [:tNODE_TYPE, "send"],
43
+ [:tOPEN_ATTRIBUTE, "["],
44
+ [:tKEY, "receiver"],
45
+ [:tEQUAL, "="],
46
+ [:tNIL, nil],
47
+ [:tCLOSE_ATTRIBUTE, "]"]
48
+ ]
49
+ assert_tokens source, expected_tokens
50
+ end
51
+
52
+ it 'matches string' do
53
+ source = '.send[message="create"]'
54
+ expected_tokens = [
55
+ [:tNODE_TYPE, "send"],
56
+ [:tOPEN_ATTRIBUTE, "["],
57
+ [:tKEY, "message"],
58
+ [:tEQUAL, "="],
59
+ [:tSTRING, "create"],
60
+ [:tCLOSE_ATTRIBUTE, "]"]
61
+ ]
62
+ assert_tokens source, expected_tokens
63
+ end
64
+
65
+ it 'matches "[]"' do
66
+ source = '.send[message="[]"]'
67
+ expected_tokens = [
68
+ [:tNODE_TYPE, "send"],
69
+ [:tOPEN_ATTRIBUTE, "["],
70
+ [:tKEY, "message"],
71
+ [:tEQUAL, "="],
72
+ [:tSTRING, "[]"],
73
+ [:tCLOSE_ATTRIBUTE, "]"]
74
+ ]
75
+ assert_tokens source, expected_tokens
76
+ end
77
+
78
+ it 'matches symbol' do
79
+ source = '.send[message=:create]'
80
+ expected_tokens = [
81
+ [:tNODE_TYPE, "send"],
82
+ [:tOPEN_ATTRIBUTE, "["],
83
+ [:tKEY, "message"],
84
+ [:tEQUAL, "="],
85
+ [:tSYMBOL, :create],
86
+ [:tCLOSE_ATTRIBUTE, "]"]
87
+ ]
88
+ assert_tokens source, expected_tokens
89
+ end
90
+
91
+ it 'matches integer' do
92
+ source = '[value=1]'
93
+ expected_tokens = [
94
+ [:tOPEN_ATTRIBUTE, "["],
95
+ [:tKEY, "value"],
96
+ [:tEQUAL, "="],
97
+ [:tINTEGER, 1],
98
+ [:tCLOSE_ATTRIBUTE, "]"]
99
+ ]
100
+ assert_tokens source, expected_tokens
101
+ end
102
+
103
+ it 'matches float' do
104
+ source = '.send[value=1.1]'
105
+ expected_tokens = [
106
+ [:tNODE_TYPE, "send"],
107
+ [:tOPEN_ATTRIBUTE, "["],
108
+ [:tKEY, "value"],
109
+ [:tEQUAL, "="],
110
+ [:tFLOAT, 1.1],
111
+ [:tCLOSE_ATTRIBUTE, "]"]
112
+ ]
113
+ assert_tokens source, expected_tokens
114
+ end
115
+
116
+ it 'matches boolean' do
117
+ source = '.send[value=true]'
118
+ expected_tokens = [
119
+ [:tNODE_TYPE, "send"],
120
+ [:tOPEN_ATTRIBUTE, "["],
121
+ [:tKEY, "value"],
122
+ [:tEQUAL, "="],
123
+ [:tBOOLEAN, true],
124
+ [:tCLOSE_ATTRIBUTE, "]"]
125
+ ]
126
+ assert_tokens source, expected_tokens
127
+ end
128
+
129
+ it 'identifier can contain !' do
130
+ source = '.send[message=create!]'
131
+ expected_tokens = [
132
+ [:tNODE_TYPE, "send"],
133
+ [:tOPEN_ATTRIBUTE, "["],
134
+ [:tKEY, "message"],
135
+ [:tEQUAL, "="],
136
+ [:tIDENTIFIER_VALUE, "create!"],
137
+ [:tCLOSE_ATTRIBUTE, "]"]
138
+ ]
139
+ assert_tokens source, expected_tokens
140
+ end
141
+
142
+ it 'matches attribute value' do
143
+ source = '.pair[key={{value}}]'
144
+ expected_tokens = [
145
+ [:tNODE_TYPE, "pair"],
146
+ [:tOPEN_ATTRIBUTE, "["],
147
+ [:tKEY, "key"],
148
+ [:tEQUAL, "="],
149
+ [:tOPEN_DYNAMIC_ATTRIBUTE, "{{"],
150
+ [:tDYNAMIC_ATTRIBUTE, "value"],
151
+ [:tCLOSE_DYNAMIC_ATTRIBUTE, "}}"],
152
+ [:tCLOSE_ATTRIBUTE, "]"]
153
+ ]
154
+ assert_tokens source, expected_tokens
155
+ end
156
+
157
+ it 'matches nested value' do
158
+ source = <<~EOS
159
+ .send[
160
+ receiver=
161
+ .send[message=:create]
162
+ ]
163
+ EOS
164
+ expected_tokens = [
165
+ [:tNODE_TYPE, "send"],
166
+ [:tOPEN_ATTRIBUTE, "["],
167
+ [:tKEY, "receiver"],
168
+ [:tEQUAL, "="],
169
+ [:tNODE_TYPE, "send"],
170
+ [:tOPEN_ATTRIBUTE, "["],
171
+ [:tKEY, "message"],
172
+ [:tEQUAL, "="],
173
+ [:tSYMBOL, :create],
174
+ [:tCLOSE_ATTRIBUTE, "]"],
175
+ [:tCLOSE_ATTRIBUTE, "]"]
176
+ ]
177
+ assert_tokens source, expected_tokens
178
+ end
179
+
180
+ it 'matches deep nested value' do
181
+ source = <<~EOS
182
+ .send[
183
+ arguments=[size=2][first=.str][last=.str]
184
+ ]
185
+ EOS
186
+ expected_tokens = [
187
+ [:tNODE_TYPE, "send"],
188
+ [:tOPEN_ATTRIBUTE, "["],
189
+ [:tKEY, "arguments"],
190
+ [:tEQUAL, "="],
191
+ [:tOPEN_ATTRIBUTE, "["],
192
+ [:tKEY, "size"],
193
+ [:tEQUAL, "="],
194
+ [:tINTEGER, 2],
195
+ [:tCLOSE_ATTRIBUTE, "]"],
196
+ [:tOPEN_ATTRIBUTE, "["],
197
+ [:tKEY, "first"],
198
+ [:tEQUAL, "="],
199
+ [:tNODE_TYPE, "str"],
200
+ [:tCLOSE_ATTRIBUTE, "]"],
201
+ [:tOPEN_ATTRIBUTE, "["],
202
+ [:tKEY, "last"],
203
+ [:tEQUAL, "="],
204
+ [:tNODE_TYPE, "str"],
205
+ [:tCLOSE_ATTRIBUTE, "]"],
206
+ [:tCLOSE_ATTRIBUTE, "]"]
207
+ ]
208
+ assert_tokens source, expected_tokens
209
+ end
210
+ end
211
+
212
+ context 'attribute condition' do
213
+ it 'matches !=' do
214
+ source = '.send[message != create]'
215
+ expected_tokens = [
216
+ [:tNODE_TYPE, "send"],
217
+ [:tOPEN_ATTRIBUTE, "["],
218
+ [:tKEY, "message"],
219
+ [:tNOT_EQUAL, "!="],
220
+ [:tIDENTIFIER_VALUE, "create"],
221
+ [:tCLOSE_ATTRIBUTE, "]"]
222
+ ]
223
+ assert_tokens source, expected_tokens
224
+ end
225
+
226
+ it 'matches >' do
227
+ source = '[value > 1]'
228
+ expected_tokens = [
229
+ [:tOPEN_ATTRIBUTE, "["],
230
+ [:tKEY, "value"],
231
+ [:tGREATER_THAN, ">"],
232
+ [:tINTEGER, 1],
233
+ [:tCLOSE_ATTRIBUTE, "]"]
234
+ ]
235
+ assert_tokens source, expected_tokens
236
+ end
237
+
238
+ it 'matches <' do
239
+ source = '[value < 1]'
240
+ expected_tokens = [
241
+ [:tOPEN_ATTRIBUTE, "["],
242
+ [:tKEY, "value"],
243
+ [:tLESS_THAN, "<"],
244
+ [:tINTEGER, 1],
245
+ [:tCLOSE_ATTRIBUTE, "]"]
246
+ ]
247
+ assert_tokens source, expected_tokens
248
+ end
249
+
250
+ it 'matches >=' do
251
+ source = '[value >= 1]'
252
+ expected_tokens = [
253
+ [:tOPEN_ATTRIBUTE, "["],
254
+ [:tKEY, "value"],
255
+ [:tGREATER_THAN_OR_EQUAL, ">="],
256
+ [:tINTEGER, 1],
257
+ [:tCLOSE_ATTRIBUTE, "]"]
258
+ ]
259
+ assert_tokens source, expected_tokens
260
+ end
261
+
262
+ it 'matches <=' do
263
+ source = '[value <= 1]'
264
+ expected_tokens = [
265
+ [:tOPEN_ATTRIBUTE, "["],
266
+ [:tKEY, "value"],
267
+ [:tLESS_THAN_OR_EQUAL, "<="],
268
+ [:tINTEGER, 1],
269
+ [:tCLOSE_ATTRIBUTE, "]"]
270
+ ]
271
+ assert_tokens source, expected_tokens
272
+ end
273
+
274
+ it 'matches =~' do
275
+ source = '.send[message=~/create/i]'
276
+ expected_tokens = [
277
+ [:tNODE_TYPE, "send"],
278
+ [:tOPEN_ATTRIBUTE, "["],
279
+ [:tKEY, "message"],
280
+ [:tMATCH, "=~"],
281
+ [:tREGEXP, /create/i],
282
+ [:tCLOSE_ATTRIBUTE, "]"]
283
+ ]
284
+ assert_tokens source, expected_tokens
285
+ end
286
+
287
+ it 'matches !~' do
288
+ source = '.send[message!~/create/i]'
289
+ expected_tokens = [
290
+ [:tNODE_TYPE, "send"],
291
+ [:tOPEN_ATTRIBUTE, "["],
292
+ [:tKEY, "message"],
293
+ [:tNOT_MATCH, "!~"],
294
+ [:tREGEXP, /create/i],
295
+ [:tCLOSE_ATTRIBUTE, "]"]
296
+ ]
297
+ assert_tokens source, expected_tokens
298
+ end
299
+
300
+ it 'matche empty array' do
301
+ source = '.send[arguments=()]'
302
+ expected_tokens = [
303
+ [:tNODE_TYPE, "send"],
304
+ [:tOPEN_ATTRIBUTE, "["],
305
+ [:tKEY, "arguments"],
306
+ [:tEQUAL, "="],
307
+ [:tOPEN_ARRAY, "("],
308
+ [:tCLOSE_ARRAY, ")"],
309
+ [:tCLOSE_ATTRIBUTE, "]"]
310
+ ]
311
+ assert_tokens source, expected_tokens
312
+ end
313
+
314
+ it 'matche equal array' do
315
+ source = '.send[arguments=(:create)]'
316
+ expected_tokens = [
317
+ [:tNODE_TYPE, "send"],
318
+ [:tOPEN_ATTRIBUTE, "["],
319
+ [:tKEY, "arguments"],
320
+ [:tEQUAL, "="],
321
+ [:tOPEN_ARRAY, "("],
322
+ [:tSYMBOL, :create],
323
+ [:tCLOSE_ARRAY, ")"],
324
+ [:tCLOSE_ATTRIBUTE, "]"]
325
+ ]
326
+ assert_tokens source, expected_tokens
327
+ end
328
+
329
+ it 'matche not equal array' do
330
+ source = '.send[arguments!=(:create)]'
331
+ expected_tokens = [
332
+ [:tNODE_TYPE, "send"],
333
+ [:tOPEN_ATTRIBUTE, "["],
334
+ [:tKEY, "arguments"],
335
+ [:tNOT_EQUAL, "!="],
336
+ [:tOPEN_ARRAY, "("],
337
+ [:tSYMBOL, :create],
338
+ [:tCLOSE_ARRAY, ")"],
339
+ [:tCLOSE_ATTRIBUTE, "]"]
340
+ ]
341
+ assert_tokens source, expected_tokens
342
+ end
343
+
344
+ it 'matches IN' do
345
+ source = '.send[message IN (create, build)]'
346
+ expected_tokens = [
347
+ [:tNODE_TYPE, "send"],
348
+ [:tOPEN_ATTRIBUTE, "["],
349
+ [:tKEY, "message"],
350
+ [:tIN, "IN"],
351
+ [:tOPEN_ARRAY, "("],
352
+ [:tIDENTIFIER_VALUE, "create"],
353
+ [:tCOMMA, ","],
354
+ [:tIDENTIFIER_VALUE, "build"],
355
+ [:tCLOSE_ARRAY, ")"],
356
+ [:tCLOSE_ATTRIBUTE, "]"]
357
+ ]
358
+ assert_tokens source, expected_tokens
359
+ end
360
+
361
+ it 'matches NOT IN' do
362
+ source = '.send[message NOT IN (create, build)]'
363
+ expected_tokens = [
364
+ [:tNODE_TYPE, "send"],
365
+ [:tOPEN_ATTRIBUTE, "["],
366
+ [:tKEY, "message"],
367
+ [:tNOT_IN, "NOT IN"],
368
+ [:tOPEN_ARRAY, "("],
369
+ [:tIDENTIFIER_VALUE, "create"],
370
+ [:tCOMMA, ","],
371
+ [:tIDENTIFIER_VALUE, "build"],
372
+ [:tCLOSE_ARRAY, ")"],
373
+ [:tCLOSE_ATTRIBUTE, "]"]
374
+ ]
375
+ assert_tokens source, expected_tokens
376
+ end
377
+
378
+ it 'matches INCLUDES' do
379
+ source = '.send[arguments INCLUDES &block]'
380
+ expected_tokens = [
381
+ [:tNODE_TYPE, "send"],
382
+ [:tOPEN_ATTRIBUTE, "["],
383
+ [:tKEY, "arguments"],
384
+ [:tINCLUDES, "INCLUDES"],
385
+ [:tIDENTIFIER_VALUE, "&block"],
386
+ [:tCLOSE_ATTRIBUTE, "]"]
387
+ ]
388
+ assert_tokens source, expected_tokens
389
+ end
390
+ end
391
+
392
+ context 'nested attribute' do
393
+ it 'matches' do
394
+ source = '.send[receiver.message=:create]'
395
+ expected_tokens = [
396
+ [:tNODE_TYPE, "send"],
397
+ [:tOPEN_ATTRIBUTE, "["],
398
+ [:tKEY, "receiver.message"],
399
+ [:tEQUAL, "="],
400
+ [:tSYMBOL, :create],
401
+ [:tCLOSE_ATTRIBUTE, "]"]
402
+ ]
403
+ assert_tokens source, expected_tokens
404
+ end
405
+ end
406
+
407
+ context 'position' do
408
+ it 'matches :first-child' do
409
+ source = '.send[arguments=:create]:first-child'
410
+ expected_tokens = [
411
+ [:tNODE_TYPE, "send"],
412
+ [:tOPEN_ATTRIBUTE, "["],
413
+ [:tKEY, "arguments"],
414
+ [:tEQUAL, "="],
415
+ [:tSYMBOL, :create],
416
+ [:tCLOSE_ATTRIBUTE, "]"],
417
+ [:tINDEX, 0]
418
+ ]
419
+ assert_tokens source, expected_tokens
420
+ end
421
+
422
+ it 'matches :last-child' do
423
+ source = '.send[arguments=:create]:last-child'
424
+ expected_tokens = [
425
+ [:tNODE_TYPE, "send"],
426
+ [:tOPEN_ATTRIBUTE, "["],
427
+ [:tKEY, "arguments"],
428
+ [:tEQUAL, "="],
429
+ [:tSYMBOL, :create],
430
+ [:tCLOSE_ATTRIBUTE, "]"],
431
+ [:tINDEX, -1]
432
+ ]
433
+ assert_tokens source, expected_tokens
434
+ end
435
+
436
+ it 'matches :nth-child(1)' do
437
+ source = '.send[arguments=:create]:nth-child(1)'
438
+ expected_tokens = [
439
+ [:tNODE_TYPE, "send"],
440
+ [:tOPEN_ATTRIBUTE, "["],
441
+ [:tKEY, "arguments"],
442
+ [:tEQUAL, "="],
443
+ [:tSYMBOL, :create],
444
+ [:tCLOSE_ATTRIBUTE, "]"],
445
+ [:tINDEX, 0]
446
+ ]
447
+ assert_tokens source, expected_tokens
448
+ end
449
+
450
+ it 'matches :nth-last-child(1)' do
451
+ source = '.send[arguments=:create]:nth-last-child(1)'
452
+ expected_tokens = [
453
+ [:tNODE_TYPE, "send"],
454
+ [:tOPEN_ATTRIBUTE, "["],
455
+ [:tKEY, "arguments"],
456
+ [:tEQUAL, "="],
457
+ [:tSYMBOL, :create],
458
+ [:tCLOSE_ATTRIBUTE, "]"],
459
+ [:tINDEX, -1]
460
+ ]
461
+ assert_tokens source, expected_tokens
462
+ end
463
+ end
464
+
465
+ context 'descendant' do
466
+ it 'matches' do
467
+ source = '.class .send'
468
+ expected_tokens = [[:tNODE_TYPE, "class"], [:tNODE_TYPE, "send"]]
469
+ assert_tokens source, expected_tokens
470
+ end
471
+ end
472
+
473
+ context 'child' do
474
+ it 'matches' do
475
+ source = '.def > .send'
476
+ expected_tokens = [[:tNODE_TYPE, "def"], [:tCHILD, ">"], [:tNODE_TYPE, "send"]]
477
+ assert_tokens source, expected_tokens
478
+ end
479
+ end
480
+
481
+ context 'subsequent sibling' do
482
+ it 'matches' do
483
+ source = '.send ~ .send'
484
+ expected_tokens = [[:tNODE_TYPE, "send"], [:tSUBSEQUENT_SIBLING, "~"], [:tNODE_TYPE, "send"]]
485
+ assert_tokens source, expected_tokens
486
+ end
487
+ end
488
+
489
+ context 'next sibling' do
490
+ it 'matches' do
491
+ source = '.send + .send'
492
+ expected_tokens = [[:tNODE_TYPE, "send"], [:tNEXT_SIBLING, "+"], [:tNODE_TYPE, "send"]]
493
+ assert_tokens source, expected_tokens
494
+ end
495
+ end
496
+
497
+ context ':has' do
498
+ it 'matches' do
499
+ source = '.class:has(> .def)'
500
+ expected_tokens = [
501
+ [:tNODE_TYPE, "class"],
502
+ [:tHAS, "has"],
503
+ [:tOPEN_SELECTOR, "("],
504
+ [:tCHILD, ">"],
505
+ [:tNODE_TYPE, "def"],
506
+ [:tCLOSE_SELECTOR, ")"]
507
+ ]
508
+ assert_tokens source, expected_tokens
509
+ end
510
+ end
511
+ end
512
+ end