ed-precompiled_prism 1.5.2

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 (154) hide show
  1. checksums.yaml +7 -0
  2. data/BSDmakefile +58 -0
  3. data/CHANGELOG.md +723 -0
  4. data/CODE_OF_CONDUCT.md +76 -0
  5. data/CONTRIBUTING.md +58 -0
  6. data/LICENSE.md +7 -0
  7. data/Makefile +110 -0
  8. data/README.md +143 -0
  9. data/config.yml +4714 -0
  10. data/docs/build_system.md +119 -0
  11. data/docs/configuration.md +68 -0
  12. data/docs/cruby_compilation.md +27 -0
  13. data/docs/design.md +53 -0
  14. data/docs/encoding.md +121 -0
  15. data/docs/fuzzing.md +88 -0
  16. data/docs/heredocs.md +36 -0
  17. data/docs/javascript.md +118 -0
  18. data/docs/local_variable_depth.md +229 -0
  19. data/docs/mapping.md +117 -0
  20. data/docs/parser_translation.md +24 -0
  21. data/docs/parsing_rules.md +22 -0
  22. data/docs/releasing.md +98 -0
  23. data/docs/relocation.md +34 -0
  24. data/docs/ripper_translation.md +72 -0
  25. data/docs/ruby_api.md +44 -0
  26. data/docs/ruby_parser_translation.md +19 -0
  27. data/docs/serialization.md +233 -0
  28. data/docs/testing.md +55 -0
  29. data/ext/prism/api_node.c +6941 -0
  30. data/ext/prism/api_pack.c +276 -0
  31. data/ext/prism/extconf.rb +127 -0
  32. data/ext/prism/extension.c +1419 -0
  33. data/ext/prism/extension.h +19 -0
  34. data/include/prism/ast.h +8220 -0
  35. data/include/prism/defines.h +260 -0
  36. data/include/prism/diagnostic.h +456 -0
  37. data/include/prism/encoding.h +283 -0
  38. data/include/prism/node.h +129 -0
  39. data/include/prism/options.h +482 -0
  40. data/include/prism/pack.h +163 -0
  41. data/include/prism/parser.h +933 -0
  42. data/include/prism/prettyprint.h +34 -0
  43. data/include/prism/regexp.h +43 -0
  44. data/include/prism/static_literals.h +121 -0
  45. data/include/prism/util/pm_buffer.h +236 -0
  46. data/include/prism/util/pm_char.h +204 -0
  47. data/include/prism/util/pm_constant_pool.h +218 -0
  48. data/include/prism/util/pm_integer.h +130 -0
  49. data/include/prism/util/pm_list.h +103 -0
  50. data/include/prism/util/pm_memchr.h +29 -0
  51. data/include/prism/util/pm_newline_list.h +113 -0
  52. data/include/prism/util/pm_string.h +200 -0
  53. data/include/prism/util/pm_strncasecmp.h +32 -0
  54. data/include/prism/util/pm_strpbrk.h +46 -0
  55. data/include/prism/version.h +29 -0
  56. data/include/prism.h +408 -0
  57. data/lib/prism/compiler.rb +801 -0
  58. data/lib/prism/desugar_compiler.rb +392 -0
  59. data/lib/prism/dispatcher.rb +2210 -0
  60. data/lib/prism/dot_visitor.rb +4762 -0
  61. data/lib/prism/dsl.rb +1003 -0
  62. data/lib/prism/ffi.rb +570 -0
  63. data/lib/prism/inspect_visitor.rb +2392 -0
  64. data/lib/prism/lex_compat.rb +928 -0
  65. data/lib/prism/mutation_compiler.rb +772 -0
  66. data/lib/prism/node.rb +18816 -0
  67. data/lib/prism/node_ext.rb +511 -0
  68. data/lib/prism/pack.rb +230 -0
  69. data/lib/prism/parse_result/comments.rb +188 -0
  70. data/lib/prism/parse_result/errors.rb +66 -0
  71. data/lib/prism/parse_result/newlines.rb +155 -0
  72. data/lib/prism/parse_result.rb +911 -0
  73. data/lib/prism/pattern.rb +269 -0
  74. data/lib/prism/polyfill/append_as_bytes.rb +15 -0
  75. data/lib/prism/polyfill/byteindex.rb +13 -0
  76. data/lib/prism/polyfill/scan_byte.rb +14 -0
  77. data/lib/prism/polyfill/unpack1.rb +14 -0
  78. data/lib/prism/polyfill/warn.rb +36 -0
  79. data/lib/prism/reflection.rb +416 -0
  80. data/lib/prism/relocation.rb +505 -0
  81. data/lib/prism/serialize.rb +2398 -0
  82. data/lib/prism/string_query.rb +31 -0
  83. data/lib/prism/translation/parser/builder.rb +62 -0
  84. data/lib/prism/translation/parser/compiler.rb +2234 -0
  85. data/lib/prism/translation/parser/lexer.rb +820 -0
  86. data/lib/prism/translation/parser.rb +374 -0
  87. data/lib/prism/translation/parser33.rb +13 -0
  88. data/lib/prism/translation/parser34.rb +13 -0
  89. data/lib/prism/translation/parser35.rb +13 -0
  90. data/lib/prism/translation/parser_current.rb +24 -0
  91. data/lib/prism/translation/ripper/sexp.rb +126 -0
  92. data/lib/prism/translation/ripper/shim.rb +5 -0
  93. data/lib/prism/translation/ripper.rb +3474 -0
  94. data/lib/prism/translation/ruby_parser.rb +1929 -0
  95. data/lib/prism/translation.rb +16 -0
  96. data/lib/prism/visitor.rb +813 -0
  97. data/lib/prism.rb +97 -0
  98. data/prism.gemspec +174 -0
  99. data/rbi/prism/compiler.rbi +12 -0
  100. data/rbi/prism/dsl.rbi +524 -0
  101. data/rbi/prism/inspect_visitor.rbi +12 -0
  102. data/rbi/prism/node.rbi +8734 -0
  103. data/rbi/prism/node_ext.rbi +107 -0
  104. data/rbi/prism/parse_result.rbi +404 -0
  105. data/rbi/prism/reflection.rbi +58 -0
  106. data/rbi/prism/string_query.rbi +12 -0
  107. data/rbi/prism/translation/parser.rbi +11 -0
  108. data/rbi/prism/translation/parser33.rbi +6 -0
  109. data/rbi/prism/translation/parser34.rbi +6 -0
  110. data/rbi/prism/translation/parser35.rbi +6 -0
  111. data/rbi/prism/translation/ripper.rbi +15 -0
  112. data/rbi/prism/visitor.rbi +473 -0
  113. data/rbi/prism.rbi +66 -0
  114. data/sig/prism/compiler.rbs +9 -0
  115. data/sig/prism/dispatcher.rbs +19 -0
  116. data/sig/prism/dot_visitor.rbs +6 -0
  117. data/sig/prism/dsl.rbs +351 -0
  118. data/sig/prism/inspect_visitor.rbs +22 -0
  119. data/sig/prism/lex_compat.rbs +10 -0
  120. data/sig/prism/mutation_compiler.rbs +159 -0
  121. data/sig/prism/node.rbs +4028 -0
  122. data/sig/prism/node_ext.rbs +149 -0
  123. data/sig/prism/pack.rbs +43 -0
  124. data/sig/prism/parse_result/comments.rbs +38 -0
  125. data/sig/prism/parse_result.rbs +196 -0
  126. data/sig/prism/pattern.rbs +13 -0
  127. data/sig/prism/reflection.rbs +50 -0
  128. data/sig/prism/relocation.rbs +185 -0
  129. data/sig/prism/serialize.rbs +8 -0
  130. data/sig/prism/string_query.rbs +11 -0
  131. data/sig/prism/visitor.rbs +169 -0
  132. data/sig/prism.rbs +254 -0
  133. data/src/diagnostic.c +850 -0
  134. data/src/encoding.c +5235 -0
  135. data/src/node.c +8676 -0
  136. data/src/options.c +328 -0
  137. data/src/pack.c +509 -0
  138. data/src/prettyprint.c +8941 -0
  139. data/src/prism.c +23361 -0
  140. data/src/regexp.c +790 -0
  141. data/src/serialize.c +2268 -0
  142. data/src/static_literals.c +617 -0
  143. data/src/token_type.c +703 -0
  144. data/src/util/pm_buffer.c +357 -0
  145. data/src/util/pm_char.c +318 -0
  146. data/src/util/pm_constant_pool.c +342 -0
  147. data/src/util/pm_integer.c +670 -0
  148. data/src/util/pm_list.c +49 -0
  149. data/src/util/pm_memchr.c +35 -0
  150. data/src/util/pm_newline_list.c +125 -0
  151. data/src/util/pm_string.c +381 -0
  152. data/src/util/pm_strncasecmp.c +36 -0
  153. data/src/util/pm_strpbrk.c +206 -0
  154. metadata +195 -0
