kapusta 0.14.0 → 0.15.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 +2 -2
- data/examples/app/args.kap +3 -3
- data/examples/digit-tools.kap +24 -0
- data/examples/excel-column.kap +26 -0
- data/examples/logger-rate-limiter.kap +26 -0
- data/examples/mruby-runtime-examples.txt +2 -0
- data/lib/kapusta/compiler/emitter/interop.rb +4 -0
- data/lib/kapusta/compiler/emitter/support.rb +7 -0
- data/lib/kapusta/compiler/language.rb +13 -2
- data/lib/kapusta/formatter.rb +10 -1
- data/lib/kapusta/version.rb +1 -1
- data/spec/examples_spec.rb +32 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 81b3b1124689c8b7a89734ab0b8a5b584ba8ef6edcffab89fefcd4e86e6a6e2a
|
|
4
|
+
data.tar.gz: f3e83405f8de3298e0bfde66dcc8afd67f38e2fa2733b34085ef00579635c247
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f2c058a5c9f86be04f3d29ade387ede8ebe818365078a2a31f161a4044eb6b6a0dfd1e65453e681e79898e57dfd2e830281572ef33cc9ca57668ae088e901eb2
|
|
7
|
+
data.tar.gz: fc20f32661cf1979ae93aba6adf7be488e96007d151f8997704f37e6d000fa9affeca6383b8fc749471fa823eb24c4fb2dcd3e6c39fa475cfdf25a7fafa4207d
|
data/README.md
CHANGED
|
@@ -128,8 +128,8 @@ require_relative "config"
|
|
|
128
128
|
|
|
129
129
|
For larger programs, organize code with Kapusta's Ruby host forms:
|
|
130
130
|
|
|
131
|
-
1. Use `module
|
|
132
|
-
2. Use `class` + `fn` for state or dependencies.
|
|
131
|
+
1. Use `module Name <<` + `fn` for stateless functions ([`examples/app/args.kap`](https://github.com/evmorov/kapusta/tree/main/examples/app/args.kap)).
|
|
132
|
+
2. Use `class` + `fn` for state or dependencies ([`examples/use_bank_account.rb`](https://github.com/evmorov/kapusta/tree/main/examples/use_bank_account.rb)).
|
|
133
133
|
3. Avoid returning hashes of functions as the main app structure.
|
|
134
134
|
|
|
135
135
|
## Comparison with Fennel
|
data/examples/app/args.kap
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
(module Digits <<
|
|
2
|
+
(fn sum [n]
|
|
3
|
+
(var total 0)
|
|
4
|
+
(var x n)
|
|
5
|
+
(while (> x 0)
|
|
6
|
+
(set total (+ total (% x 10)))
|
|
7
|
+
(set x (/ x 10)))
|
|
8
|
+
total)
|
|
9
|
+
|
|
10
|
+
(fn reverse [n]
|
|
11
|
+
(var result 0)
|
|
12
|
+
(var x n)
|
|
13
|
+
(while (> x 0)
|
|
14
|
+
(set result (+ (* result 10) (% x 10)))
|
|
15
|
+
(set x (/ x 10)))
|
|
16
|
+
result)
|
|
17
|
+
|
|
18
|
+
(fn palindrome? [n]
|
|
19
|
+
(= n (Digits.reverse n))))
|
|
20
|
+
|
|
21
|
+
(print (Digits.sum 12345))
|
|
22
|
+
(print (Digits.reverse 1230))
|
|
23
|
+
(print (Digits.palindrome? 121))
|
|
24
|
+
(print (Digits.palindrome? 123))
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
(module Excel <<)
|
|
2
|
+
|
|
3
|
+
(fn to-number [title]
|
|
4
|
+
(var result 0)
|
|
5
|
+
(each [ch title.chars]
|
|
6
|
+
(set result (+ (* result 26) (- ch.ord 64))))
|
|
7
|
+
result)
|
|
8
|
+
|
|
9
|
+
(fn to-title [number]
|
|
10
|
+
(var n number)
|
|
11
|
+
(var chars [])
|
|
12
|
+
(while (> n 0)
|
|
13
|
+
(set n (- n 1))
|
|
14
|
+
(let [code (+ 65 (% n 26))]
|
|
15
|
+
(chars.unshift code.chr))
|
|
16
|
+
(set n (/ n 26)))
|
|
17
|
+
(chars.join ""))
|
|
18
|
+
|
|
19
|
+
(end)
|
|
20
|
+
|
|
21
|
+
(print (Excel.to-number "A"))
|
|
22
|
+
(print (Excel.to-number "AB"))
|
|
23
|
+
(print (Excel.to-number "ZY"))
|
|
24
|
+
(print (Excel.to-title 1))
|
|
25
|
+
(print (Excel.to-title 28))
|
|
26
|
+
(print (Excel.to-title 701))
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
(require "singleton")
|
|
2
|
+
|
|
3
|
+
(class RateLimiter)
|
|
4
|
+
(include Singleton)
|
|
5
|
+
|
|
6
|
+
(fn initialize []
|
|
7
|
+
(set (ivar last) {}))
|
|
8
|
+
|
|
9
|
+
(fn should-print? [timestamp message]
|
|
10
|
+
(let [last (ivar last)]
|
|
11
|
+
(if (or (not (last.key? message)) (>= timestamp (: last message)))
|
|
12
|
+
(do
|
|
13
|
+
(tset last message (+ timestamp 10))
|
|
14
|
+
true)
|
|
15
|
+
false)))
|
|
16
|
+
|
|
17
|
+
(end)
|
|
18
|
+
|
|
19
|
+
(let [logger (RateLimiter.instance)]
|
|
20
|
+
(print (logger.should-print? 1 "foo"))
|
|
21
|
+
(print (logger.should-print? 2 "bar"))
|
|
22
|
+
(print (logger.should-print? 3 "foo"))
|
|
23
|
+
(print (logger.should-print? 8 "bar"))
|
|
24
|
+
(print (logger.should-print? 10 "foo"))
|
|
25
|
+
(print (logger.should-print? 11 "foo"))
|
|
26
|
+
(print (= logger (RateLimiter.instance))))
|
|
@@ -163,6 +163,7 @@ module Kapusta
|
|
|
163
163
|
body = with_class_body do
|
|
164
164
|
emit_sequence(parsed.body, env.child, :module, allow_method_definitions: true, result: false).first
|
|
165
165
|
end
|
|
166
|
+
body = wrap_singleton(body) if parsed.singleton
|
|
166
167
|
emit_module_wrapper(parsed.name, body)
|
|
167
168
|
end
|
|
168
169
|
|
|
@@ -432,6 +433,9 @@ module Kapusta
|
|
|
432
433
|
def emit_self_call(name, args, env, current_scope)
|
|
433
434
|
positional, kwargs, block_form = split_call_args(args, env, current_scope)
|
|
434
435
|
snake = Kapusta.kebab_to_snake(name)
|
|
436
|
+
if Language.class_body_declaration?(snake) && kwargs.nil? && block_form.nil?
|
|
437
|
+
return positional.empty? ? snake : "#{snake} #{positional.join(', ')}"
|
|
438
|
+
end
|
|
435
439
|
if direct_method_name?(snake)
|
|
436
440
|
return emit_direct_self_call(snake, positional, kwargs, block_form, env, current_scope)
|
|
437
441
|
end
|
|
@@ -95,6 +95,7 @@ module Kapusta
|
|
|
95
95
|
body, next_i = with_class_body do
|
|
96
96
|
emit_form_run(forms, body_start, env.child, :module, header_form: form)
|
|
97
97
|
end
|
|
98
|
+
body = wrap_singleton(body) if parsed.singleton
|
|
98
99
|
[emit_direct_module_header(parsed.name, body) || emit_module_wrapper(parsed.name, body), next_i]
|
|
99
100
|
end
|
|
100
101
|
else
|
|
@@ -157,6 +158,7 @@ module Kapusta
|
|
|
157
158
|
|
|
158
159
|
name = form.head.name
|
|
159
160
|
return if special_form?(name)
|
|
161
|
+
return if Language.class_body_declaration?(Kapusta.kebab_to_snake(name))
|
|
160
162
|
return if form.head.dotted?
|
|
161
163
|
return if env.lookup_if_defined(name)
|
|
162
164
|
|
|
@@ -175,6 +177,7 @@ module Kapusta
|
|
|
175
177
|
emit_sequence(parsed.body, env.child, :module, allow_method_definitions: true,
|
|
176
178
|
result: false).first
|
|
177
179
|
end
|
|
180
|
+
body = wrap_singleton(body) if parsed.singleton
|
|
178
181
|
emit_direct_module_header(parsed.name, body) || emit_module_wrapper(parsed.name, body)
|
|
179
182
|
else
|
|
180
183
|
parsed = Language.parse_class_args(args)
|
|
@@ -315,6 +318,10 @@ module Kapusta
|
|
|
315
318
|
text.lines.map { |line| line.strip.empty? ? line : "#{prefix}#{line}" }.join
|
|
316
319
|
end
|
|
317
320
|
|
|
321
|
+
def wrap_singleton(body)
|
|
322
|
+
['class << self', indent(body), 'end'].join("\n")
|
|
323
|
+
end
|
|
324
|
+
|
|
318
325
|
def temp(prefix)
|
|
319
326
|
@temp_index += 1
|
|
320
327
|
"kap_#{prefix}_#{@temp_index}"
|
|
@@ -8,7 +8,7 @@ module Kapusta
|
|
|
8
8
|
|
|
9
9
|
def anonymous? = name.nil?
|
|
10
10
|
end
|
|
11
|
-
ModuleForm = Struct.new(:name, :body, :prefix_length, keyword_init: true)
|
|
11
|
+
ModuleForm = Struct.new(:name, :body, :singleton, :prefix_length, keyword_init: true)
|
|
12
12
|
ClassForm = Struct.new(:name, :supers, :body, :prefix_length, keyword_init: true)
|
|
13
13
|
TryForm = Struct.new(:body, :clauses, keyword_init: true)
|
|
14
14
|
CatchClause = Struct.new(:klass, :bind_sym, :body, keyword_init: true)
|
|
@@ -165,6 +165,12 @@ module Kapusta
|
|
|
165
165
|
quasi-sym quasi-list quasi-list-tail quasi-vec quasi-vec-tail quasi-hash quasi-gensym
|
|
166
166
|
].freeze
|
|
167
167
|
SPECIAL_FORMS = (CORE_SPECIAL_FORMS + LuaCompat::SPECIAL_FORMS).freeze
|
|
168
|
+
CLASS_BODY_DECLARATIONS = %w[
|
|
169
|
+
include extend prepend
|
|
170
|
+
private public protected
|
|
171
|
+
module_function
|
|
172
|
+
attr_accessor attr_reader attr_writer
|
|
173
|
+
].freeze
|
|
168
174
|
|
|
169
175
|
module_function
|
|
170
176
|
|
|
@@ -207,6 +213,8 @@ module Kapusta
|
|
|
207
213
|
|
|
208
214
|
def special_form?(name) = SPECIAL_FORMS.include?(name)
|
|
209
215
|
|
|
216
|
+
def class_body_declaration?(name) = CLASS_BODY_DECLARATIONS.include?(name)
|
|
217
|
+
|
|
210
218
|
def function_head?(name) = FUNCTION_HEADS.include?(name)
|
|
211
219
|
|
|
212
220
|
def function_definition_head?(name) = FUNCTION_DEFINITION_HEADS.include?(name)
|
|
@@ -282,7 +290,10 @@ module Kapusta
|
|
|
282
290
|
end
|
|
283
291
|
|
|
284
292
|
def parse_module_args(args)
|
|
285
|
-
|
|
293
|
+
rest = args[1..] || []
|
|
294
|
+
singleton = rest[0].is_a?(Sym) && rest[0].name == '<<'
|
|
295
|
+
rest = rest.drop(1) if singleton
|
|
296
|
+
ModuleForm.new(name: args[0], body: rest, singleton:, prefix_length: singleton ? 2 : 1)
|
|
286
297
|
end
|
|
287
298
|
|
|
288
299
|
def parse_class_args(args)
|
data/lib/kapusta/formatter.rb
CHANGED
|
@@ -13,7 +13,7 @@ module Kapusta
|
|
|
13
13
|
STDIN_PATH = '-'
|
|
14
14
|
BODY_ONLY_HEADS = %w[do finally].freeze
|
|
15
15
|
SINGLE_PREFIX_BODY_HEADS = %w[
|
|
16
|
-
while when unless for each icollect collect fcollect accumulate faccumulate
|
|
16
|
+
while when unless for each icollect collect fcollect accumulate faccumulate
|
|
17
17
|
].freeze
|
|
18
18
|
CASE_HEADS = %w[case match].freeze
|
|
19
19
|
private_constant :BODY_ONLY_HEADS, :SINGLE_PREFIX_BODY_HEADS, :CASE_HEADS
|
|
@@ -188,6 +188,7 @@ module Kapusta
|
|
|
188
188
|
when 'try' then render_try(list, indent)
|
|
189
189
|
when *SINGLE_PREFIX_BODY_HEADS then render_single_prefix_body_form(name, raw_args, indent)
|
|
190
190
|
when 'class' then render_class(list, indent)
|
|
191
|
+
when 'module' then render_module(list, indent)
|
|
191
192
|
when 'catch' then render_catch(list, indent)
|
|
192
193
|
when 'if' then render_if(list, indent)
|
|
193
194
|
when *CASE_HEADS then render_case_or_match(name, list, raw_args, indent)
|
|
@@ -234,6 +235,14 @@ module Kapusta
|
|
|
234
235
|
render_prefix_body_form('class', raw_prefix, raw_body, indent)
|
|
235
236
|
end
|
|
236
237
|
|
|
238
|
+
def render_module(list, indent)
|
|
239
|
+
args = list_rest(list)
|
|
240
|
+
raw_args = list_raw_rest(list)
|
|
241
|
+
prefix_length = Compiler::Language.parse_module_args(args).prefix_length
|
|
242
|
+
raw_prefix, raw_body = split_raw_items(raw_args, prefix_length)
|
|
243
|
+
render_prefix_body_form('module', raw_prefix, raw_body, indent)
|
|
244
|
+
end
|
|
245
|
+
|
|
237
246
|
def render_try(list, indent)
|
|
238
247
|
args = list_rest(list)
|
|
239
248
|
return render_sequential_head_form('try', list_raw_rest(list), indent) if contains_comments?(list_raw_rest(list))
|
data/lib/kapusta/version.rb
CHANGED
data/spec/examples_spec.rb
CHANGED
|
@@ -779,6 +779,38 @@ RSpec.describe 'examples' do
|
|
|
779
779
|
"alice"
|
|
780
780
|
OUT
|
|
781
781
|
end
|
|
782
|
+
|
|
783
|
+
it 'excel-column.kap' do
|
|
784
|
+
expect(run_example('excel-column.kap')).to eq(<<~OUT)
|
|
785
|
+
1
|
|
786
|
+
28
|
|
787
|
+
701
|
|
788
|
+
"A"
|
|
789
|
+
"AB"
|
|
790
|
+
"ZY"
|
|
791
|
+
OUT
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
it 'logger-rate-limiter.kap' do
|
|
795
|
+
expect(run_example('logger-rate-limiter.kap')).to eq(<<~OUT)
|
|
796
|
+
true
|
|
797
|
+
true
|
|
798
|
+
false
|
|
799
|
+
false
|
|
800
|
+
false
|
|
801
|
+
true
|
|
802
|
+
true
|
|
803
|
+
OUT
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
it 'digit-tools.kap' do
|
|
807
|
+
expect(run_example('digit-tools.kap')).to eq(<<~OUT)
|
|
808
|
+
15
|
|
809
|
+
321
|
|
810
|
+
true
|
|
811
|
+
false
|
|
812
|
+
OUT
|
|
813
|
+
end
|
|
782
814
|
end
|
|
783
815
|
|
|
784
816
|
RSpec.describe 'mruby runtime examples' do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kapusta
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.15.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evgenii Morozov
|
|
@@ -53,12 +53,14 @@ files:
|
|
|
53
53
|
- examples/count-items-matching-rule.kap
|
|
54
54
|
- examples/counter.kap
|
|
55
55
|
- examples/destructure.kap
|
|
56
|
+
- examples/digit-tools.kap
|
|
56
57
|
- examples/divisibility-stats.kap
|
|
57
58
|
- examples/doto-hygiene.kap
|
|
58
59
|
- examples/doto.kap
|
|
59
60
|
- examples/egg-count.kap
|
|
60
61
|
- examples/equal-sums.kap
|
|
61
62
|
- examples/even-squares.kap
|
|
63
|
+
- examples/excel-column.kap
|
|
62
64
|
- examples/exceptions.kap
|
|
63
65
|
- examples/factorial.kap
|
|
64
66
|
- examples/falling-drops.kap
|
|
@@ -77,6 +79,7 @@ files:
|
|
|
77
79
|
- examples/leap-year.kap
|
|
78
80
|
- examples/left-right-difference.kap
|
|
79
81
|
- examples/length-of-last-word.kap
|
|
82
|
+
- examples/logger-rate-limiter.kap
|
|
80
83
|
- examples/macros-dbg.kap
|
|
81
84
|
- examples/macros-import-helpers.kap
|
|
82
85
|
- examples/macros-import-whole.kap
|