kapusta 0.2.3 → 0.3.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 +6 -0
- data/examples/counter.kap +1 -2
- data/examples/length-of-last-word.kap +5 -2
- data/examples/manhattan-distance.kap +9 -0
- data/examples/palindrome.kap +2 -3
- data/examples/pangram.kap +6 -5
- data/examples/pivot-index.kap +13 -0
- data/examples/subtract-product-sum.kap +14 -0
- data/lib/kapusta/compiler/emitter/bindings.rb +18 -7
- data/lib/kapusta/compiler/emitter/collections.rb +90 -61
- data/lib/kapusta/compiler/emitter/control_flow.rb +39 -66
- data/lib/kapusta/compiler/emitter/interop.rb +66 -73
- data/lib/kapusta/compiler/emitter/patterns.rb +196 -109
- data/lib/kapusta/compiler/emitter/support.rb +2 -15
- data/lib/kapusta/compiler/emitter.rb +1 -3
- data/lib/kapusta/compiler/normalizer.rb +2 -2
- data/lib/kapusta/compiler.rb +0 -1
- data/lib/kapusta/version.rb +1 -1
- data/lib/kapusta.rb +28 -4
- data/spec/examples_spec.rb +12 -0
- metadata +4 -2
- data/lib/kapusta/compiler/runtime.rb +0 -226
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 179b8b97d775c6bff99bd3d5ac3e1df1f6ab8c17637b6e16f9d688ade7f9dd06
|
|
4
|
+
data.tar.gz: 899899c13c9b1d4f85216df5d3989807d874a9ec862be94d48989f851b2d8111
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 69247136466e119860e5fee232b0bb21fa40b9d23986f09051a4acccda476a9c42b5e3e63e1382367df6c3008876af2f6a69a9b6fd2bfc0e05e1c9e69ca6a8b0
|
|
7
|
+
data.tar.gz: f65709e841e372a1dcc4c9a453d2251d58fb4fffbecd51fd1f0bcfb7c0423dc51109d6533e1b85d9b39bbc82cc994dcf64bdb5aad586c2b6476858db8e320d25
|
data/README.md
CHANGED
|
@@ -8,6 +8,12 @@ Instead, Kapusta aims to bring some of the simplicity and joy of Lisp to Ruby. W
|
|
|
8
8
|
|
|
9
9
|
For more information about Kapusta, see the official Fennel documentation and tutorials.
|
|
10
10
|
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
1. Compiles to readable Ruby.
|
|
14
|
+
2. Compiled `.rb` files don't depend on Kapusta. Run with plain `ruby`, or load `.kap` files at runtime via `require 'kapusta'`.
|
|
15
|
+
3. Two-way Ruby interop.
|
|
16
|
+
|
|
11
17
|
## Usage
|
|
12
18
|
|
|
13
19
|
```
|
data/examples/counter.kap
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
(fn manhattan [edge]
|
|
2
|
+
(let [{:from [x1 y1] :to [x2 y2]} edge]
|
|
3
|
+
(+ (: (- x1 x2) :abs) (: (- y1 y2) :abs))))
|
|
4
|
+
|
|
5
|
+
(fn total-distance [edges]
|
|
6
|
+
(accumulate [total 0 _ edge (ipairs edges)]
|
|
7
|
+
(+ total (manhattan edge))))
|
|
8
|
+
|
|
9
|
+
(print (total-distance [{:from [0 0] :to [3 4]} {:from [1 1] :to [4 5]}]))
|
data/examples/palindrome.kap
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
(fn palindrome? [s]
|
|
2
|
-
(let [
|
|
3
|
-
|
|
4
|
-
(= normalized (normalized.reverse))))
|
|
2
|
+
(let [normalized (-> s (: :downcase) (: :gsub (ruby "/[^a-z]/") ""))]
|
|
3
|
+
(= normalized (: normalized :reverse))))
|
|
5
4
|
|
|
6
5
|
(print (palindrome? "racecar"))
|
|
7
6
|
(print (palindrome? "A man, a plan, a canal: Panama"))
|
data/examples/pangram.kap
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
(fn pangram? [s]
|
|
2
|
-
(
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
(= 26
|
|
3
|
+
(length (-> s
|
|
4
|
+
(: :downcase)
|
|
5
|
+
(: :gsub (ruby "/[^a-z]/") "")
|
|
6
|
+
(: :chars)
|
|
7
|
+
(: :uniq)))))
|
|
7
8
|
|
|
8
9
|
(print (pangram? "The quick brown fox jumps over the lazy dog"))
|
|
9
10
|
(print (pangram? "Hello, world"))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
(fn pivot-index [nums]
|
|
2
|
+
(var total 0)
|
|
3
|
+
(each [n nums] (set total (+ total n)))
|
|
4
|
+
(var left 0)
|
|
5
|
+
(var found -1)
|
|
6
|
+
(each [i n (ipairs nums)]
|
|
7
|
+
(when (and (= found -1) (= left (- total left n))) (set found i))
|
|
8
|
+
(set left (+ left n)))
|
|
9
|
+
found)
|
|
10
|
+
|
|
11
|
+
(print (pivot-index [1 7 3 6 5 6]))
|
|
12
|
+
(print (pivot-index [1 2 3]))
|
|
13
|
+
(print (pivot-index [2 1 -1]))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
(fn subtract-product-sum [n]
|
|
2
|
+
(var x n)
|
|
3
|
+
(var product 1)
|
|
4
|
+
(var sum 0)
|
|
5
|
+
(while (> x 0)
|
|
6
|
+
(let [d (% x 10)]
|
|
7
|
+
(set product (* product d))
|
|
8
|
+
(set sum (+ sum d))
|
|
9
|
+
(set x (: (/ x 10) :floor))))
|
|
10
|
+
(- product sum))
|
|
11
|
+
|
|
12
|
+
(print (subtract-product-sum 234))
|
|
13
|
+
(print (subtract-product-sum 4421))
|
|
14
|
+
(print (subtract-product-sum 1))
|
|
@@ -281,12 +281,23 @@ module Kapusta
|
|
|
281
281
|
else
|
|
282
282
|
define_local(env, target.name)
|
|
283
283
|
end
|
|
284
|
-
[
|
|
284
|
+
[emit_assignment(ruby_name, value_code), env]
|
|
285
285
|
else
|
|
286
286
|
[emit_set_target(target, value_code, env, current_scope), env]
|
|
287
287
|
end
|
|
288
288
|
end
|
|
289
289
|
|
|
290
|
+
def emit_assignment(lhs, value_code)
|
|
291
|
+
prefix = "#{lhs} "
|
|
292
|
+
if value_code.start_with?(prefix) &&
|
|
293
|
+
(m = value_code[prefix.length..].match(/\A(\S+) (.*)\z/m)) &&
|
|
294
|
+
!m[1].include?('=')
|
|
295
|
+
"#{lhs} #{m[1]}= #{m[2]}"
|
|
296
|
+
else
|
|
297
|
+
"#{lhs} = #{value_code}"
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
290
301
|
def emit_set_expr(args, env, current_scope)
|
|
291
302
|
target = args[0]
|
|
292
303
|
value_code = emit_expr(args[1], env, current_scope)
|
|
@@ -302,7 +313,7 @@ module Kapusta
|
|
|
302
313
|
last = segments.last
|
|
303
314
|
snake = Kapusta.kebab_to_snake(last)
|
|
304
315
|
if direct_method_name?(last)
|
|
305
|
-
"#{receiver}.#{snake}
|
|
316
|
+
emit_assignment("#{receiver}.#{snake}", value_code)
|
|
306
317
|
else
|
|
307
318
|
"#{receiver}.public_send(:\"#{snake}=\", #{value_code})"
|
|
308
319
|
end
|
|
@@ -310,7 +321,7 @@ module Kapusta
|
|
|
310
321
|
binding = env.lookup(target.name)
|
|
311
322
|
emit_error!("cannot set method binding: #{target.name}") if method_binding?(binding)
|
|
312
323
|
|
|
313
|
-
|
|
324
|
+
emit_assignment(binding, value_code)
|
|
314
325
|
end
|
|
315
326
|
when List
|
|
316
327
|
head = target.head
|
|
@@ -319,13 +330,13 @@ module Kapusta
|
|
|
319
330
|
keys = target.items[2..].map { |item| emit_expr(item, env, current_scope) }
|
|
320
331
|
receiver = simple_expression?(object_code) ? object_code : parenthesize(object_code)
|
|
321
332
|
prefix = keys[0...-1].map { |k| "[#{k}]" }.join
|
|
322
|
-
"#{receiver}#{prefix}[#{keys.last}]
|
|
333
|
+
emit_assignment("#{receiver}#{prefix}[#{keys.last}]", value_code)
|
|
323
334
|
elsif head.is_a?(Sym) && head.name == 'ivar'
|
|
324
|
-
"@#{Kapusta.kebab_to_snake(target.items[1].name)}
|
|
335
|
+
emit_assignment("@#{Kapusta.kebab_to_snake(target.items[1].name)}", value_code)
|
|
325
336
|
elsif head.is_a?(Sym) && head.name == 'cvar'
|
|
326
|
-
"@@#{Kapusta.kebab_to_snake(target.items[1].name)}
|
|
337
|
+
emit_assignment("@@#{Kapusta.kebab_to_snake(target.items[1].name)}", value_code)
|
|
327
338
|
elsif head.is_a?(Sym) && head.name == 'gvar'
|
|
328
|
-
"$#{global_name(target.items[1].name)}
|
|
339
|
+
emit_assignment("$#{global_name(target.items[1].name)}", value_code)
|
|
329
340
|
else
|
|
330
341
|
emit_error!("bad set target: #{target.inspect}")
|
|
331
342
|
end
|
|
@@ -45,13 +45,13 @@ module Kapusta
|
|
|
45
45
|
emit_sequence_value_assignment(acc_var, body)
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
|
-
|
|
49
|
-
(-> do
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
end).call
|
|
54
|
-
|
|
48
|
+
[
|
|
49
|
+
'(-> do',
|
|
50
|
+
indent("#{acc_var} = #{emit_expr(bindings[1], env, current_scope)}"),
|
|
51
|
+
indent(iter_code),
|
|
52
|
+
indent(acc_var),
|
|
53
|
+
'end).call'
|
|
54
|
+
].join("\n")
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def emit_faccumulate(args, env, current_scope)
|
|
@@ -73,29 +73,43 @@ module Kapusta
|
|
|
73
73
|
current_scope:,
|
|
74
74
|
body_code: accumulating_body
|
|
75
75
|
)
|
|
76
|
-
|
|
77
|
-
(-> do
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
end).call
|
|
82
|
-
|
|
76
|
+
[
|
|
77
|
+
'(-> do',
|
|
78
|
+
indent("#{acc_var} = #{emit_expr(bindings[1], env, current_scope)}"),
|
|
79
|
+
indent(loop_code),
|
|
80
|
+
indent(acc_var),
|
|
81
|
+
'end).call'
|
|
82
|
+
].join("\n")
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def emit_hashfn(args, env, current_scope)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
hash_env.define("$#{
|
|
86
|
+
if needs_explicit_args?(args[0])
|
|
87
|
+
args_var = temp('args')
|
|
88
|
+
hash_env = env.child
|
|
89
|
+
hash_env.define('$', "#{args_var}[0]")
|
|
90
|
+
(1..9).each { |i| hash_env.define("$#{i}", "#{args_var}[#{i - 1}]") }
|
|
91
|
+
hash_env.define('$...', args_var)
|
|
92
|
+
body_code = emit_expr(args[0], hash_env, current_scope)
|
|
93
|
+
["->(*#{args_var}) do", indent(body_code), 'end'].join("\n")
|
|
94
|
+
else
|
|
95
|
+
hash_env = env.child
|
|
96
|
+
hash_env.define('$', '_1')
|
|
97
|
+
(1..9).each { |i| hash_env.define("$#{i}", "_#{i}") }
|
|
98
|
+
body_code = emit_expr(args[0], hash_env, current_scope)
|
|
99
|
+
['proc do', indent(body_code), 'end'].join("\n")
|
|
91
100
|
end
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def needs_explicit_args?(form)
|
|
104
|
+
case form
|
|
105
|
+
when Sym then form.name == '$...'
|
|
106
|
+
when List, Vec then form.items.any? { |item| needs_explicit_args?(item) }
|
|
107
|
+
when HashLit
|
|
108
|
+
form.entries.any? do |entry|
|
|
109
|
+
entry.is_a?(Array) ? entry.any? { |item| needs_explicit_args?(item) } : needs_explicit_args?(entry)
|
|
97
110
|
end
|
|
98
|
-
|
|
111
|
+
else false
|
|
112
|
+
end
|
|
99
113
|
end
|
|
100
114
|
|
|
101
115
|
def emit_iteration(bindings_vec, env, current_scope)
|
|
@@ -106,22 +120,24 @@ module Kapusta
|
|
|
106
120
|
if iter_expr.is_a?(List) && iter_expr.head.is_a?(Sym)
|
|
107
121
|
case iter_expr.head.name
|
|
108
122
|
when 'ipairs'
|
|
109
|
-
value_var = temp('value')
|
|
110
|
-
index_var = temp('index')
|
|
111
123
|
body_env = env.child
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
)
|
|
124
|
+
value_var, value_bind = bind_iteration_param(binding_pats[1], 'value', body_env)
|
|
125
|
+
coll_code = emit_expr(iter_expr.items[1], env, current_scope)
|
|
126
|
+
if ignored_pattern?(binding_pats[0])
|
|
127
|
+
bind_code = value_bind || ''
|
|
128
|
+
body_code = yield(body_env)
|
|
129
|
+
return iteration_block("#{coll_code}.each do |#{value_var}|", bind_code, body_code)
|
|
130
|
+
end
|
|
131
|
+
index_var, index_bind = bind_iteration_param(binding_pats[0], 'index', body_env)
|
|
132
|
+
bind_code = [index_bind, value_bind].compact.join("\n")
|
|
115
133
|
body_code = yield(body_env)
|
|
116
|
-
header = "#{
|
|
117
|
-
".each_with_index do |#{value_var}, #{index_var}|"
|
|
134
|
+
header = "#{coll_code}.each_with_index do |#{value_var}, #{index_var}|"
|
|
118
135
|
return iteration_block(header, bind_code, body_code)
|
|
119
136
|
when 'pairs'
|
|
120
|
-
key_var = temp('key')
|
|
121
|
-
value_var = temp('value')
|
|
122
137
|
body_env = env.child
|
|
123
|
-
|
|
124
|
-
|
|
138
|
+
key_var, key_bind = bind_iteration_param(binding_pats[0], 'key', body_env)
|
|
139
|
+
value_var, value_bind = bind_iteration_param(binding_pats[1], 'value', body_env)
|
|
140
|
+
bind_code = [key_bind, value_bind].compact.join("\n")
|
|
125
141
|
body_code = yield(body_env)
|
|
126
142
|
header = "#{emit_expr(iter_expr.items[1], env, current_scope)}.each do |#{key_var}, #{value_var}|"
|
|
127
143
|
return iteration_block(header, bind_code, body_code)
|
|
@@ -130,11 +146,10 @@ module Kapusta
|
|
|
130
146
|
|
|
131
147
|
coll_code = emit_expr(iter_expr, env, current_scope)
|
|
132
148
|
if binding_pats.length == 1
|
|
133
|
-
value_var = temp('value')
|
|
134
149
|
body_env = env.child
|
|
135
|
-
|
|
150
|
+
value_var, bind_code = bind_iteration_param(binding_pats[0], 'value', body_env)
|
|
136
151
|
body_code = yield(body_env)
|
|
137
|
-
iteration_block("#{coll_code}.each do |#{value_var}|", bind_code, body_code)
|
|
152
|
+
iteration_block("#{coll_code}.each do |#{value_var}|", bind_code || '', body_code)
|
|
138
153
|
else
|
|
139
154
|
parts_var = temp('parts')
|
|
140
155
|
body_env = env.child
|
|
@@ -145,6 +160,21 @@ module Kapusta
|
|
|
145
160
|
end
|
|
146
161
|
end
|
|
147
162
|
|
|
163
|
+
def ignored_pattern?(pattern)
|
|
164
|
+
pattern.is_a?(Sym) && !pattern.dotted? && pattern.name == '_'
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def bind_iteration_param(pattern, fallback_name, env)
|
|
168
|
+
if pattern.is_a?(Sym) && !pattern.dotted?
|
|
169
|
+
ruby_name = pattern.name == '_' ? '_' : define_local(env, pattern.name)
|
|
170
|
+
[ruby_name, nil]
|
|
171
|
+
else
|
|
172
|
+
tmp = temp(fallback_name)
|
|
173
|
+
bind_code, _new_env = emit_iteration_bindings([[pattern, tmp]], env)
|
|
174
|
+
[tmp, bind_code.empty? ? nil : bind_code]
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
148
178
|
def iteration_block(header, bind_code, body_code)
|
|
149
179
|
[header, indent(join_code(bind_code, body_code)), 'end'].join("\n")
|
|
150
180
|
end
|
|
@@ -161,39 +191,38 @@ module Kapusta
|
|
|
161
191
|
end
|
|
162
192
|
|
|
163
193
|
def emit_collection_result(result_var, initial_code, iter_code)
|
|
164
|
-
|
|
165
|
-
(-> do
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
end).call
|
|
170
|
-
|
|
194
|
+
[
|
|
195
|
+
'(-> do',
|
|
196
|
+
indent("#{result_var} = #{initial_code}"),
|
|
197
|
+
indent(iter_code),
|
|
198
|
+
indent(result_var),
|
|
199
|
+
'end).call'
|
|
200
|
+
].join("\n")
|
|
171
201
|
end
|
|
172
202
|
|
|
173
203
|
def emit_array_collection_step(result_var, body_code)
|
|
174
204
|
value_var = temp('value')
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
#{result_var} << #{value_var} unless #{value_var}.nil?
|
|
178
|
-
|
|
205
|
+
[
|
|
206
|
+
emit_sequence_value_assignment(value_var, body_code),
|
|
207
|
+
"#{result_var} << #{value_var} unless #{value_var}.nil?"
|
|
208
|
+
].join("\n")
|
|
179
209
|
end
|
|
180
210
|
|
|
181
211
|
def emit_hash_collection_step(result_var, body_code)
|
|
182
212
|
pair_var = temp('pair')
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if #{pair_var}.is_a?(Array) && #{pair_var}.length == 2 &&
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
213
|
+
[
|
|
214
|
+
emit_sequence_value_assignment(pair_var, body_code),
|
|
215
|
+
"if #{pair_var}.is_a?(Array) && #{pair_var}.length == 2 && " \
|
|
216
|
+
"!#{pair_var}[0].nil? && !#{pair_var}[1].nil?",
|
|
217
|
+
indent("#{result_var}[#{pair_var}[0]] = #{pair_var}[1]"),
|
|
218
|
+
'end'
|
|
219
|
+
].join("\n")
|
|
189
220
|
end
|
|
190
221
|
|
|
191
222
|
def emit_sequence_value_assignment(target_var, body_code)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
end
|
|
196
|
-
RUBY
|
|
223
|
+
return emit_assignment(target_var, body_code) unless body_code.include?("\n")
|
|
224
|
+
|
|
225
|
+
["#{target_var} = begin", indent(body_code), 'end'].join("\n")
|
|
197
226
|
end
|
|
198
227
|
end
|
|
199
228
|
end
|
|
@@ -16,6 +16,8 @@ module Kapusta
|
|
|
16
16
|
|
|
17
17
|
cond = emit_expr(args[0], env, current_scope)
|
|
18
18
|
truthy = emit_if_branch(args[1], env, current_scope)
|
|
19
|
+
return "#{truthy} if #{cond}" if args.length == 2 && !truthy.include?("\n") && !cond.include?("\n")
|
|
20
|
+
|
|
19
21
|
lines = ["if #{cond}", indent(truthy)]
|
|
20
22
|
append_else_lines(lines, args[2..], env, current_scope)
|
|
21
23
|
lines << 'end'
|
|
@@ -57,81 +59,52 @@ module Kapusta
|
|
|
57
59
|
|
|
58
60
|
def emit_case(args, env, current_scope, mode)
|
|
59
61
|
value_var = temp('case_value')
|
|
60
|
-
body =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
body = try_emit_native_case(value_var, args[1..], env, current_scope, mode)
|
|
63
|
+
emit_error!('case/match clauses use patterns this compiler cannot translate') unless body
|
|
64
|
+
[
|
|
65
|
+
'(-> do',
|
|
66
|
+
indent("#{value_var} = #{emit_expr(args[0], env, current_scope)}"),
|
|
67
|
+
indent(body),
|
|
68
|
+
'end).call'
|
|
69
|
+
].join("\n")
|
|
67
70
|
end
|
|
68
71
|
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
def try_emit_native_case(value_var, clauses, env, current_scope, mode)
|
|
73
|
+
arms = []
|
|
74
|
+
i = 0
|
|
75
|
+
while i < clauses.length
|
|
76
|
+
pattern = clauses[i]
|
|
77
|
+
body = clauses[i + 1]
|
|
78
|
+
inner, where_guards = if where_pattern?(pattern)
|
|
79
|
+
[pattern.items[1], pattern.items[2..]]
|
|
80
|
+
else
|
|
81
|
+
[pattern, []]
|
|
82
|
+
end
|
|
83
|
+
sub_patterns = or_pattern?(inner) ? inner.items[1..] : [inner]
|
|
84
|
+
sub_arms = sub_patterns.map do |sub|
|
|
85
|
+
try_native_arm(sub, body, where_guards, env, current_scope, mode)
|
|
86
|
+
end
|
|
87
|
+
return if sub_arms.any?(&:nil?)
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
emit_guarded_case_clause(value_var, pattern, body, else_code, env, current_scope, mode)
|
|
81
|
-
else
|
|
82
|
-
emit_simple_case_clause(value_var, pattern, body, else_code, env, current_scope, mode)
|
|
89
|
+
arms.concat(sub_arms)
|
|
90
|
+
i += 2
|
|
83
91
|
end
|
|
92
|
+
arms << ['else', indent('nil')].join("\n")
|
|
93
|
+
["case #{value_var}", *arms, 'end'].join("\n")
|
|
84
94
|
end
|
|
85
95
|
|
|
86
|
-
def
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
arm_env = env.child
|
|
91
|
-
assign_code, arm_env = emit_bindings_from_match(plan[:bindings], bindings_var, arm_env)
|
|
92
|
-
body_code = emit_expr(body, arm_env, current_scope)
|
|
93
|
-
arm_body = [assign_code, body_code].reject(&:empty?).join("\n")
|
|
94
|
-
<<~RUBY.chomp
|
|
95
|
-
#{match_var} = #{runtime_call(:match_pattern, plan[:pattern], value_var)}
|
|
96
|
-
if #{match_var}[0]
|
|
97
|
-
#{bindings_var} = #{match_var}[1]
|
|
98
|
-
#{arm_body}
|
|
99
|
-
else
|
|
100
|
-
#{indent(else_code)}
|
|
101
|
-
end
|
|
102
|
-
RUBY
|
|
103
|
-
end
|
|
96
|
+
def try_native_arm(pattern, body, where_guards, env, current_scope, mode)
|
|
97
|
+
allow_pins = !where_guards.empty? && mode == :case
|
|
98
|
+
plan = native_pattern_plan(pattern, env, mode:, allow_pins:)
|
|
99
|
+
return unless plan
|
|
104
100
|
|
|
105
|
-
def emit_guarded_case_clause(value_var, pattern, body, else_code, env, current_scope, mode)
|
|
106
|
-
inner = pattern.items[1]
|
|
107
|
-
guards = pattern.items[2..]
|
|
108
|
-
match_var = temp('match')
|
|
109
|
-
bindings_var = temp('bindings')
|
|
110
|
-
plan = pattern_match_plan(inner, env, mode:, allow_pins: mode == :case)
|
|
111
101
|
arm_env = env.child
|
|
112
|
-
|
|
113
|
-
|
|
102
|
+
plan[:bindings].each { |name| arm_env.define(name, sanitize_local(name)) }
|
|
103
|
+
guard_codes = plan[:guards] +
|
|
104
|
+
where_guards.map { |g| emit_expr(g, arm_env, current_scope) }
|
|
105
|
+
guard_clause = guard_codes.empty? ? '' : " if #{guard_codes.join(' && ')}"
|
|
114
106
|
body_code = emit_expr(body, arm_env, current_scope)
|
|
115
|
-
|
|
116
|
-
<<~RUBY.chomp
|
|
117
|
-
#{match_var} = #{runtime_call(:match_pattern, plan[:pattern], value_var)}
|
|
118
|
-
if #{match_var}[0]
|
|
119
|
-
#{bindings_var} = #{match_var}[1]#{bindings_line}
|
|
120
|
-
if #{guard_code}
|
|
121
|
-
#{body_code}
|
|
122
|
-
else
|
|
123
|
-
#{indent(else_code, 2)}
|
|
124
|
-
end
|
|
125
|
-
else
|
|
126
|
-
#{indent(else_code)}
|
|
127
|
-
end
|
|
128
|
-
RUBY
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def emit_case_guards(guards, env, current_scope)
|
|
132
|
-
return 'true' if guards.empty?
|
|
133
|
-
|
|
134
|
-
guards.map { |guard| parenthesize(emit_expr(guard, env, current_scope)) }.join(' && ')
|
|
107
|
+
["in #{plan[:pattern]}#{guard_clause}", indent(body_code)].join("\n")
|
|
135
108
|
end
|
|
136
109
|
|
|
137
110
|
def emit_while(args, env, current_scope)
|