parser-prism 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,1758 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prism
4
+ class ParserCompiler < Compiler
5
+ attr_reader :parser, :builder, :source_buffer, :offset_cache
6
+ attr_reader :locals, :in_destructure, :in_pattern
7
+
8
+ def initialize(parser, offset_cache, locals: nil, in_destructure: false, in_pattern: false)
9
+ @parser = parser
10
+ @builder = parser.builder
11
+ @source_buffer = parser.source_buffer
12
+ @offset_cache = offset_cache
13
+
14
+ @locals = locals
15
+ @in_destructure = in_destructure
16
+ @in_pattern = in_pattern
17
+ end
18
+
19
+ # alias foo bar
20
+ # ^^^^^^^^^^^^^
21
+ def visit_alias_method_node(node)
22
+ builder.alias(token(node.keyword_loc), visit(node.new_name), visit(node.old_name))
23
+ end
24
+
25
+ # alias $foo $bar
26
+ # ^^^^^^^^^^^^^^^
27
+ def visit_alias_global_variable_node(node)
28
+ builder.alias(token(node.keyword_loc), visit(node.new_name), visit(node.old_name))
29
+ end
30
+
31
+ # foo => bar | baz
32
+ # ^^^^^^^^^
33
+ def visit_alternation_pattern_node(node)
34
+ builder.match_alt(visit(node.left), token(node.operator_loc), visit(node.right))
35
+ end
36
+
37
+ # a and b
38
+ # ^^^^^^^
39
+ def visit_and_node(node)
40
+ builder.logical_op(:and, visit(node.left), token(node.operator_loc), visit(node.right))
41
+ end
42
+
43
+ # []
44
+ # ^^
45
+ def visit_array_node(node)
46
+ builder.array(token(node.opening_loc), visit_all(node.elements), token(node.closing_loc))
47
+ end
48
+
49
+ # foo => [bar]
50
+ # ^^^^^
51
+ def visit_array_pattern_node(node)
52
+ if node.constant
53
+ builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(nil, visit_all([*node.requireds, *node.rest, *node.posts]), nil), token(node.closing_loc))
54
+ else
55
+ builder.array_pattern(token(node.opening_loc), visit_all([*node.requireds, *node.rest, *node.posts]), token(node.closing_loc))
56
+ end
57
+ end
58
+
59
+ # foo(bar)
60
+ # ^^^
61
+ def visit_arguments_node(node)
62
+ visit_all(node.arguments)
63
+ end
64
+
65
+ # { a: 1 }
66
+ # ^^^^
67
+ def visit_assoc_node(node)
68
+ if node.value.is_a?(ImplicitNode)
69
+ builder.pair_label([node.key.slice.chomp(":"), srange(node.key.location)])
70
+ elsif in_pattern && node.value.nil?
71
+ if node.key.is_a?(SymbolNode)
72
+ builder.match_hash_var([node.key.unescaped, srange(node.key.location)])
73
+ else
74
+ builder.match_hash_var_from_str(token(node.key.opening_loc), visit_all(node.key.parts), token(node.key.closing_loc))
75
+ end
76
+ elsif node.operator_loc
77
+ builder.pair(visit(node.key), token(node.operator_loc), visit(node.value))
78
+ elsif node.key.is_a?(SymbolNode) && node.key.opening_loc.nil?
79
+ builder.pair_keyword([node.key.unescaped, srange(node.key.location)], visit(node.value))
80
+ else
81
+ parts =
82
+ if node.key.is_a?(SymbolNode)
83
+ [builder.string_internal([node.key.unescaped, srange(node.key.value_loc)])]
84
+ else
85
+ visit_all(node.key.parts)
86
+ end
87
+
88
+ builder.pair_quoted(token(node.key.opening_loc), parts, token(node.key.closing_loc), visit(node.value))
89
+ end
90
+ end
91
+
92
+ # def foo(**); bar(**); end
93
+ # ^^
94
+ #
95
+ # { **foo }
96
+ # ^^^^^
97
+ def visit_assoc_splat_node(node)
98
+ if node.value.nil? && locals.include?(:**)
99
+ builder.forwarded_kwrestarg(token(node.operator_loc))
100
+ else
101
+ builder.kwsplat(token(node.operator_loc), visit(node.value))
102
+ end
103
+ end
104
+
105
+ # $+
106
+ # ^^
107
+ def visit_back_reference_read_node(node)
108
+ builder.back_ref(token(node.location))
109
+ end
110
+
111
+ # begin end
112
+ # ^^^^^^^^^
113
+ def visit_begin_node(node)
114
+ rescue_bodies = []
115
+
116
+ if (rescue_clause = node.rescue_clause)
117
+ begin
118
+ find_start_offset = (rescue_clause.reference&.location || rescue_clause.exceptions.last&.location || rescue_clause.keyword_loc).end_offset
119
+ find_end_offset = (rescue_clause.statements&.location&.start_offset || rescue_clause.consequent&.location&.start_offset || (find_start_offset + 1))
120
+
121
+ rescue_bodies << builder.rescue_body(
122
+ token(rescue_clause.keyword_loc),
123
+ rescue_clause.exceptions.any? ? builder.array(nil, visit_all(rescue_clause.exceptions), nil) : nil,
124
+ token(rescue_clause.operator_loc),
125
+ visit(rescue_clause.reference),
126
+ srange_find(find_start_offset, find_end_offset, [";"]),
127
+ visit(rescue_clause.statements)
128
+ )
129
+ end until (rescue_clause = rescue_clause.consequent).nil?
130
+ end
131
+
132
+ begin_body =
133
+ builder.begin_body(
134
+ visit(node.statements),
135
+ rescue_bodies,
136
+ token(node.else_clause&.else_keyword_loc),
137
+ visit(node.else_clause),
138
+ token(node.ensure_clause&.ensure_keyword_loc),
139
+ visit(node.ensure_clause&.statements)
140
+ )
141
+
142
+ if node.begin_keyword_loc
143
+ builder.begin_keyword(token(node.begin_keyword_loc), begin_body, token(node.end_keyword_loc))
144
+ else
145
+ begin_body
146
+ end
147
+ end
148
+
149
+ # foo(&bar)
150
+ # ^^^^
151
+ def visit_block_argument_node(node)
152
+ builder.block_pass(token(node.operator_loc), visit(node.expression))
153
+ end
154
+
155
+ # foo { |; bar| }
156
+ # ^^^
157
+ def visit_block_local_variable_node(node)
158
+ builder.shadowarg(token(node.location))
159
+ end
160
+
161
+ # A block on a keyword or method call.
162
+ def visit_block_node(node)
163
+ raise "Cannot directly compile block nodes"
164
+ end
165
+
166
+ # def foo(&bar); end
167
+ # ^^^^
168
+ def visit_block_parameter_node(node)
169
+ builder.blockarg(token(node.operator_loc), token(node.name_loc))
170
+ end
171
+
172
+ # A block's parameters.
173
+ def visit_block_parameters_node(node)
174
+ [*visit(node.parameters)].concat(visit_all(node.locals))
175
+ end
176
+
177
+ # break
178
+ # ^^^^^
179
+ #
180
+ # break foo
181
+ # ^^^^^^^^^
182
+ def visit_break_node(node)
183
+ builder.keyword_cmd(:break, token(node.keyword_loc), nil, visit(node.arguments) || [], nil)
184
+ end
185
+
186
+ # foo
187
+ # ^^^
188
+ #
189
+ # foo.bar
190
+ # ^^^^^^^
191
+ #
192
+ # foo.bar() {}
193
+ # ^^^^^^^^^^^^
194
+ def visit_call_node(node)
195
+ name = node.name
196
+ arguments = node.arguments&.arguments || []
197
+ block = node.block
198
+
199
+ if block.is_a?(BlockArgumentNode)
200
+ arguments = [*arguments, block]
201
+ block = nil
202
+ end
203
+
204
+ visit_block(
205
+ if name == :!
206
+ builder.not_op(
207
+ token(node.message_loc),
208
+ token(node.opening_loc),
209
+ visit(node.receiver),
210
+ token(node.closing_loc)
211
+ )
212
+ elsif name == :[]
213
+ builder.index(
214
+ visit(node.receiver),
215
+ token(node.opening_loc),
216
+ visit_all(arguments),
217
+ token(node.closing_loc)
218
+ )
219
+ elsif name == :[]= && node.message != "[]=" && node.arguments && block.nil?
220
+ builder.assign(
221
+ builder.index_asgn(
222
+ visit(node.receiver),
223
+ token(node.opening_loc),
224
+ visit_all(node.arguments.arguments[...-1]),
225
+ token(node.closing_loc),
226
+ ),
227
+ srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset, ["="]),
228
+ visit(node.arguments.arguments.last)
229
+ )
230
+ else
231
+ message_loc = node.message_loc
232
+ call_operator_loc = node.call_operator_loc
233
+ call_operator = [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)] if call_operator_loc
234
+
235
+ if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil?
236
+ builder.assign(
237
+ builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)),
238
+ srange_find(message_loc.end_offset, node.arguments.location.start_offset, ["="]),
239
+ visit(node.arguments.arguments.last)
240
+ )
241
+ else
242
+ builder.call_method(
243
+ visit(node.receiver),
244
+ call_operator,
245
+ message_loc ? [node.name, srange(message_loc)] : nil,
246
+ token(node.opening_loc),
247
+ visit_all(arguments),
248
+ token(node.closing_loc)
249
+ )
250
+ end
251
+ end,
252
+ block
253
+ )
254
+ end
255
+
256
+ # foo.bar += baz
257
+ # ^^^^^^^^^^^^^^^
258
+ def visit_call_operator_write_node(node)
259
+ call_operator_loc = node.call_operator_loc
260
+
261
+ builder.op_assign(
262
+ builder.call_method(
263
+ visit(node.receiver),
264
+ call_operator_loc.nil? ? nil : [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)],
265
+ node.message_loc ? [node.read_name, srange(node.message_loc)] : nil,
266
+ nil,
267
+ [],
268
+ nil
269
+ ),
270
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
271
+ visit(node.value)
272
+ )
273
+ end
274
+
275
+ # foo.bar &&= baz
276
+ # ^^^^^^^^^^^^^^^
277
+ alias visit_call_and_write_node visit_call_operator_write_node
278
+
279
+ # foo.bar ||= baz
280
+ # ^^^^^^^^^^^^^^^
281
+ alias visit_call_or_write_node visit_call_operator_write_node
282
+
283
+ # foo.bar, = 1
284
+ # ^^^^^^^
285
+ def visit_call_target_node(node)
286
+ call_operator_loc = node.call_operator_loc
287
+
288
+ builder.attr_asgn(
289
+ visit(node.receiver),
290
+ call_operator_loc.nil? ? nil : [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)],
291
+ token(node.message_loc)
292
+ )
293
+ end
294
+
295
+ # foo => bar => baz
296
+ # ^^^^^^^^^^
297
+ def visit_capture_pattern_node(node)
298
+ builder.match_as(visit(node.value), token(node.operator_loc), visit(node.target))
299
+ end
300
+
301
+ # case foo; when bar; end
302
+ # ^^^^^^^^^^^^^^^^^^^^^^^
303
+ def visit_case_node(node)
304
+ builder.case(
305
+ token(node.case_keyword_loc),
306
+ visit(node.predicate),
307
+ visit_all(node.conditions),
308
+ token(node.consequent&.else_keyword_loc),
309
+ visit(node.consequent),
310
+ token(node.end_keyword_loc)
311
+ )
312
+ end
313
+
314
+ # case foo; in bar; end
315
+ # ^^^^^^^^^^^^^^^^^^^^^
316
+ def visit_case_match_node(node)
317
+ builder.case_match(
318
+ token(node.case_keyword_loc),
319
+ visit(node.predicate),
320
+ visit_all(node.conditions),
321
+ token(node.consequent&.else_keyword_loc),
322
+ visit(node.consequent),
323
+ token(node.end_keyword_loc)
324
+ )
325
+ end
326
+
327
+ # class Foo; end
328
+ # ^^^^^^^^^^^^^^
329
+ def visit_class_node(node)
330
+ builder.def_class(
331
+ token(node.class_keyword_loc),
332
+ visit(node.constant_path),
333
+ token(node.inheritance_operator_loc),
334
+ visit(node.superclass),
335
+ node.body&.accept(copy_compiler(locals: node.locals)),
336
+ token(node.end_keyword_loc)
337
+ )
338
+ end
339
+
340
+ # @@foo
341
+ # ^^^^^
342
+ def visit_class_variable_read_node(node)
343
+ builder.cvar(token(node.location))
344
+ end
345
+
346
+ # @@foo = 1
347
+ # ^^^^^^^^^
348
+ #
349
+ # @@foo, @@bar = 1
350
+ # ^^^^^ ^^^^^
351
+ def visit_class_variable_write_node(node)
352
+ builder.assign(
353
+ builder.assignable(builder.cvar(token(node.name_loc))),
354
+ token(node.operator_loc),
355
+ visit(node.value)
356
+ )
357
+ end
358
+
359
+ # @@foo += bar
360
+ # ^^^^^^^^^^^^
361
+ def visit_class_variable_operator_write_node(node)
362
+ builder.op_assign(
363
+ builder.assignable(builder.cvar(token(node.name_loc))),
364
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
365
+ visit(node.value)
366
+ )
367
+ end
368
+
369
+ # @@foo &&= bar
370
+ # ^^^^^^^^^^^^^
371
+ alias visit_class_variable_and_write_node visit_class_variable_operator_write_node
372
+
373
+ # @@foo ||= bar
374
+ # ^^^^^^^^^^^^^
375
+ alias visit_class_variable_or_write_node visit_class_variable_operator_write_node
376
+
377
+ # @@foo, = bar
378
+ # ^^^^^
379
+ def visit_class_variable_target_node(node)
380
+ builder.assignable(builder.cvar(token(node.location)))
381
+ end
382
+
383
+ # Foo
384
+ # ^^^
385
+ def visit_constant_read_node(node)
386
+ builder.const([node.name, srange(node.location)])
387
+ end
388
+
389
+ # Foo = 1
390
+ # ^^^^^^^
391
+ #
392
+ # Foo, Bar = 1
393
+ # ^^^ ^^^
394
+ def visit_constant_write_node(node)
395
+ builder.assign(builder.assignable(builder.const([node.name, srange(node.name_loc)])), token(node.operator_loc), visit(node.value))
396
+ end
397
+
398
+ # Foo += bar
399
+ # ^^^^^^^^^^^
400
+ def visit_constant_operator_write_node(node)
401
+ builder.op_assign(
402
+ builder.assignable(builder.const([node.name, srange(node.name_loc)])),
403
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
404
+ visit(node.value)
405
+ )
406
+ end
407
+
408
+ # Foo &&= bar
409
+ # ^^^^^^^^^^^^
410
+ alias visit_constant_and_write_node visit_constant_operator_write_node
411
+
412
+ # Foo ||= bar
413
+ # ^^^^^^^^^^^^
414
+ alias visit_constant_or_write_node visit_constant_operator_write_node
415
+
416
+ # Foo, = bar
417
+ # ^^^
418
+ def visit_constant_target_node(node)
419
+ builder.assignable(builder.const([node.name, srange(node.location)]))
420
+ end
421
+
422
+ # Foo::Bar
423
+ # ^^^^^^^^
424
+ def visit_constant_path_node(node)
425
+ if node.parent.nil?
426
+ builder.const_global(
427
+ token(node.delimiter_loc),
428
+ [node.child.name, srange(node.child.location)]
429
+ )
430
+ else
431
+ builder.const_fetch(
432
+ visit(node.parent),
433
+ token(node.delimiter_loc),
434
+ [node.child.name, srange(node.child.location)]
435
+ )
436
+ end
437
+ end
438
+
439
+ # Foo::Bar = 1
440
+ # ^^^^^^^^^^^^
441
+ #
442
+ # Foo::Foo, Bar::Bar = 1
443
+ # ^^^^^^^^ ^^^^^^^^
444
+ def visit_constant_path_write_node(node)
445
+ builder.assign(
446
+ builder.assignable(visit(node.target)),
447
+ token(node.operator_loc),
448
+ visit(node.value)
449
+ )
450
+ end
451
+
452
+ # Foo::Bar += baz
453
+ # ^^^^^^^^^^^^^^^
454
+ def visit_constant_path_operator_write_node(node)
455
+ builder.op_assign(
456
+ builder.assignable(visit(node.target)),
457
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
458
+ visit(node.value)
459
+ )
460
+ end
461
+
462
+ # Foo::Bar &&= baz
463
+ # ^^^^^^^^^^^^^^^^
464
+ alias visit_constant_path_and_write_node visit_constant_path_operator_write_node
465
+
466
+ # Foo::Bar ||= baz
467
+ # ^^^^^^^^^^^^^^^^
468
+ alias visit_constant_path_or_write_node visit_constant_path_operator_write_node
469
+
470
+ # Foo::Bar, = baz
471
+ # ^^^^^^^^
472
+ def visit_constant_path_target_node(node)
473
+ builder.assignable(visit_constant_path_node(node))
474
+ end
475
+
476
+ # def foo; end
477
+ # ^^^^^^^^^^^^
478
+ #
479
+ # def self.foo; end
480
+ # ^^^^^^^^^^^^^^^^^
481
+ def visit_def_node(node)
482
+ if node.equal_loc
483
+ if node.receiver
484
+ builder.def_endless_singleton(
485
+ token(node.def_keyword_loc),
486
+ visit(node.receiver.is_a?(ParenthesesNode) ? node.receiver.body : node.receiver),
487
+ token(node.operator_loc),
488
+ token(node.name_loc),
489
+ builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
490
+ token(node.equal_loc),
491
+ node.body&.accept(copy_compiler(locals: node.locals))
492
+ )
493
+ else
494
+ builder.def_endless_method(
495
+ token(node.def_keyword_loc),
496
+ token(node.name_loc),
497
+ builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
498
+ token(node.equal_loc),
499
+ node.body&.accept(copy_compiler(locals: node.locals))
500
+ )
501
+ end
502
+ elsif node.receiver
503
+ builder.def_singleton(
504
+ token(node.def_keyword_loc),
505
+ visit(node.receiver.is_a?(ParenthesesNode) ? node.receiver.body : node.receiver),
506
+ token(node.operator_loc),
507
+ token(node.name_loc),
508
+ builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
509
+ node.body&.accept(copy_compiler(locals: node.locals)),
510
+ token(node.end_keyword_loc)
511
+ )
512
+ else
513
+ builder.def_method(
514
+ token(node.def_keyword_loc),
515
+ token(node.name_loc),
516
+ builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
517
+ node.body&.accept(copy_compiler(locals: node.locals)),
518
+ token(node.end_keyword_loc)
519
+ )
520
+ end
521
+ end
522
+
523
+ # defined? a
524
+ # ^^^^^^^^^^
525
+ #
526
+ # defined?(a)
527
+ # ^^^^^^^^^^^
528
+ def visit_defined_node(node)
529
+ builder.keyword_cmd(
530
+ :defined?,
531
+ token(node.keyword_loc),
532
+ token(node.lparen_loc),
533
+ [visit(node.value)],
534
+ token(node.rparen_loc)
535
+ )
536
+ end
537
+
538
+ # if foo then bar else baz end
539
+ # ^^^^^^^^^^^^
540
+ def visit_else_node(node)
541
+ visit(node.statements)
542
+ end
543
+
544
+ # "foo #{bar}"
545
+ # ^^^^^^
546
+ def visit_embedded_statements_node(node)
547
+ builder.begin(
548
+ token(node.opening_loc),
549
+ visit(node.statements),
550
+ token(node.closing_loc)
551
+ )
552
+ end
553
+
554
+ # "foo #@bar"
555
+ # ^^^^^
556
+ def visit_embedded_variable_node(node)
557
+ visit(node.variable)
558
+ end
559
+
560
+ # begin; foo; ensure; bar; end
561
+ # ^^^^^^^^^^^^
562
+ def visit_ensure_node(node)
563
+ raise "Cannot directly compile ensure nodes"
564
+ end
565
+
566
+ # false
567
+ # ^^^^^
568
+ def visit_false_node(node)
569
+ builder.false(token(node.location))
570
+ end
571
+
572
+ # foo => [*, bar, *]
573
+ # ^^^^^^^^^^^
574
+ def visit_find_pattern_node(node)
575
+ if node.constant
576
+ builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.find_pattern(nil, visit_all([node.left, *node.requireds, node.right]), nil), token(node.closing_loc))
577
+ else
578
+ builder.find_pattern(token(node.opening_loc), visit_all([node.left, *node.requireds, node.right]), token(node.closing_loc))
579
+ end
580
+ end
581
+
582
+ # 1.0
583
+ # ^^^
584
+ def visit_float_node(node)
585
+ visit_numeric(node, builder.float([node.value, srange(node.location)]))
586
+ end
587
+
588
+ # for foo in bar do end
589
+ # ^^^^^^^^^^^^^^^^^^^^^
590
+ def visit_for_node(node)
591
+ builder.for(
592
+ token(node.for_keyword_loc),
593
+ visit(node.index),
594
+ token(node.in_keyword_loc),
595
+ visit(node.collection),
596
+ if node.do_keyword_loc
597
+ token(node.do_keyword_loc)
598
+ else
599
+ srange_find(node.collection.location.end_offset, (node.statements&.location || node.end_keyword_loc).start_offset, [";"])
600
+ end,
601
+ visit(node.statements),
602
+ token(node.end_keyword_loc)
603
+ )
604
+ end
605
+
606
+ # def foo(...); bar(...); end
607
+ # ^^^
608
+ def visit_forwarding_arguments_node(node)
609
+ builder.forwarded_args(token(node.location))
610
+ end
611
+
612
+ # def foo(...); end
613
+ # ^^^
614
+ def visit_forwarding_parameter_node(node)
615
+ builder.forward_arg(token(node.location))
616
+ end
617
+
618
+ # super
619
+ # ^^^^^
620
+ #
621
+ # super {}
622
+ # ^^^^^^^^
623
+ def visit_forwarding_super_node(node)
624
+ visit_block(
625
+ builder.keyword_cmd(
626
+ :zsuper,
627
+ ["super", srange_offsets(node.location.start_offset, node.location.start_offset + 5)]
628
+ ),
629
+ node.block
630
+ )
631
+ end
632
+
633
+ # $foo
634
+ # ^^^^
635
+ def visit_global_variable_read_node(node)
636
+ builder.gvar(token(node.location))
637
+ end
638
+
639
+ # $foo = 1
640
+ # ^^^^^^^^
641
+ #
642
+ # $foo, $bar = 1
643
+ # ^^^^ ^^^^
644
+ def visit_global_variable_write_node(node)
645
+ builder.assign(
646
+ builder.assignable(builder.gvar(token(node.name_loc))),
647
+ token(node.operator_loc),
648
+ visit(node.value)
649
+ )
650
+ end
651
+
652
+ # $foo += bar
653
+ # ^^^^^^^^^^^
654
+ def visit_global_variable_operator_write_node(node)
655
+ builder.op_assign(
656
+ builder.assignable(builder.gvar(token(node.name_loc))),
657
+ [node.operator, srange(node.operator_loc)],
658
+ visit(node.value)
659
+ )
660
+ end
661
+
662
+ # $foo &&= bar
663
+ # ^^^^^^^^^^^^
664
+ alias visit_global_variable_and_write_node visit_global_variable_operator_write_node
665
+
666
+ # $foo ||= bar
667
+ # ^^^^^^^^^^^^
668
+ alias visit_global_variable_or_write_node visit_global_variable_operator_write_node
669
+
670
+ # $foo, = bar
671
+ # ^^^^
672
+ def visit_global_variable_target_node(node)
673
+ builder.assignable(builder.gvar([node.slice, srange(node.location)]))
674
+ end
675
+
676
+ # {}
677
+ # ^^
678
+ def visit_hash_node(node)
679
+ builder.associate(
680
+ token(node.opening_loc),
681
+ visit_all(node.elements),
682
+ token(node.closing_loc)
683
+ )
684
+ end
685
+
686
+ # foo => {}
687
+ # ^^
688
+ def visit_hash_pattern_node(node)
689
+ if node.constant
690
+ builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.hash_pattern(nil, visit_all(node.elements), nil), token(node.closing_loc))
691
+ else
692
+ builder.hash_pattern(token(node.opening_loc), visit_all(node.elements), token(node.closing_loc))
693
+ end
694
+ end
695
+
696
+ # if foo then bar end
697
+ # ^^^^^^^^^^^^^^^^^^^
698
+ #
699
+ # bar if foo
700
+ # ^^^^^^^^^^
701
+ #
702
+ # foo ? bar : baz
703
+ # ^^^^^^^^^^^^^^^
704
+ def visit_if_node(node)
705
+ if !node.if_keyword_loc
706
+ builder.ternary(
707
+ visit(node.predicate),
708
+ token(node.then_keyword_loc),
709
+ visit(node.statements),
710
+ token(node.consequent.else_keyword_loc),
711
+ visit(node.consequent)
712
+ )
713
+ elsif node.if_keyword_loc.start_offset == node.location.start_offset
714
+ builder.condition(
715
+ token(node.if_keyword_loc),
716
+ visit(node.predicate),
717
+ if node.then_keyword_loc
718
+ token(node.then_keyword_loc)
719
+ else
720
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.consequent&.location || node.end_keyword_loc).start_offset, [";"])
721
+ end,
722
+ visit(node.statements),
723
+ case node.consequent
724
+ when IfNode
725
+ token(node.consequent.if_keyword_loc)
726
+ when ElseNode
727
+ token(node.consequent.else_keyword_loc)
728
+ end,
729
+ visit(node.consequent),
730
+ if node.if_keyword != "elsif"
731
+ token(node.end_keyword_loc)
732
+ end
733
+ )
734
+ else
735
+ builder.condition_mod(
736
+ visit(node.statements),
737
+ visit(node.consequent),
738
+ token(node.if_keyword_loc),
739
+ visit(node.predicate)
740
+ )
741
+ end
742
+ end
743
+
744
+ # 1i
745
+ def visit_imaginary_node(node)
746
+ visit_numeric(node, builder.complex([node.value, srange(node.location)]))
747
+ end
748
+
749
+ # { foo: }
750
+ # ^^^^
751
+ def visit_implicit_node(node)
752
+ raise "Cannot directly compile implicit nodes"
753
+ end
754
+
755
+ # foo { |bar,| }
756
+ # ^
757
+ def visit_implicit_rest_node(node)
758
+ raise "Cannot directly compile implicit rest nodes"
759
+ end
760
+
761
+ # case foo; in bar; end
762
+ # ^^^^^^^^^^^^^^^^^^^^^
763
+ def visit_in_node(node)
764
+ pattern = nil
765
+ guard = nil
766
+
767
+ case node.pattern
768
+ when IfNode
769
+ pattern = within_pattern { |compiler| node.pattern.statements.accept(compiler) }
770
+ guard = builder.if_guard(token(node.pattern.if_keyword_loc), visit(node.pattern.predicate))
771
+ when UnlessNode
772
+ pattern = within_pattern { |compiler| node.pattern.statements.accept(compiler) }
773
+ guard = builder.unless_guard(token(node.pattern.keyword_loc), visit(node.pattern.predicate))
774
+ else
775
+ pattern = within_pattern { |compiler| node.pattern.accept(compiler) }
776
+ end
777
+
778
+ builder.in_pattern(
779
+ token(node.in_loc),
780
+ pattern,
781
+ guard,
782
+ srange_find(node.pattern.location.end_offset, node.statements&.location&.start_offset || node.location.end_offset, [";", "then"]),
783
+ visit(node.statements)
784
+ )
785
+ end
786
+
787
+ # foo[bar] += baz
788
+ # ^^^^^^^^^^^^^^^
789
+ def visit_index_operator_write_node(node)
790
+ arguments = node.arguments&.arguments || []
791
+ arguments << node.block if node.block
792
+
793
+ builder.op_assign(
794
+ builder.index(
795
+ visit(node.receiver),
796
+ token(node.opening_loc),
797
+ visit_all(arguments),
798
+ token(node.closing_loc)
799
+ ),
800
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
801
+ visit(node.value)
802
+ )
803
+ end
804
+
805
+ # foo[bar] &&= baz
806
+ # ^^^^^^^^^^^^^^^^
807
+ alias visit_index_and_write_node visit_index_operator_write_node
808
+
809
+ # foo[bar] ||= baz
810
+ # ^^^^^^^^^^^^^^^^
811
+ alias visit_index_or_write_node visit_index_operator_write_node
812
+
813
+ # foo[bar], = 1
814
+ # ^^^^^^^^
815
+ def visit_index_target_node(node)
816
+ builder.index_asgn(
817
+ visit(node.receiver),
818
+ token(node.opening_loc),
819
+ visit_all(node.arguments.arguments),
820
+ token(node.closing_loc),
821
+ )
822
+ end
823
+
824
+ # @foo
825
+ # ^^^^
826
+ def visit_instance_variable_read_node(node)
827
+ builder.ivar(token(node.location))
828
+ end
829
+
830
+ # @foo = 1
831
+ # ^^^^^^^^
832
+ #
833
+ # @foo, @bar = 1
834
+ # ^^^^ ^^^^
835
+ def visit_instance_variable_write_node(node)
836
+ builder.assign(
837
+ builder.assignable(builder.ivar(token(node.name_loc))),
838
+ token(node.operator_loc),
839
+ visit(node.value)
840
+ )
841
+ end
842
+
843
+ # @foo += bar
844
+ # ^^^^^^^^^^^
845
+ def visit_instance_variable_operator_write_node(node)
846
+ builder.op_assign(
847
+ builder.assignable(builder.ivar(token(node.name_loc))),
848
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
849
+ visit(node.value)
850
+ )
851
+ end
852
+
853
+ # @foo &&= bar
854
+ # ^^^^^^^^^^^^
855
+ alias visit_instance_variable_and_write_node visit_instance_variable_operator_write_node
856
+
857
+ # @foo ||= bar
858
+ # ^^^^^^^^^^^^
859
+ alias visit_instance_variable_or_write_node visit_instance_variable_operator_write_node
860
+
861
+ # @foo, = bar
862
+ # ^^^^
863
+ def visit_instance_variable_target_node(node)
864
+ builder.assignable(builder.ivar(token(node.location)))
865
+ end
866
+
867
+ # 1
868
+ # ^
869
+ def visit_integer_node(node)
870
+ visit_numeric(node, builder.integer([node.value, srange(node.location)]))
871
+ end
872
+
873
+ # /foo #{bar}/
874
+ # ^^^^^^^^^^^^
875
+ def visit_interpolated_regular_expression_node(node)
876
+ builder.regexp_compose(
877
+ token(node.opening_loc),
878
+ visit_all(node.parts),
879
+ [node.closing[0], srange_offsets(node.closing_loc.start_offset, node.closing_loc.start_offset + 1)],
880
+ builder.regexp_options([node.closing[1..], srange_offsets(node.closing_loc.start_offset + 1, node.closing_loc.end_offset)])
881
+ )
882
+ end
883
+
884
+ # if /foo #{bar}/ then end
885
+ # ^^^^^^^^^^^^
886
+ alias visit_interpolated_match_last_line_node visit_interpolated_regular_expression_node
887
+
888
+ # "foo #{bar}"
889
+ # ^^^^^^^^^^^^
890
+ def visit_interpolated_string_node(node)
891
+ if node.opening&.start_with?("<<")
892
+ children, closing = visit_heredoc(node)
893
+ builder.string_compose(token(node.opening_loc), children, closing)
894
+ else
895
+ builder.string_compose(
896
+ token(node.opening_loc),
897
+ visit_all(node.parts),
898
+ token(node.closing_loc)
899
+ )
900
+ end
901
+ end
902
+
903
+ # :"foo #{bar}"
904
+ # ^^^^^^^^^^^^^
905
+ def visit_interpolated_symbol_node(node)
906
+ builder.symbol_compose(
907
+ token(node.opening_loc),
908
+ visit_all(node.parts),
909
+ token(node.closing_loc)
910
+ )
911
+ end
912
+
913
+ # `foo #{bar}`
914
+ # ^^^^^^^^^^^^
915
+ def visit_interpolated_x_string_node(node)
916
+ if node.opening.start_with?("<<")
917
+ children, closing = visit_heredoc(node)
918
+ builder.xstring_compose(token(node.opening_loc), children, closing)
919
+ else
920
+ builder.xstring_compose(
921
+ token(node.opening_loc),
922
+ visit_all(node.parts),
923
+ token(node.closing_loc)
924
+ )
925
+ end
926
+ end
927
+
928
+ # foo(bar: baz)
929
+ # ^^^^^^^^
930
+ def visit_keyword_hash_node(node)
931
+ builder.associate(nil, visit_all(node.elements), nil)
932
+ end
933
+
934
+ # def foo(**bar); end
935
+ # ^^^^^
936
+ #
937
+ # def foo(**); end
938
+ # ^^
939
+ def visit_keyword_rest_parameter_node(node)
940
+ builder.kwrestarg(
941
+ token(node.operator_loc),
942
+ node.name ? [node.name, srange(node.name_loc)] : nil
943
+ )
944
+ end
945
+
946
+ # -> {}
947
+ def visit_lambda_node(node)
948
+ builder.block(
949
+ builder.call_lambda(token(node.operator_loc)),
950
+ [node.opening, srange(node.opening_loc)],
951
+ if node.parameters
952
+ if node.parameters.is_a?(NumberedParametersNode)
953
+ visit(node.parameters)
954
+ else
955
+ builder.args(
956
+ token(node.parameters.opening_loc),
957
+ visit(node.parameters),
958
+ token(node.parameters.closing_loc),
959
+ false
960
+ )
961
+ end
962
+ else
963
+ builder.args(nil, [], nil, false)
964
+ end,
965
+ node.body&.accept(copy_compiler(locals: node.locals)),
966
+ [node.closing, srange(node.closing_loc)]
967
+ )
968
+ end
969
+
970
+ # foo
971
+ # ^^^
972
+ def visit_local_variable_read_node(node)
973
+ builder.ident([node.name, srange(node.location)]).updated(:lvar)
974
+ end
975
+
976
+ # foo = 1
977
+ # ^^^^^^^
978
+ #
979
+ # foo, bar = 1
980
+ # ^^^ ^^^
981
+ def visit_local_variable_write_node(node)
982
+ builder.assign(
983
+ builder.assignable(builder.ident(token(node.name_loc))),
984
+ token(node.operator_loc),
985
+ visit(node.value)
986
+ )
987
+ end
988
+
989
+ # foo += bar
990
+ # ^^^^^^^^^^
991
+ def visit_local_variable_operator_write_node(node)
992
+ builder.op_assign(
993
+ builder.assignable(builder.ident(token(node.name_loc))),
994
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
995
+ visit(node.value)
996
+ )
997
+ end
998
+
999
+ # foo &&= bar
1000
+ # ^^^^^^^^^^^
1001
+ alias visit_local_variable_and_write_node visit_local_variable_operator_write_node
1002
+
1003
+ # foo ||= bar
1004
+ # ^^^^^^^^^^^
1005
+ alias visit_local_variable_or_write_node visit_local_variable_operator_write_node
1006
+
1007
+ # foo, = bar
1008
+ # ^^^
1009
+ def visit_local_variable_target_node(node)
1010
+ if in_pattern
1011
+ builder.assignable(builder.match_var([node.name, srange(node.location)]))
1012
+ else
1013
+ builder.assignable(builder.ident(token(node.location)))
1014
+ end
1015
+ end
1016
+
1017
+ # foo in bar
1018
+ # ^^^^^^^^^^
1019
+ def visit_match_predicate_node(node)
1020
+ builder.match_pattern_p(
1021
+ visit(node.value),
1022
+ token(node.operator_loc),
1023
+ within_pattern { |compiler| node.pattern.accept(compiler) }
1024
+ )
1025
+ end
1026
+
1027
+ # foo => bar
1028
+ # ^^^^^^^^^^
1029
+ def visit_match_required_node(node)
1030
+ builder.match_pattern(
1031
+ visit(node.value),
1032
+ token(node.operator_loc),
1033
+ within_pattern { |compiler| node.pattern.accept(compiler) }
1034
+ )
1035
+ end
1036
+
1037
+ # /(?<foo>foo)/ =~ bar
1038
+ # ^^^^^^^^^^^^^^^^^^^^
1039
+ def visit_match_write_node(node)
1040
+ builder.match_op(
1041
+ visit(node.call.receiver),
1042
+ token(node.call.message_loc),
1043
+ visit(node.call.arguments.arguments.first)
1044
+ )
1045
+ end
1046
+
1047
+ # A node that is missing from the syntax tree. This is only used in the
1048
+ # case of a syntax error. The parser gem doesn't have such a concept, so
1049
+ # we invent our own here.
1050
+ def visit_missing_node(node)
1051
+ raise "Cannot compile missing nodes"
1052
+ end
1053
+
1054
+ # module Foo; end
1055
+ # ^^^^^^^^^^^^^^^
1056
+ def visit_module_node(node)
1057
+ builder.def_module(
1058
+ token(node.module_keyword_loc),
1059
+ visit(node.constant_path),
1060
+ node.body&.accept(copy_compiler(locals: node.locals)),
1061
+ token(node.end_keyword_loc)
1062
+ )
1063
+ end
1064
+
1065
+ # foo, bar = baz
1066
+ # ^^^^^^^^
1067
+ def visit_multi_target_node(node)
1068
+ node = node.copy(rest: nil) if node.rest.is_a?(ImplicitRestNode)
1069
+
1070
+ builder.multi_lhs(
1071
+ token(node.lparen_loc),
1072
+ visit_all([*node.lefts, *node.rest, *node.rights]),
1073
+ token(node.rparen_loc)
1074
+ )
1075
+ end
1076
+
1077
+ # foo, bar = baz
1078
+ # ^^^^^^^^^^^^^^
1079
+ def visit_multi_write_node(node)
1080
+ builder.multi_assign(
1081
+ builder.multi_lhs(
1082
+ token(node.lparen_loc),
1083
+ visit_all([*node.lefts, *node.rest, *node.rights]),
1084
+ token(node.rparen_loc)
1085
+ ),
1086
+ token(node.operator_loc),
1087
+ visit(node.value)
1088
+ )
1089
+ end
1090
+
1091
+ # next
1092
+ # ^^^^
1093
+ #
1094
+ # next foo
1095
+ # ^^^^^^^^
1096
+ def visit_next_node(node)
1097
+ builder.keyword_cmd(
1098
+ :next,
1099
+ token(node.keyword_loc),
1100
+ nil,
1101
+ visit(node.arguments) || [],
1102
+ nil
1103
+ )
1104
+ end
1105
+
1106
+ # nil
1107
+ # ^^^
1108
+ def visit_nil_node(node)
1109
+ builder.nil(token(node.location))
1110
+ end
1111
+
1112
+ # def foo(**nil); end
1113
+ # ^^^^^
1114
+ def visit_no_keywords_parameter_node(node)
1115
+ builder.kwnilarg(token(node.operator_loc), token(node.keyword_loc))
1116
+ end
1117
+
1118
+ # -> { _1 + _2 }
1119
+ # ^^^^^^^^^^^^^^
1120
+ def visit_numbered_parameters_node(node)
1121
+ builder.numargs(node.maximum)
1122
+ end
1123
+
1124
+ # $1
1125
+ # ^^
1126
+ def visit_numbered_reference_read_node(node)
1127
+ builder.nth_ref([node.number, srange(node.location)])
1128
+ end
1129
+
1130
+ # def foo(bar: baz); end
1131
+ # ^^^^^^^^
1132
+ def visit_optional_keyword_parameter_node(node)
1133
+ builder.kwoptarg([node.name, srange(node.name_loc)], visit(node.value))
1134
+ end
1135
+
1136
+ # def foo(bar = 1); end
1137
+ # ^^^^^^^
1138
+ def visit_optional_parameter_node(node)
1139
+ builder.optarg(token(node.name_loc), token(node.operator_loc), visit(node.value))
1140
+ end
1141
+
1142
+ # a or b
1143
+ # ^^^^^^
1144
+ def visit_or_node(node)
1145
+ builder.logical_op(:or, visit(node.left), token(node.operator_loc), visit(node.right))
1146
+ end
1147
+
1148
+ # def foo(bar, *baz); end
1149
+ # ^^^^^^^^^
1150
+ def visit_parameters_node(node)
1151
+ params = []
1152
+
1153
+ if node.requireds.any?
1154
+ node.requireds.each do |required|
1155
+ if required.is_a?(RequiredParameterNode)
1156
+ params << visit(required)
1157
+ else
1158
+ compiler = copy_compiler(in_destructure: true)
1159
+ params << required.accept(compiler)
1160
+ end
1161
+ end
1162
+ end
1163
+
1164
+ params.concat(visit_all(node.optionals)) if node.optionals.any?
1165
+ params << visit(node.rest) if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
1166
+
1167
+ if node.posts.any?
1168
+ node.posts.each do |post|
1169
+ if post.is_a?(RequiredParameterNode)
1170
+ params << visit(post)
1171
+ else
1172
+ compiler = copy_compiler(in_destructure: true)
1173
+ params << post.accept(compiler)
1174
+ end
1175
+ end
1176
+ end
1177
+
1178
+ params.concat(visit_all(node.keywords)) if node.keywords.any?
1179
+ params << visit(node.keyword_rest) if !node.keyword_rest.nil?
1180
+ params << visit(node.block) if !node.block.nil?
1181
+ params
1182
+ end
1183
+
1184
+ # ()
1185
+ # ^^
1186
+ #
1187
+ # (1)
1188
+ # ^^^
1189
+ def visit_parentheses_node(node)
1190
+ builder.begin(
1191
+ token(node.opening_loc),
1192
+ visit(node.body),
1193
+ token(node.closing_loc)
1194
+ )
1195
+ end
1196
+
1197
+ # foo => ^(bar)
1198
+ # ^^^^^^
1199
+ def visit_pinned_expression_node(node)
1200
+ builder.pin(token(node.operator_loc), visit(node.expression))
1201
+ end
1202
+
1203
+ # foo = 1 and bar => ^foo
1204
+ # ^^^^
1205
+ def visit_pinned_variable_node(node)
1206
+ builder.pin(token(node.operator_loc), visit(node.variable))
1207
+ end
1208
+
1209
+ # END {}
1210
+ def visit_post_execution_node(node)
1211
+ builder.postexe(
1212
+ token(node.keyword_loc),
1213
+ token(node.opening_loc),
1214
+ visit(node.statements),
1215
+ token(node.closing_loc)
1216
+ )
1217
+ end
1218
+
1219
+ # BEGIN {}
1220
+ def visit_pre_execution_node(node)
1221
+ builder.preexe(
1222
+ token(node.keyword_loc),
1223
+ token(node.opening_loc),
1224
+ visit(node.statements),
1225
+ token(node.closing_loc)
1226
+ )
1227
+ end
1228
+
1229
+ # The top-level program node.
1230
+ def visit_program_node(node)
1231
+ node.statements.accept(copy_compiler(locals: node.locals))
1232
+ end
1233
+
1234
+ # 0..5
1235
+ # ^^^^
1236
+ def visit_range_node(node)
1237
+ if node.exclude_end?
1238
+ builder.range_exclusive(
1239
+ visit(node.left),
1240
+ token(node.operator_loc),
1241
+ visit(node.right)
1242
+ )
1243
+ else
1244
+ builder.range_inclusive(
1245
+ visit(node.left),
1246
+ token(node.operator_loc),
1247
+ visit(node.right)
1248
+ )
1249
+ end
1250
+ end
1251
+
1252
+ # if foo .. bar; end
1253
+ # ^^^^^^^^^^
1254
+ alias visit_flip_flop_node visit_range_node
1255
+
1256
+ # 1r
1257
+ # ^^
1258
+ def visit_rational_node(node)
1259
+ visit_numeric(node, builder.rational([node.value, srange(node.location)]))
1260
+ end
1261
+
1262
+ # redo
1263
+ # ^^^^
1264
+ def visit_redo_node(node)
1265
+ builder.keyword_cmd(:redo, token(node.location))
1266
+ end
1267
+
1268
+ # /foo/
1269
+ # ^^^^^
1270
+ def visit_regular_expression_node(node)
1271
+ builder.regexp_compose(
1272
+ token(node.opening_loc),
1273
+ [builder.string_internal(token(node.content_loc))],
1274
+ [node.closing[0], srange_offsets(node.closing_loc.start_offset, node.closing_loc.start_offset + 1)],
1275
+ builder.regexp_options([node.closing[1..], srange_offsets(node.closing_loc.start_offset + 1, node.closing_loc.end_offset)])
1276
+ )
1277
+ end
1278
+
1279
+ # if /foo/ then end
1280
+ # ^^^^^
1281
+ alias visit_match_last_line_node visit_regular_expression_node
1282
+
1283
+ # def foo(bar:); end
1284
+ # ^^^^
1285
+ def visit_required_keyword_parameter_node(node)
1286
+ builder.kwarg([node.name, srange(node.name_loc)])
1287
+ end
1288
+
1289
+ # def foo(bar); end
1290
+ # ^^^
1291
+ def visit_required_parameter_node(node)
1292
+ builder.arg(token(node.location))
1293
+ end
1294
+
1295
+ # foo rescue bar
1296
+ # ^^^^^^^^^^^^^^
1297
+ def visit_rescue_modifier_node(node)
1298
+ builder.begin_body(
1299
+ visit(node.expression),
1300
+ [
1301
+ builder.rescue_body(
1302
+ token(node.keyword_loc),
1303
+ nil,
1304
+ nil,
1305
+ nil,
1306
+ nil,
1307
+ visit(node.rescue_expression)
1308
+ )
1309
+ ]
1310
+ )
1311
+ end
1312
+
1313
+ # begin; rescue; end
1314
+ # ^^^^^^^
1315
+ def visit_rescue_node(node)
1316
+ raise "Cannot directly compile rescue nodes"
1317
+ end
1318
+
1319
+ # def foo(*bar); end
1320
+ # ^^^^
1321
+ #
1322
+ # def foo(*); end
1323
+ # ^
1324
+ def visit_rest_parameter_node(node)
1325
+ builder.restarg(token(node.operator_loc), token(node.name_loc))
1326
+ end
1327
+
1328
+ # retry
1329
+ # ^^^^^
1330
+ def visit_retry_node(node)
1331
+ builder.keyword_cmd(:retry, token(node.location))
1332
+ end
1333
+
1334
+ # return
1335
+ # ^^^^^^
1336
+ #
1337
+ # return 1
1338
+ # ^^^^^^^^
1339
+ def visit_return_node(node)
1340
+ builder.keyword_cmd(
1341
+ :return,
1342
+ token(node.keyword_loc),
1343
+ nil,
1344
+ visit(node.arguments) || [],
1345
+ nil
1346
+ )
1347
+ end
1348
+
1349
+ # self
1350
+ # ^^^^
1351
+ def visit_self_node(node)
1352
+ builder.self(token(node.location))
1353
+ end
1354
+
1355
+ # class << self; end
1356
+ # ^^^^^^^^^^^^^^^^^^
1357
+ def visit_singleton_class_node(node)
1358
+ builder.def_sclass(
1359
+ token(node.class_keyword_loc),
1360
+ token(node.operator_loc),
1361
+ visit(node.expression),
1362
+ node.body.accept(copy_compiler(locals: node.locals)),
1363
+ token(node.end_keyword_loc)
1364
+ )
1365
+ end
1366
+
1367
+ # __ENCODING__
1368
+ # ^^^^^^^^^^^^
1369
+ def visit_source_encoding_node(node)
1370
+ builder.accessible(builder.__ENCODING__(token(node.location)))
1371
+ end
1372
+
1373
+ # __FILE__
1374
+ # ^^^^^^^^
1375
+ def visit_source_file_node(node)
1376
+ builder.accessible(builder.__FILE__(token(node.location)))
1377
+ end
1378
+
1379
+ # __LINE__
1380
+ # ^^^^^^^^
1381
+ def visit_source_line_node(node)
1382
+ builder.accessible(builder.__LINE__(token(node.location)))
1383
+ end
1384
+
1385
+ # foo(*bar)
1386
+ # ^^^^
1387
+ #
1388
+ # def foo((bar, *baz)); end
1389
+ # ^^^^
1390
+ #
1391
+ # def foo(*); bar(*); end
1392
+ # ^
1393
+ def visit_splat_node(node)
1394
+ if node.expression.nil? && locals.include?(:*)
1395
+ builder.forwarded_restarg(token(node.operator_loc))
1396
+ elsif in_destructure
1397
+ builder.restarg(token(node.operator_loc), token(node.expression&.location))
1398
+ elsif in_pattern
1399
+ builder.match_rest(token(node.operator_loc), token(node.expression&.location))
1400
+ else
1401
+ builder.splat(token(node.operator_loc), visit(node.expression))
1402
+ end
1403
+ end
1404
+
1405
+ # A list of statements.
1406
+ def visit_statements_node(node)
1407
+ builder.compstmt(visit_all(node.body))
1408
+ end
1409
+
1410
+ # "foo"
1411
+ # ^^^^^
1412
+ def visit_string_node(node)
1413
+ if node.opening&.start_with?("<<")
1414
+ children, closing = visit_heredoc(InterpolatedStringNode.new(node.opening_loc, [node.copy(opening_loc: nil, closing_loc: nil, location: node.content_loc)], node.closing_loc, node.location))
1415
+ builder.string_compose(token(node.opening_loc), children, closing)
1416
+ elsif node.opening == "?"
1417
+ builder.character([node.unescaped, srange(node.location)])
1418
+ else
1419
+ builder.string_compose(
1420
+ token(node.opening_loc),
1421
+ [builder.string_internal([node.unescaped, srange(node.content_loc)])],
1422
+ token(node.closing_loc)
1423
+ )
1424
+ end
1425
+ end
1426
+
1427
+ # super(foo)
1428
+ # ^^^^^^^^^^
1429
+ def visit_super_node(node)
1430
+ arguments = node.arguments&.arguments || []
1431
+ block = node.block
1432
+
1433
+ if block.is_a?(BlockArgumentNode)
1434
+ arguments = [*arguments, block]
1435
+ block = nil
1436
+ end
1437
+
1438
+ visit_block(
1439
+ builder.keyword_cmd(
1440
+ :super,
1441
+ token(node.keyword_loc),
1442
+ token(node.lparen_loc),
1443
+ visit_all(arguments),
1444
+ token(node.rparen_loc)
1445
+ ),
1446
+ block
1447
+ )
1448
+ end
1449
+
1450
+ # :foo
1451
+ # ^^^^
1452
+ def visit_symbol_node(node)
1453
+ if node.closing_loc.nil?
1454
+ if node.opening_loc.nil?
1455
+ builder.symbol_internal([node.unescaped, srange(node.location)])
1456
+ else
1457
+ builder.symbol([node.unescaped, srange(node.location)])
1458
+ end
1459
+ else
1460
+ builder.symbol_compose(
1461
+ token(node.opening_loc),
1462
+ [builder.string_internal([node.unescaped, srange(node.value_loc)])],
1463
+ token(node.closing_loc)
1464
+ )
1465
+ end
1466
+ end
1467
+
1468
+ # true
1469
+ # ^^^^
1470
+ def visit_true_node(node)
1471
+ builder.true(token(node.location))
1472
+ end
1473
+
1474
+ # undef foo
1475
+ # ^^^^^^^^^
1476
+ def visit_undef_node(node)
1477
+ builder.undef_method(token(node.keyword_loc), visit_all(node.names))
1478
+ end
1479
+
1480
+ # unless foo; bar end
1481
+ # ^^^^^^^^^^^^^^^^^^^
1482
+ #
1483
+ # bar unless foo
1484
+ # ^^^^^^^^^^^^^^
1485
+ def visit_unless_node(node)
1486
+ if node.keyword_loc.start_offset == node.location.start_offset
1487
+ builder.condition(
1488
+ token(node.keyword_loc),
1489
+ visit(node.predicate),
1490
+ if node.then_keyword_loc
1491
+ token(node.then_keyword_loc)
1492
+ else
1493
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.consequent&.location || node.end_keyword_loc).start_offset, [";"])
1494
+ end,
1495
+ visit(node.consequent),
1496
+ token(node.consequent&.else_keyword_loc),
1497
+ visit(node.statements),
1498
+ token(node.end_keyword_loc)
1499
+ )
1500
+ else
1501
+ builder.condition_mod(
1502
+ visit(node.consequent),
1503
+ visit(node.statements),
1504
+ token(node.keyword_loc),
1505
+ visit(node.predicate)
1506
+ )
1507
+ end
1508
+ end
1509
+
1510
+ # until foo; bar end
1511
+ # ^^^^^^^^^^^^^^^^^
1512
+ #
1513
+ # bar until foo
1514
+ # ^^^^^^^^^^^^^
1515
+ def visit_until_node(node)
1516
+ if node.location.start_offset == node.keyword_loc.start_offset
1517
+ builder.loop(
1518
+ :until,
1519
+ token(node.keyword_loc),
1520
+ visit(node.predicate),
1521
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, [";", "do"]),
1522
+ visit(node.statements),
1523
+ token(node.closing_loc)
1524
+ )
1525
+ else
1526
+ builder.loop_mod(
1527
+ :until,
1528
+ visit(node.statements),
1529
+ token(node.keyword_loc),
1530
+ visit(node.predicate)
1531
+ )
1532
+ end
1533
+ end
1534
+
1535
+ # case foo; when bar; end
1536
+ # ^^^^^^^^^^^^^
1537
+ def visit_when_node(node)
1538
+ builder.when(
1539
+ token(node.keyword_loc),
1540
+ visit_all(node.conditions),
1541
+ srange_find(node.conditions.last.location.end_offset, node.statements&.location&.start_offset || (node.conditions.last.location.end_offset + 1), [";", "then"]),
1542
+ visit(node.statements)
1543
+ )
1544
+ end
1545
+
1546
+ # while foo; bar end
1547
+ # ^^^^^^^^^^^^^^^^^^
1548
+ #
1549
+ # bar while foo
1550
+ # ^^^^^^^^^^^^^
1551
+ def visit_while_node(node)
1552
+ if node.location.start_offset == node.keyword_loc.start_offset
1553
+ builder.loop(
1554
+ :while,
1555
+ token(node.keyword_loc),
1556
+ visit(node.predicate),
1557
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, [";", "do"]),
1558
+ visit(node.statements),
1559
+ token(node.closing_loc)
1560
+ )
1561
+ else
1562
+ builder.loop_mod(
1563
+ :while,
1564
+ visit(node.statements),
1565
+ token(node.keyword_loc),
1566
+ visit(node.predicate)
1567
+ )
1568
+ end
1569
+ end
1570
+
1571
+ # `foo`
1572
+ # ^^^^^
1573
+ def visit_x_string_node(node)
1574
+ if node.opening&.start_with?("<<")
1575
+ children, closing = visit_heredoc(InterpolatedXStringNode.new(node.opening_loc, [StringNode.new(0, nil, node.content_loc, nil, node.unescaped, node.content_loc)], node.closing_loc, node.location))
1576
+ builder.xstring_compose(token(node.opening_loc), children, closing)
1577
+ else
1578
+ builder.xstring_compose(
1579
+ token(node.opening_loc),
1580
+ [builder.string_internal([node.unescaped, srange(node.content_loc)])],
1581
+ token(node.closing_loc)
1582
+ )
1583
+ end
1584
+ end
1585
+
1586
+ # yield
1587
+ # ^^^^^
1588
+ #
1589
+ # yield 1
1590
+ # ^^^^^^^
1591
+ def visit_yield_node(node)
1592
+ builder.keyword_cmd(
1593
+ :yield,
1594
+ token(node.keyword_loc),
1595
+ token(node.lparen_loc),
1596
+ visit(node.arguments) || [],
1597
+ token(node.rparen_loc)
1598
+ )
1599
+ end
1600
+
1601
+ private
1602
+
1603
+ def copy_compiler(locals: self.locals, in_destructure: self.in_destructure, in_pattern: self.in_pattern)
1604
+ ParserCompiler.new(parser, offset_cache, locals: locals, in_destructure: in_destructure, in_pattern: in_pattern)
1605
+ end
1606
+
1607
+ # Blocks can have a special set of parameters that automatically expand
1608
+ # when given arrays if they have a single required parameter and no other
1609
+ # parameters.
1610
+ def procarg0?(parameters)
1611
+ parameters &&
1612
+ parameters.requireds.length == 1 &&
1613
+ parameters.optionals.empty? &&
1614
+ parameters.rest.nil? &&
1615
+ parameters.posts.empty? &&
1616
+ parameters.keywords.empty? &&
1617
+ parameters.keyword_rest.nil? &&
1618
+ parameters.block.nil?
1619
+ end
1620
+
1621
+ # Locations in the parser gem AST are generated using this class. We store a
1622
+ # reference to its constant to make it slightly faster to look up.
1623
+ Range = ::Parser::Source::Range
1624
+
1625
+ # Constructs a new source range from the given start and end offsets.
1626
+ def srange(location)
1627
+ Range.new(source_buffer, offset_cache[location.start_offset], offset_cache[location.end_offset]) if location
1628
+ end
1629
+
1630
+ # Constructs a new source range from the given start and end offsets.
1631
+ def srange_offsets(start_offset, end_offset)
1632
+ Range.new(source_buffer, offset_cache[start_offset], offset_cache[end_offset])
1633
+ end
1634
+
1635
+ # Constructs a new source range by finding the given tokens between the
1636
+ # given start offset and end offset. If the needle is not found, it
1637
+ # returns nil.
1638
+ def srange_find(start_offset, end_offset, tokens)
1639
+ tokens.find do |token|
1640
+ next unless (index = source_buffer.source.byteslice(start_offset...end_offset).index(token))
1641
+ offset = start_offset + index
1642
+ return [token, Range.new(source_buffer, offset_cache[offset], offset_cache[offset + token.length])]
1643
+ end
1644
+ end
1645
+
1646
+ # Transform a location into a token that the parser gem expects.
1647
+ def token(location)
1648
+ [location.slice, Range.new(source_buffer, offset_cache[location.start_offset], offset_cache[location.end_offset])] if location
1649
+ end
1650
+
1651
+ # Visit a block node on a call.
1652
+ def visit_block(call, block)
1653
+ if block
1654
+ builder.block(
1655
+ call,
1656
+ token(block.opening_loc),
1657
+ if (parameters = block.parameters)
1658
+ if parameters.is_a?(NumberedParametersNode)
1659
+ visit(parameters)
1660
+ else
1661
+ builder.args(
1662
+ token(parameters.opening_loc),
1663
+ if procarg0?(parameters.parameters)
1664
+ parameter = parameters.parameters.requireds.first
1665
+ [builder.procarg0(visit(parameter))].concat(visit_all(parameters.locals))
1666
+ else
1667
+ visit(parameters)
1668
+ end,
1669
+ token(parameters.closing_loc),
1670
+ false
1671
+ )
1672
+ end
1673
+ else
1674
+ builder.args(nil, [], nil, false)
1675
+ end,
1676
+ visit(block.body),
1677
+ token(block.closing_loc)
1678
+ )
1679
+ else
1680
+ call
1681
+ end
1682
+ end
1683
+
1684
+ # Visit a heredoc that can be either a string or an xstring.
1685
+ def visit_heredoc(node)
1686
+ children = []
1687
+ node.parts.each do |part|
1688
+ pushing =
1689
+ if part.is_a?(StringNode) && part.unescaped.count("\n") > 1
1690
+ unescaped = part.unescaped.split("\n")
1691
+ escaped = part.content.split("\n")
1692
+
1693
+ escaped_lengths =
1694
+ if node.opening.end_with?("'")
1695
+ escaped.map { |line| line.bytesize + 1 }
1696
+ else
1697
+ escaped.chunk_while { |before, after| before.match?(/(?<!\\)\\$/) }.map { |line| line.join.bytesize + line.length }
1698
+ end
1699
+
1700
+ start_offset = part.location.start_offset
1701
+ end_offset = nil
1702
+
1703
+ unescaped.zip(escaped_lengths).map do |unescaped_line, escaped_length|
1704
+ end_offset = start_offset + (escaped_length || 0)
1705
+ inner_part = builder.string_internal(["#{unescaped_line}\n", srange_offsets(start_offset, end_offset)])
1706
+
1707
+ start_offset = end_offset
1708
+ inner_part
1709
+ end
1710
+ else
1711
+ [visit(part)]
1712
+ end
1713
+
1714
+ pushing.each do |child|
1715
+ if child.type == :str && child.children.last == ""
1716
+ # nothing
1717
+ elsif child.type == :str && children.last && children.last.type == :str && !children.last.children.first.end_with?("\n")
1718
+ children.last.children.first << child.children.first
1719
+ else
1720
+ children << child
1721
+ end
1722
+ end
1723
+ end
1724
+
1725
+ closing = node.closing
1726
+ closing_t = [closing.chomp, srange_offsets(node.closing_loc.start_offset, node.closing_loc.end_offset - closing[/\s+$/].length)]
1727
+
1728
+ [children, closing_t]
1729
+ end
1730
+
1731
+ # Visit a numeric node and account for the optional sign.
1732
+ def visit_numeric(node, value)
1733
+ if (slice = node.slice).match?(/^[+-]/)
1734
+ builder.unary_num(
1735
+ [slice[0].to_sym, srange_offsets(node.location.start_offset, node.location.start_offset + 1)],
1736
+ value
1737
+ )
1738
+ else
1739
+ value
1740
+ end
1741
+ end
1742
+
1743
+ # Within the given block, track that we're within a pattern.
1744
+ def within_pattern
1745
+ begin
1746
+ parser.pattern_variables.push
1747
+ yield copy_compiler(in_pattern: true)
1748
+ ensure
1749
+ parser.pattern_variables.pop
1750
+ end
1751
+ end
1752
+ end
1753
+ end
1754
+
1755
+ # Store an alias to the compiler on the Parser::Prism namespace to be more
1756
+ # consistent with expected constants. We put it on the Prism namespace in the
1757
+ # first place to make the constant lookups faster.
1758
+ Parser::Prism::Compiler = Prism::ParserCompiler