prism 2.0.0.rc1-arm64-darwin

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