prism 0.19.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -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 +1700 -505
  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 +34 -18
  22. data/include/prism/util/pm_buffer.h +9 -0
  23. data/include/prism/util/pm_constant_pool.h +18 -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 +1828 -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 +171 -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 +5 -9
  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 +1344 -479
  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 +37 -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,1828 @@
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
+ def visit_match_predicate_node(node)
1066
+ builder.match_pattern_p(
1067
+ visit(node.value),
1068
+ token(node.operator_loc),
1069
+ within_pattern { |compiler| node.pattern.accept(compiler) }
1070
+ )
1071
+ end
1072
+
1073
+ # foo => bar
1074
+ # ^^^^^^^^^^
1075
+ def visit_match_required_node(node)
1076
+ builder.match_pattern(
1077
+ visit(node.value),
1078
+ token(node.operator_loc),
1079
+ within_pattern { |compiler| node.pattern.accept(compiler) }
1080
+ )
1081
+ end
1082
+
1083
+ # /(?<foo>foo)/ =~ bar
1084
+ # ^^^^^^^^^^^^^^^^^^^^
1085
+ def visit_match_write_node(node)
1086
+ builder.match_op(
1087
+ visit(node.call.receiver),
1088
+ token(node.call.message_loc),
1089
+ visit(node.call.arguments.arguments.first)
1090
+ )
1091
+ end
1092
+
1093
+ # A node that is missing from the syntax tree. This is only used in the
1094
+ # case of a syntax error. The parser gem doesn't have such a concept, so
1095
+ # we invent our own here.
1096
+ def visit_missing_node(node)
1097
+ ::AST::Node.new(:missing, [], location: ::Parser::Source::Map.new(srange(node.location)))
1098
+ end
1099
+
1100
+ # module Foo; end
1101
+ # ^^^^^^^^^^^^^^^
1102
+ def visit_module_node(node)
1103
+ builder.def_module(
1104
+ token(node.module_keyword_loc),
1105
+ visit(node.constant_path),
1106
+ node.body&.accept(copy_compiler(forwarding: [])),
1107
+ token(node.end_keyword_loc)
1108
+ )
1109
+ end
1110
+
1111
+ # foo, bar = baz
1112
+ # ^^^^^^^^
1113
+ def visit_multi_target_node(node)
1114
+ elements = [*node.lefts]
1115
+ elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
1116
+ elements.concat(node.rights)
1117
+
1118
+ builder.multi_lhs(
1119
+ token(node.lparen_loc),
1120
+ visit_all(elements),
1121
+ token(node.rparen_loc)
1122
+ )
1123
+ end
1124
+
1125
+ # foo, bar = baz
1126
+ # ^^^^^^^^^^^^^^
1127
+ def visit_multi_write_node(node)
1128
+ elements = [*node.lefts]
1129
+ elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
1130
+ elements.concat(node.rights)
1131
+
1132
+ builder.multi_assign(
1133
+ builder.multi_lhs(
1134
+ token(node.lparen_loc),
1135
+ visit_all(elements),
1136
+ token(node.rparen_loc)
1137
+ ),
1138
+ token(node.operator_loc),
1139
+ visit(node.value)
1140
+ )
1141
+ end
1142
+
1143
+ # next
1144
+ # ^^^^
1145
+ #
1146
+ # next foo
1147
+ # ^^^^^^^^
1148
+ def visit_next_node(node)
1149
+ builder.keyword_cmd(
1150
+ :next,
1151
+ token(node.keyword_loc),
1152
+ nil,
1153
+ visit(node.arguments) || [],
1154
+ nil
1155
+ )
1156
+ end
1157
+
1158
+ # nil
1159
+ # ^^^
1160
+ def visit_nil_node(node)
1161
+ builder.nil(token(node.location))
1162
+ end
1163
+
1164
+ # def foo(**nil); end
1165
+ # ^^^^^
1166
+ def visit_no_keywords_parameter_node(node)
1167
+ if in_pattern
1168
+ builder.match_nil_pattern(token(node.operator_loc), token(node.keyword_loc))
1169
+ else
1170
+ builder.kwnilarg(token(node.operator_loc), token(node.keyword_loc))
1171
+ end
1172
+ end
1173
+
1174
+ # -> { _1 + _2 }
1175
+ # ^^^^^^^^^^^^^^
1176
+ def visit_numbered_parameters_node(node)
1177
+ builder.numargs(node.maximum)
1178
+ end
1179
+
1180
+ # $1
1181
+ # ^^
1182
+ def visit_numbered_reference_read_node(node)
1183
+ builder.nth_ref([node.number, srange(node.location)])
1184
+ end
1185
+
1186
+ # def foo(bar: baz); end
1187
+ # ^^^^^^^^
1188
+ def visit_optional_keyword_parameter_node(node)
1189
+ builder.kwoptarg([node.name, srange(node.name_loc)], visit(node.value))
1190
+ end
1191
+
1192
+ # def foo(bar = 1); end
1193
+ # ^^^^^^^
1194
+ def visit_optional_parameter_node(node)
1195
+ builder.optarg(token(node.name_loc), token(node.operator_loc), visit(node.value))
1196
+ end
1197
+
1198
+ # a or b
1199
+ # ^^^^^^
1200
+ def visit_or_node(node)
1201
+ builder.logical_op(:or, visit(node.left), token(node.operator_loc), visit(node.right))
1202
+ end
1203
+
1204
+ # def foo(bar, *baz); end
1205
+ # ^^^^^^^^^
1206
+ def visit_parameters_node(node)
1207
+ params = []
1208
+
1209
+ if node.requireds.any?
1210
+ node.requireds.each do |required|
1211
+ if required.is_a?(RequiredParameterNode)
1212
+ params << visit(required)
1213
+ else
1214
+ compiler = copy_compiler(in_destructure: true)
1215
+ params << required.accept(compiler)
1216
+ end
1217
+ end
1218
+ end
1219
+
1220
+ params.concat(visit_all(node.optionals)) if node.optionals.any?
1221
+ params << visit(node.rest) if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
1222
+
1223
+ if node.posts.any?
1224
+ node.posts.each do |post|
1225
+ if post.is_a?(RequiredParameterNode)
1226
+ params << visit(post)
1227
+ else
1228
+ compiler = copy_compiler(in_destructure: true)
1229
+ params << post.accept(compiler)
1230
+ end
1231
+ end
1232
+ end
1233
+
1234
+ params.concat(visit_all(node.keywords)) if node.keywords.any?
1235
+ params << visit(node.keyword_rest) if !node.keyword_rest.nil?
1236
+ params << visit(node.block) if !node.block.nil?
1237
+ params
1238
+ end
1239
+
1240
+ # ()
1241
+ # ^^
1242
+ #
1243
+ # (1)
1244
+ # ^^^
1245
+ def visit_parentheses_node(node)
1246
+ builder.begin(
1247
+ token(node.opening_loc),
1248
+ visit(node.body),
1249
+ token(node.closing_loc)
1250
+ )
1251
+ end
1252
+
1253
+ # foo => ^(bar)
1254
+ # ^^^^^^
1255
+ def visit_pinned_expression_node(node)
1256
+ builder.pin(token(node.operator_loc), visit(node.expression))
1257
+ end
1258
+
1259
+ # foo = 1 and bar => ^foo
1260
+ # ^^^^
1261
+ def visit_pinned_variable_node(node)
1262
+ builder.pin(token(node.operator_loc), visit(node.variable))
1263
+ end
1264
+
1265
+ # END {}
1266
+ def visit_post_execution_node(node)
1267
+ builder.postexe(
1268
+ token(node.keyword_loc),
1269
+ token(node.opening_loc),
1270
+ visit(node.statements),
1271
+ token(node.closing_loc)
1272
+ )
1273
+ end
1274
+
1275
+ # BEGIN {}
1276
+ def visit_pre_execution_node(node)
1277
+ builder.preexe(
1278
+ token(node.keyword_loc),
1279
+ token(node.opening_loc),
1280
+ visit(node.statements),
1281
+ token(node.closing_loc)
1282
+ )
1283
+ end
1284
+
1285
+ # The top-level program node.
1286
+ def visit_program_node(node)
1287
+ visit(node.statements)
1288
+ end
1289
+
1290
+ # 0..5
1291
+ # ^^^^
1292
+ def visit_range_node(node)
1293
+ if node.exclude_end?
1294
+ builder.range_exclusive(
1295
+ visit(node.left),
1296
+ token(node.operator_loc),
1297
+ visit(node.right)
1298
+ )
1299
+ else
1300
+ builder.range_inclusive(
1301
+ visit(node.left),
1302
+ token(node.operator_loc),
1303
+ visit(node.right)
1304
+ )
1305
+ end
1306
+ end
1307
+
1308
+ # if foo .. bar; end
1309
+ # ^^^^^^^^^^
1310
+ alias visit_flip_flop_node visit_range_node
1311
+
1312
+ # 1r
1313
+ # ^^
1314
+ def visit_rational_node(node)
1315
+ visit_numeric(node, builder.rational([node.value, srange(node.location)]))
1316
+ end
1317
+
1318
+ # redo
1319
+ # ^^^^
1320
+ def visit_redo_node(node)
1321
+ builder.keyword_cmd(:redo, token(node.location))
1322
+ end
1323
+
1324
+ # /foo/
1325
+ # ^^^^^
1326
+ def visit_regular_expression_node(node)
1327
+ builder.regexp_compose(
1328
+ token(node.opening_loc),
1329
+ [builder.string_internal(token(node.content_loc))],
1330
+ [node.closing[0], srange_offsets(node.closing_loc.start_offset, node.closing_loc.start_offset + 1)],
1331
+ builder.regexp_options([node.closing[1..], srange_offsets(node.closing_loc.start_offset + 1, node.closing_loc.end_offset)])
1332
+ )
1333
+ end
1334
+
1335
+ # if /foo/ then end
1336
+ # ^^^^^
1337
+ alias visit_match_last_line_node visit_regular_expression_node
1338
+
1339
+ # def foo(bar:); end
1340
+ # ^^^^
1341
+ def visit_required_keyword_parameter_node(node)
1342
+ builder.kwarg([node.name, srange(node.name_loc)])
1343
+ end
1344
+
1345
+ # def foo(bar); end
1346
+ # ^^^
1347
+ def visit_required_parameter_node(node)
1348
+ builder.arg(token(node.location))
1349
+ end
1350
+
1351
+ # foo rescue bar
1352
+ # ^^^^^^^^^^^^^^
1353
+ def visit_rescue_modifier_node(node)
1354
+ builder.begin_body(
1355
+ visit(node.expression),
1356
+ [
1357
+ builder.rescue_body(
1358
+ token(node.keyword_loc),
1359
+ nil,
1360
+ nil,
1361
+ nil,
1362
+ nil,
1363
+ visit(node.rescue_expression)
1364
+ )
1365
+ ]
1366
+ )
1367
+ end
1368
+
1369
+ # begin; rescue; end
1370
+ # ^^^^^^^
1371
+ def visit_rescue_node(node)
1372
+ raise CompilationError, "Cannot directly compile rescue nodes"
1373
+ end
1374
+
1375
+ # def foo(*bar); end
1376
+ # ^^^^
1377
+ #
1378
+ # def foo(*); end
1379
+ # ^
1380
+ def visit_rest_parameter_node(node)
1381
+ builder.restarg(token(node.operator_loc), token(node.name_loc))
1382
+ end
1383
+
1384
+ # retry
1385
+ # ^^^^^
1386
+ def visit_retry_node(node)
1387
+ builder.keyword_cmd(:retry, token(node.location))
1388
+ end
1389
+
1390
+ # return
1391
+ # ^^^^^^
1392
+ #
1393
+ # return 1
1394
+ # ^^^^^^^^
1395
+ def visit_return_node(node)
1396
+ builder.keyword_cmd(
1397
+ :return,
1398
+ token(node.keyword_loc),
1399
+ nil,
1400
+ visit(node.arguments) || [],
1401
+ nil
1402
+ )
1403
+ end
1404
+
1405
+ # self
1406
+ # ^^^^
1407
+ def visit_self_node(node)
1408
+ builder.self(token(node.location))
1409
+ end
1410
+
1411
+ # class << self; end
1412
+ # ^^^^^^^^^^^^^^^^^^
1413
+ def visit_singleton_class_node(node)
1414
+ builder.def_sclass(
1415
+ token(node.class_keyword_loc),
1416
+ token(node.operator_loc),
1417
+ visit(node.expression),
1418
+ node.body&.accept(copy_compiler(forwarding: [])),
1419
+ token(node.end_keyword_loc)
1420
+ )
1421
+ end
1422
+
1423
+ # __ENCODING__
1424
+ # ^^^^^^^^^^^^
1425
+ def visit_source_encoding_node(node)
1426
+ builder.accessible(builder.__ENCODING__(token(node.location)))
1427
+ end
1428
+
1429
+ # __FILE__
1430
+ # ^^^^^^^^
1431
+ def visit_source_file_node(node)
1432
+ builder.accessible(builder.__FILE__(token(node.location)))
1433
+ end
1434
+
1435
+ # __LINE__
1436
+ # ^^^^^^^^
1437
+ def visit_source_line_node(node)
1438
+ builder.accessible(builder.__LINE__(token(node.location)))
1439
+ end
1440
+
1441
+ # foo(*bar)
1442
+ # ^^^^
1443
+ #
1444
+ # def foo((bar, *baz)); end
1445
+ # ^^^^
1446
+ #
1447
+ # def foo(*); bar(*); end
1448
+ # ^
1449
+ def visit_splat_node(node)
1450
+ if node.expression.nil? && forwarding.include?(:*)
1451
+ builder.forwarded_restarg(token(node.operator_loc))
1452
+ elsif in_destructure
1453
+ builder.restarg(token(node.operator_loc), token(node.expression&.location))
1454
+ elsif in_pattern
1455
+ builder.match_rest(token(node.operator_loc), token(node.expression&.location))
1456
+ else
1457
+ builder.splat(token(node.operator_loc), visit(node.expression))
1458
+ end
1459
+ end
1460
+
1461
+ # A list of statements.
1462
+ def visit_statements_node(node)
1463
+ builder.compstmt(visit_all(node.body))
1464
+ end
1465
+
1466
+ # "foo"
1467
+ # ^^^^^
1468
+ def visit_string_node(node)
1469
+ if node.opening&.start_with?("<<")
1470
+ 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))
1471
+ builder.string_compose(token(node.opening_loc), children, closing)
1472
+ elsif node.opening == "?"
1473
+ builder.character([node.unescaped, srange(node.location)])
1474
+ else
1475
+ builder.string_compose(
1476
+ token(node.opening_loc),
1477
+ [builder.string_internal([node.unescaped, srange(node.content_loc)])],
1478
+ token(node.closing_loc)
1479
+ )
1480
+ end
1481
+ end
1482
+
1483
+ # super(foo)
1484
+ # ^^^^^^^^^^
1485
+ def visit_super_node(node)
1486
+ arguments = node.arguments&.arguments || []
1487
+ block = node.block
1488
+
1489
+ if block.is_a?(BlockArgumentNode)
1490
+ arguments = [*arguments, block]
1491
+ block = nil
1492
+ end
1493
+
1494
+ visit_block(
1495
+ builder.keyword_cmd(
1496
+ :super,
1497
+ token(node.keyword_loc),
1498
+ token(node.lparen_loc),
1499
+ visit_all(arguments),
1500
+ token(node.rparen_loc)
1501
+ ),
1502
+ block
1503
+ )
1504
+ end
1505
+
1506
+ # :foo
1507
+ # ^^^^
1508
+ def visit_symbol_node(node)
1509
+ if node.closing_loc.nil?
1510
+ if node.opening_loc.nil?
1511
+ builder.symbol_internal([node.unescaped, srange(node.location)])
1512
+ else
1513
+ builder.symbol([node.unescaped, srange(node.location)])
1514
+ end
1515
+ else
1516
+ builder.symbol_compose(
1517
+ token(node.opening_loc),
1518
+ [builder.string_internal([node.unescaped, srange(node.value_loc)])],
1519
+ token(node.closing_loc)
1520
+ )
1521
+ end
1522
+ end
1523
+
1524
+ # true
1525
+ # ^^^^
1526
+ def visit_true_node(node)
1527
+ builder.true(token(node.location))
1528
+ end
1529
+
1530
+ # undef foo
1531
+ # ^^^^^^^^^
1532
+ def visit_undef_node(node)
1533
+ builder.undef_method(token(node.keyword_loc), visit_all(node.names))
1534
+ end
1535
+
1536
+ # unless foo; bar end
1537
+ # ^^^^^^^^^^^^^^^^^^^
1538
+ #
1539
+ # bar unless foo
1540
+ # ^^^^^^^^^^^^^^
1541
+ def visit_unless_node(node)
1542
+ if node.keyword_loc.start_offset == node.location.start_offset
1543
+ builder.condition(
1544
+ token(node.keyword_loc),
1545
+ visit(node.predicate),
1546
+ if node.then_keyword_loc
1547
+ token(node.then_keyword_loc)
1548
+ else
1549
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.consequent&.location || node.end_keyword_loc).start_offset, [";"])
1550
+ end,
1551
+ visit(node.consequent),
1552
+ token(node.consequent&.else_keyword_loc),
1553
+ visit(node.statements),
1554
+ token(node.end_keyword_loc)
1555
+ )
1556
+ else
1557
+ builder.condition_mod(
1558
+ visit(node.consequent),
1559
+ visit(node.statements),
1560
+ token(node.keyword_loc),
1561
+ visit(node.predicate)
1562
+ )
1563
+ end
1564
+ end
1565
+
1566
+ # until foo; bar end
1567
+ # ^^^^^^^^^^^^^^^^^
1568
+ #
1569
+ # bar until foo
1570
+ # ^^^^^^^^^^^^^
1571
+ def visit_until_node(node)
1572
+ if node.location.start_offset == node.keyword_loc.start_offset
1573
+ builder.loop(
1574
+ :until,
1575
+ token(node.keyword_loc),
1576
+ visit(node.predicate),
1577
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, [";", "do"]),
1578
+ visit(node.statements),
1579
+ token(node.closing_loc)
1580
+ )
1581
+ else
1582
+ builder.loop_mod(
1583
+ :until,
1584
+ visit(node.statements),
1585
+ token(node.keyword_loc),
1586
+ visit(node.predicate)
1587
+ )
1588
+ end
1589
+ end
1590
+
1591
+ # case foo; when bar; end
1592
+ # ^^^^^^^^^^^^^
1593
+ def visit_when_node(node)
1594
+ builder.when(
1595
+ token(node.keyword_loc),
1596
+ visit_all(node.conditions),
1597
+ srange_find(node.conditions.last.location.end_offset, node.statements&.location&.start_offset || (node.conditions.last.location.end_offset + 1), [";", "then"]),
1598
+ visit(node.statements)
1599
+ )
1600
+ end
1601
+
1602
+ # while foo; bar end
1603
+ # ^^^^^^^^^^^^^^^^^^
1604
+ #
1605
+ # bar while foo
1606
+ # ^^^^^^^^^^^^^
1607
+ def visit_while_node(node)
1608
+ if node.location.start_offset == node.keyword_loc.start_offset
1609
+ builder.loop(
1610
+ :while,
1611
+ token(node.keyword_loc),
1612
+ visit(node.predicate),
1613
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, [";", "do"]),
1614
+ visit(node.statements),
1615
+ token(node.closing_loc)
1616
+ )
1617
+ else
1618
+ builder.loop_mod(
1619
+ :while,
1620
+ visit(node.statements),
1621
+ token(node.keyword_loc),
1622
+ visit(node.predicate)
1623
+ )
1624
+ end
1625
+ end
1626
+
1627
+ # `foo`
1628
+ # ^^^^^
1629
+ def visit_x_string_node(node)
1630
+ if node.opening&.start_with?("<<")
1631
+ 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))
1632
+ builder.xstring_compose(token(node.opening_loc), children, closing)
1633
+ else
1634
+ builder.xstring_compose(
1635
+ token(node.opening_loc),
1636
+ [builder.string_internal([node.unescaped, srange(node.content_loc)])],
1637
+ token(node.closing_loc)
1638
+ )
1639
+ end
1640
+ end
1641
+
1642
+ # yield
1643
+ # ^^^^^
1644
+ #
1645
+ # yield 1
1646
+ # ^^^^^^^
1647
+ def visit_yield_node(node)
1648
+ builder.keyword_cmd(
1649
+ :yield,
1650
+ token(node.keyword_loc),
1651
+ token(node.lparen_loc),
1652
+ visit(node.arguments) || [],
1653
+ token(node.rparen_loc)
1654
+ )
1655
+ end
1656
+
1657
+ private
1658
+
1659
+ # Initialize a new compiler with the given option overrides, used to
1660
+ # visit a subtree with the given options.
1661
+ def copy_compiler(forwarding: self.forwarding, in_destructure: self.in_destructure, in_pattern: self.in_pattern)
1662
+ Compiler.new(parser, offset_cache, forwarding: forwarding, in_destructure: in_destructure, in_pattern: in_pattern)
1663
+ end
1664
+
1665
+ # When *, **, &, or ... are used as an argument in a method call, we
1666
+ # check if they were allowed by the current context. To determine that
1667
+ # we build this lookup table.
1668
+ def find_forwarding(node)
1669
+ return [] if node.nil?
1670
+
1671
+ forwarding = []
1672
+ forwarding << :* if node.rest.is_a?(RestParameterNode) && node.rest.name.nil?
1673
+ forwarding << :** if node.keyword_rest.is_a?(KeywordRestParameterNode) && node.keyword_rest.name.nil?
1674
+ forwarding << :& if !node.block.nil? && node.block.name.nil?
1675
+ forwarding |= [:&, :"..."] if node.keyword_rest.is_a?(ForwardingParameterNode)
1676
+
1677
+ forwarding
1678
+ end
1679
+
1680
+ # Blocks can have a special set of parameters that automatically expand
1681
+ # when given arrays if they have a single required parameter and no
1682
+ # other parameters.
1683
+ def procarg0?(parameters)
1684
+ parameters &&
1685
+ parameters.requireds.length == 1 &&
1686
+ parameters.optionals.empty? &&
1687
+ parameters.rest.nil? &&
1688
+ parameters.posts.empty? &&
1689
+ parameters.keywords.empty? &&
1690
+ parameters.keyword_rest.nil? &&
1691
+ parameters.block.nil?
1692
+ end
1693
+
1694
+ # Locations in the parser gem AST are generated using this class. We
1695
+ # store a reference to its constant to make it slightly faster to look
1696
+ # up.
1697
+ Range = ::Parser::Source::Range
1698
+
1699
+ # Constructs a new source range from the given start and end offsets.
1700
+ def srange(location)
1701
+ Range.new(source_buffer, offset_cache[location.start_offset], offset_cache[location.end_offset]) if location
1702
+ end
1703
+
1704
+ # Constructs a new source range from the given start and end offsets.
1705
+ def srange_offsets(start_offset, end_offset)
1706
+ Range.new(source_buffer, offset_cache[start_offset], offset_cache[end_offset])
1707
+ end
1708
+
1709
+ # Constructs a new source range by finding the given tokens between the
1710
+ # given start offset and end offset. If the needle is not found, it
1711
+ # returns nil.
1712
+ def srange_find(start_offset, end_offset, tokens)
1713
+ tokens.find do |token|
1714
+ next unless (index = source_buffer.source.byteslice(start_offset...end_offset).index(token))
1715
+ offset = start_offset + index
1716
+ return [token, Range.new(source_buffer, offset_cache[offset], offset_cache[offset + token.length])]
1717
+ end
1718
+ end
1719
+
1720
+ # Transform a location into a token that the parser gem expects.
1721
+ def token(location)
1722
+ [location.slice, Range.new(source_buffer, offset_cache[location.start_offset], offset_cache[location.end_offset])] if location
1723
+ end
1724
+
1725
+ # Visit a block node on a call.
1726
+ def visit_block(call, block)
1727
+ if block
1728
+ parameters = block.parameters
1729
+
1730
+ builder.block(
1731
+ call,
1732
+ token(block.opening_loc),
1733
+ if parameters.nil?
1734
+ builder.args(nil, [], nil, false)
1735
+ elsif parameters.is_a?(NumberedParametersNode)
1736
+ visit(parameters)
1737
+ else
1738
+ builder.args(
1739
+ token(parameters.opening_loc),
1740
+ if procarg0?(parameters.parameters)
1741
+ parameter = parameters.parameters.requireds.first
1742
+ [builder.procarg0(visit(parameter))].concat(visit_all(parameters.locals))
1743
+ else
1744
+ visit(parameters)
1745
+ end,
1746
+ token(parameters.closing_loc),
1747
+ false
1748
+ )
1749
+ end,
1750
+ block.body&.accept(copy_compiler(forwarding: parameters.is_a?(NumberedParametersNode) ? [] : find_forwarding(parameters&.parameters))),
1751
+ token(block.closing_loc)
1752
+ )
1753
+ else
1754
+ call
1755
+ end
1756
+ end
1757
+
1758
+ # Visit a heredoc that can be either a string or an xstring.
1759
+ def visit_heredoc(node)
1760
+ children = []
1761
+ node.parts.each do |part|
1762
+ pushing =
1763
+ if part.is_a?(StringNode) && part.unescaped.include?("\n")
1764
+ unescaped = part.unescaped.lines(chomp: true)
1765
+ escaped = part.content.lines(chomp: true)
1766
+
1767
+ escaped_lengths =
1768
+ if node.opening.end_with?("'")
1769
+ escaped.map { |line| line.bytesize + 1 }
1770
+ else
1771
+ escaped.chunk_while { |before, after| before.match?(/(?<!\\)\\$/) }.map { |line| line.join.bytesize + line.length }
1772
+ end
1773
+
1774
+ start_offset = part.location.start_offset
1775
+ end_offset = nil
1776
+
1777
+ unescaped.zip(escaped_lengths).map do |unescaped_line, escaped_length|
1778
+ end_offset = start_offset + (escaped_length || 0)
1779
+ inner_part = builder.string_internal(["#{unescaped_line}\n", srange_offsets(start_offset, end_offset)])
1780
+ start_offset = end_offset
1781
+ inner_part
1782
+ end
1783
+ else
1784
+ [visit(part)]
1785
+ end
1786
+
1787
+ pushing.each do |child|
1788
+ if child.type == :str && child.children.last == ""
1789
+ # nothing
1790
+ elsif child.type == :str && children.last && children.last.type == :str && !children.last.children.first.end_with?("\n")
1791
+ children.last.children.first << child.children.first
1792
+ else
1793
+ children << child
1794
+ end
1795
+ end
1796
+ end
1797
+
1798
+ closing = node.closing
1799
+ closing_t = [closing.chomp, srange_offsets(node.closing_loc.start_offset, node.closing_loc.end_offset - (closing[/\s+$/]&.length || 0))]
1800
+
1801
+ [children, closing_t]
1802
+ end
1803
+
1804
+ # Visit a numeric node and account for the optional sign.
1805
+ def visit_numeric(node, value)
1806
+ if (slice = node.slice).match?(/^[+-]/)
1807
+ builder.unary_num(
1808
+ [slice[0].to_sym, srange_offsets(node.location.start_offset, node.location.start_offset + 1)],
1809
+ value
1810
+ )
1811
+ else
1812
+ value
1813
+ end
1814
+ end
1815
+
1816
+ # Within the given block, track that we're within a pattern.
1817
+ def within_pattern
1818
+ begin
1819
+ parser.pattern_variables.push
1820
+ yield copy_compiler(in_pattern: true)
1821
+ ensure
1822
+ parser.pattern_variables.pop
1823
+ end
1824
+ end
1825
+ end
1826
+ end
1827
+ end
1828
+ end