syntax_tree 5.3.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -1
  3. data/CHANGELOG.md +64 -1
  4. data/Gemfile.lock +2 -2
  5. data/README.md +28 -9
  6. data/Rakefile +12 -8
  7. data/bin/console +1 -0
  8. data/bin/whitequark +79 -0
  9. data/doc/changing_structure.md +16 -0
  10. data/lib/syntax_tree/basic_visitor.rb +44 -5
  11. data/lib/syntax_tree/cli.rb +2 -2
  12. data/lib/syntax_tree/dsl.rb +23 -11
  13. data/lib/syntax_tree/{visitor/field_visitor.rb → field_visitor.rb} +54 -55
  14. data/lib/syntax_tree/formatter.rb +1 -1
  15. data/lib/syntax_tree/index.rb +56 -54
  16. data/lib/syntax_tree/json_visitor.rb +55 -0
  17. data/lib/syntax_tree/language_server.rb +157 -2
  18. data/lib/syntax_tree/match_visitor.rb +120 -0
  19. data/lib/syntax_tree/mermaid.rb +177 -0
  20. data/lib/syntax_tree/mermaid_visitor.rb +69 -0
  21. data/lib/syntax_tree/{visitor/mutation_visitor.rb → mutation_visitor.rb} +27 -27
  22. data/lib/syntax_tree/node.rb +198 -107
  23. data/lib/syntax_tree/parser.rb +322 -118
  24. data/lib/syntax_tree/pretty_print_visitor.rb +83 -0
  25. data/lib/syntax_tree/reflection.rb +241 -0
  26. data/lib/syntax_tree/translation/parser.rb +3019 -0
  27. data/lib/syntax_tree/translation/rubocop_ast.rb +21 -0
  28. data/lib/syntax_tree/translation.rb +28 -0
  29. data/lib/syntax_tree/version.rb +1 -1
  30. data/lib/syntax_tree/with_scope.rb +244 -0
  31. data/lib/syntax_tree/yarv/basic_block.rb +53 -0
  32. data/lib/syntax_tree/yarv/calldata.rb +91 -0
  33. data/lib/syntax_tree/yarv/compiler.rb +110 -100
  34. data/lib/syntax_tree/yarv/control_flow_graph.rb +257 -0
  35. data/lib/syntax_tree/yarv/data_flow_graph.rb +338 -0
  36. data/lib/syntax_tree/yarv/decompiler.rb +1 -1
  37. data/lib/syntax_tree/yarv/disassembler.rb +104 -80
  38. data/lib/syntax_tree/yarv/instruction_sequence.rb +43 -18
  39. data/lib/syntax_tree/yarv/instructions.rb +203 -649
  40. data/lib/syntax_tree/yarv/legacy.rb +12 -24
  41. data/lib/syntax_tree/yarv/sea_of_nodes.rb +534 -0
  42. data/lib/syntax_tree/yarv.rb +18 -0
  43. data/lib/syntax_tree.rb +88 -56
  44. data/tasks/sorbet.rake +277 -0
  45. data/tasks/whitequark.rake +87 -0
  46. metadata +23 -11
  47. data/.gitmodules +0 -9
  48. data/lib/syntax_tree/language_server/inlay_hints.rb +0 -159
  49. data/lib/syntax_tree/visitor/environment.rb +0 -84
  50. data/lib/syntax_tree/visitor/json_visitor.rb +0 -55
  51. data/lib/syntax_tree/visitor/match_visitor.rb +0 -122
  52. data/lib/syntax_tree/visitor/pretty_print_visitor.rb +0 -85
  53. data/lib/syntax_tree/visitor/with_environment.rb +0 -140
