kapusta 0.1.5 → 0.2.1
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 +43 -4
- data/examples/bank-account.kap +21 -0
- data/examples/baseball-game.kap +11 -0
- data/examples/best-time-to-buy-sell-stock.kap +12 -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/majority-element.kap +11 -0
- data/examples/maximum-subarray.kap +12 -0
- data/examples/move-zeroes.kap +13 -0
- data/examples/plus-one.kap +14 -0
- data/examples/reverse-integer.kap +13 -0
- data/examples/roman-to-integer.kap +17 -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/examples/zoo-animal-1.kap +5 -0
- data/examples/zoo-animal-inheritance-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 +15 -7
- 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 +257 -81
- data/spec/reader_spec.rb +26 -0
- metadata +23 -8
- data/examples/inheritance.kap +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ee9b062de9860ef3b9f3cfcbac8503e69c1549f0f09af35b41e5eb13303c1ddb
|
|
4
|
+
data.tar.gz: daa87e0b3b65dfc82f21ff47e88b41652d05f087e46320ca1c3f49bac2457b71
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 564c441ea9395cd4f28daaa631de6022e39125eede47032bf32bbc2a45f18979b7376f264b5302459b78403ef039ef8aca93e6f469cc0666b033f6e6717894a2
|
|
7
|
+
data.tar.gz: 38479cb8d38b81df15697a56c1a14e63d4dda7a0dd793db4e121e06126395339d9c7676a4bb14411a6b43398b64196f877ce3126990eb07d24f2a0c769a577e2
|
data/README.md
CHANGED
|
@@ -29,6 +29,48 @@ 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
|
+
|
|
44
|
+
## Examples
|
|
45
|
+
|
|
46
|
+
See [`examples/`](https://github.com/evmorov/kapusta/tree/main/examples).
|
|
47
|
+
|
|
48
|
+
```fennel
|
|
49
|
+
(fn ack [m n]
|
|
50
|
+
(if (= m 0) (+ n 1)
|
|
51
|
+
(= n 0) (ack (- m 1) 1)
|
|
52
|
+
(ack (- m 1) (ack m (- n 1)))))
|
|
53
|
+
|
|
54
|
+
(print (ack 2 3))
|
|
55
|
+
(print (ack 3 3))
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Compiles to:
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
def ack(m, n)
|
|
62
|
+
if m == 0
|
|
63
|
+
n + 1
|
|
64
|
+
elsif n == 0
|
|
65
|
+
ack(m - 1, 1)
|
|
66
|
+
else
|
|
67
|
+
ack(m - 1, ack(m, n - 1))
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
p(ack(2, 3))
|
|
71
|
+
p(ack(3, 3))
|
|
72
|
+
```
|
|
73
|
+
|
|
32
74
|
## Comparison with Fennel
|
|
33
75
|
|
|
34
76
|
Kapusta keeps most core Fennel forms. The main differences come from Ruby's runtime and object model.
|
|
@@ -40,6 +82,7 @@ Kapusta keeps most core Fennel forms. The main differences come from Ruby's runt
|
|
|
40
82
|
| `(. xs 1)` is the first element | `(. xs 0)` is the first element |
|
|
41
83
|
| `string.format`, `table.insert`, etc. | use Ruby methods and stdlib instead |
|
|
42
84
|
| `values` uses Lua multiple returns | `values` lowers to a Ruby array, usually destructured |
|
|
85
|
+
| `(print x)` is Lua's `print` (bare) | `(print x)` is Ruby's `p` (inspect-style) |
|
|
43
86
|
| `with-open`, `tail!` | not provided |
|
|
44
87
|
| macros | not provided for now |
|
|
45
88
|
|
|
@@ -52,10 +95,6 @@ Kapusta-specific additions:
|
|
|
52
95
|
- a trailing symbol-keyed hash is emitted as Ruby keyword arguments
|
|
53
96
|
- a final function literal argument is emitted as a Ruby block
|
|
54
97
|
|
|
55
|
-
## Examples
|
|
56
|
-
|
|
57
|
-
See `examples/`.
|
|
58
|
-
|
|
59
98
|
## Formatting
|
|
60
99
|
|
|
61
100
|
```
|
|
@@ -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,12 @@
|
|
|
1
|
+
(fn max-profit [prices]
|
|
2
|
+
(var min-price (. prices 0))
|
|
3
|
+
(var best 0)
|
|
4
|
+
(for [i 1 (- (length prices) 1)]
|
|
5
|
+
(let [p (. prices i)]
|
|
6
|
+
(when (< p min-price) (set min-price p))
|
|
7
|
+
(when (> (- p min-price) best) (set best (- p min-price)))))
|
|
8
|
+
best)
|
|
9
|
+
|
|
10
|
+
(print (max-profit [7 1 5 3 6 4]))
|
|
11
|
+
(print (max-profit [7 6 4 3 1]))
|
|
12
|
+
(print (max-profit [2 4 1]))
|
|
@@ -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,11 @@
|
|
|
1
|
+
(fn majority [nums]
|
|
2
|
+
(var candidate nil)
|
|
3
|
+
(var count 0)
|
|
4
|
+
(each [n nums]
|
|
5
|
+
(when (= count 0) (set candidate n))
|
|
6
|
+
(if (= n candidate) (set count (+ count 1)) (set count (- count 1))))
|
|
7
|
+
candidate)
|
|
8
|
+
|
|
9
|
+
(print (majority [3 2 3]))
|
|
10
|
+
(print (majority [2 2 1 1 1 2 2]))
|
|
11
|
+
(print (majority [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]))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
(fn plus-one [digits]
|
|
2
|
+
(var i (- (length digits) 1))
|
|
3
|
+
(var carry 1)
|
|
4
|
+
(while (and (>= i 0) (> carry 0))
|
|
5
|
+
(let [total (+ (. digits i) carry)]
|
|
6
|
+
(tset digits i (% total 10))
|
|
7
|
+
(set carry (: (/ total 10) :floor)))
|
|
8
|
+
(set i (- i 1)))
|
|
9
|
+
(if (> carry 0) (: digits :unshift carry) digits))
|
|
10
|
+
|
|
11
|
+
(print (plus-one [1 2 3]))
|
|
12
|
+
(print (plus-one [4 3 2 1]))
|
|
13
|
+
(print (plus-one [9]))
|
|
14
|
+
(print (plus-one [9 9]))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
(fn reverse-integer [x]
|
|
2
|
+
(let [sign (if (< x 0) -1 1)]
|
|
3
|
+
(var remaining (* x sign))
|
|
4
|
+
(var result 0)
|
|
5
|
+
(while (> remaining 0)
|
|
6
|
+
(set result (+ (* result 10) (% remaining 10)))
|
|
7
|
+
(set remaining (: (/ remaining 10) :floor)))
|
|
8
|
+
(* result sign)))
|
|
9
|
+
|
|
10
|
+
(print (reverse-integer 123))
|
|
11
|
+
(print (reverse-integer -123))
|
|
12
|
+
(print (reverse-integer 120))
|
|
13
|
+
(print (reverse-integer 0))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
(fn roman-to-integer [s]
|
|
2
|
+
(let [values {"I" 1 "V" 5 "X" 10 "L" 50 "C" 100 "D" 500 "M" 1000}
|
|
3
|
+
chars (s.chars)
|
|
4
|
+
n (length chars)]
|
|
5
|
+
(var total 0)
|
|
6
|
+
(var i 0)
|
|
7
|
+
(while (< i n)
|
|
8
|
+
(let [curr (. values (. chars i))
|
|
9
|
+
ahead (if (< (+ i 1) n) (. values (. chars (+ i 1))) 0)
|
|
10
|
+
subtract? (< curr ahead)]
|
|
11
|
+
(set total (+ total (if subtract? (- ahead curr) curr)))
|
|
12
|
+
(set i (+ i (if subtract? 2 1)))))
|
|
13
|
+
total))
|
|
14
|
+
|
|
15
|
+
(print (roman-to-integer "III"))
|
|
16
|
+
(print (roman-to-integer "LVIII"))
|
|
17
|
+
(print (roman-to-integer "MCMXCIV"))
|
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
|
|
@@ -15,7 +15,7 @@ module Kapusta
|
|
|
15
15
|
return emit_expr(args[0], env, current_scope) if args.length == 1
|
|
16
16
|
|
|
17
17
|
cond = emit_expr(args[0], env, current_scope)
|
|
18
|
-
truthy =
|
|
18
|
+
truthy = emit_if_branch(args[1], env, current_scope)
|
|
19
19
|
lines = ["if #{cond}", indent(truthy)]
|
|
20
20
|
append_else_lines(lines, args[2..], env, current_scope)
|
|
21
21
|
lines << 'end'
|
|
@@ -31,7 +31,7 @@ module Kapusta
|
|
|
31
31
|
append_elsif_lines(lines, args, env, current_scope)
|
|
32
32
|
else
|
|
33
33
|
lines << 'else'
|
|
34
|
-
lines << indent(
|
|
34
|
+
lines << indent(emit_if_branch(args[0], env, current_scope))
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
@@ -39,10 +39,18 @@ module Kapusta
|
|
|
39
39
|
return append_else_lines(lines, args, env, current_scope) if args.length < 2
|
|
40
40
|
|
|
41
41
|
lines << "elsif #{emit_expr(args[0], env, current_scope)}"
|
|
42
|
-
lines << indent(
|
|
42
|
+
lines << indent(emit_if_branch(args[1], env, current_scope))
|
|
43
43
|
append_else_lines(lines, args[2..], env, current_scope)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
def emit_if_branch(form, env, current_scope)
|
|
47
|
+
return emit_expr(form, env, current_scope) unless do_form?(form)
|
|
48
|
+
|
|
49
|
+
emit_sequence(form.rest, env, current_scope,
|
|
50
|
+
allow_method_definitions: false,
|
|
51
|
+
result: true).first
|
|
52
|
+
end
|
|
53
|
+
|
|
46
54
|
def if_form?(form)
|
|
47
55
|
form.is_a?(List) && form.head.is_a?(Sym) && form.head.name == 'if'
|
|
48
56
|
end
|
|
@@ -82,12 +90,12 @@ module Kapusta
|
|
|
82
90
|
arm_env = env.child
|
|
83
91
|
assign_code, arm_env = emit_bindings_from_match(plan[:bindings], bindings_var, arm_env)
|
|
84
92
|
body_code = emit_expr(body, arm_env, current_scope)
|
|
93
|
+
arm_body = [assign_code, body_code].reject(&:empty?).join("\n")
|
|
85
94
|
<<~RUBY.chomp
|
|
86
95
|
#{match_var} = #{runtime_call(:match_pattern, plan[:pattern], value_var)}
|
|
87
96
|
if #{match_var}[0]
|
|
88
97
|
#{bindings_var} = #{match_var}[1]
|
|
89
|
-
#{
|
|
90
|
-
#{body_code}
|
|
98
|
+
#{arm_body}
|
|
91
99
|
else
|
|
92
100
|
#{indent(else_code)}
|
|
93
101
|
end
|
|
@@ -104,11 +112,11 @@ module Kapusta
|
|
|
104
112
|
assign_code, arm_env = emit_bindings_from_match(plan[:bindings], bindings_var, arm_env)
|
|
105
113
|
guard_code = emit_case_guards(guards, arm_env, current_scope)
|
|
106
114
|
body_code = emit_expr(body, arm_env, current_scope)
|
|
115
|
+
bindings_line = assign_code.empty? ? '' : "\n #{assign_code}"
|
|
107
116
|
<<~RUBY.chomp
|
|
108
117
|
#{match_var} = #{runtime_call(:match_pattern, plan[:pattern], value_var)}
|
|
109
118
|
if #{match_var}[0]
|
|
110
|
-
#{bindings_var} = #{match_var}[1]
|
|
111
|
-
#{assign_code}
|
|
119
|
+
#{bindings_var} = #{match_var}[1]#{bindings_line}
|
|
112
120
|
if #{guard_code}
|
|
113
121
|
#{body_code}
|
|
114
122
|
else
|