prism 0.22.0 → 0.24.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -1
  3. data/README.md +2 -1
  4. data/docs/releasing.md +67 -17
  5. data/docs/ruby_parser_translation.md +19 -0
  6. data/docs/serialization.md +2 -0
  7. data/ext/prism/api_node.c +1982 -1538
  8. data/ext/prism/extension.c +12 -7
  9. data/ext/prism/extension.h +2 -2
  10. data/include/prism/diagnostic.h +3 -4
  11. data/include/prism/encoding.h +7 -0
  12. data/include/prism/util/pm_constant_pool.h +1 -1
  13. data/include/prism/util/pm_newline_list.h +4 -3
  14. data/include/prism/util/pm_strpbrk.h +4 -1
  15. data/include/prism/version.h +2 -2
  16. data/lib/prism/desugar_compiler.rb +225 -80
  17. data/lib/prism/dsl.rb +302 -299
  18. data/lib/prism/ffi.rb +103 -77
  19. data/lib/prism/lex_compat.rb +1 -0
  20. data/lib/prism/node.rb +3624 -2114
  21. data/lib/prism/node_ext.rb +25 -2
  22. data/lib/prism/parse_result.rb +56 -19
  23. data/lib/prism/serialize.rb +605 -303
  24. data/lib/prism/translation/parser/compiler.rb +1 -1
  25. data/lib/prism/translation/parser/rubocop.rb +11 -3
  26. data/lib/prism/translation/parser.rb +25 -12
  27. data/lib/prism/translation/parser33.rb +12 -0
  28. data/lib/prism/translation/parser34.rb +12 -0
  29. data/lib/prism/translation/ripper.rb +696 -0
  30. data/lib/prism/translation/ruby_parser.rb +1521 -0
  31. data/lib/prism/translation.rb +3 -3
  32. data/lib/prism.rb +0 -1
  33. data/prism.gemspec +6 -2
  34. data/src/diagnostic.c +10 -11
  35. data/src/encoding.c +16 -17
  36. data/src/options.c +7 -2
  37. data/src/prettyprint.c +3 -3
  38. data/src/prism.c +172 -97
  39. data/src/serialize.c +24 -13
  40. data/src/token_type.c +3 -3
  41. data/src/util/pm_constant_pool.c +1 -1
  42. data/src/util/pm_newline_list.c +6 -3
  43. data/src/util/pm_strpbrk.c +122 -14
  44. metadata +8 -4
  45. data/lib/prism/ripper_compat.rb +0 -285