@@ -0,0 +1,2234 @@
1
+ # frozen_string_literal: true
2
+ # :markup: markdown
3
+
4
+ module Prism
5
+ module Translation
6
+ class Parser
7
+ # A visitor that knows how to convert a prism syntax tree into the
8
+ # whitequark/parser gem's syntax tree.
9
+ class Compiler < ::Prism::Compiler
10
+ # Raised when the tree is malformed or there is a bug in the compiler.
11
+ class CompilationError < StandardError
12
+ end
13
+
14
+ # The Parser::Base instance that is being used to build the AST.
15
+ attr_reader :parser
16
+
17
+ # The Parser::Builders::Default instance that is being used to build the
18
+ # AST.
19
+ attr_reader :builder
20
+
21
+ # The Parser::Source::Buffer instance that is holding a reference to the
22
+ # source code.
23
+ attr_reader :source_buffer
24
+
25
+ # The offset cache that is used to map between byte and character
26
+ # offsets in the file.
27
+ attr_reader :offset_cache
28
+
29
+ # The types of values that can be forwarded in the current scope.
30
+ attr_reader :forwarding
31
+
32
+ # Whether or not the current node is in a destructure.
33
+ attr_reader :in_destructure
34
+
35
+ # Whether or not the current node is in a pattern.
36
+ attr_reader :in_pattern
37
+
38
+ # Initialize a new compiler with the given parser, offset cache, and
39
+ # options.
40
+ def initialize(parser, offset_cache, forwarding: [], in_destructure: false, in_pattern: false)
41
+ @parser = parser
42
+ @builder = parser.builder
43
+ @source_buffer = parser.source_buffer
44
+ @offset_cache = offset_cache
45
+
46
+ @forwarding = forwarding
47
+ @in_destructure = in_destructure
48
+ @in_pattern = in_pattern
49
+ end
50
+
51
+ # alias foo bar
52
+ # ^^^^^^^^^^^^^
53
+ def visit_alias_method_node(node)
54
+ builder.alias(token(node.keyword_loc), visit(node.new_name), visit(node.old_name))
55
+ end
56
+
57
+ # alias $foo $bar
58
+ # ^^^^^^^^^^^^^^^
59
+ def visit_alias_global_variable_node(node)
60
+ builder.alias(token(node.keyword_loc), visit(node.new_name), visit(node.old_name))
61
+ end
62
+
63
+ # foo => bar | baz
64
+ # ^^^^^^^^^
65
+ def visit_alternation_pattern_node(node)
66
+ builder.match_alt(visit(node.left), token(node.operator_loc), visit(node.right))
67
+ end
68
+
69
+ # a and b
70
+ # ^^^^^^^
71
+ def visit_and_node(node)
72
+ builder.logical_op(:and, visit(node.left), token(node.operator_loc), visit(node.right))
73
+ end
74
+
75
+ # []
76
+ # ^^
77
+ def visit_array_node(node)
78
+ if node.opening&.start_with?("%w", "%W", "%i", "%I")
79
+ elements = node.elements.flat_map do |element|
80
+ if element.is_a?(StringNode)
81
+ if element.content.include?("\n")
82
+ string_nodes_from_line_continuations(element.unescaped, element.content, element.content_loc.start_offset, node.opening)
83
+ else
84
+ [builder.string_internal([element.unescaped, srange(element.content_loc)])]
85
+ end
86
+ elsif element.is_a?(InterpolatedStringNode)
87
+ builder.string_compose(
88
+ token(element.opening_loc),
89
+ string_nodes_from_interpolation(element, node.opening),
90
+ token(element.closing_loc)
91
+ )
92
+ else
93
+ [visit(element)]
94
+ end
95
+ end
96
+ else
97
+ elements = visit_all(node.elements)
98
+ end
99
+
100
+ builder.array(token(node.opening_loc), elements, token(node.closing_loc))
101
+ end
102
+
103
+ # foo => [bar]
104
+ # ^^^^^
105
+ def visit_array_pattern_node(node)
106
+ elements = [*node.requireds]
107
+ elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
108
+ elements.concat(node.posts)
109
+ visited = visit_all(elements)
110
+
111
+ if node.rest.is_a?(ImplicitRestNode)
112
+ visited[-1] = builder.match_with_trailing_comma(visited[-1], token(node.rest.location))
113
+ end
114
+
115
+ if node.constant
116
+ if visited.empty?
117
+ builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(token(node.opening_loc), visited, token(node.closing_loc)), token(node.closing_loc))
118
+ else
119
+ builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(nil, visited, nil), token(node.closing_loc))
120
+ end
121
+ else
122
+ builder.array_pattern(token(node.opening_loc), visited, token(node.closing_loc))
123
+ end
124
+ end
125
+
126
+ # foo(bar)
127
+ # ^^^
128
+ def visit_arguments_node(node)
129
+ visit_all(node.arguments)
130
+ end
131
+
132
+ # { a: 1 }
133
+ # ^^^^
134
+ def visit_assoc_node(node)
135
+ key = node.key
136
+
137
+ if node.value.is_a?(ImplicitNode)
138
+ if in_pattern
139
+ if key.is_a?(SymbolNode)
140
+ if key.opening.nil?
141
+ builder.match_hash_var([key.unescaped, srange(key.location)])
142
+ else
143
+ builder.match_hash_var_from_str(token(key.opening_loc), [builder.string_internal([key.unescaped, srange(key.value_loc)])], token(key.closing_loc))
144
+ end
145
+ else
146
+ builder.match_hash_var_from_str(token(key.opening_loc), visit_all(key.parts), token(key.closing_loc))
147
+ end
148
+ else
149
+ value = node.value.value
150
+
151
+ implicit_value = if value.is_a?(CallNode)
152
+ builder.call_method(nil, nil, [value.name, srange(value.message_loc)])
153
+ elsif value.is_a?(ConstantReadNode)
154
+ builder.const([value.name, srange(key.value_loc)])
155
+ else
156
+ builder.ident([value.name, srange(key.value_loc)]).updated(:lvar)
157
+ end
158
+
159
+ builder.pair_keyword([key.unescaped, srange(key)], implicit_value)
160
+ end
161
+ elsif node.operator_loc
162
+ builder.pair(visit(key), token(node.operator_loc), visit(node.value))
163
+ elsif key.is_a?(SymbolNode) && key.opening_loc.nil?
164
+ builder.pair_keyword([key.unescaped, srange(key.location)], visit(node.value))
165
+ else
166
+ parts =
167
+ if key.is_a?(SymbolNode)
168
+ [builder.string_internal([key.unescaped, srange(key.value_loc)])]
169
+ else
170
+ visit_all(key.parts)
171
+ end
172
+
173
+ builder.pair_quoted(token(key.opening_loc), parts, token(key.closing_loc), visit(node.value))
174
+ end
175
+ end
176
+
177
+ # def foo(**); bar(**); end
178
+ # ^^
179
+ #
180
+ # { **foo }
181
+ # ^^^^^
182
+ def visit_assoc_splat_node(node)
183
+ if in_pattern
184
+ builder.match_rest(token(node.operator_loc), token(node.value&.location))
185
+ elsif node.value.nil? && forwarding.include?(:**)
186
+ builder.forwarded_kwrestarg(token(node.operator_loc))
187
+ else
188
+ builder.kwsplat(token(node.operator_loc), visit(node.value))
189
+ end
190
+ end
191
+
192
+ # $+
193
+ # ^^
194
+ def visit_back_reference_read_node(node)
195
+ builder.back_ref(token(node.location))
196
+ end
197
+
198
+ # begin end
199
+ # ^^^^^^^^^
200
+ def visit_begin_node(node)
201
+ rescue_bodies = []
202
+
203
+ if (rescue_clause = node.rescue_clause)
204
+ begin
205
+ find_start_offset = (rescue_clause.reference&.location || rescue_clause.exceptions.last&.location || rescue_clause.keyword_loc).end_offset
206
+ find_end_offset = (
207
+ rescue_clause.statements&.location&.start_offset ||
208
+ rescue_clause.subsequent&.location&.start_offset ||
209
+ node.else_clause&.location&.start_offset ||
210
+ node.ensure_clause&.location&.start_offset ||
211
+ node.end_keyword_loc&.start_offset ||
212
+ find_start_offset + 1
213
+ )
214
+
215
+ rescue_bodies << builder.rescue_body(
216
+ token(rescue_clause.keyword_loc),
217
+ rescue_clause.exceptions.any? ? builder.array(nil, visit_all(rescue_clause.exceptions), nil) : nil,
218
+ token(rescue_clause.operator_loc),
219
+ visit(rescue_clause.reference),
220
+ srange_find(find_start_offset, find_end_offset, ";"),
221
+ visit(rescue_clause.statements)
222
+ )
223
+ end until (rescue_clause = rescue_clause.subsequent).nil?
224
+ end
225
+
226
+ begin_body =
227
+ builder.begin_body(
228
+ visit(node.statements),
229
+ rescue_bodies,
230
+ token(node.else_clause&.else_keyword_loc),
231
+ visit(node.else_clause),
232
+ token(node.ensure_clause&.ensure_keyword_loc),
233
+ visit(node.ensure_clause&.statements)
234
+ )
235
+
236
+ if node.begin_keyword_loc
237
+ builder.begin_keyword(token(node.begin_keyword_loc), begin_body, token(node.end_keyword_loc))
238
+ else
239
+ begin_body
240
+ end
241
+ end
242
+
243
+ # foo(&bar)
244
+ # ^^^^
245
+ def visit_block_argument_node(node)
246
+ builder.block_pass(token(node.operator_loc), visit(node.expression))
247
+ end
248
+
249
+ # foo { |; bar| }
250
+ # ^^^
251
+ def visit_block_local_variable_node(node)
252
+ builder.shadowarg(token(node.location))
253
+ end
254
+
255
+ # A block on a keyword or method call.
256
+ def visit_block_node(node)
257
+ raise CompilationError, "Cannot directly compile block nodes"
258
+ end
259
+
260
+ # def foo(&bar); end
261
+ # ^^^^
262
+ def visit_block_parameter_node(node)
263
+ builder.blockarg(token(node.operator_loc), token(node.name_loc))
264
+ end
265
+
266
+ # A block's parameters.
267
+ def visit_block_parameters_node(node)
268
+ [*visit(node.parameters)].concat(visit_all(node.locals))
269
+ end
270
+
271
+ # break
272
+ # ^^^^^
273
+ #
274
+ # break foo
275
+ # ^^^^^^^^^
276
+ def visit_break_node(node)
277
+ builder.keyword_cmd(:break, token(node.keyword_loc), nil, visit(node.arguments) || [], nil)
278
+ end
279
+
280
+ # foo
281
+ # ^^^
282
+ #
283
+ # foo.bar
284
+ # ^^^^^^^
285
+ #
286
+ # foo.bar() {}
287
+ # ^^^^^^^^^^^^
288
+ def visit_call_node(node)
289
+ name = node.name
290
+ arguments = node.arguments&.arguments || []
291
+ block = node.block
292
+
293
+ if block.is_a?(BlockArgumentNode)
294
+ arguments = [*arguments, block]
295
+ block = nil
296
+ end
297
+
298
+ if node.call_operator_loc.nil?
299
+ case name
300
+ when :-@
301
+ case (receiver = node.receiver).type
302
+ when :integer_node, :float_node, :rational_node, :imaginary_node
303
+ return visit(numeric_negate(node.message_loc, receiver))
304
+ end
305
+ when :!
306
+ return visit_block(builder.not_op(token(node.message_loc), token(node.opening_loc), visit(node.receiver), token(node.closing_loc)), block)
307
+ when :=~
308
+ if (receiver = node.receiver).is_a?(RegularExpressionNode)
309
+ return builder.match_op(visit(receiver), token(node.message_loc), visit(node.arguments.arguments.first))
310
+ end
311
+ when :[]
312
+ return visit_block(builder.index(visit(node.receiver), token(node.opening_loc), visit_all(arguments), token(node.closing_loc)), block)
313
+ when :[]=
314
+ if node.message != "[]=" && node.arguments && block.nil? && !node.safe_navigation?
315
+ arguments = node.arguments.arguments[...-1]
316
+ arguments << node.block if node.block
317
+
318
+ return visit_block(
319
+ builder.assign(
320
+ builder.index_asgn(
321
+ visit(node.receiver),
322
+ token(node.opening_loc),
323
+ visit_all(arguments),
324
+ token(node.closing_loc),
325
+ ),
326
+ srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset, "="),
327
+ visit(node.arguments.arguments.last)
328
+ ),
329
+ block
330
+ )
331
+ end
332
+ end
333
+ end
334
+
335
+ message_loc = node.message_loc
336
+ call_operator_loc = node.call_operator_loc
337
+ call_operator = [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)] if call_operator_loc
338
+
339
+ visit_block(
340
+ if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil?
341
+ builder.assign(
342
+ builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)),
343
+ srange_find(message_loc.end_offset, node.arguments.location.start_offset, "="),
344
+ visit(node.arguments.arguments.last)
345
+ )
346
+ else
347
+ builder.call_method(
348
+ visit(node.receiver),
349
+ call_operator,
350
+ message_loc ? [node.name, srange(message_loc)] : nil,
351
+ token(node.opening_loc),
352
+ visit_all(arguments),
353
+ token(node.closing_loc)
354
+ )
355
+ end,
356
+ block
357
+ )
358
+ end
359
+
360
+ # foo.bar += baz
361
+ # ^^^^^^^^^^^^^^^
362
+ def visit_call_operator_write_node(node)
363
+ call_operator_loc = node.call_operator_loc
364
+
365
+ builder.op_assign(
366
+ builder.call_method(
367
+ visit(node.receiver),
368
+ call_operator_loc.nil? ? nil : [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)],
369
+ node.message_loc ? [node.read_name, srange(node.message_loc)] : nil,
370
+ nil,
371
+ [],
372
+ nil
373
+ ),
374
+ [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)],
375
+ visit(node.value)
376
+ )
377
+ end
378
+
379
+ # foo.bar &&= baz
380
+ # ^^^^^^^^^^^^^^^
381
+ def visit_call_and_write_node(node)
382
+ call_operator_loc = node.call_operator_loc
383
+
384
+ builder.op_assign(
385
+ builder.call_method(
386
+ visit(node.receiver),
387
+ call_operator_loc.nil? ? nil : [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)],
388
+ node.message_loc ? [node.read_name, srange(node.message_loc)] : nil,
389
+ nil,
390
+ [],
391
+ nil
392
+ ),
393
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
394
+ visit(node.value)
395
+ )
396
+ end
397
+
398
+ # foo.bar ||= baz
399
+ # ^^^^^^^^^^^^^^^
400
+ def visit_call_or_write_node(node)
401
+ call_operator_loc = node.call_operator_loc
402
+
403
+ builder.op_assign(
404
+ builder.call_method(
405
+ visit(node.receiver),
406
+ call_operator_loc.nil? ? nil : [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)],
407
+ node.message_loc ? [node.read_name, srange(node.message_loc)] : nil,
408
+ nil,
409
+ [],
410
+ nil
411
+ ),
412
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
413
+ visit(node.value)
414
+ )
415
+ end
416
+
417
+ # foo.bar, = 1
418
+ # ^^^^^^^
419
+ def visit_call_target_node(node)
420
+ call_operator_loc = node.call_operator_loc
421
+
422
+ builder.attr_asgn(
423
+ visit(node.receiver),
424
+ call_operator_loc.nil? ? nil : [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)],
425
+ token(node.message_loc)
426
+ )
427
+ end
428
+
429
+ # foo => bar => baz
430
+ # ^^^^^^^^^^
431
+ def visit_capture_pattern_node(node)
432
+ builder.match_as(visit(node.value), token(node.operator_loc), visit(node.target))
433
+ end
434
+
435
+ # case foo; when bar; end
436
+ # ^^^^^^^^^^^^^^^^^^^^^^^
437
+ def visit_case_node(node)
438
+ builder.case(
439
+ token(node.case_keyword_loc),
440
+ visit(node.predicate),
441
+ visit_all(node.conditions),
442
+ token(node.else_clause&.else_keyword_loc),
443
+ visit(node.else_clause),
444
+ token(node.end_keyword_loc)
445
+ )
446
+ end
447
+
448
+ # case foo; in bar; end
449
+ # ^^^^^^^^^^^^^^^^^^^^^
450
+ def visit_case_match_node(node)
451
+ builder.case_match(
452
+ token(node.case_keyword_loc),
453
+ visit(node.predicate),
454
+ visit_all(node.conditions),
455
+ token(node.else_clause&.else_keyword_loc),
456
+ visit(node.else_clause),
457
+ token(node.end_keyword_loc)
458
+ )
459
+ end
460
+
461
+ # class Foo; end
462
+ # ^^^^^^^^^^^^^^
463
+ def visit_class_node(node)
464
+ builder.def_class(
465
+ token(node.class_keyword_loc),
466
+ visit(node.constant_path),
467
+ token(node.inheritance_operator_loc),
468
+ visit(node.superclass),
469
+ node.body&.accept(copy_compiler(forwarding: [])),
470
+ token(node.end_keyword_loc)
471
+ )
472
+ end
473
+
474
+ # @@foo
475
+ # ^^^^^
476
+ def visit_class_variable_read_node(node)
477
+ builder.cvar(token(node.location))
478
+ end
479
+
480
+ # @@foo = 1
481
+ # ^^^^^^^^^
482
+ def visit_class_variable_write_node(node)
483
+ builder.assign(
484
+ builder.assignable(builder.cvar(token(node.name_loc))),
485
+ token(node.operator_loc),
486
+ visit(node.value)
487
+ )
488
+ end
489
+
490
+ # @@foo += bar
491
+ # ^^^^^^^^^^^^
492
+ def visit_class_variable_operator_write_node(node)
493
+ builder.op_assign(
494
+ builder.assignable(builder.cvar(token(node.name_loc))),
495
+ [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)],
496
+ visit(node.value)
497
+ )
498
+ end
499
+
500
+ # @@foo &&= bar
501
+ # ^^^^^^^^^^^^^
502
+ def visit_class_variable_and_write_node(node)
503
+ builder.op_assign(
504
+ builder.assignable(builder.cvar(token(node.name_loc))),
505
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
506
+ visit(node.value)
507
+ )
508
+ end
509
+
510
+ # @@foo ||= bar
511
+ # ^^^^^^^^^^^^^
512
+ def visit_class_variable_or_write_node(node)
513
+ builder.op_assign(
514
+ builder.assignable(builder.cvar(token(node.name_loc))),
515
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
516
+ visit(node.value)
517
+ )
518
+ end
519
+
520
+ # @@foo, = bar
521
+ # ^^^^^
522
+ def visit_class_variable_target_node(node)
523
+ builder.assignable(builder.cvar(token(node.location)))
524
+ end
525
+
526
+ # Foo
527
+ # ^^^
528
+ def visit_constant_read_node(node)
529
+ builder.const([node.name, srange(node.location)])
530
+ end
531
+
532
+ # Foo = 1
533
+ # ^^^^^^^
534
+ #
535
+ # Foo, Bar = 1
536
+ # ^^^ ^^^
537
+ def visit_constant_write_node(node)
538
+ builder.assign(builder.assignable(builder.const([node.name, srange(node.name_loc)])), token(node.operator_loc), visit(node.value))
539
+ end
540
+
541
+ # Foo += bar
542
+ # ^^^^^^^^^^^
543
+ def visit_constant_operator_write_node(node)
544
+ builder.op_assign(
545
+ builder.assignable(builder.const([node.name, srange(node.name_loc)])),
546
+ [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)],
547
+ visit(node.value)
548
+ )
549
+ end
550
+
551
+ # Foo &&= bar
552
+ # ^^^^^^^^^^^^
553
+ def visit_constant_and_write_node(node)
554
+ builder.op_assign(
555
+ builder.assignable(builder.const([node.name, srange(node.name_loc)])),
556
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
557
+ visit(node.value)
558
+ )
559
+ end
560
+
561
+ # Foo ||= bar
562
+ # ^^^^^^^^^^^^
563
+ def visit_constant_or_write_node(node)
564
+ builder.op_assign(
565
+ builder.assignable(builder.const([node.name, srange(node.name_loc)])),
566
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
567
+ visit(node.value)
568
+ )
569
+ end
570
+
571
+ # Foo, = bar
572
+ # ^^^
573
+ def visit_constant_target_node(node)
574
+ builder.assignable(builder.const([node.name, srange(node.location)]))
575
+ end
576
+
577
+ # Foo::Bar
578
+ # ^^^^^^^^
579
+ def visit_constant_path_node(node)
580
+ if node.parent.nil?
581
+ builder.const_global(
582
+ token(node.delimiter_loc),
583
+ [node.name, srange(node.name_loc)]
584
+ )
585
+ else
586
+ builder.const_fetch(
587
+ visit(node.parent),
588
+ token(node.delimiter_loc),
589
+ [node.name, srange(node.name_loc)]
590
+ )
591
+ end
592
+ end
593
+
594
+ # Foo::Bar = 1
595
+ # ^^^^^^^^^^^^
596
+ #
597
+ # Foo::Foo, Bar::Bar = 1
598
+ # ^^^^^^^^ ^^^^^^^^
599
+ def visit_constant_path_write_node(node)
600
+ builder.assign(
601
+ builder.assignable(visit(node.target)),
602
+ token(node.operator_loc),
603
+ visit(node.value)
604
+ )
605
+ end
606
+
607
+ # Foo::Bar += baz
608
+ # ^^^^^^^^^^^^^^^
609
+ def visit_constant_path_operator_write_node(node)
610
+ builder.op_assign(
611
+ builder.assignable(visit(node.target)),
612
+ [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)],
613
+ visit(node.value)
614
+ )
615
+ end
616
+
617
+ # Foo::Bar &&= baz
618
+ # ^^^^^^^^^^^^^^^^
619
+ def visit_constant_path_and_write_node(node)
620
+ builder.op_assign(
621
+ builder.assignable(visit(node.target)),
622
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
623
+ visit(node.value)
624
+ )
625
+ end
626
+
627
+ # Foo::Bar ||= baz
628
+ # ^^^^^^^^^^^^^^^^
629
+ def visit_constant_path_or_write_node(node)
630
+ builder.op_assign(
631
+ builder.assignable(visit(node.target)),
632
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
633
+ visit(node.value)
634
+ )
635
+ end
636
+
637
+ # Foo::Bar, = baz
638
+ # ^^^^^^^^
639
+ def visit_constant_path_target_node(node)
640
+ builder.assignable(visit_constant_path_node(node))
641
+ end
642
+
643
+ # def foo; end
644
+ # ^^^^^^^^^^^^
645
+ #
646
+ # def self.foo; end
647
+ # ^^^^^^^^^^^^^^^^^
648
+ def visit_def_node(node)
649
+ if node.equal_loc
650
+ if node.receiver
651
+ builder.def_endless_singleton(
652
+ token(node.def_keyword_loc),
653
+ visit(node.receiver.is_a?(ParenthesesNode) ? node.receiver.body : node.receiver),
654
+ token(node.operator_loc),
655
+ token(node.name_loc),
656
+ builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
657
+ token(node.equal_loc),
658
+ node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters)))
659
+ )
660
+ else
661
+ builder.def_endless_method(
662
+ token(node.def_keyword_loc),
663
+ token(node.name_loc),
664
+ builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
665
+ token(node.equal_loc),
666
+ node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters)))
667
+ )
668
+ end
669
+ elsif node.receiver
670
+ builder.def_singleton(
671
+ token(node.def_keyword_loc),
672
+ visit(node.receiver.is_a?(ParenthesesNode) ? node.receiver.body : node.receiver),
673
+ token(node.operator_loc),
674
+ token(node.name_loc),
675
+ builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
676
+ node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))),
677
+ token(node.end_keyword_loc)
678
+ )
679
+ else
680
+ builder.def_method(
681
+ token(node.def_keyword_loc),
682
+ token(node.name_loc),
683
+ builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
684
+ node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))),
685
+ token(node.end_keyword_loc)
686
+ )
687
+ end
688
+ end
689
+
690
+ # defined? a
691
+ # ^^^^^^^^^^
692
+ #
693
+ # defined?(a)
694
+ # ^^^^^^^^^^^
695
+ def visit_defined_node(node)
696
+ # Very weird circumstances here where something like:
697
+ #
698
+ # defined?
699
+ # (1)
700
+ #
701
+ # gets parsed in Ruby as having only the `1` expression but in parser
702
+ # it gets parsed as having a begin. In this case we need to synthesize
703
+ # that begin to match parser's behavior.
704
+ if node.lparen_loc && node.keyword_loc.join(node.lparen_loc).slice.include?("\n")
705
+ builder.keyword_cmd(
706
+ :defined?,
707
+ token(node.keyword_loc),
708
+ nil,
709
+ [
710
+ builder.begin(
711
+ token(node.lparen_loc),
712
+ visit(node.value),
713
+ token(node.rparen_loc)
714
+ )
715
+ ],
716
+ nil
717
+ )
718
+ else
719
+ builder.keyword_cmd(
720
+ :defined?,
721
+ token(node.keyword_loc),
722
+ token(node.lparen_loc),
723
+ [visit(node.value)],
724
+ token(node.rparen_loc)
725
+ )
726
+ end
727
+ end
728
+
729
+ # if foo then bar else baz end
730
+ # ^^^^^^^^^^^^
731
+ def visit_else_node(node)
732
+ visit(node.statements)
733
+ end
734
+
735
+ # "foo #{bar}"
736
+ # ^^^^^^
737
+ def visit_embedded_statements_node(node)
738
+ builder.begin(
739
+ token(node.opening_loc),
740
+ visit(node.statements),
741
+ token(node.closing_loc)
742
+ )
743
+ end
744
+
745
+ # "foo #@bar"
746
+ # ^^^^^
747
+ def visit_embedded_variable_node(node)
748
+ visit(node.variable)
749
+ end
750
+
751
+ # begin; foo; ensure; bar; end
752
+ # ^^^^^^^^^^^^
753
+ def visit_ensure_node(node)
754
+ raise CompilationError, "Cannot directly compile ensure nodes"
755
+ end
756
+
757
+ # false
758
+ # ^^^^^
759
+ def visit_false_node(node)
760
+ builder.false(token(node.location))
761
+ end
762
+
763
+ # foo => [*, bar, *]
764
+ # ^^^^^^^^^^^
765
+ def visit_find_pattern_node(node)
766
+ elements = [node.left, *node.requireds, node.right]
767
+
768
+ if node.constant
769
+ builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.find_pattern(nil, visit_all(elements), nil), token(node.closing_loc))
770
+ else
771
+ builder.find_pattern(token(node.opening_loc), visit_all(elements), token(node.closing_loc))
772
+ end
773
+ end
774
+
775
+ # 1.0
776
+ # ^^^
777
+ def visit_float_node(node)
778
+ visit_numeric(node, builder.float([node.value, srange(node.location)]))
779
+ end
780
+
781
+ # for foo in bar do end
782
+ # ^^^^^^^^^^^^^^^^^^^^^
783
+ def visit_for_node(node)
784
+ builder.for(
785
+ token(node.for_keyword_loc),
786
+ visit(node.index),
787
+ token(node.in_keyword_loc),
788
+ visit(node.collection),
789
+ if (do_keyword_loc = node.do_keyword_loc)
790
+ token(do_keyword_loc)
791
+ else
792
+ srange_find(node.collection.location.end_offset, (node.statements&.location || node.end_keyword_loc).start_offset, ";")
793
+ end,
794
+ visit(node.statements),
795
+ token(node.end_keyword_loc)
796
+ )
797
+ end
798
+
799
+ # def foo(...); bar(...); end
800
+ # ^^^
801
+ def visit_forwarding_arguments_node(node)
802
+ builder.forwarded_args(token(node.location))
803
+ end
804
+
805
+ # def foo(...); end
806
+ # ^^^
807
+ def visit_forwarding_parameter_node(node)
808
+ builder.forward_arg(token(node.location))
809
+ end
810
+
811
+ # super
812
+ # ^^^^^
813
+ #
814
+ # super {}
815
+ # ^^^^^^^^
816
+ def visit_forwarding_super_node(node)
817
+ visit_block(
818
+ builder.keyword_cmd(
819
+ :zsuper,
820
+ ["super", srange_offsets(node.location.start_offset, node.location.start_offset + 5)]
821
+ ),
822
+ node.block
823
+ )
824
+ end
825
+
826
+ # $foo
827
+ # ^^^^
828
+ def visit_global_variable_read_node(node)
829
+ builder.gvar(token(node.location))
830
+ end
831
+
832
+ # $foo = 1
833
+ # ^^^^^^^^
834
+ def visit_global_variable_write_node(node)
835
+ builder.assign(
836
+ builder.assignable(builder.gvar(token(node.name_loc))),
837
+ token(node.operator_loc),
838
+ visit(node.value)
839
+ )
840
+ end
841
+
842
+ # $foo += bar
843
+ # ^^^^^^^^^^^
844
+ def visit_global_variable_operator_write_node(node)
845
+ builder.op_assign(
846
+ builder.assignable(builder.gvar(token(node.name_loc))),
847
+ [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)],
848
+ visit(node.value)
849
+ )
850
+ end
851
+
852
+ # $foo &&= bar
853
+ # ^^^^^^^^^^^^
854
+ def visit_global_variable_and_write_node(node)
855
+ builder.op_assign(
856
+ builder.assignable(builder.gvar(token(node.name_loc))),
857
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
858
+ visit(node.value)
859
+ )
860
+ end
861
+
862
+ # $foo ||= bar
863
+ # ^^^^^^^^^^^^
864
+ def visit_global_variable_or_write_node(node)
865
+ builder.op_assign(
866
+ builder.assignable(builder.gvar(token(node.name_loc))),
867
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
868
+ visit(node.value)
869
+ )
870
+ end
871
+
872
+ # $foo, = bar
873
+ # ^^^^
874
+ def visit_global_variable_target_node(node)
875
+ builder.assignable(builder.gvar([node.slice, srange(node.location)]))
876
+ end
877
+
878
+ # {}
879
+ # ^^
880
+ def visit_hash_node(node)
881
+ builder.associate(
882
+ token(node.opening_loc),
883
+ visit_all(node.elements),
884
+ token(node.closing_loc)
885
+ )
886
+ end
887
+
888
+ # foo => {}
889
+ # ^^
890
+ def visit_hash_pattern_node(node)
891
+ elements = [*node.elements, *node.rest]
892
+
893
+ if node.constant
894
+ builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.hash_pattern(nil, visit_all(elements), nil), token(node.closing_loc))
895
+ else
896
+ builder.hash_pattern(token(node.opening_loc), visit_all(elements), token(node.closing_loc))
897
+ end
898
+ end
899
+
900
+ # if foo then bar end
901
+ # ^^^^^^^^^^^^^^^^^^^
902
+ #
903
+ # bar if foo
904
+ # ^^^^^^^^^^
905
+ #
906
+ # foo ? bar : baz
907
+ # ^^^^^^^^^^^^^^^
908
+ def visit_if_node(node)
909
+ if !node.if_keyword_loc
910
+ builder.ternary(
911
+ visit(node.predicate),
912
+ token(node.then_keyword_loc),
913
+ visit(node.statements),
914
+ token(node.subsequent.else_keyword_loc),
915
+ visit(node.subsequent)
916
+ )
917
+ elsif node.if_keyword_loc.start_offset == node.location.start_offset
918
+ builder.condition(
919
+ token(node.if_keyword_loc),
920
+ visit(node.predicate),
921
+ if (then_keyword_loc = node.then_keyword_loc)
922
+ token(then_keyword_loc)
923
+ else
924
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.subsequent&.location || node.end_keyword_loc).start_offset, ";")
925
+ end,
926
+ visit(node.statements),
927
+ case node.subsequent
928
+ when IfNode
929
+ token(node.subsequent.if_keyword_loc)
930
+ when ElseNode
931
+ token(node.subsequent.else_keyword_loc)
932
+ end,
933
+ visit(node.subsequent),
934
+ if node.if_keyword != "elsif"
935
+ token(node.end_keyword_loc)
936
+ end
937
+ )
938
+ else
939
+ builder.condition_mod(
940
+ visit(node.statements),
941
+ visit(node.subsequent),
942
+ token(node.if_keyword_loc),
943
+ visit(node.predicate)
944
+ )
945
+ end
946
+ end
947
+
948
+ # 1i
949
+ # ^^
950
+ def visit_imaginary_node(node)
951
+ visit_numeric(node, builder.complex([Complex(0, node.numeric.value), srange(node.location)]))
952
+ end
953
+
954
+ # { foo: }
955
+ # ^^^^
956
+ def visit_implicit_node(node)
957
+ raise CompilationError, "Cannot directly compile implicit nodes"
958
+ end
959
+
960
+ # foo { |bar,| }
961
+ # ^
962
+ def visit_implicit_rest_node(node)
963
+ raise CompilationError, "Cannot compile implicit rest nodes"
964
+ end
965
+
966
+ # case foo; in bar; end
967
+ # ^^^^^^^^^^^^^^^^^^^^^
968
+ def visit_in_node(node)
969
+ pattern = nil
970
+ guard = nil
971
+
972
+ case node.pattern
973
+ when IfNode
974
+ pattern = within_pattern { |compiler| node.pattern.statements.accept(compiler) }
975
+ guard = builder.if_guard(token(node.pattern.if_keyword_loc), visit(node.pattern.predicate))
976
+ when UnlessNode
977
+ pattern = within_pattern { |compiler| node.pattern.statements.accept(compiler) }
978
+ guard = builder.unless_guard(token(node.pattern.keyword_loc), visit(node.pattern.predicate))
979
+ else
980
+ pattern = within_pattern { |compiler| node.pattern.accept(compiler) }
981
+ end
982
+
983
+ builder.in_pattern(
984
+ token(node.in_loc),
985
+ pattern,
986
+ guard,
987
+ if (then_loc = node.then_loc)
988
+ token(then_loc)
989
+ else
990
+ srange_find(node.pattern.location.end_offset, node.statements&.location&.start_offset, ";")
991
+ end,
992
+ visit(node.statements)
993
+ )
994
+ end
995
+
996
+ # foo[bar] += baz
997
+ # ^^^^^^^^^^^^^^^
998
+ def visit_index_operator_write_node(node)
999
+ arguments = node.arguments&.arguments || []
1000
+ arguments << node.block if node.block
1001
+
1002
+ builder.op_assign(
1003
+ builder.index(
1004
+ visit(node.receiver),
1005
+ token(node.opening_loc),
1006
+ visit_all(arguments),
1007
+ token(node.closing_loc)
1008
+ ),
1009
+ [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)],
1010
+ visit(node.value)
1011
+ )
1012
+ end
1013
+
1014
+ # foo[bar] &&= baz
1015
+ # ^^^^^^^^^^^^^^^^
1016
+ def visit_index_and_write_node(node)
1017
+ arguments = node.arguments&.arguments || []
1018
+ arguments << node.block if node.block
1019
+
1020
+ builder.op_assign(
1021
+ builder.index(
1022
+ visit(node.receiver),
1023
+ token(node.opening_loc),
1024
+ visit_all(arguments),
1025
+ token(node.closing_loc)
1026
+ ),
1027
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
1028
+ visit(node.value)
1029
+ )
1030
+ end
1031
+
1032
+ # foo[bar] ||= baz
1033
+ # ^^^^^^^^^^^^^^^^
1034
+ def visit_index_or_write_node(node)
1035
+ arguments = node.arguments&.arguments || []
1036
+ arguments << node.block if node.block
1037
+
1038
+ builder.op_assign(
1039
+ builder.index(
1040
+ visit(node.receiver),
1041
+ token(node.opening_loc),
1042
+ visit_all(arguments),
1043
+ token(node.closing_loc)
1044
+ ),
1045
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
1046
+ visit(node.value)
1047
+ )
1048
+ end
1049
+
1050
+ # foo[bar], = 1
1051
+ # ^^^^^^^^
1052
+ def visit_index_target_node(node)
1053
+ builder.index_asgn(
1054
+ visit(node.receiver),
1055
+ token(node.opening_loc),
1056
+ visit_all(node.arguments&.arguments || []),
1057
+ token(node.closing_loc),
1058
+ )
1059
+ end
1060
+
1061
+ # @foo
1062
+ # ^^^^
1063
+ def visit_instance_variable_read_node(node)
1064
+ builder.ivar(token(node.location))
1065
+ end
1066
+
1067
+ # @foo = 1
1068
+ # ^^^^^^^^
1069
+ def visit_instance_variable_write_node(node)
1070
+ builder.assign(
1071
+ builder.assignable(builder.ivar(token(node.name_loc))),
1072
+ token(node.operator_loc),
1073
+ visit(node.value)
1074
+ )
1075
+ end
1076
+
1077
+ # @foo += bar
1078
+ # ^^^^^^^^^^^
1079
+ def visit_instance_variable_operator_write_node(node)
1080
+ builder.op_assign(
1081
+ builder.assignable(builder.ivar(token(node.name_loc))),
1082
+ [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)],
1083
+ visit(node.value)
1084
+ )
1085
+ end
1086
+
1087
+ # @foo &&= bar
1088
+ # ^^^^^^^^^^^^
1089
+ def visit_instance_variable_and_write_node(node)
1090
+ builder.op_assign(
1091
+ builder.assignable(builder.ivar(token(node.name_loc))),
1092
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
1093
+ visit(node.value)
1094
+ )
1095
+ end
1096
+
1097
+ # @foo ||= bar
1098
+ # ^^^^^^^^^^^^
1099
+ def visit_instance_variable_or_write_node(node)
1100
+ builder.op_assign(
1101
+ builder.assignable(builder.ivar(token(node.name_loc))),
1102
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
1103
+ visit(node.value)
1104
+ )
1105
+ end
1106
+
1107
+ # @foo, = bar
1108
+ # ^^^^
1109
+ def visit_instance_variable_target_node(node)
1110
+ builder.assignable(builder.ivar(token(node.location)))
1111
+ end
1112
+
1113
+ # 1
1114
+ # ^
1115
+ def visit_integer_node(node)
1116
+ visit_numeric(node, builder.integer([node.value, srange(node.location)]))
1117
+ end
1118
+
1119
+ # /foo #{bar}/
1120
+ # ^^^^^^^^^^^^
1121
+ def visit_interpolated_regular_expression_node(node)
1122
+ builder.regexp_compose(
1123
+ token(node.opening_loc),
1124
+ string_nodes_from_interpolation(node, node.opening),
1125
+ [node.closing[0], srange_offsets(node.closing_loc.start_offset, node.closing_loc.start_offset + 1)],
1126
+ builder.regexp_options([node.closing[1..], srange_offsets(node.closing_loc.start_offset + 1, node.closing_loc.end_offset)])
1127
+ )
1128
+ end
1129
+
1130
+ # if /foo #{bar}/ then end
1131
+ # ^^^^^^^^^^^^
1132
+ alias visit_interpolated_match_last_line_node visit_interpolated_regular_expression_node
1133
+
1134
+ # "foo #{bar}"
1135
+ # ^^^^^^^^^^^^
1136
+ def visit_interpolated_string_node(node)
1137
+ if node.heredoc?
1138
+ return visit_heredoc(node) { |children, closing| builder.string_compose(token(node.opening_loc), children, closing) }
1139
+ end
1140
+
1141
+ builder.string_compose(
1142
+ token(node.opening_loc),
1143
+ string_nodes_from_interpolation(node, node.opening),
1144
+ token(node.closing_loc)
1145
+ )
1146
+ end
1147
+
1148
+ # :"foo #{bar}"
1149
+ # ^^^^^^^^^^^^^
1150
+ def visit_interpolated_symbol_node(node)
1151
+ builder.symbol_compose(
1152
+ token(node.opening_loc),
1153
+ string_nodes_from_interpolation(node, node.opening),
1154
+ token(node.closing_loc)
1155
+ )
1156
+ end
1157
+
1158
+ # `foo #{bar}`
1159
+ # ^^^^^^^^^^^^
1160
+ def visit_interpolated_x_string_node(node)
1161
+ if node.heredoc?
1162
+ return visit_heredoc(node) { |children, closing| builder.xstring_compose(token(node.opening_loc), children, closing) }
1163
+ end
1164
+
1165
+ builder.xstring_compose(
1166
+ token(node.opening_loc),
1167
+ string_nodes_from_interpolation(node, node.opening),
1168
+ token(node.closing_loc)
1169
+ )
1170
+ end
1171
+
1172
+ # -> { it }
1173
+ # ^^
1174
+ def visit_it_local_variable_read_node(node)
1175
+ builder.ident([:it, srange(node.location)]).updated(:lvar)
1176
+ end
1177
+
1178
+ # -> { it }
1179
+ # ^^^^^^^^^
1180
+ def visit_it_parameters_node(node)
1181
+ # FIXME: The builder _should_ always be a subclass of the prism builder.
1182
+ # Currently RuboCop passes in its own builder that always inherits from the
1183
+ # parser builder (which is lacking the `itarg` method). Once rubocop-ast
1184
+ # opts in to use the custom prism builder a warning can be emitted when
1185
+ # it is not the expected class, and eventually raise.
1186
+ # https://github.com/rubocop/rubocop-ast/pull/354
1187
+ if builder.is_a?(Translation::Parser::Builder)
1188
+ builder.itarg
1189
+ else
1190
+ builder.args(nil, [], nil, false)
1191
+ end
1192
+ end
1193
+
1194
+ # foo(bar: baz)
1195
+ # ^^^^^^^^
1196
+ def visit_keyword_hash_node(node)
1197
+ builder.associate(nil, visit_all(node.elements), nil)
1198
+ end
1199
+
1200
+ # def foo(**bar); end
1201
+ # ^^^^^
1202
+ #
1203
+ # def foo(**); end
1204
+ # ^^
1205
+ def visit_keyword_rest_parameter_node(node)
1206
+ builder.kwrestarg(
1207
+ token(node.operator_loc),
1208
+ node.name ? [node.name, srange(node.name_loc)] : nil
1209
+ )
1210
+ end
1211
+
1212
+ # -> {}
1213
+ # ^^^^^
1214
+ def visit_lambda_node(node)
1215
+ parameters = node.parameters
1216
+ implicit_parameters = parameters.is_a?(NumberedParametersNode) || parameters.is_a?(ItParametersNode)
1217
+
1218
+ builder.block(
1219
+ builder.call_lambda(token(node.operator_loc)),
1220
+ [node.opening, srange(node.opening_loc)],
1221
+ if parameters.nil?
1222
+ builder.args(nil, [], nil, false)
1223
+ elsif implicit_parameters
1224
+ visit(node.parameters)
1225
+ else
1226
+ builder.args(
1227
+ token(node.parameters.opening_loc),
1228
+ visit(node.parameters),
1229
+ token(node.parameters.closing_loc),
1230
+ false
1231
+ )
1232
+ end,
1233
+ visit(node.body),
1234
+ [node.closing, srange(node.closing_loc)]
1235
+ )
1236
+ end
1237
+
1238
+ # foo
1239
+ # ^^^
1240
+ def visit_local_variable_read_node(node)
1241
+ builder.ident([node.name, srange(node.location)]).updated(:lvar)
1242
+ end
1243
+
1244
+ # foo = 1
1245
+ # ^^^^^^^
1246
+ def visit_local_variable_write_node(node)
1247
+ builder.assign(
1248
+ builder.assignable(builder.ident(token(node.name_loc))),
1249
+ token(node.operator_loc),
1250
+ visit(node.value)
1251
+ )
1252
+ end
1253
+
1254
+ # foo += bar
1255
+ # ^^^^^^^^^^
1256
+ def visit_local_variable_operator_write_node(node)
1257
+ builder.op_assign(
1258
+ builder.assignable(builder.ident(token(node.name_loc))),
1259
+ [node.binary_operator_loc.slice.chomp("="), srange(node.binary_operator_loc)],
1260
+ visit(node.value)
1261
+ )
1262
+ end
1263
+
1264
+ # foo &&= bar
1265
+ # ^^^^^^^^^^^
1266
+ def visit_local_variable_and_write_node(node)
1267
+ builder.op_assign(
1268
+ builder.assignable(builder.ident(token(node.name_loc))),
1269
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
1270
+ visit(node.value)
1271
+ )
1272
+ end
1273
+
1274
+ # foo ||= bar
1275
+ # ^^^^^^^^^^^
1276
+ def visit_local_variable_or_write_node(node)
1277
+ builder.op_assign(
1278
+ builder.assignable(builder.ident(token(node.name_loc))),
1279
+ [node.operator_loc.slice.chomp("="), srange(node.operator_loc)],
1280
+ visit(node.value)
1281
+ )
1282
+ end
1283
+
1284
+ # foo, = bar
1285
+ # ^^^
1286
+ def visit_local_variable_target_node(node)
1287
+ if in_pattern
1288
+ builder.assignable(builder.match_var([node.name, srange(node.location)]))
1289
+ else
1290
+ builder.assignable(builder.ident(token(node.location)))
1291
+ end
1292
+ end
1293
+
1294
+ # foo in bar
1295
+ # ^^^^^^^^^^
1296
+ def visit_match_predicate_node(node)
1297
+ builder.match_pattern_p(
1298
+ visit(node.value),
1299
+ token(node.operator_loc),
1300
+ within_pattern { |compiler| node.pattern.accept(compiler) }
1301
+ )
1302
+ end
1303
+
1304
+ # foo => bar
1305
+ # ^^^^^^^^^^
1306
+ def visit_match_required_node(node)
1307
+ builder.match_pattern(
1308
+ visit(node.value),
1309
+ token(node.operator_loc),
1310
+ within_pattern { |compiler| node.pattern.accept(compiler) }
1311
+ )
1312
+ end
1313
+
1314
+ # /(?<foo>foo)/ =~ bar
1315
+ # ^^^^^^^^^^^^^^^^^^^^
1316
+ def visit_match_write_node(node)
1317
+ builder.match_op(
1318
+ visit(node.call.receiver),
1319
+ token(node.call.message_loc),
1320
+ visit(node.call.arguments.arguments.first)
1321
+ )
1322
+ end
1323
+
1324
+ # A node that is missing from the syntax tree. This is only used in the
1325
+ # case of a syntax error. The parser gem doesn't have such a concept, so
1326
+ # we invent our own here.
1327
+ def visit_missing_node(node)
1328
+ ::AST::Node.new(:missing, [], location: ::Parser::Source::Map.new(srange(node.location)))
1329
+ end
1330
+
1331
+ # module Foo; end
1332
+ # ^^^^^^^^^^^^^^^
1333
+ def visit_module_node(node)
1334
+ builder.def_module(
1335
+ token(node.module_keyword_loc),
1336
+ visit(node.constant_path),
1337
+ node.body&.accept(copy_compiler(forwarding: [])),
1338
+ token(node.end_keyword_loc)
1339
+ )
1340
+ end
1341
+
1342
+ # foo, bar = baz
1343
+ # ^^^^^^^^
1344
+ def visit_multi_target_node(node)
1345
+ builder.multi_lhs(
1346
+ token(node.lparen_loc),
1347
+ visit_all(multi_target_elements(node)),
1348
+ token(node.rparen_loc)
1349
+ )
1350
+ end
1351
+
1352
+ # foo, bar = baz
1353
+ # ^^^^^^^^^^^^^^
1354
+ def visit_multi_write_node(node)
1355
+ elements = multi_target_elements(node)
1356
+
1357
+ if elements.length == 1 && elements.first.is_a?(MultiTargetNode) && !node.rest
1358
+ elements = multi_target_elements(elements.first)
1359
+ end
1360
+
1361
+ builder.multi_assign(
1362
+ builder.multi_lhs(
1363
+ token(node.lparen_loc),
1364
+ visit_all(elements),
1365
+ token(node.rparen_loc)
1366
+ ),
1367
+ token(node.operator_loc),
1368
+ visit(node.value)
1369
+ )
1370
+ end
1371
+
1372
+ # next
1373
+ # ^^^^
1374
+ #
1375
+ # next foo
1376
+ # ^^^^^^^^
1377
+ def visit_next_node(node)
1378
+ builder.keyword_cmd(
1379
+ :next,
1380
+ token(node.keyword_loc),
1381
+ nil,
1382
+ visit(node.arguments) || [],
1383
+ nil
1384
+ )
1385
+ end
1386
+
1387
+ # nil
1388
+ # ^^^
1389
+ def visit_nil_node(node)
1390
+ builder.nil(token(node.location))
1391
+ end
1392
+
1393
+ # def foo(**nil); end
1394
+ # ^^^^^
1395
+ def visit_no_keywords_parameter_node(node)
1396
+ if in_pattern
1397
+ builder.match_nil_pattern(token(node.operator_loc), token(node.keyword_loc))
1398
+ else
1399
+ builder.kwnilarg(token(node.operator_loc), token(node.keyword_loc))
1400
+ end
1401
+ end
1402
+
1403
+ # -> { _1 + _2 }
1404
+ # ^^^^^^^^^^^^^^
1405
+ def visit_numbered_parameters_node(node)
1406
+ builder.numargs(node.maximum)
1407
+ end
1408
+
1409
+ # $1
1410
+ # ^^
1411
+ def visit_numbered_reference_read_node(node)
1412
+ builder.nth_ref([node.number, srange(node.location)])
1413
+ end
1414
+
1415
+ # def foo(bar: baz); end
1416
+ # ^^^^^^^^
1417
+ def visit_optional_keyword_parameter_node(node)
1418
+ builder.kwoptarg([node.name, srange(node.name_loc)], visit(node.value))
1419
+ end
1420
+
1421
+ # def foo(bar = 1); end
1422
+ # ^^^^^^^
1423
+ def visit_optional_parameter_node(node)
1424
+ builder.optarg(token(node.name_loc), token(node.operator_loc), visit(node.value))
1425
+ end
1426
+
1427
+ # a or b
1428
+ # ^^^^^^
1429
+ def visit_or_node(node)
1430
+ builder.logical_op(:or, visit(node.left), token(node.operator_loc), visit(node.right))
1431
+ end
1432
+
1433
+ # def foo(bar, *baz); end
1434
+ # ^^^^^^^^^
1435
+ def visit_parameters_node(node)
1436
+ params = []
1437
+
1438
+ if node.requireds.any?
1439
+ node.requireds.each do |required|
1440
+ params <<
1441
+ if required.is_a?(RequiredParameterNode)
1442
+ visit(required)
1443
+ else
1444
+ required.accept(copy_compiler(in_destructure: true))
1445
+ end
1446
+ end
1447
+ end
1448
+
1449
+ params.concat(visit_all(node.optionals)) if node.optionals.any?
1450
+ params << visit(node.rest) if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
1451
+
1452
+ if node.posts.any?
1453
+ node.posts.each do |post|
1454
+ params <<
1455
+ if post.is_a?(RequiredParameterNode)
1456
+ visit(post)
1457
+ else
1458
+ post.accept(copy_compiler(in_destructure: true))
1459
+ end
1460
+ end
1461
+ end
1462
+
1463
+ params.concat(visit_all(node.keywords)) if node.keywords.any?
1464
+ params << visit(node.keyword_rest) if !node.keyword_rest.nil?
1465
+ params << visit(node.block) if !node.block.nil?
1466
+ params
1467
+ end
1468
+
1469
+ # ()
1470
+ # ^^
1471
+ #
1472
+ # (1)
1473
+ # ^^^
1474
+ def visit_parentheses_node(node)
1475
+ builder.begin(
1476
+ token(node.opening_loc),
1477
+ visit(node.body),
1478
+ token(node.closing_loc)
1479
+ )
1480
+ end
1481
+
1482
+ # foo => ^(bar)
1483
+ # ^^^^^^
1484
+ def visit_pinned_expression_node(node)
1485
+ parts = node.expression.accept(copy_compiler(in_pattern: false)) # Don't treat * and similar as match_rest
1486
+ expression = builder.begin(token(node.lparen_loc), parts, token(node.rparen_loc))
1487
+ builder.pin(token(node.operator_loc), expression)
1488
+ end
1489
+
1490
+ # foo = 1 and bar => ^foo
1491
+ # ^^^^
1492
+ def visit_pinned_variable_node(node)
1493
+ builder.pin(token(node.operator_loc), visit(node.variable))
1494
+ end
1495
+
1496
+ # END {}
1497
+ def visit_post_execution_node(node)
1498
+ builder.postexe(
1499
+ token(node.keyword_loc),
1500
+ token(node.opening_loc),
1501
+ visit(node.statements),
1502
+ token(node.closing_loc)
1503
+ )
1504
+ end
1505
+
1506
+ # BEGIN {}
1507
+ def visit_pre_execution_node(node)
1508
+ builder.preexe(
1509
+ token(node.keyword_loc),
1510
+ token(node.opening_loc),
1511
+ visit(node.statements),
1512
+ token(node.closing_loc)
1513
+ )
1514
+ end
1515
+
1516
+ # The top-level program node.
1517
+ def visit_program_node(node)
1518
+ visit(node.statements)
1519
+ end
1520
+
1521
+ # 0..5
1522
+ # ^^^^
1523
+ def visit_range_node(node)
1524
+ if node.exclude_end?
1525
+ builder.range_exclusive(
1526
+ visit(node.left),
1527
+ token(node.operator_loc),
1528
+ visit(node.right)
1529
+ )
1530
+ else
1531
+ builder.range_inclusive(
1532
+ visit(node.left),
1533
+ token(node.operator_loc),
1534
+ visit(node.right)
1535
+ )
1536
+ end
1537
+ end
1538
+
1539
+ # if foo .. bar; end
1540
+ # ^^^^^^^^^^
1541
+ alias visit_flip_flop_node visit_range_node
1542
+
1543
+ # 1r
1544
+ # ^^
1545
+ def visit_rational_node(node)
1546
+ visit_numeric(node, builder.rational([node.value, srange(node.location)]))
1547
+ end
1548
+
1549
+ # redo
1550
+ # ^^^^
1551
+ def visit_redo_node(node)
1552
+ builder.keyword_cmd(:redo, token(node.location))
1553
+ end
1554
+
1555
+ # /foo/
1556
+ # ^^^^^
1557
+ def visit_regular_expression_node(node)
1558
+ parts =
1559
+ if node.content == ""
1560
+ []
1561
+ elsif node.content.include?("\n")
1562
+ string_nodes_from_line_continuations(node.unescaped, node.content, node.content_loc.start_offset, node.opening)
1563
+ else
1564
+ [builder.string_internal([node.unescaped, srange(node.content_loc)])]
1565
+ end
1566
+
1567
+ builder.regexp_compose(
1568
+ token(node.opening_loc),
1569
+ parts,
1570
+ [node.closing[0], srange_offsets(node.closing_loc.start_offset, node.closing_loc.start_offset + 1)],
1571
+ builder.regexp_options([node.closing[1..], srange_offsets(node.closing_loc.start_offset + 1, node.closing_loc.end_offset)])
1572
+ )
1573
+ end
1574
+
1575
+ # if /foo/ then end
1576
+ # ^^^^^
1577
+ alias visit_match_last_line_node visit_regular_expression_node
1578
+
1579
+ # def foo(bar:); end
1580
+ # ^^^^
1581
+ def visit_required_keyword_parameter_node(node)
1582
+ builder.kwarg([node.name, srange(node.name_loc)])
1583
+ end
1584
+
1585
+ # def foo(bar); end
1586
+ # ^^^
1587
+ def visit_required_parameter_node(node)
1588
+ builder.arg(token(node.location))
1589
+ end
1590
+
1591
+ # foo rescue bar
1592
+ # ^^^^^^^^^^^^^^
1593
+ def visit_rescue_modifier_node(node)
1594
+ builder.begin_body(
1595
+ visit(node.expression),
1596
+ [
1597
+ builder.rescue_body(
1598
+ token(node.keyword_loc),
1599
+ nil,
1600
+ nil,
1601
+ nil,
1602
+ nil,
1603
+ visit(node.rescue_expression)
1604
+ )
1605
+ ]
1606
+ )
1607
+ end
1608
+
1609
+ # begin; rescue; end
1610
+ # ^^^^^^^
1611
+ def visit_rescue_node(node)
1612
+ raise CompilationError, "Cannot directly compile rescue nodes"
1613
+ end
1614
+
1615
+ # def foo(*bar); end
1616
+ # ^^^^
1617
+ #
1618
+ # def foo(*); end
1619
+ # ^
1620
+ def visit_rest_parameter_node(node)
1621
+ builder.restarg(token(node.operator_loc), token(node.name_loc))
1622
+ end
1623
+
1624
+ # retry
1625
+ # ^^^^^
1626
+ def visit_retry_node(node)
1627
+ builder.keyword_cmd(:retry, token(node.location))
1628
+ end
1629
+
1630
+ # return
1631
+ # ^^^^^^
1632
+ #
1633
+ # return 1
1634
+ # ^^^^^^^^
1635
+ def visit_return_node(node)
1636
+ builder.keyword_cmd(
1637
+ :return,
1638
+ token(node.keyword_loc),
1639
+ nil,
1640
+ visit(node.arguments) || [],
1641
+ nil
1642
+ )
1643
+ end
1644
+
1645
+ # self
1646
+ # ^^^^
1647
+ def visit_self_node(node)
1648
+ builder.self(token(node.location))
1649
+ end
1650
+
1651
+ # A shareable constant.
1652
+ def visit_shareable_constant_node(node)
1653
+ visit(node.write)
1654
+ end
1655
+
1656
+ # class << self; end
1657
+ # ^^^^^^^^^^^^^^^^^^
1658
+ def visit_singleton_class_node(node)
1659
+ builder.def_sclass(
1660
+ token(node.class_keyword_loc),
1661
+ token(node.operator_loc),
1662
+ visit(node.expression),
1663
+ node.body&.accept(copy_compiler(forwarding: [])),
1664
+ token(node.end_keyword_loc)
1665
+ )
1666
+ end
1667
+
1668
+ # __ENCODING__
1669
+ # ^^^^^^^^^^^^
1670
+ def visit_source_encoding_node(node)
1671
+ builder.accessible(builder.__ENCODING__(token(node.location)))
1672
+ end
1673
+
1674
+ # __FILE__
1675
+ # ^^^^^^^^
1676
+ def visit_source_file_node(node)
1677
+ builder.accessible(builder.__FILE__(token(node.location)))
1678
+ end
1679
+
1680
+ # __LINE__
1681
+ # ^^^^^^^^
1682
+ def visit_source_line_node(node)
1683
+ builder.accessible(builder.__LINE__(token(node.location)))
1684
+ end
1685
+
1686
+ # foo(*bar)
1687
+ # ^^^^
1688
+ #
1689
+ # def foo((bar, *baz)); end
1690
+ # ^^^^
1691
+ #
1692
+ # def foo(*); bar(*); end
1693
+ # ^
1694
+ def visit_splat_node(node)
1695
+ if node.expression.nil? && forwarding.include?(:*)
1696
+ builder.forwarded_restarg(token(node.operator_loc))
1697
+ elsif in_destructure
1698
+ builder.restarg(token(node.operator_loc), token(node.expression&.location))
1699
+ elsif in_pattern
1700
+ builder.match_rest(token(node.operator_loc), token(node.expression&.location))
1701
+ else
1702
+ builder.splat(token(node.operator_loc), visit(node.expression))
1703
+ end
1704
+ end
1705
+
1706
+ # A list of statements.
1707
+ def visit_statements_node(node)
1708
+ builder.compstmt(visit_all(node.body))
1709
+ end
1710
+
1711
+ # "foo"
1712
+ # ^^^^^
1713
+ def visit_string_node(node)
1714
+ if node.heredoc?
1715
+ visit_heredoc(node.to_interpolated) { |children, closing| builder.string_compose(token(node.opening_loc), children, closing) }
1716
+ elsif node.opening == "?"
1717
+ builder.character([node.unescaped, srange(node.location)])
1718
+ elsif node.opening&.start_with?("%") && node.unescaped.empty?
1719
+ builder.string_compose(token(node.opening_loc), [], token(node.closing_loc))
1720
+ else
1721
+ parts =
1722
+ if node.content.include?("\n")
1723
+ string_nodes_from_line_continuations(node.unescaped, node.content, node.content_loc.start_offset, node.opening)
1724
+ else
1725
+ [builder.string_internal([node.unescaped, srange(node.content_loc)])]
1726
+ end
1727
+
1728
+ builder.string_compose(
1729
+ token(node.opening_loc),
1730
+ parts,
1731
+ token(node.closing_loc)
1732
+ )
1733
+ end
1734
+ end
1735
+
1736
+ # super(foo)
1737
+ # ^^^^^^^^^^
1738
+ def visit_super_node(node)
1739
+ arguments = node.arguments&.arguments || []
1740
+ block = node.block
1741
+
1742
+ if block.is_a?(BlockArgumentNode)
1743
+ arguments = [*arguments, block]
1744
+ block = nil
1745
+ end
1746
+
1747
+ visit_block(
1748
+ builder.keyword_cmd(
1749
+ :super,
1750
+ token(node.keyword_loc),
1751
+ token(node.lparen_loc),
1752
+ visit_all(arguments),
1753
+ token(node.rparen_loc)
1754
+ ),
1755
+ block
1756
+ )
1757
+ end
1758
+
1759
+ # :foo
1760
+ # ^^^^
1761
+ def visit_symbol_node(node)
1762
+ if node.closing_loc.nil?
1763
+ if node.opening_loc.nil?
1764
+ builder.symbol_internal([node.unescaped, srange(node.location)])
1765
+ else
1766
+ builder.symbol([node.unescaped, srange(node.location)])
1767
+ end
1768
+ else
1769
+ parts =
1770
+ if node.value == ""
1771
+ []
1772
+ elsif node.value.include?("\n")
1773
+ string_nodes_from_line_continuations(node.unescaped, node.value, node.value_loc.start_offset, node.opening)
1774
+ else
1775
+ [builder.string_internal([node.unescaped, srange(node.value_loc)])]
1776
+ end
1777
+
1778
+ builder.symbol_compose(
1779
+ token(node.opening_loc),
1780
+ parts,
1781
+ token(node.closing_loc)
1782
+ )
1783
+ end
1784
+ end
1785
+
1786
+ # true
1787
+ # ^^^^
1788
+ def visit_true_node(node)
1789
+ builder.true(token(node.location))
1790
+ end
1791
+
1792
+ # undef foo
1793
+ # ^^^^^^^^^
1794
+ def visit_undef_node(node)
1795
+ builder.undef_method(token(node.keyword_loc), visit_all(node.names))
1796
+ end
1797
+
1798
+ # unless foo; bar end
1799
+ # ^^^^^^^^^^^^^^^^^^^
1800
+ #
1801
+ # bar unless foo
1802
+ # ^^^^^^^^^^^^^^
1803
+ def visit_unless_node(node)
1804
+ if node.keyword_loc.start_offset == node.location.start_offset
1805
+ builder.condition(
1806
+ token(node.keyword_loc),
1807
+ visit(node.predicate),
1808
+ if (then_keyword_loc = node.then_keyword_loc)
1809
+ token(then_keyword_loc)
1810
+ else
1811
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.else_clause&.location || node.end_keyword_loc).start_offset, ";")
1812
+ end,
1813
+ visit(node.else_clause),
1814
+ token(node.else_clause&.else_keyword_loc),
1815
+ visit(node.statements),
1816
+ token(node.end_keyword_loc)
1817
+ )
1818
+ else
1819
+ builder.condition_mod(
1820
+ visit(node.else_clause),
1821
+ visit(node.statements),
1822
+ token(node.keyword_loc),
1823
+ visit(node.predicate)
1824
+ )
1825
+ end
1826
+ end
1827
+
1828
+ # until foo; bar end
1829
+ # ^^^^^^^^^^^^^^^^^^
1830
+ #
1831
+ # bar until foo
1832
+ # ^^^^^^^^^^^^^
1833
+ def visit_until_node(node)
1834
+ if node.location.start_offset == node.keyword_loc.start_offset
1835
+ builder.loop(
1836
+ :until,
1837
+ token(node.keyword_loc),
1838
+ visit(node.predicate),
1839
+ if (do_keyword_loc = node.do_keyword_loc)
1840
+ token(do_keyword_loc)
1841
+ else
1842
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, ";")
1843
+ end,
1844
+ visit(node.statements),
1845
+ token(node.closing_loc)
1846
+ )
1847
+ else
1848
+ builder.loop_mod(
1849
+ :until,
1850
+ visit(node.statements),
1851
+ token(node.keyword_loc),
1852
+ visit(node.predicate)
1853
+ )
1854
+ end
1855
+ end
1856
+
1857
+ # case foo; when bar; end
1858
+ # ^^^^^^^^^^^^^
1859
+ def visit_when_node(node)
1860
+ builder.when(
1861
+ token(node.keyword_loc),
1862
+ visit_all(node.conditions),
1863
+ if (then_keyword_loc = node.then_keyword_loc)
1864
+ token(then_keyword_loc)
1865
+ else
1866
+ srange_find(node.conditions.last.location.end_offset, node.statements&.location&.start_offset, ";")
1867
+ end,
1868
+ visit(node.statements)
1869
+ )
1870
+ end
1871
+
1872
+ # while foo; bar end
1873
+ # ^^^^^^^^^^^^^^^^^^
1874
+ #
1875
+ # bar while foo
1876
+ # ^^^^^^^^^^^^^
1877
+ def visit_while_node(node)
1878
+ if node.location.start_offset == node.keyword_loc.start_offset
1879
+ builder.loop(
1880
+ :while,
1881
+ token(node.keyword_loc),
1882
+ visit(node.predicate),
1883
+ if (do_keyword_loc = node.do_keyword_loc)
1884
+ token(do_keyword_loc)
1885
+ else
1886
+ srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, ";")
1887
+ end,
1888
+ visit(node.statements),
1889
+ token(node.closing_loc)
1890
+ )
1891
+ else
1892
+ builder.loop_mod(
1893
+ :while,
1894
+ visit(node.statements),
1895
+ token(node.keyword_loc),
1896
+ visit(node.predicate)
1897
+ )
1898
+ end
1899
+ end
1900
+
1901
+ # `foo`
1902
+ # ^^^^^
1903
+ def visit_x_string_node(node)
1904
+ if node.heredoc?
1905
+ return visit_heredoc(node.to_interpolated) { |children, closing| builder.xstring_compose(token(node.opening_loc), children, closing) }
1906
+ end
1907
+
1908
+ parts =
1909
+ if node.content == ""
1910
+ []
1911
+ elsif node.content.include?("\n")
1912
+ string_nodes_from_line_continuations(node.unescaped, node.content, node.content_loc.start_offset, node.opening)
1913
+ else
1914
+ [builder.string_internal([node.unescaped, srange(node.content_loc)])]
1915
+ end
1916
+
1917
+ builder.xstring_compose(
1918
+ token(node.opening_loc),
1919
+ parts,
1920
+ token(node.closing_loc)
1921
+ )
1922
+ end
1923
+
1924
+ # yield
1925
+ # ^^^^^
1926
+ #
1927
+ # yield 1
1928
+ # ^^^^^^^
1929
+ def visit_yield_node(node)
1930
+ builder.keyword_cmd(
1931
+ :yield,
1932
+ token(node.keyword_loc),
1933
+ token(node.lparen_loc),
1934
+ visit(node.arguments) || [],
1935
+ token(node.rparen_loc)
1936
+ )
1937
+ end
1938
+
1939
+ private
1940
+
1941
+ # Initialize a new compiler with the given option overrides, used to
1942
+ # visit a subtree with the given options.
1943
+ def copy_compiler(forwarding: self.forwarding, in_destructure: self.in_destructure, in_pattern: self.in_pattern)
1944
+ Compiler.new(parser, offset_cache, forwarding: forwarding, in_destructure: in_destructure, in_pattern: in_pattern)
1945
+ end
1946
+
1947
+ # When *, **, &, or ... are used as an argument in a method call, we
1948
+ # check if they were allowed by the current context. To determine that
1949
+ # we build this lookup table.
1950
+ def find_forwarding(node)
1951
+ return [] if node.nil?
1952
+
1953
+ forwarding = []
1954
+ forwarding << :* if node.rest.is_a?(RestParameterNode) && node.rest.name.nil?
1955
+ forwarding << :** if node.keyword_rest.is_a?(KeywordRestParameterNode) && node.keyword_rest.name.nil?
1956
+ forwarding << :& if !node.block.nil? && node.block.name.nil?
1957
+ forwarding |= [:&, :"..."] if node.keyword_rest.is_a?(ForwardingParameterNode)
1958
+
1959
+ forwarding
1960
+ end
1961
+
1962
+ # Returns the set of targets for a MultiTargetNode or a MultiWriteNode.
1963
+ def multi_target_elements(node)
1964
+ elements = [*node.lefts]
1965
+ elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
1966
+ elements.concat(node.rights)
1967
+ elements
1968
+ end
1969
+
1970
+ # Negate the value of a numeric node. This is a special case where you
1971
+ # have a negative sign on one line and then a number on the next line.
1972
+ # In normal Ruby, this will always be a method call. The parser gem,
1973
+ # however, marks this as a numeric literal. We have to massage the tree
1974
+ # here to get it into the correct form.
1975
+ def numeric_negate(message_loc, receiver)
1976
+ case receiver.type
1977
+ when :integer_node, :float_node
1978
+ receiver.copy(value: -receiver.value, location: message_loc.join(receiver.location))
1979
+ when :rational_node
1980
+ receiver.copy(numerator: -receiver.numerator, location: message_loc.join(receiver.location))
1981
+ when :imaginary_node
1982
+ receiver.copy(numeric: numeric_negate(message_loc, receiver.numeric), location: message_loc.join(receiver.location))
1983
+ end
1984
+ end
1985
+
1986
+ # Blocks can have a special set of parameters that automatically expand
1987
+ # when given arrays if they have a single required parameter and no
1988
+ # other parameters.
1989
+ def procarg0?(parameters)
1990
+ parameters &&
1991
+ parameters.requireds.length == 1 &&
1992
+ parameters.optionals.empty? &&
1993
+ parameters.rest.nil? &&
1994
+ parameters.posts.empty? &&
1995
+ parameters.keywords.empty? &&
1996
+ parameters.keyword_rest.nil? &&
1997
+ parameters.block.nil?
1998
+ end
1999
+
2000
+ # Locations in the parser gem AST are generated using this class. We
2001
+ # store a reference to its constant to make it slightly faster to look
2002
+ # up.
2003
+ Range = ::Parser::Source::Range
2004
+
2005
+ # Constructs a new source range from the given start and end offsets.
2006
+ def srange(location)
2007
+ Range.new(source_buffer, offset_cache[location.start_offset], offset_cache[location.end_offset]) if location
2008
+ end
2009
+
2010
+ # Constructs a new source range from the given start and end offsets.
2011
+ def srange_offsets(start_offset, end_offset)
2012
+ Range.new(source_buffer, offset_cache[start_offset], offset_cache[end_offset])
2013
+ end
2014
+
2015
+ # Constructs a new source range by finding the given character between
2016
+ # the given start offset and end offset. If the needle is not found, it
2017
+ # returns nil. Importantly it does not search past newlines or comments.
2018
+ #
2019
+ # Note that end_offset is allowed to be nil, in which case this will
2020
+ # search until the end of the string.
2021
+ def srange_find(start_offset, end_offset, character)
2022
+ if (match = source_buffer.source.byteslice(start_offset...end_offset)[/\A\s*#{character}/])
2023
+ final_offset = start_offset + match.bytesize
2024
+ [character, Range.new(source_buffer, offset_cache[final_offset - character.bytesize], offset_cache[final_offset])]
2025
+ end
2026
+ end
2027
+
2028
+ # Transform a location into a token that the parser gem expects.
2029
+ def token(location)
2030
+ [location.slice, Range.new(source_buffer, offset_cache[location.start_offset], offset_cache[location.end_offset])] if location
2031
+ end
2032
+
2033
+ # Visit a block node on a call.
2034
+ def visit_block(call, block)
2035
+ if block
2036
+ parameters = block.parameters
2037
+ implicit_parameters = parameters.is_a?(NumberedParametersNode) || parameters.is_a?(ItParametersNode)
2038
+
2039
+ builder.block(
2040
+ call,
2041
+ token(block.opening_loc),
2042
+ if parameters.nil?
2043
+ builder.args(nil, [], nil, false)
2044
+ elsif implicit_parameters
2045
+ visit(parameters)
2046
+ else
2047
+ builder.args(
2048
+ token(parameters.opening_loc),
2049
+ if procarg0?(parameters.parameters)
2050
+ parameter = parameters.parameters.requireds.first
2051
+ visited = parameter.is_a?(RequiredParameterNode) ? visit(parameter) : parameter.accept(copy_compiler(in_destructure: true))
2052
+ [builder.procarg0(visited)].concat(visit_all(parameters.locals))
2053
+ else
2054
+ visit(parameters)
2055
+ end,
2056
+ token(parameters.closing_loc),
2057
+ false
2058
+ )
2059
+ end,
2060
+ visit(block.body),
2061
+ token(block.closing_loc)
2062
+ )
2063
+ else
2064
+ call
2065
+ end
2066
+ end
2067
+
2068
+ # Visit a heredoc that can be either a string or an xstring.
2069
+ def visit_heredoc(node)
2070
+ children = Array.new
2071
+ indented = false
2072
+
2073
+ # If this is a dedenting heredoc, then we need to insert the opening
2074
+ # content into the children as well.
2075
+ if node.opening.start_with?("<<~") && node.parts.length > 0 && !node.parts.first.is_a?(StringNode)
2076
+ location = node.parts.first.location
2077
+ location = location.copy(start_offset: location.start_offset - location.start_line_slice.bytesize)
2078
+ children << builder.string_internal(token(location))
2079
+ indented = true
2080
+ end
2081
+
2082
+ node.parts.each do |part|
2083
+ pushing =
2084
+ if part.is_a?(StringNode) && part.content.include?("\n")
2085
+ string_nodes_from_line_continuations(part.unescaped, part.content, part.location.start_offset, node.opening)
2086
+ else
2087
+ [visit(part)]
2088
+ end
2089
+
2090
+ pushing.each do |child|
2091
+ if child.type == :str && child.children.last == ""
2092
+ # nothing
2093
+ elsif child.type == :str && children.last && children.last.type == :str && !children.last.children.first.end_with?("\n")
2094
+ appendee = children[-1]
2095
+
2096
+ location = appendee.loc
2097
+ location = location.with_expression(location.expression.join(child.loc.expression))
2098
+
2099
+ children[-1] = appendee.updated(:str, ["#{appendee.children.first}#{child.children.first}"], location: location)
2100
+ else
2101
+ children << child
2102
+ end
2103
+ end
2104
+ end
2105
+
2106
+ closing = node.closing
2107
+ closing_t = [closing.chomp, srange_offsets(node.closing_loc.start_offset, node.closing_loc.end_offset - (closing[/\s+$/]&.length || 0))]
2108
+ composed = yield children, closing_t
2109
+
2110
+ composed = composed.updated(nil, children[1..-1]) if indented
2111
+ composed
2112
+ end
2113
+
2114
+ # Visit a numeric node and account for the optional sign.
2115
+ def visit_numeric(node, value)
2116
+ if (slice = node.slice).match?(/^[+-]/)
2117
+ builder.unary_num(
2118
+ [slice[0].to_sym, srange_offsets(node.location.start_offset, node.location.start_offset + 1)],
2119
+ value
2120
+ )
2121
+ else
2122
+ value
2123
+ end
2124
+ end
2125
+
2126
+ # Within the given block, track that we're within a pattern.
2127
+ def within_pattern
2128
+ begin
2129
+ parser.pattern_variables.push
2130
+ yield copy_compiler(in_pattern: true)
2131
+ ensure
2132
+ parser.pattern_variables.pop
2133
+ end
2134
+ end
2135
+
2136
+ # When the content of a string node is split across multiple lines, the
2137
+ # parser gem creates individual string nodes for each line the content is part of.
2138
+ def string_nodes_from_interpolation(node, opening)
2139
+ node.parts.flat_map do |part|
2140
+ if part.type == :string_node && part.content.include?("\n") && part.opening_loc.nil?
2141
+ string_nodes_from_line_continuations(part.unescaped, part.content, part.content_loc.start_offset, opening)
2142
+ else
2143
+ visit(part)
2144
+ end
2145
+ end
2146
+ end
2147
+
2148
+ # Create parser string nodes from a single prism node. The parser gem
2149
+ # "glues" strings together when a line continuation is encountered.
2150
+ def string_nodes_from_line_continuations(unescaped, escaped, start_offset, opening)
2151
+ unescaped = unescaped.lines
2152
+ escaped = escaped.lines
2153
+ percent_array = opening&.start_with?("%w", "%W", "%i", "%I")
2154
+ regex = opening == "/" || opening&.start_with?("%r")
2155
+
2156
+ # Non-interpolating strings
2157
+ if opening&.end_with?("'") || opening&.start_with?("%q", "%s", "%w", "%i")
2158
+ current_length = 0
2159
+ current_line = +""
2160
+
2161
+ escaped.filter_map.with_index do |escaped_line, index|
2162
+ unescaped_line = unescaped.fetch(index, "")
2163
+ current_length += escaped_line.bytesize
2164
+ current_line << unescaped_line
2165
+
2166
+ # Glue line continuations together. Only %w and %i arrays can contain these.
2167
+ if percent_array && escaped_line[/(\\)*\n$/, 1]&.length&.odd?
2168
+ next unless index == escaped.count - 1
2169
+ end
2170
+ s = builder.string_internal([current_line, srange_offsets(start_offset, start_offset + current_length)])
2171
+ start_offset += escaped_line.bytesize
2172
+ current_line = +""
2173
+ current_length = 0
2174
+ s
2175
+ end
2176
+ else
2177
+ escaped_lengths = []
2178
+ normalized_lengths = []
2179
+ # Keeps track of where an unescaped line should start a new token. An unescaped
2180
+ # \n would otherwise be indistinguishable from the actual newline at the end of
2181
+ # of the line. The parser gem only emits a new string node at "real" newlines,
2182
+ # line continuations don't start a new node as well.
2183
+ do_next_tokens = []
2184
+
2185
+ escaped
2186
+ .chunk_while { |before, after| before[/(\\*)\r?\n$/, 1]&.length&.odd? || false }
2187
+ .each do |lines|
2188
+ escaped_lengths << lines.sum(&:bytesize)
2189
+
2190
+ unescaped_lines_count =
2191
+ if regex
2192
+ 0 # Will always be preserved as is
2193
+ else
2194
+ lines.sum do |line|
2195
+ count = line.scan(/(\\*)n/).count { |(backslashes)| backslashes&.length&.odd? }
2196
+ count -= 1 if !line.end_with?("\n") && count > 0
2197
+ count
2198
+ end
2199
+ end
2200
+
2201
+ extra = 1
2202
+ extra = lines.count if percent_array # Account for line continuations in percent arrays
2203
+
2204
+ normalized_lengths.concat(Array.new(unescaped_lines_count + extra, 0))
2205
+ normalized_lengths[-1] = lines.sum { |line| line.bytesize }
2206
+ do_next_tokens.concat(Array.new(unescaped_lines_count + extra, false))
2207
+ do_next_tokens[-1] = true
2208
+ end
2209
+
2210
+ current_line = +""
2211
+ current_normalized_length = 0
2212
+
2213
+ emitted_count = 0
2214
+ unescaped.filter_map.with_index do |unescaped_line, index|
2215
+ current_line << unescaped_line
2216
+ current_normalized_length += normalized_lengths.fetch(index, 0)
2217
+
2218
+ if do_next_tokens[index]
2219
+ inner_part = builder.string_internal([current_line, srange_offsets(start_offset, start_offset + current_normalized_length)])
2220
+ start_offset += escaped_lengths.fetch(emitted_count, 0)
2221
+ current_line = +""
2222
+ current_normalized_length = 0
2223
+ emitted_count += 1
2224
+ inner_part
2225
+ else
2226
+ nil
2227
+ end
2228
+ end
2229
+ end
2230
+ end
2231
+ end
2232
+ end
2233
+ end
2234
+ end