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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +50 -11
  3. data/bin/check-all +6 -1
  4. data/bin/compile-examples +7 -2
  5. data/examples/anagram.kap +3 -3
  6. data/examples/app/args.kap +9 -0
  7. data/examples/arrange-coins.kap +3 -3
  8. data/examples/baseball-game.kap +5 -5
  9. data/examples/best-time-to-buy-sell-stock.kap +2 -2
  10. data/examples/binary-search.kap +2 -2
  11. data/examples/binary-to-decimal.kap +1 -1
  12. data/examples/blocks-and-kwargs.kap +2 -2
  13. data/examples/count-effects.kap +1 -1
  14. data/examples/count-items-matching-rule.kap +18 -0
  15. data/examples/doto-hygiene.kap +2 -2
  16. data/examples/doto.kap +2 -2
  17. data/examples/egg-count.kap +1 -1
  18. data/examples/exceptions.kap +1 -1
  19. data/examples/falling-drops.kap +7 -7
  20. data/examples/fennel-parity-examples.txt +3 -6
  21. data/examples/good-pairs.kap +18 -0
  22. data/examples/greet.kap +1 -1
  23. data/examples/happy-number.kap +2 -2
  24. data/examples/left-right-difference.kap +60 -0
  25. data/examples/length-of-last-word.kap +4 -4
  26. data/examples/manhattan-distance.kap +1 -1
  27. data/examples/maximum-subarray.kap +23 -7
  28. data/examples/minimum-start-value.kap +19 -0
  29. data/examples/move-zeroes.kap +2 -2
  30. data/examples/mruby-runtime-examples.txt +8 -2
  31. data/examples/non-constant-local.kap +1 -1
  32. data/examples/number-of-1-bits.kap +1 -1
  33. data/examples/number-of-steps.kap +1 -1
  34. data/examples/palindrome.kap +2 -2
  35. data/examples/pangram.kap +4 -4
  36. data/examples/pcall.kap +3 -3
  37. data/examples/pipeline.kap +4 -4
  38. data/examples/plus-one.kap +3 -3
  39. data/examples/raindrops.kap +1 -1
  40. data/examples/range-width.kap +14 -0
  41. data/examples/recent-counter.kap +6 -6
  42. data/examples/require-local-args.kap +9 -0
  43. data/examples/require-local.kap +7 -0
  44. data/examples/require-module-local.kap +4 -0
  45. data/examples/require-module.kap +4 -0
  46. data/examples/reverse-integer.kap +1 -1
  47. data/examples/roman-to-integer.kap +3 -3
  48. data/examples/running-sum.kap +20 -0
  49. data/examples/safe-lookup.kap +2 -2
  50. data/examples/single-number.kap +1 -1
  51. data/examples/stack.kap +14 -14
  52. data/examples/subtract-product-sum.kap +1 -1
  53. data/examples/summary-ranges.kap +45 -0
  54. data/examples/threading.kap +5 -5
  55. data/examples/tset.kap +1 -1
  56. data/examples/two-sum-hash.kap +3 -3
  57. data/examples/two-sum.kap +1 -1
  58. data/examples/ugly-number.kap +1 -1
  59. data/examples/underground-system.kap +39 -0
  60. data/examples/valid-parentheses-1.kap +6 -6
  61. data/exe/kapusta-ls +49 -2
  62. data/lib/kapusta/ast.rb +8 -0
  63. data/lib/kapusta/compiler/emitter/bindings.rb +111 -89
  64. data/lib/kapusta/compiler/emitter/collections.rb +32 -40
  65. data/lib/kapusta/compiler/emitter/control_flow.rb +33 -31
  66. data/lib/kapusta/compiler/emitter/expressions.rb +21 -5
  67. data/lib/kapusta/compiler/emitter/interop.rb +168 -48
  68. data/lib/kapusta/compiler/emitter/patterns.rb +12 -14
  69. data/lib/kapusta/compiler/emitter/support.rb +63 -81
  70. data/lib/kapusta/compiler/language.rb +522 -0
  71. data/lib/kapusta/compiler/lua_compat.rb +23 -28
  72. data/lib/kapusta/compiler/macro_expander.rb +30 -30
  73. data/lib/kapusta/compiler/macro_lowerer.rb +12 -24
  74. data/lib/kapusta/compiler/normalizer.rb +25 -17
  75. data/lib/kapusta/compiler.rb +3 -24
  76. data/lib/kapusta/env.rb +2 -2
  77. data/lib/kapusta/errors.rb +2 -1
  78. data/lib/kapusta/formatter/ast_helpers.rb +78 -0
  79. data/lib/kapusta/formatter/cli.rb +125 -0
  80. data/lib/kapusta/formatter/line_helpers.rb +44 -0
  81. data/lib/kapusta/formatter/validator.rb +32 -0
  82. data/lib/kapusta/formatter.rb +354 -325
  83. data/lib/kapusta/lsp/identifier.rb +1 -1
  84. data/lib/kapusta/lsp/rename.rb +21 -11
  85. data/lib/kapusta/lsp/scope_walker.rb +122 -212
  86. data/lib/kapusta/lsp/workspace_index.rb +17 -5
  87. data/lib/kapusta/reader.rb +4 -2
  88. data/lib/kapusta/version.rb +1 -1
  89. data/lib/kapusta.rb +39 -6
  90. data/spec/cli_spec.rb +13 -0
  91. data/spec/examples_errors_spec.rb +3 -1
  92. data/spec/examples_spec.rb +67 -15
  93. data/spec/formatter_spec.rb +246 -0
  94. data/spec/lsp_spec.rb +69 -0
  95. data/spec/require_spec.rb +294 -0
  96. metadata +20 -2
  97. 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
