syntax_tree-translator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1269 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SyntaxTree
4
+ module Translator
5
+ class Parser < Visitor
6
+ attr_reader :buffer, :stack
7
+
8
+ def initialize(buffer)
9
+ @buffer = buffer
10
+ @stack = []
11
+ end
12
+
13
+ def visit(node)
14
+ stack << node
15
+ result = super
16
+ stack.pop
17
+ result
18
+ end
19
+
20
+ def visit_alias(node)
21
+ s(:alias, [visit(node.left), visit(node.right)])
22
+ end
23
+
24
+ def visit_aref_field(node)
25
+ if ::Parser::Builders::Default.emit_index
26
+ case node
27
+ in { index: Args[parts:] }
28
+ s(:indexasgn, [visit(node.collection), *visit_all(parts)])
29
+ in { index: nil }
30
+ s(:indexasgn, [visit(node.collection), nil])
31
+ end
32
+ else
33
+ case node
34
+ in { index: Args[parts:] }
35
+ s(:send, [visit(node.collection), :[]=, *visit_all(parts)])
36
+ in { index: nil }
37
+ s(:send, [visit(node.collection), :[]=, nil])
38
+ end
39
+ end
40
+ end
41
+
42
+ def visit_aref(node)
43
+ if ::Parser::Builders::Default.emit_index
44
+ case node
45
+ in { index: Args[parts:] }
46
+ s(:index, [visit(node.collection), *visit_all(parts)])
47
+ in { index: nil }
48
+ s(:index, [visit(node.collection)])
49
+ end
50
+ else
51
+ case node
52
+ in { index: Args[parts:] }
53
+ s(:send, [visit(node.collection), :[], *visit_all(parts)])
54
+ in { index: nil }
55
+ s(:send, [visit(node.collection), :[], nil])
56
+ end
57
+ end
58
+ end
59
+
60
+ def visit_arg_block(node)
61
+ s(:block_pass, [visit(node.value)])
62
+ end
63
+
64
+ def visit_arg_paren(node)
65
+ raise
66
+ end
67
+
68
+ def visit_arg_star(node)
69
+ if stack[-3] in MLHSParen[contents: MLHS]
70
+ case node
71
+ in { value: nil }
72
+ s(:restarg)
73
+ in { value: VarField[value: { value: }]}
74
+ s(:restarg, [value.to_sym])
75
+ in { value: Ident[value:] }
76
+ s(:restarg, [value.to_sym])
77
+ end
78
+ else
79
+ case node
80
+ in { value: nil }
81
+ s(:splat)
82
+ else
83
+ s(:splat, [visit(node.value)])
84
+ end
85
+ end
86
+ end
87
+
88
+ def visit_args(node)
89
+ raise
90
+ end
91
+
92
+ def visit_args_forward(node)
93
+ s(:forwarded_args)
94
+ end
95
+
96
+ def visit_array(node)
97
+ case node
98
+ in { contents: nil }
99
+ s(:array)
100
+ in { contents: Args[parts:] }
101
+ s(:array, visit_all(parts))
102
+ end
103
+ end
104
+
105
+ def visit_aryptn(node)
106
+ type = :array_pattern
107
+ children = visit_all(node.requireds)
108
+
109
+ case node.rest
110
+ in VarField[value: nil, location: { start_char:, end_char: ^(start_char) }] if node.posts.empty?
111
+ # Here we have an implicit rest, as in [foo,]. parser has a specific
112
+ # type for these patterns.
113
+ type = :array_pattern_with_tail
114
+ in VarField[value: nil]
115
+ children << s(:match_rest)
116
+ in VarField
117
+ children << s(:match_rest, [visit(node.rest)])
118
+ else
119
+ end
120
+
121
+ inner = s(type, children + visit_all(node.posts))
122
+ node.constant ? s(:const_pattern, [visit(node.constant), inner]) : inner
123
+ end
124
+
125
+ def visit_assign(node)
126
+ target = visit(node.target)
127
+ s(target.type, target.children + [visit(node.value)])
128
+ end
129
+
130
+ def visit_assoc(node)
131
+ case node
132
+ in { key:, value: nil } if key.value.start_with?(/[a-z]/)
133
+ s(:pair, [visit(key), s(:send, [nil, key.value.chomp(":").to_sym])])
134
+ in { key:, value: nil } if key.value.start_with?(/[A-Z]/)
135
+ s(:pair, [visit(key), s(:const, [nil, key.value.chomp(":").to_sym])])
136
+ in { key:, value: }
137
+ s(:pair, [visit(key), visit(value)])
138
+ end
139
+ end
140
+
141
+ def visit_assoc_splat(node)
142
+ s(:kwsplat, [visit(node.value)])
143
+ end
144
+
145
+ def visit_backref(node)
146
+ if node.value.match?(/^\$\d+$/)
147
+ s(:nth_ref, [node.value[1..-1].to_i])
148
+ else
149
+ s(:back_ref, [node.value.to_sym])
150
+ end
151
+ end
152
+
153
+ def visit_backtick(node)
154
+ raise
155
+ end
156
+
157
+ def visit_bare_assoc_hash(node)
158
+ type =
159
+ if ::Parser::Builders::Default.emit_kwargs && !(stack[-2] in ArrayLiteral)
160
+ :kwargs
161
+ else
162
+ :hash
163
+ end
164
+
165
+ s(type, visit_all(node.assocs))
166
+ end
167
+
168
+ def visit_BEGIN(node)
169
+ s(:preexe, [visit(node.statements)])
170
+ end
171
+
172
+ def visit_begin(node)
173
+ if node.bodystmt.empty?
174
+ s(:kwbegin)
175
+ elsif node.bodystmt in { statements:, rescue_clause: nil, ensure_clause: nil, else_clause: nil }
176
+ visited = visit(statements)
177
+ s(:kwbegin, visited.type == :begin ? visited.children : [visited])
178
+ else
179
+ s(:kwbegin, [visit(node.bodystmt)])
180
+ end
181
+ end
182
+
183
+ def visit_binary(node)
184
+ case node
185
+ in { operator: :| }
186
+ current = -2
187
+ current -= 1 while (stack[current] in Binary[operator: :|])
188
+
189
+ if stack[current] in In
190
+ s(:match_alt, [visit(node.left), visit(node.right)])
191
+ else
192
+ s(:send, [visit(node.left), node.operator, visit(node.right)])
193
+ end
194
+ in { operator: :"=>" }
195
+ s(:match_as, [visit(node.left), visit(node.right)])
196
+ in { operator: :"&&" | :and }
197
+ s(:and, [visit(node.left), visit(node.right)])
198
+ in { operator: :"||" | :or }
199
+ s(:or, [visit(node.left), visit(node.right)])
200
+ in { left: RegexpLiteral[parts: [TStringContent]], operator: :=~ }
201
+ s(:match_with_lvasgn, [visit(node.left), visit(node.right)])
202
+ else
203
+ s(:send, [visit(node.left), node.operator, visit(node.right)])
204
+ end
205
+ end
206
+
207
+ def visit_blockarg(node)
208
+ case node
209
+ in { name: nil }
210
+ s(:blockarg, [nil])
211
+ else
212
+ s(:blockarg, [node.name.value.to_sym])
213
+ end
214
+ end
215
+
216
+ def visit_block_var(node)
217
+ shadowargs = node.locals.map { |local| s(:shadowarg, [local.value.to_sym]) }
218
+
219
+ case node
220
+ in { params: { requireds: [(Ident | MLHSParen) => required], optionals: [], rest: nil, posts: [], keywords: [], keyword_rest: nil, block: nil } } if ::Parser::Builders::Default.emit_procarg0
221
+ procarg0 =
222
+ if ::Parser::Builders::Default.emit_arg_inside_procarg0 && required in Ident
223
+ s(:procarg0, [s(:arg, [required.value.to_sym])])
224
+ else
225
+ s(:procarg0, visit(required).children)
226
+ end
227
+
228
+ s(:args, [procarg0] + shadowargs)
229
+ else
230
+ s(:args, visit(node.params).children + shadowargs)
231
+ end
232
+ end
233
+
234
+ def visit_bodystmt(node)
235
+ inner = visit(node.statements)
236
+
237
+ if node.rescue_clause
238
+ children = [inner] + visit(node.rescue_clause).children
239
+
240
+ if node.else_clause
241
+ children.pop
242
+ children << visit(node.else_clause)
243
+ end
244
+
245
+ inner = s(:rescue, children)
246
+ end
247
+
248
+ if node.ensure_clause
249
+ inner = s(:ensure, [inner] + visit(node.ensure_clause).children)
250
+ end
251
+
252
+ inner
253
+ end
254
+
255
+ def visit_brace_block(node)
256
+ raise
257
+ end
258
+
259
+ def visit_break(node)
260
+ s(:break, visit_all(node.arguments.parts))
261
+ end
262
+
263
+ def visit_call(node)
264
+ type = send_type(node.operator)
265
+
266
+ case node
267
+ in { message: :call, arguments: ArgParen[arguments: nil] }
268
+ s(type, [visit(node.receiver), :call])
269
+ in { message: :call, arguments: ArgParen[arguments: { parts: }] }
270
+ s(type, [visit(node.receiver), :call, *visit_all(parts)])
271
+ in { arguments: nil | ArgParen[arguments: nil] }
272
+ s(type, [visit(node.receiver), node.message.value.to_sym])
273
+ in { arguments: Args[parts:] }
274
+ s(type, [visit(node.receiver), node.message.value.to_sym, *visit_all(parts)])
275
+ in { arguments: ArgParen[arguments: { parts: }] }
276
+ s(type, [visit(node.receiver), node.message.value.to_sym, *visit_all(parts)])
277
+ end
278
+ end
279
+
280
+ def visit_case(node)
281
+ clauses = [node.consequent]
282
+ clauses << clauses.last.consequent while clauses.last && !(clauses.last in Else)
283
+
284
+ type = (node.consequent in In) ? :case_match : :case
285
+ s(type, [visit(node.value)] + clauses.map { |clause| visit(clause) })
286
+ end
287
+
288
+ def visit_CHAR(node)
289
+ s(:str, [node.value[1..-1]])
290
+ end
291
+
292
+ def visit_class(node)
293
+ s(:class, [visit(node.constant), visit(node.superclass), visit(node.bodystmt)])
294
+ end
295
+
296
+ def visit_comma(node)
297
+ raise
298
+ end
299
+
300
+ def visit_command(node)
301
+ s(:send, [nil, node.message.value.to_sym, *visit_all(node.arguments.parts)])
302
+ end
303
+
304
+ def visit_command_call(node)
305
+ children = [visit(node.receiver), node.message.value.to_sym]
306
+
307
+ case node.arguments
308
+ in nil
309
+ # do nothing
310
+ in Args[parts:]
311
+ children += visit_all(parts)
312
+ in ArgParen[arguments: { parts: }]
313
+ children += visit_all(parts)
314
+ end
315
+
316
+ s(send_type(node.operator), children)
317
+ end
318
+
319
+ def visit_comment(node)
320
+ raise
321
+ end
322
+
323
+ def visit_const(node)
324
+ s(:const, [nil, node.value.to_sym])
325
+ end
326
+
327
+ def visit_const_path_field(node)
328
+ if node in { parent: VarRef[value: Kw[value: "self"]] => parent, constant: Ident[value:] }
329
+ s(:send, [visit(parent), :"#{value}="])
330
+ else
331
+ s(:casgn, [visit(node.parent), node.constant.value.to_sym])
332
+ end
333
+ end
334
+
335
+ def visit_const_path_ref(node)
336
+ s(:const, [visit(node.parent), node.constant.value.to_sym])
337
+ end
338
+
339
+ def visit_const_ref(node)
340
+ s(:const, [nil, node.constant.value.to_sym])
341
+ end
342
+
343
+ def visit_cvar(node)
344
+ s(:cvar, [node.value.to_sym])
345
+ end
346
+
347
+ def visit_def(node)
348
+ args = (node.params in Params) ? node.params : node.params.contents
349
+ s(:def, [node.name.value.to_sym, visit(args), visit(node.bodystmt)])
350
+ end
351
+
352
+ def visit_def_endless(node)
353
+ children = []
354
+ children << visit(node.target) if node.target
355
+
356
+ args = (node.paren in Params) ? node.paren : node.paren.contents
357
+ children += [node.name.value.to_sym, visit(args), visit(node.statement)]
358
+
359
+ s(node.target ? :defs : :def, children)
360
+ end
361
+
362
+ def visit_defined(node)
363
+ s(:defined?, [visit(node.value)])
364
+ end
365
+
366
+ def visit_defs(node)
367
+ args = (node.params in Params) ? node.params : node.params.contents
368
+ s(:defs, [visit(node.target), node.name.value.to_sym, visit(args), visit(node.bodystmt)])
369
+ end
370
+
371
+ def visit_do_block(node)
372
+ raise
373
+ end
374
+
375
+ def visit_dot2(node)
376
+ s(:irange, [visit(node.left), visit(node.right)])
377
+ end
378
+
379
+ def visit_dot3(node)
380
+ s(:erange, [visit(node.left), visit(node.right)])
381
+ end
382
+
383
+ def visit_dyna_symbol(node)
384
+ case node
385
+ in { parts: [TStringContent[value:]] }
386
+ s(:sym, ["\"#{value}\"".undump.to_sym])
387
+ else
388
+ s(:dsym, visit_all(node.parts))
389
+ end
390
+ end
391
+
392
+ def visit_else(node)
393
+ if node.statements.empty? && (stack[-2] in Case)
394
+ s(:empty_else)
395
+ else
396
+ visit(node.statements)
397
+ end
398
+ end
399
+
400
+ def visit_elsif(node)
401
+ s(:if, [visit(node.predicate), visit(node.statements), visit(node.consequent)])
402
+ end
403
+
404
+ def visit_embdoc(node)
405
+ raise
406
+ end
407
+
408
+ def visit_embexpr_beg(node)
409
+ raise
410
+ end
411
+
412
+ def visit_embexpr_end(node)
413
+ raise
414
+ end
415
+
416
+ def visit_embvar(node)
417
+ raise
418
+ end
419
+
420
+ def visit_END(node)
421
+ s(:postexe, [visit(node.statements)])
422
+ end
423
+
424
+ def visit_ensure(node)
425
+ s(:ensure, [visit(node.statements)])
426
+ end
427
+
428
+ def visit_excessed_comma(node)
429
+ raise
430
+ end
431
+
432
+ def visit_fcall(node)
433
+ case node
434
+ in { arguments: Args[parts: []] | ArgParen[arguments: nil] }
435
+ s(:send, [nil, node.value.value.to_sym])
436
+ in { arguments: ArgParen[arguments: { parts: }] }
437
+ s(:send, [nil, node.value.value.to_sym, *visit_all(parts)])
438
+ in { arguments: ArgParen[arguments: ArgsForward] }
439
+ s(:send, [nil, node.value.value.to_sym, s(:forwarded_args)])
440
+ end
441
+ end
442
+
443
+ def visit_field(node)
444
+ if stack[-2] in Assign | MLHS
445
+ s(send_type(node.operator), [visit(node.parent), :"#{node.name.value}="])
446
+ else
447
+ s(send_type(node.operator), [visit(node.parent), node.name.value.to_sym])
448
+ end
449
+ end
450
+
451
+ def visit_float(node)
452
+ s(:float, [node.value.to_f])
453
+ end
454
+
455
+ def visit_fndptn(node)
456
+ children = [
457
+ s(:match_rest, (node.left in VarField[value: nil]) ? [] : [visit(node.left)]),
458
+ *visit_all(node.values),
459
+ s(:match_rest, (node.right in VarField[value: nil]) ? [] : [visit(node.right)])
460
+ ]
461
+
462
+ inner = s(:find_pattern, children)
463
+ node.constant ? s(:const_pattern, [visit(node.constant), inner]) : inner
464
+ end
465
+
466
+ def visit_for(node)
467
+ s(:for, [visit(node.index), visit(node.collection), visit(node.statements)])
468
+ end
469
+
470
+ def visit_gvar(node)
471
+ s(:gvar, [node.value.to_sym])
472
+ end
473
+
474
+ def visit_hash(node)
475
+ s(:hash, visit_all(node.assocs))
476
+ end
477
+
478
+ class HeredocSegments
479
+ attr_reader :node, :segments
480
+
481
+ def initialize(node)
482
+ @node = node
483
+ @segments = []
484
+ end
485
+
486
+ def <<(segment)
487
+ if segment.type == :str && segments.last && segments.last.type == :str && !segments.last.children.first.end_with?("\n")
488
+ segments.last.children.first << segment.children.first
489
+ else
490
+ segments << segment
491
+ end
492
+ end
493
+
494
+ HeredocLine = Struct.new(:value, :segments)
495
+
496
+ def trim!
497
+ return unless node.beginning.value[2] == "~"
498
+ lines = [HeredocLine.new(+"", [])]
499
+
500
+ segments.each do |segment|
501
+ lines.last.segments << segment
502
+
503
+ if segment.type == :str
504
+ lines.last.value << segment.children.first
505
+
506
+ if lines.last.value.end_with?("\n")
507
+ lines << HeredocLine.new(+"", [])
508
+ end
509
+ end
510
+ end
511
+
512
+ lines.pop if lines.last.value.empty?
513
+ return if lines.empty?
514
+
515
+ segments.clear
516
+ lines.each do |line|
517
+ remaining = node.dedent
518
+
519
+ line.segments.each do |segment|
520
+ if segment.type == :str
521
+ if remaining > 0
522
+ whitespace = segment.children.first[/^\s{0,#{remaining}}/]
523
+ segment.children.first.sub!(/^#{whitespace}/, "")
524
+ remaining -= whitespace.length
525
+ end
526
+
527
+ if node.beginning.value[3] != "'" && segments.any? && segments.last.type == :str && segments.last.children.first.end_with?("\\\n")
528
+ segments.last.children.first.gsub!(/\\\n\z/, "")
529
+ segments.last.children.first.concat(segment.children.first)
530
+ elsif segment.children.first.length > 0
531
+ segments << segment
532
+ end
533
+ else
534
+ segments << segment
535
+ end
536
+ end
537
+ end
538
+ end
539
+ end
540
+
541
+ def visit_heredoc(node)
542
+ heredoc_segments = HeredocSegments.new(node)
543
+
544
+ node.parts.each do |part|
545
+ if (part in TStringContent[value:]) && value.count("\n") > 1
546
+ part.value.split("\n").each do |line|
547
+ heredoc_segments << s(:str, ["#{line}\n"])
548
+ end
549
+ else
550
+ heredoc_segments << visit(part)
551
+ end
552
+ end
553
+
554
+ heredoc_segments.trim!
555
+
556
+ if node.beginning.value.match?(/`\w+`\z/)
557
+ s(:xstr, heredoc_segments.segments)
558
+ elsif heredoc_segments.segments.length > 1
559
+ s(:dstr, heredoc_segments.segments)
560
+ elsif heredoc_segments.segments.empty?
561
+ s(:dstr)
562
+ else
563
+ heredoc_segments.segments.first
564
+ end
565
+ end
566
+
567
+ def visit_heredoc_beg(node)
568
+ raise
569
+ end
570
+
571
+ def visit_hshptn(node)
572
+ children =
573
+ node.keywords.map do |(keyword, value)|
574
+ next s(:pair, [visit(keyword), visit(value)]) if value
575
+
576
+ case keyword
577
+ in Label
578
+ s(:match_var, [keyword.value.chomp(":").to_sym])
579
+ in StringContent[parts: [TStringContent[value:]]]
580
+ s(:match_var, [value.to_sym])
581
+ end
582
+ end
583
+
584
+ case node.keyword_rest
585
+ in VarField[value: nil]
586
+ children << s(:match_rest)
587
+ in VarField[value: :nil]
588
+ children << s(:match_nil_pattern)
589
+ in VarField
590
+ children << s(:match_rest, [visit(node.keyword_rest)])
591
+ else
592
+ end
593
+
594
+ inner = s(:hash_pattern, children)
595
+ node.constant ? s(:const_pattern, [visit(node.constant), inner]) : inner
596
+ end
597
+
598
+ def visit_ident(node)
599
+ s(:lvar, [node.value.to_sym])
600
+ end
601
+
602
+ def visit_if(node)
603
+ predicate =
604
+ case node.predicate
605
+ in Dot2
606
+ s(:iflipflop, visit(node.predicate).children)
607
+ in Dot3
608
+ s(:eflipflop, visit(node.predicate).children)
609
+ else
610
+ visit(node.predicate)
611
+ end
612
+
613
+ s(:if, [predicate, visit(node.statements), visit(node.consequent)])
614
+ end
615
+
616
+ def visit_if_mod(node)
617
+ s(:if, [visit(node.predicate), visit(node.statement), nil])
618
+ end
619
+
620
+ def visit_if_op(node)
621
+ s(:if, [visit(node.predicate), visit(node.truthy), visit(node.falsy)])
622
+ end
623
+
624
+ def visit_imaginary(node)
625
+ # We have to do an eval here in order to get the value in case it's
626
+ # something like 42ri. to_c will not give the right value in that case.
627
+ # Maybe there's an API for this but I can't find it.
628
+ s(:complex, [eval(node.value)])
629
+ end
630
+
631
+ def visit_in(node)
632
+ case node
633
+ in { pattern: IfMod[predicate:, statement:], statements: }
634
+ s(:in_pattern, [visit(statement), s(:if_guard, [visit(predicate)]), visit(statements)])
635
+ in { pattern: UnlessMod[predicate:, statement:], statements: }
636
+ s(:in_pattern, [visit(statement), s(:unless_guard, [visit(predicate)]), visit(statements)])
637
+ else
638
+ s(:in_pattern, [visit(node.pattern), nil, visit(node.statements)])
639
+ end
640
+ end
641
+
642
+ def visit_int(node)
643
+ s(:int, [node.value.to_i])
644
+ end
645
+
646
+ def visit_ivar(node)
647
+ s(:ivar, [node.value.to_sym])
648
+ end
649
+
650
+ def visit_kw(node)
651
+ case node.value
652
+ in "__FILE__"
653
+ s(:str, [buffer.name])
654
+ in "__LINE__"
655
+ s(:int, [node.location.start_line + buffer.first_line - 1])
656
+ in "__ENCODING__" unless ::Parser::Builders::Default.emit_encoding
657
+ s(:const, [s(:const, [nil, :Encoding]), :UTF_8])
658
+ else
659
+ s(node.value.to_sym)
660
+ end
661
+ end
662
+
663
+ def visit_kwrest_param(node)
664
+ case node
665
+ in { name: nil }
666
+ s(:kwrestarg)
667
+ else
668
+ s(:kwrestarg, [node.name.value.to_sym])
669
+ end
670
+ end
671
+
672
+ def visit_label(node)
673
+ s(:sym, [node.value.chomp(":").to_sym])
674
+ end
675
+
676
+ def visit_label_end(node)
677
+ raise
678
+ end
679
+
680
+ def visit_lambda(node)
681
+ args = (node.params in Params) ? node.params : node.params.contents
682
+
683
+ arguments = visit(args)
684
+ child =
685
+ if ::Parser::Builders::Default.emit_lambda
686
+ s(:lambda)
687
+ else
688
+ s(:send, [nil, :lambda])
689
+ end
690
+
691
+ type = :block
692
+ if args.empty? && (maximum = num_block_type(node.statements))
693
+ type = :numblock
694
+ arguments = maximum
695
+ end
696
+
697
+ s(type, [child, arguments, visit(node.statements)])
698
+ end
699
+
700
+ def visit_lbrace(node)
701
+ raise
702
+ end
703
+
704
+ def visit_lbracket(node)
705
+ raise
706
+ end
707
+
708
+ def visit_lparen(node)
709
+ raise
710
+ end
711
+
712
+ def visit_massign(node)
713
+ s(:masgn, [visit(node.target), visit(node.value)])
714
+ end
715
+
716
+ def visit_method_add_block(node)
717
+ statements =
718
+ if node.block in BraceBlock
719
+ node.block.statements
720
+ else
721
+ node.block.bodystmt
722
+ end
723
+
724
+ arguments =
725
+ if node.block.block_var
726
+ visit(node.block.block_var)
727
+ else
728
+ s(:args)
729
+ end
730
+
731
+ type = :block
732
+ if !node.block.block_var && (maximum = num_block_type(statements))
733
+ type = :numblock
734
+ arguments = maximum
735
+ end
736
+
737
+ if node.call in Break | Next | Return
738
+ call = visit(node.call)
739
+ s(call.type, [s(type, [*call.children, arguments, visit(statements)])])
740
+ else
741
+ s(type, [visit(node.call), arguments, visit(statements)])
742
+ end
743
+ end
744
+
745
+ def visit_mlhs(node)
746
+ s(:mlhs, node.parts.map { |part| (part in Ident[value:]) ? s(:arg, [value.to_sym]) : visit(part) })
747
+ end
748
+
749
+ def visit_mlhs_paren(node)
750
+ visit(node.contents)
751
+ end
752
+
753
+ def visit_module(node)
754
+ s(:module, [visit(node.constant), visit(node.bodystmt)])
755
+ end
756
+
757
+ def visit_mrhs(node)
758
+ s(:array, visit_all(node.parts))
759
+ end
760
+
761
+ def visit_next(node)
762
+ s(:next, visit_all(node.arguments.parts))
763
+ end
764
+
765
+ def visit_not(node)
766
+ case node
767
+ in { statement: nil }
768
+ s(:send, [s(:begin), :"!"])
769
+ else
770
+ s(:send, [visit(node.statement), :"!"])
771
+ end
772
+ end
773
+
774
+ def visit_op(node)
775
+ raise
776
+ end
777
+
778
+ def visit_opassign(node)
779
+ case node.operator
780
+ in { value: "||=" }
781
+ s(:or_asgn, [visit(node.target), visit(node.value)])
782
+ in { value: "&&=" }
783
+ s(:and_asgn, [visit(node.target), visit(node.value)])
784
+ else
785
+ s(:op_asgn, [visit(node.target), node.operator.value.chomp("=").to_sym, visit(node.value)])
786
+ end
787
+ end
788
+
789
+ def visit_params(node)
790
+ children = []
791
+
792
+ children +=
793
+ node.requireds.map do |required|
794
+ case required
795
+ in MLHSParen
796
+ visit(required)
797
+ else
798
+ s(:arg, [required.value.to_sym])
799
+ end
800
+ end
801
+
802
+ children += node.optionals.map { |(name, value)| s(:optarg, [name.value.to_sym, visit(value)]) }
803
+ children << visit(node.rest) if node.rest && !(node.rest in ExcessedComma)
804
+ children += node.posts.map { |post| s(:arg, [post.value.to_sym]) }
805
+ children +=
806
+ node.keywords.map do |(name, value)|
807
+ key = name.value.chomp(":").to_sym
808
+ value ? s(:kwoptarg, [key, visit(value)]) : s(:kwarg, [key])
809
+ end
810
+
811
+ case node.keyword_rest
812
+ in nil | ArgsForward
813
+ # do nothing
814
+ in :nil
815
+ children << s(:kwnilarg)
816
+ else
817
+ children << visit(node.keyword_rest)
818
+ end
819
+
820
+ children << visit(node.block) if node.block
821
+
822
+ if (node.keyword_rest in ArgsForward)
823
+ if children.empty? && !::Parser::Builders::Default.emit_forward_arg
824
+ return s(:forward_args)
825
+ end
826
+
827
+ children.insert(node.requireds.length + node.optionals.length + node.keywords.length, s(:forward_arg))
828
+ end
829
+
830
+ s(:args, children)
831
+ end
832
+
833
+ def visit_paren(node)
834
+ if node in { contents: nil | Statements[body: [VoidStmt]] }
835
+ s(:begin)
836
+ elsif stack[-2] in Defs[target: ^(node)]
837
+ visit(node.contents)
838
+ else
839
+ visited = visit(node.contents)
840
+ visited.type == :begin ? visited : s(:begin, [visited])
841
+ end
842
+ end
843
+
844
+ def visit_period(node)
845
+ raise
846
+ end
847
+
848
+ def visit_pinned_begin(node)
849
+ s(:pin, [s(:begin, [visit(node.statement)])])
850
+ end
851
+
852
+ def visit_pinned_var_ref(node)
853
+ s(:pin, [visit(node.value)])
854
+ end
855
+
856
+ def visit_program(node)
857
+ visit(node.statements)
858
+ end
859
+
860
+ def visit_qsymbols(node)
861
+ s(:array, node.elements.map { |element| s(:sym, [element.value.to_sym]) })
862
+ end
863
+
864
+ def visit_qsymbols_beg(node)
865
+ raise
866
+ end
867
+
868
+ def visit_qwords(node)
869
+ s(:array, visit_all(node.elements))
870
+ end
871
+
872
+ def visit_qwords_beg(node)
873
+ raise
874
+ end
875
+
876
+ def visit_rassign(node)
877
+ type = (node.operator in Op[value: "=>"]) ? :match_pattern : :match_pattern_p
878
+ s(type, [visit(node.value), visit(node.pattern)])
879
+ end
880
+
881
+ def visit_rational(node)
882
+ s(:rational, [node.value.to_r])
883
+ end
884
+
885
+ def visit_rbrace(node)
886
+ raise
887
+ end
888
+
889
+ def visit_rbracket(node)
890
+ raise
891
+ end
892
+
893
+ def visit_redo(node)
894
+ s(:redo)
895
+ end
896
+
897
+ def visit_regexp_beg(node)
898
+ raise
899
+ end
900
+
901
+ def visit_regexp_content(node)
902
+ raise
903
+ end
904
+
905
+ def visit_regexp_end(node)
906
+ raise
907
+ end
908
+
909
+ def visit_regexp_literal(node)
910
+ children = visit_all(node.parts)
911
+ children << s(:regopt, node.ending.scan(/[a-z]/).sort.map(&:to_sym))
912
+ regexp = s(:regexp, children)
913
+
914
+ if stack[-2] in If[predicate: ^(node)] | Unless[predicate: ^(node)]
915
+ s(:match_current_line, [regexp])
916
+ elsif stack[-3] in If[predicate: Unary[statement: ^(node), operator: "!"]] | Unless[predicate: Unary[statement: ^(node), operator: "!"]]
917
+ s(:match_current_line, [regexp])
918
+ elsif stack[-4] in Program[statements: { body: [*, Unary[statement: ^(node), operator: "!"]] }]
919
+ s(:match_current_line, [regexp])
920
+ else
921
+ regexp
922
+ end
923
+ end
924
+
925
+ def visit_rescue(node)
926
+ exceptions =
927
+ case node.exception
928
+ in nil | { exceptions: nil }
929
+ nil
930
+ in { exceptions: VarRef => part }
931
+ s(:array, [visit(part)])
932
+ in { exceptions: MRHS[parts:] }
933
+ s(:array, visit_all(parts))
934
+ else
935
+ s(:array, [visit(node.exception.exceptions)])
936
+ end
937
+
938
+ resbody =
939
+ case node.exception
940
+ in nil
941
+ s(:resbody, [nil, nil, visit(node.statements)])
942
+ in { variable: nil }
943
+ s(:resbody, [exceptions, nil, visit(node.statements)])
944
+ in { variable: VarField => variable }
945
+ s(:resbody, [exceptions, visit(variable), visit(node.statements)])
946
+ end
947
+
948
+ children = [resbody]
949
+ if node.consequent
950
+ children += visit(node.consequent).children
951
+ else
952
+ children << nil
953
+ end
954
+
955
+ s(:rescue, children)
956
+ end
957
+
958
+ def visit_rescue_ex(node)
959
+ raise
960
+ end
961
+
962
+ def visit_rescue_mod(node)
963
+ s(:rescue, [visit(node.statement), s(:resbody, [nil, nil, visit(node.value)]), nil])
964
+ end
965
+
966
+ def visit_rest_param(node)
967
+ s(:restarg, node.name ? [node.name.value.to_sym] : [])
968
+ end
969
+
970
+ def visit_retry(node)
971
+ s(:retry)
972
+ end
973
+
974
+ def visit_return(node)
975
+ s(:return, visit_all(node.arguments.parts))
976
+ end
977
+
978
+ def visit_return0(node)
979
+ s(:return)
980
+ end
981
+
982
+ def visit_rparen(node)
983
+ raise
984
+ end
985
+
986
+ def visit_sclass(node)
987
+ s(:sclass, [visit(node.target), visit(node.bodystmt)])
988
+ end
989
+
990
+ def visit_statements(node)
991
+ children = node.body.reject { |child| child in Comment | EmbDoc | EndContent | VoidStmt }
992
+
993
+ case children
994
+ in []
995
+ nil
996
+ in [statement]
997
+ visit(statement)
998
+ else
999
+ s(:begin, visit_all(children))
1000
+ end
1001
+ end
1002
+
1003
+ def visit_string_concat(node)
1004
+ s(:dstr, [visit(node.left), visit(node.right)])
1005
+ end
1006
+
1007
+ def visit_string_content(node)
1008
+ # Can get here if you're inside a hash pattern, e.g., in "a": 1
1009
+ s(:sym, [node.parts.first.value.to_sym])
1010
+ end
1011
+
1012
+ def visit_string_dvar(node)
1013
+ visit(node.variable)
1014
+ end
1015
+
1016
+ def visit_string_embexpr(node)
1017
+ child = visit(node.statements)
1018
+ s(:begin, child ? [child] : [])
1019
+ end
1020
+
1021
+ def visit_string_literal(node)
1022
+ case node
1023
+ in { parts: [] }
1024
+ s(:str, [""])
1025
+ in { parts: [TStringContent => part] }
1026
+ visit(part)
1027
+ else
1028
+ s(:dstr, visit_all(node.parts))
1029
+ end
1030
+ end
1031
+
1032
+ def visit_super(node)
1033
+ case node.arguments
1034
+ in ArgParen[arguments: nil]
1035
+ s(:super)
1036
+ in ArgParen[arguments: ArgsForward => arguments]
1037
+ s(:super, [visit(arguments)])
1038
+ in ArgParen[arguments: { parts: }]
1039
+ s(:super, visit_all(parts))
1040
+ in Args[parts:]
1041
+ s(:super, visit_all(parts))
1042
+ end
1043
+ end
1044
+
1045
+ def visit_symbeg(node)
1046
+ raise
1047
+ end
1048
+
1049
+ def visit_symbol_content(node)
1050
+ raise
1051
+ end
1052
+
1053
+ def visit_symbol_literal(node)
1054
+ s(:sym, [node.value.value.to_sym])
1055
+ end
1056
+
1057
+ def visit_symbols(node)
1058
+ children =
1059
+ node.elements.map do |element|
1060
+ if element.parts.length > 1 || !(element.parts.first in TStringContent)
1061
+ s(:dsym, visit_all(element.parts))
1062
+ else
1063
+ s(:sym, [element.parts.first.value.to_sym])
1064
+ end
1065
+ end
1066
+
1067
+ s(:array, children)
1068
+ end
1069
+
1070
+ def visit_symbols_beg(node)
1071
+ raise
1072
+ end
1073
+
1074
+ def visit_tlambda(node)
1075
+ raise
1076
+ end
1077
+
1078
+ def visit_tlambeg(node)
1079
+ raise
1080
+ end
1081
+
1082
+ def visit_top_const_field(node)
1083
+ s(:casgn, [s(:cbase), node.constant.value.to_sym])
1084
+ end
1085
+
1086
+ def visit_top_const_ref(node)
1087
+ s(:const, [s(:cbase), node.constant.value.to_sym])
1088
+ end
1089
+
1090
+ def visit_tstring_beg(node)
1091
+ raise
1092
+ end
1093
+
1094
+ def visit_tstring_content(node)
1095
+ value = node.value.gsub(/([^[:ascii:]])/) { $1.dump[1...-1] }
1096
+ s(:str, ["\"#{value}\"".undump])
1097
+ end
1098
+
1099
+ def visit_tstring_end(node)
1100
+ raise
1101
+ end
1102
+
1103
+ def visit_unary(node)
1104
+ case node
1105
+ in { statement: Paren[contents: Statements[body: [Dot2 => contents]]], operator: "!" }
1106
+ s(:send, [s(:begin, [s(:iflipflop, visit(contents).children)]), :"!"])
1107
+ in { statement: Paren[contents: Statements[body: [Dot3 => contents]]], operator: "!" }
1108
+ s(:send, [s(:begin, [s(:eflipflop, visit(contents).children)]), :"!"])
1109
+ in { statement: Int[value:], operator: "+" }
1110
+ s(:int, [value.to_i])
1111
+ in { statement: Int[value:], operator: "-" }
1112
+ s(:int, [-value.to_i])
1113
+ in { statement: FloatLiteral[value:], operator: "+" }
1114
+ s(:float, [value.to_f])
1115
+ in { statement: FloatLiteral[value:], operator: "-" }
1116
+ s(:float, [-value.to_f])
1117
+ in { statement:, operator: "+" }
1118
+ s(:send, [visit(statement), :"+@"])
1119
+ in { statement:, operator: "-" }
1120
+ s(:send, [visit(statement), :"-@"])
1121
+ else
1122
+ s(:send, [visit(node.statement), node.operator.to_sym])
1123
+ end
1124
+ end
1125
+
1126
+ def visit_undef(node)
1127
+ s(:undef, visit_all(node.symbols))
1128
+ end
1129
+
1130
+ def visit_unless(node)
1131
+ s(:if, [visit(node.predicate), visit(node.consequent), visit(node.statements)])
1132
+ end
1133
+
1134
+ def visit_unless_mod(node)
1135
+ s(:if, [visit(node.predicate), nil, visit(node.statement)])
1136
+ end
1137
+
1138
+ def visit_until(node)
1139
+ s(:until, [visit(node.predicate), visit(node.statements)])
1140
+ end
1141
+
1142
+ def visit_until_mod(node)
1143
+ children = [visit(node.predicate), visit(node.statement)]
1144
+ s((node.statement in Begin) ? :until_post : :until, children)
1145
+ end
1146
+
1147
+ def visit_var_alias(node)
1148
+ s(:alias, [visit(node.left), visit(node.right)])
1149
+ end
1150
+
1151
+ def visit_var_field(node)
1152
+ if stack[-2] in AryPtn | Binary[operator: :"=>"] | FndPtn | HshPtn | In | RAssign
1153
+ return s(:match_var, [node.value.value.to_sym])
1154
+ end
1155
+
1156
+ case node.value
1157
+ in Const[value:] then s(:casgn, [nil, value.to_sym])
1158
+ in CVar[value:] then s(:cvasgn, [value.to_sym])
1159
+ in GVar[value:] then s(:gvasgn, [value.to_sym])
1160
+ in Ident[value:] then s(:lvasgn, [value.to_sym])
1161
+ in IVar[value:] then s(:ivasgn, [value.to_sym])
1162
+ in VarRef[value:] then s(:lvasgn, [value.to_sym])
1163
+ in nil then s(:match_rest)
1164
+ end
1165
+ end
1166
+
1167
+ def visit_var_ref(node)
1168
+ visit(node.value)
1169
+ end
1170
+
1171
+ def visit_vcall(node)
1172
+ range = ::Parser::Source::Range.new(buffer, node.location.start_char, node.location.end_char)
1173
+ location = ::Parser::Source::Map::Send.new(nil, range, nil, nil, range)
1174
+
1175
+ s(:send, [nil, node.value.value.to_sym], location: location)
1176
+ end
1177
+
1178
+ def visit_void_stmt(node)
1179
+ raise
1180
+ end
1181
+
1182
+ def visit_when(node)
1183
+ s(:when, visit_all(node.arguments.parts) + [visit(node.statements)])
1184
+ end
1185
+
1186
+ def visit_while(node)
1187
+ s(:while, [visit(node.predicate), visit(node.statements)])
1188
+ end
1189
+
1190
+ def visit_while_mod(node)
1191
+ children = [visit(node.predicate), visit(node.statement)]
1192
+ s((node.statement in Begin) ? :while_post : :while, children)
1193
+ end
1194
+
1195
+ def visit_word(node)
1196
+ case node
1197
+ in { parts: [TStringContent => part] }
1198
+ visit(part)
1199
+ else
1200
+ s(:dstr, visit_all(node.parts))
1201
+ end
1202
+ end
1203
+
1204
+ def visit_words(node)
1205
+ s(:array, visit_all(node.elements))
1206
+ end
1207
+
1208
+ def visit_words_beg(node)
1209
+ raise
1210
+ end
1211
+
1212
+ def visit_xstring(node)
1213
+ raise
1214
+ end
1215
+
1216
+ def visit_xstring_literal(node)
1217
+ s(:xstr, visit_all(node.parts))
1218
+ end
1219
+
1220
+ def visit_yield(node)
1221
+ case node.arguments
1222
+ in Args[parts:]
1223
+ s(:yield, visit_all(parts))
1224
+ in Paren[contents: Args[parts:]]
1225
+ s(:yield, visit_all(parts))
1226
+ end
1227
+ end
1228
+
1229
+ def visit_yield0(node)
1230
+ s(:yield)
1231
+ end
1232
+
1233
+ def visit_zsuper(node)
1234
+ s(:zsuper)
1235
+ end
1236
+
1237
+ def visit___end__(node)
1238
+ raise
1239
+ end
1240
+
1241
+ private
1242
+
1243
+ # We need to find if we should transform this block into a numblock
1244
+ # since there could be new numbered variables like _1.
1245
+ def num_block_type(statements)
1246
+ variables = []
1247
+ queue = [statements]
1248
+
1249
+ while child_node = queue.shift
1250
+ if (child_node in VarRef[value: Ident[value:]]) && (value =~ /^_(\d+)$/)
1251
+ variables << $1.to_i
1252
+ end
1253
+
1254
+ queue += child_node.child_nodes.compact
1255
+ end
1256
+
1257
+ variables.max
1258
+ end
1259
+
1260
+ def s(type, children = [], opts = {})
1261
+ ::Parser::AST::Node.new(type, children, opts)
1262
+ end
1263
+
1264
+ def send_type(operator)
1265
+ (operator in Op[value: "&."]) ? :csend : :send
1266
+ end
1267
+ end
1268
+ end
1269
+ end