prism 0.19.0 → 0.24.0

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