syntax_tree-translator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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