arpaka 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -2
  3. data/README.md +104 -7
  4. data/Rakefile +15 -1
  5. data/docs/architecture.dot +30 -0
  6. data/docs/architecture.md +96 -0
  7. data/docs/architecture.webp +0 -0
  8. data/docs/contributes.md +115 -0
  9. data/docs/usage.md +497 -0
  10. data/lib/arpaka/cli.rb +5 -53
  11. data/lib/arpaka/plugins/cli.rb +61 -0
  12. data/lib/arpaka/plugins/sample.rb +14 -0
  13. data/lib/arpaka/plugins/sample_runner.rb +21 -0
  14. data/lib/arpaka/{actions.rb → ruby/actions.rb} +230 -151
  15. data/lib/arpaka/ruby/class_name_parser.rb +97 -0
  16. data/lib/arpaka/ruby/cli.rb +64 -0
  17. data/lib/arpaka/ruby/frontend_emitter.rb +59 -0
  18. data/lib/arpaka/ruby/generator.rb +41 -0
  19. data/lib/arpaka/{ruby_grammar.rb → ruby/grammar.rb} +5 -56
  20. data/lib/arpaka/ruby/grammar_emitter.rb +71 -0
  21. data/lib/arpaka/ruby/profile.rb +129 -0
  22. data/lib/arpaka/{profile → ruby/profiles}/ruby_3_3.rb +21 -21
  23. data/lib/arpaka/{profile → ruby/profiles}/ruby_3_4.rb +21 -21
  24. data/lib/arpaka/{runtime → ruby/runtime/detailed}/ast.rb.erb +41 -5
  25. data/lib/arpaka/ruby/runtime/detailed/builder.rb.erb +1177 -0
  26. data/lib/arpaka/{runtime → ruby/runtime}/frontend.rb.erb +17 -4
  27. data/lib/arpaka/{runtime → ruby/runtime}/lexer.rb.erb +576 -99
  28. data/lib/arpaka/ruby/runtime/structured.rb.erb +111 -0
  29. data/lib/arpaka/ruby.rb +23 -0
  30. data/lib/arpaka.rb +6 -9
  31. data/lib/lrama/ruby/backend.rb +4 -9
  32. data/lib/lrama/ruby/parser.rb.erb +35 -9
  33. data/lib/lrama/ruby/parser_emitter.rb +41 -0
  34. data/lib/lrama/ruby/table_codec.rb +50 -0
  35. data/lib/lrama/ruby/version.rb +1 -1
  36. data/lib/lrama/ruby.rb +1 -0
  37. data/sig/arpaka.rbs +8 -3
  38. metadata +32 -16
  39. data/lib/arpaka/generator.rb +0 -51
  40. data/lib/arpaka/profile/ruby.rb +0 -49
  41. data/lib/arpaka/runtime/builder.rb.erb +0 -323
  42. /data/lib/arpaka/{BSDL → ruby/BSDL} +0 -0
  43. /data/lib/arpaka/{COPYING → ruby/COPYING} +0 -0
  44. /data/lib/arpaka/{source.json → ruby/source.json} +0 -0
  45. /data/lib/arpaka/{upstream_rules.json → ruby/upstream_rules.json} +0 -0
