arpaka 0.1.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -2
- data/README.md +118 -7
- data/Rakefile +15 -1
- data/docs/architecture.dot +30 -0
- data/docs/architecture.md +96 -0
- data/docs/architecture.webp +0 -0
- data/docs/contributes.md +115 -0
- data/docs/usage.md +497 -0
- data/lib/arpaka/cli.rb +5 -50
- data/lib/arpaka/plugins/cli.rb +61 -0
- data/lib/arpaka/plugins/sample.rb +14 -0
- data/lib/arpaka/plugins/sample_runner.rb +21 -0
- data/lib/arpaka/{actions.rb → ruby/actions.rb} +230 -151
- data/lib/arpaka/ruby/class_name_parser.rb +97 -0
- data/lib/arpaka/ruby/cli.rb +64 -0
- data/lib/arpaka/ruby/frontend_emitter.rb +59 -0
- data/lib/arpaka/ruby/generator.rb +41 -0
- data/lib/arpaka/{ruby_grammar.rb → ruby/grammar.rb} +53 -59
- data/lib/arpaka/ruby/grammar_emitter.rb +71 -0
- data/lib/arpaka/ruby/profile.rb +129 -0
- data/lib/arpaka/ruby/profiles/ruby_3_3.rb +800 -0
- data/lib/arpaka/ruby/profiles/ruby_3_4.rb +802 -0
- data/lib/arpaka/ruby/runtime/detailed/ast.rb.erb +92 -0
- data/lib/arpaka/ruby/runtime/detailed/builder.rb.erb +1177 -0
- data/lib/arpaka/{runtime → ruby/runtime}/frontend.rb.erb +17 -4
- data/lib/arpaka/{runtime → ruby/runtime}/lexer.rb.erb +583 -99
- data/lib/arpaka/ruby/runtime/structured.rb.erb +111 -0
- data/lib/arpaka/ruby.rb +23 -0
- data/lib/arpaka.rb +6 -9
- data/lib/lrama/ruby/backend.rb +4 -9
- data/lib/lrama/ruby/parser.rb.erb +35 -9
- data/lib/lrama/ruby/parser_emitter.rb +41 -0
- data/lib/lrama/ruby/table_codec.rb +50 -0
- data/lib/lrama/ruby/version.rb +1 -1
- data/lib/lrama/ruby.rb +1 -0
- data/sig/arpaka.rbs +8 -3
- metadata +46 -13
- data/lib/arpaka/generator.rb +0 -43
- data/lib/arpaka/runtime/ast.rb.erb +0 -35
- data/lib/arpaka/runtime/builder.rb.erb +0 -247
- /data/lib/arpaka/{BSDL → ruby/BSDL} +0 -0
- /data/lib/arpaka/{COPYING → ruby/COPYING} +0 -0
- /data/lib/arpaka/{source.json → ruby/source.json} +0 -0
- /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
|