prism 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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