@@ -0,0 +1,1521 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby_parser"
4
+
5
+ module Prism
6
+ module Translation
7
+ # This module is the entry-point for converting a prism syntax tree into the
8
+ # seattlerb/ruby_parser gem's syntax tree.
9
+ module RubyParser
10
+ # A prism visitor that builds Sexp objects.
11
+ class Compiler < ::Prism::Compiler
12
+ # This is the name of the file that we are compiling. We set it on every
13
+ # Sexp object that is generated, and also use it to compile __FILE__
14
+ # nodes.
15
+ attr_reader :file
16
+
17
+ # Class variables will change their type based on if they are inside of
18
+ # a method definition or not, so we need to track that state.
19
+ attr_reader :in_def
20
+
21
+ # Some nodes will change their representation if they are inside of a
22
+ # pattern, so we need to track that state.
23
+ attr_reader :in_pattern
24
+
25
+ # Initialize a new compiler with the given file name.
26
+ def initialize(file, in_def: false, in_pattern: false)
27
+ @file = file
28
+ @in_def = in_def
29
+ @in_pattern = in_pattern
30
+ end
31
+
32
+ # alias foo bar
33
+ # ^^^^^^^^^^^^^
34
+ def visit_alias_method_node(node)
35
+ s(node, :alias, visit(node.new_name), visit(node.old_name))
36
+ end
37
+
38
+ # alias $foo $bar
39
+ # ^^^^^^^^^^^^^^^
40
+ def visit_alias_global_variable_node(node)
41
+ s(node, :valias, node.new_name.name, node.old_name.name)
42
+ end
43
+
44
+ # foo => bar | baz
45
+ # ^^^^^^^^^
46
+ def visit_alternation_pattern_node(node)
47
+ s(node, :or, visit(node.left), visit(node.right))
48
+ end
49
+
50
+ # a and b
51
+ # ^^^^^^^
52
+ def visit_and_node(node)
53
+ s(node, :and, visit(node.left), visit(node.right))
54
+ end
55
+
56
+ # []
57
+ # ^^
58
+ def visit_array_node(node)
59
+ if in_pattern
60
+ s(node, :array_pat, nil).concat(visit_all(node.elements))
61
+ else
62
+ s(node, :array).concat(visit_all(node.elements))
63
+ end
64
+ end
65
+
66
+ # foo => [bar]
67
+ # ^^^^^
68
+ def visit_array_pattern_node(node)
69
+ if node.constant.nil? && node.requireds.empty? && node.rest.nil? && node.posts.empty?
70
+ s(node, :array_pat)
71
+ else
72
+ result = s(node, :array_pat, visit_pattern_constant(node.constant)).concat(visit_all(node.requireds))
73
+
74
+ case node.rest
75
+ when SplatNode
76
+ result << :"*#{node.rest.expression&.name}"
77
+ when ImplicitRestNode
78
+ result << :*
79
+
80
+ # This doesn't make any sense at all, but since we're trying to
81
+ # replicate the behavior directly, we'll copy it.
82
+ result.line(666)
83
+ end
84
+
85
+ result.concat(visit_all(node.posts))
86
+ end
87
+ end
88
+
89
+ # foo(bar)
90
+ # ^^^
91
+ def visit_arguments_node(node)
92
+ raise "Cannot visit arguments directly"
93
+ end
94
+
95
+ # { a: 1 }
96
+ # ^^^^
97
+ def visit_assoc_node(node)
98
+ [visit(node.key), visit(node.value)]
99
+ end
100
+
101
+ # def foo(**); bar(**); end
102
+ # ^^
103
+ #
104
+ # { **foo }
105
+ # ^^^^^
106
+ def visit_assoc_splat_node(node)
107
+ if node.value.nil?
108
+ [s(node, :kwsplat)]
109
+ else
110
+ [s(node, :kwsplat, visit(node.value))]
111
+ end
112
+ end
113
+
114
+ # $+
115
+ # ^^
116
+ def visit_back_reference_read_node(node)
117
+ s(node, :back_ref, node.name.name.delete_prefix("$").to_sym)
118
+ end
119
+
120
+ # begin end
121
+ # ^^^^^^^^^
122
+ def visit_begin_node(node)
123
+ result = node.statements.nil? ? s(node, :nil) : visit(node.statements)
124
+
125
+ if !node.rescue_clause.nil?
126
+ if !node.statements.nil?
127
+ result = s(node.statements, :rescue, result, visit(node.rescue_clause))
128
+ else
129
+ result = s(node.rescue_clause, :rescue, visit(node.rescue_clause))
130
+ end
131
+
132
+ current = node.rescue_clause
133
+ until (current = current.consequent).nil?
134
+ result << visit(current)
135
+ end
136
+ end
137
+
138
+ if !node.else_clause&.statements.nil?
139
+ result << visit(node.else_clause)
140
+ end
141
+
142
+ if !node.ensure_clause.nil?
143
+ if !node.statements.nil? || !node.rescue_clause.nil? || !node.else_clause.nil?
144
+ result = s(node.statements || node.rescue_clause || node.else_clause || node.ensure_clause, :ensure, result, visit(node.ensure_clause))
145
+ else
146
+ result = s(node.ensure_clause, :ensure, visit(node.ensure_clause))
147
+ end
148
+ end
149
+
150
+ result
151
+ end
152
+
153
+ # foo(&bar)
154
+ # ^^^^
155
+ def visit_block_argument_node(node)
156
+ s(node, :block_pass).tap do |result|
157
+ result << visit(node.expression) unless node.expression.nil?
158
+ end
159
+ end
160
+
161
+ # foo { |; bar| }
162
+ # ^^^
163
+ def visit_block_local_variable_node(node)
164
+ node.name
165
+ end
166
+
167
+ # A block on a keyword or method call.
168
+ def visit_block_node(node)
169
+ s(node, :block_pass, visit(node.expression))
170
+ end
171
+
172
+ # def foo(&bar); end
173
+ # ^^^^
174
+ def visit_block_parameter_node(node)
175
+ :"&#{node.name}"
176
+ end
177
+
178
+ # A block's parameters.
179
+ def visit_block_parameters_node(node)
180
+ # If this block parameters has no parameters and is using pipes, then
181
+ # it inherits its location from its shadow locals, even if they're not
182
+ # on the same lines as the pipes.
183
+ shadow_loc = true
184
+
185
+ result =
186
+ if node.parameters.nil?
187
+ s(node, :args)
188
+ else
189
+ shadow_loc = false
190
+ visit(node.parameters)
191
+ end
192
+
193
+ if node.opening == "("
194
+ result.line = node.opening_loc.start_line
195
+ result.max_line = node.closing_loc.end_line
196
+ shadow_loc = false
197
+ end
198
+
199
+ if node.locals.any?
200
+ shadow = s(node, :shadow).concat(visit_all(node.locals))
201
+ shadow.line = node.locals.first.location.start_line
202
+ shadow.max_line = node.locals.last.location.end_line
203
+ result << shadow
204
+
205
+ if shadow_loc
206
+ result.line = shadow.line
207
+ result.max_line = shadow.max_line
208
+ end
209
+ end
210
+
211
+ result
212
+ end
213
+
214
+ # break
215
+ # ^^^^^
216
+ #
217
+ # break foo
218
+ # ^^^^^^^^^
219
+ def visit_break_node(node)
220
+ if node.arguments.nil?
221
+ s(node, :break)
222
+ elsif node.arguments.arguments.length == 1
223
+ s(node, :break, visit(node.arguments.arguments.first))
224
+ else
225
+ s(node, :break, s(node.arguments, :array).concat(visit_all(node.arguments.arguments)))
226
+ end
227
+ end
228
+
229
+ # foo
230
+ # ^^^
231
+ #
232
+ # foo.bar
233
+ # ^^^^^^^
234
+ #
235
+ # foo.bar() {}
236
+ # ^^^^^^^^^^^^
237
+ def visit_call_node(node)
238
+ case node.name
239
+ when :!~
240
+ return s(node, :not, visit(node.copy(name: :"=~")))
241
+ when :=~
242
+ if node.arguments&.arguments&.length == 1 && node.block.nil?
243
+ case node.receiver
244
+ when StringNode
245
+ return s(node, :match3, visit(node.arguments.arguments.first), visit(node.receiver))
246
+ when RegularExpressionNode, InterpolatedRegularExpressionNode
247
+ return s(node, :match2, visit(node.receiver), visit(node.arguments.arguments.first))
248
+ end
249
+ end
250
+ end
251
+
252
+ type = node.attribute_write? ? :attrasgn : :call
253
+ type = :"safe_#{type}" if node.safe_navigation?
254
+
255
+ arguments = node.arguments&.arguments || []
256
+ write_value = arguments.pop if type == :attrasgn
257
+ block = node.block
258
+
259
+ if block.is_a?(BlockArgumentNode)
260
+ arguments << block
261
+ block = nil
262
+ end
263
+
264
+ result = s(node, type, visit(node.receiver), node.name).concat(visit_all(arguments))
265
+ result << visit_write_value(write_value) unless write_value.nil?
266
+
267
+ visit_block(node, result, block)
268
+ end
269
+
270
+ # foo.bar += baz
271
+ # ^^^^^^^^^^^^^^^
272
+ def visit_call_operator_write_node(node)
273
+ if op_asgn?(node)
274
+ s(node, op_asgn_type(node, :op_asgn), visit(node.receiver), visit_write_value(node.value), node.read_name, node.operator)
275
+ else
276
+ s(node, op_asgn_type(node, :op_asgn2), visit(node.receiver), node.write_name, node.operator, visit_write_value(node.value))
277
+ end
278
+ end
279
+
280
+ # foo.bar &&= baz
281
+ # ^^^^^^^^^^^^^^^
282
+ def visit_call_and_write_node(node)
283
+ if op_asgn?(node)
284
+ s(node, op_asgn_type(node, :op_asgn), visit(node.receiver), visit_write_value(node.value), node.read_name, :"&&")
285
+ else
286
+ s(node, op_asgn_type(node, :op_asgn2), visit(node.receiver), node.write_name, :"&&", visit_write_value(node.value))
287
+ end
288
+ end
289
+
290
+ # foo.bar ||= baz
291
+ # ^^^^^^^^^^^^^^^
292
+ def visit_call_or_write_node(node)
293
+ if op_asgn?(node)
294
+ s(node, op_asgn_type(node, :op_asgn), visit(node.receiver), visit_write_value(node.value), node.read_name, :"||")
295
+ else
296
+ s(node, op_asgn_type(node, :op_asgn2), visit(node.receiver), node.write_name, :"||", visit_write_value(node.value))
297
+ end
298
+ end
299
+
300
+ # Call nodes with operators following them will either be op_asgn or
301
+ # op_asgn2 nodes. That is determined by their call operator and their
302
+ # right-hand side.
303
+ private def op_asgn?(node)
304
+ node.call_operator == "::" || (node.value.is_a?(CallNode) && node.value.opening_loc.nil? && !node.value.arguments.nil?)
305
+ end
306
+
307
+ # Call nodes with operators following them can use &. as an operator,
308
+ # which changes their type by prefixing "safe_".
309
+ private def op_asgn_type(node, type)
310
+ node.safe_navigation? ? :"safe_#{type}" : type
311
+ end
312
+
313
+ # foo.bar, = 1
314
+ # ^^^^^^^
315
+ def visit_call_target_node(node)
316
+ s(node, :attrasgn, visit(node.receiver), node.name)
317
+ end
318
+
319
+ # foo => bar => baz
320
+ # ^^^^^^^^^^
321
+ def visit_capture_pattern_node(node)
322
+ visit(node.target) << visit(node.value)
323
+ end
324
+
325
+ # case foo; when bar; end
326
+ # ^^^^^^^^^^^^^^^^^^^^^^^
327
+ def visit_case_node(node)
328
+ s(node, :case, visit(node.predicate)).concat(visit_all(node.conditions)) << visit(node.consequent)
329
+ end
330
+
331
+ # case foo; in bar; end
332
+ # ^^^^^^^^^^^^^^^^^^^^^
333
+ def visit_case_match_node(node)
334
+ s(node, :case, visit(node.predicate)).concat(visit_all(node.conditions)) << visit(node.consequent)
335
+ end
336
+
337
+ # class Foo; end
338
+ # ^^^^^^^^^^^^^^
339
+ def visit_class_node(node)
340
+ name =
341
+ if node.constant_path.is_a?(ConstantReadNode)
342
+ node.name
343
+ else
344
+ visit(node.constant_path)
345
+ end
346
+
347
+ if node.body.nil?
348
+ s(node, :class, name, visit(node.superclass))
349
+ elsif node.body.is_a?(StatementsNode)
350
+ compiler = copy_compiler(in_def: false)
351
+ s(node, :class, name, visit(node.superclass)).concat(node.body.body.map { |child| child.accept(compiler) })
352
+ else
353
+ s(node, :class, name, visit(node.superclass), node.body.accept(copy_compiler(in_def: false)))
354
+ end
355
+ end
356
+
357
+ # @@foo
358
+ # ^^^^^
359
+ def visit_class_variable_read_node(node)
360
+ s(node, :cvar, node.name)
361
+ end
362
+
363
+ # @@foo = 1
364
+ # ^^^^^^^^^
365
+ #
366
+ # @@foo, @@bar = 1
367
+ # ^^^^^ ^^^^^
368
+ def visit_class_variable_write_node(node)
369
+ s(node, class_variable_write_type, node.name, visit_write_value(node.value))
370
+ end
371
+
372
+ # @@foo += bar
373
+ # ^^^^^^^^^^^^
374
+ def visit_class_variable_operator_write_node(node)
375
+ s(node, class_variable_write_type, node.name, s(node, :call, s(node, :cvar, node.name), node.operator, visit_write_value(node.value)))
376
+ end
377
+
378
+ # @@foo &&= bar
379
+ # ^^^^^^^^^^^^^
380
+ def visit_class_variable_and_write_node(node)
381
+ s(node, :op_asgn_and, s(node, :cvar, node.name), s(node, class_variable_write_type, node.name, visit_write_value(node.value)))
382
+ end
383
+
384
+ # @@foo ||= bar
385
+ # ^^^^^^^^^^^^^
386
+ def visit_class_variable_or_write_node(node)
387
+ s(node, :op_asgn_or, s(node, :cvar, node.name), s(node, class_variable_write_type, node.name, visit_write_value(node.value)))
388
+ end
389
+
390
+ # @@foo, = bar
391
+ # ^^^^^
392
+ def visit_class_variable_target_node(node)
393
+ s(node, class_variable_write_type, node.name)
394
+ end
395
+
396
+ # If a class variable is written within a method definition, it has a
397
+ # different type than everywhere else.
398
+ private def class_variable_write_type
399
+ in_def ? :cvasgn : :cvdecl
400
+ end
401
+
402
+ # Foo
403
+ # ^^^
404
+ def visit_constant_read_node(node)
405
+ s(node, :const, node.name)
406
+ end
407
+
408
+ # Foo = 1
409
+ # ^^^^^^^
410
+ #
411
+ # Foo, Bar = 1
412
+ # ^^^ ^^^
413
+ def visit_constant_write_node(node)
414
+ s(node, :cdecl, node.name, visit_write_value(node.value))
415
+ end
416
+
417
+ # Foo += bar
418
+ # ^^^^^^^^^^^
419
+ def visit_constant_operator_write_node(node)
420
+ s(node, :cdecl, node.name, s(node, :call, s(node, :const, node.name), node.operator, visit_write_value(node.value)))
421
+ end
422
+
423
+ # Foo &&= bar
424
+ # ^^^^^^^^^^^^
425
+ def visit_constant_and_write_node(node)
426
+ s(node, :op_asgn_and, s(node, :const, node.name), s(node, :cdecl, node.name, visit(node.value)))
427
+ end
428
+
429
+ # Foo ||= bar
430
+ # ^^^^^^^^^^^^
431
+ def visit_constant_or_write_node(node)
432
+ s(node, :op_asgn_or, s(node, :const, node.name), s(node, :cdecl, node.name, visit(node.value)))
433
+ end
434
+
435
+ # Foo, = bar
436
+ # ^^^
437
+ def visit_constant_target_node(node)
438
+ s(node, :cdecl, node.name)
439
+ end
440
+
441
+ # Foo::Bar
442
+ # ^^^^^^^^
443
+ def visit_constant_path_node(node)
444
+ if node.parent.nil?
445
+ s(node, :colon3, node.child.name)
446
+ else
447
+ s(node, :colon2, visit(node.parent), node.child.name)
448
+ end
449
+ end
450
+
451
+ # Foo::Bar = 1
452
+ # ^^^^^^^^^^^^
453
+ #
454
+ # Foo::Foo, Bar::Bar = 1
455
+ # ^^^^^^^^ ^^^^^^^^
456
+ def visit_constant_path_write_node(node)
457
+ s(node, :cdecl, visit(node.target), visit_write_value(node.value))
458
+ end
459
+
460
+ # Foo::Bar += baz
461
+ # ^^^^^^^^^^^^^^^
462
+ def visit_constant_path_operator_write_node(node)
463
+ s(node, :op_asgn, visit(node.target), node.operator, visit_write_value(node.value))
464
+ end
465
+
466
+ # Foo::Bar &&= baz
467
+ # ^^^^^^^^^^^^^^^^
468
+ def visit_constant_path_and_write_node(node)
469
+ s(node, :op_asgn_and, visit(node.target), visit_write_value(node.value))
470
+ end
471
+
472
+ # Foo::Bar ||= baz
473
+ # ^^^^^^^^^^^^^^^^
474
+ def visit_constant_path_or_write_node(node)
475
+ s(node, :op_asgn_or, visit(node.target), visit_write_value(node.value))
476
+ end
477
+
478
+ # Foo::Bar, = baz
479
+ # ^^^^^^^^
480
+ def visit_constant_path_target_node(node)
481
+ inner =
482
+ if node.parent.nil?
483
+ s(node, :colon3, node.child.name)
484
+ else
485
+ s(node, :colon2, visit(node.parent), node.child.name)
486
+ end
487
+
488
+ s(node, :const, inner)
489
+ end
490
+
491
+ # def foo; end
492
+ # ^^^^^^^^^^^^
493
+ #
494
+ # def self.foo; end
495
+ # ^^^^^^^^^^^^^^^^^
496
+ def visit_def_node(node)
497
+ name = node.name_loc.slice.to_sym
498
+ result =
499
+ if node.receiver.nil?
500
+ s(node, :defn, name)
501
+ else
502
+ s(node, :defs, visit(node.receiver), name)
503
+ end
504
+
505
+ result.line(node.name_loc.start_line)
506
+ if node.parameters.nil?
507
+ result << s(node, :args).line(node.name_loc.start_line)
508
+ else
509
+ result << visit(node.parameters)
510
+ end
511
+
512
+ if node.body.nil?
513
+ result << s(node, :nil)
514
+ elsif node.body.is_a?(StatementsNode)
515
+ compiler = copy_compiler(in_def: true)
516
+ result.concat(node.body.body.map { |child| child.accept(compiler) })
517
+ else
518
+ result << node.body.accept(copy_compiler(in_def: true))
519
+ end
520
+ end
521
+
522
+ # defined? a
523
+ # ^^^^^^^^^^
524
+ #
525
+ # defined?(a)
526
+ # ^^^^^^^^^^^
527
+ def visit_defined_node(node)
528
+ s(node, :defined, visit(node.value))
529
+ end
530
+
531
+ # if foo then bar else baz end
532
+ # ^^^^^^^^^^^^
533
+ def visit_else_node(node)
534
+ visit(node.statements)
535
+ end
536
+
537
+ # "foo #{bar}"
538
+ # ^^^^^^
539
+ def visit_embedded_statements_node(node)
540
+ result = s(node, :evstr)
541
+ result << visit(node.statements) unless node.statements.nil?
542
+ result
543
+ end
544
+
545
+ # "foo #@bar"
546
+ # ^^^^^
547
+ def visit_embedded_variable_node(node)
548
+ s(node, :evstr, visit(node.variable))
549
+ end
550
+
551
+ # begin; foo; ensure; bar; end
552
+ # ^^^^^^^^^^^^
553
+ def visit_ensure_node(node)
554
+ node.statements.nil? ? s(node, :nil) : visit(node.statements)
555
+ end
556
+
557
+ # false
558
+ # ^^^^^
559
+ def visit_false_node(node)
560
+ s(node, :false)
561
+ end
562
+
563
+ # foo => [*, bar, *]
564
+ # ^^^^^^^^^^^
565
+ def visit_find_pattern_node(node)
566
+ s(node, :find_pat, visit_pattern_constant(node.constant), :"*#{node.left.expression&.name}", *visit_all(node.requireds), :"*#{node.right.expression&.name}")
567
+ end
568
+
569
+ # if foo .. bar; end
570
+ # ^^^^^^^^^^
571
+ def visit_flip_flop_node(node)
572
+ if node.left.is_a?(IntegerNode) && node.right.is_a?(IntegerNode)
573
+ s(node, :lit, Range.new(node.left.value, node.right.value, node.exclude_end?))
574
+ else
575
+ s(node, node.exclude_end? ? :flip3 : :flip2, visit(node.left), visit(node.right))
576
+ end
577
+ end
578
+
579
+ # 1.0
580
+ # ^^^
581
+ def visit_float_node(node)
582
+ s(node, :lit, node.value)
583
+ end
584
+
585
+ # for foo in bar do end
586
+ # ^^^^^^^^^^^^^^^^^^^^^
587
+ def visit_for_node(node)
588
+ s(node, :for, visit(node.collection), visit(node.index), visit(node.statements))
589
+ end
590
+
591
+ # def foo(...); bar(...); end
592
+ # ^^^
593
+ def visit_forwarding_arguments_node(node)
594
+ s(node, :forward_args)
595
+ end
596
+
597
+ # def foo(...); end
598
+ # ^^^
599
+ def visit_forwarding_parameter_node(node)
600
+ s(node, :forward_args)
601
+ end
602
+
603
+ # super
604
+ # ^^^^^
605
+ #
606
+ # super {}
607
+ # ^^^^^^^^
608
+ def visit_forwarding_super_node(node)
609
+ visit_block(node, s(node, :zsuper), node.block)
610
+ end
611
+
612
+ # $foo
613
+ # ^^^^
614
+ def visit_global_variable_read_node(node)
615
+ s(node, :gvar, node.name)
616
+ end
617
+
618
+ # $foo = 1
619
+ # ^^^^^^^^
620
+ #
621
+ # $foo, $bar = 1
622
+ # ^^^^ ^^^^
623
+ def visit_global_variable_write_node(node)
624
+ s(node, :gasgn, node.name, visit_write_value(node.value))
625
+ end
626
+
627
+ # $foo += bar
628
+ # ^^^^^^^^^^^
629
+ def visit_global_variable_operator_write_node(node)
630
+ s(node, :gasgn, node.name, s(node, :call, s(node, :gvar, node.name), node.operator, visit(node.value)))
631
+ end
632
+
633
+ # $foo &&= bar
634
+ # ^^^^^^^^^^^^
635
+ def visit_global_variable_and_write_node(node)
636
+ s(node, :op_asgn_and, s(node, :gvar, node.name), s(node, :gasgn, node.name, visit_write_value(node.value)))
637
+ end
638
+
639
+ # $foo ||= bar
640
+ # ^^^^^^^^^^^^
641
+ def visit_global_variable_or_write_node(node)
642
+ s(node, :op_asgn_or, s(node, :gvar, node.name), s(node, :gasgn, node.name, visit_write_value(node.value)))
643
+ end
644
+
645
+ # $foo, = bar
646
+ # ^^^^
647
+ def visit_global_variable_target_node(node)
648
+ s(node, :gasgn, node.name)
649
+ end
650
+
651
+ # {}
652
+ # ^^
653
+ def visit_hash_node(node)
654
+ s(node, :hash).concat(node.elements.flat_map { |element| visit(element) })
655
+ end
656
+
657
+ # foo => {}
658
+ # ^^
659
+ def visit_hash_pattern_node(node)
660
+ result = s(node, :hash_pat, visit_pattern_constant(node.constant)).concat(node.elements.flat_map { |element| visit(element) })
661
+
662
+ case node.rest
663
+ when AssocSplatNode
664
+ result << s(node.rest, :kwrest, :"**#{node.rest.value&.name}")
665
+ when NoKeywordsParameterNode
666
+ result << visit(node.rest)
667
+ end
668
+
669
+ result
670
+ end
671
+
672
+ # if foo then bar end
673
+ # ^^^^^^^^^^^^^^^^^^^
674
+ #
675
+ # bar if foo
676
+ # ^^^^^^^^^^
677
+ #
678
+ # foo ? bar : baz
679
+ # ^^^^^^^^^^^^^^^
680
+ def visit_if_node(node)
681
+ s(node, :if, visit(node.predicate), visit(node.statements), visit(node.consequent))
682
+ end
683
+
684
+ # 1i
685
+ def visit_imaginary_node(node)
686
+ s(node, :lit, node.value)
687
+ end
688
+
689
+ # { foo: }
690
+ # ^^^^
691
+ def visit_implicit_node(node)
692
+ end
693
+
694
+ # foo { |bar,| }
695
+ # ^
696
+ def visit_implicit_rest_node(node)
697
+ end
698
+
699
+ # case foo; in bar; end
700
+ # ^^^^^^^^^^^^^^^^^^^^^
701
+ def visit_in_node(node)
702
+ pattern =
703
+ if node.pattern.is_a?(ConstantPathNode)
704
+ s(node.pattern, :const, visit(node.pattern))
705
+ else
706
+ node.pattern.accept(copy_compiler(in_pattern: true))
707
+ end
708
+
709
+ s(node, :in, pattern).concat(node.statements.nil? ? [nil] : visit_all(node.statements.body))
710
+ end
711
+
712
+ # foo[bar] += baz
713
+ # ^^^^^^^^^^^^^^^
714
+ def visit_index_operator_write_node(node)
715
+ arglist = nil
716
+
717
+ if !node.arguments.nil? || !node.block.nil?
718
+ arglist = s(node, :arglist).concat(visit_all(node.arguments&.arguments || []))
719
+ arglist << visit(node.block) if !node.block.nil?
720
+ end
721
+
722
+ s(node, :op_asgn1, visit(node.receiver), arglist, node.operator, visit_write_value(node.value))
723
+ end
724
+
725
+ # foo[bar] &&= baz
726
+ # ^^^^^^^^^^^^^^^^
727
+ def visit_index_and_write_node(node)
728
+ arglist = nil
729
+
730
+ if !node.arguments.nil? || !node.block.nil?
731
+ arglist = s(node, :arglist).concat(visit_all(node.arguments&.arguments || []))
732
+ arglist << visit(node.block) if !node.block.nil?
733
+ end
734
+
735
+ s(node, :op_asgn1, visit(node.receiver), arglist, :"&&", visit_write_value(node.value))
736
+ end
737
+
738
+ # foo[bar] ||= baz
739
+ # ^^^^^^^^^^^^^^^^
740
+ def visit_index_or_write_node(node)
741
+ arglist = nil
742
+
743
+ if !node.arguments.nil? || !node.block.nil?
744
+ arglist = s(node, :arglist).concat(visit_all(node.arguments&.arguments || []))
745
+ arglist << visit(node.block) if !node.block.nil?
746
+ end
747
+
748
+ s(node, :op_asgn1, visit(node.receiver), arglist, :"||", visit_write_value(node.value))
749
+ end
750
+
751
+ # foo[bar], = 1
752
+ # ^^^^^^^^
753
+ def visit_index_target_node(node)
754
+ arguments = visit_all(node.arguments&.arguments || [])
755
+ arguments << visit(node.block) unless node.block.nil?
756
+
757
+ s(node, :attrasgn, visit(node.receiver), :[]=).concat(arguments)
758
+ end
759
+
760
+ # @foo
761
+ # ^^^^
762
+ def visit_instance_variable_read_node(node)
763
+ s(node, :ivar, node.name)
764
+ end
765
+
766
+ # @foo = 1
767
+ # ^^^^^^^^
768
+ #
769
+ # @foo, @bar = 1
770
+ # ^^^^ ^^^^
771
+ def visit_instance_variable_write_node(node)
772
+ s(node, :iasgn, node.name, visit_write_value(node.value))
773
+ end
774
+
775
+ # @foo += bar
776
+ # ^^^^^^^^^^^
777
+ def visit_instance_variable_operator_write_node(node)
778
+ s(node, :iasgn, node.name, s(node, :call, s(node, :ivar, node.name), node.operator, visit_write_value(node.value)))
779
+ end
780
+
781
+ # @foo &&= bar
782
+ # ^^^^^^^^^^^^
783
+ def visit_instance_variable_and_write_node(node)
784
+ s(node, :op_asgn_and, s(node, :ivar, node.name), s(node, :iasgn, node.name, visit(node.value)))
785
+ end
786
+
787
+ # @foo ||= bar
788
+ # ^^^^^^^^^^^^
789
+ def visit_instance_variable_or_write_node(node)
790
+ s(node, :op_asgn_or, s(node, :ivar, node.name), s(node, :iasgn, node.name, visit(node.value)))
791
+ end
792
+
793
+ # @foo, = bar
794
+ # ^^^^
795
+ def visit_instance_variable_target_node(node)
796
+ s(node, :iasgn, node.name)
797
+ end
798
+
799
+ # 1
800
+ # ^
801
+ def visit_integer_node(node)
802
+ s(node, :lit, node.value)
803
+ end
804
+
805
+ # if /foo #{bar}/ then end
806
+ # ^^^^^^^^^^^^
807
+ def visit_interpolated_match_last_line_node(node)
808
+ s(node, :match, s(node, :dregx).concat(visit_interpolated_parts(node.parts)))
809
+ end
810
+
811
+ # /foo #{bar}/
812
+ # ^^^^^^^^^^^^
813
+ def visit_interpolated_regular_expression_node(node)
814
+ if node.parts.all? { |part| part.is_a?(StringNode) || (part.is_a?(EmbeddedStatementsNode) && part.statements&.body&.length == 1 && part.statements.body.first.is_a?(StringNode)) }
815
+ unescaped = node.parts.map { |part| part.is_a?(StringNode) ? part.unescaped : part.statements.body.first.unescaped }.join
816
+ s(node, :lit, Regexp.new(unescaped, node.options))
817
+ else
818
+ s(node, :dregx).concat(visit_interpolated_parts(node.parts)).tap do |result|
819
+ options = node.options
820
+ result << options if options != 0
821
+ end
822
+ end
823
+ end
824
+
825
+ # "foo #{bar}"
826
+ # ^^^^^^^^^^^^
827
+ def visit_interpolated_string_node(node)
828
+ if (node.parts.all? { |part| part.is_a?(StringNode) || (part.is_a?(EmbeddedStatementsNode) && part.statements&.body&.length == 1 && part.statements.body.first.is_a?(StringNode)) }) ||
829
+ (node.opening.nil? && node.parts.all? { |part| part.is_a?(StringNode) && !part.opening_loc.nil? })
830
+ unescaped = node.parts.map { |part| part.is_a?(StringNode) ? part.unescaped : part.statements.body.first.unescaped }.join
831
+ s(node, :str, unescaped)
832
+ else
833
+ s(node, :dstr).concat(visit_interpolated_parts(node.parts))
834
+ end
835
+ end
836
+
837
+ # :"foo #{bar}"
838
+ # ^^^^^^^^^^^^^
839
+ def visit_interpolated_symbol_node(node)
840
+ if node.parts.all? { |part| part.is_a?(StringNode) || (part.is_a?(EmbeddedStatementsNode) && part.statements&.body&.length == 1 && part.statements.body.first.is_a?(StringNode)) }
841
+ unescaped = node.parts.map { |part| part.is_a?(StringNode) ? part.unescaped : part.statements.body.first.unescaped }.join
842
+ s(node, :lit, unescaped.to_sym)
843
+ else
844
+ s(node, :dsym).concat(visit_interpolated_parts(node.parts))
845
+ end
846
+ end
847
+
848
+ # `foo #{bar}`
849
+ # ^^^^^^^^^^^^
850
+ def visit_interpolated_x_string_node(node)
851
+ children = visit_interpolated_parts(node.parts)
852
+ s(node.heredoc? ? node.parts.first : node, :dxstr).concat(children)
853
+ end
854
+
855
+ # Visit the interpolated content of the string-like node.
856
+ private def visit_interpolated_parts(parts)
857
+ parts.each_with_object([]).with_index do |(part, results), index|
858
+ if index == 0
859
+ if part.is_a?(StringNode)
860
+ results << part.unescaped
861
+ else
862
+ results << ""
863
+ results << visit(part)
864
+ end
865
+ else
866
+ results << visit(part)
867
+ end
868
+ end
869
+ end
870
+
871
+ # foo(bar: baz)
872
+ # ^^^^^^^^
873
+ def visit_keyword_hash_node(node)
874
+ s(node, :hash).concat(node.elements.flat_map { |element| visit(element) })
875
+ end
876
+
877
+ # def foo(**bar); end
878
+ # ^^^^^
879
+ #
880
+ # def foo(**); end
881
+ # ^^
882
+ def visit_keyword_rest_parameter_node(node)
883
+ :"**#{node.name}"
884
+ end
885
+
886
+ # -> {}
887
+ def visit_lambda_node(node)
888
+ parameters =
889
+ case node.parameters
890
+ when nil, NumberedParametersNode
891
+ s(node, :args)
892
+ else
893
+ visit(node.parameters)
894
+ end
895
+
896
+ if node.body.nil?
897
+ s(node, :iter, s(node, :lambda), parameters)
898
+ else
899
+ s(node, :iter, s(node, :lambda), parameters, visit(node.body))
900
+ end
901
+ end
902
+
903
+ # foo
904
+ # ^^^
905
+ def visit_local_variable_read_node(node)
906
+ if node.name.match?(/^_\d$/)
907
+ s(node, :call, nil, node.name)
908
+ else
909
+ s(node, :lvar, node.name)
910
+ end
911
+ end
912
+
913
+ # foo = 1
914
+ # ^^^^^^^
915
+ #
916
+ # foo, bar = 1
917
+ # ^^^ ^^^
918
+ def visit_local_variable_write_node(node)
919
+ s(node, :lasgn, node.name, visit_write_value(node.value))
920
+ end
921
+
922
+ # foo += bar
923
+ # ^^^^^^^^^^
924
+ def visit_local_variable_operator_write_node(node)
925
+ s(node, :lasgn, node.name, s(node, :call, s(node, :lvar, node.name), node.operator, visit_write_value(node.value)))
926
+ end
927
+
928
+ # foo &&= bar
929
+ # ^^^^^^^^^^^
930
+ def visit_local_variable_and_write_node(node)
931
+ s(node, :op_asgn_and, s(node, :lvar, node.name), s(node, :lasgn, node.name, visit_write_value(node.value)))
932
+ end
933
+
934
+ # foo ||= bar
935
+ # ^^^^^^^^^^^
936
+ def visit_local_variable_or_write_node(node)
937
+ s(node, :op_asgn_or, s(node, :lvar, node.name), s(node, :lasgn, node.name, visit_write_value(node.value)))
938
+ end
939
+
940
+ # foo, = bar
941
+ # ^^^
942
+ def visit_local_variable_target_node(node)
943
+ s(node, :lasgn, node.name)
944
+ end
945
+
946
+ # if /foo/ then end
947
+ # ^^^^^
948
+ def visit_match_last_line_node(node)
949
+ s(node, :match, s(node, :lit, Regexp.new(node.unescaped, node.options)))
950
+ end
951
+
952
+ # foo in bar
953
+ # ^^^^^^^^^^
954
+ def visit_match_predicate_node(node)
955
+ s(node, :case, visit(node.value), s(node, :in, node.pattern.accept(copy_compiler(in_pattern: true)), nil), nil)
956
+ end
957
+
958
+ # foo => bar
959
+ # ^^^^^^^^^^
960
+ def visit_match_required_node(node)
961
+ s(node, :case, visit(node.value), s(node, :in, node.pattern.accept(copy_compiler(in_pattern: true)), nil), nil)
962
+ end
963
+
964
+ # /(?<foo>foo)/ =~ bar
965
+ # ^^^^^^^^^^^^^^^^^^^^
966
+ def visit_match_write_node(node)
967
+ s(node, :match2, visit(node.call.receiver), visit(node.call.arguments.arguments.first))
968
+ end
969
+
970
+ # A node that is missing from the syntax tree. This is only used in the
971
+ # case of a syntax error. The parser gem doesn't have such a concept, so
972
+ # we invent our own here.
973
+ def visit_missing_node(node)
974
+ raise "Cannot visit missing node directly"
975
+ end
976
+
977
+ # module Foo; end
978
+ # ^^^^^^^^^^^^^^^
979
+ def visit_module_node(node)
980
+ name =
981
+ if node.constant_path.is_a?(ConstantReadNode)
982
+ node.name
983
+ else
984
+ visit(node.constant_path)
985
+ end
986
+
987
+ if node.body.nil?
988
+ s(node, :module, name)
989
+ elsif node.body.is_a?(StatementsNode)
990
+ compiler = copy_compiler(in_def: false)
991
+ s(node, :module, name).concat(node.body.body.map { |child| child.accept(compiler) })
992
+ else
993
+ s(node, :module, name, node.body.accept(copy_compiler(in_def: false)))
994
+ end
995
+ end
996
+
997
+ # foo, bar = baz
998
+ # ^^^^^^^^
999
+ def visit_multi_target_node(node)
1000
+ targets = [*node.lefts]
1001
+ targets << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
1002
+ targets.concat(node.rights)
1003
+
1004
+ s(node, :masgn, s(node, :array).concat(visit_all(targets)))
1005
+ end
1006
+
1007
+ # foo, bar = baz
1008
+ # ^^^^^^^^^^^^^^
1009
+ def visit_multi_write_node(node)
1010
+ targets = [*node.lefts]
1011
+ targets << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
1012
+ targets.concat(node.rights)
1013
+
1014
+ value =
1015
+ if node.value.is_a?(ArrayNode) && node.value.opening_loc.nil?
1016
+ if node.value.elements.length == 1 && node.value.elements.first.is_a?(SplatNode)
1017
+ visit(node.value.elements.first)
1018
+ else
1019
+ visit(node.value)
1020
+ end
1021
+ else
1022
+ s(node.value, :to_ary, visit(node.value))
1023
+ end
1024
+
1025
+ s(node, :masgn, s(node, :array).concat(visit_all(targets)), value)
1026
+ end
1027
+
1028
+ # next
1029
+ # ^^^^
1030
+ #
1031
+ # next foo
1032
+ # ^^^^^^^^
1033
+ def visit_next_node(node)
1034
+ if node.arguments.nil?
1035
+ s(node, :next)
1036
+ elsif node.arguments.arguments.length == 1
1037
+ argument = node.arguments.arguments.first
1038
+ s(node, :next, argument.is_a?(SplatNode) ? s(node, :svalue, visit(argument)) : visit(argument))
1039
+ else
1040
+ s(node, :next, s(node, :array).concat(visit_all(node.arguments.arguments)))
1041
+ end
1042
+ end
1043
+
1044
+ # nil
1045
+ # ^^^
1046
+ def visit_nil_node(node)
1047
+ s(node, :nil)
1048
+ end
1049
+
1050
+ # def foo(**nil); end
1051
+ # ^^^^^
1052
+ def visit_no_keywords_parameter_node(node)
1053
+ in_pattern ? s(node, :kwrest, :"**nil") : :"**nil"
1054
+ end
1055
+
1056
+ # -> { _1 + _2 }
1057
+ # ^^^^^^^^^^^^^^
1058
+ def visit_numbered_parameters_node(node)
1059
+ raise "Cannot visit numbered parameters directly"
1060
+ end
1061
+
1062
+ # $1
1063
+ # ^^
1064
+ def visit_numbered_reference_read_node(node)
1065
+ s(node, :nth_ref, node.number)
1066
+ end
1067
+
1068
+ # def foo(bar: baz); end
1069
+ # ^^^^^^^^
1070
+ def visit_optional_keyword_parameter_node(node)
1071
+ s(node, :kwarg, node.name, visit(node.value))
1072
+ end
1073
+
1074
+ # def foo(bar = 1); end
1075
+ # ^^^^^^^
1076
+ def visit_optional_parameter_node(node)
1077
+ s(node, :lasgn, node.name, visit(node.value))
1078
+ end
1079
+
1080
+ # a or b
1081
+ # ^^^^^^
1082
+ def visit_or_node(node)
1083
+ s(node, :or, visit(node.left), visit(node.right))
1084
+ end
1085
+
1086
+ # def foo(bar, *baz); end
1087
+ # ^^^^^^^^^
1088
+ def visit_parameters_node(node)
1089
+ children =
1090
+ node.compact_child_nodes.map do |element|
1091
+ if element.is_a?(MultiTargetNode)
1092
+ visit_destructured_parameter(element)
1093
+ else
1094
+ visit(element)
1095
+ end
1096
+ end
1097
+
1098
+ s(node, :args).concat(children)
1099
+ end
1100
+
1101
+ # def foo((bar, baz)); end
1102
+ # ^^^^^^^^^^
1103
+ private def visit_destructured_parameter(node)
1104
+ children =
1105
+ [*node.lefts, *node.rest, *node.rights].map do |child|
1106
+ case child
1107
+ when RequiredParameterNode
1108
+ visit(child)
1109
+ when MultiTargetNode
1110
+ visit_destructured_parameter(child)
1111
+ when SplatNode
1112
+ :"*#{child.expression&.name}"
1113
+ else
1114
+ raise
1115
+ end
1116
+ end
1117
+
1118
+ s(node, :masgn).concat(children)
1119
+ end
1120
+
1121
+ # ()
1122
+ # ^^
1123
+ #
1124
+ # (1)
1125
+ # ^^^
1126
+ def visit_parentheses_node(node)
1127
+ if node.body.nil?
1128
+ s(node, :nil)
1129
+ else
1130
+ visit(node.body)
1131
+ end
1132
+ end
1133
+
1134
+ # foo => ^(bar)
1135
+ # ^^^^^^
1136
+ def visit_pinned_expression_node(node)
1137
+ node.expression.accept(copy_compiler(in_pattern: false))
1138
+ end
1139
+
1140
+ # foo = 1 and bar => ^foo
1141
+ # ^^^^
1142
+ def visit_pinned_variable_node(node)
1143
+ if node.variable.is_a?(LocalVariableReadNode) && node.variable.name.match?(/^_\d$/)
1144
+ s(node, :lvar, node.variable.name)
1145
+ else
1146
+ visit(node.variable)
1147
+ end
1148
+ end
1149
+
1150
+ # END {}
1151
+ def visit_post_execution_node(node)
1152
+ s(node, :iter, s(node, :postexe), 0, visit(node.statements))
1153
+ end
1154
+
1155
+ # BEGIN {}
1156
+ def visit_pre_execution_node(node)
1157
+ s(node, :iter, s(node, :preexe), 0, visit(node.statements))
1158
+ end
1159
+
1160
+ # The top-level program node.
1161
+ def visit_program_node(node)
1162
+ visit(node.statements)
1163
+ end
1164
+
1165
+ # 0..5
1166
+ # ^^^^
1167
+ def visit_range_node(node)
1168
+ if !in_pattern && !node.left.nil? && !node.right.nil? && ([node.left.type, node.right.type] - %i[nil_node integer_node]).empty?
1169
+ left = node.left.value if node.left.is_a?(IntegerNode)
1170
+ right = node.right.value if node.right.is_a?(IntegerNode)
1171
+ s(node, :lit, Range.new(left, right, node.exclude_end?))
1172
+ else
1173
+ s(node, node.exclude_end? ? :dot3 : :dot2, visit_range_bounds_node(node.left), visit_range_bounds_node(node.right))
1174
+ end
1175
+ end
1176
+
1177
+ # If the bounds of a range node are empty parentheses, then they do not
1178
+ # get replaced by their usual s(:nil), but instead are s(:begin).
1179
+ private def visit_range_bounds_node(node)
1180
+ if node.is_a?(ParenthesesNode) && node.body.nil?
1181
+ s(node, :begin)
1182
+ else
1183
+ visit(node)
1184
+ end
1185
+ end
1186
+
1187
+ # 1r
1188
+ # ^^
1189
+ def visit_rational_node(node)
1190
+ s(node, :lit, node.value)
1191
+ end
1192
+
1193
+ # redo
1194
+ # ^^^^
1195
+ def visit_redo_node(node)
1196
+ s(node, :redo)
1197
+ end
1198
+
1199
+ # /foo/
1200
+ # ^^^^^
1201
+ def visit_regular_expression_node(node)
1202
+ s(node, :lit, Regexp.new(node.unescaped, node.options))
1203
+ end
1204
+
1205
+ # def foo(bar:); end
1206
+ # ^^^^
1207
+ def visit_required_keyword_parameter_node(node)
1208
+ s(node, :kwarg, node.name)
1209
+ end
1210
+
1211
+ # def foo(bar); end
1212
+ # ^^^
1213
+ def visit_required_parameter_node(node)
1214
+ node.name
1215
+ end
1216
+
1217
+ # foo rescue bar
1218
+ # ^^^^^^^^^^^^^^
1219
+ def visit_rescue_modifier_node(node)
1220
+ s(node, :rescue, visit(node.expression), s(node.rescue_expression, :resbody, s(node.rescue_expression, :array), visit(node.rescue_expression)))
1221
+ end
1222
+
1223
+ # begin; rescue; end
1224
+ # ^^^^^^^
1225
+ def visit_rescue_node(node)
1226
+ exceptions =
1227
+ if node.exceptions.length == 1 && node.exceptions.first.is_a?(SplatNode)
1228
+ visit(node.exceptions.first)
1229
+ else
1230
+ s(node, :array).concat(visit_all(node.exceptions))
1231
+ end
1232
+
1233
+ if !node.reference.nil?
1234
+ exceptions << (visit(node.reference) << s(node.reference, :gvar, :"$!"))
1235
+ end
1236
+
1237
+ s(node, :resbody, exceptions).concat(node.statements.nil? ? [nil] : visit_all(node.statements.body))
1238
+ end
1239
+
1240
+ # def foo(*bar); end
1241
+ # ^^^^
1242
+ #
1243
+ # def foo(*); end
1244
+ # ^
1245
+ def visit_rest_parameter_node(node)
1246
+ :"*#{node.name}"
1247
+ end
1248
+
1249
+ # retry
1250
+ # ^^^^^
1251
+ def visit_retry_node(node)
1252
+ s(node, :retry)
1253
+ end
1254
+
1255
+ # return
1256
+ # ^^^^^^
1257
+ #
1258
+ # return 1
1259
+ # ^^^^^^^^
1260
+ def visit_return_node(node)
1261
+ if node.arguments.nil?
1262
+ s(node, :return)
1263
+ elsif node.arguments.arguments.length == 1
1264
+ argument = node.arguments.arguments.first
1265
+ s(node, :return, argument.is_a?(SplatNode) ? s(node, :svalue, visit(argument)) : visit(argument))
1266
+ else
1267
+ s(node, :return, s(node, :array).concat(visit_all(node.arguments.arguments)))
1268
+ end
1269
+ end
1270
+
1271
+ # self
1272
+ # ^^^^
1273
+ def visit_self_node(node)
1274
+ s(node, :self)
1275
+ end
1276
+
1277
+ # class << self; end
1278
+ # ^^^^^^^^^^^^^^^^^^
1279
+ def visit_singleton_class_node(node)
1280
+ s(node, :sclass, visit(node.expression)).tap do |sexp|
1281
+ sexp << node.body.accept(copy_compiler(in_def: false)) unless node.body.nil?
1282
+ end
1283
+ end
1284
+
1285
+ # __ENCODING__
1286
+ # ^^^^^^^^^^^^
1287
+ def visit_source_encoding_node(node)
1288
+ # TODO
1289
+ s(node, :colon2, s(node, :const, :Encoding), :UTF_8)
1290
+ end
1291
+
1292
+ # __FILE__
1293
+ # ^^^^^^^^
1294
+ def visit_source_file_node(node)
1295
+ s(node, :str, file)
1296
+ end
1297
+
1298
+ # __LINE__
1299
+ # ^^^^^^^^
1300
+ def visit_source_line_node(node)
1301
+ s(node, :lit, node.location.start_line)
1302
+ end
1303
+
1304
+ # foo(*bar)
1305
+ # ^^^^
1306
+ #
1307
+ # def foo((bar, *baz)); end
1308
+ # ^^^^
1309
+ #
1310
+ # def foo(*); bar(*); end
1311
+ # ^
1312
+ def visit_splat_node(node)
1313
+ if node.expression.nil?
1314
+ s(node, :splat)
1315
+ else
1316
+ s(node, :splat, visit(node.expression))
1317
+ end
1318
+ end
1319
+
1320
+ # A list of statements.
1321
+ def visit_statements_node(node)
1322
+ first, *rest = node.body
1323
+
1324
+ if rest.empty?
1325
+ visit(first)
1326
+ else
1327
+ s(node, :block).concat(visit_all(node.body))
1328
+ end
1329
+ end
1330
+
1331
+ # "foo"
1332
+ # ^^^^^
1333
+ def visit_string_node(node)
1334
+ s(node, :str, node.unescaped)
1335
+ end
1336
+
1337
+ # super(foo)
1338
+ # ^^^^^^^^^^
1339
+ def visit_super_node(node)
1340
+ arguments = node.arguments&.arguments || []
1341
+ block = node.block
1342
+
1343
+ if block.is_a?(BlockArgumentNode)
1344
+ arguments << block
1345
+ block = nil
1346
+ end
1347
+
1348
+ visit_block(node, s(node, :super).concat(visit_all(arguments)), block)
1349
+ end
1350
+
1351
+ # :foo
1352
+ # ^^^^
1353
+ def visit_symbol_node(node)
1354
+ node.value == "!@" ? s(node, :lit, :"!@") : s(node, :lit, node.unescaped.to_sym)
1355
+ end
1356
+
1357
+ # true
1358
+ # ^^^^
1359
+ def visit_true_node(node)
1360
+ s(node, :true)
1361
+ end
1362
+
1363
+ # undef foo
1364
+ # ^^^^^^^^^
1365
+ def visit_undef_node(node)
1366
+ names = node.names.map { |name| s(node, :undef, visit(name)) }
1367
+ names.length == 1 ? names.first : s(node, :block).concat(names)
1368
+ end
1369
+
1370
+ # unless foo; bar end
1371
+ # ^^^^^^^^^^^^^^^^^^^
1372
+ #
1373
+ # bar unless foo
1374
+ # ^^^^^^^^^^^^^^
1375
+ def visit_unless_node(node)
1376
+ s(node, :if, visit(node.predicate), visit(node.consequent), visit(node.statements))
1377
+ end
1378
+
1379
+ # until foo; bar end
1380
+ # ^^^^^^^^^^^^^^^^^
1381
+ #
1382
+ # bar until foo
1383
+ # ^^^^^^^^^^^^^
1384
+ def visit_until_node(node)
1385
+ s(node, :until, visit(node.predicate), visit(node.statements), !node.begin_modifier?)
1386
+ end
1387
+
1388
+ # case foo; when bar; end
1389
+ # ^^^^^^^^^^^^^
1390
+ def visit_when_node(node)
1391
+ s(node, :when, s(node, :array).concat(visit_all(node.conditions))).concat(node.statements.nil? ? [nil] : visit_all(node.statements.body))
1392
+ end
1393
+
1394
+ # while foo; bar end
1395
+ # ^^^^^^^^^^^^^^^^^^
1396
+ #
1397
+ # bar while foo
1398
+ # ^^^^^^^^^^^^^
1399
+ def visit_while_node(node)
1400
+ s(node, :while, visit(node.predicate), visit(node.statements), !node.begin_modifier?)
1401
+ end
1402
+
1403
+ # `foo`
1404
+ # ^^^^^
1405
+ def visit_x_string_node(node)
1406
+ result = s(node, :xstr, node.unescaped)
1407
+
1408
+ if node.heredoc?
1409
+ result.line = node.content_loc.start_line
1410
+ result.max_line = node.content_loc.end_line
1411
+ end
1412
+
1413
+ result
1414
+ end
1415
+
1416
+ # yield
1417
+ # ^^^^^
1418
+ #
1419
+ # yield 1
1420
+ # ^^^^^^^
1421
+ def visit_yield_node(node)
1422
+ s(node, :yield).concat(visit_all(node.arguments&.arguments || []))
1423
+ end
1424
+
1425
+ private
1426
+
1427
+ # Create a new compiler with the given options.
1428
+ def copy_compiler(in_def: self.in_def, in_pattern: self.in_pattern)
1429
+ Compiler.new(file, in_def: in_def, in_pattern: in_pattern)
1430
+ end
1431
+
1432
+ # Create a new Sexp object from the given prism node and arguments.
1433
+ def s(node, *arguments)
1434
+ result = Sexp.new(*arguments)
1435
+ result.file = file
1436
+ result.line = node.location.start_line
1437
+ result.max_line = node.location.end_line
1438
+ result
1439
+ end
1440
+
1441
+ # Visit a block node, which will modify the AST by wrapping the given
1442
+ # visited node in an iter node.
1443
+ def visit_block(node, sexp, block)
1444
+ if block.nil?
1445
+ sexp
1446
+ else
1447
+ parameters =
1448
+ case block.parameters
1449
+ when nil, NumberedParametersNode
1450
+ 0
1451
+ else
1452
+ visit(block.parameters)
1453
+ end
1454
+
1455
+ if block.body.nil?
1456
+ s(node, :iter, sexp, parameters)
1457
+ else
1458
+ s(node, :iter, sexp, parameters, visit(block.body))
1459
+ end
1460
+ end
1461
+ end
1462
+
1463
+ # Pattern constants get wrapped in another layer of :const.
1464
+ def visit_pattern_constant(node)
1465
+ case node
1466
+ when nil
1467
+ # nothing
1468
+ when ConstantReadNode
1469
+ visit(node)
1470
+ else
1471
+ s(node, :const, visit(node))
1472
+ end
1473
+ end
1474
+
1475
+ # Visit the value of a write, which will be on the right-hand side of
1476
+ # a write operator. Because implicit arrays can have splats, those could
1477
+ # potentially be wrapped in an svalue node.
1478
+ def visit_write_value(node)
1479
+ if node.is_a?(ArrayNode) && node.opening_loc.nil?
1480
+ if node.elements.length == 1 && node.elements.first.is_a?(SplatNode)
1481
+ s(node, :svalue, visit(node.elements.first))
1482
+ else
1483
+ s(node, :svalue, visit(node))
1484
+ end
1485
+ else
1486
+ visit(node)
1487
+ end
1488
+ end
1489
+ end
1490
+
1491
+ private_constant :Compiler
1492
+
1493
+ class << self
1494
+ # Parse the given source and translate it into the seattlerb/ruby_parser
1495
+ # gem's Sexp format.
1496
+ def parse(source, filepath = "(string)")
1497
+ translate(Prism.parse(source), filepath)
1498
+ end
1499
+
1500
+ # Parse the given file and translate it into the seattlerb/ruby_parser
1501
+ # gem's Sexp format.
1502
+ def parse_file(filepath)
1503
+ translate(Prism.parse_file(filepath), filepath)
1504
+ end
1505
+
1506
+ private
1507
+
1508
+ # Translate the given parse result and filepath into the
1509
+ # seattlerb/ruby_parser gem's Sexp format.
1510
+ def translate(result, filepath)
1511
+ if result.failure?
1512
+ error = result.errors.first
1513
+ raise ::RubyParser::SyntaxError, "#{filepath}:#{error.location.start_line} :: #{error.message}"
1514
+ end
1515
+
1516
+ result.value.accept(Compiler.new(filepath))
1517
+ end
1518
+ end
1519
+ end
1520
+ end
1521
+ end