@@ -0,0 +1,1177 @@
1
+ class Builder
2
+ attr_reader :modifier_nodes
3
+
4
+ BlockBody = Data.define(:parameters, :body, :outer_local_names, :style)
5
+ BlockScope = Data.define(:outer_locals, :lambda_local_counts)
6
+ private_constant :BlockBody
7
+ private_constant :BlockScope
8
+
9
+ RULES = <%= rules.select { |rule| rule.fetch("status") == "unsupported" }.inspect %>.to_h do |rule|
10
+ [rule.fetch("id"), [rule.fetch("rule"), rule.fetch("line")].freeze]
11
+ end.freeze
12
+ private_constant :RULES
13
+
14
+ def initialize
15
+ @modifier_nodes = {}.compare_by_identity
16
+ @locals = {}
17
+ @block_local_scopes = []
18
+ @method_local_scopes = []
19
+ @method_lambda_local_counts = []
20
+ @lambda_parameter_states = []
21
+ @lambda_local_counts = Hash.new(0)
22
+ @declaring_method_parameters = false
23
+ end
24
+
25
+ def program(statements)
26
+ AST::Program.new(resolve_local_reads(body_nodes(statements), [], [:it]))
27
+ end
28
+
29
+ def integer(value)
30
+ raise ArgumentError, "tINTEGER requires an Integer value" unless value.is_a?(Integer)
31
+ AST::Literal.new(value)
32
+ end
33
+
34
+ def float(value)
35
+ raise ArgumentError, "tFLOAT requires a Float value" unless value.is_a?(Float)
36
+ AST::Literal.new(value)
37
+ end
38
+
39
+ def literal(value)
40
+ AST::Literal.new(value)
41
+ end
42
+
43
+ def line_literal(value)
44
+ AST::Literal.new(value.is_a?(Integer) ? value : :__LINE__)
45
+ end
46
+
47
+ def encoding_literal
48
+ AST::EncodingLiteral.new(@source_encoding || Encoding::UTF_8.name)
49
+ end
50
+
51
+ <% if runtime.fetch("builder_methods", []).include?("self") -%>
52
+ def self_node
53
+ AST::Self.new
54
+ end
55
+ <% end -%>
56
+
57
+ def reserved_word(value)
58
+ value.to_sym
59
+ end
60
+
61
+ def binary(operator, left, right)
62
+ if [:"&&", :"||"].include?(operator) && right.is_a?(AST::Binary) &&
63
+ right.operator == operator && logical_chain_starts_with_defined?(operator, right)
64
+ return prepend_logical_chain(operator, left, right)
65
+ end
66
+ AST::Binary.new(operator, left, right)
67
+ end
68
+
69
+ def logical_chain_starts_with_defined?(operator, value)
70
+ if value.is_a?(AST::Binary) && value.operator == operator
71
+ logical_chain_starts_with_defined?(operator, value.left)
72
+ elsif value.is_a?(AST::Unary) && value.operator == :!
73
+ value.operand.is_a?(AST::Call) && value.operand.name == :defined
74
+ else
75
+ value.is_a?(AST::Call) && value.name == :defined
76
+ end
77
+ end
78
+
79
+ def prepend_logical_chain(operator, left, value)
80
+ if value.is_a?(AST::Binary) && value.operator == operator
81
+ AST::Binary.new(operator, prepend_logical_chain(operator, left, value.left), value.right)
82
+ else
83
+ AST::Binary.new(operator, left, value)
84
+ end
85
+ end
86
+
87
+ def operator(value)
88
+ value.to_sym
89
+ end
90
+
91
+ def range(operator, begin_value, end_value)
92
+ AST::RangeLiteral.new(operator, begin_value, end_value)
93
+ end
94
+
95
+ def ternary(condition, then_value, else_value)
96
+ AST::Ternary.new(condition, then_value, else_value)
97
+ end
98
+
99
+ def call(name, arguments)
100
+ AST::Call.new(name, normalize_call_arguments(arguments))
101
+ end
102
+
103
+ def super_call(arguments)
104
+ AST::Super.new(arguments&.freeze)
105
+ end
106
+
107
+ def receiver_call(receiver, operator, name, arguments)
108
+ AST::ReceiverCall.new(receiver, operator, name, normalize_call_arguments(arguments))
109
+ end
110
+
111
+ def block_pass(arguments, value)
112
+ AST::BlockPass.new(normalize_call_arguments(arguments), value)
113
+ end
114
+
115
+ def call_arguments_with_block_pass(arguments, pass)
116
+ return normalize_call_arguments(arguments) if pass.nil?
117
+
118
+ [AST::BlockPass.new(normalize_call_arguments(arguments), pass.value)].freeze
119
+ end
120
+
121
+ def forwarded_block_pass
122
+ block_pass([], AST::LocalRead.new(:&))
123
+ end
124
+
125
+ <% if runtime.fetch("builder_methods", []).include?("keyword_arguments") -%>
126
+ def normalize_call_arguments(arguments)
127
+ return [] if arguments.nil?
128
+ arguments = [arguments] unless arguments.is_a?(Array)
129
+
130
+ result = []
131
+ pairs = []
132
+ flush_pairs = lambda do
133
+ unless pairs.empty?
134
+ result << AST::HashLiteral.new(pairs.freeze)
135
+ pairs = []
136
+ end
137
+ end
138
+
139
+ arguments.each do |argument|
140
+ if argument.is_a?(AST::Pair)
141
+ pairs << argument
142
+ else
143
+ flush_pairs.call
144
+ result << argument
145
+ end
146
+ end
147
+ flush_pairs.call
148
+ splat_index = result.rindex { |argument| argument.is_a?(AST::Splat) }
149
+ if splat_index && result[(splat_index + 1)..].length > 1 &&
150
+ result[(splat_index + 1)..].any? { |argument| argument.is_a?(AST::HashLiteral) }
151
+ result[splat_index + 1..] = [AST::Splat.new(AST::ArrayLiteral.new(result[(splat_index + 1)..].freeze))]
152
+ end
153
+ result.freeze
154
+ end
155
+ <% else -%>
156
+ def normalize_call_arguments(arguments)
157
+ arguments.nil? ? [] : (arguments.is_a?(Array) ? arguments.freeze : [arguments].freeze)
158
+ end
159
+ <% end -%>
160
+
161
+ def block_call(call, body)
162
+ if body.is_a?(BlockBody)
163
+ parameters = body.parameters
164
+ implicit_names = []
165
+ if parameters.is_a?(AST::BlockParameters) && parameters.kind == :none
166
+ implicit_names = implicit_block_parameter_names(body.body, body.outer_local_names)
167
+ unless implicit_names.empty?
168
+ parameters = AST::BlockParameters.new(:required, implicit_names.freeze)
169
+ end
170
+ end
171
+ outer_local_names = body.outer_local_names
172
+ block_local_names = (block_parameter_names(parameters) + implicit_names).uniq - outer_local_names
173
+ local_names = outer_local_names + block_local_names
174
+ resolved_call = rewrite_shadowed_local_reads(call, block_local_names)
175
+ resolved_call = resolve_local_reads(resolved_call, outer_local_names)
176
+ resolved_body = resolve_local_reads(
177
+ rewrite_implicit_block_parameter_reads(body.body, implicit_names), local_names)
178
+ AST::BlockCall.new(resolved_call, parameters, resolved_body, body.style)
179
+ else
180
+ AST::BlockCall.new(call, nil, resolve_local_reads(body_nodes(body), @locals.keys), nil)
181
+ end
182
+ end
183
+
184
+ def block_body(parameters, body, style = nil)
185
+ scope = @block_local_scopes.pop
186
+ if scope
187
+ @locals = scope.outer_locals
188
+ outer_local_names = scope.outer_locals.keys.freeze
189
+ @lambda_local_counts = scope.lambda_local_counts
190
+ else
191
+ outer_local_names = @locals.keys.freeze
192
+ end
193
+ BlockBody.new(parameters, body_nodes(body), outer_local_names, style)
194
+ end
195
+
196
+ def begin_block_parameters(value, locals = nil)
197
+ local_names = Array(locals).compact
198
+ parameter_names = (block_parameter_names(value) + block_parameter_names(local_names)).uniq
199
+ outer_locals = @locals.dup
200
+ parameter_names.each do |name|
201
+ count = outer_locals[name]
202
+ if count && count > 1
203
+ outer_locals[name] = count - 1
204
+ else
205
+ outer_locals.delete(name)
206
+ end
207
+ end
208
+ @lambda_local_counts.each do |name, count|
209
+ current = outer_locals[name]
210
+ outer_locals.delete(name) if current && current <= count
211
+ end
212
+ @block_local_scopes << BlockScope.new(outer_locals, @lambda_local_counts)
213
+ @locals = outer_locals.dup
214
+ @lambda_local_counts = @lambda_local_counts.dup
215
+ parameter_names.each { |name| declare_local(name) }
216
+ case value
217
+ when nil
218
+ parameters = AST::BlockParameters.new(:none, nil)
219
+ when Symbol
220
+ parameters = AST::BlockParameters.new(:required, [value].freeze)
221
+ when Array
222
+ parameters = AST::BlockParameters.new(:required, value.freeze)
223
+ when AST::BlockParameters
224
+ parameters = value
225
+ when AST::Parameter
226
+ # A block-only parameter (`|&block|`) is delivered by the grammar as a
227
+ # Parameter rather than an array of parameters. Keep it in the same
228
+ # representation as other block parameter forms instead of losing it as
229
+ # an unknown shape.
230
+ parameters = AST::BlockParameters.new(:required, [value].freeze)
231
+ else
232
+ parameters = AST::BlockParameters.new(:unknown, value)
233
+ end
234
+ return parameters if local_names.empty?
235
+ AST::BlockParameters.new(parameters.kind,
236
+ Array(parameters.value) + [AST::BlockParameters.new(:locals, local_names.freeze)])
237
+ end
238
+
239
+ def block_destructure(names)
240
+ names = [AST::BlockParameters.new(:rest, names)] if names.is_a?(Symbol)
241
+ AST::BlockParameters.new(:destructured, Array(names).freeze)
242
+ end
243
+
244
+ def append_block_rest(parameters, rest, post = nil)
245
+ values = Array(parameters).dup
246
+ rest_name = rest.is_a?(AST::Parameter) ? rest.name : Array(rest).first
247
+ values << AST::BlockParameters.new(:rest, rest_name)
248
+ values.concat(Array(post).map { |name| AST::BlockParameters.new(:post, name) }) unless post.nil?
249
+ values.freeze
250
+ end
251
+
252
+ def block_parameter_names(parameters)
253
+ parameters = [parameters] unless parameters.is_a?(Array)
254
+ parameters.flat_map do |parameter|
255
+ case parameter
256
+ when Symbol then [parameter]
257
+ when Array then block_parameter_names(parameter)
258
+ when AST::BlockParameters
259
+ case parameter.kind
260
+ when :destructured then block_parameter_names(parameter.value)
261
+ when :rest, :post then [parameter.value].compact
262
+ else []
263
+ end
264
+ when AST::Parameter
265
+ parameter.name ? [parameter.name] : []
266
+ else []
267
+ end
268
+ end
269
+ end
270
+
271
+ def implicit_block_parameter_names(value, outer_local_names = [])
272
+ numbered = []
273
+ visit = lambda do |current|
274
+ case current
275
+ when Array
276
+ current.each { |item| visit.call(item) }
277
+ else
278
+ return unless data_node?(current)
279
+
280
+ type = current.class.name.split("::").last
281
+ if type == "BareCall" && current.name == :it && !outer_local_names.include?(:it)
282
+ numbered << :it
283
+ elsif type == "BareCall" && current.name.to_s.match?(/\A_[1-9][0-9]*\z/)
284
+ numbered << current.name.to_s.delete_prefix("_").to_i
285
+ elsif type == "BlockCall"
286
+ visit.call(current.call)
287
+ elsif !["Def", "SingletonMethodDef", "Lambda", "ClassDef", "ModuleDef", "SingletonClassDef"].include?(type)
288
+ current.members.each { |member| visit.call(current.public_send(member)) }
289
+ end
290
+ end
291
+ end
292
+ visit.call(value)
293
+ implicit_it = numbered.include?(:it) ? [:"<it>"] : []
294
+ indexes = numbered.grep(Integer)
295
+ maximum = indexes.max
296
+ implicit_it + (maximum ? (1..maximum).map { |index| "_#{index}".to_sym } : [])
297
+ end
298
+
299
+ def rewrite_implicit_block_parameter_reads(value, implicit_names)
300
+ return value unless implicit_names.include?(:"<it>")
301
+ case value
302
+ when Array
303
+ return value.map { |item| rewrite_implicit_block_parameter_reads(item, implicit_names) }
304
+ end
305
+ return value unless data_node?(value)
306
+
307
+ type = value.class.name.split("::").last
308
+ return AST::BareCall.new(:"<it>") if type == "BareCall" && value.name == :it
309
+ if type == "BlockCall"
310
+ return value.with(call: rewrite_implicit_block_parameter_reads(value.call, implicit_names))
311
+ end
312
+ return value if ["Def", "SingletonMethodDef", "Lambda", "ClassDef", "ModuleDef", "SingletonClassDef"].include?(type)
313
+
314
+ fields = value.members.to_h do |member|
315
+ [member, rewrite_implicit_block_parameter_reads(value.public_send(member), implicit_names)]
316
+ end
317
+ preserve_modifier_if(value, value.with(**fields))
318
+ end
319
+
320
+ def append_arguments(arguments, argument)
321
+ list = arguments.is_a?(Array) ? arguments : [arguments]
322
+ (list + [argument]).freeze
323
+ end
324
+
325
+ def combine_arguments(left, right)
326
+ left = left.nil? ? [] : left.is_a?(Array) ? left : [left]
327
+ right = right.nil? ? [] : right.is_a?(Array) ? right : [right]
328
+ (left + right).freeze
329
+ end
330
+
331
+ def parameter(kind, name, value = nil)
332
+ if method_parameter_scope?
333
+ declare_local(name) if name.is_a?(Symbol)
334
+ declare_local(:**) if kind == :keyword_rest && name.nil?
335
+ end
336
+ AST::Parameter.new(kind, name, value)
337
+ end
338
+
339
+ def finish_method_parameters(value)
340
+ @declaring_method_parameters = false
341
+ value
342
+ end
343
+
344
+ def parameter_list(*values)
345
+ parameters = values.flat_map do |value|
346
+ value.is_a?(Array) ? value : value.nil? ? [] : [value]
347
+ end
348
+ return nil if parameters.empty?
349
+ return parameters.first if parameters.length == 1
350
+ parameters.freeze
351
+ end
352
+
353
+ def string(value)
354
+ if value.is_a?(Array)
355
+ parts = value.flat_map do |part|
356
+ part.is_a?(AST::InterpolatedString) ? part.parts : [part]
357
+ end
358
+ return AST::InterpolatedString.new(parts.freeze) if parts.any? { |part| !part.is_a?(String) }
359
+ value = parts.join
360
+ end
361
+ AST::StringLiteral.new(value.to_s)
362
+ end
363
+
364
+ def regexp(value, metadata = nil)
365
+ metadata ||= {}
366
+ parts = value.is_a?(Array) ? value : [value]
367
+ parts = parts.flat_map { |part| part.is_a?(AST::InterpolatedString) ? part.parts : [part] }
368
+ pattern = parts.all? { |part| part.is_a?(String) } ? parts.join : parts.freeze
369
+ options = metadata.fetch(:options, 0)
370
+ encoding = metadata[:encoding]
371
+ if pattern.is_a?(String)
372
+ pattern = pattern.dup.force_encoding(encoding || metadata[:source_encoding] || Encoding::UTF_8.name)
373
+ regexp = Regexp.new(pattern, options)
374
+ regexp.names.each { |name| declare_local(name.to_sym) }
375
+ options = regexp.options
376
+ encoding = regexp.encoding.name
377
+ end
378
+ AST::RegexpLiteral.new(pattern, options, encoding, metadata.fetch(:once, false))
379
+ end
380
+
381
+ def xstring(value)
382
+ if value.is_a?(Array)
383
+ parts = value.flat_map { |part| part.is_a?(AST::InterpolatedString) ? part.parts : [part] }
384
+ return AST::XStringLiteral.new(parts.freeze) if parts.any? { |part| !part.is_a?(String) }
385
+
386
+ value = parts.join
387
+ end
388
+ AST::XStringLiteral.new(value.to_s)
389
+ end
390
+
391
+ def for_node(variable, enumerable, body)
392
+ AST::For.new(variable, enumerable, body)
393
+ end
394
+
395
+ def def_node(name, arguments, body)
396
+ method_node(nil, name, arguments, body)
397
+ end
398
+ <% if runtime.fetch("builder_methods", []).include?("singleton_method") -%>
399
+ def singleton_method_node(receiver, name, arguments, body)
400
+ if arguments.is_a?(AST::BlockParameters) && arguments.kind == :destructured
401
+ arguments.value.grep(Symbol).each { |argument| declare_local(argument) }
402
+ if arguments.value.any? { |value| value.is_a?(AST::BlockParameters) }
403
+ arguments = arguments.value
404
+ end
405
+ end
406
+ method_node(receiver, name, arguments, body)
407
+ end
408
+ <% end -%>
409
+
410
+ private
411
+
412
+ def method_node(receiver, name, arguments, body)
413
+ body = resolve_method_local_reads(
414
+ method_body_nodes(body, preserve_terminal_nil: method_arguments_present?(arguments)),
415
+ @locals.keys, block_parameter_names(arguments)
416
+ )
417
+ node = if receiver
418
+ AST::SingletonMethodDef.new(receiver, name.to_sym, (arguments || []).freeze, body)
419
+ else
420
+ AST::Def.new(name.to_sym, (arguments || []).freeze, body)
421
+ end
422
+ end_method_scope
423
+ node
424
+ end
425
+
426
+ public
427
+
428
+ def class_node(name, superclass, body)
429
+ AST::ClassDef.new(declaration_name(name), superclass, body_nodes(body))
430
+ end
431
+
432
+ def module_node(name, body)
433
+ AST::ModuleDef.new(declaration_name(name), body_nodes(body))
434
+ end
435
+ <% if runtime.fetch("builder_methods", []).include?("singleton_class") -%>
436
+ def singleton_class_node(receiver, body)
437
+ AST::SingletonClassDef.new(receiver, body_nodes(body))
438
+ end
439
+ <% end -%>
440
+
441
+ def body_nodes(body)
442
+ value = body
443
+ while value.is_a?(Array) && value.length == 1 && value.first.is_a?(Array)
444
+ value = value.first
445
+ end
446
+ value || [].freeze
447
+ end
448
+
449
+ def begin_body(body)
450
+ nodes = body_nodes(body)
451
+ return nodes.first if nodes.is_a?(Array) && nodes.length == 1 && nodes.first.is_a?(AST::Undef)
452
+
453
+ nodes
454
+ end
455
+
456
+ def method_arguments_present?(arguments)
457
+ return false if arguments.is_a?(AST::Parameter) && arguments.name.nil? &&
458
+ [:forwarding, :rest].include?(arguments.kind)
459
+
460
+ !arguments.nil? && (!arguments.is_a?(Array) || arguments.any?)
461
+ end
462
+
463
+ def method_body_nodes(body, preserve_terminal_nil: false)
464
+ nodes = body_nodes(body)
465
+ return [].freeze if !preserve_terminal_nil && nodes.is_a?(Array) && nodes.length == 1 &&
466
+ nodes.first.is_a?(AST::Literal) && nodes.first.value.nil?
467
+
468
+ nodes
469
+ end
470
+
471
+ def symbol(value)
472
+ if value.is_a?(Array)
473
+ parts = value.flat_map { |part| part.is_a?(AST::InterpolatedString) ? part.parts : [part] }
474
+ return AST::DynamicSymbol.new(parts.freeze) if parts.any? { |part| !part.is_a?(String) }
475
+
476
+ value = parts.join
477
+ end
478
+ value = value.name if value.is_a?(AST::Variable)
479
+ AST::Literal.new(value.to_sym)
480
+ end
481
+
482
+ def join_strings(parts)
483
+ parts
484
+ end
485
+
486
+ def interpolation(value)
487
+ value = value.is_a?(Array) && value.length == 1 ? value.first : value
488
+ AST::InterpolatedString.new([value].freeze)
489
+ end
490
+
491
+ def concat_strings(left, right)
492
+ left_parts = string_parts(left)
493
+ right_parts = string_parts(right)
494
+ parts = left_parts + right_parts
495
+ parts.all? { |part| part.is_a?(String) } ? AST::StringLiteral.new(parts.join) : AST::InterpolatedString.new(parts.freeze)
496
+ end
497
+
498
+ def string_parts(value)
499
+ case value
500
+ when String then [value]
501
+ when AST::StringLiteral then [value.value]
502
+ when AST::InterpolatedString then value.parts
503
+ else [value]
504
+ end
505
+ end
506
+
507
+ def index(receiver, arguments)
508
+ AST::Index.new(receiver, arguments.freeze)
509
+ end
510
+
511
+ def splat(value)
512
+ AST::Splat.new(value)
513
+ end
514
+
515
+ def loop(kind, condition, body)
516
+ AST::Loop.new(kind, condition, body)
517
+ end
518
+
519
+ def modifier_loop(kind, condition, body)
520
+ node = loop(kind, condition, body)
521
+ @modifier_nodes[node] = true
522
+ node
523
+ end
524
+
525
+ def control(kind, arguments = nil)
526
+ arguments = arguments.nil? ? [] : arguments.is_a?(Array) ? arguments : [arguments]
527
+ value = case arguments.length
528
+ when 0 then nil
529
+ when 1 then arguments.first
530
+ else AST::ArrayLiteral.new(arguments.freeze)
531
+ end
532
+ AST::Control.new(kind, value)
533
+ end
534
+
535
+ def variable(kind, name)
536
+ AST::Variable.new(kind, name.is_a?(String) ? name.to_sym : name)
537
+ end
538
+
539
+ def constant_path(receiver, name)
540
+ AST::ConstantPath.new(receiver, name.to_sym)
541
+ end
542
+
543
+ def absolute_constant(name)
544
+ AST::AbsoluteConstant.new(name.to_sym)
545
+ end
546
+
547
+ def declaration_name(name)
548
+ if name.is_a?(AST::Variable) && name.kind == :constant
549
+ name.name
550
+ else
551
+ name
552
+ end
553
+ end
554
+
555
+ def rescue_modifier(expression, fallback)
556
+ AST::Rescue.new(expression, fallback)
557
+ end
558
+
559
+ def when_node(patterns, body)
560
+ AST::When.new(patterns.freeze, body)
561
+ end
562
+
563
+ def case_node(expression, whens, else_body)
564
+ AST::Case.new(expression, whens.freeze, else_body)
565
+ end
566
+
567
+ def case_clauses(value)
568
+ value.is_a?(Array) ? value : [value]
569
+ end
570
+
571
+ def case_parts(value)
572
+ values = case_clauses(value)
573
+ if values.all? { |item| item.is_a?(AST::When) }
574
+ [values, nil]
575
+ else
576
+ [values.grep(AST::When), values.reject { |item| item.is_a?(AST::When) }]
577
+ end
578
+ end
579
+
580
+ def case_chain(when_node, rest)
581
+ [when_node] + (rest.nil? ? [] : flatten_case_clauses(rest))
582
+ end
583
+
584
+ def flatten_case_clauses(value)
585
+ value.is_a?(Array) ? value.flat_map { |item| flatten_case_clauses(item) } : [value]
586
+ end
587
+
588
+ def lambda_node(arguments, body)
589
+ parameter_names = lambda_parameter_names(arguments)
590
+ implicit_names = []
591
+ if parameter_names.empty?
592
+ implicit_names = implicit_block_parameter_names(body)
593
+ unless implicit_names.empty?
594
+ arguments = implicit_names
595
+ parameter_names = implicit_names
596
+ end
597
+ end
598
+ inherited_names = (@locals.keys + parameter_names).uniq
599
+ parameter_names.each { |name| @lambda_local_counts[name] += 1 }
600
+ previous_state = @lambda_parameter_states.pop
601
+ resolved_body = rewrite_implicit_block_parameter_reads(body_nodes(body), implicit_names)
602
+ node = AST::Lambda.new((arguments || []).freeze, resolve_local_reads(resolved_body, inherited_names))
603
+ @declaring_method_parameters = previous_state unless previous_state.nil?
604
+ node
605
+ end
606
+
607
+ def lambda_parameter_names(arguments)
608
+ values = arguments.nil? ? [] : arguments.is_a?(Array) ? arguments : [arguments]
609
+ values.flat_map do |value|
610
+ case value
611
+ when Symbol
612
+ [value]
613
+ when AST::Parameter
614
+ value.name.is_a?(Symbol) ? [value.name] : []
615
+ when Array
616
+ lambda_parameter_names(value)
617
+ else
618
+ []
619
+ end
620
+ end
621
+ end
622
+ <% if runtime.fetch("builder_methods", []).include?("pattern") -%>
623
+ def pattern(kind, values)
624
+ kind = kind.to_sym
625
+ values = Array(values)
626
+ declare_local(values.first) if kind == :rest && values.first.is_a?(Symbol)
627
+ AST::Pattern.new(kind, values.freeze)
628
+ end
629
+
630
+ def pattern_constant(value)
631
+ value.is_a?(Symbol) ? variable(:constant, value) : value
632
+ end
633
+
634
+ def pattern_value(value)
635
+ return value if value.is_a?(AST::Pattern)
636
+ return pattern_variable(value) if value.is_a?(Symbol)
637
+ pattern(:value, [value])
638
+ end
639
+
640
+ def pattern_variable(name)
641
+ declare_local(name)
642
+ pattern(:variable, [name])
643
+ end
644
+
645
+ def pattern_array_parts(pre, rest = nil, post = nil)
646
+ [Array(pre).map { |value| pattern_value(value) }.freeze,
647
+ rest.nil? ? nil : pattern_value(rest),
648
+ Array(post).map { |value| pattern_value(value) }.freeze].freeze
649
+ end
650
+
651
+ def array_pattern(parts)
652
+ pattern(:array, pattern_array_parts(*parts))
653
+ end
654
+
655
+ def pattern_find_parts(pre_rest, values, post_rest)
656
+ rest_pattern = ->(value) { value.nil? ? pattern(:rest, [nil]) : pattern_value(value) }
657
+ [rest_pattern.call(pre_rest), Array(values).map { |value| pattern_value(value) }.freeze,
658
+ rest_pattern.call(post_rest)].freeze
659
+ end
660
+
661
+ def pattern_pair(key, value = :__arpaka_missing_pattern_value__)
662
+ key_node = key.is_a?(AST::Literal) ? key : AST::Literal.new(key)
663
+ value_node = if value == :__arpaka_missing_pattern_value__
664
+ pattern_variable(key)
665
+ else
666
+ pattern_value(value)
667
+ end
668
+ AST::Pair.new(key_node, value_node)
669
+ end
670
+
671
+ def hash_pattern_parts(pairs, rest = nil)
672
+ [Array(pairs).freeze, rest].freeze
673
+ end
674
+ <% end -%>
675
+ <% if runtime.fetch("builder_methods", []).include?("rescue") -%>
676
+ def body_statement(body, clauses, else_body, ensure_body)
677
+ clauses = clauses.nil? ? [] : Array(clauses)
678
+ if clauses.empty? && else_body.nil? && ensure_body.nil?
679
+ body_nodes(body)
680
+ else
681
+ full_rescue(body, clauses, else_body, ensure_body)
682
+ end
683
+ end
684
+
685
+ def rescue_chain(clause, rest)
686
+ [clause] + (rest.nil? ? [] : Array(rest))
687
+ end
688
+
689
+ def rescue_clause(exceptions, variable, body)
690
+ # A splatted rescue exception is represented by RubyVM as an array
691
+ # expression containing the splat, rather than as the splat itself.
692
+ exceptions = Array(exceptions)
693
+ if exceptions.any? { |exception| exception.is_a?(AST::Splat) }
694
+ exceptions = [AST::ArrayLiteral.new(exceptions.freeze)]
695
+ end
696
+ AST::RescueClause.new(exceptions.freeze, variable, body_nodes(body))
697
+ end
698
+
699
+ def full_rescue(body, clauses, else_body, ensure_body)
700
+ AST::FullRescue.new(body_nodes(body), Array(clauses).freeze,
701
+ else_body.nil? ? nil : body_nodes(else_body),
702
+ ensure_body.nil? ? nil : body_nodes(ensure_body))
703
+ end
704
+
705
+ def begin_block(body)
706
+ AST::BeginBlock.new(body_nodes(body))
707
+ end
708
+
709
+ def end_block(body)
710
+ AST::EndBlock.new(body_nodes(body))
711
+ end
712
+ <% end -%>
713
+
714
+ def unary(operator, operand)
715
+ if operator == :! && operand.is_a?(AST::Binary) &&
716
+ [:"&&", :"||"].include?(operand.operator) && logical_chain_starts_with_defined?(operand.operator, operand)
717
+ return negate_logical_chain(operand)
718
+ end
719
+ AST::Unary.new(operator, operand)
720
+ end
721
+
722
+ def negate_logical_chain(value)
723
+ if value.is_a?(AST::Binary) && [:"&&", :"||"].include?(value.operator)
724
+ AST::Binary.new(value.operator, negate_logical_chain(value.left), value.right)
725
+ else
726
+ AST::Unary.new(:!, value)
727
+ end
728
+ end
729
+
730
+ def if_node(condition, then_body, else_body)
731
+ AST::If.new(condition, then_body, else_body)
732
+ end
733
+
734
+ def modifier_if_node(condition, body)
735
+ node = if_node(condition, body, nil)
736
+ @modifier_nodes[node] = true
737
+ node
738
+ end
739
+
740
+ def unless_node(condition, then_body, else_body)
741
+ AST::If.new(AST::Unary.new(:!, condition), then_body, else_body)
742
+ end
743
+
744
+ def modifier_unless_node(condition, body)
745
+ node = unless_node(condition, body, nil)
746
+ @modifier_nodes[node] = true
747
+ node
748
+ end
749
+
750
+ def preserve_modifier_if(original, rewritten)
751
+ @modifier_nodes[rewritten] = true if @modifier_nodes.key?(original)
752
+ rewritten
753
+ end
754
+
755
+ def elsif_node(condition, then_body, else_body)
756
+ AST::If.new(condition, then_body, else_body)
757
+ end
758
+
759
+ def parentheses(statements, rule_id)
760
+ return AST::Sequence.new(statements.freeze) if @defined_depth.to_i.positive?
761
+ return statements.first if statements.length == 1
762
+ AST::Sequence.new(statements.freeze)
763
+ end
764
+
765
+ def begin_defined
766
+ @defined_depth = @defined_depth.to_i + 1
767
+ nil
768
+ end
769
+
770
+ def defined_call(value)
771
+ @defined_depth = [@defined_depth.to_i - 1, 0].max
772
+ if value.is_a?(AST::Ternary)
773
+ condition = value.condition
774
+ if condition.is_a?(AST::Sequence) && condition.statements.length == 1
775
+ condition = condition.statements.first
776
+ return AST::Ternary.new(AST::Call.new(:defined, [condition].freeze),
777
+ value.then_value, value.else_value)
778
+ end
779
+ end
780
+ value = defined_group_value(value)
781
+ operand = defined_logical_operand(value)
782
+ return defined_logical_groups(operand) if operand
783
+ AST::Call.new(:defined, [value].freeze)
784
+ end
785
+
786
+ def defined_group_value(value)
787
+ while value.is_a?(AST::Sequence) && value.statements.length == 1
788
+ value = value.statements.first
789
+ end
790
+ value
791
+ end
792
+
793
+ def defined_logical_operand(value)
794
+ return unless value.is_a?(AST::Binary)
795
+
796
+ left = value.left
797
+ if left.is_a?(AST::Sequence)
798
+ operand = defined_group_value(left)
799
+ return defined_logical_chain(value.operator, AST::Call.new(:defined, [operand].freeze), value.right)
800
+ end
801
+
802
+ nested = defined_logical_operand(left)
803
+ return unless nested
804
+
805
+ defined_logical_chain(value.operator, nested, value.right)
806
+ end
807
+
808
+ def defined_logical_chain(operator, left, right)
809
+ if right.is_a?(AST::Binary) && right.operator == operator && [:"&&", :"||"].include?(operator)
810
+ left = defined_logical_chain(operator, left, right.left)
811
+ return AST::Binary.new(operator, left, right.right)
812
+ end
813
+ AST::Binary.new(operator, left, right)
814
+ end
815
+
816
+ def defined_logical_groups(value)
817
+ value = defined_group_value(value)
818
+ return value unless value.is_a?(AST::Binary)
819
+
820
+ AST::Binary.new(value.operator, defined_logical_groups(value.left), defined_logical_groups(value.right))
821
+ end
822
+
823
+ def identifier(value)
824
+ unless value.is_a?(Symbol) || value.is_a?(String)
825
+ raise ArgumentError, "tIDENTIFIER requires a Symbol or String value"
826
+ end
827
+ value.to_sym
828
+ end
829
+
830
+ def declare_local(name)
831
+ @locals[name] = @locals.fetch(name, 0) + 1
832
+ name
833
+ end
834
+
835
+ def begin_method_scope(value)
836
+ @method_local_scopes << @locals
837
+ @method_lambda_local_counts << @lambda_local_counts
838
+ @locals = {}
839
+ @lambda_local_counts = Hash.new(0)
840
+ @declaring_method_parameters = true
841
+ value
842
+ end
843
+
844
+ def begin_lambda_parameters
845
+ @lambda_parameter_states << @declaring_method_parameters
846
+ @declaring_method_parameters = false
847
+ nil
848
+ end
849
+
850
+ def declare_method_parameter(name)
851
+ declare_local(name)
852
+ end
853
+
854
+ def declare_method_parameter_list(name)
855
+ [declare_local(name)].freeze
856
+ end
857
+
858
+ def end_method_scope
859
+ if @method_local_scopes.any?
860
+ @locals = @method_local_scopes.pop
861
+ @lambda_local_counts = @method_lambda_local_counts.pop
862
+ end
863
+ @declaring_method_parameters = false
864
+ end
865
+
866
+ def finish_method_scope(value)
867
+ end_method_scope
868
+ value
869
+ end
870
+
871
+ def read_local(name)
872
+ return name if name.is_a?(AST::Variable)
873
+ (@locals.key?(name) ? AST::LocalRead : AST::BareCall).new(name)
874
+ end
875
+
876
+ def method_parameter_scope?
877
+ @method_local_scopes.any? && @lambda_parameter_states.empty?
878
+ end
879
+
880
+ # Ruby decides whether an identifier is a local variable for the whole
881
+ # lexical scope. The parser reduces a reference before it necessarily sees
882
+ # a later assignment, so read_local cannot make that decision on its own.
883
+ # Resolve the remaining BareCall nodes once a scope's AST is complete.
884
+ def resolve_local_reads(value, inherited_names, excluded_names = [])
885
+ local_names = ((Array(inherited_names) + local_names_in_scope(value)).uniq - excluded_names)
886
+ rewrite_local_reads(value, local_names, for_target_names_in_scope(value))
887
+ end
888
+
889
+ def resolve_method_local_reads(value, local_names, parameter_names)
890
+ return resolve_local_reads(value, local_names) unless value.is_a?(Array)
891
+
892
+ assigned_names = Array(parameter_names).compact.uniq
893
+ result = value.map do |statement|
894
+ names = (assigned_names + direct_assignment_names(statement) + for_target_names_in_scope(statement)).uniq
895
+ rewritten = rewrite_local_reads(statement, names, for_target_names_in_scope(statement))
896
+ assigned_names |= local_names_in_scope(statement)
897
+ rewritten
898
+ end
899
+ result.freeze if value.frozen?
900
+ result
901
+ end
902
+
903
+ def direct_assignment_names(value)
904
+ return [] unless data_node?(value)
905
+
906
+ case value.class.name.split("::").last
907
+ when "LocalWrite"
908
+ [value.name]
909
+ when "OpAssign"
910
+ target_names(value.target)
911
+ when "MultipleAssign"
912
+ Array(value.targets).flat_map { |target| target_names(target) } + target_names(value.rest) +
913
+ Array(value.post).flat_map { |target| target_names(target) }
914
+ else
915
+ []
916
+ end
917
+ end
918
+
919
+ def for_target_names_in_scope(value)
920
+ case value
921
+ when Array
922
+ return value.flat_map { |item| for_target_names_in_scope(item) }
923
+ end
924
+ return [] unless data_node?(value)
925
+
926
+ if value.class.name.split("::").last == "For"
927
+ target_names(value.variable) + value.members.flat_map do |member|
928
+ for_target_names_in_scope(value.public_send(member))
929
+ end
930
+ else
931
+ value.members.flat_map { |member| for_target_names_in_scope(value.public_send(member)) }
932
+ end
933
+ end
934
+
935
+ def local_names_in_scope(value)
936
+ case value
937
+ when Array
938
+ return value.flat_map { |item| local_names_in_scope(item) }
939
+ end
940
+ return [] unless data_node?(value)
941
+
942
+ case value.class.name.split("::").last
943
+ when "LocalWrite"
944
+ [value.name] + local_names_in_scope(value.value)
945
+ when "OpAssign"
946
+ target_names(value.target) + local_names_in_scope(value.value)
947
+ when "MultipleAssign"
948
+ Array(value.targets).flat_map { |target| target_names(target) } +
949
+ local_names_in_scope(value.value)
950
+ when "For"
951
+ target_names(value.variable) + value.members.flat_map do |member|
952
+ local_names_in_scope(value.public_send(member))
953
+ end
954
+ when "Def", "SingletonMethodDef", "Lambda", "BlockCall",
955
+ "ClassDef", "ModuleDef", "SingletonClassDef"
956
+ []
957
+ else
958
+ value.members.flat_map { |member| local_names_in_scope(value.public_send(member)) }
959
+ end
960
+ end
961
+
962
+ def target_names(target)
963
+ case target
964
+ when Symbol then [target]
965
+ when Array then target.flat_map { |item| target_names(item) }
966
+ else []
967
+ end
968
+ end
969
+
970
+ def declare_local_targets(targets)
971
+ target_names(targets).each { |name| declare_local(name) }
972
+ end
973
+
974
+ def rewrite_local_reads(value, local_names, for_target_names = [])
975
+ case value
976
+ when Array
977
+ result = value.map { |item| rewrite_local_reads(item, local_names, for_target_names) }
978
+ result.freeze if value.frozen?
979
+ return result
980
+ end
981
+ return value unless data_node?(value)
982
+
983
+ type = value.class.name.split("::").last
984
+ if type == "Call" && local_names.include?(value.name) &&
985
+ value.arguments.is_a?(Array) && value.arguments.length == 1 &&
986
+ value.arguments.first.is_a?(AST::ArrayLiteral)
987
+ arguments = rewrite_local_reads(value.arguments.first.elements, local_names, for_target_names)
988
+ return AST::ReceiverCall.new(AST::LocalRead.new(value.name), :".", :[], arguments)
989
+ end
990
+ if type == "BareCall" && local_names.include?(value.name)
991
+ return AST::LocalRead.new(value.name)
992
+ end
993
+ case type
994
+ when "For"
995
+ target_names = target_names(value.variable)
996
+ return value.with(
997
+ enumerable: rewrite_local_reads(value.enumerable, local_names - target_names, for_target_names),
998
+ body: rewrite_local_reads(value.body, local_names, for_target_names)
999
+ )
1000
+ when "If"
1001
+ condition_target_names = for_target_names_in_scope(value.condition)
1002
+ branch_local_names = local_names - condition_target_names
1003
+ return preserve_modifier_if(value, value.with(
1004
+ condition: rewrite_local_reads(value.condition, local_names, for_target_names),
1005
+ then_body: rewrite_local_reads(value.then_body, branch_local_names, for_target_names),
1006
+ else_body: rewrite_local_reads(value.else_body, branch_local_names, for_target_names)
1007
+ ))
1008
+ when "ReceiverCall"
1009
+ receiver = value.receiver
1010
+ unless receiver.is_a?(AST::BareCall) && for_target_names.include?(receiver.name)
1011
+ receiver = rewrite_local_reads(receiver, local_names, for_target_names)
1012
+ end
1013
+ return value.with(receiver: receiver,
1014
+ arguments: rewrite_local_reads(value.arguments, local_names, for_target_names))
1015
+ when "BlockCall"
1016
+ parameter_names = block_parameter_names(value.parameters)
1017
+ return value.with(
1018
+ call: rewrite_local_reads(value.call, local_names, for_target_names),
1019
+ body: rewrite_local_reads(value.body, local_names - parameter_names, for_target_names)
1020
+ )
1021
+ when "SingletonMethodDef"
1022
+ return value.with(receiver: rewrite_local_reads(value.receiver, local_names, for_target_names))
1023
+ when "SingletonClassDef"
1024
+ receiver = rewrite_local_reads(value.receiver, local_names, for_target_names)
1025
+ shadowed_names = local_names
1026
+ shadowed_names += [receiver.name] if receiver.is_a?(AST::LocalRead)
1027
+ return value.with(
1028
+ receiver: receiver,
1029
+ body: rewrite_shadowed_local_reads(value.body, shadowed_names)
1030
+ )
1031
+ when "Lambda"
1032
+ parameter_names = lambda_parameter_names(value.arguments)
1033
+ return value.with(body: resolve_local_reads(value.body, local_names + parameter_names))
1034
+ when "Def", "ClassDef", "ModuleDef"
1035
+ return value
1036
+ end
1037
+
1038
+ fields = value.members.to_h do |member|
1039
+ [member, rewrite_local_reads(value.public_send(member), local_names, for_target_names)]
1040
+ end
1041
+ preserve_modifier_if(value, value.with(**fields))
1042
+ end
1043
+
1044
+ def rewrite_shadowed_local_reads(value, local_names)
1045
+ case value
1046
+ when Array
1047
+ result = value.map { |item| rewrite_shadowed_local_reads(item, local_names) }
1048
+ result.freeze if value.frozen?
1049
+ return result
1050
+ end
1051
+ return value unless data_node?(value)
1052
+
1053
+ type = value.class.name.split("::").last
1054
+ if type == "LocalRead" && local_names.include?(value.name)
1055
+ return AST::BareCall.new(value.name)
1056
+ end
1057
+
1058
+ case type
1059
+ when "BlockCall", "Def", "Lambda", "ClassDef", "ModuleDef", "SingletonClassDef"
1060
+ return value
1061
+ end
1062
+
1063
+ fields = value.members.to_h do |member|
1064
+ [member, rewrite_shadowed_local_reads(value.public_send(member), local_names)]
1065
+ end
1066
+ preserve_modifier_if(value, value.with(**fields))
1067
+ end
1068
+
1069
+ def data_node?(value)
1070
+ value.respond_to?(:members) && value.respond_to?(:with)
1071
+ end
1072
+
1073
+ def assign(name, value)
1074
+ if name.is_a?(AST::ConstantPath) || name.is_a?(AST::AbsoluteConstant)
1075
+ AST::Assignment.new(name, value)
1076
+ else
1077
+ AST::LocalWrite.new(name, value)
1078
+ end
1079
+ end
1080
+ <% if runtime.fetch("builder_methods", []).include?("assignment") -%>
1081
+ def assignment(name, value)
1082
+ if name.is_a?(AST::Field) || name.is_a?(AST::Index)
1083
+ AST::Assignment.new(name, value)
1084
+ else
1085
+ AST::LocalWrite.new(name, value)
1086
+ end
1087
+ end
1088
+
1089
+ def opassign(target, operator, value)
1090
+ AST::OpAssign.new(target, operator.to_s.delete_suffix("=").to_sym, value)
1091
+ end
1092
+
1093
+ def multiple_assign(targets, value)
1094
+ value = array(value) if value.is_a?(Array)
1095
+ if targets.is_a?(AST::MultipleAssign)
1096
+ declare_local_targets(targets.targets)
1097
+ return AST::MultipleAssign.new(targets.targets, value, rest: targets.rest, post: targets.post)
1098
+ end
1099
+ declare_local_targets(targets)
1100
+ AST::MultipleAssign.new(Array(targets).freeze, value)
1101
+ end
1102
+
1103
+ def multiple_assign_post(targets, rest, post)
1104
+ declare_local_targets(targets)
1105
+ declare_local_targets(rest)
1106
+ declare_local_targets(post)
1107
+ AST::MultipleAssign.new(Array(targets).freeze, nil, rest: rest, post: Array(post))
1108
+ end
1109
+
1110
+ def assign_mrhs(targets, value)
1111
+ value = array(value) if value.is_a?(Array)
1112
+ targets = targets.is_a?(Array) ? targets : [targets]
1113
+ return assignment(targets.first, value) if targets.length == 1
1114
+ multiple_assign(targets, value)
1115
+ end
1116
+
1117
+ def field(receiver, operator, name)
1118
+ AST::Field.new(receiver, operator.to_sym, name.to_sym)
1119
+ end
1120
+
1121
+ def alias_node(kind, new_name, old_name)
1122
+ AST::Alias.new(kind, new_name, old_name)
1123
+ end
1124
+
1125
+ def undef_node(names)
1126
+ AST::Undef.new(Array(names).freeze)
1127
+ end
1128
+ <% end -%>
1129
+
1130
+ def array(elements)
1131
+ elements = elements.is_a?(Array) ? elements : [elements]
1132
+ if elements.first.is_a?(AST::Splat)
1133
+ last_splat_index = elements.rindex { |element| element.is_a?(AST::Splat) }
1134
+ trailing = elements[(last_splat_index + 1)..]
1135
+ if trailing && trailing.length > 1 && trailing.none? { |element| element.is_a?(AST::Pair) }
1136
+ elements = elements[0..last_splat_index] + [AST::Splat.new(AST::ArrayLiteral.new(trailing.freeze))]
1137
+ end
1138
+ end
1139
+ pair_index = elements.index { |element| element.is_a?(AST::Pair) }
1140
+ if pair_index && elements.drop(pair_index).all? { |element| element.is_a?(AST::Pair) }
1141
+ elements = elements.take(pair_index) + [AST::HashLiteral.new(elements.drop(pair_index).freeze)]
1142
+ end
1143
+ AST::ArrayLiteral.new(elements.freeze)
1144
+ end
1145
+
1146
+ def word_array(elements, symbols: false)
1147
+ elements ||= []
1148
+ elements = elements.elements if elements.is_a?(AST::ArrayLiteral)
1149
+ values = elements.map do |value|
1150
+ if value.is_a?(AST::InterpolatedString)
1151
+ symbols ? AST::DynamicSymbol.new(value.parts) : value
1152
+ else
1153
+ value = value.value if value.is_a?(AST::StringLiteral)
1154
+ symbols ? value.to_sym : AST::StringLiteral.new(value.to_s)
1155
+ end
1156
+ end
1157
+ AST::ArrayLiteral.new(values.freeze)
1158
+ end
1159
+
1160
+ def hash(pairs)
1161
+ AST::HashLiteral.new(pairs.freeze)
1162
+ end
1163
+
1164
+ def pair(key, value)
1165
+ AST::Pair.new(key, value)
1166
+ end
1167
+
1168
+ def append(list, value)
1169
+ value.nil? ? list : (list + [value]).freeze
1170
+ end
1171
+
1172
+ def unsupported(id)
1173
+ rule, line = RULES.fetch(id)
1174
+ raise UnsupportedSyntax.new(rule, line)
1175
+ end
1176
+ end
1177
+ private_constant :Builder