@@ -0,0 +1,3019 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SyntaxTree
4
+ module Translation
5
+ # This visitor is responsible for converting the syntax tree produced by
6
+ # Syntax Tree into the syntax tree produced by the whitequark/parser gem.
7
+ class Parser < BasicVisitor
8
+ # Heredocs are represented _very_ differently in the parser gem from how
9
+ # they are represented in the Syntax Tree AST. This class is responsible
10
+ # for handling the translation.
11
+ class HeredocBuilder
12
+ Line = Struct.new(:value, :segments)
13
+
14
+ attr_reader :node, :segments
15
+
16
+ def initialize(node)
17
+ @node = node
18
+ @segments = []
19
+ end
20
+
21
+ def <<(segment)
22
+ if segment.type == :str && segments.last &&
23
+ segments.last.type == :str &&
24
+ !segments.last.children.first.end_with?("\n")
25
+ segments.last.children.first << segment.children.first
26
+ else
27
+ segments << segment
28
+ end
29
+ end
30
+
31
+ def trim!
32
+ return unless node.beginning.value[2] == "~"
33
+ lines = [Line.new(+"", [])]
34
+
35
+ segments.each do |segment|
36
+ lines.last.segments << segment
37
+
38
+ if segment.type == :str
39
+ lines.last.value << segment.children.first
40
+ lines << Line.new(+"", []) if lines.last.value.end_with?("\n")
41
+ end
42
+ end
43
+
44
+ lines.pop if lines.last.value.empty?
45
+ return if lines.empty?
46
+
47
+ segments.clear
48
+ lines.each do |line|
49
+ remaining = node.dedent
50
+
51
+ line.segments.each do |segment|
52
+ if segment.type == :str
53
+ if remaining > 0
54
+ whitespace = segment.children.first[/^\s{0,#{remaining}}/]
55
+ segment.children.first.sub!(/^#{whitespace}/, "")
56
+ remaining -= whitespace.length
57
+ end
58
+
59
+ if node.beginning.value[3] != "'" && segments.any? &&
60
+ segments.last.type == :str &&
61
+ segments.last.children.first.end_with?("\\\n")
62
+ segments.last.children.first.gsub!(/\\\n\z/, "")
63
+ segments.last.children.first.concat(segment.children.first)
64
+ elsif !segment.children.first.empty?
65
+ segments << segment
66
+ end
67
+ else
68
+ segments << segment
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ attr_reader :buffer, :stack
76
+
77
+ def initialize(buffer)
78
+ @buffer = buffer
79
+ @stack = []
80
+ end
81
+
82
+ # For each node that we visit, we keep track of it in a stack as we
83
+ # descend into its children. We do this so that child nodes can reflect on
84
+ # their parents if they need additional information about their context.
85
+ def visit(node)
86
+ stack << node
87
+ result = super
88
+ stack.pop
89
+ result
90
+ end
91
+
92
+ visit_methods do
93
+ # Visit an AliasNode node.
94
+ def visit_alias(node)
95
+ s(
96
+ :alias,
97
+ [visit(node.left), visit(node.right)],
98
+ smap_keyword_bare(
99
+ srange_length(node.start_char, 5),
100
+ srange_node(node)
101
+ )
102
+ )
103
+ end
104
+
105
+ # Visit an ARefNode.
106
+ def visit_aref(node)
107
+ if ::Parser::Builders::Default.emit_index
108
+ if node.index.nil?
109
+ s(
110
+ :index,
111
+ [visit(node.collection)],
112
+ smap_index(
113
+ srange_find(node.collection.end_char, node.end_char, "["),
114
+ srange_length(node.end_char, -1),
115
+ srange_node(node)
116
+ )
117
+ )
118
+ else
119
+ s(
120
+ :index,
121
+ [visit(node.collection)].concat(visit_all(node.index.parts)),
122
+ smap_index(
123
+ srange_find_between(node.collection, node.index, "["),
124
+ srange_length(node.end_char, -1),
125
+ srange_node(node)
126
+ )
127
+ )
128
+ end
129
+ else
130
+ if node.index.nil?
131
+ s(
132
+ :send,
133
+ [visit(node.collection), :[]],
134
+ smap_send_bare(
135
+ srange_find(node.collection.end_char, node.end_char, "[]"),
136
+ srange_node(node)
137
+ )
138
+ )
139
+ else
140
+ s(
141
+ :send,
142
+ [visit(node.collection), :[], *visit_all(node.index.parts)],
143
+ smap_send_bare(
144
+ srange(
145
+ srange_find_between(
146
+ node.collection,
147
+ node.index,
148
+ "["
149
+ ).begin_pos,
150
+ node.end_char
151
+ ),
152
+ srange_node(node)
153
+ )
154
+ )
155
+ end
156
+ end
157
+ end
158
+
159
+ # Visit an ARefField node.
160
+ def visit_aref_field(node)
161
+ if ::Parser::Builders::Default.emit_index
162
+ if node.index.nil?
163
+ s(
164
+ :indexasgn,
165
+ [visit(node.collection)],
166
+ smap_index(
167
+ srange_find(node.collection.end_char, node.end_char, "["),
168
+ srange_length(node.end_char, -1),
169
+ srange_node(node)
170
+ )
171
+ )
172
+ else
173
+ s(
174
+ :indexasgn,
175
+ [visit(node.collection)].concat(visit_all(node.index.parts)),
176
+ smap_index(
177
+ srange_find_between(node.collection, node.index, "["),
178
+ srange_length(node.end_char, -1),
179
+ srange_node(node)
180
+ )
181
+ )
182
+ end
183
+ else
184
+ if node.index.nil?
185
+ s(
186
+ :send,
187
+ [visit(node.collection), :[]=],
188
+ smap_send_bare(
189
+ srange_find(node.collection.end_char, node.end_char, "[]"),
190
+ srange_node(node)
191
+ )
192
+ )
193
+ else
194
+ s(
195
+ :send,
196
+ [visit(node.collection), :[]=].concat(
197
+ visit_all(node.index.parts)
198
+ ),
199
+ smap_send_bare(
200
+ srange(
201
+ srange_find_between(
202
+ node.collection,
203
+ node.index,
204
+ "["
205
+ ).begin_pos,
206
+ node.end_char
207
+ ),
208
+ srange_node(node)
209
+ )
210
+ )
211
+ end
212
+ end
213
+ end
214
+
215
+ # Visit an ArgBlock node.
216
+ def visit_arg_block(node)
217
+ s(
218
+ :block_pass,
219
+ [visit(node.value)],
220
+ smap_operator(srange_length(node.start_char, 1), srange_node(node))
221
+ )
222
+ end
223
+
224
+ # Visit an ArgStar node.
225
+ def visit_arg_star(node)
226
+ if stack[-3].is_a?(MLHSParen) && stack[-3].contents.is_a?(MLHS)
227
+ if node.value.nil?
228
+ s(:restarg, [], smap_variable(nil, srange_node(node)))
229
+ else
230
+ s(
231
+ :restarg,
232
+ [node.value.value.to_sym],
233
+ smap_variable(srange_node(node.value), srange_node(node))
234
+ )
235
+ end
236
+ else
237
+ s(
238
+ :splat,
239
+ node.value.nil? ? [] : [visit(node.value)],
240
+ smap_operator(
241
+ srange_length(node.start_char, 1),
242
+ srange_node(node)
243
+ )
244
+ )
245
+ end
246
+ end
247
+
248
+ # Visit an ArgsForward node.
249
+ def visit_args_forward(node)
250
+ s(:forwarded_args, [], smap(srange_node(node)))
251
+ end
252
+
253
+ # Visit an ArrayLiteral node.
254
+ def visit_array(node)
255
+ s(
256
+ :array,
257
+ node.contents ? visit_all(node.contents.parts) : [],
258
+ if node.lbracket.nil?
259
+ smap_collection_bare(srange_node(node))
260
+ else
261
+ smap_collection(
262
+ srange_node(node.lbracket),
263
+ srange_length(node.end_char, -1),
264
+ srange_node(node)
265
+ )
266
+ end
267
+ )
268
+ end
269
+
270
+ # Visit an AryPtn node.
271
+ def visit_aryptn(node)
272
+ type = :array_pattern
273
+ children = visit_all(node.requireds)
274
+
275
+ if node.rest.is_a?(VarField)
276
+ if !node.rest.value.nil?
277
+ children << s(:match_rest, [visit(node.rest)], nil)
278
+ elsif node.posts.empty? &&
279
+ node.rest.start_char == node.rest.end_char
280
+ # Here we have an implicit rest, as in [foo,]. parser has a
281
+ # specific type for these patterns.
282
+ type = :array_pattern_with_tail
283
+ else
284
+ children << s(:match_rest, [], nil)
285
+ end
286
+ end
287
+
288
+ if node.constant
289
+ s(
290
+ :const_pattern,
291
+ [
292
+ visit(node.constant),
293
+ s(
294
+ type,
295
+ children + visit_all(node.posts),
296
+ smap_collection_bare(
297
+ srange(node.constant.end_char + 1, node.end_char - 1)
298
+ )
299
+ )
300
+ ],
301
+ smap_collection(
302
+ srange_length(node.constant.end_char, 1),
303
+ srange_length(node.end_char, -1),
304
+ srange_node(node)
305
+ )
306
+ )
307
+ else
308
+ s(
309
+ type,
310
+ children + visit_all(node.posts),
311
+ if buffer.source[node.start_char] == "["
312
+ smap_collection(
313
+ srange_length(node.start_char, 1),
314
+ srange_length(node.end_char, -1),
315
+ srange_node(node)
316
+ )
317
+ else
318
+ smap_collection_bare(srange_node(node))
319
+ end
320
+ )
321
+ end
322
+ end
323
+
324
+ # Visit an Assign node.
325
+ def visit_assign(node)
326
+ target = visit(node.target)
327
+ location =
328
+ target
329
+ .location
330
+ .with_operator(srange_find_between(node.target, node.value, "="))
331
+ .with_expression(srange_node(node))
332
+
333
+ s(target.type, target.children + [visit(node.value)], location)
334
+ end
335
+
336
+ # Visit an Assoc node.
337
+ def visit_assoc(node)
338
+ if node.value.nil?
339
+ expression = srange(node.start_char, node.end_char - 1)
340
+
341
+ type, location =
342
+ if node.key.value.start_with?(/[A-Z]/)
343
+ [:const, smap_constant(nil, expression, expression)]
344
+ else
345
+ [:send, smap_send_bare(expression, expression)]
346
+ end
347
+
348
+ s(
349
+ :pair,
350
+ [
351
+ visit(node.key),
352
+ s(type, [nil, node.key.value.chomp(":").to_sym], location)
353
+ ],
354
+ smap_operator(
355
+ srange_length(node.key.end_char, -1),
356
+ srange_node(node)
357
+ )
358
+ )
359
+ else
360
+ s(
361
+ :pair,
362
+ [visit(node.key), visit(node.value)],
363
+ smap_operator(
364
+ srange_search_between(node.key, node.value, "=>") ||
365
+ srange_length(node.key.end_char, -1),
366
+ srange_node(node)
367
+ )
368
+ )
369
+ end
370
+ end
371
+
372
+ # Visit an AssocSplat node.
373
+ def visit_assoc_splat(node)
374
+ s(
375
+ :kwsplat,
376
+ [visit(node.value)],
377
+ smap_operator(srange_length(node.start_char, 2), srange_node(node))
378
+ )
379
+ end
380
+
381
+ # Visit a Backref node.
382
+ def visit_backref(node)
383
+ location = smap(srange_node(node))
384
+
385
+ if node.value.match?(/^\$\d+$/)
386
+ s(:nth_ref, [node.value[1..].to_i], location)
387
+ else
388
+ s(:back_ref, [node.value.to_sym], location)
389
+ end
390
+ end
391
+
392
+ # Visit a BareAssocHash node.
393
+ def visit_bare_assoc_hash(node)
394
+ s(
395
+ if ::Parser::Builders::Default.emit_kwargs &&
396
+ !stack[-2].is_a?(ArrayLiteral)
397
+ :kwargs
398
+ else
399
+ :hash
400
+ end,
401
+ visit_all(node.assocs),
402
+ smap_collection_bare(srange_node(node))
403
+ )
404
+ end
405
+
406
+ # Visit a BEGINBlock node.
407
+ def visit_BEGIN(node)
408
+ s(
409
+ :preexe,
410
+ [visit(node.statements)],
411
+ smap_keyword(
412
+ srange_length(node.start_char, 5),
413
+ srange_find(node.start_char + 5, node.statements.start_char, "{"),
414
+ srange_length(node.end_char, -1),
415
+ srange_node(node)
416
+ )
417
+ )
418
+ end
419
+
420
+ # Visit a Begin node.
421
+ def visit_begin(node)
422
+ location =
423
+ smap_collection(
424
+ srange_length(node.start_char, 5),
425
+ srange_length(node.end_char, -3),
426
+ srange_node(node)
427
+ )
428
+
429
+ if node.bodystmt.empty?
430
+ s(:kwbegin, [], location)
431
+ elsif node.bodystmt.rescue_clause.nil? &&
432
+ node.bodystmt.ensure_clause.nil? &&
433
+ node.bodystmt.else_clause.nil?
434
+ child = visit(node.bodystmt.statements)
435
+
436
+ s(
437
+ :kwbegin,
438
+ child.type == :begin ? child.children : [child],
439
+ location
440
+ )
441
+ else
442
+ s(:kwbegin, [visit(node.bodystmt)], location)
443
+ end
444
+ end
445
+
446
+ # Visit a Binary node.
447
+ def visit_binary(node)
448
+ case node.operator
449
+ when :|
450
+ current = -2
451
+ while stack[current].is_a?(Binary) && stack[current].operator == :|
452
+ current -= 1
453
+ end
454
+
455
+ if stack[current].is_a?(In)
456
+ s(:match_alt, [visit(node.left), visit(node.right)], nil)
457
+ else
458
+ visit(canonical_binary(node))
459
+ end
460
+ when :"=>", :"&&", :and, :"||", :or
461
+ s(
462
+ { "=>": :match_as, "&&": :and, "||": :or }.fetch(
463
+ node.operator,
464
+ node.operator
465
+ ),
466
+ [visit(node.left), visit(node.right)],
467
+ smap_operator(
468
+ srange_find_between(node.left, node.right, node.operator.to_s),
469
+ srange_node(node)
470
+ )
471
+ )
472
+ when :=~
473
+ # When you use a regular expression on the left hand side of a =~
474
+ # operator and it doesn't have interpolatoin, then its named capture
475
+ # groups introduce local variables into the scope. In this case the
476
+ # parser gem has a different node (match_with_lvasgn) instead of the
477
+ # regular send.
478
+ if node.left.is_a?(RegexpLiteral) && node.left.parts.length == 1 &&
479
+ node.left.parts.first.is_a?(TStringContent)
480
+ s(
481
+ :match_with_lvasgn,
482
+ [visit(node.left), visit(node.right)],
483
+ smap_operator(
484
+ srange_find_between(
485
+ node.left,
486
+ node.right,
487
+ node.operator.to_s
488
+ ),
489
+ srange_node(node)
490
+ )
491
+ )
492
+ else
493
+ visit(canonical_binary(node))
494
+ end
495
+ else
496
+ visit(canonical_binary(node))
497
+ end
498
+ end
499
+
500
+ # Visit a BlockArg node.
501
+ def visit_blockarg(node)
502
+ if node.name.nil?
503
+ s(:blockarg, [nil], smap_variable(nil, srange_node(node)))
504
+ else
505
+ s(
506
+ :blockarg,
507
+ [node.name.value.to_sym],
508
+ smap_variable(srange_node(node.name), srange_node(node))
509
+ )
510
+ end
511
+ end
512
+
513
+ # Visit a BlockVar node.
514
+ def visit_block_var(node)
515
+ shadowargs =
516
+ node.locals.map do |local|
517
+ s(
518
+ :shadowarg,
519
+ [local.value.to_sym],
520
+ smap_variable(srange_node(local), srange_node(local))
521
+ )
522
+ end
523
+
524
+ params = node.params
525
+ children =
526
+ if ::Parser::Builders::Default.emit_procarg0 && node.arg0?
527
+ # There is a special node type in the parser gem for when a single
528
+ # required parameter to a block would potentially be expanded
529
+ # automatically. We handle that case here.
530
+ required = params.requireds.first
531
+ procarg0 =
532
+ if ::Parser::Builders::Default.emit_arg_inside_procarg0 &&
533
+ required.is_a?(Ident)
534
+ s(
535
+ :procarg0,
536
+ [
537
+ s(
538
+ :arg,
539
+ [required.value.to_sym],
540
+ smap_variable(
541
+ srange_node(required),
542
+ srange_node(required)
543
+ )
544
+ )
545
+ ],
546
+ smap_collection_bare(srange_node(required))
547
+ )
548
+ else
549
+ child = visit(required)
550
+ s(:procarg0, child, child.location)
551
+ end
552
+
553
+ [procarg0]
554
+ else
555
+ visit(params).children
556
+ end
557
+
558
+ s(
559
+ :args,
560
+ children + shadowargs,
561
+ smap_collection(
562
+ srange_length(node.start_char, 1),
563
+ srange_length(node.end_char, -1),
564
+ srange_node(node)
565
+ )
566
+ )
567
+ end
568
+
569
+ # Visit a BodyStmt node.
570
+ def visit_bodystmt(node)
571
+ result = visit(node.statements)
572
+
573
+ if node.rescue_clause
574
+ rescue_node = visit(node.rescue_clause)
575
+
576
+ children = [result] + rescue_node.children
577
+ location = rescue_node.location
578
+
579
+ if node.else_clause
580
+ children.pop
581
+ children << visit(node.else_clause)
582
+
583
+ location =
584
+ smap_condition(
585
+ nil,
586
+ nil,
587
+ srange_length(node.else_clause.start_char - 3, -4),
588
+ nil,
589
+ srange(
590
+ location.expression.begin_pos,
591
+ node.else_clause.end_char
592
+ )
593
+ )
594
+ end
595
+
596
+ result = s(rescue_node.type, children, location)
597
+ end
598
+
599
+ if node.ensure_clause
600
+ ensure_node = visit(node.ensure_clause)
601
+
602
+ expression =
603
+ (
604
+ if result
605
+ result.location.expression.join(
606
+ ensure_node.location.expression
607
+ )
608
+ else
609
+ ensure_node.location.expression
610
+ end
611
+ )
612
+ location = ensure_node.location.with_expression(expression)
613
+
614
+ result =
615
+ s(ensure_node.type, [result] + ensure_node.children, location)
616
+ end
617
+
618
+ result
619
+ end
620
+
621
+ # Visit a Break node.
622
+ def visit_break(node)
623
+ s(
624
+ :break,
625
+ visit_all(node.arguments.parts),
626
+ smap_keyword_bare(
627
+ srange_length(node.start_char, 5),
628
+ srange_node(node)
629
+ )
630
+ )
631
+ end
632
+
633
+ # Visit a CallNode node.
634
+ def visit_call(node)
635
+ visit_command_call(
636
+ CommandCall.new(
637
+ receiver: node.receiver,
638
+ operator: node.operator,
639
+ message: node.message,
640
+ arguments: node.arguments,
641
+ block: nil,
642
+ location: node.location
643
+ )
644
+ )
645
+ end
646
+
647
+ # Visit a Case node.
648
+ def visit_case(node)
649
+ clauses = [node.consequent]
650
+ while clauses.last && !clauses.last.is_a?(Else)
651
+ clauses << clauses.last.consequent
652
+ end
653
+
654
+ else_token =
655
+ if clauses.last.is_a?(Else)
656
+ srange_length(clauses.last.start_char, 4)
657
+ end
658
+
659
+ s(
660
+ node.consequent.is_a?(In) ? :case_match : :case,
661
+ [visit(node.value)] + clauses.map { |clause| visit(clause) },
662
+ smap_condition(
663
+ srange_length(node.start_char, 4),
664
+ nil,
665
+ else_token,
666
+ srange_length(node.end_char, -3),
667
+ srange_node(node)
668
+ )
669
+ )
670
+ end
671
+
672
+ # Visit a CHAR node.
673
+ def visit_CHAR(node)
674
+ s(
675
+ :str,
676
+ [node.value[1..]],
677
+ smap_collection(
678
+ srange_length(node.start_char, 1),
679
+ nil,
680
+ srange_node(node)
681
+ )
682
+ )
683
+ end
684
+
685
+ # Visit a ClassDeclaration node.
686
+ def visit_class(node)
687
+ operator =
688
+ if node.superclass
689
+ srange_find_between(node.constant, node.superclass, "<")
690
+ end
691
+
692
+ s(
693
+ :class,
694
+ [
695
+ visit(node.constant),
696
+ visit(node.superclass),
697
+ visit(node.bodystmt)
698
+ ],
699
+ smap_definition(
700
+ srange_length(node.start_char, 5),
701
+ operator,
702
+ srange_node(node.constant),
703
+ srange_length(node.end_char, -3)
704
+ ).with_expression(srange_node(node))
705
+ )
706
+ end
707
+
708
+ # Visit a Command node.
709
+ def visit_command(node)
710
+ visit_command_call(
711
+ CommandCall.new(
712
+ receiver: nil,
713
+ operator: nil,
714
+ message: node.message,
715
+ arguments: node.arguments,
716
+ block: node.block,
717
+ location: node.location
718
+ )
719
+ )
720
+ end
721
+
722
+ # Visit a CommandCall node.
723
+ def visit_command_call(node)
724
+ children = [
725
+ visit(node.receiver),
726
+ node.message == :call ? :call : node.message.value.to_sym
727
+ ]
728
+
729
+ begin_token = nil
730
+ end_token = nil
731
+
732
+ case node.arguments
733
+ when Args
734
+ children += visit_all(node.arguments.parts)
735
+ when ArgParen
736
+ case node.arguments.arguments
737
+ when nil
738
+ # skip
739
+ when ArgsForward
740
+ children << visit(node.arguments.arguments)
741
+ else
742
+ children += visit_all(node.arguments.arguments.parts)
743
+ end
744
+
745
+ begin_token = srange_length(node.arguments.start_char, 1)
746
+ end_token = srange_length(node.arguments.end_char, -1)
747
+ end
748
+
749
+ dot_bound =
750
+ if node.arguments
751
+ node.arguments.start_char
752
+ elsif node.block
753
+ node.block.start_char
754
+ else
755
+ node.end_char
756
+ end
757
+
758
+ expression =
759
+ if node.arguments.is_a?(ArgParen)
760
+ srange(node.start_char, node.arguments.end_char)
761
+ elsif node.arguments.is_a?(Args) && node.arguments.parts.any?
762
+ last_part = node.arguments.parts.last
763
+ end_char =
764
+ if last_part.is_a?(Heredoc)
765
+ last_part.beginning.end_char
766
+ else
767
+ last_part.end_char
768
+ end
769
+
770
+ srange(node.start_char, end_char)
771
+ elsif node.block
772
+ srange_node(node.message)
773
+ else
774
+ srange_node(node)
775
+ end
776
+
777
+ call =
778
+ s(
779
+ if node.operator.is_a?(Op) && node.operator.value == "&."
780
+ :csend
781
+ else
782
+ :send
783
+ end,
784
+ children,
785
+ smap_send(
786
+ if node.operator == :"::"
787
+ srange_find(
788
+ node.receiver.end_char,
789
+ if node.message == :call
790
+ dot_bound
791
+ else
792
+ node.message.start_char
793
+ end,
794
+ "::"
795
+ )
796
+ elsif node.operator
797
+ srange_node(node.operator)
798
+ end,
799
+ node.message == :call ? nil : srange_node(node.message),
800
+ begin_token,
801
+ end_token,
802
+ expression
803
+ )
804
+ )
805
+
806
+ if node.block
807
+ type, arguments = block_children(node.block)
808
+
809
+ s(
810
+ type,
811
+ [call, arguments, visit(node.block.bodystmt)],
812
+ smap_collection(
813
+ srange_node(node.block.opening),
814
+ srange_length(
815
+ node.end_char,
816
+ node.block.opening.is_a?(Kw) ? -3 : -1
817
+ ),
818
+ srange_node(node)
819
+ )
820
+ )
821
+ else
822
+ call
823
+ end
824
+ end
825
+
826
+ # Visit a Const node.
827
+ def visit_const(node)
828
+ s(
829
+ :const,
830
+ [nil, node.value.to_sym],
831
+ smap_constant(nil, srange_node(node), srange_node(node))
832
+ )
833
+ end
834
+
835
+ # Visit a ConstPathField node.
836
+ def visit_const_path_field(node)
837
+ if node.parent.is_a?(VarRef) && node.parent.value.is_a?(Kw) &&
838
+ node.parent.value.value == "self" && node.constant.is_a?(Ident)
839
+ s(:send, [visit(node.parent), :"#{node.constant.value}="], nil)
840
+ else
841
+ s(
842
+ :casgn,
843
+ [visit(node.parent), node.constant.value.to_sym],
844
+ smap_constant(
845
+ srange_find_between(node.parent, node.constant, "::"),
846
+ srange_node(node.constant),
847
+ srange_node(node)
848
+ )
849
+ )
850
+ end
851
+ end
852
+
853
+ # Visit a ConstPathRef node.
854
+ def visit_const_path_ref(node)
855
+ s(
856
+ :const,
857
+ [visit(node.parent), node.constant.value.to_sym],
858
+ smap_constant(
859
+ srange_find_between(node.parent, node.constant, "::"),
860
+ srange_node(node.constant),
861
+ srange_node(node)
862
+ )
863
+ )
864
+ end
865
+
866
+ # Visit a ConstRef node.
867
+ def visit_const_ref(node)
868
+ s(
869
+ :const,
870
+ [nil, node.constant.value.to_sym],
871
+ smap_constant(nil, srange_node(node.constant), srange_node(node))
872
+ )
873
+ end
874
+
875
+ # Visit a CVar node.
876
+ def visit_cvar(node)
877
+ s(
878
+ :cvar,
879
+ [node.value.to_sym],
880
+ smap_variable(srange_node(node), srange_node(node))
881
+ )
882
+ end
883
+
884
+ # Visit a DefNode node.
885
+ def visit_def(node)
886
+ name = node.name.value.to_sym
887
+ args =
888
+ case node.params
889
+ when Params
890
+ child = visit(node.params)
891
+
892
+ s(
893
+ child.type,
894
+ child.children,
895
+ smap_collection_bare(child.location&.expression)
896
+ )
897
+ when Paren
898
+ child = visit(node.params.contents)
899
+
900
+ s(
901
+ child.type,
902
+ child.children,
903
+ smap_collection(
904
+ srange_length(node.params.start_char, 1),
905
+ srange_length(node.params.end_char, -1),
906
+ srange_node(node.params)
907
+ )
908
+ )
909
+ else
910
+ s(:args, [], smap_collection_bare(nil))
911
+ end
912
+
913
+ location =
914
+ if node.endless?
915
+ smap_method_definition(
916
+ srange_length(node.start_char, 3),
917
+ nil,
918
+ srange_node(node.name),
919
+ nil,
920
+ srange_find_between(
921
+ (node.params || node.name),
922
+ node.bodystmt,
923
+ "="
924
+ ),
925
+ srange_node(node)
926
+ )
927
+ else
928
+ smap_method_definition(
929
+ srange_length(node.start_char, 3),
930
+ nil,
931
+ srange_node(node.name),
932
+ srange_length(node.end_char, -3),
933
+ nil,
934
+ srange_node(node)
935
+ )
936
+ end
937
+
938
+ if node.target
939
+ target =
940
+ node.target.is_a?(Paren) ? node.target.contents : node.target
941
+
942
+ s(
943
+ :defs,
944
+ [visit(target), name, args, visit(node.bodystmt)],
945
+ smap_method_definition(
946
+ location.keyword,
947
+ srange_node(node.operator),
948
+ location.name,
949
+ location.end,
950
+ location.assignment,
951
+ location.expression
952
+ )
953
+ )
954
+ else
955
+ s(:def, [name, args, visit(node.bodystmt)], location)
956
+ end
957
+ end
958
+
959
+ # Visit a Defined node.
960
+ def visit_defined(node)
961
+ paren_range = (node.start_char + 8)...node.end_char
962
+ begin_token, end_token =
963
+ if buffer.source[paren_range].include?("(")
964
+ [
965
+ srange_find(paren_range.begin, paren_range.end, "("),
966
+ srange_length(node.end_char, -1)
967
+ ]
968
+ end
969
+
970
+ s(
971
+ :defined?,
972
+ [visit(node.value)],
973
+ smap_keyword(
974
+ srange_length(node.start_char, 8),
975
+ begin_token,
976
+ end_token,
977
+ srange_node(node)
978
+ )
979
+ )
980
+ end
981
+
982
+ # Visit a DynaSymbol node.
983
+ def visit_dyna_symbol(node)
984
+ location =
985
+ if node.quote
986
+ smap_collection(
987
+ srange_length(node.start_char, node.quote.length),
988
+ srange_length(node.end_char, -1),
989
+ srange_node(node)
990
+ )
991
+ else
992
+ smap_collection_bare(srange_node(node))
993
+ end
994
+
995
+ if node.parts.length == 1 && node.parts.first.is_a?(TStringContent)
996
+ s(:sym, ["\"#{node.parts.first.value}\"".undump.to_sym], location)
997
+ else
998
+ s(:dsym, visit_all(node.parts), location)
999
+ end
1000
+ end
1001
+
1002
+ # Visit an Else node.
1003
+ def visit_else(node)
1004
+ if node.statements.empty? && stack[-2].is_a?(Case)
1005
+ s(:empty_else, [], nil)
1006
+ else
1007
+ visit(node.statements)
1008
+ end
1009
+ end
1010
+
1011
+ # Visit an Elsif node.
1012
+ def visit_elsif(node)
1013
+ else_token =
1014
+ case node.consequent
1015
+ when Elsif
1016
+ srange_length(node.consequent.start_char, 5)
1017
+ when Else
1018
+ srange_length(node.consequent.start_char, 4)
1019
+ end
1020
+
1021
+ expression = srange(node.start_char, node.statements.end_char - 1)
1022
+
1023
+ s(
1024
+ :if,
1025
+ [
1026
+ visit(node.predicate),
1027
+ visit(node.statements),
1028
+ visit(node.consequent)
1029
+ ],
1030
+ smap_condition(
1031
+ srange_length(node.start_char, 5),
1032
+ nil,
1033
+ else_token,
1034
+ nil,
1035
+ expression
1036
+ )
1037
+ )
1038
+ end
1039
+
1040
+ # Visit an ENDBlock node.
1041
+ def visit_END(node)
1042
+ s(
1043
+ :postexe,
1044
+ [visit(node.statements)],
1045
+ smap_keyword(
1046
+ srange_length(node.start_char, 3),
1047
+ srange_find(node.start_char + 3, node.statements.start_char, "{"),
1048
+ srange_length(node.end_char, -1),
1049
+ srange_node(node)
1050
+ )
1051
+ )
1052
+ end
1053
+
1054
+ # Visit an Ensure node.
1055
+ def visit_ensure(node)
1056
+ start_char = node.start_char
1057
+ end_char =
1058
+ if node.statements.empty?
1059
+ start_char + 6
1060
+ else
1061
+ node.statements.body.last.end_char
1062
+ end
1063
+
1064
+ s(
1065
+ :ensure,
1066
+ [visit(node.statements)],
1067
+ smap_condition(
1068
+ srange_length(start_char, 6),
1069
+ nil,
1070
+ nil,
1071
+ nil,
1072
+ srange(start_char, end_char)
1073
+ )
1074
+ )
1075
+ end
1076
+
1077
+ # Visit a Field node.
1078
+ def visit_field(node)
1079
+ message =
1080
+ case stack[-2]
1081
+ when Assign, MLHS
1082
+ Ident.new(
1083
+ value: "#{node.name.value}=",
1084
+ location: node.name.location
1085
+ )
1086
+ else
1087
+ node.name
1088
+ end
1089
+
1090
+ visit_command_call(
1091
+ CommandCall.new(
1092
+ receiver: node.parent,
1093
+ operator: node.operator,
1094
+ message: message,
1095
+ arguments: nil,
1096
+ block: nil,
1097
+ location: node.location
1098
+ )
1099
+ )
1100
+ end
1101
+
1102
+ # Visit a FloatLiteral node.
1103
+ def visit_float(node)
1104
+ operator =
1105
+ if %w[+ -].include?(buffer.source[node.start_char])
1106
+ srange_length(node.start_char, 1)
1107
+ end
1108
+
1109
+ s(
1110
+ :float,
1111
+ [node.value.to_f],
1112
+ smap_operator(operator, srange_node(node))
1113
+ )
1114
+ end
1115
+
1116
+ # Visit a FndPtn node.
1117
+ def visit_fndptn(node)
1118
+ left, right =
1119
+ [node.left, node.right].map do |child|
1120
+ location =
1121
+ smap_operator(
1122
+ srange_length(child.start_char, 1),
1123
+ srange_node(child)
1124
+ )
1125
+
1126
+ if child.is_a?(VarField) && child.value.nil?
1127
+ s(:match_rest, [], location)
1128
+ else
1129
+ s(:match_rest, [visit(child)], location)
1130
+ end
1131
+ end
1132
+
1133
+ inner =
1134
+ s(
1135
+ :find_pattern,
1136
+ [left, *visit_all(node.values), right],
1137
+ smap_collection(
1138
+ srange_length(node.start_char, 1),
1139
+ srange_length(node.end_char, -1),
1140
+ srange_node(node)
1141
+ )
1142
+ )
1143
+
1144
+ if node.constant
1145
+ s(:const_pattern, [visit(node.constant), inner], nil)
1146
+ else
1147
+ inner
1148
+ end
1149
+ end
1150
+
1151
+ # Visit a For node.
1152
+ def visit_for(node)
1153
+ s(
1154
+ :for,
1155
+ [visit(node.index), visit(node.collection), visit(node.statements)],
1156
+ smap_for(
1157
+ srange_length(node.start_char, 3),
1158
+ srange_find_between(node.index, node.collection, "in"),
1159
+ srange_search_between(node.collection, node.statements, "do") ||
1160
+ srange_search_between(node.collection, node.statements, ";"),
1161
+ srange_length(node.end_char, -3),
1162
+ srange_node(node)
1163
+ )
1164
+ )
1165
+ end
1166
+
1167
+ # Visit a GVar node.
1168
+ def visit_gvar(node)
1169
+ s(
1170
+ :gvar,
1171
+ [node.value.to_sym],
1172
+ smap_variable(srange_node(node), srange_node(node))
1173
+ )
1174
+ end
1175
+
1176
+ # Visit a HashLiteral node.
1177
+ def visit_hash(node)
1178
+ s(
1179
+ :hash,
1180
+ visit_all(node.assocs),
1181
+ smap_collection(
1182
+ srange_length(node.start_char, 1),
1183
+ srange_length(node.end_char, -1),
1184
+ srange_node(node)
1185
+ )
1186
+ )
1187
+ end
1188
+
1189
+ # Visit a Heredoc node.
1190
+ def visit_heredoc(node)
1191
+ heredoc = HeredocBuilder.new(node)
1192
+
1193
+ # For each part of the heredoc, if it's a string content node, split
1194
+ # it into multiple string content nodes, one for each line. Otherwise,
1195
+ # visit the node as normal.
1196
+ node.parts.each do |part|
1197
+ if part.is_a?(TStringContent) && part.value.count("\n") > 1
1198
+ index = part.start_char
1199
+ lines = part.value.split("\n")
1200
+
1201
+ lines.each do |line|
1202
+ length = line.length + 1
1203
+ location = smap_collection_bare(srange_length(index, length))
1204
+
1205
+ heredoc << s(:str, ["#{line}\n"], location)
1206
+ index += length
1207
+ end
1208
+ else
1209
+ heredoc << visit(part)
1210
+ end
1211
+ end
1212
+
1213
+ # Now that we have all of the pieces on the heredoc, we can trim it if
1214
+ # it is a heredoc that supports trimming (i.e., it has a ~ on the
1215
+ # declaration).
1216
+ heredoc.trim!
1217
+
1218
+ # Generate the location for the heredoc, which goes from the
1219
+ # declaration to the ending delimiter.
1220
+ location =
1221
+ smap_heredoc(
1222
+ srange_node(node.beginning),
1223
+ srange(
1224
+ if node.parts.empty?
1225
+ node.beginning.end_char + 1
1226
+ else
1227
+ node.parts.first.start_char
1228
+ end,
1229
+ node.ending.start_char
1230
+ ),
1231
+ srange(node.ending.start_char, node.ending.end_char - 1)
1232
+ )
1233
+
1234
+ # Finally, decide which kind of heredoc node to generate based on its
1235
+ # declaration and contents.
1236
+ if node.beginning.value.match?(/`\w+`\z/)
1237
+ s(:xstr, heredoc.segments, location)
1238
+ elsif heredoc.segments.length == 1
1239
+ segment = heredoc.segments.first
1240
+ s(segment.type, segment.children, location)
1241
+ else
1242
+ s(:dstr, heredoc.segments, location)
1243
+ end
1244
+ end
1245
+
1246
+ # Visit a HshPtn node.
1247
+ def visit_hshptn(node)
1248
+ children =
1249
+ node.keywords.map do |(keyword, value)|
1250
+ next s(:pair, [visit(keyword), visit(value)], nil) if value
1251
+
1252
+ case keyword
1253
+ when DynaSymbol
1254
+ raise if keyword.parts.length > 1
1255
+ s(:match_var, [keyword.parts.first.value.to_sym], nil)
1256
+ when Label
1257
+ s(:match_var, [keyword.value.chomp(":").to_sym], nil)
1258
+ end
1259
+ end
1260
+
1261
+ if node.keyword_rest.is_a?(VarField)
1262
+ children << if node.keyword_rest.value.nil?
1263
+ s(:match_rest, [], nil)
1264
+ elsif node.keyword_rest.value == :nil
1265
+ s(:match_nil_pattern, [], nil)
1266
+ else
1267
+ s(:match_rest, [visit(node.keyword_rest)], nil)
1268
+ end
1269
+ end
1270
+
1271
+ inner = s(:hash_pattern, children, nil)
1272
+ if node.constant
1273
+ s(:const_pattern, [visit(node.constant), inner], nil)
1274
+ else
1275
+ inner
1276
+ end
1277
+ end
1278
+
1279
+ # Visit an Ident node.
1280
+ def visit_ident(node)
1281
+ s(
1282
+ :lvar,
1283
+ [node.value.to_sym],
1284
+ smap_variable(srange_node(node), srange_node(node))
1285
+ )
1286
+ end
1287
+
1288
+ # Visit an IfNode node.
1289
+ def visit_if(node)
1290
+ predicate =
1291
+ case node.predicate
1292
+ when RangeNode
1293
+ type =
1294
+ node.predicate.operator.value == ".." ? :iflipflop : :eflipflop
1295
+ s(type, visit(node.predicate).children, nil)
1296
+ when RegexpLiteral
1297
+ s(:match_current_line, [visit(node.predicate)], nil)
1298
+ when Unary
1299
+ if node.predicate.operator.value == "!" &&
1300
+ node.predicate.statement.is_a?(RegexpLiteral)
1301
+ s(
1302
+ :send,
1303
+ [
1304
+ s(:match_current_line, [visit(node.predicate.statement)]),
1305
+ :!
1306
+ ],
1307
+ nil
1308
+ )
1309
+ else
1310
+ visit(node.predicate)
1311
+ end
1312
+ else
1313
+ visit(node.predicate)
1314
+ end
1315
+
1316
+ s(
1317
+ :if,
1318
+ [predicate, visit(node.statements), visit(node.consequent)],
1319
+ if node.modifier?
1320
+ smap_keyword_bare(
1321
+ srange_find_between(node.statements, node.predicate, "if"),
1322
+ srange_node(node)
1323
+ )
1324
+ else
1325
+ begin_start = node.predicate.end_char
1326
+ begin_end =
1327
+ if node.statements.empty?
1328
+ node.statements.end_char
1329
+ else
1330
+ node.statements.body.first.start_char
1331
+ end
1332
+
1333
+ begin_token =
1334
+ if buffer.source[begin_start...begin_end].include?("then")
1335
+ srange_find(begin_start, begin_end, "then")
1336
+ elsif buffer.source[begin_start...begin_end].include?(";")
1337
+ srange_find(begin_start, begin_end, ";")
1338
+ end
1339
+
1340
+ else_token =
1341
+ case node.consequent
1342
+ when Elsif
1343
+ srange_length(node.consequent.start_char, 5)
1344
+ when Else
1345
+ srange_length(node.consequent.start_char, 4)
1346
+ end
1347
+
1348
+ smap_condition(
1349
+ srange_length(node.start_char, 2),
1350
+ begin_token,
1351
+ else_token,
1352
+ srange_length(node.end_char, -3),
1353
+ srange_node(node)
1354
+ )
1355
+ end
1356
+ )
1357
+ end
1358
+
1359
+ # Visit an IfOp node.
1360
+ def visit_if_op(node)
1361
+ s(
1362
+ :if,
1363
+ [visit(node.predicate), visit(node.truthy), visit(node.falsy)],
1364
+ smap_ternary(
1365
+ srange_find_between(node.predicate, node.truthy, "?"),
1366
+ srange_find_between(node.truthy, node.falsy, ":"),
1367
+ srange_node(node)
1368
+ )
1369
+ )
1370
+ end
1371
+
1372
+ # Visit an Imaginary node.
1373
+ def visit_imaginary(node)
1374
+ s(
1375
+ :complex,
1376
+ [
1377
+ # We have to do an eval here in order to get the value in case
1378
+ # it's something like 42ri. to_c will not give the right value in
1379
+ # that case. Maybe there's an API for this but I can't find it.
1380
+ eval(node.value)
1381
+ ],
1382
+ smap_operator(nil, srange_node(node))
1383
+ )
1384
+ end
1385
+
1386
+ # Visit an In node.
1387
+ def visit_in(node)
1388
+ case node.pattern
1389
+ when IfNode
1390
+ s(
1391
+ :in_pattern,
1392
+ [
1393
+ visit(node.pattern.statements),
1394
+ s(:if_guard, [visit(node.pattern.predicate)], nil),
1395
+ visit(node.statements)
1396
+ ],
1397
+ nil
1398
+ )
1399
+ when UnlessNode
1400
+ s(
1401
+ :in_pattern,
1402
+ [
1403
+ visit(node.pattern.statements),
1404
+ s(:unless_guard, [visit(node.pattern.predicate)], nil),
1405
+ visit(node.statements)
1406
+ ],
1407
+ nil
1408
+ )
1409
+ else
1410
+ begin_token =
1411
+ srange_search_between(node.pattern, node.statements, "then")
1412
+
1413
+ end_char =
1414
+ if begin_token || node.statements.empty?
1415
+ node.statements.end_char - 1
1416
+ else
1417
+ node.statements.body.last.start_char
1418
+ end
1419
+
1420
+ s(
1421
+ :in_pattern,
1422
+ [visit(node.pattern), nil, visit(node.statements)],
1423
+ smap_keyword(
1424
+ srange_length(node.start_char, 2),
1425
+ begin_token,
1426
+ nil,
1427
+ srange(node.start_char, end_char)
1428
+ )
1429
+ )
1430
+ end
1431
+ end
1432
+
1433
+ # Visit an Int node.
1434
+ def visit_int(node)
1435
+ operator =
1436
+ if %w[+ -].include?(buffer.source[node.start_char])
1437
+ srange_length(node.start_char, 1)
1438
+ end
1439
+
1440
+ s(:int, [node.value.to_i], smap_operator(operator, srange_node(node)))
1441
+ end
1442
+
1443
+ # Visit an IVar node.
1444
+ def visit_ivar(node)
1445
+ s(
1446
+ :ivar,
1447
+ [node.value.to_sym],
1448
+ smap_variable(srange_node(node), srange_node(node))
1449
+ )
1450
+ end
1451
+
1452
+ # Visit a Kw node.
1453
+ def visit_kw(node)
1454
+ location = smap(srange_node(node))
1455
+
1456
+ case node.value
1457
+ when "__FILE__"
1458
+ s(:str, [buffer.name], location)
1459
+ when "__LINE__"
1460
+ s(
1461
+ :int,
1462
+ [node.location.start_line + buffer.first_line - 1],
1463
+ location
1464
+ )
1465
+ when "__ENCODING__"
1466
+ if ::Parser::Builders::Default.emit_encoding
1467
+ s(:__ENCODING__, [], location)
1468
+ else
1469
+ s(:const, [s(:const, [nil, :Encoding], nil), :UTF_8], location)
1470
+ end
1471
+ else
1472
+ s(node.value.to_sym, [], location)
1473
+ end
1474
+ end
1475
+
1476
+ # Visit a KwRestParam node.
1477
+ def visit_kwrest_param(node)
1478
+ if node.name.nil?
1479
+ s(:kwrestarg, [], smap_variable(nil, srange_node(node)))
1480
+ else
1481
+ s(
1482
+ :kwrestarg,
1483
+ [node.name.value.to_sym],
1484
+ smap_variable(srange_node(node.name), srange_node(node))
1485
+ )
1486
+ end
1487
+ end
1488
+
1489
+ # Visit a Label node.
1490
+ def visit_label(node)
1491
+ s(
1492
+ :sym,
1493
+ [node.value.chomp(":").to_sym],
1494
+ smap_collection_bare(srange(node.start_char, node.end_char - 1))
1495
+ )
1496
+ end
1497
+
1498
+ # Visit a Lambda node.
1499
+ def visit_lambda(node)
1500
+ args =
1501
+ node.params.is_a?(LambdaVar) ? node.params : node.params.contents
1502
+ args_node = visit(args)
1503
+
1504
+ type = :block
1505
+ if args.empty? && (maximum = num_block_type(node.statements))
1506
+ type = :numblock
1507
+ args_node = maximum
1508
+ end
1509
+
1510
+ begin_token, end_token =
1511
+ if (
1512
+ srange =
1513
+ srange_search_between(node.params, node.statements, "{")
1514
+ )
1515
+ [srange, srange_length(node.end_char, -1)]
1516
+ else
1517
+ [
1518
+ srange_find_between(node.params, node.statements, "do"),
1519
+ srange_length(node.end_char, -3)
1520
+ ]
1521
+ end
1522
+
1523
+ selector = srange_length(node.start_char, 2)
1524
+
1525
+ s(
1526
+ type,
1527
+ [
1528
+ if ::Parser::Builders::Default.emit_lambda
1529
+ s(:lambda, [], smap(selector))
1530
+ else
1531
+ s(:send, [nil, :lambda], smap_send_bare(selector, selector))
1532
+ end,
1533
+ args_node,
1534
+ visit(node.statements)
1535
+ ],
1536
+ smap_collection(begin_token, end_token, srange_node(node))
1537
+ )
1538
+ end
1539
+
1540
+ # Visit a LambdaVar node.
1541
+ def visit_lambda_var(node)
1542
+ shadowargs =
1543
+ node.locals.map do |local|
1544
+ s(
1545
+ :shadowarg,
1546
+ [local.value.to_sym],
1547
+ smap_variable(srange_node(local), srange_node(local))
1548
+ )
1549
+ end
1550
+
1551
+ location =
1552
+ if node.start_char == node.end_char
1553
+ smap_collection_bare(nil)
1554
+ else
1555
+ smap_collection(
1556
+ srange_length(node.start_char, 1),
1557
+ srange_length(node.end_char, -1),
1558
+ srange_node(node)
1559
+ )
1560
+ end
1561
+
1562
+ s(:args, visit(node.params).children + shadowargs, location)
1563
+ end
1564
+
1565
+ # Visit an MAssign node.
1566
+ def visit_massign(node)
1567
+ s(
1568
+ :masgn,
1569
+ [visit(node.target), visit(node.value)],
1570
+ smap_operator(
1571
+ srange_find_between(node.target, node.value, "="),
1572
+ srange_node(node)
1573
+ )
1574
+ )
1575
+ end
1576
+
1577
+ # Visit a MethodAddBlock node.
1578
+ def visit_method_add_block(node)
1579
+ case node.call
1580
+ when Break, Next, ReturnNode
1581
+ type, arguments = block_children(node.block)
1582
+ call = visit(node.call)
1583
+
1584
+ s(
1585
+ call.type,
1586
+ [
1587
+ s(
1588
+ type,
1589
+ [*call.children, arguments, visit(node.block.bodystmt)],
1590
+ nil
1591
+ )
1592
+ ],
1593
+ nil
1594
+ )
1595
+ when ARef, Super, ZSuper
1596
+ type, arguments = block_children(node.block)
1597
+
1598
+ s(
1599
+ type,
1600
+ [visit(node.call), arguments, visit(node.block.bodystmt)],
1601
+ nil
1602
+ )
1603
+ else
1604
+ visit_command_call(
1605
+ CommandCall.new(
1606
+ receiver: node.call.receiver,
1607
+ operator: node.call.operator,
1608
+ message: node.call.message,
1609
+ arguments: node.call.arguments,
1610
+ block: node.block,
1611
+ location: node.location
1612
+ )
1613
+ )
1614
+ end
1615
+ end
1616
+
1617
+ # Visit an MLHS node.
1618
+ def visit_mlhs(node)
1619
+ s(
1620
+ :mlhs,
1621
+ node.parts.map do |part|
1622
+ if part.is_a?(Ident)
1623
+ s(
1624
+ :arg,
1625
+ [part.value.to_sym],
1626
+ smap_variable(srange_node(part), srange_node(part))
1627
+ )
1628
+ else
1629
+ visit(part)
1630
+ end
1631
+ end,
1632
+ smap_collection_bare(srange_node(node))
1633
+ )
1634
+ end
1635
+
1636
+ # Visit an MLHSParen node.
1637
+ def visit_mlhs_paren(node)
1638
+ child = visit(node.contents)
1639
+
1640
+ s(
1641
+ child.type,
1642
+ child.children,
1643
+ smap_collection(
1644
+ srange_length(node.start_char, 1),
1645
+ srange_length(node.end_char, -1),
1646
+ srange_node(node)
1647
+ )
1648
+ )
1649
+ end
1650
+
1651
+ # Visit a ModuleDeclaration node.
1652
+ def visit_module(node)
1653
+ s(
1654
+ :module,
1655
+ [visit(node.constant), visit(node.bodystmt)],
1656
+ smap_definition(
1657
+ srange_length(node.start_char, 6),
1658
+ nil,
1659
+ srange_node(node.constant),
1660
+ srange_length(node.end_char, -3)
1661
+ ).with_expression(srange_node(node))
1662
+ )
1663
+ end
1664
+
1665
+ # Visit an MRHS node.
1666
+ def visit_mrhs(node)
1667
+ visit_array(
1668
+ ArrayLiteral.new(
1669
+ lbracket: nil,
1670
+ contents: Args.new(parts: node.parts, location: node.location),
1671
+ location: node.location
1672
+ )
1673
+ )
1674
+ end
1675
+
1676
+ # Visit a Next node.
1677
+ def visit_next(node)
1678
+ s(
1679
+ :next,
1680
+ visit_all(node.arguments.parts),
1681
+ smap_keyword_bare(
1682
+ srange_length(node.start_char, 4),
1683
+ srange_node(node)
1684
+ )
1685
+ )
1686
+ end
1687
+
1688
+ # Visit a Not node.
1689
+ def visit_not(node)
1690
+ if node.statement.nil?
1691
+ begin_token = srange_find(node.start_char, nil, "(")
1692
+ end_token = srange_find(node.start_char, nil, ")")
1693
+
1694
+ s(
1695
+ :send,
1696
+ [
1697
+ s(
1698
+ :begin,
1699
+ [],
1700
+ smap_collection(
1701
+ begin_token,
1702
+ end_token,
1703
+ begin_token.join(end_token)
1704
+ )
1705
+ ),
1706
+ :!
1707
+ ],
1708
+ smap_send_bare(
1709
+ srange_length(node.start_char, 3),
1710
+ srange_node(node)
1711
+ )
1712
+ )
1713
+ else
1714
+ begin_token, end_token =
1715
+ if node.parentheses?
1716
+ [
1717
+ srange_find(
1718
+ node.start_char + 3,
1719
+ node.statement.start_char,
1720
+ "("
1721
+ ),
1722
+ srange_length(node.end_char, -1)
1723
+ ]
1724
+ end
1725
+
1726
+ s(
1727
+ :send,
1728
+ [visit(node.statement), :!],
1729
+ smap_send(
1730
+ nil,
1731
+ srange_length(node.start_char, 3),
1732
+ begin_token,
1733
+ end_token,
1734
+ srange_node(node)
1735
+ )
1736
+ )
1737
+ end
1738
+ end
1739
+
1740
+ # Visit an OpAssign node.
1741
+ def visit_opassign(node)
1742
+ target = visit(node.target)
1743
+ location =
1744
+ target
1745
+ .location
1746
+ .with_expression(srange_node(node))
1747
+ .with_operator(srange_node(node.operator))
1748
+
1749
+ case node.operator.value
1750
+ when "||="
1751
+ s(:or_asgn, [target, visit(node.value)], location)
1752
+ when "&&="
1753
+ s(:and_asgn, [target, visit(node.value)], location)
1754
+ else
1755
+ s(
1756
+ :op_asgn,
1757
+ [
1758
+ target,
1759
+ node.operator.value.chomp("=").to_sym,
1760
+ visit(node.value)
1761
+ ],
1762
+ location
1763
+ )
1764
+ end
1765
+ end
1766
+
1767
+ # Visit a Params node.
1768
+ def visit_params(node)
1769
+ children = []
1770
+
1771
+ children +=
1772
+ node.requireds.map do |required|
1773
+ case required
1774
+ when MLHSParen
1775
+ visit(required)
1776
+ else
1777
+ s(
1778
+ :arg,
1779
+ [required.value.to_sym],
1780
+ smap_variable(srange_node(required), srange_node(required))
1781
+ )
1782
+ end
1783
+ end
1784
+
1785
+ children +=
1786
+ node.optionals.map do |(name, value)|
1787
+ s(
1788
+ :optarg,
1789
+ [name.value.to_sym, visit(value)],
1790
+ smap_variable(
1791
+ srange_node(name),
1792
+ srange_node(name).join(srange_node(value))
1793
+ ).with_operator(srange_find_between(name, value, "="))
1794
+ )
1795
+ end
1796
+
1797
+ if node.rest && !node.rest.is_a?(ExcessedComma)
1798
+ children << visit(node.rest)
1799
+ end
1800
+
1801
+ children +=
1802
+ node.posts.map do |post|
1803
+ s(
1804
+ :arg,
1805
+ [post.value.to_sym],
1806
+ smap_variable(srange_node(post), srange_node(post))
1807
+ )
1808
+ end
1809
+
1810
+ children +=
1811
+ node.keywords.map do |(name, value)|
1812
+ key = name.value.chomp(":").to_sym
1813
+
1814
+ if value
1815
+ s(
1816
+ :kwoptarg,
1817
+ [key, visit(value)],
1818
+ smap_variable(
1819
+ srange(name.start_char, name.end_char - 1),
1820
+ srange_node(name).join(srange_node(value))
1821
+ )
1822
+ )
1823
+ else
1824
+ s(
1825
+ :kwarg,
1826
+ [key],
1827
+ smap_variable(
1828
+ srange(name.start_char, name.end_char - 1),
1829
+ srange_node(name)
1830
+ )
1831
+ )
1832
+ end
1833
+ end
1834
+
1835
+ case node.keyword_rest
1836
+ when nil, ArgsForward
1837
+ # do nothing
1838
+ when :nil
1839
+ children << s(
1840
+ :kwnilarg,
1841
+ [],
1842
+ smap_variable(srange_length(node.end_char, -3), srange_node(node))
1843
+ )
1844
+ else
1845
+ children << visit(node.keyword_rest)
1846
+ end
1847
+
1848
+ children << visit(node.block) if node.block
1849
+
1850
+ if node.keyword_rest.is_a?(ArgsForward)
1851
+ location = smap(srange_node(node.keyword_rest))
1852
+
1853
+ # If there are no other arguments and we have the emit_forward_arg
1854
+ # option enabled, then the entire argument list is represented by a
1855
+ # single forward_args node.
1856
+ if children.empty? && !::Parser::Builders::Default.emit_forward_arg
1857
+ return s(:forward_args, [], location)
1858
+ end
1859
+
1860
+ # Otherwise, we need to insert a forward_arg node into the list of
1861
+ # parameters before any keyword rest or block parameters.
1862
+ index =
1863
+ node.requireds.length + node.optionals.length +
1864
+ node.keywords.length
1865
+ children.insert(index, s(:forward_arg, [], location))
1866
+ end
1867
+
1868
+ location =
1869
+ unless children.empty?
1870
+ first = children.first.location.expression
1871
+ last = children.last.location.expression
1872
+ smap_collection_bare(first.join(last))
1873
+ end
1874
+
1875
+ s(:args, children, location)
1876
+ end
1877
+
1878
+ # Visit a Paren node.
1879
+ def visit_paren(node)
1880
+ location =
1881
+ smap_collection(
1882
+ srange_length(node.start_char, 1),
1883
+ srange_length(node.end_char, -1),
1884
+ srange_node(node)
1885
+ )
1886
+
1887
+ if node.contents.nil? ||
1888
+ (node.contents.is_a?(Statements) && node.contents.empty?)
1889
+ s(:begin, [], location)
1890
+ else
1891
+ child = visit(node.contents)
1892
+ child.type == :begin ? child : s(:begin, [child], location)
1893
+ end
1894
+ end
1895
+
1896
+ # Visit a PinnedBegin node.
1897
+ def visit_pinned_begin(node)
1898
+ s(
1899
+ :pin,
1900
+ [
1901
+ s(
1902
+ :begin,
1903
+ [visit(node.statement)],
1904
+ smap_collection(
1905
+ srange_length(node.start_char + 1, 1),
1906
+ srange_length(node.end_char, -1),
1907
+ srange(node.start_char + 1, node.end_char)
1908
+ )
1909
+ )
1910
+ ],
1911
+ smap_send_bare(srange_length(node.start_char, 1), srange_node(node))
1912
+ )
1913
+ end
1914
+
1915
+ # Visit a PinnedVarRef node.
1916
+ def visit_pinned_var_ref(node)
1917
+ s(
1918
+ :pin,
1919
+ [visit(node.value)],
1920
+ smap_send_bare(srange_length(node.start_char, 1), srange_node(node))
1921
+ )
1922
+ end
1923
+
1924
+ # Visit a Program node.
1925
+ def visit_program(node)
1926
+ visit(node.statements)
1927
+ end
1928
+
1929
+ # Visit a QSymbols node.
1930
+ def visit_qsymbols(node)
1931
+ parts =
1932
+ node.elements.map do |element|
1933
+ SymbolLiteral.new(value: element, location: element.location)
1934
+ end
1935
+
1936
+ visit_array(
1937
+ ArrayLiteral.new(
1938
+ lbracket: node.beginning,
1939
+ contents: Args.new(parts: parts, location: node.location),
1940
+ location: node.location
1941
+ )
1942
+ )
1943
+ end
1944
+
1945
+ # Visit a QWords node.
1946
+ def visit_qwords(node)
1947
+ visit_array(
1948
+ ArrayLiteral.new(
1949
+ lbracket: node.beginning,
1950
+ contents: Args.new(parts: node.elements, location: node.location),
1951
+ location: node.location
1952
+ )
1953
+ )
1954
+ end
1955
+
1956
+ # Visit a RangeNode node.
1957
+ def visit_range(node)
1958
+ s(
1959
+ node.operator.value == ".." ? :irange : :erange,
1960
+ [visit(node.left), visit(node.right)],
1961
+ smap_operator(srange_node(node.operator), srange_node(node))
1962
+ )
1963
+ end
1964
+
1965
+ # Visit an RAssign node.
1966
+ def visit_rassign(node)
1967
+ s(
1968
+ node.operator.value == "=>" ? :match_pattern : :match_pattern_p,
1969
+ [visit(node.value), visit(node.pattern)],
1970
+ smap_operator(srange_node(node.operator), srange_node(node))
1971
+ )
1972
+ end
1973
+
1974
+ # Visit a Rational node.
1975
+ def visit_rational(node)
1976
+ s(:rational, [node.value.to_r], smap_operator(nil, srange_node(node)))
1977
+ end
1978
+
1979
+ # Visit a Redo node.
1980
+ def visit_redo(node)
1981
+ s(:redo, [], smap_keyword_bare(srange_node(node), srange_node(node)))
1982
+ end
1983
+
1984
+ # Visit a RegexpLiteral node.
1985
+ def visit_regexp_literal(node)
1986
+ s(
1987
+ :regexp,
1988
+ visit_all(node.parts).push(
1989
+ s(
1990
+ :regopt,
1991
+ node.ending.scan(/[a-z]/).sort.map(&:to_sym),
1992
+ smap(srange_length(node.end_char, -(node.ending.length - 1)))
1993
+ )
1994
+ ),
1995
+ smap_collection(
1996
+ srange_length(node.start_char, node.beginning.length),
1997
+ srange_length(node.end_char - node.ending.length, 1),
1998
+ srange_node(node)
1999
+ )
2000
+ )
2001
+ end
2002
+
2003
+ # Visit a Rescue node.
2004
+ def visit_rescue(node)
2005
+ # In the parser gem, there is a separation between the rescue node and
2006
+ # the rescue body. They have different bounds, so we have to calculate
2007
+ # those here.
2008
+ start_char = node.start_char
2009
+
2010
+ body_end_char =
2011
+ if node.statements.empty?
2012
+ start_char + 6
2013
+ else
2014
+ node.statements.body.last.end_char
2015
+ end
2016
+
2017
+ end_char =
2018
+ if node.consequent
2019
+ end_node = node.consequent
2020
+ end_node = end_node.consequent while end_node.consequent
2021
+
2022
+ if end_node.statements.empty?
2023
+ start_char + 6
2024
+ else
2025
+ end_node.statements.body.last.end_char
2026
+ end
2027
+ else
2028
+ body_end_char
2029
+ end
2030
+
2031
+ # These locations are reused for multiple children.
2032
+ keyword = srange_length(start_char, 6)
2033
+ body_expression = srange(start_char, body_end_char)
2034
+ expression = srange(start_char, end_char)
2035
+
2036
+ exceptions =
2037
+ case node.exception&.exceptions
2038
+ when nil
2039
+ nil
2040
+ when MRHS
2041
+ visit_array(
2042
+ ArrayLiteral.new(
2043
+ lbracket: nil,
2044
+ contents:
2045
+ Args.new(
2046
+ parts: node.exception.exceptions.parts,
2047
+ location: node.exception.exceptions.location
2048
+ ),
2049
+ location: node.exception.exceptions.location
2050
+ )
2051
+ )
2052
+ else
2053
+ visit_array(
2054
+ ArrayLiteral.new(
2055
+ lbracket: nil,
2056
+ contents:
2057
+ Args.new(
2058
+ parts: [node.exception.exceptions],
2059
+ location: node.exception.exceptions.location
2060
+ ),
2061
+ location: node.exception.exceptions.location
2062
+ )
2063
+ )
2064
+ end
2065
+
2066
+ resbody =
2067
+ if node.exception.nil?
2068
+ s(
2069
+ :resbody,
2070
+ [nil, nil, visit(node.statements)],
2071
+ smap_rescue_body(keyword, nil, nil, body_expression)
2072
+ )
2073
+ elsif node.exception.variable.nil?
2074
+ s(
2075
+ :resbody,
2076
+ [exceptions, nil, visit(node.statements)],
2077
+ smap_rescue_body(keyword, nil, nil, body_expression)
2078
+ )
2079
+ else
2080
+ s(
2081
+ :resbody,
2082
+ [
2083
+ exceptions,
2084
+ visit(node.exception.variable),
2085
+ visit(node.statements)
2086
+ ],
2087
+ smap_rescue_body(
2088
+ keyword,
2089
+ srange_find(
2090
+ node.start_char + 6,
2091
+ node.exception.variable.start_char,
2092
+ "=>"
2093
+ ),
2094
+ nil,
2095
+ body_expression
2096
+ )
2097
+ )
2098
+ end
2099
+
2100
+ children = [resbody]
2101
+ if node.consequent
2102
+ children += visit(node.consequent).children
2103
+ else
2104
+ children << nil
2105
+ end
2106
+
2107
+ s(:rescue, children, smap_condition_bare(expression))
2108
+ end
2109
+
2110
+ # Visit a RescueMod node.
2111
+ def visit_rescue_mod(node)
2112
+ keyword = srange_find_between(node.statement, node.value, "rescue")
2113
+
2114
+ s(
2115
+ :rescue,
2116
+ [
2117
+ visit(node.statement),
2118
+ s(
2119
+ :resbody,
2120
+ [nil, nil, visit(node.value)],
2121
+ smap_rescue_body(
2122
+ keyword,
2123
+ nil,
2124
+ nil,
2125
+ keyword.join(srange_node(node.value))
2126
+ )
2127
+ ),
2128
+ nil
2129
+ ],
2130
+ smap_condition_bare(srange_node(node))
2131
+ )
2132
+ end
2133
+
2134
+ # Visit a RestParam node.
2135
+ def visit_rest_param(node)
2136
+ if node.name
2137
+ s(
2138
+ :restarg,
2139
+ [node.name.value.to_sym],
2140
+ smap_variable(srange_node(node.name), srange_node(node))
2141
+ )
2142
+ else
2143
+ s(:restarg, [], smap_variable(nil, srange_node(node)))
2144
+ end
2145
+ end
2146
+
2147
+ # Visit a Retry node.
2148
+ def visit_retry(node)
2149
+ s(:retry, [], smap_keyword_bare(srange_node(node), srange_node(node)))
2150
+ end
2151
+
2152
+ # Visit a ReturnNode node.
2153
+ def visit_return(node)
2154
+ s(
2155
+ :return,
2156
+ node.arguments ? visit_all(node.arguments.parts) : [],
2157
+ smap_keyword_bare(
2158
+ srange_length(node.start_char, 6),
2159
+ srange_node(node)
2160
+ )
2161
+ )
2162
+ end
2163
+
2164
+ # Visit an SClass node.
2165
+ def visit_sclass(node)
2166
+ s(
2167
+ :sclass,
2168
+ [visit(node.target), visit(node.bodystmt)],
2169
+ smap_definition(
2170
+ srange_length(node.start_char, 5),
2171
+ srange_find(node.start_char + 5, node.target.start_char, "<<"),
2172
+ nil,
2173
+ srange_length(node.end_char, -3)
2174
+ ).with_expression(srange_node(node))
2175
+ )
2176
+ end
2177
+
2178
+ # Visit a Statements node.
2179
+ def visit_statements(node)
2180
+ children =
2181
+ node.body.reject do |child|
2182
+ child.is_a?(Comment) || child.is_a?(EmbDoc) ||
2183
+ child.is_a?(EndContent) || child.is_a?(VoidStmt)
2184
+ end
2185
+
2186
+ case children.length
2187
+ when 0
2188
+ nil
2189
+ when 1
2190
+ visit(children.first)
2191
+ else
2192
+ s(
2193
+ :begin,
2194
+ visit_all(children),
2195
+ smap_collection_bare(
2196
+ srange(children.first.start_char, children.last.end_char)
2197
+ )
2198
+ )
2199
+ end
2200
+ end
2201
+
2202
+ # Visit a StringConcat node.
2203
+ def visit_string_concat(node)
2204
+ s(
2205
+ :dstr,
2206
+ [visit(node.left), visit(node.right)],
2207
+ smap_collection_bare(srange_node(node))
2208
+ )
2209
+ end
2210
+
2211
+ # Visit a StringDVar node.
2212
+ def visit_string_dvar(node)
2213
+ visit(node.variable)
2214
+ end
2215
+
2216
+ # Visit a StringEmbExpr node.
2217
+ def visit_string_embexpr(node)
2218
+ s(
2219
+ :begin,
2220
+ visit(node.statements).then { |child| child ? [child] : [] },
2221
+ smap_collection(
2222
+ srange_length(node.start_char, 2),
2223
+ srange_length(node.end_char, -1),
2224
+ srange_node(node)
2225
+ )
2226
+ )
2227
+ end
2228
+
2229
+ # Visit a StringLiteral node.
2230
+ def visit_string_literal(node)
2231
+ location =
2232
+ if node.quote
2233
+ smap_collection(
2234
+ srange_length(node.start_char, node.quote.length),
2235
+ srange_length(node.end_char, -1),
2236
+ srange_node(node)
2237
+ )
2238
+ else
2239
+ smap_collection_bare(srange_node(node))
2240
+ end
2241
+
2242
+ if node.parts.empty?
2243
+ s(:str, [""], location)
2244
+ elsif node.parts.length == 1 && node.parts.first.is_a?(TStringContent)
2245
+ child = visit(node.parts.first)
2246
+ s(child.type, child.children, location)
2247
+ else
2248
+ s(:dstr, visit_all(node.parts), location)
2249
+ end
2250
+ end
2251
+
2252
+ # Visit a Super node.
2253
+ def visit_super(node)
2254
+ if node.arguments.is_a?(Args)
2255
+ s(
2256
+ :super,
2257
+ visit_all(node.arguments.parts),
2258
+ smap_keyword_bare(
2259
+ srange_length(node.start_char, 5),
2260
+ srange_node(node)
2261
+ )
2262
+ )
2263
+ else
2264
+ case node.arguments.arguments
2265
+ when nil
2266
+ s(
2267
+ :super,
2268
+ [],
2269
+ smap_keyword(
2270
+ srange_length(node.start_char, 5),
2271
+ srange_find(node.start_char + 5, node.end_char, "("),
2272
+ srange_length(node.end_char, -1),
2273
+ srange_node(node)
2274
+ )
2275
+ )
2276
+ when ArgsForward
2277
+ s(:super, [visit(node.arguments.arguments)], nil)
2278
+ else
2279
+ s(
2280
+ :super,
2281
+ visit_all(node.arguments.arguments.parts),
2282
+ smap_keyword(
2283
+ srange_length(node.start_char, 5),
2284
+ srange_find(node.start_char + 5, node.end_char, "("),
2285
+ srange_length(node.end_char, -1),
2286
+ srange_node(node)
2287
+ )
2288
+ )
2289
+ end
2290
+ end
2291
+ end
2292
+
2293
+ # Visit a SymbolLiteral node.
2294
+ def visit_symbol_literal(node)
2295
+ begin_token =
2296
+ if buffer.source[node.start_char] == ":"
2297
+ srange_length(node.start_char, 1)
2298
+ end
2299
+
2300
+ s(
2301
+ :sym,
2302
+ [node.value.value.to_sym],
2303
+ smap_collection(begin_token, nil, srange_node(node))
2304
+ )
2305
+ end
2306
+
2307
+ # Visit a Symbols node.
2308
+ def visit_symbols(node)
2309
+ parts =
2310
+ node.elements.map do |element|
2311
+ part = element.parts.first
2312
+
2313
+ if element.parts.length == 1 && part.is_a?(TStringContent)
2314
+ SymbolLiteral.new(value: part, location: part.location)
2315
+ else
2316
+ DynaSymbol.new(
2317
+ parts: element.parts,
2318
+ quote: nil,
2319
+ location: element.location
2320
+ )
2321
+ end
2322
+ end
2323
+
2324
+ visit_array(
2325
+ ArrayLiteral.new(
2326
+ lbracket: node.beginning,
2327
+ contents: Args.new(parts: parts, location: node.location),
2328
+ location: node.location
2329
+ )
2330
+ )
2331
+ end
2332
+
2333
+ # Visit a TopConstField node.
2334
+ def visit_top_const_field(node)
2335
+ s(
2336
+ :casgn,
2337
+ [
2338
+ s(:cbase, [], smap(srange_length(node.start_char, 2))),
2339
+ node.constant.value.to_sym
2340
+ ],
2341
+ smap_constant(
2342
+ srange_length(node.start_char, 2),
2343
+ srange_node(node.constant),
2344
+ srange_node(node)
2345
+ )
2346
+ )
2347
+ end
2348
+
2349
+ # Visit a TopConstRef node.
2350
+ def visit_top_const_ref(node)
2351
+ s(
2352
+ :const,
2353
+ [
2354
+ s(:cbase, [], smap(srange_length(node.start_char, 2))),
2355
+ node.constant.value.to_sym
2356
+ ],
2357
+ smap_constant(
2358
+ srange_length(node.start_char, 2),
2359
+ srange_node(node.constant),
2360
+ srange_node(node)
2361
+ )
2362
+ )
2363
+ end
2364
+
2365
+ # Visit a TStringContent node.
2366
+ def visit_tstring_content(node)
2367
+ dumped = node.value.gsub(/([^[:ascii:]])/) { $1.dump[1...-1] }
2368
+
2369
+ s(
2370
+ :str,
2371
+ ["\"#{dumped}\"".undump],
2372
+ smap_collection_bare(srange_node(node))
2373
+ )
2374
+ end
2375
+
2376
+ # Visit a Unary node.
2377
+ def visit_unary(node)
2378
+ # Special handling here for flipflops
2379
+ if node.statement.is_a?(Paren) &&
2380
+ node.statement.contents.is_a?(Statements) &&
2381
+ node.statement.contents.body.length == 1 &&
2382
+ (range = node.statement.contents.body.first).is_a?(RangeNode) &&
2383
+ node.operator == "!"
2384
+ type = range.operator.value == ".." ? :iflipflop : :eflipflop
2385
+ return(
2386
+ s(
2387
+ :send,
2388
+ [s(:begin, [s(type, visit(range).children, nil)], nil), :!],
2389
+ nil
2390
+ )
2391
+ )
2392
+ end
2393
+
2394
+ visit(canonical_unary(node))
2395
+ end
2396
+
2397
+ # Visit an Undef node.
2398
+ def visit_undef(node)
2399
+ s(
2400
+ :undef,
2401
+ visit_all(node.symbols),
2402
+ smap_keyword_bare(
2403
+ srange_length(node.start_char, 5),
2404
+ srange_node(node)
2405
+ )
2406
+ )
2407
+ end
2408
+
2409
+ # Visit an UnlessNode node.
2410
+ def visit_unless(node)
2411
+ predicate =
2412
+ case node.predicate
2413
+ when RegexpLiteral
2414
+ s(:match_current_line, [visit(node.predicate)], nil)
2415
+ when Unary
2416
+ if node.predicate.operator.value == "!" &&
2417
+ node.predicate.statement.is_a?(RegexpLiteral)
2418
+ s(
2419
+ :send,
2420
+ [
2421
+ s(:match_current_line, [visit(node.predicate.statement)]),
2422
+ :!
2423
+ ],
2424
+ nil
2425
+ )
2426
+ else
2427
+ visit(node.predicate)
2428
+ end
2429
+ else
2430
+ visit(node.predicate)
2431
+ end
2432
+
2433
+ s(
2434
+ :if,
2435
+ [predicate, visit(node.consequent), visit(node.statements)],
2436
+ if node.modifier?
2437
+ smap_keyword_bare(
2438
+ srange_find_between(node.statements, node.predicate, "unless"),
2439
+ srange_node(node)
2440
+ )
2441
+ else
2442
+ smap_condition(
2443
+ srange_length(node.start_char, 6),
2444
+ srange_search_between(node.predicate, node.statements, "then"),
2445
+ nil,
2446
+ srange_length(node.end_char, -3),
2447
+ srange_node(node)
2448
+ )
2449
+ end
2450
+ )
2451
+ end
2452
+
2453
+ # Visit an UntilNode node.
2454
+ def visit_until(node)
2455
+ s(
2456
+ loop_post?(node) ? :until_post : :until,
2457
+ [visit(node.predicate), visit(node.statements)],
2458
+ if node.modifier?
2459
+ smap_keyword_bare(
2460
+ srange_find_between(node.statements, node.predicate, "until"),
2461
+ srange_node(node)
2462
+ )
2463
+ else
2464
+ smap_keyword(
2465
+ srange_length(node.start_char, 5),
2466
+ srange_search_between(node.predicate, node.statements, "do") ||
2467
+ srange_search_between(node.predicate, node.statements, ";"),
2468
+ srange_length(node.end_char, -3),
2469
+ srange_node(node)
2470
+ )
2471
+ end
2472
+ )
2473
+ end
2474
+
2475
+ # Visit a VarField node.
2476
+ def visit_var_field(node)
2477
+ name = node.value.value.to_sym
2478
+ match_var =
2479
+ [stack[-3], stack[-2]].any? do |parent|
2480
+ case parent
2481
+ when AryPtn, FndPtn, HshPtn, In, RAssign
2482
+ true
2483
+ when Binary
2484
+ parent.operator == :"=>"
2485
+ else
2486
+ false
2487
+ end
2488
+ end
2489
+
2490
+ if match_var
2491
+ s(
2492
+ :match_var,
2493
+ [name],
2494
+ smap_variable(srange_node(node.value), srange_node(node.value))
2495
+ )
2496
+ elsif node.value.is_a?(Const)
2497
+ s(
2498
+ :casgn,
2499
+ [nil, name],
2500
+ smap_constant(nil, srange_node(node.value), srange_node(node))
2501
+ )
2502
+ else
2503
+ location = smap_variable(srange_node(node), srange_node(node))
2504
+
2505
+ case node.value
2506
+ when CVar
2507
+ s(:cvasgn, [name], location)
2508
+ when GVar
2509
+ s(:gvasgn, [name], location)
2510
+ when Ident
2511
+ s(:lvasgn, [name], location)
2512
+ when IVar
2513
+ s(:ivasgn, [name], location)
2514
+ when VarRef
2515
+ s(:lvasgn, [name], location)
2516
+ else
2517
+ s(:match_rest, [], nil)
2518
+ end
2519
+ end
2520
+ end
2521
+
2522
+ # Visit a VarRef node.
2523
+ def visit_var_ref(node)
2524
+ visit(node.value)
2525
+ end
2526
+
2527
+ # Visit a VCall node.
2528
+ def visit_vcall(node)
2529
+ visit_command_call(
2530
+ CommandCall.new(
2531
+ receiver: nil,
2532
+ operator: nil,
2533
+ message: node.value,
2534
+ arguments: nil,
2535
+ block: nil,
2536
+ location: node.location
2537
+ )
2538
+ )
2539
+ end
2540
+
2541
+ # Visit a When node.
2542
+ def visit_when(node)
2543
+ keyword = srange_length(node.start_char, 4)
2544
+ begin_token =
2545
+ if buffer.source[node.statements.start_char] == ";"
2546
+ srange_length(node.statements.start_char, 1)
2547
+ end
2548
+
2549
+ end_char =
2550
+ if node.statements.body.empty?
2551
+ node.statements.end_char
2552
+ else
2553
+ node.statements.body.last.end_char
2554
+ end
2555
+
2556
+ s(
2557
+ :when,
2558
+ visit_all(node.arguments.parts) + [visit(node.statements)],
2559
+ smap_keyword(
2560
+ keyword,
2561
+ begin_token,
2562
+ nil,
2563
+ srange(keyword.begin_pos, end_char)
2564
+ )
2565
+ )
2566
+ end
2567
+
2568
+ # Visit a WhileNode node.
2569
+ def visit_while(node)
2570
+ s(
2571
+ loop_post?(node) ? :while_post : :while,
2572
+ [visit(node.predicate), visit(node.statements)],
2573
+ if node.modifier?
2574
+ smap_keyword_bare(
2575
+ srange_find_between(node.statements, node.predicate, "while"),
2576
+ srange_node(node)
2577
+ )
2578
+ else
2579
+ smap_keyword(
2580
+ srange_length(node.start_char, 5),
2581
+ srange_search_between(node.predicate, node.statements, "do") ||
2582
+ srange_search_between(node.predicate, node.statements, ";"),
2583
+ srange_length(node.end_char, -3),
2584
+ srange_node(node)
2585
+ )
2586
+ end
2587
+ )
2588
+ end
2589
+
2590
+ # Visit a Word node.
2591
+ def visit_word(node)
2592
+ visit_string_literal(
2593
+ StringLiteral.new(
2594
+ parts: node.parts,
2595
+ quote: nil,
2596
+ location: node.location
2597
+ )
2598
+ )
2599
+ end
2600
+
2601
+ # Visit a Words node.
2602
+ def visit_words(node)
2603
+ visit_array(
2604
+ ArrayLiteral.new(
2605
+ lbracket: node.beginning,
2606
+ contents: Args.new(parts: node.elements, location: node.location),
2607
+ location: node.location
2608
+ )
2609
+ )
2610
+ end
2611
+
2612
+ # Visit an XStringLiteral node.
2613
+ def visit_xstring_literal(node)
2614
+ s(
2615
+ :xstr,
2616
+ visit_all(node.parts),
2617
+ smap_collection(
2618
+ srange_length(
2619
+ node.start_char,
2620
+ buffer.source[node.start_char] == "%" ? 3 : 1
2621
+ ),
2622
+ srange_length(node.end_char, -1),
2623
+ srange_node(node)
2624
+ )
2625
+ )
2626
+ end
2627
+
2628
+ def visit_yield(node)
2629
+ case node.arguments
2630
+ when nil
2631
+ s(
2632
+ :yield,
2633
+ [],
2634
+ smap_keyword_bare(
2635
+ srange_length(node.start_char, 5),
2636
+ srange_node(node)
2637
+ )
2638
+ )
2639
+ when Args
2640
+ s(
2641
+ :yield,
2642
+ visit_all(node.arguments.parts),
2643
+ smap_keyword_bare(
2644
+ srange_length(node.start_char, 5),
2645
+ srange_node(node)
2646
+ )
2647
+ )
2648
+ else
2649
+ s(
2650
+ :yield,
2651
+ visit_all(node.arguments.contents.parts),
2652
+ smap_keyword(
2653
+ srange_length(node.start_char, 5),
2654
+ srange_length(node.arguments.start_char, 1),
2655
+ srange_length(node.end_char, -1),
2656
+ srange_node(node)
2657
+ )
2658
+ )
2659
+ end
2660
+ end
2661
+
2662
+ # Visit a ZSuper node.
2663
+ def visit_zsuper(node)
2664
+ s(
2665
+ :zsuper,
2666
+ [],
2667
+ smap_keyword_bare(
2668
+ srange_length(node.start_char, 5),
2669
+ srange_node(node)
2670
+ )
2671
+ )
2672
+ end
2673
+ end
2674
+
2675
+ private
2676
+
2677
+ def block_children(node)
2678
+ arguments =
2679
+ if node.block_var
2680
+ visit(node.block_var)
2681
+ else
2682
+ s(:args, [], smap_collection_bare(nil))
2683
+ end
2684
+
2685
+ type = :block
2686
+ if !node.block_var && (maximum = num_block_type(node.bodystmt))
2687
+ type = :numblock
2688
+ arguments = maximum
2689
+ end
2690
+
2691
+ [type, arguments]
2692
+ end
2693
+
2694
+ # Convert a Unary node into a canonical CommandCall node.
2695
+ def canonical_unary(node)
2696
+ # For integers and floats with a leading + or -, parser represents them
2697
+ # as just their values with the signs attached.
2698
+ if %w[+ -].include?(node.operator) &&
2699
+ (node.statement.is_a?(Int) || node.statement.is_a?(FloatLiteral))
2700
+ return(
2701
+ node.statement.class.new(
2702
+ value: "#{node.operator}#{node.statement.value}",
2703
+ location: node.location
2704
+ )
2705
+ )
2706
+ end
2707
+
2708
+ value = { "+" => "+@", "-" => "-@" }.fetch(node.operator, node.operator)
2709
+ length = node.operator.length
2710
+
2711
+ CommandCall.new(
2712
+ receiver: node.statement,
2713
+ operator: nil,
2714
+ message:
2715
+ Op.new(
2716
+ value: value,
2717
+ location:
2718
+ Location.new(
2719
+ start_line: node.location.start_line,
2720
+ start_char: node.start_char,
2721
+ start_column: node.location.start_column,
2722
+ end_line: node.location.start_line,
2723
+ end_char: node.start_char + length,
2724
+ end_column: node.location.start_column + length
2725
+ )
2726
+ ),
2727
+ arguments: nil,
2728
+ block: nil,
2729
+ location: node.location
2730
+ )
2731
+ end
2732
+
2733
+ # Convert a Binary node into a canonical CommandCall node.
2734
+ def canonical_binary(node)
2735
+ operator = node.operator.to_s
2736
+
2737
+ start_char = node.left.end_char
2738
+ end_char = node.right.start_char
2739
+
2740
+ index = buffer.source[start_char...end_char].index(operator)
2741
+ start_line =
2742
+ node.location.start_line +
2743
+ buffer.source[start_char...index].count("\n")
2744
+ start_column =
2745
+ index - (buffer.source[start_char...index].rindex("\n") || 0)
2746
+
2747
+ op_location =
2748
+ Location.new(
2749
+ start_line: start_line,
2750
+ start_column: start_column,
2751
+ start_char: start_char + index,
2752
+ end_line: start_line,
2753
+ end_column: start_column + operator.length,
2754
+ end_char: start_char + index + operator.length
2755
+ )
2756
+
2757
+ CommandCall.new(
2758
+ receiver: node.left,
2759
+ operator: nil,
2760
+ message: Op.new(value: operator, location: op_location),
2761
+ arguments:
2762
+ Args.new(parts: [node.right], location: node.right.location),
2763
+ block: nil,
2764
+ location: node.location
2765
+ )
2766
+ end
2767
+
2768
+ # When you have a begin..end while or begin..end until, it's a special
2769
+ # kind of syntax that executes the block in a loop. In this case the
2770
+ # parser gem has a special node type for it.
2771
+ def loop_post?(node)
2772
+ node.modifier? && node.statements.is_a?(Statements) &&
2773
+ node.statements.body.length == 1 &&
2774
+ node.statements.body.first.is_a?(Begin)
2775
+ end
2776
+
2777
+ # We need to find if we should transform this block into a numblock
2778
+ # since there could be new numbered variables like _1.
2779
+ def num_block_type(statements)
2780
+ variables = []
2781
+ queue = [statements]
2782
+
2783
+ while (child_node = queue.shift)
2784
+ if child_node.is_a?(VarRef) && child_node.value.is_a?(Ident) &&
2785
+ child_node.value.value =~ /^_(\d+)$/
2786
+ variables << $1.to_i
2787
+ end
2788
+
2789
+ queue += child_node.child_nodes.compact
2790
+ end
2791
+
2792
+ variables.max
2793
+ end
2794
+
2795
+ # This method comes almost directly from the parser gem and creates a new
2796
+ # parser gem node from the given s-expression. type is expected to be a
2797
+ # symbol, children is expected to be an array, and location is expected to
2798
+ # be a source map.
2799
+ def s(type, children, location)
2800
+ ::Parser::AST::Node.new(type, children, location: location)
2801
+ end
2802
+
2803
+ # Constructs a plain source map just for an expression.
2804
+ def smap(expression)
2805
+ ::Parser::Source::Map.new(expression)
2806
+ end
2807
+
2808
+ # Constructs a new source map for a collection.
2809
+ def smap_collection(begin_token, end_token, expression)
2810
+ ::Parser::Source::Map::Collection.new(
2811
+ begin_token,
2812
+ end_token,
2813
+ expression
2814
+ )
2815
+ end
2816
+
2817
+ # Constructs a new source map for a collection without a begin or end.
2818
+ def smap_collection_bare(expression)
2819
+ smap_collection(nil, nil, expression)
2820
+ end
2821
+
2822
+ # Constructs a new source map for a conditional expression.
2823
+ def smap_condition(
2824
+ keyword,
2825
+ begin_token,
2826
+ else_token,
2827
+ end_token,
2828
+ expression
2829
+ )
2830
+ ::Parser::Source::Map::Condition.new(
2831
+ keyword,
2832
+ begin_token,
2833
+ else_token,
2834
+ end_token,
2835
+ expression
2836
+ )
2837
+ end
2838
+
2839
+ # Constructs a new source map for a conditional expression with no begin
2840
+ # or end.
2841
+ def smap_condition_bare(expression)
2842
+ smap_condition(nil, nil, nil, nil, expression)
2843
+ end
2844
+
2845
+ # Constructs a new source map for a constant reference.
2846
+ def smap_constant(double_colon, name, expression)
2847
+ ::Parser::Source::Map::Constant.new(double_colon, name, expression)
2848
+ end
2849
+
2850
+ # Constructs a new source map for a class definition.
2851
+ def smap_definition(keyword, operator, name, end_token)
2852
+ ::Parser::Source::Map::Definition.new(
2853
+ keyword,
2854
+ operator,
2855
+ name,
2856
+ end_token
2857
+ )
2858
+ end
2859
+
2860
+ # Constructs a new source map for a for loop.
2861
+ def smap_for(keyword, in_token, begin_token, end_token, expression)
2862
+ ::Parser::Source::Map::For.new(
2863
+ keyword,
2864
+ in_token,
2865
+ begin_token,
2866
+ end_token,
2867
+ expression
2868
+ )
2869
+ end
2870
+
2871
+ # Constructs a new source map for a heredoc.
2872
+ def smap_heredoc(expression, heredoc_body, heredoc_end)
2873
+ ::Parser::Source::Map::Heredoc.new(
2874
+ expression,
2875
+ heredoc_body,
2876
+ heredoc_end
2877
+ )
2878
+ end
2879
+
2880
+ # Construct a source map for an index operation.
2881
+ def smap_index(begin_token, end_token, expression)
2882
+ ::Parser::Source::Map::Index.new(begin_token, end_token, expression)
2883
+ end
2884
+
2885
+ # Constructs a new source map for the use of a keyword.
2886
+ def smap_keyword(keyword, begin_token, end_token, expression)
2887
+ ::Parser::Source::Map::Keyword.new(
2888
+ keyword,
2889
+ begin_token,
2890
+ end_token,
2891
+ expression
2892
+ )
2893
+ end
2894
+
2895
+ # Constructs a new source map for the use of a keyword without a begin or
2896
+ # end token.
2897
+ def smap_keyword_bare(keyword, expression)
2898
+ smap_keyword(keyword, nil, nil, expression)
2899
+ end
2900
+
2901
+ # Constructs a new source map for a method definition.
2902
+ def smap_method_definition(
2903
+ keyword,
2904
+ operator,
2905
+ name,
2906
+ end_token,
2907
+ assignment,
2908
+ expression
2909
+ )
2910
+ ::Parser::Source::Map::MethodDefinition.new(
2911
+ keyword,
2912
+ operator,
2913
+ name,
2914
+ end_token,
2915
+ assignment,
2916
+ expression
2917
+ )
2918
+ end
2919
+
2920
+ # Constructs a new source map for an operator.
2921
+ def smap_operator(operator, expression)
2922
+ ::Parser::Source::Map::Operator.new(operator, expression)
2923
+ end
2924
+
2925
+ # Constructs a source map for the body of a rescue clause.
2926
+ def smap_rescue_body(keyword, assoc, begin_token, expression)
2927
+ ::Parser::Source::Map::RescueBody.new(
2928
+ keyword,
2929
+ assoc,
2930
+ begin_token,
2931
+ expression
2932
+ )
2933
+ end
2934
+
2935
+ # Constructs a new source map for a method call.
2936
+ def smap_send(dot, selector, begin_token, end_token, expression)
2937
+ ::Parser::Source::Map::Send.new(
2938
+ dot,
2939
+ selector,
2940
+ begin_token,
2941
+ end_token,
2942
+ expression
2943
+ )
2944
+ end
2945
+
2946
+ # Constructs a new source map for a method call without a begin or end.
2947
+ def smap_send_bare(selector, expression)
2948
+ smap_send(nil, selector, nil, nil, expression)
2949
+ end
2950
+
2951
+ # Constructs a new source map for a ternary expression.
2952
+ def smap_ternary(question, colon, expression)
2953
+ ::Parser::Source::Map::Ternary.new(question, colon, expression)
2954
+ end
2955
+
2956
+ # Constructs a new source map for a variable.
2957
+ def smap_variable(name, expression)
2958
+ ::Parser::Source::Map::Variable.new(name, expression)
2959
+ end
2960
+
2961
+ # Constructs a new source range from the given start and end offsets.
2962
+ def srange(start_char, end_char)
2963
+ ::Parser::Source::Range.new(buffer, start_char, end_char)
2964
+ end
2965
+
2966
+ # Constructs a new source range by finding the given needle in the given
2967
+ # range of the source. If the needle is not found, returns nil.
2968
+ def srange_search(start_char, end_char, needle)
2969
+ index = buffer.source[start_char...end_char].index(needle)
2970
+ return unless index
2971
+
2972
+ offset = start_char + index
2973
+ srange(offset, offset + needle.length)
2974
+ end
2975
+
2976
+ # Constructs a new source range by searching for the given needle between
2977
+ # the end location of the start node and the start location of the end
2978
+ # node. If the needle is not found, returns nil.
2979
+ def srange_search_between(start_node, end_node, needle)
2980
+ srange_search(start_node.end_char, end_node.start_char, needle)
2981
+ end
2982
+
2983
+ # Constructs a new source range by finding the given needle in the given
2984
+ # range of the source. If it needle is not found, raises an error.
2985
+ def srange_find(start_char, end_char, needle)
2986
+ srange = srange_search(start_char, end_char, needle)
2987
+
2988
+ unless srange
2989
+ slice = buffer.source[start_char...end_char].inspect
2990
+ raise "Could not find #{needle.inspect} in #{slice}"
2991
+ end
2992
+
2993
+ srange
2994
+ end
2995
+
2996
+ # Constructs a new source range by finding the given needle between the
2997
+ # end location of the start node and the start location of the end node.
2998
+ # If the needle is not found, returns raises an error.
2999
+ def srange_find_between(start_node, end_node, needle)
3000
+ srange_find(start_node.end_char, end_node.start_char, needle)
3001
+ end
3002
+
3003
+ # Constructs a new source range from the given start offset and length.
3004
+ def srange_length(start_char, length)
3005
+ if length > 0
3006
+ srange(start_char, start_char + length)
3007
+ else
3008
+ srange(start_char + length, start_char)
3009
+ end
3010
+ end
3011
+
3012
+ # Constructs a new source range using the given node's location.
3013
+ def srange_node(node)
3014
+ location = node.location
3015
+ srange(location.start_char, location.end_char)
3016
+ end
3017
+ end
3018
+ end
3019
+ end