kapusta 0.13.2 → 0.14.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 +50 -11
- data/bin/check-all +6 -1
- data/bin/compile-examples +7 -2
- data/examples/anagram.kap +3 -3
- data/examples/app/args.kap +9 -0
- data/examples/arrange-coins.kap +3 -3
- data/examples/baseball-game.kap +5 -5
- data/examples/best-time-to-buy-sell-stock.kap +2 -2
- data/examples/binary-search.kap +2 -2
- data/examples/binary-to-decimal.kap +1 -1
- data/examples/blocks-and-kwargs.kap +2 -2
- data/examples/count-effects.kap +1 -1
- data/examples/count-items-matching-rule.kap +18 -0
- data/examples/doto-hygiene.kap +2 -2
- data/examples/doto.kap +2 -2
- data/examples/egg-count.kap +1 -1
- data/examples/exceptions.kap +1 -1
- data/examples/falling-drops.kap +7 -7
- data/examples/fennel-parity-examples.txt +3 -6
- data/examples/good-pairs.kap +18 -0
- data/examples/greet.kap +1 -1
- data/examples/happy-number.kap +2 -2
- data/examples/left-right-difference.kap +60 -0
- data/examples/length-of-last-word.kap +4 -4
- data/examples/manhattan-distance.kap +1 -1
- data/examples/maximum-subarray.kap +23 -7
- data/examples/minimum-start-value.kap +19 -0
- data/examples/move-zeroes.kap +2 -2
- data/examples/mruby-runtime-examples.txt +8 -2
- data/examples/non-constant-local.kap +1 -1
- data/examples/number-of-1-bits.kap +1 -1
- data/examples/number-of-steps.kap +1 -1
- data/examples/palindrome.kap +2 -2
- data/examples/pangram.kap +4 -4
- data/examples/pcall.kap +3 -3
- data/examples/pipeline.kap +4 -4
- data/examples/plus-one.kap +3 -3
- data/examples/raindrops.kap +1 -1
- data/examples/range-width.kap +14 -0
- data/examples/recent-counter.kap +6 -6
- data/examples/require-local-args.kap +9 -0
- data/examples/require-local.kap +7 -0
- data/examples/require-module-local.kap +4 -0
- data/examples/require-module.kap +4 -0
- data/examples/reverse-integer.kap +1 -1
- data/examples/roman-to-integer.kap +3 -3
- data/examples/running-sum.kap +20 -0
- data/examples/safe-lookup.kap +2 -2
- data/examples/single-number.kap +1 -1
- data/examples/stack.kap +14 -14
- data/examples/subtract-product-sum.kap +1 -1
- data/examples/summary-ranges.kap +45 -0
- data/examples/threading.kap +5 -5
- data/examples/tset.kap +1 -1
- data/examples/two-sum-hash.kap +3 -3
- data/examples/two-sum.kap +1 -1
- data/examples/ugly-number.kap +1 -1
- data/examples/underground-system.kap +39 -0
- data/examples/valid-parentheses-1.kap +6 -6
- data/exe/kapusta-ls +49 -2
- data/lib/kapusta/ast.rb +8 -0
- data/lib/kapusta/compiler/emitter/bindings.rb +111 -89
- data/lib/kapusta/compiler/emitter/collections.rb +32 -40
- data/lib/kapusta/compiler/emitter/control_flow.rb +33 -31
- data/lib/kapusta/compiler/emitter/expressions.rb +21 -5
- data/lib/kapusta/compiler/emitter/interop.rb +168 -48
- data/lib/kapusta/compiler/emitter/patterns.rb +12 -14
- data/lib/kapusta/compiler/emitter/support.rb +63 -81
- data/lib/kapusta/compiler/language.rb +522 -0
- data/lib/kapusta/compiler/lua_compat.rb +23 -28
- data/lib/kapusta/compiler/macro_expander.rb +30 -30
- data/lib/kapusta/compiler/macro_lowerer.rb +12 -24
- data/lib/kapusta/compiler/normalizer.rb +25 -17
- data/lib/kapusta/compiler.rb +3 -24
- data/lib/kapusta/env.rb +2 -2
- data/lib/kapusta/errors.rb +2 -1
- data/lib/kapusta/formatter/ast_helpers.rb +78 -0
- data/lib/kapusta/formatter/cli.rb +125 -0
- data/lib/kapusta/formatter/line_helpers.rb +44 -0
- data/lib/kapusta/formatter/validator.rb +32 -0
- data/lib/kapusta/formatter.rb +354 -325
- data/lib/kapusta/lsp/identifier.rb +1 -1
- data/lib/kapusta/lsp/rename.rb +21 -11
- data/lib/kapusta/lsp/scope_walker.rb +122 -212
- data/lib/kapusta/lsp/workspace_index.rb +17 -5
- data/lib/kapusta/reader.rb +4 -2
- data/lib/kapusta/version.rb +1 -1
- data/lib/kapusta.rb +39 -6
- data/spec/cli_spec.rb +13 -0
- data/spec/examples_errors_spec.rb +3 -1
- data/spec/examples_spec.rb +67 -15
- data/spec/formatter_spec.rb +246 -0
- data/spec/lsp_spec.rb +69 -0
- data/spec/require_spec.rb +294 -0
- metadata +20 -2
- data/examples/describe.kap +0 -9
|
@@ -50,7 +50,7 @@ module Kapusta
|
|
|
50
50
|
return [join_definition_aware(entries, current_scope), i + 1]
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
if bodyless_header?(form)
|
|
53
|
+
if Language.bodyless_header?(form)
|
|
54
54
|
header_code, i = emit_bodyless_header(form, forms, i + 1, env)
|
|
55
55
|
entries << [form, header_code]
|
|
56
56
|
next
|
|
@@ -67,7 +67,7 @@ module Kapusta
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def end_form?(form)
|
|
70
|
-
|
|
70
|
+
Language.end_form?(form)
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def validate_end_form!(form)
|
|
@@ -75,52 +75,35 @@ module Kapusta
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def validate_header_name!(form, head)
|
|
78
|
-
name_sym = head == 'module' ? form.
|
|
78
|
+
name_sym = head == 'module' ? Language.parse_module_form(form).name : Language.parse_class_form(form).name
|
|
79
79
|
return if constant_segments(name_sym)
|
|
80
80
|
|
|
81
81
|
code = head == 'module' ? :invalid_module_name : :invalid_class_name
|
|
82
82
|
emit_error!(code, name: name_sym.respond_to?(:name) ? name_sym.name : name_sym.inspect)
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
-
def bodyless_header?(form)
|
|
86
|
-
return false unless form.is_a?(List) && !form.empty? && form.head.is_a?(Sym)
|
|
87
|
-
|
|
88
|
-
case form.head.name
|
|
89
|
-
when 'module'
|
|
90
|
-
body = form.items[2..] || []
|
|
91
|
-
return true if body.empty?
|
|
92
|
-
|
|
93
|
-
body.length == 1 && bodyless_header?(body[0])
|
|
94
|
-
when 'class'
|
|
95
|
-
_, _, body = split_class_args(form.items[1..])
|
|
96
|
-
body.empty?
|
|
97
|
-
else
|
|
98
|
-
false
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
85
|
def emit_bodyless_header(form, forms, body_start, env)
|
|
103
86
|
head = form.head.name
|
|
104
87
|
validate_header_name!(form, head)
|
|
105
88
|
if head == 'module'
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
89
|
+
parsed = Language.parse_module_form(form)
|
|
90
|
+
if parsed.body.length == 1 && Language.bodyless_header?(parsed.body[0])
|
|
91
|
+
inner_code, next_i = emit_bodyless_header(parsed.body[0], forms, body_start, env)
|
|
92
|
+
[emit_direct_module_header(parsed.name, inner_code) || emit_module_wrapper(parsed.name, inner_code),
|
|
93
|
+
next_i]
|
|
111
94
|
else
|
|
112
95
|
body, next_i = with_class_body do
|
|
113
96
|
emit_form_run(forms, body_start, env.child, :module, header_form: form)
|
|
114
97
|
end
|
|
115
|
-
[emit_direct_module_header(
|
|
98
|
+
[emit_direct_module_header(parsed.name, body) || emit_module_wrapper(parsed.name, body), next_i]
|
|
116
99
|
end
|
|
117
100
|
else
|
|
118
|
-
|
|
101
|
+
parsed = Language.parse_class_form(form)
|
|
119
102
|
body, next_i = with_class_body do
|
|
120
103
|
emit_form_run(forms, body_start, env.child, :class, header_form: form)
|
|
121
104
|
end
|
|
122
|
-
code = emit_direct_class_header(
|
|
123
|
-
emit_class_wrapper(
|
|
105
|
+
code = emit_direct_class_header(parsed.name, parsed.supers, body, env) ||
|
|
106
|
+
emit_class_wrapper(parsed.name, parsed.supers, env, body)
|
|
124
107
|
[code, next_i]
|
|
125
108
|
end
|
|
126
109
|
end
|
|
@@ -133,18 +116,20 @@ module Kapusta
|
|
|
133
116
|
end
|
|
134
117
|
|
|
135
118
|
def lower_defn_in_sequence(form, current_scope)
|
|
136
|
-
emit_error!(:defn_outside_header) unless
|
|
119
|
+
emit_error!(:defn_outside_header) unless Language.header_scope?(current_scope)
|
|
137
120
|
lower_defn_to_fn(form)
|
|
138
121
|
end
|
|
139
122
|
|
|
140
123
|
def emit_form_body(form, env, current_scope, allow_method_definitions:, result_needed:)
|
|
141
124
|
if allow_method_definitions &&
|
|
142
125
|
method_definition_form?(form) &&
|
|
143
|
-
|
|
126
|
+
Language.definition_scope?(current_scope)
|
|
144
127
|
code, env = emit_definition_form(form, env, current_scope)
|
|
145
128
|
return [code, env] if code
|
|
146
129
|
end
|
|
147
130
|
|
|
131
|
+
validate_header_body_form!(form, env, current_scope, allow_method_definitions:)
|
|
132
|
+
|
|
148
133
|
if named_function_form?(form)
|
|
149
134
|
emit_named_fn_assignment(form, env, current_scope)
|
|
150
135
|
elsif local_form?(form)
|
|
@@ -165,28 +150,40 @@ module Kapusta
|
|
|
165
150
|
end
|
|
166
151
|
end
|
|
167
152
|
|
|
153
|
+
def validate_header_body_form!(form, env, current_scope, allow_method_definitions:)
|
|
154
|
+
return unless allow_method_definitions
|
|
155
|
+
return unless Language.header_scope?(current_scope)
|
|
156
|
+
return unless form.is_a?(List) && form.head.is_a?(Sym)
|
|
157
|
+
|
|
158
|
+
name = form.head.name
|
|
159
|
+
return if special_form?(name)
|
|
160
|
+
return if form.head.dotted?
|
|
161
|
+
return if env.lookup_if_defined(name)
|
|
162
|
+
|
|
163
|
+
emit_error!(:invalid_header_body_form, scope: current_scope, name:)
|
|
164
|
+
end
|
|
165
|
+
|
|
168
166
|
def class_or_module_form?(form)
|
|
169
|
-
|
|
170
|
-
%w[class module].include?(form.head.name)
|
|
167
|
+
Language.header_form?(form)
|
|
171
168
|
end
|
|
172
169
|
|
|
173
170
|
def emit_class_or_module_statement(form, env)
|
|
174
171
|
args = form.rest
|
|
175
172
|
if form.head.name == 'module'
|
|
176
|
-
|
|
173
|
+
parsed = Language.parse_module_args(args)
|
|
177
174
|
body = with_class_body do
|
|
178
|
-
emit_sequence(
|
|
179
|
-
|
|
175
|
+
emit_sequence(parsed.body, env.child, :module, allow_method_definitions: true,
|
|
176
|
+
result: false).first
|
|
180
177
|
end
|
|
181
|
-
emit_direct_module_header(
|
|
178
|
+
emit_direct_module_header(parsed.name, body) || emit_module_wrapper(parsed.name, body)
|
|
182
179
|
else
|
|
183
|
-
|
|
180
|
+
parsed = Language.parse_class_args(args)
|
|
184
181
|
body = with_class_body do
|
|
185
|
-
emit_sequence(
|
|
186
|
-
|
|
182
|
+
emit_sequence(parsed.body, env.child, :class, allow_method_definitions: true,
|
|
183
|
+
result: false).first
|
|
187
184
|
end
|
|
188
|
-
emit_direct_class_header(
|
|
189
|
-
emit_class_wrapper(
|
|
185
|
+
emit_direct_class_header(parsed.name, parsed.supers, body, env) ||
|
|
186
|
+
emit_class_wrapper(parsed.name, parsed.supers, env, body)
|
|
190
187
|
end
|
|
191
188
|
end
|
|
192
189
|
|
|
@@ -223,25 +220,21 @@ module Kapusta
|
|
|
223
220
|
end
|
|
224
221
|
|
|
225
222
|
def blank_between_definitions?(prev_form, curr_form, current_scope)
|
|
226
|
-
return false unless
|
|
223
|
+
return false unless Language.definition_scope?(current_scope)
|
|
227
224
|
|
|
228
225
|
definition_form?(prev_form) || definition_form?(curr_form)
|
|
229
226
|
end
|
|
230
227
|
|
|
231
228
|
def definition_form?(form)
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
case form.head.name
|
|
229
|
+
case Language.list_head_name(form)
|
|
235
230
|
when 'defn', 'class', 'module' then true
|
|
236
|
-
when
|
|
231
|
+
when *Language::FUNCTION_HEADS then Language.parse_function_form(form)&.named?
|
|
237
232
|
else false
|
|
238
233
|
end
|
|
239
234
|
end
|
|
240
235
|
|
|
241
236
|
def sequence_statement_form?(form)
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
%w[let while for each case match].include?(form.head.name)
|
|
237
|
+
Language.sequence_statement_form?(form)
|
|
245
238
|
end
|
|
246
239
|
|
|
247
240
|
def emit_sequence_statement_form(form, env, current_scope, result_needed:)
|
|
@@ -265,29 +258,19 @@ module Kapusta
|
|
|
265
258
|
end
|
|
266
259
|
|
|
267
260
|
def special_form?(name)
|
|
268
|
-
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
def split_class_args(args)
|
|
272
|
-
name_sym = args[0]
|
|
273
|
-
if args[1].is_a?(Vec)
|
|
274
|
-
[name_sym, args[1], args[2..] || []]
|
|
275
|
-
else
|
|
276
|
-
[name_sym, nil, args[1..] || []]
|
|
277
|
-
end
|
|
261
|
+
Language.special_form?(name)
|
|
278
262
|
end
|
|
279
263
|
|
|
280
264
|
def named_function_form?(form)
|
|
281
|
-
|
|
282
|
-
%w[fn lambda λ].include?(form.head.name) && form.items[1].is_a?(Sym)
|
|
265
|
+
Language.parse_function_form(form)&.named?
|
|
283
266
|
end
|
|
284
267
|
|
|
285
268
|
def defn_form?(form)
|
|
286
|
-
|
|
269
|
+
Language.defn_form?(form)
|
|
287
270
|
end
|
|
288
271
|
|
|
289
272
|
def lower_defn_to_fn(form)
|
|
290
|
-
name_sym = form
|
|
273
|
+
name_sym = Language.parse_function_form(form, heads: Language::FUNCTION_DEFINITION_HEADS)&.name
|
|
291
274
|
emit_error!(:fn_no_params) unless name_sym.is_a?(Sym)
|
|
292
275
|
|
|
293
276
|
fn_sym = Sym.new('fn')
|
|
@@ -307,21 +290,23 @@ module Kapusta
|
|
|
307
290
|
end
|
|
308
291
|
|
|
309
292
|
def block_form?(form)
|
|
310
|
-
|
|
293
|
+
name = Language.list_head_name(form)
|
|
294
|
+
!name.nil? && (Language.function_head?(name) || name == 'hashfn')
|
|
311
295
|
end
|
|
312
296
|
|
|
313
297
|
def local_form?(form)
|
|
314
|
-
|
|
298
|
+
Language.binding_form?(form)
|
|
315
299
|
end
|
|
316
300
|
|
|
317
301
|
def do_form?(form)
|
|
318
|
-
|
|
302
|
+
Language.do_form?(form)
|
|
319
303
|
end
|
|
320
304
|
|
|
321
305
|
def set_new_local_form?(form, env)
|
|
322
|
-
|
|
306
|
+
parsed = Language.parse_set_form(form)
|
|
307
|
+
return false unless parsed
|
|
323
308
|
|
|
324
|
-
target =
|
|
309
|
+
target = parsed.target
|
|
325
310
|
target.is_a?(Sym) && !target.dotted? && !env.defined?(target.name)
|
|
326
311
|
end
|
|
327
312
|
|
|
@@ -397,23 +382,20 @@ module Kapusta
|
|
|
397
382
|
binding
|
|
398
383
|
end
|
|
399
384
|
|
|
400
|
-
def parse_counted_for_bindings(
|
|
401
|
-
emit_error!(:counted_no_range) if
|
|
402
|
-
name_sym =
|
|
385
|
+
def parse_counted_for_bindings(parsed, env, current_scope)
|
|
386
|
+
emit_error!(:counted_no_range) if parsed.items.length < 3
|
|
387
|
+
name_sym = parsed.counter
|
|
403
388
|
loop_env = env.child
|
|
404
389
|
ruby_name = define_local(loop_env, name_sym.name)
|
|
405
|
-
start_code = emit_expr(
|
|
406
|
-
finish_code = emit_expr(
|
|
390
|
+
start_code = emit_expr(parsed.start, env, current_scope)
|
|
391
|
+
finish_code = emit_expr(parsed.finish, env, current_scope)
|
|
407
392
|
step_code = '1'
|
|
408
393
|
until_form = nil
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
until_form = bindings[i + 1]
|
|
413
|
-
i += 2
|
|
394
|
+
parsed.each_extra do |kind, form|
|
|
395
|
+
if kind == :until
|
|
396
|
+
until_form = form
|
|
414
397
|
else
|
|
415
|
-
step_code = emit_expr(
|
|
416
|
-
i += 1
|
|
398
|
+
step_code = emit_expr(form, env, current_scope)
|
|
417
399
|
end
|
|
418
400
|
end
|
|
419
401
|
{ ruby_name:, loop_env:, start_code:, finish_code:, step_code:, until_form: }
|