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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62ac0b0f6d3ce6116b82110191b5c36783ed1689e2567076dbe68fc66e6ab3f7
4
- data.tar.gz: 6b705edf9d441c4f753e8420b0e8bc6a2327d91f6ab9041a603c3afe6546487b
3
+ metadata.gz: c136e4c7cc9c5dde0ced8c828ab80882900b278db86010986eb7271a7b45309e
4
+ data.tar.gz: 46ff912715738e99c454727224a5e19f6d1635fbfccb1ade3820a9a51a996d0d
5
5
  SHA512:
6
- metadata.gz: 243b1e47fd6a5684f7b4d2a9bf46e27d6b96883fb3cf71bf4f90edb4d48f0e5afa006c2da0191f5fc3cb116e37b506243b0f7030b90a6c6096629173e98074de
7
- data.tar.gz: 825b21b71cb0eacb24623beff28dc2fcb1b542cf50268d78ebcfba2b156bf634992975fb1f1a3a0e27f407c51e54c8eb96683d544f861de5f5cc51731fc819c3
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,4 @@
1
+ (let [name nil
2
+ greet (fn [value] (if value (.. "Hello, " value "!") "Hello, anonymous!"))]
3
+ (print (greet name))
4
+ (print (greet "Ada")))
@@ -0,0 +1,7 @@
1
+ (fn binary-to-decimal [bits]
2
+ (faccumulate [value 0 i 0 (- (length bits) 1)]
3
+ (+ (* value 2) (if (= (. bits i) "1") 1 0))))
4
+
5
+ (print (binary-to-decimal "1011"))
6
+ (print (binary-to-decimal "0"))
7
+ (print (binary-to-decimal "101010"))
@@ -0,0 +1,7 @@
1
+ (fn contains-duplicate? [nums]
2
+ (let [seen (collect [n nums] (values n true))]
3
+ (< (length seen) (length nums))))
4
+
5
+ (print (contains-duplicate? [1 2 3 1]))
6
+ (print (contains-duplicate? [1 2 3 4]))
7
+ (print (contains-duplicate? [1 1 1 3 3 4 3 2 4 2]))
@@ -0,0 +1,8 @@
1
+ (fn swap-kind [v]
2
+ (case v
3
+ (where (or [:pair x y] [:flipped y x])) (.. x ":" y)
4
+ _ "other"))
5
+
6
+ (print (swap-kind [:pair 1 2]))
7
+ (print (swap-kind [:flipped 1 2]))
8
+ (print (swap-kind [:nope 1 2]))
@@ -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)))
@@ -0,0 +1,14 @@
1
+ (fn loose [v]
2
+ (case v
3
+ _x _x
4
+ _ "fallback"))
5
+
6
+ (fn strict [v]
7
+ (case v
8
+ x x
9
+ _ "fallback"))
10
+
11
+ (print (loose 5))
12
+ (print (loose nil))
13
+ (print (strict 5))
14
+ (print (strict nil))
@@ -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.define(name_sym.name, ruby_name)
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
- <<~RUBY.chomp
35
- ->(*#{args_var}) do
36
- #{bindings_code}
37
- #{body_code}
38
- end
39
- RUBY
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 = temp(sanitize_local(name_sym.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
- method_env = env.child
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
- <<~RUBY.chomp
64
- define_singleton_method(#{ruby_name}) do |*#{args_var}|
65
- #{bindings_code}
66
- #{body_code}
67
- end
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
- <<~RUBY.chomp
72
- define_method(#{ruby_name}) do |*#{args_var}|
73
- #{bindings_code}
74
- #{body_code}
75
- end
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, allow_method_definitions: false)
95
- <<~RUBY.chomp
96
- (-> do
97
- #{binding_codes.join("\n")}
98
- #{body_code}
99
- end).call
100
- RUBY
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 = temp(sanitize_local(target.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
- fresh = temp(sanitize_local(target.name))
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
- runtime_call(:set_gvar, target.items[1].name.inspect, value_code)
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
- <<~RUBY.chomp
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
- <<~RUBY.chomp
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
- <<~RUBY.chomp
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
- <<~RUBY.chomp
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
- bindings = args[0].items
54
- ruby_name = temp(sanitize_local(bindings[0].name))
55
- loop_env = env.child
56
- loop_env.define(bindings[0].name, ruby_name)
57
- start_code = emit_expr(bindings[1], env, current_scope)
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.define(acc_name.name, acc_var)
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
- "#{acc_var} = begin\n#{indent(body)}\nend"
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.define(acc_name.name, acc_var)
126
- loop_env.define(loop_name.name, loop_var)
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
- finish_var = temp('finish')
132
- step_var = temp('step')
133
- cmp_var = temp('cmp')
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
- #{loop_var} = #{start_code}
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