kapusta 0.1.5 → 0.2.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 +13 -0
- data/examples/bank-account.kap +21 -0
- data/examples/baseball-game.kap +11 -0
- data/examples/climbing-stairs.kap +13 -0
- data/examples/doto-hygiene.kap +5 -0
- data/examples/happy-number.kap +20 -0
- data/examples/length-of-last-word.kap +7 -0
- data/examples/maximum-subarray.kap +12 -0
- data/examples/move-zeroes.kap +13 -0
- data/examples/stack.kap +27 -10
- data/examples/two-sum-hash.kap +17 -0
- data/examples/use_bank_account.rb +13 -0
- data/examples/valid-parentheses-1.kap +19 -0
- data/examples/valid-parentheses-2.kap +8 -0
- data/lib/kapusta/ast.rb +33 -3
- data/lib/kapusta/compiler/emitter/bindings.rb +35 -25
- data/lib/kapusta/compiler/emitter/control_flow.rb +4 -4
- data/lib/kapusta/compiler/emitter/expressions.rb +13 -13
- data/lib/kapusta/compiler/emitter/interop.rb +91 -40
- data/lib/kapusta/compiler/emitter/patterns.rb +14 -11
- data/lib/kapusta/compiler/emitter/support.rb +13 -11
- data/lib/kapusta/compiler/emitter.rb +1 -5
- data/lib/kapusta/compiler/normalizer.rb +41 -17
- data/lib/kapusta/compiler/runtime.rb +0 -152
- data/lib/kapusta/env.rb +21 -6
- data/lib/kapusta/reader.rb +27 -3
- data/lib/kapusta/version.rb +1 -1
- data/lib/kapusta.rb +62 -1
- data/spec/cli_spec.rb +25 -2
- data/spec/examples_spec.rb +234 -87
- data/spec/reader_spec.rb +26 -0
- metadata +16 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 53512e4cdebbb07a50f1bff53338574994140e5b16ddc5c1fd05e27b8cbc8a20
|
|
4
|
+
data.tar.gz: c7907d4901ecfb193c4df0c0b09bca2869babab65f6327d542c59a88a9edff51
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c185f6d33f64071522cef35498fb7310f47d4091d3873868361294ff40edb51e836cdb2c0168dac1f599bc9ccd7c230d34d276fa6ab1a3fca479a4a4c40b9025
|
|
7
|
+
data.tar.gz: dcf0acd60680784bb38685305f5454466e4c36358d08a2994e2ca988e5fa19d32fa6eafdb48bb1054681e5699daaddc4b9ebf73477f0450d8dc78482cfad0147
|
data/README.md
CHANGED
|
@@ -29,6 +29,18 @@ exe/kapusta --compile examples/fizzbuzz.kap > examples/fizzbuzz.rb
|
|
|
29
29
|
ruby examples/fizzbuzz.rb
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
## Using from Ruby
|
|
33
|
+
|
|
34
|
+
Ruby can require a `.kap` file and use it directly.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
require 'kapusta'
|
|
38
|
+
Kapusta.require('./bank-account', relative_to: __FILE__)
|
|
39
|
+
account = BankAccount.new('Ada', 100)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
See `examples/bank-account.kap` and `examples/use_bank_account.rb`.
|
|
43
|
+
|
|
32
44
|
## Comparison with Fennel
|
|
33
45
|
|
|
34
46
|
Kapusta keeps most core Fennel forms. The main differences come from Ruby's runtime and object model.
|
|
@@ -40,6 +52,7 @@ Kapusta keeps most core Fennel forms. The main differences come from Ruby's runt
|
|
|
40
52
|
| `(. xs 1)` is the first element | `(. xs 0)` is the first element |
|
|
41
53
|
| `string.format`, `table.insert`, etc. | use Ruby methods and stdlib instead |
|
|
42
54
|
| `values` uses Lua multiple returns | `values` lowers to a Ruby array, usually destructured |
|
|
55
|
+
| `(print x)` is Lua's `print` (bare) | `(print x)` is Ruby's `p` (inspect-style) |
|
|
43
56
|
| `with-open`, `tail!` | not provided |
|
|
44
57
|
| macros | not provided for now |
|
|
45
58
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
(class BankAccount)
|
|
2
|
+
|
|
3
|
+
(fn initialize [owner balance]
|
|
4
|
+
(set (ivar owner) owner)
|
|
5
|
+
(set (ivar balance) balance))
|
|
6
|
+
|
|
7
|
+
(fn owner []
|
|
8
|
+
(ivar owner))
|
|
9
|
+
|
|
10
|
+
(fn balance []
|
|
11
|
+
(ivar balance))
|
|
12
|
+
|
|
13
|
+
(fn deposit [amount]
|
|
14
|
+
(set (ivar balance)
|
|
15
|
+
(+ (ivar balance) amount))
|
|
16
|
+
self)
|
|
17
|
+
|
|
18
|
+
(fn withdraw [amount]
|
|
19
|
+
(set (ivar balance)
|
|
20
|
+
(- (ivar balance) amount))
|
|
21
|
+
self)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
(fn cal-points [ops]
|
|
2
|
+
(let [scores []]
|
|
3
|
+
(each [op ops]
|
|
4
|
+
(if (= op "C") (do (-?> (scores.pop) (: :abs)) nil)
|
|
5
|
+
(= op "D") (scores.push (* 2 (. scores -1)))
|
|
6
|
+
(= op "+") (scores.push (+ (. scores -1) (. scores -2)))
|
|
7
|
+
(scores.push (Integer op))))
|
|
8
|
+
scores.sum))
|
|
9
|
+
|
|
10
|
+
(print (cal-points ["5" "2" "C" "D" "+"]))
|
|
11
|
+
(print (cal-points ["5" "-2" "4" "C" "D" "9" "+" "+"]))
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
(fn sum-of-squares [n]
|
|
2
|
+
(var x n)
|
|
3
|
+
(var total 0)
|
|
4
|
+
(while (> x 0)
|
|
5
|
+
(let [d (% x 10)]
|
|
6
|
+
(set total (+ total (* d d)))
|
|
7
|
+
(set x (: (/ x 10) :floor))))
|
|
8
|
+
total)
|
|
9
|
+
|
|
10
|
+
(fn happy? [n]
|
|
11
|
+
(let [seen {}]
|
|
12
|
+
(var x n)
|
|
13
|
+
(while (and (not= x 1) (not (seen.key? x)))
|
|
14
|
+
(tset seen x true)
|
|
15
|
+
(set x (sum-of-squares x)))
|
|
16
|
+
(= x 1)))
|
|
17
|
+
|
|
18
|
+
(print (happy? 19))
|
|
19
|
+
(print (happy? 2))
|
|
20
|
+
(print (happy? 1))
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
(fn max-subarray [nums]
|
|
2
|
+
(var best (. nums 0))
|
|
3
|
+
(var curr (. nums 0))
|
|
4
|
+
(for [i 1 (- (length nums) 1)]
|
|
5
|
+
(let [n (. nums i)]
|
|
6
|
+
(set curr (if (> (+ curr n) n) (+ curr n) n))
|
|
7
|
+
(when (> curr best) (set best curr))))
|
|
8
|
+
best)
|
|
9
|
+
|
|
10
|
+
(print (max-subarray [-2 1 -3 4 -1 2 1 -5 4]))
|
|
11
|
+
(print (max-subarray [1]))
|
|
12
|
+
(print (max-subarray [5 4 -1 7 8]))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
(fn move-zeroes [nums]
|
|
2
|
+
(var write 0)
|
|
3
|
+
(for [read 0 (- (length nums) 1)]
|
|
4
|
+
(when (not= (. nums read) 0)
|
|
5
|
+
(tset nums write (. nums read))
|
|
6
|
+
(set write (+ write 1))))
|
|
7
|
+
(for [i write (- (length nums) 1)]
|
|
8
|
+
(tset nums i 0))
|
|
9
|
+
nums)
|
|
10
|
+
|
|
11
|
+
(print (move-zeroes [0 1 0 3 12]))
|
|
12
|
+
(print (move-zeroes [0]))
|
|
13
|
+
(print (move-zeroes [1 2 3]))
|
data/examples/stack.kap
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
|
-
(class
|
|
1
|
+
(class MinStack)
|
|
2
2
|
|
|
3
3
|
(fn initialize []
|
|
4
|
-
(set (ivar xs) [])
|
|
4
|
+
(set (ivar xs) [])
|
|
5
|
+
(set (ivar mins) []))
|
|
5
6
|
|
|
6
|
-
(fn push
|
|
7
|
-
(let [xs (ivar xs)
|
|
8
|
-
|
|
7
|
+
(fn push [x]
|
|
8
|
+
(let [xs (ivar xs)
|
|
9
|
+
mins (ivar mins)
|
|
10
|
+
current-min (if (mins.empty?) x (< x (. mins -1)) x (. mins -1))]
|
|
11
|
+
(xs.push x)
|
|
12
|
+
(mins.push current-min))
|
|
9
13
|
self)
|
|
10
14
|
|
|
11
|
-
(fn pop
|
|
15
|
+
(fn pop []
|
|
16
|
+
(let [mins (ivar mins)]
|
|
17
|
+
(mins.pop))
|
|
12
18
|
(let [xs (ivar xs)]
|
|
13
19
|
(xs.pop)))
|
|
14
20
|
|
|
15
|
-
(fn
|
|
16
|
-
(
|
|
21
|
+
(fn top []
|
|
22
|
+
(. (ivar xs) -1))
|
|
23
|
+
|
|
24
|
+
(fn get-min []
|
|
25
|
+
(. (ivar mins) -1))
|
|
26
|
+
|
|
27
|
+
(let [s (MinStack.new)]
|
|
28
|
+
(s.push -2)
|
|
29
|
+
(s.push 0)
|
|
30
|
+
(s.push -3)
|
|
31
|
+
(print (s.get-min))
|
|
32
|
+
(s.pop)
|
|
33
|
+
(print (s.top))
|
|
34
|
+
(print (s.get-min)))
|
|
17
35
|
|
|
18
|
-
(
|
|
19
|
-
(length (ivar xs)))
|
|
36
|
+
(print (= MinStack.superclass Object))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
(let [
|
|
2
|
+
two-sum-hash
|
|
3
|
+
(fn [nums target seen]
|
|
4
|
+
(var i 0)
|
|
5
|
+
(var answer nil)
|
|
6
|
+
(while (and (< i (length nums)) (= answer nil))
|
|
7
|
+
(let [n (. nums i)
|
|
8
|
+
complement (- target n)]
|
|
9
|
+
(if (seen.key? complement)
|
|
10
|
+
(set answer [(. seen complement) i])
|
|
11
|
+
(tset seen n i)))
|
|
12
|
+
(set i (+ i 1)))
|
|
13
|
+
answer)
|
|
14
|
+
]
|
|
15
|
+
(print (two-sum-hash [2 7 11 15] 9 {}))
|
|
16
|
+
(print (two-sum-hash [3 2 4] 6 {}))
|
|
17
|
+
(print (two-sum-hash [1 2 3] 10 {})))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
require 'kapusta'
|
|
5
|
+
|
|
6
|
+
Kapusta.require('./bank-account', relative_to: __FILE__)
|
|
7
|
+
|
|
8
|
+
account = BankAccount.new('Ada', 100)
|
|
9
|
+
account.deposit(50)
|
|
10
|
+
account.withdraw(30)
|
|
11
|
+
|
|
12
|
+
puts "Owner: #{account.owner}"
|
|
13
|
+
puts "Balance: #{account.balance}"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
(class ValidParenthesesSolution)
|
|
2
|
+
|
|
3
|
+
(fn initialize []
|
|
4
|
+
(set (ivar pairs)
|
|
5
|
+
{")" "(" "]" "[" "}" "{"}))
|
|
6
|
+
|
|
7
|
+
(fn valid? [s]
|
|
8
|
+
(let [pairs (ivar pairs)
|
|
9
|
+
stack []
|
|
10
|
+
chars (s.chars)]
|
|
11
|
+
(var i 0)
|
|
12
|
+
(var ok true)
|
|
13
|
+
(while (and ok (< i (length chars)))
|
|
14
|
+
(let [ch (. chars i)]
|
|
15
|
+
(if (pairs.key? ch)
|
|
16
|
+
(if (-?> (stack.pop) (= (. pairs ch))) nil (set ok false))
|
|
17
|
+
(stack.push ch)))
|
|
18
|
+
(set i (+ i 1)))
|
|
19
|
+
(and ok (stack.empty?))))
|
data/lib/kapusta/ast.rb
CHANGED
|
@@ -25,12 +25,16 @@ module Kapusta
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def ==(other)
|
|
28
|
-
other.
|
|
28
|
+
other.instance_of?(self.class) && other.name == @name
|
|
29
29
|
end
|
|
30
30
|
alias eql? ==
|
|
31
31
|
|
|
32
32
|
def hash
|
|
33
|
-
@name.hash
|
|
33
|
+
[self.class, @name].hash
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def binding_key
|
|
37
|
+
@name
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
def dotted?
|
|
@@ -42,6 +46,32 @@ module Kapusta
|
|
|
42
46
|
end
|
|
43
47
|
end
|
|
44
48
|
|
|
49
|
+
class GeneratedSym < Sym
|
|
50
|
+
attr_reader :id
|
|
51
|
+
|
|
52
|
+
def initialize(name, id)
|
|
53
|
+
super(name)
|
|
54
|
+
@id = id
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def inspect
|
|
58
|
+
"#<GeneratedSym #{@name} #{@id}>"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def ==(other)
|
|
62
|
+
other.is_a?(GeneratedSym) && other.id == @id
|
|
63
|
+
end
|
|
64
|
+
alias eql? ==
|
|
65
|
+
|
|
66
|
+
def hash
|
|
67
|
+
[self.class, @id].hash
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def binding_key
|
|
71
|
+
[self.class, @id]
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
45
75
|
class Vec
|
|
46
76
|
attr_reader :items
|
|
47
77
|
|
|
@@ -62,7 +92,7 @@ module Kapusta
|
|
|
62
92
|
end
|
|
63
93
|
|
|
64
94
|
def all_sym_keys?
|
|
65
|
-
pairs.all? { |key, _| key.is_a?(Symbol) }
|
|
95
|
+
pairs.any? && pairs.all? { |key, _| key.is_a?(Symbol) }
|
|
66
96
|
end
|
|
67
97
|
end
|
|
68
98
|
|
|
@@ -42,9 +42,7 @@ module Kapusta
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def emit_simple_lambda(pattern, body, env, current_scope)
|
|
45
|
-
|
|
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)
|
|
45
|
+
params, body_code = build_simple_block_parts(pattern, body, env, current_scope)
|
|
48
46
|
header = params.empty? ? 'proc do' : "proc do |#{params.join(', ')}|"
|
|
49
47
|
[
|
|
50
48
|
header,
|
|
@@ -53,6 +51,13 @@ module Kapusta
|
|
|
53
51
|
].join("\n")
|
|
54
52
|
end
|
|
55
53
|
|
|
54
|
+
def build_simple_block_parts(pattern, body, env, current_scope)
|
|
55
|
+
body_env = env.child
|
|
56
|
+
params = pattern.items.map { |sym| define_local(body_env, sym.name, shadow: true) }
|
|
57
|
+
body_code, = emit_sequence(body, body_env, current_scope, allow_method_definitions: false)
|
|
58
|
+
[params, body_code]
|
|
59
|
+
end
|
|
60
|
+
|
|
56
61
|
def simple_parameter_pattern?(pattern)
|
|
57
62
|
pattern.is_a?(Vec) && pattern.items.all? { |item| item.is_a?(Sym) && !item.dotted? && item.name != '&' }
|
|
58
63
|
end
|
|
@@ -172,9 +177,7 @@ module Kapusta
|
|
|
172
177
|
end
|
|
173
178
|
|
|
174
179
|
def emit_simple_method_body(pattern, body, env)
|
|
175
|
-
|
|
176
|
-
params = pattern.items.map { |sym| define_local(body_env, sym.name, shadow: true) }
|
|
177
|
-
body_code, = emit_sequence(body, body_env, :toplevel, allow_method_definitions: false)
|
|
180
|
+
params, body_code = build_simple_block_parts(pattern, body, env, :toplevel)
|
|
178
181
|
[params.empty? ? 'do' : "do |#{params.join(', ')}|", body_code]
|
|
179
182
|
end
|
|
180
183
|
|
|
@@ -200,9 +203,12 @@ module Kapusta
|
|
|
200
203
|
|
|
201
204
|
def sym_captures_outer_binding?(sym, env, local_names)
|
|
202
205
|
name = sym.dotted? ? sym.segments.first : sym.name
|
|
203
|
-
return false if local_names.include?(name)
|
|
206
|
+
return false if local_names.include?(name)
|
|
207
|
+
|
|
208
|
+
binding = env.lookup_if_defined(name)
|
|
209
|
+
return false if binding.nil?
|
|
204
210
|
|
|
205
|
-
!method_binding?(
|
|
211
|
+
!method_binding?(binding)
|
|
206
212
|
end
|
|
207
213
|
|
|
208
214
|
def emit_let(args, env, current_scope)
|
|
@@ -266,10 +272,10 @@ module Kapusta
|
|
|
266
272
|
value_code = emit_expr(form.items[2], env, current_scope)
|
|
267
273
|
|
|
268
274
|
if target.is_a?(Sym) && !target.dotted?
|
|
275
|
+
binding = env.lookup_if_defined(target.name)
|
|
269
276
|
ruby_name =
|
|
270
|
-
if
|
|
271
|
-
binding
|
|
272
|
-
raise Error, "cannot set method binding: #{target.name}" if method_binding?(binding)
|
|
277
|
+
if binding
|
|
278
|
+
emit_error!("cannot set method binding: #{target.name}") if method_binding?(binding)
|
|
273
279
|
|
|
274
280
|
binding
|
|
275
281
|
else
|
|
@@ -292,10 +298,17 @@ module Kapusta
|
|
|
292
298
|
when Sym
|
|
293
299
|
if target.dotted?
|
|
294
300
|
base_code, segments = multisym_base(target.segments, env)
|
|
295
|
-
|
|
301
|
+
receiver = emit_method_path(base_code, segments[0...-1])
|
|
302
|
+
last = segments.last
|
|
303
|
+
snake = Kapusta.kebab_to_snake(last)
|
|
304
|
+
if direct_method_name?(last)
|
|
305
|
+
"#{receiver}.#{snake} = #{value_code}"
|
|
306
|
+
else
|
|
307
|
+
"#{receiver}.public_send(:\"#{snake}=\", #{value_code})"
|
|
308
|
+
end
|
|
296
309
|
else
|
|
297
310
|
binding = env.lookup(target.name)
|
|
298
|
-
|
|
311
|
+
emit_error!("cannot set method binding: #{target.name}") if method_binding?(binding)
|
|
299
312
|
|
|
300
313
|
"#{binding} = #{value_code}"
|
|
301
314
|
end
|
|
@@ -303,24 +316,21 @@ module Kapusta
|
|
|
303
316
|
head = target.head
|
|
304
317
|
if head.is_a?(Sym) && head.name == '.'
|
|
305
318
|
object_code = emit_expr(target.items[1], env, current_scope)
|
|
306
|
-
|
|
307
|
-
|
|
319
|
+
keys = target.items[2..].map { |item| emit_expr(item, env, current_scope) }
|
|
320
|
+
receiver = simple_expression?(object_code) ? object_code : parenthesize(object_code)
|
|
321
|
+
prefix = keys[0...-1].map { |k| "[#{k}]" }.join
|
|
322
|
+
"#{receiver}#{prefix}[#{keys.last}] = #{value_code}"
|
|
308
323
|
elsif head.is_a?(Sym) && head.name == 'ivar'
|
|
309
|
-
|
|
324
|
+
"@#{Kapusta.kebab_to_snake(target.items[1].name)} = #{value_code}"
|
|
310
325
|
elsif head.is_a?(Sym) && head.name == 'cvar'
|
|
311
|
-
|
|
326
|
+
"@@#{Kapusta.kebab_to_snake(target.items[1].name)} = #{value_code}"
|
|
312
327
|
elsif head.is_a?(Sym) && head.name == 'gvar'
|
|
313
|
-
|
|
314
|
-
if direct_global_name?(ruby_name)
|
|
315
|
-
"$#{ruby_name} = #{value_code}"
|
|
316
|
-
else
|
|
317
|
-
runtime_call(:set_gvar, target.items[1].name.inspect, value_code)
|
|
318
|
-
end
|
|
328
|
+
"$#{global_name(target.items[1].name)} = #{value_code}"
|
|
319
329
|
else
|
|
320
|
-
|
|
330
|
+
emit_error!("bad set target: #{target.inspect}")
|
|
321
331
|
end
|
|
322
332
|
else
|
|
323
|
-
|
|
333
|
+
emit_error!("bad set target: #{target.inspect}")
|
|
324
334
|
end
|
|
325
335
|
end
|
|
326
336
|
end
|
|
@@ -82,12 +82,12 @@ module Kapusta
|
|
|
82
82
|
arm_env = env.child
|
|
83
83
|
assign_code, arm_env = emit_bindings_from_match(plan[:bindings], bindings_var, arm_env)
|
|
84
84
|
body_code = emit_expr(body, arm_env, current_scope)
|
|
85
|
+
arm_body = [assign_code, body_code].reject(&:empty?).join("\n")
|
|
85
86
|
<<~RUBY.chomp
|
|
86
87
|
#{match_var} = #{runtime_call(:match_pattern, plan[:pattern], value_var)}
|
|
87
88
|
if #{match_var}[0]
|
|
88
89
|
#{bindings_var} = #{match_var}[1]
|
|
89
|
-
#{
|
|
90
|
-
#{body_code}
|
|
90
|
+
#{arm_body}
|
|
91
91
|
else
|
|
92
92
|
#{indent(else_code)}
|
|
93
93
|
end
|
|
@@ -104,11 +104,11 @@ module Kapusta
|
|
|
104
104
|
assign_code, arm_env = emit_bindings_from_match(plan[:bindings], bindings_var, arm_env)
|
|
105
105
|
guard_code = emit_case_guards(guards, arm_env, current_scope)
|
|
106
106
|
body_code = emit_expr(body, arm_env, current_scope)
|
|
107
|
+
bindings_line = assign_code.empty? ? '' : "\n #{assign_code}"
|
|
107
108
|
<<~RUBY.chomp
|
|
108
109
|
#{match_var} = #{runtime_call(:match_pattern, plan[:pattern], value_var)}
|
|
109
110
|
if #{match_var}[0]
|
|
110
|
-
#{bindings_var} = #{match_var}[1]
|
|
111
|
-
#{assign_code}
|
|
111
|
+
#{bindings_var} = #{match_var}[1]#{bindings_line}
|
|
112
112
|
if #{guard_code}
|
|
113
113
|
#{body_code}
|
|
114
114
|
else
|
|
@@ -17,7 +17,7 @@ module Kapusta
|
|
|
17
17
|
when List then emit_list(form, env, current_scope)
|
|
18
18
|
when String, Symbol, Numeric, true, false, nil then form.inspect
|
|
19
19
|
else
|
|
20
|
-
|
|
20
|
+
emit_error!("cannot emit form: #{form.inspect}")
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -36,7 +36,9 @@ module Kapusta
|
|
|
36
36
|
if head.is_a?(Sym)
|
|
37
37
|
return emit_special(head.name, args, env, current_scope) if special_form?(head.name)
|
|
38
38
|
return emit_multisym_call(head, args, env, current_scope) if head.dotted?
|
|
39
|
-
|
|
39
|
+
if (binding = env.lookup_if_defined(head.name))
|
|
40
|
+
return emit_bound_call(binding, args, env, current_scope)
|
|
41
|
+
end
|
|
40
42
|
|
|
41
43
|
return emit_self_call(head.name, args, env, current_scope)
|
|
42
44
|
end
|
|
@@ -69,21 +71,21 @@ module Kapusta
|
|
|
69
71
|
when '?.' then emit_safe_lookup(args, env, current_scope)
|
|
70
72
|
when ':' then emit_colon(args, env, current_scope)
|
|
71
73
|
when '..' then emit_concat(args, env, current_scope)
|
|
72
|
-
when 'length' then "
|
|
74
|
+
when 'length' then "#{parenthesize(emit_expr(args[0], env, current_scope))}.length"
|
|
73
75
|
when 'require' then emit_require(args[0], env, current_scope)
|
|
74
76
|
when 'module' then emit_module_expr(args, env)
|
|
75
77
|
when 'class' then emit_class_expr(args, env)
|
|
76
78
|
when 'try' then emit_try(args, env, current_scope)
|
|
77
79
|
when 'raise' then emit_raise(args, env, current_scope)
|
|
78
|
-
when 'ivar' then
|
|
79
|
-
when 'cvar' then
|
|
80
|
+
when 'ivar' then "@#{Kapusta.kebab_to_snake(args[0].name)}"
|
|
81
|
+
when 'cvar' then "@@#{Kapusta.kebab_to_snake(args[0].name)}"
|
|
80
82
|
when 'gvar' then emit_gvar(args[0])
|
|
81
83
|
when 'ruby' then "Kernel.eval(#{emit_expr(args[0], env, current_scope)})"
|
|
82
84
|
when 'and' then emit_and(args, env, current_scope)
|
|
83
85
|
when 'or' then emit_or(args, env, current_scope)
|
|
84
|
-
when 'not' then "
|
|
86
|
+
when 'not' then "!#{parenthesize(emit_expr(args[0], env, current_scope))}"
|
|
85
87
|
when '=' then emit_compare(args, env, current_scope, '==')
|
|
86
|
-
when 'not=' then
|
|
88
|
+
when 'not=' then emit_compare_any(args, env, current_scope, '!=')
|
|
87
89
|
when '<' then emit_compare(args, env, current_scope, '<')
|
|
88
90
|
when '<=' then emit_compare(args, env, current_scope, '<=')
|
|
89
91
|
when '>' then emit_compare(args, env, current_scope, '>')
|
|
@@ -95,7 +97,7 @@ module Kapusta
|
|
|
95
97
|
when '%' then args.map { |arg| parenthesize(emit_expr(arg, env, current_scope)) }.join(' % ')
|
|
96
98
|
when 'print' then emit_print(args, env, current_scope)
|
|
97
99
|
else
|
|
98
|
-
|
|
100
|
+
emit_error!("unknown special form: #{name}")
|
|
99
101
|
end
|
|
100
102
|
end
|
|
101
103
|
|
|
@@ -106,17 +108,15 @@ module Kapusta
|
|
|
106
108
|
end
|
|
107
109
|
|
|
108
110
|
def emit_print(args, env, current_scope)
|
|
109
|
-
return '
|
|
111
|
+
return 'p' if args.empty?
|
|
110
112
|
|
|
111
|
-
|
|
112
|
-
output = values.length == 1 ? values.first : "[#{values.join(', ')}].join(\"\\t\")"
|
|
113
|
-
"$stdout.puts(#{output})"
|
|
113
|
+
"p(#{args.map { |arg| emit_expr(arg, env, current_scope) }.join(', ')})"
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
def emit_string_part(arg, env, current_scope)
|
|
117
117
|
return arg.inspect if arg.is_a?(String)
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
"(#{emit_expr(arg, env, current_scope)}).to_s"
|
|
120
120
|
end
|
|
121
121
|
end
|
|
122
122
|
end
|