- form.is_a?(List) && !form.empty? && form.head.is_a?(Sym) && form.head.name == 'end'
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.items[1] : split_class_args(form.items[1..])[0]
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
- name_sym = form.items[1]
107
- inner = form.items[2..] || []
108
- if inner.length == 1 && bodyless_header?(inner[0])
109
- inner_code, next_i = emit_bodyless_header(inner[0], forms, body_start, env)
110
- [emit_direct_module_header(name_sym, inner_code) || emit_module_wrapper(name_sym, inner_code), next_i]
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(name_sym, body) || emit_module_wrapper(name_sym, body), next_i]
98
+ [emit_direct_module_header(parsed.name, body) || emit_module_wrapper(parsed.name, body), next_i]
116
99
  end
117
100
  else
118
- name_sym, supers, = split_class_args(form.items[1..])
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(name_sym, supers, body, env) ||
123
- emit_class_wrapper(name_sym, supers, env, body)
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 %i[module class].include?(current_scope)
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
- %i[toplevel module class].include?(current_scope)
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
- form.is_a?(List) && form.head.is_a?(Sym) &&
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
- name_sym = args[0]
173
+ parsed = Language.parse_module_args(args)
177
174
  body = with_class_body do
178
- emit_sequence(args[1..], env.child, :module, allow_method_definitions: true,
179
- result: false).first
175
+ emit_sequence(parsed.body, env.child, :module, allow_method_definitions: true,
176
+ result: false).first
180
177
  end
181
- emit_direct_module_header(name_sym, body) || emit_module_wrapper(name_sym, body)
178
+ emit_direct_module_header(parsed.name, body) || emit_module_wrapper(parsed.name, body)
182
179
  else
183
- name_sym, supers, body_forms = split_class_args(args)
180
+ parsed = Language.parse_class_args(args)
184
181
  body = with_class_body do
185
- emit_sequence(body_forms, env.child, :class, allow_method_definitions: true,
186
- result: false).first
182
+ emit_sequence(parsed.body, env.child, :class, allow_method_definitions: true,
183
+ result: false).first
187
184
  end
188
- emit_direct_class_header(name_sym, supers, body, env) ||
189
- emit_class_wrapper(name_sym, supers, env, body)
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 %i[toplevel module class].include?(current_scope)
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
- return false unless form.is_a?(List) && !form.empty? && form.head.is_a?(Sym)
233
-
234
- case form.head.name
229
+ case Language.list_head_name(form)
235
230
  when 'defn', 'class', 'module' then true
236
- when 'fn', 'lambda', 'λ' then form.items[1].is_a?(Sym)
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
- return false unless form.is_a?(List) && form.head.is_a?(Sym)
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
- Compiler::SPECIAL_FORMS.include?(name)
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
- form.is_a?(List) && !form.empty? && form.head.is_a?(Sym) &&
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
- form.is_a?(List) && !form.empty? && form.head.is_a?(Sym) && form.head.name == 'defn'
269
+ Language.defn_form?(form)
287
270
  end
288
271
 
289
272
  def lower_defn_to_fn(form)
290
- name_sym = form.items[1]
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
- form.is_a?(List) && form.head.is_a?(Sym) && %w[fn lambda λ hashfn].include?(form.head.name)
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
- form.is_a?(List) && form.head.is_a?(Sym) && %w[local var].include?(form.head.name)
298
+ Language.binding_form?(form)
315
299
  end
316
300
 
317
301
  def do_form?(form)
318
- form.is_a?(List) && form.head.is_a?(Sym) && form.head.name == 'do'
302
+ Language.do_form?(form)
319
303
  end
320
304
 
321
305
  def set_new_local_form?(form, env)
322
- return false unless form.is_a?(List) && form.head.is_a?(Sym) && form.head.name == 'set'
306
+ parsed = Language.parse_set_form(form)
307
+ return false unless parsed
323
308
 
324
- target = form.items[1]
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(bindings, env, current_scope)
401
- emit_error!(:counted_no_range) if bindings.length < 3
402
- name_sym = bindings[0]
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(bindings[1], env, current_scope)
406
- finish_code = emit_expr(bindings[2], env, current_scope)
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
- i = 3
410
- while i < bindings.length
411
- if bindings[i].is_a?(Sym) && bindings[i].name == '&until'
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(bindings[i], env, current_scope)
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: }