kapusta 0.13.2 → 0.14.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/README.md +50 -11
- data/bin/check-all +6 -1
- data/bin/compile-examples +7 -2
- data/examples/anagram.kap +3 -3
- data/examples/app/args.kap +9 -0
- data/examples/arrange-coins.kap +3 -3
- data/examples/baseball-game.kap +5 -5
- data/examples/best-time-to-buy-sell-stock.kap +2 -2
- data/examples/binary-search.kap +2 -2
- data/examples/binary-to-decimal.kap +1 -1
- data/examples/blocks-and-kwargs.kap +2 -2
- data/examples/count-effects.kap +1 -1
- data/examples/count-items-matching-rule.kap +18 -0
- data/examples/doto-hygiene.kap +2 -2
- data/examples/doto.kap +2 -2
- data/examples/egg-count.kap +1 -1
- data/examples/exceptions.kap +1 -1
- data/examples/falling-drops.kap +7 -7
- data/examples/fennel-parity-examples.txt +3 -6
- data/examples/good-pairs.kap +18 -0
- data/examples/greet.kap +1 -1
- data/examples/happy-number.kap +2 -2
- data/examples/left-right-difference.kap +60 -0
- data/examples/length-of-last-word.kap +4 -4
- data/examples/manhattan-distance.kap +1 -1
- data/examples/maximum-subarray.kap +23 -7
- data/examples/minimum-start-value.kap +19 -0
- data/examples/move-zeroes.kap +2 -2
- data/examples/mruby-runtime-examples.txt +8 -2
- data/examples/non-constant-local.kap +1 -1
- data/examples/number-of-1-bits.kap +1 -1
- data/examples/number-of-steps.kap +1 -1
- data/examples/palindrome.kap +2 -2
- data/examples/pangram.kap +4 -4
- data/examples/pcall.kap +3 -3
- data/examples/pipeline.kap +4 -4
- data/examples/plus-one.kap +3 -3
- data/examples/raindrops.kap +1 -1
- data/examples/range-width.kap +14 -0
- data/examples/recent-counter.kap +6 -6
- data/examples/require-local-args.kap +9 -0
- data/examples/require-local.kap +7 -0
- data/examples/require-module-local.kap +4 -0
- data/examples/require-module.kap +4 -0
- data/examples/reverse-integer.kap +1 -1
- data/examples/roman-to-integer.kap +3 -3
- data/examples/running-sum.kap +20 -0
- data/examples/safe-lookup.kap +2 -2
- data/examples/single-number.kap +1 -1
- data/examples/stack.kap +14 -14
- data/examples/subtract-product-sum.kap +1 -1
- data/examples/summary-ranges.kap +45 -0
- data/examples/threading.kap +5 -5
- data/examples/tset.kap +1 -1
- data/examples/two-sum-hash.kap +3 -3
- data/examples/two-sum.kap +1 -1
- data/examples/ugly-number.kap +1 -1
- data/examples/underground-system.kap +39 -0
- data/examples/valid-parentheses-1.kap +6 -6
- data/exe/kapusta-ls +49 -2
- data/lib/kapusta/ast.rb +8 -0
- data/lib/kapusta/compiler/emitter/bindings.rb +111 -89
- data/lib/kapusta/compiler/emitter/collections.rb +32 -40
- data/lib/kapusta/compiler/emitter/control_flow.rb +33 -31
- data/lib/kapusta/compiler/emitter/expressions.rb +21 -5
- data/lib/kapusta/compiler/emitter/interop.rb +168 -48
- data/lib/kapusta/compiler/emitter/patterns.rb +12 -14
- data/lib/kapusta/compiler/emitter/support.rb +63 -81
- data/lib/kapusta/compiler/language.rb +522 -0
- data/lib/kapusta/compiler/lua_compat.rb +23 -28
- data/lib/kapusta/compiler/macro_expander.rb +30 -30
- data/lib/kapusta/compiler/macro_lowerer.rb +12 -24
- data/lib/kapusta/compiler/normalizer.rb +25 -17
- data/lib/kapusta/compiler.rb +3 -24
- data/lib/kapusta/env.rb +2 -2
- data/lib/kapusta/errors.rb +2 -1
- data/lib/kapusta/formatter/ast_helpers.rb +78 -0
- data/lib/kapusta/formatter/cli.rb +125 -0
- data/lib/kapusta/formatter/line_helpers.rb +44 -0
- data/lib/kapusta/formatter/validator.rb +32 -0
- data/lib/kapusta/formatter.rb +354 -325
- data/lib/kapusta/lsp/identifier.rb +1 -1
- data/lib/kapusta/lsp/rename.rb +21 -11
- data/lib/kapusta/lsp/scope_walker.rb +122 -212
- data/lib/kapusta/lsp/workspace_index.rb +17 -5
- data/lib/kapusta/reader.rb +4 -2
- data/lib/kapusta/version.rb +1 -1
- data/lib/kapusta.rb +39 -6
- data/spec/cli_spec.rb +13 -0
- data/spec/examples_errors_spec.rb +3 -1
- data/spec/examples_spec.rb +67 -15
- data/spec/formatter_spec.rb +246 -0
- data/spec/lsp_spec.rb +69 -0
- data/spec/require_spec.rb +294 -0
- metadata +20 -2
- data/examples/describe.kap +0 -9
|
@@ -7,24 +7,19 @@ module Kapusta
|
|
|
7
7
|
private
|
|
8
8
|
|
|
9
9
|
def emit_fn(args, env, current_scope)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
#{ruby_name} = #{emit_lambda(pattern, body, fn_env, current_scope)}
|
|
24
|
-
#{ruby_name}
|
|
25
|
-
end.call
|
|
26
|
-
RUBY
|
|
27
|
-
end
|
|
10
|
+
parsed = Language.parse_function_args(args)
|
|
11
|
+
emit_error!(:fn_no_params) unless parsed
|
|
12
|
+
return emit_lambda(parsed.params, parsed.body, env, current_scope) if parsed.anonymous?
|
|
13
|
+
|
|
14
|
+
fn_env = env.child
|
|
15
|
+
ruby_name = define_local(fn_env, parsed.name.name)
|
|
16
|
+
<<~RUBY.chomp
|
|
17
|
+
lambda do
|
|
18
|
+
#{ruby_name} = nil
|
|
19
|
+
#{ruby_name} = #{emit_lambda(parsed.params, parsed.body, fn_env, current_scope)}
|
|
20
|
+
#{ruby_name}
|
|
21
|
+
end.call
|
|
22
|
+
RUBY
|
|
28
23
|
end
|
|
29
24
|
|
|
30
25
|
def emit_lambda(pattern, body, env, current_scope)
|
|
@@ -89,19 +84,21 @@ module Kapusta
|
|
|
89
84
|
end
|
|
90
85
|
|
|
91
86
|
def register_self_method_binding(form, env)
|
|
92
|
-
name_sym = form
|
|
87
|
+
name_sym = Language.parse_function_form(form)&.name
|
|
93
88
|
return unless name_sym.is_a?(Sym) && !name_sym.dotted?
|
|
94
89
|
|
|
95
90
|
ruby_name = Kapusta.kebab_to_snake(name_sym.name)
|
|
96
91
|
return unless direct_method_name?(ruby_name)
|
|
97
92
|
|
|
98
|
-
|
|
93
|
+
parsed = Language.parse_function_form(form)
|
|
94
|
+
env.define(name_sym.name, Env::SelfMethodBinding.new(ruby_name, multi_return_body?(parsed.body)))
|
|
99
95
|
end
|
|
100
96
|
|
|
101
97
|
def emit_toplevel_method_definition(form, env)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
parsed = Language.parse_function_form(form)
|
|
99
|
+
name_sym = parsed.name
|
|
100
|
+
pattern = parsed.params
|
|
101
|
+
body = parsed.body
|
|
105
102
|
return [nil, env] if name_sym.dotted?
|
|
106
103
|
return [nil, env] unless simple_parameter_pattern?(pattern)
|
|
107
104
|
|
|
@@ -109,27 +106,30 @@ module Kapusta
|
|
|
109
106
|
return [nil, env] unless ruby_name
|
|
110
107
|
return [nil, env] if captures_outer_binding?(body, env, pattern_names(pattern))
|
|
111
108
|
|
|
112
|
-
env.define(name_sym.name, Env::MethodBinding.new(ruby_name))
|
|
109
|
+
env.define(name_sym.name, Env::MethodBinding.new(ruby_name, multi_return_body?(body)))
|
|
113
110
|
definition = emit_direct_method_definition(name_sym, pattern, body, env)
|
|
114
|
-
if needs_toplevel_method_bridge?(ruby_name)
|
|
115
|
-
definition = join_code(definition, emit_toplevel_method_bridge(ruby_name))
|
|
116
|
-
end
|
|
117
111
|
[definition, env]
|
|
118
112
|
end
|
|
119
113
|
|
|
114
|
+
def multi_return_body?(body)
|
|
115
|
+
Language.list_head?(body.last, 'values')
|
|
116
|
+
end
|
|
117
|
+
|
|
120
118
|
def emit_named_fn_assignment(form, env, current_scope)
|
|
121
|
-
|
|
119
|
+
parsed = Language.parse_function_form(form)
|
|
120
|
+
name_sym = parsed.name
|
|
122
121
|
ruby_name = define_local(env, name_sym.name)
|
|
123
122
|
fn_env = env.child
|
|
124
123
|
fn_env.define(name_sym.name, ruby_name)
|
|
125
|
-
lambda_code = emit_lambda(
|
|
124
|
+
lambda_code = emit_lambda(parsed.params, parsed.body, fn_env, current_scope)
|
|
126
125
|
["#{ruby_name} = nil\n#{ruby_name} = #{lambda_code}", env]
|
|
127
126
|
end
|
|
128
127
|
|
|
129
128
|
def emit_method_definition(form, env)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
parsed = Language.parse_function_form(form)
|
|
130
|
+
name_sym = parsed.name
|
|
131
|
+
pattern = parsed.params
|
|
132
|
+
body = parsed.body
|
|
133
133
|
direct_definition = emit_direct_method_definition(name_sym, pattern, body, env)
|
|
134
134
|
return direct_definition if direct_definition
|
|
135
135
|
|
|
@@ -184,23 +184,6 @@ module Kapusta
|
|
|
184
184
|
end
|
|
185
185
|
end
|
|
186
186
|
|
|
187
|
-
def needs_toplevel_method_bridge?(ruby_name)
|
|
188
|
-
%w[context describe example it specify].include?(ruby_name)
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def emit_toplevel_method_bridge(ruby_name)
|
|
192
|
-
method_name = ruby_name.to_sym.inspect
|
|
193
|
-
if mruby3_target?
|
|
194
|
-
return [
|
|
195
|
-
"define_singleton_method(#{method_name}) do |*args|",
|
|
196
|
-
indent("Object.instance_method(#{method_name}).bind(self).call(*args)"),
|
|
197
|
-
'end'
|
|
198
|
-
].join("\n")
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
"define_singleton_method(#{method_name}, Object.instance_method(#{method_name}).bind(self))"
|
|
202
|
-
end
|
|
203
|
-
|
|
204
187
|
def emit_method_body(pattern, body, env)
|
|
205
188
|
validate_fn_params!(pattern)
|
|
206
189
|
return emit_simple_method_body(pattern, body, env) if simple_parameter_pattern?(pattern)
|
|
@@ -271,26 +254,29 @@ module Kapusta
|
|
|
271
254
|
end
|
|
272
255
|
|
|
273
256
|
def emit_let_parts(args, env, current_scope, result:)
|
|
274
|
-
|
|
257
|
+
parsed = Language.parse_let_args(args)
|
|
258
|
+
bindings = parsed.bindings
|
|
275
259
|
emit_error!(:let_odd_bindings) if bindings.items.length.odd?
|
|
276
260
|
emit_error!(:let_no_body) if args.length < 2
|
|
277
261
|
|
|
278
|
-
body = args[1..]
|
|
279
262
|
child_env = env.child
|
|
280
263
|
binding_codes = []
|
|
281
|
-
|
|
282
|
-
i = 0
|
|
283
|
-
while i < items.length
|
|
284
|
-
pattern = items[i]
|
|
285
|
-
value_form = items[i + 1]
|
|
264
|
+
parsed.binding_pairs.each do |pattern, value_form|
|
|
286
265
|
check_destructure_value!(pattern, value_form)
|
|
266
|
+
if (constant_code = kapusta_require_module_constant(value_form)) && pattern.is_a?(Sym)
|
|
267
|
+
require_code = emit_expr(value_form, child_env, current_scope)
|
|
268
|
+
bind_code, child_env = emit_constant_require_bind(pattern, require_code, constant_code, child_env)
|
|
269
|
+
mark_mutability(child_env, pattern.name, mutable: false)
|
|
270
|
+
binding_codes << bind_code
|
|
271
|
+
next
|
|
272
|
+
end
|
|
273
|
+
|
|
287
274
|
value_code = emit_expr(value_form, child_env, current_scope)
|
|
288
275
|
bind_code, child_env = emit_pattern_bind(pattern, value_code, child_env)
|
|
289
276
|
walk_pattern_syms(pattern) { |sym| mark_mutability(child_env, sym, mutable: false) }
|
|
290
277
|
binding_codes << bind_code
|
|
291
|
-
i += 2
|
|
292
278
|
end
|
|
293
|
-
body_code, = emit_sequence(body, child_env, current_scope,
|
|
279
|
+
body_code, = emit_sequence(parsed.body, child_env, current_scope,
|
|
294
280
|
allow_method_definitions: false,
|
|
295
281
|
result:)
|
|
296
282
|
[join_binding_codes(binding_codes), body_code]
|
|
@@ -317,15 +303,24 @@ module Kapusta
|
|
|
317
303
|
end
|
|
318
304
|
|
|
319
305
|
def emit_local_form(form, env, current_scope, allow_constant: false)
|
|
320
|
-
|
|
306
|
+
parsed = Language.parse_binding_form(form)
|
|
307
|
+
emit_error!(:local_arity, form: form.head.name) unless parsed
|
|
321
308
|
|
|
322
|
-
target =
|
|
323
|
-
|
|
309
|
+
target = parsed.target
|
|
310
|
+
value_form = parsed.value
|
|
324
311
|
|
|
325
312
|
if target.is_a?(Sym)
|
|
326
313
|
validate_binding_symbol!(target)
|
|
327
|
-
if
|
|
328
|
-
|
|
314
|
+
if parsed.head == 'local' && (constant_code = kapusta_require_module_constant(value_form))
|
|
315
|
+
require_code = emit_expr(value_form, env, current_scope)
|
|
316
|
+
bind_code, env = emit_constant_require_bind(target, require_code, constant_code, env)
|
|
317
|
+
mark_mutability(env, target.name, mutable: false)
|
|
318
|
+
return ["#{bind_code}\nnil", env]
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
value_code = emit_expr(value_form, env, current_scope)
|
|
322
|
+
if allow_constant && parsed.head == 'local' &&
|
|
323
|
+
constant_value?(value_form) &&
|
|
329
324
|
(constant_name = constant_name_for(target.name))
|
|
330
325
|
env.define(target.name, constant_name)
|
|
331
326
|
mark_mutability(env, target.name, mutable: false)
|
|
@@ -333,14 +328,23 @@ module Kapusta
|
|
|
333
328
|
end
|
|
334
329
|
|
|
335
330
|
ruby_name = define_local(env, target.name)
|
|
336
|
-
mark_mutability(env, target.name, mutable:
|
|
331
|
+
mark_mutability(env, target.name, mutable: parsed.mutable?)
|
|
337
332
|
["#{ruby_name} = #{value_code}\nnil", env]
|
|
338
333
|
else
|
|
334
|
+
value_code = emit_expr(value_form, env, current_scope)
|
|
339
335
|
bind_code, env = emit_pattern_bind(target, value_code, env)
|
|
340
336
|
[join_code(bind_code, 'nil'), env]
|
|
341
337
|
end
|
|
342
338
|
end
|
|
343
339
|
|
|
340
|
+
def emit_constant_require_bind(target, require_code, constant_code, env)
|
|
341
|
+
validate_binding_symbol!(target)
|
|
342
|
+
return [require_code, env] if target.name == '_'
|
|
343
|
+
|
|
344
|
+
ruby_name = define_local(env, target.name)
|
|
345
|
+
["#{require_code}\n#{ruby_name} = #{constant_code}", env]
|
|
346
|
+
end
|
|
347
|
+
|
|
344
348
|
def constant_name_for(source_name)
|
|
345
349
|
candidate = source_name.tr('-', '_').upcase
|
|
346
350
|
candidate if candidate.match?(/\A[A-Z][A-Z0-9_]*\z/)
|
|
@@ -354,7 +358,7 @@ module Kapusta
|
|
|
354
358
|
end
|
|
355
359
|
|
|
356
360
|
def check_destructure_value!(pattern, value_form)
|
|
357
|
-
return unless pattern.is_a?(Vec) || pattern.is_a?(HashLit)
|
|
361
|
+
return unless pattern.is_a?(Vec) || pattern.is_a?(List) || pattern.is_a?(HashLit)
|
|
358
362
|
|
|
359
363
|
case value_form
|
|
360
364
|
when String, Numeric, Symbol, true, false
|
|
@@ -372,7 +376,7 @@ module Kapusta
|
|
|
372
376
|
case pattern
|
|
373
377
|
when Sym
|
|
374
378
|
yield pattern unless pattern.name == '_'
|
|
375
|
-
when Vec
|
|
379
|
+
when Vec, List
|
|
376
380
|
pattern.items.each do |item|
|
|
377
381
|
next if item.is_a?(Sym) && ['&', '...'].include?(item.name)
|
|
378
382
|
|
|
@@ -390,24 +394,29 @@ module Kapusta
|
|
|
390
394
|
(@binding_mutability ||= {}).fetch(ruby_name, true)
|
|
391
395
|
end
|
|
392
396
|
|
|
393
|
-
def emit_local_expr(args, env, current_scope)
|
|
394
|
-
|
|
397
|
+
def emit_local_expr(head, args, env, current_scope)
|
|
398
|
+
parsed = Language.parse_binding_args(head, args)
|
|
399
|
+
emit_error!(:local_arity, form: head) unless parsed
|
|
400
|
+
|
|
401
|
+
synthetic = List.new([Sym.new(head), parsed.target, parsed.value])
|
|
402
|
+
code, = emit_local_form(synthetic, env.child, current_scope)
|
|
395
403
|
"lambda do\n#{indent(code)}\nend.call"
|
|
396
404
|
end
|
|
397
405
|
|
|
398
406
|
def emit_global_expr(args, _env, _current_scope)
|
|
399
|
-
|
|
400
|
-
unless
|
|
401
|
-
|
|
407
|
+
parsed = Language.parse_global_args(args)
|
|
408
|
+
emit_error!(:global_arity) unless parsed
|
|
409
|
+
unless parsed.name.is_a?(Sym)
|
|
410
|
+
emit_error!(:global_non_symbol_name, type: parsed.name.class.name.downcase, value: parsed.name.inspect)
|
|
402
411
|
end
|
|
403
412
|
|
|
404
|
-
name =
|
|
405
|
-
"$#{global_name(name)} = #{emit_expr(args[1], Env.new, :toplevel)}\nnil"
|
|
413
|
+
"$#{global_name(parsed.name.name)} = #{emit_expr(parsed.value, Env.new, :toplevel)}\nnil"
|
|
406
414
|
end
|
|
407
415
|
|
|
408
416
|
def emit_set_form(form, env, current_scope)
|
|
409
|
-
|
|
410
|
-
|
|
417
|
+
parsed = Language.parse_set_form(form)
|
|
418
|
+
target = parsed.target
|
|
419
|
+
value_code = emit_expr(parsed.value, env, current_scope)
|
|
411
420
|
|
|
412
421
|
if target.is_a?(Sym) && !target.dotted?
|
|
413
422
|
binding = env.lookup_if_defined(target.name)
|
|
@@ -437,15 +446,22 @@ module Kapusta
|
|
|
437
446
|
end
|
|
438
447
|
|
|
439
448
|
def emit_set_expr(args, env, current_scope)
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
emit_set_target(target, value_code, env, current_scope)
|
|
449
|
+
parsed = Language.parse_set_args(args)
|
|
450
|
+
emit_set_target(parsed.target, emit_expr(parsed.value, env, current_scope), env, current_scope)
|
|
443
451
|
end
|
|
444
452
|
|
|
445
453
|
def emit_set_target(target, value_code, env, current_scope)
|
|
446
454
|
case target
|
|
447
455
|
when Sym
|
|
448
|
-
if target.
|
|
456
|
+
if target.colonized?
|
|
457
|
+
base_code, segments = multihash_base(target.colon_segments, env)
|
|
458
|
+
receiver = simple_expression?(base_code) ? base_code : parenthesize(base_code)
|
|
459
|
+
prefix = segments[0...-1].map do |segment|
|
|
460
|
+
"[#{Kapusta.kebab_to_snake(segment).to_sym.inspect}]"
|
|
461
|
+
end.join
|
|
462
|
+
key = Kapusta.kebab_to_snake(segments.last).to_sym.inspect
|
|
463
|
+
emit_assignment("#{receiver}#{prefix}[#{key}]", value_code)
|
|
464
|
+
elsif target.dotted?
|
|
449
465
|
base_code, segments = multisym_base(target.segments, env)
|
|
450
466
|
receiver = emit_method_path(base_code, segments[0...-1])
|
|
451
467
|
last = segments.last
|
|
@@ -463,19 +479,14 @@ module Kapusta
|
|
|
463
479
|
emit_assignment(binding, value_code)
|
|
464
480
|
end
|
|
465
481
|
when List
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
keys = target.items[2..].map { |item| emit_expr(item, env, current_scope) }
|
|
482
|
+
if (dot_target = Language.parse_dot_target(target))
|
|
483
|
+
object_code = emit_expr(dot_target.object, env, current_scope)
|
|
484
|
+
keys = dot_target.keys.map { |item| emit_expr(item, env, current_scope) }
|
|
470
485
|
receiver = simple_expression?(object_code) ? object_code : parenthesize(object_code)
|
|
471
486
|
prefix = keys[0...-1].map { |k| "[#{k}]" }.join
|
|
472
487
|
emit_assignment("#{receiver}#{prefix}[#{keys.last}]", value_code)
|
|
473
|
-
elsif
|
|
474
|
-
|
|
475
|
-
elsif head.is_a?(Sym) && head.name == 'cvar'
|
|
476
|
-
emit_assignment("@@#{Kapusta.kebab_to_snake(target.items[1].name)}", value_code)
|
|
477
|
-
elsif head.is_a?(Sym) && head.name == 'gvar'
|
|
478
|
-
emit_assignment("$#{global_name(target.items[1].name)}", value_code)
|
|
488
|
+
elsif (sigil = Language.parse_sigil_form(target))
|
|
489
|
+
emit_sigil_assignment(sigil, value_code)
|
|
479
490
|
else
|
|
480
491
|
emit_error!(:bad_set_target, target: target.inspect)
|
|
481
492
|
end
|
|
@@ -483,6 +494,17 @@ module Kapusta
|
|
|
483
494
|
emit_error!(:bad_set_target, target: target.inspect)
|
|
484
495
|
end
|
|
485
496
|
end
|
|
497
|
+
|
|
498
|
+
def emit_sigil_assignment(sigil, value_code)
|
|
499
|
+
name = sigil.name
|
|
500
|
+
emit_error!(:bad_set_target, target: sigil.inspect) unless name.is_a?(Sym)
|
|
501
|
+
|
|
502
|
+
case sigil.kind
|
|
503
|
+
when :ivar then emit_assignment("@#{Kapusta.kebab_to_snake(name.name)}", value_code)
|
|
504
|
+
when :cvar then emit_assignment("@@#{Kapusta.kebab_to_snake(name.name)}", value_code)
|
|
505
|
+
when :gvar then emit_assignment("$#{global_name(name.name)}", value_code)
|
|
506
|
+
end
|
|
507
|
+
end
|
|
486
508
|
end
|
|
487
509
|
end
|
|
488
510
|
end
|
|
@@ -9,37 +9,32 @@ module Kapusta
|
|
|
9
9
|
private
|
|
10
10
|
|
|
11
11
|
def emit_icollect(args, env, current_scope)
|
|
12
|
-
|
|
12
|
+
parsed = Language.parse_iteration_args(args)
|
|
13
|
+
emit_error!(:icollect_no_iterator) unless parsed.bindings.is_a?(Vec) && parsed.bindings.items.length >= 2
|
|
13
14
|
|
|
14
|
-
emit_iteration(
|
|
15
|
-
emit_sequence(
|
|
15
|
+
emit_iteration(parsed.bindings, env, current_scope, method: 'filter_map') do |iter_env|
|
|
16
|
+
emit_sequence(parsed.body, iter_env, current_scope, allow_method_definitions: false).first
|
|
16
17
|
end
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def emit_collect(args, env, current_scope)
|
|
21
|
+
parsed = Language.parse_iteration_args(args)
|
|
20
22
|
result_var = temp('result')
|
|
21
|
-
values_form =
|
|
22
|
-
emit_iteration(
|
|
23
|
+
values_form = Language.parse_values_form(parsed.body[0]) if parsed.body.length == 1
|
|
24
|
+
emit_iteration(parsed.bindings, env, current_scope,
|
|
23
25
|
method: 'each_with_object({})', extra_block_param: result_var) do |iter_env|
|
|
24
26
|
if values_form
|
|
25
27
|
emit_collect_values_step(result_var, values_form, iter_env, current_scope)
|
|
26
28
|
else
|
|
27
|
-
body = emit_sequence(
|
|
29
|
+
body = emit_sequence(parsed.body, iter_env, current_scope, allow_method_definitions: false).first
|
|
28
30
|
emit_hash_collection_step(result_var, body)
|
|
29
31
|
end
|
|
30
32
|
end
|
|
31
33
|
end
|
|
32
34
|
|
|
33
|
-
def simple_values_call(form)
|
|
34
|
-
return unless form.is_a?(List) && form.items.length == 3
|
|
35
|
-
|
|
36
|
-
head = form.head
|
|
37
|
-
form if head.is_a?(Sym) && head.name == 'values'
|
|
38
|
-
end
|
|
39
|
-
|
|
40
35
|
def emit_collect_values_step(result_var, values_form, iter_env, current_scope)
|
|
41
|
-
key_form = values_form.
|
|
42
|
-
val_form = values_form.
|
|
36
|
+
key_form = values_form.key
|
|
37
|
+
val_form = values_form.value
|
|
43
38
|
key_code = emit_expr(key_form, iter_env, current_scope)
|
|
44
39
|
val_code = emit_expr(val_form, iter_env, current_scope)
|
|
45
40
|
assignment = "#{result_var}[#{key_code}] = #{val_code}"
|
|
@@ -60,32 +55,29 @@ module Kapusta
|
|
|
60
55
|
|
|
61
56
|
def emit_fcollect(args, env, current_scope)
|
|
62
57
|
result_var = temp('result')
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
loop_form = Language.parse_counted_for_args(args)
|
|
59
|
+
parsed = parse_counted_for_bindings(loop_form, env, current_scope)
|
|
60
|
+
body_code, = emit_sequence(loop_form.body, parsed[:loop_env], current_scope,
|
|
61
|
+
allow_method_definitions: false)
|
|
65
62
|
collecting_body = emit_array_collection_step(result_var, body_code)
|
|
66
63
|
loop_code = emit_counted_loop(**parsed, current_scope:, body_code: collecting_body)
|
|
67
64
|
emit_collection_result(result_var, '[]', loop_code)
|
|
68
65
|
end
|
|
69
66
|
|
|
70
67
|
def emit_accumulate(args, env, current_scope)
|
|
71
|
-
|
|
72
|
-
emit_error!(:accumulate_no_iterator) if
|
|
73
|
-
|
|
74
|
-
acc_name = bindings[0]
|
|
75
|
-
init_code = emit_expr(bindings[1], env, current_scope)
|
|
76
|
-
iter_items = bindings[2..]
|
|
77
|
-
iter_expr = iter_items.last
|
|
78
|
-
binding_pats = iter_items[0...-1]
|
|
68
|
+
parsed = Language.parse_accumulate_args(args)
|
|
69
|
+
emit_error!(:accumulate_no_iterator) if parsed.items.length < 4
|
|
79
70
|
|
|
71
|
+
init_code = emit_expr(parsed.initial, env, current_scope)
|
|
80
72
|
body_env = env.child
|
|
81
|
-
acc_var = define_local(body_env, acc_name.name)
|
|
73
|
+
acc_var = define_local(body_env, parsed.acc_name.name)
|
|
82
74
|
|
|
83
|
-
inject_code = try_emit_inject(iter_expr, binding_pats, body_env, env, current_scope,
|
|
84
|
-
init_code,
|
|
75
|
+
inject_code = try_emit_inject(parsed.iter_expr, parsed.binding_pats, body_env, env, current_scope,
|
|
76
|
+
acc_var, init_code, parsed.body)
|
|
85
77
|
return inject_code if inject_code
|
|
86
78
|
|
|
87
|
-
iter_code = emit_iteration(Vec.new(iter_items), body_env, current_scope) do |iter_env|
|
|
88
|
-
body_code, = emit_sequence(
|
|
79
|
+
iter_code = emit_iteration(Vec.new(parsed.iter_items), body_env, current_scope) do |iter_env|
|
|
80
|
+
body_code, = emit_sequence(parsed.body, iter_env, current_scope, allow_method_definitions: false)
|
|
89
81
|
emit_sequence_value_assignment(acc_var, body_code)
|
|
90
82
|
end
|
|
91
83
|
[
|
|
@@ -108,18 +100,18 @@ module Kapusta
|
|
|
108
100
|
end
|
|
109
101
|
|
|
110
102
|
def emit_faccumulate(args, env, current_scope)
|
|
111
|
-
|
|
112
|
-
emit_error!(:accumulate_no_iterator) if
|
|
103
|
+
parsed = Language.parse_faccumulate_args(args)
|
|
104
|
+
emit_error!(:accumulate_no_iterator) if parsed.items.length < 5
|
|
113
105
|
|
|
114
106
|
body_env = env.child
|
|
115
|
-
acc_var = define_local(body_env,
|
|
116
|
-
loop_var = define_local(body_env,
|
|
117
|
-
|
|
118
|
-
init_code = emit_expr(
|
|
119
|
-
start_code = emit_expr(
|
|
120
|
-
finish_code = emit_expr(
|
|
121
|
-
step_code =
|
|
122
|
-
body_code, = emit_sequence(
|
|
107
|
+
acc_var = define_local(body_env, parsed.acc_name.name)
|
|
108
|
+
loop_var = define_local(body_env, parsed.counter.name)
|
|
109
|
+
|
|
110
|
+
init_code = emit_expr(parsed.initial, env, current_scope)
|
|
111
|
+
start_code = emit_expr(parsed.start, env, current_scope)
|
|
112
|
+
finish_code = emit_expr(parsed.finish, env, current_scope)
|
|
113
|
+
step_code = parsed.step ? emit_expr(parsed.step, env, current_scope) : nil
|
|
114
|
+
body_code, = emit_sequence(parsed.body, body_env, current_scope, allow_method_definitions: false)
|
|
123
115
|
|
|
124
116
|
receiver =
|
|
125
117
|
if step_code
|
|
@@ -53,7 +53,7 @@ module Kapusta
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def if_form?(form)
|
|
56
|
-
|
|
56
|
+
Language.list_head?(form, 'if')
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def emit_case(args, env, current_scope, mode)
|
|
@@ -78,28 +78,28 @@ module Kapusta
|
|
|
78
78
|
def build_case_parts(args, env, current_scope, mode)
|
|
79
79
|
emit_error!(:case_no_subject) if args.empty?
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
emit_error!(:case_no_patterns) if clauses.empty?
|
|
83
|
-
emit_error!(:case_odd_patterns) if clauses.length.odd?
|
|
81
|
+
parsed = Language.parse_case_args(args)
|
|
82
|
+
emit_error!(:case_no_patterns) if parsed.clauses.empty?
|
|
83
|
+
emit_error!(:case_odd_patterns) if parsed.clauses.length.odd?
|
|
84
84
|
|
|
85
|
-
value_code = emit_expr(
|
|
86
|
-
if simple_case_subject?(
|
|
87
|
-
body = emit_case_body(value_code,
|
|
85
|
+
value_code = emit_expr(parsed.subject, env, current_scope)
|
|
86
|
+
if simple_case_subject?(parsed.subject) && simple_expression?(value_code)
|
|
87
|
+
body = emit_case_body(value_code, parsed, env, current_scope, mode)
|
|
88
88
|
emit_error!(:case_unsupported) unless body
|
|
89
89
|
return [value_code, nil, body]
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
value_var = temp('case_value')
|
|
93
|
-
body = emit_case_body(value_var,
|
|
93
|
+
body = emit_case_body(value_var, parsed, env, current_scope, mode)
|
|
94
94
|
emit_error!(:case_unsupported) unless body
|
|
95
95
|
[value_code, value_var, body]
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
-
def emit_case_body(value_var,
|
|
99
|
-
return try_emit_compat_case(value_var,
|
|
98
|
+
def emit_case_body(value_var, parsed, env, current_scope, mode)
|
|
99
|
+
return try_emit_compat_case(value_var, parsed, env, current_scope, mode) if mruby3_target?
|
|
100
100
|
|
|
101
|
-
try_emit_native_case(value_var,
|
|
102
|
-
try_emit_compat_case(value_var,
|
|
101
|
+
try_emit_native_case(value_var, parsed, env, current_scope, mode) ||
|
|
102
|
+
try_emit_compat_case(value_var, parsed, env, current_scope, mode)
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
def simple_case_subject?(form)
|
|
@@ -110,29 +110,28 @@ module Kapusta
|
|
|
110
110
|
end
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
def try_emit_native_case(value_var,
|
|
114
|
-
arms = collect_case_arms(
|
|
113
|
+
def try_emit_native_case(value_var, parsed, env, current_scope, mode)
|
|
114
|
+
arms = collect_case_arms(parsed.complete_arms) do |pattern, body, where_guards|
|
|
115
115
|
try_native_arm(pattern, body, where_guards, env, current_scope, mode)
|
|
116
116
|
end
|
|
117
117
|
return unless arms
|
|
118
118
|
|
|
119
|
-
arms << ['else', indent('nil')].join("\n") unless wildcard_last?(
|
|
119
|
+
arms << ['else', indent('nil')].join("\n") unless wildcard_last?(parsed.complete_arms)
|
|
120
120
|
["case #{value_var}", *arms, 'end'].join("\n")
|
|
121
121
|
end
|
|
122
122
|
|
|
123
|
-
def collect_case_arms(
|
|
123
|
+
def collect_case_arms(arm_pairs)
|
|
124
124
|
arms = []
|
|
125
125
|
i = 0
|
|
126
|
-
while i <
|
|
127
|
-
pattern =
|
|
128
|
-
body = clauses[i + 1]
|
|
126
|
+
while i < arm_pairs.length
|
|
127
|
+
pattern, body = arm_pairs[i]
|
|
129
128
|
inner, where_guards = extract_pattern_and_guards(pattern)
|
|
130
|
-
sub_patterns =
|
|
129
|
+
sub_patterns = Language.parse_or_pattern(inner)&.alternatives || [inner]
|
|
131
130
|
sub_arms = sub_patterns.map { |sub| yield sub, body, where_guards }
|
|
132
131
|
return if sub_arms.any?(&:nil?)
|
|
133
132
|
|
|
134
133
|
arms.concat(sub_arms)
|
|
135
|
-
i +=
|
|
134
|
+
i += 1
|
|
136
135
|
end
|
|
137
136
|
arms
|
|
138
137
|
end
|
|
@@ -151,8 +150,8 @@ module Kapusta
|
|
|
151
150
|
["in #{plan[:pattern]}#{guard_clause}", indent(body_code)].join("\n")
|
|
152
151
|
end
|
|
153
152
|
|
|
154
|
-
def try_emit_compat_case(value_var,
|
|
155
|
-
arms = collect_case_arms(
|
|
153
|
+
def try_emit_compat_case(value_var, parsed, env, current_scope, mode)
|
|
154
|
+
arms = collect_case_arms(parsed.complete_arms) do |pattern, body, where_guards|
|
|
156
155
|
try_compat_arm(pattern, body, where_guards, value_var, env, current_scope, mode)
|
|
157
156
|
end
|
|
158
157
|
return unless arms
|
|
@@ -160,15 +159,16 @@ module Kapusta
|
|
|
160
159
|
emit_compat_case_lines(arms)
|
|
161
160
|
end
|
|
162
161
|
|
|
163
|
-
def wildcard_last?(
|
|
164
|
-
last_pattern =
|
|
162
|
+
def wildcard_last?(arm_pairs)
|
|
163
|
+
last_pattern = arm_pairs.last&.first
|
|
165
164
|
last_pattern.is_a?(Sym) && last_pattern.name == '_'
|
|
166
165
|
end
|
|
167
166
|
|
|
168
167
|
def extract_pattern_and_guards(pattern)
|
|
169
|
-
|
|
168
|
+
parsed = Language.parse_where_pattern(pattern)
|
|
169
|
+
return [pattern, []] unless parsed
|
|
170
170
|
|
|
171
|
-
[
|
|
171
|
+
[parsed.inner, parsed.guards]
|
|
172
172
|
end
|
|
173
173
|
|
|
174
174
|
def try_compat_arm(pattern, body, where_guards, value_var, env, current_scope, mode)
|
|
@@ -251,16 +251,18 @@ module Kapusta
|
|
|
251
251
|
end
|
|
252
252
|
|
|
253
253
|
def emit_for_statement(args, env, current_scope)
|
|
254
|
-
|
|
255
|
-
|
|
254
|
+
loop_form = Language.parse_counted_for_args(args)
|
|
255
|
+
parsed = parse_counted_for_bindings(loop_form, env, current_scope)
|
|
256
|
+
body_code, = emit_sequence(loop_form.body, parsed[:loop_env], current_scope,
|
|
256
257
|
allow_method_definitions: false,
|
|
257
258
|
result: false)
|
|
258
259
|
emit_counted_loop(**parsed, current_scope:, body_code:)
|
|
259
260
|
end
|
|
260
261
|
|
|
261
262
|
def emit_each_statement(args, env, current_scope)
|
|
262
|
-
|
|
263
|
-
|
|
263
|
+
parsed = Language.parse_iteration_args(args)
|
|
264
|
+
emit_iteration(parsed.bindings, env, current_scope) do |iter_env|
|
|
265
|
+
emit_sequence(parsed.body, iter_env, current_scope,
|
|
264
266
|
allow_method_definitions: false,
|
|
265
267
|
result: false).first
|
|
266
268
|
end
|