parser-prism 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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