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
@@ -15,7 +15,7 @@ module Kapusta
15
15
  return false if name.match?(/[#{Regexp.escape(DELIM_CHARS)}]/o)
16
16
  return false if name.match?(/\A-?\d/)
17
17
  return false if name.include?('.')
18
- return false if Kapusta::Compiler::SPECIAL_FORMS.include?(name)
18
+ return false if Kapusta::Compiler::Language.special_form?(name)
19
19
 
20
20
  true
21
21
  end
@@ -64,7 +64,7 @@ module Kapusta
64
64
  return if synthetic?(sym)
65
65
 
66
66
  seg = segment_at_column(sym, col)
67
- return unless seg && seg[:index] != :on_dot
67
+ return unless seg && seg[:index] != :on_delimiter
68
68
 
69
69
  binding = walker.bindings.find { |b| b.sym.equal?(sym) }
70
70
  reference = walker.references.find { |r| r.sym.equal?(sym) }
@@ -98,7 +98,7 @@ module Kapusta
98
98
  end
99
99
 
100
100
  def segment_at_column(sym, col)
101
- unless sym.dotted?
101
+ unless path_sym?(sym)
102
102
  start_col = sym.column
103
103
  end_col = sym.column + sym.name.length
104
104
  return unless col.between?(start_col, end_col)
@@ -107,14 +107,14 @@ module Kapusta
107
107
  end
108
108
 
109
109
  pos = sym.column
110
- segments = sym.segments
110
+ segments = path_segments(sym)
111
111
  segments.each_with_index do |seg, k|
112
112
  seg_start = pos
113
113
  seg_end = pos + seg.length
114
114
  return { index: k, start: seg_start, end: seg_end } if col >= seg_start && col < seg_end
115
115
 
116
116
  if k < segments.length - 1
117
- return { index: :on_dot, start: seg_end, end: seg_end + 1 } if col == seg_end
117
+ return { index: :on_delimiter, start: seg_end, end: seg_end + 1 } if col == seg_end
118
118
  elsif col == seg_end
119
119
  return { index: k, start: seg_start, end: seg_end }
120
120
  end
@@ -124,13 +124,15 @@ module Kapusta
124
124
  end
125
125
 
126
126
  def classify(walker, sym, binding, reference, seg)
127
+ return if sym.colonized? && seg[:index].positive?
128
+
127
129
  if sym.dotted? && seg[:index].positive?
128
130
  segment_text = sym.segments[seg[:index]]
129
131
  return if segment_text.match?(/\A[a-z]/)
130
132
  end
131
133
 
132
134
  if binding
133
- return constant_target(walker, binding, seg) if %i[module class].include?(binding.kind)
135
+ return constant_target(walker, binding, seg) if Compiler::Language.header_scope?(binding.kind)
134
136
 
135
137
  return local_target(walker, binding, seg)
136
138
  end
@@ -138,17 +140,17 @@ module Kapusta
138
140
  if reference
139
141
  target = reference.target
140
142
  if target
141
- return constant_target(walker, target, seg, sym:) if %i[module class].include?(target.kind)
143
+ return constant_target(walker, target, seg, sym:) if Compiler::Language.header_scope?(target.kind)
142
144
 
143
145
  return local_target(walker, target, seg, sym:)
144
146
  end
145
147
  end
146
148
 
147
- first_seg = sym.dotted? ? sym.segments.first : sym.name
149
+ first_seg = path_sym?(sym) ? path_segments(sym).first : sym.name
148
150
  if first_seg.match?(/\A[A-Z]/)
149
151
  Target.new(
150
152
  kind: :free_constant, sym:, name: sym.name,
151
- segment_index: seg[:index], segment_prefix: (sym.dotted? ? sym.segments[0..seg[:index]] : [sym.name]),
153
+ segment_index: seg[:index], segment_prefix: (sym.dotted? ? sym.segments[0..seg[:index]] : [first_seg]),
152
154
  seg_start: seg[:start], seg_end: seg[:end], walker:
153
155
  )
154
156
  else
@@ -392,9 +394,9 @@ module Kapusta
392
394
  end
393
395
 
394
396
  def segment_range(sym, segment_index)
395
- return [sym.column, sym.column + sym.name.length] unless sym.dotted?
397
+ return [sym.column, sym.column + sym.name.length] unless path_sym?(sym)
396
398
 
397
- segments = sym.segments
399
+ segments = path_segments(sym)
398
400
  prior = segments[0...segment_index].sum { |s| s.length + 1 }
399
401
  start_col = sym.column + prior
400
402
  [start_col, start_col + segments[segment_index].length]
@@ -415,7 +417,7 @@ module Kapusta
415
417
 
416
418
  def text_edit_first_segment(occurrence, new_name)
417
419
  sym = occurrence.sym
418
- return text_edit_full(occurrence, new_name) unless sym.is_a?(Sym) && sym.dotted?
420
+ return text_edit_full(occurrence, new_name) unless sym.is_a?(Sym) && path_sym?(sym)
419
421
 
420
422
  seg_start, seg_end = segment_range(sym, 0)
421
423
  {
@@ -430,6 +432,14 @@ module Kapusta
430
432
  def error(message)
431
433
  { error: { code: RESPONSE_REQUEST_FAILED, message: } }
432
434
  end
435
+
436
+ def path_sym?(sym)
437
+ sym.dotted? || sym.colonized?
438
+ end
439
+
440
+ def path_segments(sym)
441
+ sym.dotted? ? sym.segments : sym.colon_segments
442
+ end
433
443
  end
434
444
  end
435
445
  end
@@ -19,22 +19,12 @@ module Kapusta
19
19
 
20
20
  DISPATCHERS = {
21
21
  'macros' => :skip,
22
- 'quasi-sym' => :skip,
23
- 'quasi-list' => :skip,
24
- 'quasi-list-tail' => :skip,
25
- 'quasi-vec' => :skip,
26
- 'quasi-vec-tail' => :skip,
27
- 'quasi-hash' => :skip,
28
- 'quasi-gensym' => :skip,
22
+ **Compiler::Language::QUASI_HEADS.to_h { |head| [head, :skip] },
29
23
  'let' => :walk_let,
30
- 'local' => :walk_local_var,
31
- 'var' => :walk_local_var,
24
+ **Compiler::Language::BINDING_HEADS.to_h { |head| [head, :walk_local_var] },
32
25
  'global' => :walk_global,
33
26
  'set' => :walk_set,
34
- 'fn' => :walk_fn,
35
- 'defn' => :walk_fn,
36
- 'lambda' => :walk_fn,
37
- 'λ' => :walk_fn,
27
+ **Compiler::Language::FUNCTION_DEFINITION_HEADS.to_h { |head| [head, :walk_fn] },
38
28
  'for' => :walk_for,
39
29
  'each' => :walk_each_like,
40
30
  'collect' => :walk_each_like,
@@ -98,7 +88,7 @@ module Kapusta
98
88
  return i + 1
99
89
  end
100
90
 
101
- if bodyless_header?(form)
91
+ if Compiler::Language.bodyless_header?(form)
102
92
  i = walk_bodyless_header(form, forms, i + 1, scope)
103
93
  next
104
94
  end
@@ -122,7 +112,7 @@ module Kapusta
122
112
  end
123
113
 
124
114
  def end_form?(form)
125
- form.is_a?(List) && !form.empty? && form.head.is_a?(Sym) && form.head.name == 'end'
115
+ Compiler::Language.end_form?(form)
126
116
  end
127
117
 
128
118
  def binding_at(line, column)
@@ -150,39 +140,23 @@ module Kapusta
150
140
  Scope.new(@scope_seq, parent, {}, kind)
151
141
  end
152
142
 
153
- def bodyless_header?(form)
154
- return false unless form.is_a?(List) && !form.empty? && form.head.is_a?(Sym)
155
-
156
- case form.head.name
157
- when 'module'
158
- body = form.items[2..] || []
159
- body.empty? || (body.length == 1 && bodyless_header?(body[0]))
160
- when 'class'
161
- _name_sym, _supers, body = split_class_args(form.items[1..] || [])
162
- body.empty?
163
- else
164
- false
165
- end
166
- end
167
-
168
143
  def walk_bodyless_header(form, forms, body_start, scope)
169
144
  case form.head.name
170
145
  when 'module'
171
- name_sym = form.items[1]
172
- binding = name_sym.is_a?(Sym) ? add_constant_binding(name_sym, scope, :module) : nil
173
- body = form.items[2..] || []
146
+ parsed = Compiler::Language.parse_module_form(form)
147
+ binding = parsed.name.is_a?(Sym) ? add_constant_binding(parsed.name, scope, :module) : nil
174
148
  inside_module_or_class do
175
- if body.length == 1 && bodyless_header?(body[0])
176
- walk_bodyless_header(body[0], forms, body_start, scope)
149
+ if parsed.body.length == 1 && Compiler::Language.bodyless_header?(parsed.body[0])
150
+ walk_bodyless_header(parsed.body[0], forms, body_start, scope)
177
151
  else
178
152
  body_scope = make_scope(scope, :module)
179
153
  walk_form_run(forms, body_start, body_scope, header_target: binding)
180
154
  end
181
155
  end
182
156
  when 'class'
183
- name_sym, supers, = split_class_args(form.items[1..] || [])
184
- supers&.items&.each { |item| walk_form(item, scope) }
185
- binding = name_sym.is_a?(Sym) ? add_constant_binding(name_sym, scope, :class) : nil
157
+ parsed = Compiler::Language.parse_class_form(form)
158
+ parsed.supers&.items&.each { |item| walk_form(item, scope) }
159
+ binding = parsed.name.is_a?(Sym) ? add_constant_binding(parsed.name, scope, :class) : nil
186
160
  inside_class do
187
161
  body_scope = make_scope(scope, :class)
188
162
  walk_form_run(forms, body_start, body_scope, header_target: binding)
@@ -190,15 +164,6 @@ module Kapusta
190
164
  end
191
165
  end
192
166
 
193
- def split_class_args(args)
194
- name_sym = args[0]
195
- if args[1].is_a?(Vec)
196
- [name_sym, args[1], args[2..] || []]
197
- else
198
- [name_sym, nil, args[1..] || []]
199
- end
200
- end
201
-
202
167
  def inside_module_or_class
203
168
  @in_module_or_class += 1
204
169
  yield
@@ -275,62 +240,51 @@ module Kapusta
275
240
  end
276
241
 
277
242
  def walk_let(list, scope)
278
- bindings_vec = list.items[1]
279
- body = list.items[2..]
280
- return unless bindings_vec.is_a?(Vec)
243
+ parsed = Compiler::Language.parse_let_form(list)
244
+ return unless parsed.bindings.is_a?(Vec)
281
245
 
282
246
  let_scope = make_scope(scope, :let)
283
- items = bindings_vec.items
284
- i = 0
285
- while i < items.length
286
- name_pat = items[i]
287
- value = items[i + 1]
247
+ parsed.binding_pairs.each do |name_pat, value|
288
248
  walk_form(value, let_scope) if value
289
249
  bind_pattern(name_pat, let_scope, :let)
290
- i += 2
291
250
  end
292
- body&.each { |form| walk_form(form, let_scope) }
251
+ parsed.body.each { |form| walk_form(form, let_scope) }
293
252
  end
294
253
 
295
254
  def walk_local_var(list, scope)
296
- kind = list.head.name == 'var' ? :var : :local
297
- target = list.items[1]
298
- value = list.items[2]
299
- walk_form(value, scope) if value
300
- bind_pattern(target, scope, kind)
255
+ parsed = Compiler::Language.parse_binding_form(list)
256
+ return unless parsed
257
+
258
+ walk_form(parsed.value, scope)
259
+ bind_pattern(parsed.target, scope, parsed.mutable? ? :var : :local)
301
260
  end
302
261
 
303
262
  def walk_global(list, _scope)
304
- # Globals are not renamable; skip the binder name and walk only the value.
305
- value = list.items[2]
306
- walk_form(value, @root_scope) if value
263
+ parsed = Compiler::Language.parse_global_form(list)
264
+ walk_form(parsed.value, @root_scope) if parsed
307
265
  end
308
266
 
309
267
  def walk_hashfn(list, scope)
310
- list.items[1..]&.each { |form| walk_form(form, scope) }
268
+ Compiler::Language.parse_hashfn_form(list).body.each { |form| walk_form(form, scope) }
311
269
  end
312
270
 
313
271
  def walk_macro_def(list, scope)
314
- items = list.items
315
- name_sym = items[1]
316
- params = items[2]
317
- body = items[3..] || []
318
- return unless name_sym.is_a?(Sym) && params.is_a?(Vec)
272
+ parsed = Compiler::Language.parse_macro_definition_form(list)
273
+ return unless parsed.name.is_a?(Sym) && parsed.params.is_a?(Vec)
319
274
 
320
- add_binding(name_sym, @root_scope, :macro)
275
+ add_binding(parsed.name, @root_scope, :macro)
321
276
  fn_scope = make_scope(scope, :fn)
322
- bind_param_vec(params, fn_scope)
323
- body.each { |form| walk_form(form, fn_scope) }
277
+ bind_param_vec(parsed.params, fn_scope)
278
+ parsed.body.each { |form| walk_form(form, fn_scope) }
324
279
  end
325
280
 
326
281
  def walk_import_macros(list, scope)
327
- destructure = list.items[1]
328
- module_arg = list.items[2]
329
- return unless destructure.is_a?(HashLit)
330
- return unless module_arg.is_a?(Symbol) || module_arg.is_a?(String)
282
+ parsed = Compiler::Language.parse_import_macros_form(list)
283
+ return unless parsed.destructure.is_a?(HashLit)
284
+ return unless parsed.module_arg.is_a?(Symbol) || parsed.module_arg.is_a?(String)
331
285
 
332
- module_label = module_arg.to_s.tr('_', '-')
333
- destructure.pairs.each do |key, target|
286
+ module_label = parsed.module_arg.to_s.tr('_', '-')
287
+ parsed.destructure.pairs.each do |key, target|
334
288
  next unless target.is_a?(Sym) && key.is_a?(Symbol)
335
289
 
336
290
  add_import_macro_binding(target, scope, module_label, key)
@@ -357,9 +311,9 @@ module Kapusta
357
311
  end
358
312
 
359
313
  def walk_set(list, scope)
360
- target = list.items[1]
361
- value = list.items[2]
362
- walk_form(value, scope) if value
314
+ parsed = Compiler::Language.parse_set_form(list)
315
+ target = parsed.target
316
+ walk_form(parsed.value, scope) if parsed.value
363
317
  if target.is_a?(List)
364
318
  walk_form(target, scope)
365
319
  return
@@ -375,18 +329,15 @@ module Kapusta
375
329
  end
376
330
 
377
331
  def walk_sigil_form(list, _scope)
378
- return if list.items.length < 2
332
+ parsed = Compiler::Language.parse_sigil_form(list)
333
+ return unless parsed&.name.is_a?(Sym)
379
334
 
380
- inner = list.items[1]
381
- return unless inner.is_a?(Sym)
382
-
383
- kind = list.head.name.to_sym
384
- target_scope = sigil_target_scope(kind)
385
- existing = target_scope.bindings[inner.name]
335
+ target_scope = sigil_target_scope(parsed.kind)
336
+ existing = target_scope.bindings[parsed.name.name]
386
337
  if existing
387
- add_reference(inner, target_scope, existing)
338
+ add_reference(parsed.name, target_scope, existing)
388
339
  else
389
- add_binding(inner, target_scope, kind)
340
+ add_binding(parsed.name, target_scope, parsed.kind)
390
341
  end
391
342
  end
392
343
 
@@ -398,32 +349,24 @@ module Kapusta
398
349
  end
399
350
 
400
351
  def walk_fn(list, scope)
401
- items = list.items
402
- if items[1].is_a?(Vec)
403
- name_sym = nil
404
- params = items[1]
405
- body = items[2..]
406
- elsif items[1].is_a?(Sym) && items[2].is_a?(Vec)
407
- name_sym = items[1]
408
- params = items[2]
409
- body = items[3..]
410
- else
411
- items[1..]&.each { |item| walk_form(item, scope) }
352
+ parsed = Compiler::Language.parse_function_form(list, heads: Compiler::Language::FUNCTION_DEFINITION_HEADS)
353
+ unless parsed
354
+ list.items[1..]&.each { |item| walk_form(item, scope) }
412
355
  return
413
356
  end
414
357
 
415
358
  fn_scope = make_scope(scope, :fn)
416
- if name_sym
359
+ if parsed.named?
417
360
  kind = if method_definition_context?
418
361
  :method
419
362
  else
420
363
  (scope == @root_scope ? :toplevel_fn : :fn_local)
421
364
  end
422
- binding = add_binding(name_sym, scope, kind, lexical: true)
423
- fn_scope.bindings[name_sym.name] = binding unless kind == :method
365
+ binding = add_binding(parsed.name, scope, kind, lexical: true)
366
+ fn_scope.bindings[parsed.name.name] = binding unless kind == :method
424
367
  end
425
- bind_param_vec(params, fn_scope)
426
- body.each { |form| walk_form(form, fn_scope) }
368
+ bind_param_vec(parsed.params, fn_scope)
369
+ parsed.body.each { |form| walk_form(form, fn_scope) }
427
370
  end
428
371
 
429
372
  def method_definition_context?
@@ -431,94 +374,70 @@ module Kapusta
431
374
  end
432
375
 
433
376
  def walk_for(list, scope)
434
- bindings_vec = list.items[1]
435
- body = list.items[2..]
436
- return unless bindings_vec.is_a?(Vec)
377
+ parsed = Compiler::Language.parse_counted_for_form(list)
378
+ return unless parsed.bindings.is_a?(Vec)
437
379
 
438
380
  for_scope = make_scope(scope, :for)
439
- items = bindings_vec.items
440
- counter = items[0]
441
- i = 1
442
381
  until_forms = []
443
- while i < items.length
444
- item = items[i]
445
- if item.is_a?(Sym) && item.name == '&until'
446
- until_forms << items[i + 1] if items[i + 1]
447
- i += 2
448
- else
449
- walk_form(item, scope)
450
- i += 1
451
- end
382
+ [parsed.start, parsed.finish].compact.each { |form| walk_form(form, scope) }
383
+ parsed.each_extra do |kind, form|
384
+ next unless form
385
+
386
+ kind == :until ? until_forms << form : walk_form(form, scope)
452
387
  end
453
- bind_pattern(counter, for_scope, :for_counter) if counter
388
+ bind_pattern(parsed.counter, for_scope, :for_counter) if parsed.counter
454
389
  until_forms.each { |form| walk_form(form, for_scope) }
455
- body&.each { |form| walk_form(form, for_scope) }
390
+ parsed.body.each { |form| walk_form(form, for_scope) }
456
391
  end
457
392
 
458
393
  def walk_for_like(list, scope) = walk_for(list, scope)
459
394
 
460
395
  def walk_each_like(list, scope)
461
- bindings_vec = list.items[1]
462
- body = list.items[2..]
396
+ parsed = Compiler::Language.parse_iteration_form(list)
397
+ bindings_vec = parsed.bindings
463
398
  return unless bindings_vec.is_a?(Vec)
464
399
 
465
- items = bindings_vec.items
466
- return if items.empty?
400
+ return if parsed.items.empty?
467
401
 
468
402
  each_scope = make_scope(scope, :each)
469
- iter_expr = items.last
470
- binders = items[0..-2]
471
- walk_form(iter_expr, scope)
472
- binders.each { |b| bind_pattern(b, each_scope, :each_var) }
473
- body&.each { |form| walk_form(form, each_scope) }
403
+ walk_form(parsed.iter_expr, scope)
404
+ parsed.binding_pats.each { |b| bind_pattern(b, each_scope, :each_var) }
405
+ parsed.body.each { |form| walk_form(form, each_scope) }
474
406
  end
475
407
 
476
408
  def walk_accumulate(list, scope)
477
- bindings_vec = list.items[1]
478
- body = list.items[2..]
479
- return unless bindings_vec.is_a?(Vec)
409
+ parsed = Compiler::Language.parse_accumulate_form(list)
410
+ return unless parsed.bindings.is_a?(Vec)
480
411
 
481
- items = bindings_vec.items
482
- return if items.length < 4
412
+ return if parsed.items.length < 4
483
413
 
484
414
  acc_scope = make_scope(scope, :accumulate)
485
- acc_name = items[0]
486
- acc_init = items[1]
487
- iter_items = items[2..]
488
- iter_expr = iter_items.last
489
- binders = iter_items[0...-1]
490
- walk_form(acc_init, scope)
491
- bind_pattern(acc_name, acc_scope, :accumulator)
492
- walk_form(iter_expr, scope)
493
- binders.each { |b| bind_pattern(b, acc_scope, :each_var) }
494
- body&.each { |form| walk_form(form, acc_scope) }
415
+ walk_form(parsed.initial, scope)
416
+ bind_pattern(parsed.acc_name, acc_scope, :accumulator)
417
+ walk_form(parsed.iter_expr, scope)
418
+ parsed.binding_pats.each { |b| bind_pattern(b, acc_scope, :each_var) }
419
+ parsed.body.each { |form| walk_form(form, acc_scope) }
495
420
  end
496
421
 
497
422
  def walk_faccumulate(list, scope)
498
- bindings_vec = list.items[1]
499
- body = list.items[2..]
500
- return unless bindings_vec.is_a?(Vec)
423
+ parsed = Compiler::Language.parse_faccumulate_form(list)
424
+ return unless parsed.bindings.is_a?(Vec)
501
425
 
502
- items = bindings_vec.items
503
- return if items.length < 5
426
+ return if parsed.items.length < 5
504
427
 
505
428
  acc_scope = make_scope(scope, :faccumulate)
506
- acc_name = items[0]
507
- acc_init = items[1]
508
- counter = items[2]
509
- walk_form(acc_init, scope)
510
- items[3..]&.each { |form| walk_form(form, scope) }
511
- bind_pattern(acc_name, acc_scope, :accumulator)
512
- bind_pattern(counter, acc_scope, :for_counter)
513
- body&.each { |form| walk_form(form, acc_scope) }
429
+ walk_form(parsed.initial, scope)
430
+ [parsed.start, parsed.finish, parsed.step].compact.each { |form| walk_form(form, scope) }
431
+ bind_pattern(parsed.acc_name, acc_scope, :accumulator)
432
+ bind_pattern(parsed.counter, acc_scope, :for_counter)
433
+ parsed.body.each { |form| walk_form(form, acc_scope) }
514
434
  end
515
435
 
516
436
  def walk_case_match(list, scope)
517
- mode = list.head.name == 'match' ? :match : :case
518
- subject = list.items[1]
519
- arms = list.items[2..] || []
520
- walk_form(subject, scope)
521
- arms.each_slice(2) do |pattern, body|
437
+ mode = Compiler::Language.list_head_name(list) == 'match' ? :match : :case
438
+ parsed = Compiler::Language.parse_case_form(list)
439
+ walk_form(parsed.subject, scope)
440
+ parsed.arm_pairs.each do |pattern, body|
522
441
  arm_scope = make_scope(scope, :case_arm)
523
442
  walk_pattern(pattern, arm_scope, scope, mode)
524
443
  walk_form(body, arm_scope) if body
@@ -526,51 +445,39 @@ module Kapusta
526
445
  end
527
446
 
528
447
  def walk_try(list, scope)
529
- body = list.items[1]
530
- clauses = list.items[2..] || []
531
- walk_form(body, scope)
532
- clauses.each do |clause|
533
- next unless clause.is_a?(List)
534
-
535
- head = clause.head
536
- next unless head.is_a?(Sym)
537
-
538
- if head.name == 'catch'
539
- walk_catch(clause, scope)
540
- elsif head.name == 'finally'
541
- clause.items[1..]&.each { |form| walk_form(form, scope) }
448
+ parsed = Compiler::Language.parse_try_form(list)
449
+ walk_form(parsed.body, scope)
450
+ parsed.clauses.each do |clause|
451
+ case clause
452
+ when Compiler::Language::CatchClause then walk_catch(clause, scope)
453
+ when Compiler::Language::FinallyClause
454
+ clause.body.each { |form| walk_form(form, scope) }
542
455
  end
543
456
  end
544
457
  end
545
458
 
546
459
  def walk_catch(clause, scope)
547
- rest = clause.items[1..]
548
- if rest[0].is_a?(Sym) && (rest[0].name.match?(/\A[A-Z]/) || rest[0].dotted?)
549
- klass = rest[0]
550
- bind_sym = rest[1]
551
- body = rest[2..]
552
- walk_form(klass, scope)
553
- else
554
- bind_sym = rest[0]
555
- body = rest[1..]
556
- end
460
+ walk_form(clause.klass, scope) if clause.klass
557
461
  catch_scope = make_scope(scope, :catch)
558
- bind_pattern(bind_sym, catch_scope, :catch) if bind_sym.is_a?(Sym)
559
- body&.each { |form| walk_form(form, catch_scope) }
462
+ bind_pattern(clause.bind_sym, catch_scope, :catch) if clause.bind_sym.is_a?(Sym)
463
+ clause.body.each { |form| walk_form(form, catch_scope) }
560
464
  end
561
465
 
562
466
  def walk_module_class(list, scope)
563
- kind = list.head.name == 'module' ? :module : :class
564
- name_sym = list.items[1]
565
- body_start = 2
566
- if kind == :class && list.items[2].is_a?(Vec)
567
- list.items[2].items.each { |item| walk_form(item, scope) }
568
- body_start = 3
467
+ if (module_form = Compiler::Language.parse_module_form(list))
468
+ kind = :module
469
+ name_sym = module_form.name
470
+ body = module_form.body
471
+ else
472
+ kind = :class
473
+ parsed = Compiler::Language.parse_class_form(list)
474
+ name_sym = parsed.name
475
+ parsed.supers&.items&.each { |item| walk_form(item, scope) }
476
+ body = parsed.body
569
477
  end
570
478
 
571
479
  add_constant_binding(name_sym, scope, kind) if name_sym.is_a?(Sym)
572
480
 
573
- body = list.items[body_start..] || []
574
481
  body_scope = make_scope(scope, kind)
575
482
  if kind == :class
576
483
  inside_class { body.each { |form| walk_form(form, body_scope) } }
@@ -583,11 +490,17 @@ module Kapusta
583
490
  return if hashfn_synthetic?(sym.name)
584
491
  return if sym.is_a?(MacroSym) || sym.is_a?(AutoGensym)
585
492
 
586
- target_name = sym.dotted? ? sym.segments.first : sym.name
493
+ target_name = if sym.dotted?
494
+ sym.segments.first
495
+ elsif sym.colonized?
496
+ sym.colon_segments.first
497
+ else
498
+ sym.name
499
+ end
587
500
  return if target_name.nil? || target_name.empty?
588
501
 
589
502
  target = scope.lookup(target_name)
590
- return if target.nil? && Compiler::SPECIAL_FORMS.include?(sym.name)
503
+ return if target.nil? && Compiler::Language.special_form?(sym.name)
591
504
 
592
505
  add_reference(sym, scope, target)
593
506
  end
@@ -602,7 +515,7 @@ module Kapusta
602
515
  return if pattern.name == '_'
603
516
 
604
517
  add_binding(pattern, scope, kind)
605
- when Vec
518
+ when Vec, List
606
519
  bind_vec_pattern(pattern, scope, kind)
607
520
  when HashLit
608
521
  bind_hash_pattern(pattern, scope, kind)
@@ -670,16 +583,13 @@ module Kapusta
670
583
  end
671
584
 
672
585
  def walk_pattern_list(list, scope, outer_scope, mode)
673
- head = list.head
674
- if head.is_a?(Sym) && head.name == 'where'
675
- inner = list.items[1]
676
- guards = list.items[2..]
677
- walk_pattern(inner, scope, outer_scope, mode)
678
- guards&.each { |g| walk_form(g, scope) }
679
- elsif head.is_a?(Sym) && head.name == 'or'
680
- list.items[1..]&.each { |alt| walk_pattern(alt, scope, outer_scope, mode) }
681
- elsif head.is_a?(Sym) && head.name == '=' && list.items.length == 2
682
- name_sym = list.items[1]
586
+ if (where = Compiler::Language.parse_where_pattern(list))
587
+ walk_pattern(where.inner, scope, outer_scope, mode)
588
+ where.guards.each { |guard| walk_form(guard, scope) }
589
+ elsif (or_pattern = Compiler::Language.parse_or_pattern(list))
590
+ or_pattern.alternatives.each { |alt| walk_pattern(alt, scope, outer_scope, mode) }
591
+ elsif (pin = Compiler::Language.parse_pin_pattern(list))
592
+ name_sym = pin.name
683
593
  if name_sym.is_a?(Sym) && (existing = outer_scope.lookup(name_sym.name))
684
594
  add_reference(name_sym, outer_scope, existing)
685
595
  end