kapusta 0.1.2 → 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/anonymous-greeter.kap +4 -0
- data/examples/binary-to-decimal.kap +7 -0
- data/examples/contains-duplicate.kap +7 -0
- data/examples/or-patterns.kap +8 -0
- data/examples/packet-router.kap +26 -0
- data/examples/tic-tac-toe.kap +18 -0
- data/examples/underscore-patterns.kap +14 -0
- data/lib/kapusta/compiler/emitter/bindings.rb +87 -39
- data/lib/kapusta/compiler/emitter/collections.rb +61 -92
- data/lib/kapusta/compiler/emitter/control_flow.rb +84 -65
- data/lib/kapusta/compiler/emitter/expressions.rb +3 -2
- data/lib/kapusta/compiler/emitter/interop.rb +105 -22
- data/lib/kapusta/compiler/emitter/patterns.rb +136 -8
- data/lib/kapusta/compiler/emitter/support.rb +120 -16
- data/lib/kapusta/compiler/runtime.rb +68 -53
- 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 +10 -17
- 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 +29 -1
- data/spec/formatter_spec.rb +20 -0
- metadata +10 -3
- data/kapfmt +0 -4
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
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
(fn inbox-line [user event]
|
|
2
|
+
(match event
|
|
3
|
+
[:score user points] (.. "score:" points)
|
|
4
|
+
[:profile user ?city] (if city (.. "city:" city) "city:nil")
|
|
5
|
+
_ "other"))
|
|
6
|
+
|
|
7
|
+
(fn score-delta [user event]
|
|
8
|
+
(case event
|
|
9
|
+
(where (or [:bonus (= user) points] [:score (= user) points])
|
|
10
|
+
(> points 0)
|
|
11
|
+
(< points 10))
|
|
12
|
+
points
|
|
13
|
+
_ 0))
|
|
14
|
+
|
|
15
|
+
(fn packet-kind [packet]
|
|
16
|
+
(case packet
|
|
17
|
+
[:ping seq] (.. "ping:" seq)
|
|
18
|
+
[:pong seq] (.. "pong:" seq)
|
|
19
|
+
_ "other"))
|
|
20
|
+
|
|
21
|
+
(print (inbox-line "Ada" [:score "Ada" 9]))
|
|
22
|
+
(print (inbox-line "Ada" [:score "Lin" 7]))
|
|
23
|
+
(print (inbox-line "Ada" [:profile "Ada" nil]))
|
|
24
|
+
(print (score-delta "Ada" [:bonus "Ada" 5]))
|
|
25
|
+
(print (score-delta "Ada" [:score "Lin" 5]))
|
|
26
|
+
(print (packet-kind [:ping 7 :fast]))
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
(fn winner [board]
|
|
2
|
+
(case board
|
|
3
|
+
[["X" "X" "X"] _ _] "X"
|
|
4
|
+
[_ ["O" "O" "O"] _] "O"
|
|
5
|
+
[["X" _ _] [_ "X" _] [_ _ "X"]] "X"
|
|
6
|
+
[["O" _ _] ["O" _ _] ["O" _ _]] "O"
|
|
7
|
+
_ "draw"))
|
|
8
|
+
|
|
9
|
+
(each
|
|
10
|
+
[
|
|
11
|
+
_
|
|
12
|
+
board
|
|
13
|
+
(ipairs [
|
|
14
|
+
[["X" "X" "X"] ["O" "" ""] ["" "O" ""]]
|
|
15
|
+
[["O" "X" "X"] ["O" "" "X"] ["O" "" ""]]
|
|
16
|
+
[["X" "O" ""] ["" "X" "O"] ["" "" "X"]]
|
|
17
|
+
[["X" "O" "X"] ["O" "X" "O"] ["O" "X" "O"]]])]
|
|
18
|
+
(print (winner board)))
|
|
@@ -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,100 +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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
finish_code = emit_expr(bindings[2], env, current_scope)
|
|
59
|
-
step_code = '1'
|
|
60
|
-
until_form = nil
|
|
61
|
-
i = 3
|
|
62
|
-
while i < bindings.length
|
|
63
|
-
if bindings[i].is_a?(Sym) && bindings[i].name == '&until'
|
|
64
|
-
until_form = bindings[i + 1]
|
|
65
|
-
i += 2
|
|
66
|
-
else
|
|
67
|
-
step_code = emit_expr(bindings[i], env, current_scope)
|
|
68
|
-
i += 1
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
body_code, = emit_sequence(args[1..], loop_env, current_scope, allow_method_definitions: false)
|
|
72
|
-
until_code = until_form ? "break if #{emit_expr(until_form, loop_env, current_scope)}" : nil
|
|
73
|
-
finish_var = temp('finish')
|
|
74
|
-
step_var = temp('step')
|
|
75
|
-
cmp_var = temp('cmp')
|
|
76
|
-
<<~RUBY.chomp
|
|
77
|
-
(-> do
|
|
78
|
-
#{result_var} = []
|
|
79
|
-
#{ruby_name} = #{start_code}
|
|
80
|
-
#{finish_var} = #{finish_code}
|
|
81
|
-
#{step_var} = #{step_code}
|
|
82
|
-
#{cmp_var} = #{step_var} >= 0 ? :<= : :>=
|
|
83
|
-
while #{ruby_name}.public_send(#{cmp_var}, #{finish_var})
|
|
84
|
-
#{until_code}
|
|
85
|
-
__kap_value = begin
|
|
86
|
-
#{indent(body_code)}
|
|
87
|
-
end
|
|
88
|
-
#{result_var} << __kap_value unless __kap_value.nil?
|
|
89
|
-
#{ruby_name} += #{step_var}
|
|
90
|
-
end
|
|
91
|
-
#{result_var}
|
|
92
|
-
end).call
|
|
93
|
-
RUBY
|
|
29
|
+
parsed = parse_counted_for_bindings(args[0].items, env, current_scope)
|
|
30
|
+
body_code, = emit_sequence(args[1..], parsed[:loop_env], current_scope, allow_method_definitions: false)
|
|
31
|
+
collecting_body = emit_array_collection_step(result_var, body_code)
|
|
32
|
+
loop_code = emit_counted_loop(**parsed, current_scope:, body_code: collecting_body)
|
|
33
|
+
emit_collection_result(result_var, '[]', loop_code)
|
|
94
34
|
end
|
|
95
35
|
|
|
96
36
|
def emit_accumulate(args, env, current_scope)
|
|
97
37
|
bindings = args[0].items
|
|
98
38
|
acc_name = bindings[0]
|
|
99
|
-
acc_var = temp(sanitize_local(acc_name.name))
|
|
100
39
|
iter_bindings = Vec.new(bindings[2..])
|
|
101
40
|
loop_env = env.child
|
|
102
|
-
loop_env
|
|
41
|
+
acc_var = define_local(loop_env, acc_name.name)
|
|
103
42
|
iter_code = emit_iteration(iter_bindings, loop_env, current_scope) do |iter_env|
|
|
104
43
|
iter_env.define(acc_name.name, acc_var)
|
|
105
44
|
emit_sequence(args[1..], iter_env, current_scope, allow_method_definitions: false).first.then do |body|
|
|
106
|
-
|
|
45
|
+
emit_sequence_value_assignment(acc_var, body)
|
|
107
46
|
end
|
|
108
47
|
end
|
|
109
48
|
<<~RUBY.chomp
|
|
@@ -118,32 +57,26 @@ module Kapusta
|
|
|
118
57
|
def emit_faccumulate(args, env, current_scope)
|
|
119
58
|
bindings = args[0].items
|
|
120
59
|
acc_name = bindings[0]
|
|
121
|
-
acc_var = temp(sanitize_local(acc_name.name))
|
|
122
60
|
loop_name = bindings[2]
|
|
123
|
-
loop_var = temp(sanitize_local(loop_name.name))
|
|
124
61
|
loop_env = env.child
|
|
125
|
-
loop_env
|
|
126
|
-
loop_env
|
|
127
|
-
start_code = emit_expr(bindings[3], env, current_scope)
|
|
128
|
-
finish_code = emit_expr(bindings[4], env, current_scope)
|
|
129
|
-
step_code = bindings[5] ? emit_expr(bindings[5], env, current_scope) : '1'
|
|
62
|
+
acc_var = define_local(loop_env, acc_name.name)
|
|
63
|
+
loop_var = define_local(loop_env, loop_name.name)
|
|
130
64
|
body_code, = emit_sequence(args[1..], loop_env, current_scope, allow_method_definitions: false)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
65
|
+
accumulating_body = emit_sequence_value_assignment(acc_var, body_code)
|
|
66
|
+
loop_code = emit_counted_loop(
|
|
67
|
+
ruby_name: loop_var,
|
|
68
|
+
start_code: emit_expr(bindings[3], env, current_scope),
|
|
69
|
+
finish_code: emit_expr(bindings[4], env, current_scope),
|
|
70
|
+
step_code: bindings[5] ? emit_expr(bindings[5], env, current_scope) : '1',
|
|
71
|
+
until_form: nil,
|
|
72
|
+
loop_env:,
|
|
73
|
+
current_scope:,
|
|
74
|
+
body_code: accumulating_body
|
|
75
|
+
)
|
|
134
76
|
<<~RUBY.chomp
|
|
135
77
|
(-> do
|
|
136
78
|
#{acc_var} = #{emit_expr(bindings[1], env, current_scope)}
|
|
137
|
-
#{
|
|
138
|
-
#{finish_var} = #{finish_code}
|
|
139
|
-
#{step_var} = #{step_code}
|
|
140
|
-
#{cmp_var} = #{step_var} >= 0 ? :<= : :>=
|
|
141
|
-
while #{loop_var}.public_send(#{cmp_var}, #{finish_var})
|
|
142
|
-
#{acc_var} = begin
|
|
143
|
-
#{indent(body_code)}
|
|
144
|
-
end
|
|
145
|
-
#{loop_var} += #{step_var}
|
|
146
|
-
end
|
|
79
|
+
#{indent(loop_code)}
|
|
147
80
|
#{acc_var}
|
|
148
81
|
end).call
|
|
149
82
|
RUBY
|
|
@@ -239,6 +172,42 @@ module Kapusta
|
|
|
239
172
|
end.compact
|
|
240
173
|
[codes.join("\n"), current_env]
|
|
241
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
|
|
242
211
|
end
|
|
243
212
|
end
|
|
244
213
|
end
|