kapusta 0.1.3 → 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 +4 -4
- data/README.md +1 -0
- data/examples/contains-duplicate.kap +7 -0
- data/lib/kapusta/compiler/emitter/bindings.rb +87 -39
- data/lib/kapusta/compiler/emitter/collections.rb +47 -49
- data/lib/kapusta/compiler/emitter/control_flow.rb +59 -18
- data/lib/kapusta/compiler/emitter/expressions.rb +1 -1
- data/lib/kapusta/compiler/emitter/interop.rb +105 -22
- data/lib/kapusta/compiler/emitter/patterns.rb +3 -6
- data/lib/kapusta/compiler/emitter/support.rb +92 -29
- data/lib/kapusta/compiler/runtime.rb +49 -49
- data/lib/kapusta/compiler.rb +2 -1
- data/lib/kapusta/env.rb +8 -0
- data/lib/kapusta/error.rb +5 -0
- data/lib/kapusta/formatter.rb +1 -10
- data/lib/kapusta/reader.rb +13 -9
- data/lib/kapusta/version.rb +1 -1
- data/lib/kapusta.rb +1 -0
- data/spec/cli_spec.rb +12 -21
- data/spec/examples_spec.rb +5 -1
- metadata +3 -1
|
@@ -20,12 +20,12 @@ module Kapusta
|
|
|
20
20
|
|
|
21
21
|
HELPER_SOURCES = {
|
|
22
22
|
kebab_to_snake: <<~RUBY.chomp,
|
|
23
|
-
def
|
|
23
|
+
def kap_kebab_to_snake(name)
|
|
24
24
|
name.tr('-', '_')
|
|
25
25
|
end
|
|
26
26
|
RUBY
|
|
27
27
|
call: <<~'RUBY'.chomp,
|
|
28
|
-
def
|
|
28
|
+
def kap_call(callee, positional, kwargs = nil, block = nil)
|
|
29
29
|
raise "not callable: #{callee.inspect}" unless callee.respond_to?(:call)
|
|
30
30
|
|
|
31
31
|
if block
|
|
@@ -36,7 +36,7 @@ module Kapusta
|
|
|
36
36
|
end
|
|
37
37
|
RUBY
|
|
38
38
|
send_call: <<~RUBY.chomp,
|
|
39
|
-
def
|
|
39
|
+
def kap_send_call(receiver, method_name, positional, kwargs = nil, block = nil)
|
|
40
40
|
if block
|
|
41
41
|
if kwargs
|
|
42
42
|
receiver.public_send(method_name, *positional, **kwargs, &block)
|
|
@@ -51,7 +51,7 @@ module Kapusta
|
|
|
51
51
|
end
|
|
52
52
|
RUBY
|
|
53
53
|
invoke_self: <<~RUBY.chomp,
|
|
54
|
-
def
|
|
54
|
+
def kap_invoke_self(receiver, method_name, positional, kwargs = nil, block = nil)
|
|
55
55
|
if block
|
|
56
56
|
if kwargs
|
|
57
57
|
receiver.send(method_name, *positional, **kwargs, &block)
|
|
@@ -64,7 +64,7 @@ module Kapusta
|
|
|
64
64
|
end
|
|
65
65
|
RUBY
|
|
66
66
|
stringify: <<~'RUBY'.chomp,
|
|
67
|
-
def
|
|
67
|
+
def kap_stringify(value)
|
|
68
68
|
render = nil
|
|
69
69
|
render = lambda do |item|
|
|
70
70
|
case item
|
|
@@ -91,23 +91,23 @@ module Kapusta
|
|
|
91
91
|
end
|
|
92
92
|
RUBY
|
|
93
93
|
print_values: <<~'RUBY'.chomp,
|
|
94
|
-
def
|
|
95
|
-
$stdout.puts(values.map { |value|
|
|
94
|
+
def kap_print_values(*values)
|
|
95
|
+
$stdout.puts(values.map { |value| kap_stringify(value) }.join("\t"))
|
|
96
96
|
nil
|
|
97
97
|
end
|
|
98
98
|
RUBY
|
|
99
99
|
concat: <<~RUBY.chomp,
|
|
100
|
-
def
|
|
101
|
-
values.map { |value|
|
|
100
|
+
def kap_concat(values)
|
|
101
|
+
values.map { |value| kap_stringify(value) }.join
|
|
102
102
|
end
|
|
103
103
|
RUBY
|
|
104
104
|
get_path: <<~RUBY.chomp,
|
|
105
|
-
def
|
|
105
|
+
def kap_get_path(obj, keys)
|
|
106
106
|
keys.reduce(obj) { |acc, key| acc[key] }
|
|
107
107
|
end
|
|
108
108
|
RUBY
|
|
109
109
|
qget_path: <<~RUBY.chomp,
|
|
110
|
-
def
|
|
110
|
+
def kap_qget_path(obj, keys)
|
|
111
111
|
keys.each do |key|
|
|
112
112
|
return nil if obj.nil?
|
|
113
113
|
|
|
@@ -117,64 +117,64 @@ module Kapusta
|
|
|
117
117
|
end
|
|
118
118
|
RUBY
|
|
119
119
|
set_path: <<~RUBY.chomp,
|
|
120
|
-
def
|
|
120
|
+
def kap_set_path(obj, keys, value)
|
|
121
121
|
target = obj
|
|
122
122
|
keys[0...-1].each { |key| target = target[key] }
|
|
123
123
|
target[keys.last] = value
|
|
124
124
|
end
|
|
125
125
|
RUBY
|
|
126
126
|
method_path_value: <<~RUBY.chomp,
|
|
127
|
-
def
|
|
128
|
-
segments.reduce(base) { |obj, segment| obj.public_send(
|
|
127
|
+
def kap_method_path_value(base, segments)
|
|
128
|
+
segments.reduce(base) { |obj, segment| obj.public_send(kap_kebab_to_snake(segment).to_sym) }
|
|
129
129
|
end
|
|
130
130
|
RUBY
|
|
131
131
|
set_method_path: <<~'RUBY'.chomp,
|
|
132
|
-
def
|
|
132
|
+
def kap_set_method_path(base, segments, value)
|
|
133
133
|
target = base
|
|
134
134
|
segments[0...-1].each do |segment|
|
|
135
|
-
target = target.public_send(
|
|
135
|
+
target = target.public_send(kap_kebab_to_snake(segment).to_sym)
|
|
136
136
|
end
|
|
137
|
-
setter = "#{
|
|
137
|
+
setter = "#{kap_kebab_to_snake(segments.last)}="
|
|
138
138
|
target.public_send(setter.to_sym, value)
|
|
139
139
|
end
|
|
140
140
|
RUBY
|
|
141
141
|
current_class_scope: <<~RUBY.chomp,
|
|
142
|
-
def
|
|
142
|
+
def kap_current_class_scope(receiver)
|
|
143
143
|
receiver.is_a?(Module) ? receiver : receiver.class
|
|
144
144
|
end
|
|
145
145
|
RUBY
|
|
146
146
|
get_ivar: <<~'RUBY'.chomp,
|
|
147
|
-
def
|
|
148
|
-
receiver.instance_variable_get("@#{
|
|
147
|
+
def kap_get_ivar(receiver, name)
|
|
148
|
+
receiver.instance_variable_get("@#{kap_kebab_to_snake(name)}")
|
|
149
149
|
end
|
|
150
150
|
RUBY
|
|
151
151
|
set_ivar: <<~'RUBY'.chomp,
|
|
152
|
-
def
|
|
153
|
-
receiver.instance_variable_set("@#{
|
|
152
|
+
def kap_set_ivar(receiver, name, value)
|
|
153
|
+
receiver.instance_variable_set("@#{kap_kebab_to_snake(name)}", value)
|
|
154
154
|
end
|
|
155
155
|
RUBY
|
|
156
156
|
get_cvar: <<~'RUBY'.chomp,
|
|
157
|
-
def
|
|
158
|
-
|
|
157
|
+
def kap_get_cvar(receiver, name)
|
|
158
|
+
kap_current_class_scope(receiver).class_variable_get("@@#{kap_kebab_to_snake(name)}")
|
|
159
159
|
end
|
|
160
160
|
RUBY
|
|
161
161
|
set_cvar: <<~'RUBY'.chomp,
|
|
162
|
-
def
|
|
163
|
-
|
|
162
|
+
def kap_set_cvar(receiver, name, value)
|
|
163
|
+
kap_current_class_scope(receiver).class_variable_set("@@#{kap_kebab_to_snake(name)}", value)
|
|
164
164
|
end
|
|
165
165
|
RUBY
|
|
166
166
|
get_gvar: <<~'RUBY'.chomp,
|
|
167
|
-
def
|
|
168
|
-
Kernel.eval("$#{
|
|
167
|
+
def kap_get_gvar(name)
|
|
168
|
+
Kernel.eval("$#{kap_kebab_to_snake(name)}", binding, __FILE__, __LINE__)
|
|
169
169
|
end
|
|
170
170
|
RUBY
|
|
171
171
|
set_gvar: <<~'RUBY'.chomp,
|
|
172
|
-
def
|
|
173
|
-
Kernel.eval("$#{
|
|
172
|
+
def kap_set_gvar(name, value)
|
|
173
|
+
Kernel.eval("$#{kap_kebab_to_snake(name)} = value", binding, __FILE__, __LINE__)
|
|
174
174
|
end
|
|
175
175
|
RUBY
|
|
176
176
|
ensure_module: <<~RUBY.chomp,
|
|
177
|
-
def
|
|
177
|
+
def kap_ensure_module(holder, path)
|
|
178
178
|
segments = path.split('.')
|
|
179
179
|
last = segments.pop
|
|
180
180
|
scope = holder.is_a?(Module) ? holder : Object
|
|
@@ -198,7 +198,7 @@ module Kapusta
|
|
|
198
198
|
end
|
|
199
199
|
RUBY
|
|
200
200
|
ensure_class: <<~RUBY.chomp,
|
|
201
|
-
def
|
|
201
|
+
def kap_ensure_class(holder, path, super_class)
|
|
202
202
|
segments = path.split('.')
|
|
203
203
|
last = segments.pop
|
|
204
204
|
scope = holder.is_a?(Module) ? holder : Object
|
|
@@ -222,14 +222,14 @@ module Kapusta
|
|
|
222
222
|
end
|
|
223
223
|
RUBY
|
|
224
224
|
destructure: <<~RUBY.chomp,
|
|
225
|
-
def
|
|
225
|
+
def kap_destructure(pattern, value)
|
|
226
226
|
bindings = {}
|
|
227
|
-
|
|
227
|
+
kap_destructure_into(pattern, value, bindings)
|
|
228
228
|
bindings
|
|
229
229
|
end
|
|
230
230
|
RUBY
|
|
231
231
|
destructure_into: <<~'RUBY'.chomp,
|
|
232
|
-
def
|
|
232
|
+
def kap_destructure_into(pattern, value, bindings)
|
|
233
233
|
case pattern[0]
|
|
234
234
|
when :sym
|
|
235
235
|
name = pattern[1]
|
|
@@ -241,18 +241,18 @@ module Kapusta
|
|
|
241
241
|
before = items[0...rest_idx]
|
|
242
242
|
rest_pattern = items[rest_idx][1]
|
|
243
243
|
before.each_with_index do |item, i|
|
|
244
|
-
|
|
244
|
+
kap_destructure_into(item, value ? value[i] : nil, bindings)
|
|
245
245
|
end
|
|
246
246
|
rest_value = value ? (value[rest_idx..] || []) : []
|
|
247
|
-
|
|
247
|
+
kap_destructure_into(rest_pattern, rest_value, bindings)
|
|
248
248
|
else
|
|
249
249
|
items.each_with_index do |item, i|
|
|
250
|
-
|
|
250
|
+
kap_destructure_into(item, value ? value[i] : nil, bindings)
|
|
251
251
|
end
|
|
252
252
|
end
|
|
253
253
|
when :hash
|
|
254
254
|
pattern[1].each do |key, subpattern|
|
|
255
|
-
|
|
255
|
+
kap_destructure_into(subpattern, value ? value[key] : nil, bindings)
|
|
256
256
|
end
|
|
257
257
|
when :ignore
|
|
258
258
|
nil
|
|
@@ -262,13 +262,13 @@ module Kapusta
|
|
|
262
262
|
end
|
|
263
263
|
RUBY
|
|
264
264
|
match_pattern: <<~RUBY.chomp,
|
|
265
|
-
def
|
|
265
|
+
def kap_match_pattern(pattern, value)
|
|
266
266
|
bindings = {}
|
|
267
|
-
[
|
|
267
|
+
[kap_match_pattern_into(pattern, value, bindings), bindings]
|
|
268
268
|
end
|
|
269
269
|
RUBY
|
|
270
270
|
match_pattern_into: <<~'RUBY'.chomp
|
|
271
|
-
def
|
|
271
|
+
def kap_match_pattern_into(pattern, value, bindings)
|
|
272
272
|
case pattern[0]
|
|
273
273
|
when :bind
|
|
274
274
|
name = pattern[1]
|
|
@@ -293,14 +293,14 @@ module Kapusta
|
|
|
293
293
|
return false if array.length < before.length
|
|
294
294
|
|
|
295
295
|
before.each_with_index do |item, i|
|
|
296
|
-
return false unless
|
|
296
|
+
return false unless kap_match_pattern_into(item, array[i], bindings)
|
|
297
297
|
end
|
|
298
|
-
|
|
298
|
+
kap_match_pattern_into(rest_pattern, array[rest_idx..], bindings)
|
|
299
299
|
else
|
|
300
300
|
return false unless array.length >= items.length
|
|
301
301
|
|
|
302
302
|
items.each_with_index do |item, i|
|
|
303
|
-
return false unless
|
|
303
|
+
return false unless kap_match_pattern_into(item, array[i], bindings)
|
|
304
304
|
end
|
|
305
305
|
true
|
|
306
306
|
end
|
|
@@ -309,7 +309,7 @@ module Kapusta
|
|
|
309
309
|
|
|
310
310
|
pattern[1].each do |key, subpattern|
|
|
311
311
|
return false unless value.key?(key)
|
|
312
|
-
return false unless
|
|
312
|
+
return false unless kap_match_pattern_into(subpattern, value[key], bindings)
|
|
313
313
|
end
|
|
314
314
|
true
|
|
315
315
|
when :lit
|
|
@@ -319,7 +319,7 @@ module Kapusta
|
|
|
319
319
|
when :or
|
|
320
320
|
pattern[1].any? do |option|
|
|
321
321
|
option_bindings = bindings.dup
|
|
322
|
-
next false unless
|
|
322
|
+
next false unless kap_match_pattern_into(option, value, option_bindings)
|
|
323
323
|
|
|
324
324
|
bindings.replace(option_bindings)
|
|
325
325
|
true
|
|
@@ -334,7 +334,7 @@ module Kapusta
|
|
|
334
334
|
module_function
|
|
335
335
|
|
|
336
336
|
def helper_name(name)
|
|
337
|
-
"
|
|
337
|
+
"kap_#{name}"
|
|
338
338
|
end
|
|
339
339
|
|
|
340
340
|
def helper_source(helpers)
|
|
@@ -366,7 +366,7 @@ module Kapusta
|
|
|
366
366
|
helper_methods = []
|
|
367
367
|
|
|
368
368
|
HELPER_SOURCES.each_key do |name|
|
|
369
|
-
helper_method = :"
|
|
369
|
+
helper_method = :"kap_#{name}"
|
|
370
370
|
define_singleton_method(name, instance_method(helper_method))
|
|
371
371
|
helper_methods << helper_method
|
|
372
372
|
end
|
data/lib/kapusta/compiler.rb
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'error'
|
|
3
4
|
require_relative 'compiler/runtime'
|
|
4
5
|
require_relative 'compiler/normalizer'
|
|
5
6
|
require_relative 'compiler/emitter'
|
|
6
7
|
|
|
7
8
|
module Kapusta
|
|
8
9
|
module Compiler
|
|
9
|
-
class Error <
|
|
10
|
+
class Error < Kapusta::Error; end
|
|
10
11
|
SPECIAL_FORMS = %w[
|
|
11
12
|
fn lambda λ let local var set if when unless case match
|
|
12
13
|
while for each do values
|
data/lib/kapusta/env.rb
CHANGED
|
@@ -25,6 +25,14 @@ module Kapusta
|
|
|
25
25
|
@vars.key?(name) || @parent&.defined?(name)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
def ruby_name_defined?(name)
|
|
29
|
+
@vars.value?(name) || @parent&.ruby_name_defined?(name)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def local_ruby_name_defined?(name)
|
|
33
|
+
@vars.value?(name)
|
|
34
|
+
end
|
|
35
|
+
|
|
28
36
|
def set_existing!(name, value)
|
|
29
37
|
if @vars.key?(name)
|
|
30
38
|
@vars[name] = value
|
data/lib/kapusta/formatter.rb
CHANGED
|
@@ -675,15 +675,6 @@ module Kapusta
|
|
|
675
675
|
end
|
|
676
676
|
end
|
|
677
677
|
|
|
678
|
-
def fn_body(form)
|
|
679
|
-
args = list_rest(form)
|
|
680
|
-
if args[0].is_a?(Sym) && args[1].is_a?(Vec)
|
|
681
|
-
args.drop(2)
|
|
682
|
-
else
|
|
683
|
-
args.drop(1)
|
|
684
|
-
end
|
|
685
|
-
end
|
|
686
|
-
|
|
687
678
|
def consecutive_requires?(previous, current)
|
|
688
679
|
require_form?(previous) && require_form?(current)
|
|
689
680
|
end
|
|
@@ -834,6 +825,6 @@ module Kapusta
|
|
|
834
825
|
puts 'Formats Kapusta source using the built-in Kapusta reader and pretty-printer.'
|
|
835
826
|
end
|
|
836
827
|
|
|
837
|
-
class Error <
|
|
828
|
+
class Error < Kapusta::Error; end
|
|
838
829
|
end
|
|
839
830
|
end
|
data/lib/kapusta/reader.rb
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'error'
|
|
4
|
+
|
|
3
5
|
module Kapusta
|
|
4
6
|
class Reader
|
|
7
|
+
class Error < Kapusta::Error; end
|
|
8
|
+
|
|
5
9
|
WHITESPACE = [' ', "\t", "\n", "\r", "\f", "\v", ','].freeze
|
|
6
10
|
DELIMS = ['(', ')', '[', ']', '{', '}', '"', ';'].freeze
|
|
7
11
|
|
|
@@ -61,7 +65,7 @@ module Kapusta
|
|
|
61
65
|
|
|
62
66
|
def read_next_item
|
|
63
67
|
skip_ws
|
|
64
|
-
raise 'unexpected eof' if eof?
|
|
68
|
+
raise Error, 'unexpected eof' if eof?
|
|
65
69
|
|
|
66
70
|
return read_comment if @preserve_comments && peek == ';'
|
|
67
71
|
|
|
@@ -70,7 +74,7 @@ module Kapusta
|
|
|
70
74
|
|
|
71
75
|
def read_form
|
|
72
76
|
skip_ws
|
|
73
|
-
raise 'unexpected eof' if eof?
|
|
77
|
+
raise Error, 'unexpected eof' if eof?
|
|
74
78
|
|
|
75
79
|
return read_comment if @preserve_comments && peek == ';'
|
|
76
80
|
|
|
@@ -93,7 +97,7 @@ module Kapusta
|
|
|
93
97
|
items = []
|
|
94
98
|
loop do
|
|
95
99
|
skip_ws
|
|
96
|
-
raise 'unclosed (' if eof?
|
|
100
|
+
raise Error, 'unclosed (' if eof?
|
|
97
101
|
break if peek == ')'
|
|
98
102
|
|
|
99
103
|
items << read_next_item
|
|
@@ -107,7 +111,7 @@ module Kapusta
|
|
|
107
111
|
items = []
|
|
108
112
|
loop do
|
|
109
113
|
skip_ws
|
|
110
|
-
raise 'unclosed [' if eof?
|
|
114
|
+
raise Error, 'unclosed [' if eof?
|
|
111
115
|
break if peek == ']'
|
|
112
116
|
|
|
113
117
|
items << read_next_item
|
|
@@ -122,7 +126,7 @@ module Kapusta
|
|
|
122
126
|
pending = []
|
|
123
127
|
loop do
|
|
124
128
|
skip_ws
|
|
125
|
-
raise 'unclosed {' if eof?
|
|
129
|
+
raise Error, 'unclosed {' if eof?
|
|
126
130
|
break if peek == '}'
|
|
127
131
|
|
|
128
132
|
item = read_next_item
|
|
@@ -139,7 +143,7 @@ module Kapusta
|
|
|
139
143
|
end
|
|
140
144
|
advance
|
|
141
145
|
|
|
142
|
-
raise 'odd number of forms in hash' unless pending.empty?
|
|
146
|
+
raise Error, 'odd number of forms in hash' unless pending.empty?
|
|
143
147
|
|
|
144
148
|
HashLit.new(entries)
|
|
145
149
|
end
|
|
@@ -168,7 +172,7 @@ module Kapusta
|
|
|
168
172
|
buffer << advance
|
|
169
173
|
end
|
|
170
174
|
end
|
|
171
|
-
raise 'unterminated string' if eof?
|
|
175
|
+
raise Error, 'unterminated string' if eof?
|
|
172
176
|
|
|
173
177
|
advance
|
|
174
178
|
buffer
|
|
@@ -209,7 +213,7 @@ module Kapusta
|
|
|
209
213
|
start = @pos
|
|
210
214
|
advance until delim?(peek)
|
|
211
215
|
token = @src[start...@pos]
|
|
212
|
-
raise 'empty token' if token.empty?
|
|
216
|
+
raise Error, 'empty token' if token.empty?
|
|
213
217
|
|
|
214
218
|
parse_atom(token)
|
|
215
219
|
end
|
|
@@ -230,7 +234,7 @@ module Kapusta
|
|
|
230
234
|
|
|
231
235
|
def normalize_hash_pair(item, value)
|
|
232
236
|
if item.is_a?(Sym) && item.name == ':'
|
|
233
|
-
raise 'bad shorthand' unless value.is_a?(Sym)
|
|
237
|
+
raise Error, 'bad shorthand' unless value.is_a?(Sym)
|
|
234
238
|
|
|
235
239
|
key = Kapusta.kebab_to_snake(value.name).to_sym
|
|
236
240
|
[key, value]
|
data/lib/kapusta/version.rb
CHANGED
data/lib/kapusta.rb
CHANGED
data/spec/cli_spec.rb
CHANGED
|
@@ -26,17 +26,23 @@ ensure
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
RSpec.describe Kapusta::CLI do
|
|
29
|
-
it 'compiles a .kap file to
|
|
29
|
+
it 'compiles a .kap file to runnable Ruby with --compile' do
|
|
30
30
|
path = File.expand_path('../examples/fizzbuzz.kap', __dir__)
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
ruby = capture_stdout do
|
|
33
33
|
described_class.start(['--compile', path])
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
Dir.mktmpdir do |dir|
|
|
37
|
+
output_path = File.join(dir, 'fizzbuzz.rb')
|
|
38
|
+
File.write(output_path, ruby)
|
|
39
|
+
|
|
40
|
+
stdout, stderr, status = Open3.capture3(RbConfig.ruby, output_path)
|
|
41
|
+
|
|
42
|
+
expected = "1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n16\n17\nFizz\n19\nBuzz\n"
|
|
43
|
+
expect(status.success?).to eq(true), stderr
|
|
44
|
+
expect(stdout).to eq(expected)
|
|
45
|
+
end
|
|
40
46
|
end
|
|
41
47
|
|
|
42
48
|
it 'rejects extra positional arguments in compile mode' do
|
|
@@ -50,21 +56,6 @@ RSpec.describe Kapusta::CLI do
|
|
|
50
56
|
expect(error_output).to include('usage: kapusta')
|
|
51
57
|
end
|
|
52
58
|
|
|
53
|
-
it 'emits standalone Ruby that runs with plain ruby' do
|
|
54
|
-
source_path = File.expand_path('../examples/fizzbuzz.kap', __dir__)
|
|
55
|
-
|
|
56
|
-
Dir.mktmpdir do |dir|
|
|
57
|
-
output_path = File.join(dir, 'fizzbuzz.rb')
|
|
58
|
-
ruby = Kapusta.compile(File.read(source_path), path: source_path)
|
|
59
|
-
File.write(output_path, ruby)
|
|
60
|
-
|
|
61
|
-
stdout, stderr, status = Open3.capture3(RbConfig.ruby, output_path)
|
|
62
|
-
|
|
63
|
-
expect(status.success?).to eq(true), stderr
|
|
64
|
-
expect(stdout).to include("FizzBuzz\n")
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
59
|
it 'passes remaining arguments through to the Kapusta program' do
|
|
69
60
|
path = File.expand_path('../examples/greet.kap', __dir__)
|
|
70
61
|
|
data/spec/examples_spec.rb
CHANGED
|
@@ -63,6 +63,10 @@ RSpec.describe 'examples' do
|
|
|
63
63
|
expect(run_example('counter.kap')).to eq("12\n")
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
it 'contains-duplicate.kap' do
|
|
67
|
+
expect(run_example('contains-duplicate.kap')).to eq("true\nfalse\ntrue\n")
|
|
68
|
+
end
|
|
69
|
+
|
|
66
70
|
it 'doto.kap' do
|
|
67
71
|
expect(run_example('doto.kap')).to eq("1, 2, 3\n")
|
|
68
72
|
end
|
|
@@ -302,6 +306,6 @@ end
|
|
|
302
306
|
RSpec.describe 'errors' do
|
|
303
307
|
it 'raises on unclosed list' do
|
|
304
308
|
expect { Kapusta.eval('(fn hello [name] (.. "Hi " name "!")') }
|
|
305
|
-
.to raise_error(/unclosed \(/)
|
|
309
|
+
.to raise_error(Kapusta::Reader::Error, /unclosed \(/)
|
|
306
310
|
end
|
|
307
311
|
end
|
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.1.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evgenii Morozov
|
|
@@ -33,6 +33,7 @@ files:
|
|
|
33
33
|
- examples/block-sort.kap
|
|
34
34
|
- examples/blocks-and-kwargs.kap
|
|
35
35
|
- examples/calc.kap
|
|
36
|
+
- examples/contains-duplicate.kap
|
|
36
37
|
- examples/counter.kap
|
|
37
38
|
- examples/describe.kap
|
|
38
39
|
- examples/destructure.kap
|
|
@@ -94,6 +95,7 @@ files:
|
|
|
94
95
|
- lib/kapusta/compiler/normalizer.rb
|
|
95
96
|
- lib/kapusta/compiler/runtime.rb
|
|
96
97
|
- lib/kapusta/env.rb
|
|
98
|
+
- lib/kapusta/error.rb
|
|
97
99
|
- lib/kapusta/formatter.rb
|
|
98
100
|
- lib/kapusta/reader.rb
|
|
99
101
|
- lib/kapusta/support.rb
|