kapusta 0.2.4 → 0.4.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 +7 -2
- data/bin/check-all +19 -0
- data/bin/fennel-parity +157 -0
- data/examples/macros-dbg.kap +9 -0
- data/examples/macros-multi.kap +12 -0
- data/examples/macros-swap.kap +9 -0
- data/examples/macros-thrice-if.kap +18 -0
- data/examples/macros-unless.kap +7 -0
- data/examples/macros-when-let.kap +7 -0
- data/examples/manhattan-distance.kap +9 -0
- data/examples/packet-router.kap +2 -5
- data/examples/subtract-product-sum.kap +14 -0
- data/examples/tic-tac-toe.kap +4 -9
- data/examples/ugly-number.kap +22 -0
- data/lib/kapusta/ast.rb +42 -0
- data/lib/kapusta/compiler/emitter/bindings.rb +18 -7
- data/lib/kapusta/compiler/emitter/collections.rb +69 -45
- data/lib/kapusta/compiler/emitter/control_flow.rb +39 -66
- data/lib/kapusta/compiler/emitter/expressions.rb +34 -0
- data/lib/kapusta/compiler/emitter/interop.rb +65 -72
- data/lib/kapusta/compiler/emitter/patterns.rb +192 -109
- data/lib/kapusta/compiler/emitter/support.rb +4 -16
- data/lib/kapusta/compiler/emitter.rb +1 -3
- data/lib/kapusta/compiler/macro_expander.rb +256 -0
- data/lib/kapusta/compiler/normalizer.rb +2 -2
- data/lib/kapusta/compiler.rb +8 -1
- data/lib/kapusta/formatter.rb +216 -87
- data/lib/kapusta/reader.rb +46 -10
- data/lib/kapusta/version.rb +1 -1
- data/lib/kapusta.rb +28 -4
- data/spec/examples_spec.rb +59 -0
- data/spec/formatter_spec.rb +8 -10
- metadata +13 -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: 01cc980a37ff63b56db021edb64f7ed9b4381569bb82e8e4ada099a2b17ba19e
|
|
4
|
+
data.tar.gz: 035f0f4a38d5d9fad65f4a53e8343bbcad4b681bd33dfa975f7c9c638a83ccb4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: caaeb31630a5c80607fe6c2907548c2874f177add0bdbd3555e72039dd598aaf481f91778043f6b81cd511c9ac8609e48f61c474766dac9b3e8a2fa41d408970
|
|
7
|
+
data.tar.gz: 5417ad8088be602618c3662660af820be6dbf15eb27d8300b1c542e3889fbfe1813d51ca7306f988a630616d1857f333141bf20b964d0869c2caa8c3eba869e3
|
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
|
```
|
|
@@ -43,7 +49,7 @@ See `examples/bank-account.kap` and `examples/use_bank_account.rb`.
|
|
|
43
49
|
|
|
44
50
|
## Examples
|
|
45
51
|
|
|
46
|
-
See [`examples/`](https://github.com/evmorov/kapusta/tree/main/examples).
|
|
52
|
+
See [`examples/`](https://github.com/evmorov/kapusta/tree/main/examples/) and [`examples-compiled/`](https://github.com/evmorov/kapusta/tree/main/examples-compiled/).
|
|
47
53
|
|
|
48
54
|
```fennel
|
|
49
55
|
(fn ack [m n]
|
|
@@ -84,7 +90,6 @@ Kapusta keeps most core Fennel forms. The main differences come from Ruby's runt
|
|
|
84
90
|
| `values` uses Lua multiple returns | `values` lowers to a Ruby array, usually destructured |
|
|
85
91
|
| `(print x)` is Lua's `print` (bare) | `(print x)` is Ruby's `p` (inspect-style) |
|
|
86
92
|
| `with-open`, `tail!` | not provided |
|
|
87
|
-
| macros | not provided for now |
|
|
88
93
|
|
|
89
94
|
Kapusta-specific additions:
|
|
90
95
|
|
data/bin/check-all
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
echo "== rspec =="
|
|
5
|
+
bundle exec rspec
|
|
6
|
+
|
|
7
|
+
echo "== rubocop -A =="
|
|
8
|
+
bundle exec rubocop -A
|
|
9
|
+
|
|
10
|
+
echo "== kapfmt =="
|
|
11
|
+
for file in examples/*.kap; do ./exe/kapfmt --fix "$file"; done
|
|
12
|
+
|
|
13
|
+
echo "== fennel parity =="
|
|
14
|
+
bin/fennel-parity
|
|
15
|
+
|
|
16
|
+
echo "== compile examples =="
|
|
17
|
+
bin/compile-examples
|
|
18
|
+
|
|
19
|
+
echo "== Success! =="
|
data/bin/fennel-parity
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'open3'
|
|
5
|
+
|
|
6
|
+
ROOT = File.expand_path('..', __dir__)
|
|
7
|
+
EXAMPLES = File.join(ROOT, 'examples')
|
|
8
|
+
KAPUSTA = File.join(ROOT, 'exe', 'kapusta')
|
|
9
|
+
KAPFMT = File.join(ROOT, 'exe', 'kapfmt')
|
|
10
|
+
|
|
11
|
+
COMPATIBLE = %w[
|
|
12
|
+
ackermann.kap
|
|
13
|
+
anonymous-greeter.kap
|
|
14
|
+
climbing-stairs.kap
|
|
15
|
+
describe.kap
|
|
16
|
+
destructure.kap
|
|
17
|
+
factorial.kap
|
|
18
|
+
fib.kap
|
|
19
|
+
fizzbuzz.kap
|
|
20
|
+
gcd.kap
|
|
21
|
+
hashfn.kap
|
|
22
|
+
leap-year.kap
|
|
23
|
+
macros-dbg.kap
|
|
24
|
+
macros-multi.kap
|
|
25
|
+
macros-swap.kap
|
|
26
|
+
macros-thrice-if.kap
|
|
27
|
+
macros-unless.kap
|
|
28
|
+
macros-when-let.kap
|
|
29
|
+
match.kap
|
|
30
|
+
min-max.kap
|
|
31
|
+
or-patterns.kap
|
|
32
|
+
packet-router.kap
|
|
33
|
+
points.kap
|
|
34
|
+
primes.kap
|
|
35
|
+
safe-lookup.kap
|
|
36
|
+
shapes.kap
|
|
37
|
+
squares.kap
|
|
38
|
+
sum.kap
|
|
39
|
+
tic-tac-toe.kap
|
|
40
|
+
underscore-patterns.kap
|
|
41
|
+
].freeze
|
|
42
|
+
|
|
43
|
+
def run(cmd, file)
|
|
44
|
+
out, err, status = Open3.capture3(cmd, file, chdir: EXAMPLES)
|
|
45
|
+
[out, err, status]
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
['', e.message, nil]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def strip_outer_quotes(line)
|
|
51
|
+
if line.length >= 2 && line.start_with?('"') && line.end_with?('"')
|
|
52
|
+
line[1..-2]
|
|
53
|
+
else
|
|
54
|
+
line
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def normalize_kapusta(out)
|
|
59
|
+
out.lines.map { |l| strip_outer_quotes(l.chomp) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def normalize_fennel(out)
|
|
63
|
+
# Lua's `print` joins multiple args with TAB on one line; Ruby's `p`
|
|
64
|
+
# prints each on its own line. Split tabs so the two layouts line up.
|
|
65
|
+
out.lines.flat_map { |l| l.chomp.split("\t") }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def unified_diff(label_kap, kap_lines, label_fnl, fnl_lines)
|
|
69
|
+
max = [kap_lines.length, fnl_lines.length].max
|
|
70
|
+
rows = []
|
|
71
|
+
(0...max).each do |i|
|
|
72
|
+
la = kap_lines[i]
|
|
73
|
+
lb = fnl_lines[i]
|
|
74
|
+
next if la == lb
|
|
75
|
+
|
|
76
|
+
rows << format(' line %<n>3d %<a>s: %<la>s',
|
|
77
|
+
n: i + 1, a: label_kap, la: la.inspect)
|
|
78
|
+
rows << format(' %<b>s: %<lb>s',
|
|
79
|
+
b: label_fnl, lb: lb.inspect)
|
|
80
|
+
end
|
|
81
|
+
rows.join("\n")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def check_format(path)
|
|
85
|
+
k_out, k_err, k_status = run(KAPFMT, path)
|
|
86
|
+
f_out, f_err, f_status = run('fnlfmt', path)
|
|
87
|
+
|
|
88
|
+
return "kapfmt exited #{k_status&.exitstatus}: #{k_err.strip}" if k_status.nil? || !k_status.success?
|
|
89
|
+
return "fnlfmt exited #{f_status&.exitstatus}: #{f_err.strip}" if f_status.nil? || !f_status.success?
|
|
90
|
+
return if k_out == f_out
|
|
91
|
+
|
|
92
|
+
k_lines = k_out.lines.map(&:chomp)
|
|
93
|
+
f_lines = f_out.lines.map(&:chomp)
|
|
94
|
+
"format differs:\n#{unified_diff('kapfmt', k_lines, 'fnlfmt', f_lines)}"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def check_run(path)
|
|
98
|
+
k_out, k_err, k_status = run(KAPUSTA, path)
|
|
99
|
+
f_out, f_err, f_status = run('fennel', path)
|
|
100
|
+
|
|
101
|
+
return "kapusta exited #{k_status&.exitstatus}: #{k_err.strip}" if k_status.nil? || !k_status.success?
|
|
102
|
+
return "fennel exited #{f_status&.exitstatus}: #{f_err.strip}" if f_status.nil? || !f_status.success?
|
|
103
|
+
|
|
104
|
+
k_lines = normalize_kapusta(k_out)
|
|
105
|
+
f_lines = normalize_fennel(f_out)
|
|
106
|
+
return if k_lines == f_lines
|
|
107
|
+
|
|
108
|
+
if k_lines.length == f_lines.length
|
|
109
|
+
"output differs:\n#{unified_diff('kapusta', k_lines, 'fennel', f_lines)}"
|
|
110
|
+
else
|
|
111
|
+
"line count differs (kapusta=#{k_lines.length}, fennel=#{f_lines.length})"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def check(name)
|
|
116
|
+
path = File.join(EXAMPLES, name)
|
|
117
|
+
return [:miss, "missing file: #{path}"] unless File.exist?(path)
|
|
118
|
+
|
|
119
|
+
fmt_reason = check_format(path)
|
|
120
|
+
return [:fail, fmt_reason] if fmt_reason
|
|
121
|
+
|
|
122
|
+
run_reason = check_run(path)
|
|
123
|
+
return [:fail, run_reason] if run_reason
|
|
124
|
+
|
|
125
|
+
[:ok, nil]
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
puts "Checking #{COMPATIBLE.size} compatible examples (kapfmt vs fnlfmt, kapusta vs fennel)...\n\n"
|
|
129
|
+
|
|
130
|
+
failures = []
|
|
131
|
+
COMPATIBLE.each do |name|
|
|
132
|
+
status, reason = check(name)
|
|
133
|
+
case status
|
|
134
|
+
when :ok
|
|
135
|
+
puts "OK #{name}"
|
|
136
|
+
when :miss
|
|
137
|
+
failures << [name, reason]
|
|
138
|
+
puts "MISS #{name}"
|
|
139
|
+
when :fail
|
|
140
|
+
failures << [name, reason]
|
|
141
|
+
puts "FAIL #{name}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
puts
|
|
146
|
+
if failures.empty?
|
|
147
|
+
puts "All #{COMPATIBLE.size} compatible examples produce matching output."
|
|
148
|
+
exit 0
|
|
149
|
+
else
|
|
150
|
+
puts "#{failures.size} of #{COMPATIBLE.size} examples failed:\n\n"
|
|
151
|
+
failures.each do |name, reason|
|
|
152
|
+
puts " * #{name}"
|
|
153
|
+
reason.each_line { |l| puts " #{l.rstrip}" }
|
|
154
|
+
puts
|
|
155
|
+
end
|
|
156
|
+
exit 1
|
|
157
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
(macro thrice-if [condition result]
|
|
2
|
+
(fn step [i]
|
|
3
|
+
(if (< 0 i)
|
|
4
|
+
`(if ,condition
|
|
5
|
+
(do
|
|
6
|
+
,result
|
|
7
|
+
,(step (- i 1))))))
|
|
8
|
+
|
|
9
|
+
(step 3))
|
|
10
|
+
|
|
11
|
+
(var counter 0)
|
|
12
|
+
|
|
13
|
+
(fn ready? [] (< counter 5))
|
|
14
|
+
|
|
15
|
+
(fn tick [] (set counter (+ counter 1)) (print "tick" counter))
|
|
16
|
+
|
|
17
|
+
(thrice-if (ready?) (tick))
|
|
18
|
+
(print "final" counter)
|
|
@@ -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/packet-router.kap
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
(fn inbox-line [user event]
|
|
2
2
|
(match event
|
|
3
3
|
[:score user points] (.. "score:" points)
|
|
4
|
-
[:profile user ?city] (if city (.. "city:" city) "city:nil")
|
|
4
|
+
[:profile user ?city] (if ?city (.. "city:" ?city) "city:nil")
|
|
5
5
|
_ "other"))
|
|
6
6
|
|
|
7
7
|
(fn score-delta [user event]
|
|
8
8
|
(case event
|
|
9
|
-
(where (or [:bonus (= user)
|
|
10
|
-
(> points 0)
|
|
11
|
-
(< points 10))
|
|
12
|
-
points
|
|
9
|
+
(where (or [:bonus (= user) p] [:score (= user) p]) (> p 0) (< p 10)) p
|
|
13
10
|
_ 0))
|
|
14
11
|
|
|
15
12
|
(fn packet-kind [packet]
|
|
@@ -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))
|
data/examples/tic-tac-toe.kap
CHANGED
|
@@ -6,13 +6,8 @@
|
|
|
6
6
|
[["O" _ _] ["O" _ _] ["O" _ _]] "O"
|
|
7
7
|
_ "draw"))
|
|
8
8
|
|
|
9
|
-
(each
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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"]]])]
|
|
9
|
+
(each [_ board (ipairs [[["X" "X" "X"] ["O" "" ""] ["" "O" ""]]
|
|
10
|
+
[["O" "X" "X"] ["O" "" "X"] ["O" "" ""]]
|
|
11
|
+
[["X" "O" ""] ["" "X" "O"] ["" "" "X"]]
|
|
12
|
+
[["X" "O" "X"] ["O" "X" "O"] ["O" "X" "O"]]])]
|
|
18
13
|
(print (winner board)))
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
(macro floor-div [a b]
|
|
2
|
+
`(: (/ ,a ,b) :floor))
|
|
3
|
+
|
|
4
|
+
(macro divide-out! [v d]
|
|
5
|
+
`(while (= 0 (% ,v ,d))
|
|
6
|
+
(set ,v (floor-div ,v ,d))))
|
|
7
|
+
|
|
8
|
+
(fn ugly? [n]
|
|
9
|
+
(var x n)
|
|
10
|
+
(if (<= n 0)
|
|
11
|
+
false
|
|
12
|
+
(do
|
|
13
|
+
(divide-out! x 2)
|
|
14
|
+
(divide-out! x 3)
|
|
15
|
+
(divide-out! x 5)
|
|
16
|
+
(= x 1))))
|
|
17
|
+
|
|
18
|
+
(print (ugly? 6))
|
|
19
|
+
(print (ugly? 1))
|
|
20
|
+
(print (ugly? 14))
|
|
21
|
+
(print (ugly? 0))
|
|
22
|
+
(print (ugly? 30))
|
data/lib/kapusta/ast.rb
CHANGED
|
@@ -9,6 +9,8 @@ module Kapusta
|
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
BlankLine = Class.new
|
|
13
|
+
|
|
12
14
|
class Sym
|
|
13
15
|
attr_reader :name
|
|
14
16
|
|
|
@@ -74,17 +76,25 @@ module Kapusta
|
|
|
74
76
|
|
|
75
77
|
class Vec
|
|
76
78
|
attr_reader :items
|
|
79
|
+
attr_accessor :multiline_source
|
|
77
80
|
|
|
78
81
|
def initialize(items)
|
|
79
82
|
@items = items
|
|
83
|
+
@multiline_source = false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def to_ary
|
|
87
|
+
@items
|
|
80
88
|
end
|
|
81
89
|
end
|
|
82
90
|
|
|
83
91
|
class HashLit
|
|
84
92
|
attr_reader :entries
|
|
93
|
+
attr_accessor :multiline_source
|
|
85
94
|
|
|
86
95
|
def initialize(entries)
|
|
87
96
|
@entries = entries
|
|
97
|
+
@multiline_source = false
|
|
88
98
|
end
|
|
89
99
|
|
|
90
100
|
def pairs
|
|
@@ -98,9 +108,11 @@ module Kapusta
|
|
|
98
108
|
|
|
99
109
|
class List
|
|
100
110
|
attr_reader :items
|
|
111
|
+
attr_accessor :multiline_source
|
|
101
112
|
|
|
102
113
|
def initialize(items)
|
|
103
114
|
@items = items
|
|
115
|
+
@multiline_source = false
|
|
104
116
|
end
|
|
105
117
|
|
|
106
118
|
def head
|
|
@@ -115,4 +127,34 @@ module Kapusta
|
|
|
115
127
|
@items.empty?
|
|
116
128
|
end
|
|
117
129
|
end
|
|
130
|
+
|
|
131
|
+
class AutoGensym < Sym
|
|
132
|
+
def inspect
|
|
133
|
+
"#<AutoGensym #{@name}#>"
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
class Quasiquote
|
|
138
|
+
attr_reader :form
|
|
139
|
+
|
|
140
|
+
def initialize(form)
|
|
141
|
+
@form = form
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
class Unquote
|
|
146
|
+
attr_reader :form
|
|
147
|
+
|
|
148
|
+
def initialize(form)
|
|
149
|
+
@form = form
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class UnquoteSplice
|
|
154
|
+
attr_reader :form
|
|
155
|
+
|
|
156
|
+
def initialize(form)
|
|
157
|
+
@form = form
|
|
158
|
+
end
|
|
159
|
+
end
|
|
118
160
|
end
|
|
@@ -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
|