prism 0.19.0 → 0.20.0

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