kapusta 0.1.3 → 0.1.4
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 +1 -0
- data/examples/contains-duplicate.kap +7 -0
- data/lib/kapusta/compiler/emitter/bindings.rb +87 -39
- data/lib/kapusta/compiler/emitter/collections.rb +47 -49
- data/lib/kapusta/compiler/emitter/control_flow.rb +59 -18
- data/lib/kapusta/compiler/emitter/expressions.rb +1 -1
- data/lib/kapusta/compiler/emitter/interop.rb +105 -22
- data/lib/kapusta/compiler/emitter/patterns.rb +3 -6
- data/lib/kapusta/compiler/emitter/support.rb +92 -29
- data/lib/kapusta/compiler/runtime.rb +49 -49
- data/lib/kapusta/compiler.rb +2 -1
- data/lib/kapusta/env.rb +8 -0
- data/lib/kapusta/error.rb +5 -0
- data/lib/kapusta/formatter.rb +1 -10
- data/lib/kapusta/reader.rb +13 -9
- data/lib/kapusta/version.rb +1 -1
- data/lib/kapusta.rb +1 -0
- data/spec/cli_spec.rb +12 -21
- data/spec/examples_spec.rb +5 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c136e4c7cc9c5dde0ced8c828ab80882900b278db86010986eb7271a7b45309e
|
|
4
|
+
data.tar.gz: 46ff912715738e99c454727224a5e19f6d1635fbfccb1ade3820a9a51a996d0d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d36ad79125c72a9e804f10ce9e0e5983e852b428f1a1a3718e21ef9394c66f39e57bec366ed81257bd5e62439429cc1cd93039710404db2c8dc0f3d9c09c44a
|
|
7
|
+
data.tar.gz: 0cb236654de0a57184387087c5a7f47dc49d548c1fed0167638439c071b83cda592cd0346f68ffc086c74afccff0b1f08c90ec977238213b9672fa8244052b27
|
data/README.md
CHANGED
|
@@ -41,6 +41,7 @@ Kapusta keeps most core Fennel forms. The main differences come from Ruby's runt
|
|
|
41
41
|
| `string.format`, `table.insert`, etc. | use Ruby methods and stdlib instead |
|
|
42
42
|
| `values` uses Lua multiple returns | `values` lowers to a Ruby array, usually destructured |
|
|
43
43
|
| `with-open`, `tail!` | not provided |
|
|
44
|
+
| macros | not provided for now |
|
|
44
45
|
|
|
45
46
|
Kapusta-specific additions:
|
|
46
47
|
|
|
@@ -13,9 +13,8 @@ module Kapusta
|
|
|
13
13
|
name_sym = args[0]
|
|
14
14
|
pattern = args[1]
|
|
15
15
|
body = args[2..]
|
|
16
|
-
ruby_name = temp(sanitize_local(name_sym.name))
|
|
17
16
|
fn_env = env.child
|
|
18
|
-
fn_env
|
|
17
|
+
ruby_name = define_local(fn_env, name_sym.name)
|
|
19
18
|
<<~RUBY.chomp
|
|
20
19
|
(-> do
|
|
21
20
|
#{ruby_name} = nil
|
|
@@ -27,22 +26,40 @@ module Kapusta
|
|
|
27
26
|
end
|
|
28
27
|
|
|
29
28
|
def emit_lambda(pattern, body, env, current_scope)
|
|
29
|
+
return emit_simple_lambda(pattern, body, env, current_scope) if simple_parameter_pattern?(pattern)
|
|
30
|
+
|
|
30
31
|
args_var = temp('args')
|
|
31
32
|
body_env = env.child
|
|
32
33
|
bindings_code, body_env = emit_pattern_bind(pattern, args_var, body_env)
|
|
33
34
|
body_code, = emit_sequence(body, body_env, current_scope, allow_method_definitions: false)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
block_locals = pattern_names(pattern).map { |name| body_env.lookup(name) }.uniq
|
|
36
|
+
block_locals_clause = block_locals.empty? ? '' : "; #{block_locals.join(', ')}"
|
|
37
|
+
[
|
|
38
|
+
"->(*#{args_var}#{block_locals_clause}) do",
|
|
39
|
+
indent(join_code(bindings_code, body_code)),
|
|
40
|
+
'end'
|
|
41
|
+
].join("\n")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def emit_simple_lambda(pattern, body, env, current_scope)
|
|
45
|
+
body_env = env.child
|
|
46
|
+
params = pattern.items.map { |sym| define_local(body_env, sym.name, shadow: true) }
|
|
47
|
+
body_code, = emit_sequence(body, body_env, current_scope, allow_method_definitions: false)
|
|
48
|
+
header = params.empty? ? 'proc do' : "proc do |#{params.join(', ')}|"
|
|
49
|
+
[
|
|
50
|
+
header,
|
|
51
|
+
indent(body_code),
|
|
52
|
+
'end'
|
|
53
|
+
].join("\n")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def simple_parameter_pattern?(pattern)
|
|
57
|
+
pattern.is_a?(Vec) && pattern.items.all? { |item| item.is_a?(Sym) && !item.dotted? && item.name != '&' }
|
|
40
58
|
end
|
|
41
59
|
|
|
42
60
|
def emit_named_fn_assignment(form, env, current_scope)
|
|
43
61
|
name_sym = form.items[1]
|
|
44
|
-
ruby_name =
|
|
45
|
-
env.define(name_sym.name, ruby_name)
|
|
62
|
+
ruby_name = define_local(env, name_sym.name)
|
|
46
63
|
fn_env = env.child
|
|
47
64
|
fn_env.define(name_sym.name, ruby_name)
|
|
48
65
|
lambda_code = emit_lambda(form.items[2], form.items[3..], fn_env, current_scope)
|
|
@@ -53,31 +70,59 @@ module Kapusta
|
|
|
53
70
|
name_sym = form.items[1]
|
|
54
71
|
pattern = form.items[2]
|
|
55
72
|
body = form.items[3..]
|
|
56
|
-
|
|
57
|
-
args_var = temp('args')
|
|
58
|
-
bindings_code, body_env = emit_pattern_bind(pattern, args_var, method_env)
|
|
59
|
-
body_code, = emit_sequence(body, body_env, :toplevel, allow_method_definitions: false)
|
|
73
|
+
block_header, body_code = emit_method_body(pattern, body, env)
|
|
60
74
|
|
|
61
75
|
if name_sym.name.start_with?('self.')
|
|
62
76
|
ruby_name = Kapusta.kebab_to_snake(name_sym.name.delete_prefix('self.')).to_sym.inspect
|
|
63
|
-
|
|
64
|
-
define_singleton_method(#{ruby_name})
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
RUBY
|
|
77
|
+
[
|
|
78
|
+
"define_singleton_method(#{ruby_name}) #{block_header}",
|
|
79
|
+
indent(body_code),
|
|
80
|
+
'end'
|
|
81
|
+
].join("\n")
|
|
69
82
|
else
|
|
70
83
|
ruby_name = Kapusta.kebab_to_snake(name_sym.name).to_sym.inspect
|
|
71
|
-
|
|
72
|
-
define_method(#{ruby_name})
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
RUBY
|
|
84
|
+
[
|
|
85
|
+
"define_method(#{ruby_name}) #{block_header}",
|
|
86
|
+
indent(body_code),
|
|
87
|
+
'end'
|
|
88
|
+
].join("\n")
|
|
77
89
|
end
|
|
78
90
|
end
|
|
79
91
|
|
|
92
|
+
def emit_method_body(pattern, body, env)
|
|
93
|
+
return emit_simple_method_body(pattern, body, env) if simple_parameter_pattern?(pattern)
|
|
94
|
+
|
|
95
|
+
args_var = temp('args')
|
|
96
|
+
method_env = env.child
|
|
97
|
+
bindings_code, body_env = emit_pattern_bind(pattern, args_var, method_env)
|
|
98
|
+
body_code, = emit_sequence(body, body_env, :toplevel, allow_method_definitions: false)
|
|
99
|
+
block_locals = pattern_names(pattern).map { |name| body_env.lookup(name) }.uniq
|
|
100
|
+
block_locals_clause = block_locals.empty? ? '' : "; #{block_locals.join(', ')}"
|
|
101
|
+
["do |*#{args_var}#{block_locals_clause}|", join_code(bindings_code, body_code)]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def emit_simple_method_body(pattern, body, env)
|
|
105
|
+
body_env = env.child
|
|
106
|
+
params = pattern.items.map { |sym| define_local(body_env, sym.name, shadow: true) }
|
|
107
|
+
body_code, = emit_sequence(body, body_env, :toplevel, allow_method_definitions: false)
|
|
108
|
+
[params.empty? ? 'do' : "do |#{params.join(', ')}|", body_code]
|
|
109
|
+
end
|
|
110
|
+
|
|
80
111
|
def emit_let(args, env, current_scope)
|
|
112
|
+
binding_code, body_code = emit_let_parts(args, env, current_scope, result: true)
|
|
113
|
+
<<~RUBY.chomp
|
|
114
|
+
(-> do
|
|
115
|
+
#{indent(join_code(binding_code, body_code))}
|
|
116
|
+
end).call
|
|
117
|
+
RUBY
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def emit_let_statement(args, env, current_scope)
|
|
121
|
+
binding_code, body_code = emit_let_parts(args, env, current_scope, result: false)
|
|
122
|
+
join_code(binding_code, body_code)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def emit_let_parts(args, env, current_scope, result:)
|
|
81
126
|
bindings = args[0]
|
|
82
127
|
body = args[1..]
|
|
83
128
|
child_env = env.child
|
|
@@ -91,13 +136,14 @@ module Kapusta
|
|
|
91
136
|
binding_codes << bind_code
|
|
92
137
|
i += 2
|
|
93
138
|
end
|
|
94
|
-
body_code, = emit_sequence(body, child_env, current_scope,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
139
|
+
body_code, = emit_sequence(body, child_env, current_scope,
|
|
140
|
+
allow_method_definitions: false,
|
|
141
|
+
result:)
|
|
142
|
+
[binding_codes.join("\n"), body_code]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def join_code(*chunks)
|
|
146
|
+
chunks.reject(&:empty?).join("\n")
|
|
101
147
|
end
|
|
102
148
|
|
|
103
149
|
def emit_local_form(form, env, current_scope)
|
|
@@ -105,8 +151,7 @@ module Kapusta
|
|
|
105
151
|
value_code = emit_expr(form.items[2], env, current_scope)
|
|
106
152
|
|
|
107
153
|
if target.is_a?(Sym)
|
|
108
|
-
ruby_name =
|
|
109
|
-
env.define(target.name, ruby_name)
|
|
154
|
+
ruby_name = define_local(env, target.name)
|
|
110
155
|
["#{ruby_name} = #{value_code}\nnil", env]
|
|
111
156
|
else
|
|
112
157
|
bind_code, env = emit_pattern_bind(target, value_code, env)
|
|
@@ -128,9 +173,7 @@ module Kapusta
|
|
|
128
173
|
if env.defined?(target.name)
|
|
129
174
|
env.lookup(target.name)
|
|
130
175
|
else
|
|
131
|
-
|
|
132
|
-
env.define(target.name, fresh)
|
|
133
|
-
fresh
|
|
176
|
+
define_local(env, target.name)
|
|
134
177
|
end
|
|
135
178
|
["#{ruby_name} = #{value_code}", env]
|
|
136
179
|
else
|
|
@@ -164,7 +207,12 @@ module Kapusta
|
|
|
164
207
|
elsif head.is_a?(Sym) && head.name == 'cvar'
|
|
165
208
|
runtime_call(:set_cvar, 'self', target.items[1].name.inspect, value_code)
|
|
166
209
|
elsif head.is_a?(Sym) && head.name == 'gvar'
|
|
167
|
-
|
|
210
|
+
ruby_name = global_name(target.items[1].name)
|
|
211
|
+
if direct_global_name?(ruby_name)
|
|
212
|
+
"$#{ruby_name} = #{value_code}"
|
|
213
|
+
else
|
|
214
|
+
runtime_call(:set_gvar, target.items[1].name.inspect, value_code)
|
|
215
|
+
end
|
|
168
216
|
else
|
|
169
217
|
raise Error, "bad set target: #{target.inspect}"
|
|
170
218
|
end
|
|
@@ -10,75 +10,39 @@ module Kapusta
|
|
|
10
10
|
result_var = temp('result')
|
|
11
11
|
iter_code = emit_iteration(args[0], env, current_scope) do |iter_env|
|
|
12
12
|
body = emit_sequence(args[1..], iter_env, current_scope, allow_method_definitions: false).first
|
|
13
|
-
|
|
14
|
-
__kap_value = begin
|
|
15
|
-
#{indent(body)}
|
|
16
|
-
end
|
|
17
|
-
#{result_var} << __kap_value unless __kap_value.nil?
|
|
18
|
-
RUBY
|
|
13
|
+
emit_array_collection_step(result_var, body)
|
|
19
14
|
end
|
|
20
|
-
|
|
21
|
-
(-> do
|
|
22
|
-
#{result_var} = []
|
|
23
|
-
#{iter_code}
|
|
24
|
-
#{result_var}
|
|
25
|
-
end).call
|
|
26
|
-
RUBY
|
|
15
|
+
emit_collection_result(result_var, '[]', iter_code)
|
|
27
16
|
end
|
|
28
17
|
|
|
29
18
|
def emit_collect(args, env, current_scope)
|
|
30
19
|
result_var = temp('result')
|
|
31
20
|
iter_code = emit_iteration(args[0], env, current_scope) do |iter_env|
|
|
32
21
|
body = emit_sequence(args[1..], iter_env, current_scope, allow_method_definitions: false).first
|
|
33
|
-
|
|
34
|
-
__kap_pair = begin
|
|
35
|
-
#{indent(body)}
|
|
36
|
-
end
|
|
37
|
-
if __kap_pair.is_a?(Array) && __kap_pair.length == 2 && !__kap_pair[0].nil? && !__kap_pair[1].nil?
|
|
38
|
-
#{result_var}[__kap_pair[0]] = __kap_pair[1]
|
|
39
|
-
end
|
|
40
|
-
RUBY
|
|
22
|
+
emit_hash_collection_step(result_var, body)
|
|
41
23
|
end
|
|
42
|
-
|
|
43
|
-
(-> do
|
|
44
|
-
#{result_var} = {}
|
|
45
|
-
#{iter_code}
|
|
46
|
-
#{result_var}
|
|
47
|
-
end).call
|
|
48
|
-
RUBY
|
|
24
|
+
emit_collection_result(result_var, '{}', iter_code)
|
|
49
25
|
end
|
|
50
26
|
|
|
51
27
|
def emit_fcollect(args, env, current_scope)
|
|
52
28
|
result_var = temp('result')
|
|
53
29
|
parsed = parse_counted_for_bindings(args[0].items, env, current_scope)
|
|
54
30
|
body_code, = emit_sequence(args[1..], parsed[:loop_env], current_scope, allow_method_definitions: false)
|
|
55
|
-
collecting_body =
|
|
56
|
-
__kap_value = begin
|
|
57
|
-
#{indent(body_code)}
|
|
58
|
-
end
|
|
59
|
-
#{result_var} << __kap_value unless __kap_value.nil?
|
|
60
|
-
RUBY
|
|
31
|
+
collecting_body = emit_array_collection_step(result_var, body_code)
|
|
61
32
|
loop_code = emit_counted_loop(**parsed, current_scope:, body_code: collecting_body)
|
|
62
|
-
|
|
63
|
-
(-> do
|
|
64
|
-
#{result_var} = []
|
|
65
|
-
#{indent(loop_code)}
|
|
66
|
-
#{result_var}
|
|
67
|
-
end).call
|
|
68
|
-
RUBY
|
|
33
|
+
emit_collection_result(result_var, '[]', loop_code)
|
|
69
34
|
end
|
|
70
35
|
|
|
71
36
|
def emit_accumulate(args, env, current_scope)
|
|
72
37
|
bindings = args[0].items
|
|
73
38
|
acc_name = bindings[0]
|
|
74
|
-
acc_var = temp(sanitize_local(acc_name.name))
|
|
75
39
|
iter_bindings = Vec.new(bindings[2..])
|
|
76
40
|
loop_env = env.child
|
|
77
|
-
loop_env
|
|
41
|
+
acc_var = define_local(loop_env, acc_name.name)
|
|
78
42
|
iter_code = emit_iteration(iter_bindings, loop_env, current_scope) do |iter_env|
|
|
79
43
|
iter_env.define(acc_name.name, acc_var)
|
|
80
44
|
emit_sequence(args[1..], iter_env, current_scope, allow_method_definitions: false).first.then do |body|
|
|
81
|
-
|
|
45
|
+
emit_sequence_value_assignment(acc_var, body)
|
|
82
46
|
end
|
|
83
47
|
end
|
|
84
48
|
<<~RUBY.chomp
|
|
@@ -93,14 +57,12 @@ module Kapusta
|
|
|
93
57
|
def emit_faccumulate(args, env, current_scope)
|
|
94
58
|
bindings = args[0].items
|
|
95
59
|
acc_name = bindings[0]
|
|
96
|
-
acc_var = temp(sanitize_local(acc_name.name))
|
|
97
60
|
loop_name = bindings[2]
|
|
98
|
-
loop_var = temp(sanitize_local(loop_name.name))
|
|
99
61
|
loop_env = env.child
|
|
100
|
-
loop_env
|
|
101
|
-
loop_env
|
|
62
|
+
acc_var = define_local(loop_env, acc_name.name)
|
|
63
|
+
loop_var = define_local(loop_env, loop_name.name)
|
|
102
64
|
body_code, = emit_sequence(args[1..], loop_env, current_scope, allow_method_definitions: false)
|
|
103
|
-
accumulating_body =
|
|
65
|
+
accumulating_body = emit_sequence_value_assignment(acc_var, body_code)
|
|
104
66
|
loop_code = emit_counted_loop(
|
|
105
67
|
ruby_name: loop_var,
|
|
106
68
|
start_code: emit_expr(bindings[3], env, current_scope),
|
|
@@ -210,6 +172,42 @@ module Kapusta
|
|
|
210
172
|
end.compact
|
|
211
173
|
[codes.join("\n"), current_env]
|
|
212
174
|
end
|
|
175
|
+
|
|
176
|
+
def emit_collection_result(result_var, initial_code, iter_code)
|
|
177
|
+
<<~RUBY.chomp
|
|
178
|
+
(-> do
|
|
179
|
+
#{result_var} = #{initial_code}
|
|
180
|
+
#{indent(iter_code)}
|
|
181
|
+
#{result_var}
|
|
182
|
+
end).call
|
|
183
|
+
RUBY
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def emit_array_collection_step(result_var, body_code)
|
|
187
|
+
value_var = temp('value')
|
|
188
|
+
<<~RUBY.chomp
|
|
189
|
+
#{emit_sequence_value_assignment(value_var, body_code)}
|
|
190
|
+
#{result_var} << #{value_var} unless #{value_var}.nil?
|
|
191
|
+
RUBY
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def emit_hash_collection_step(result_var, body_code)
|
|
195
|
+
pair_var = temp('pair')
|
|
196
|
+
<<~RUBY.chomp
|
|
197
|
+
#{emit_sequence_value_assignment(pair_var, body_code)}
|
|
198
|
+
if #{pair_var}.is_a?(Array) && #{pair_var}.length == 2 && !#{pair_var}[0].nil? && !#{pair_var}[1].nil?
|
|
199
|
+
#{result_var}[#{pair_var}[0]] = #{pair_var}[1]
|
|
200
|
+
end
|
|
201
|
+
RUBY
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def emit_sequence_value_assignment(target_var, body_code)
|
|
205
|
+
<<~RUBY.chomp
|
|
206
|
+
#{target_var} = begin
|
|
207
|
+
#{indent(body_code)}
|
|
208
|
+
end
|
|
209
|
+
RUBY
|
|
210
|
+
end
|
|
213
211
|
end
|
|
214
212
|
end
|
|
215
213
|
end
|
|
@@ -16,14 +16,35 @@ module Kapusta
|
|
|
16
16
|
|
|
17
17
|
cond = emit_expr(args[0], env, current_scope)
|
|
18
18
|
truthy = emit_expr(args[1], env, current_scope)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
lines = ["if #{cond}", indent(truthy)]
|
|
20
|
+
append_else_lines(lines, args[2..], env, current_scope)
|
|
21
|
+
lines << 'end'
|
|
22
|
+
lines.join("\n")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def append_else_lines(lines, args, env, current_scope)
|
|
26
|
+
return if args.empty?
|
|
27
|
+
|
|
28
|
+
if args.length == 1 && if_form?(args[0])
|
|
29
|
+
append_elsif_lines(lines, args[0].rest, env, current_scope)
|
|
30
|
+
elsif args.length >= 2
|
|
31
|
+
append_elsif_lines(lines, args, env, current_scope)
|
|
32
|
+
else
|
|
33
|
+
lines << 'else'
|
|
34
|
+
lines << indent(emit_expr(args[0], env, current_scope))
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def append_elsif_lines(lines, args, env, current_scope)
|
|
39
|
+
return append_else_lines(lines, args, env, current_scope) if args.length < 2
|
|
40
|
+
|
|
41
|
+
lines << "elsif #{emit_expr(args[0], env, current_scope)}"
|
|
42
|
+
lines << indent(emit_expr(args[1], env, current_scope))
|
|
43
|
+
append_else_lines(lines, args[2..], env, current_scope)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def if_form?(form)
|
|
47
|
+
form.is_a?(List) && form.head.is_a?(Sym) && form.head.name == 'if'
|
|
27
48
|
end
|
|
28
49
|
|
|
29
50
|
def emit_case(args, env, current_scope, mode)
|
|
@@ -106,21 +127,16 @@ module Kapusta
|
|
|
106
127
|
end
|
|
107
128
|
|
|
108
129
|
def emit_while(args, env, current_scope)
|
|
109
|
-
body_code, = emit_sequence(args[1..], env, current_scope, allow_method_definitions: false)
|
|
110
130
|
<<~RUBY.chomp
|
|
111
131
|
(-> do
|
|
112
|
-
|
|
113
|
-
#{indent(body_code)}
|
|
114
|
-
end
|
|
132
|
+
#{indent(emit_while_statement(args, env, current_scope))}
|
|
115
133
|
nil
|
|
116
134
|
end).call
|
|
117
135
|
RUBY
|
|
118
136
|
end
|
|
119
137
|
|
|
120
138
|
def emit_for(args, env, current_scope)
|
|
121
|
-
|
|
122
|
-
body_code, = emit_sequence(args[1..], parsed[:loop_env], current_scope, allow_method_definitions: false)
|
|
123
|
-
loop_code = emit_counted_loop(**parsed, current_scope:, body_code:)
|
|
139
|
+
loop_code = emit_for_statement(args, env, current_scope)
|
|
124
140
|
<<~RUBY.chomp
|
|
125
141
|
(-> do
|
|
126
142
|
#{indent(loop_code)}
|
|
@@ -130,9 +146,7 @@ module Kapusta
|
|
|
130
146
|
end
|
|
131
147
|
|
|
132
148
|
def emit_each(args, env, current_scope)
|
|
133
|
-
iter_code =
|
|
134
|
-
emit_sequence(args[1..], iter_env, current_scope, allow_method_definitions: false).first
|
|
135
|
-
end
|
|
149
|
+
iter_code = emit_each_statement(args, env, current_scope)
|
|
136
150
|
<<~RUBY.chomp
|
|
137
151
|
(-> do
|
|
138
152
|
#{iter_code}
|
|
@@ -140,6 +154,33 @@ module Kapusta
|
|
|
140
154
|
end).call
|
|
141
155
|
RUBY
|
|
142
156
|
end
|
|
157
|
+
|
|
158
|
+
def emit_while_statement(args, env, current_scope)
|
|
159
|
+
body_code, = emit_sequence(args[1..], env, current_scope,
|
|
160
|
+
allow_method_definitions: false,
|
|
161
|
+
result: false)
|
|
162
|
+
[
|
|
163
|
+
"while #{emit_expr(args[0], env, current_scope)}",
|
|
164
|
+
indent(body_code),
|
|
165
|
+
'end'
|
|
166
|
+
].join("\n")
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def emit_for_statement(args, env, current_scope)
|
|
170
|
+
parsed = parse_counted_for_bindings(args[0].items, env, current_scope)
|
|
171
|
+
body_code, = emit_sequence(args[1..], parsed[:loop_env], current_scope,
|
|
172
|
+
allow_method_definitions: false,
|
|
173
|
+
result: false)
|
|
174
|
+
emit_counted_loop(**parsed, current_scope:, body_code:)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def emit_each_statement(args, env, current_scope)
|
|
178
|
+
emit_iteration(args[0], env, current_scope) do |iter_env|
|
|
179
|
+
emit_sequence(args[1..], iter_env, current_scope,
|
|
180
|
+
allow_method_definitions: false,
|
|
181
|
+
result: false).first
|
|
182
|
+
end
|
|
183
|
+
end
|
|
143
184
|
end
|
|
144
185
|
end
|
|
145
186
|
end
|
|
@@ -79,7 +79,7 @@ module Kapusta
|
|
|
79
79
|
when 'raise' then emit_raise(args, env, current_scope)
|
|
80
80
|
when 'ivar' then runtime_call(:get_ivar, 'self', args[0].name.inspect)
|
|
81
81
|
when 'cvar' then runtime_call(:get_cvar, 'self', args[0].name.inspect)
|
|
82
|
-
when 'gvar' then
|
|
82
|
+
when 'gvar' then emit_gvar(args[0])
|
|
83
83
|
when 'ruby' then "Kernel.eval(#{emit_expr(args[0], env, current_scope)})"
|
|
84
84
|
when 'and' then emit_and(args, env, current_scope)
|
|
85
85
|
when 'or' then emit_or(args, env, current_scope)
|