kapusta 0.13.2 → 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.
Files changed (100) 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/digit-tools.kap +24 -0
  16. data/examples/doto-hygiene.kap +2 -2
  17. data/examples/doto.kap +2 -2
  18. data/examples/egg-count.kap +1 -1
  19. data/examples/excel-column.kap +26 -0
  20. data/examples/exceptions.kap +1 -1
  21. data/examples/falling-drops.kap +7 -7
  22. data/examples/fennel-parity-examples.txt +3 -6
  23. data/examples/good-pairs.kap +18 -0
  24. data/examples/greet.kap +1 -1
  25. data/examples/happy-number.kap +2 -2
  26. data/examples/left-right-difference.kap +60 -0
  27. data/examples/length-of-last-word.kap +4 -4
  28. data/examples/logger-rate-limiter.kap +26 -0
  29. data/examples/manhattan-distance.kap +1 -1
  30. data/examples/maximum-subarray.kap +23 -7
  31. data/examples/minimum-start-value.kap +19 -0
  32. data/examples/move-zeroes.kap +2 -2
  33. data/examples/mruby-runtime-examples.txt +10 -2
  34. data/examples/non-constant-local.kap +1 -1
  35. data/examples/number-of-1-bits.kap +1 -1
  36. data/examples/number-of-steps.kap +1 -1
  37. data/examples/palindrome.kap +2 -2
  38. data/examples/pangram.kap +4 -4
  39. data/examples/pcall.kap +3 -3
  40. data/examples/pipeline.kap +4 -4
  41. data/examples/plus-one.kap +3 -3
  42. data/examples/raindrops.kap +1 -1
  43. data/examples/range-width.kap +14 -0
  44. data/examples/recent-counter.kap +6 -6
  45. data/examples/require-local-args.kap +9 -0
  46. data/examples/require-local.kap +7 -0
  47. data/examples/require-module-local.kap +4 -0
  48. data/examples/require-module.kap +4 -0
  49. data/examples/reverse-integer.kap +1 -1
  50. data/examples/roman-to-integer.kap +3 -3
  51. data/examples/running-sum.kap +20 -0
  52. data/examples/safe-lookup.kap +2 -2
  53. data/examples/single-number.kap +1 -1
  54. data/examples/stack.kap +14 -14
  55. data/examples/subtract-product-sum.kap +1 -1
  56. data/examples/summary-ranges.kap +45 -0
  57. data/examples/threading.kap +5 -5
  58. data/examples/tset.kap +1 -1
  59. data/examples/two-sum-hash.kap +3 -3
  60. data/examples/two-sum.kap +1 -1
  61. data/examples/ugly-number.kap +1 -1
  62. data/examples/underground-system.kap +39 -0
  63. data/examples/valid-parentheses-1.kap +6 -6
  64. data/exe/kapusta-ls +49 -2
  65. data/lib/kapusta/ast.rb +8 -0
  66. data/lib/kapusta/compiler/emitter/bindings.rb +111 -89
  67. data/lib/kapusta/compiler/emitter/collections.rb +32 -40
  68. data/lib/kapusta/compiler/emitter/control_flow.rb +33 -31
  69. data/lib/kapusta/compiler/emitter/expressions.rb +21 -5
  70. data/lib/kapusta/compiler/emitter/interop.rb +172 -48
  71. data/lib/kapusta/compiler/emitter/patterns.rb +12 -14
  72. data/lib/kapusta/compiler/emitter/support.rb +70 -81
  73. data/lib/kapusta/compiler/language.rb +533 -0
  74. data/lib/kapusta/compiler/lua_compat.rb +23 -28
  75. data/lib/kapusta/compiler/macro_expander.rb +30 -30
  76. data/lib/kapusta/compiler/macro_lowerer.rb +12 -24
  77. data/lib/kapusta/compiler/normalizer.rb +25 -17
  78. data/lib/kapusta/compiler.rb +3 -24
  79. data/lib/kapusta/env.rb +2 -2
  80. data/lib/kapusta/errors.rb +2 -1
  81. data/lib/kapusta/formatter/ast_helpers.rb +78 -0
  82. data/lib/kapusta/formatter/cli.rb +125 -0
  83. data/lib/kapusta/formatter/line_helpers.rb +44 -0
  84. data/lib/kapusta/formatter/validator.rb +32 -0
  85. data/lib/kapusta/formatter.rb +363 -325
  86. data/lib/kapusta/lsp/identifier.rb +1 -1
  87. data/lib/kapusta/lsp/rename.rb +21 -11
  88. data/lib/kapusta/lsp/scope_walker.rb +122 -212
  89. data/lib/kapusta/lsp/workspace_index.rb +17 -5
  90. data/lib/kapusta/reader.rb +4 -2
  91. data/lib/kapusta/version.rb +1 -1
  92. data/lib/kapusta.rb +39 -6
  93. data/spec/cli_spec.rb +13 -0
  94. data/spec/examples_errors_spec.rb +3 -1
  95. data/spec/examples_spec.rb +99 -15
  96. data/spec/formatter_spec.rb +246 -0
  97. data/spec/lsp_spec.rb +69 -0
  98. data/spec/require_spec.rb +294 -0
  99. metadata +23 -2
  100. data/examples/describe.kap +0 -9
@@ -0,0 +1,533 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kapusta
4
+ module Compiler
5
+ module Language
6
+ FunctionForm = Struct.new(:head, :name, :params, :body, :prefix_length, keyword_init: true) do
7
+ def named? = !name.nil?
8
+
9
+ def anonymous? = name.nil?
10
+ end
11
+ ModuleForm = Struct.new(:name, :body, :singleton, :prefix_length, keyword_init: true)
12
+ ClassForm = Struct.new(:name, :supers, :body, :prefix_length, keyword_init: true)
13
+ TryForm = Struct.new(:body, :clauses, keyword_init: true)
14
+ CatchClause = Struct.new(:klass, :bind_sym, :body, keyword_init: true)
15
+ FinallyClause = Struct.new(:body, keyword_init: true)
16
+ BindingForm = Struct.new(:head, :target, :value, keyword_init: true) do
17
+ def mutable? = head == 'var'
18
+ end
19
+ GlobalForm = Struct.new(:name, :value, keyword_init: true)
20
+ SetForm = Struct.new(:target, :value, keyword_init: true)
21
+ DotTarget = Struct.new(:object, :keys, keyword_init: true)
22
+ TsetForm = Struct.new(:table, :key, :value, keyword_init: true)
23
+ ConditionalBodyForm = Struct.new(:condition, :body, keyword_init: true) do
24
+ def body? = !body.empty?
25
+ end
26
+ ValuesForm = Struct.new(:key, :value, keyword_init: true)
27
+ LuaIteratorForm = Struct.new(:head, :collection, keyword_init: true) do
28
+ def name = head.name
29
+ end
30
+ LuaPcallForm = Struct.new(:callable, :args, keyword_init: true)
31
+ LuaXpcallForm = Struct.new(:callable, :handler, :args, keyword_init: true)
32
+ SigilForm = Struct.new(:head, :name, keyword_init: true) do
33
+ def kind = head.to_sym
34
+ end
35
+ MacroDefinitionForm = Struct.new(:name, :params, :body, keyword_init: true)
36
+ ImportMacrosForm = Struct.new(:destructure, :module_arg, keyword_init: true)
37
+ LetForm = Struct.new(:bindings, :body, keyword_init: true) do
38
+ def binding_pairs
39
+ return [] unless bindings.is_a?(Vec)
40
+
41
+ bindings.items.each_slice(2).map { |pattern, value| [pattern, value] }
42
+ end
43
+ end
44
+ CaseForm = Struct.new(:subject, :clauses, keyword_init: true) do
45
+ def arm_pairs
46
+ clauses.each_slice(2).to_a
47
+ end
48
+
49
+ def complete_arms
50
+ arm_pairs.select { |pair| pair.length == 2 }
51
+ end
52
+ end
53
+ WherePattern = Struct.new(:inner, :guards, keyword_init: true)
54
+ OrPattern = Struct.new(:alternatives, keyword_init: true)
55
+ PinPattern = Struct.new(:name, keyword_init: true)
56
+ UnpackCall = Struct.new(:value, keyword_init: true)
57
+ HashFnForm = Struct.new(:body, keyword_init: true)
58
+ IterationForm = Struct.new(:bindings, :body, keyword_init: true) do
59
+ def items = bindings.is_a?(Vec) ? bindings.items : []
60
+
61
+ def iter_expr = items.last
62
+
63
+ def binding_pats = items[0...-1] || []
64
+ end
65
+ AccumulateForm = Struct.new(:bindings, :body, keyword_init: true) do
66
+ def items = bindings.is_a?(Vec) ? bindings.items : []
67
+
68
+ def acc_name = items[0]
69
+
70
+ def initial = items[1]
71
+
72
+ def iter_items = items[2..] || []
73
+
74
+ def iter_expr = iter_items.last
75
+
76
+ def binding_pats = iter_items[0...-1] || []
77
+ end
78
+ FaccumulateForm = Struct.new(:bindings, :body, keyword_init: true) do
79
+ def items = bindings.is_a?(Vec) ? bindings.items : []
80
+
81
+ def acc_name = items[0]
82
+
83
+ def initial = items[1]
84
+
85
+ def counter = items[2]
86
+
87
+ def start = items[3]
88
+
89
+ def finish = items[4]
90
+
91
+ def step = items[5]
92
+ end
93
+ CountedForForm = Struct.new(:bindings, :body, keyword_init: true) do
94
+ def items = bindings.is_a?(Vec) ? bindings.items : []
95
+
96
+ def counter = items[0]
97
+
98
+ def start = items[1]
99
+
100
+ def finish = items[2]
101
+
102
+ def extras = items[3..] || []
103
+
104
+ def each_extra
105
+ i = 0
106
+ while i < extras.length
107
+ item = extras[i]
108
+ if item.is_a?(Sym) && item.name == '&until'
109
+ yield :until, extras[i + 1]
110
+ i += 2
111
+ else
112
+ yield :step, item
113
+ i += 1
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ FUNCTION_HEADS = %w[fn lambda λ].freeze
120
+ FUNCTION_DEFINITION_HEADS = (FUNCTION_HEADS + %w[defn]).freeze
121
+ MACRO_FUNCTION_HEADS = (FUNCTION_HEADS + %w[macro]).freeze
122
+ BINDING_HEADS = %w[local var].freeze
123
+ HEADER_HEADS = %w[class module].freeze
124
+ HEADER_SCOPES = %i[module class].freeze
125
+ DEFINITION_SCOPES = ([:toplevel] + HEADER_SCOPES).freeze
126
+ THREAD_HEADS = %w[-> ->> -?> -?>>].freeze
127
+ PIPELINE_HEADS = (THREAD_HEADS + %w[doto]).freeze
128
+ SHORT_PIPELINE_HEADS = %w[-?> -?>>].freeze
129
+ THREAD_FIRST_HEADS = %w[-> -?>].freeze
130
+ SEQUENCE_STATEMENT_HEADS = %w[let while for each case match].freeze
131
+ MULTILINE_BODY_HEADS = %w[
132
+ if case match let try catch finally do for -> ->> -?> -?>> doto
133
+ fn lambda λ macro
134
+ ].freeze
135
+ FLAT_BODY_HEADS = %w[
136
+ fn lambda λ macro when unless for each icollect collect fcollect
137
+ accumulate faccumulate
138
+ ].freeze
139
+ NEVER_FLAT_HEADS = %w[let case match try catch finally do -> ->> -?> -?>> doto].freeze
140
+ QUASI_HEADS = %w[
141
+ quasi-sym quasi-list quasi-list-tail quasi-vec quasi-vec-tail
142
+ quasi-hash quasi-gensym
143
+ ].freeze
144
+ CORE_SPECIAL_FORMS = %w[
145
+ fn defn lambda λ let local var global set if when unless case match
146
+ while for each do values
147
+ -> ->> -?> -?>> doto
148
+ icollect collect fcollect accumulate faccumulate
149
+ hashfn
150
+ . ?: :
151
+ ..
152
+ length
153
+ require
154
+ module class end
155
+ try catch finally
156
+ raise
157
+ ivar cvar gvar
158
+ ruby
159
+ tset
160
+ and or not
161
+ = not= < <= > >=
162
+ + - * / %
163
+ print
164
+ macro macros import-macros
165
+ quasi-sym quasi-list quasi-list-tail quasi-vec quasi-vec-tail quasi-hash quasi-gensym
166
+ ].freeze
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
174
+
175
+ module_function
176
+
177
+ def list_head(form)
178
+ return unless form.is_a?(List) && !form.empty?
179
+
180
+ form.head
181
+ end
182
+
183
+ def list_head_name(form)
184
+ head = list_head(form)
185
+ head.name if head.is_a?(Sym)
186
+ end
187
+
188
+ def list_head?(form, *names)
189
+ name = list_head_name(form)
190
+ !name.nil? && names.flatten.include?(name)
191
+ end
192
+
193
+ def end_form?(form) = list_head?(form, 'end')
194
+
195
+ def do_form?(form) = list_head?(form, 'do')
196
+
197
+ def defn_form?(form) = list_head?(form, 'defn')
198
+
199
+ def header_form?(form)
200
+ name = list_head_name(form)
201
+ !name.nil? && header_head?(name)
202
+ end
203
+
204
+ def sequence_statement_form?(form)
205
+ name = list_head_name(form)
206
+ !name.nil? && sequence_statement_head?(name)
207
+ end
208
+
209
+ def binding_form?(form)
210
+ name = list_head_name(form)
211
+ !name.nil? && binding_head?(name)
212
+ end
213
+
214
+ def special_form?(name) = SPECIAL_FORMS.include?(name)
215
+
216
+ def class_body_declaration?(name) = CLASS_BODY_DECLARATIONS.include?(name)
217
+
218
+ def function_head?(name) = FUNCTION_HEADS.include?(name)
219
+
220
+ def function_definition_head?(name) = FUNCTION_DEFINITION_HEADS.include?(name)
221
+
222
+ def macro_function_head?(name) = MACRO_FUNCTION_HEADS.include?(name)
223
+
224
+ def binding_head?(name) = BINDING_HEADS.include?(name)
225
+
226
+ def header_head?(name) = HEADER_HEADS.include?(name)
227
+
228
+ def header_scope?(scope) = HEADER_SCOPES.include?(scope)
229
+
230
+ def definition_scope?(scope) = DEFINITION_SCOPES.include?(scope)
231
+
232
+ def pipeline_head?(name) = PIPELINE_HEADS.include?(name)
233
+
234
+ def short_pipeline_head?(name) = SHORT_PIPELINE_HEADS.include?(name)
235
+
236
+ def thread_first_head?(name) = THREAD_FIRST_HEADS.include?(name)
237
+
238
+ def sequence_statement_head?(name) = SEQUENCE_STATEMENT_HEADS.include?(name)
239
+
240
+ def multiline_body_head?(name) = MULTILINE_BODY_HEADS.include?(name)
241
+
242
+ def flat_body_head?(name) = FLAT_BODY_HEADS.include?(name)
243
+
244
+ def never_flat_head?(name) = NEVER_FLAT_HEADS.include?(name)
245
+
246
+ def quasi_head?(name) = QUASI_HEADS.include?(name)
247
+
248
+ def bodyless_header?(form)
249
+ return false unless header_form?(form)
250
+
251
+ case form.head.name
252
+ when 'module'
253
+ parsed = parse_module_form(form)
254
+ parsed.body.empty? || (parsed.body.length == 1 && bodyless_header?(parsed.body[0]))
255
+ when 'class'
256
+ parse_class_form(form).body.empty?
257
+ else
258
+ false
259
+ end
260
+ end
261
+
262
+ def function_form?(form, heads: FUNCTION_HEADS)
263
+ !parse_function_form(form, heads:).nil?
264
+ end
265
+
266
+ def parse_function_form(form, heads: FUNCTION_HEADS)
267
+ return unless list_head?(form, heads)
268
+
269
+ parse_function_args(form.rest, head: form.head)
270
+ end
271
+
272
+ def parse_function_args(args, head: nil)
273
+ if args[0].is_a?(Vec)
274
+ FunctionForm.new(head:, name: nil, params: args[0], body: args[1..] || [], prefix_length: 1)
275
+ elsif args[0].is_a?(Sym) && args[1].is_a?(Vec)
276
+ FunctionForm.new(head:, name: args[0], params: args[1], body: args[2..] || [], prefix_length: 2)
277
+ end
278
+ end
279
+
280
+ def parse_class_form(form)
281
+ return unless list_head?(form, 'class')
282
+
283
+ parse_class_args(form.rest)
284
+ end
285
+
286
+ def parse_module_form(form)
287
+ return unless list_head?(form, 'module')
288
+
289
+ parse_module_args(form.rest)
290
+ end
291
+
292
+ def parse_module_args(args)
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)
297
+ end
298
+
299
+ def parse_class_args(args)
300
+ if args[1].is_a?(Vec)
301
+ ClassForm.new(name: args[0], supers: args[1], body: args[2..] || [], prefix_length: 2)
302
+ else
303
+ ClassForm.new(name: args[0], supers: nil, body: args[1..] || [], prefix_length: 1)
304
+ end
305
+ end
306
+
307
+ def parse_try_args(args)
308
+ TryForm.new(body: args[0], clauses: (args[1..] || []).filter_map { |clause| parse_try_clause(clause) })
309
+ end
310
+
311
+ def parse_try_clause(clause)
312
+ return unless list_head_name(clause)
313
+
314
+ case clause.head.name
315
+ when 'catch' then parse_catch_clause(clause.rest)
316
+ when 'finally' then FinallyClause.new(body: clause.rest)
317
+ end
318
+ end
319
+
320
+ def parse_catch_clause(args)
321
+ if exception_class_form?(args[0])
322
+ CatchClause.new(klass: args[0], bind_sym: args[1], body: args[2..] || [])
323
+ else
324
+ CatchClause.new(klass: nil, bind_sym: args[0], body: args[1..] || [])
325
+ end
326
+ end
327
+
328
+ def exception_class_form?(form)
329
+ form.is_a?(Sym) && (form.name.match?(/\A[A-Z]/) || form.dotted?)
330
+ end
331
+
332
+ def parse_binding_form(form)
333
+ return unless binding_form?(form)
334
+
335
+ parse_binding_args(form.head.name, form.rest)
336
+ end
337
+
338
+ def parse_binding_args(head, args)
339
+ return unless args.length == 2
340
+
341
+ BindingForm.new(head:, target: args[0], value: args[1])
342
+ end
343
+
344
+ def parse_global_args(args)
345
+ return unless args.length == 2
346
+
347
+ GlobalForm.new(name: args[0], value: args[1])
348
+ end
349
+
350
+ def parse_global_form(form)
351
+ return unless list_head?(form, 'global')
352
+
353
+ parse_global_args(form.rest)
354
+ end
355
+
356
+ def parse_set_form(form)
357
+ return unless list_head?(form, 'set')
358
+
359
+ parse_set_args(form.rest)
360
+ end
361
+
362
+ def parse_set_args(args)
363
+ SetForm.new(target: args[0], value: args[1])
364
+ end
365
+
366
+ def parse_dot_target(form)
367
+ return unless list_head?(form, ':')
368
+
369
+ DotTarget.new(object: form.items[1], keys: form.items[2..] || [])
370
+ end
371
+
372
+ def parse_tset_args(args)
373
+ return unless args.length >= 3
374
+
375
+ TsetForm.new(table: args[0], key: args[1], value: args[2])
376
+ end
377
+
378
+ def parse_conditional_body_args(args)
379
+ ConditionalBodyForm.new(condition: args[0], body: args[1..] || [])
380
+ end
381
+
382
+ def parse_values_form(form)
383
+ return unless list_head?(form, 'values') && form.items.length == 3
384
+
385
+ ValuesForm.new(key: form.items[1], value: form.items[2])
386
+ end
387
+
388
+ def parse_lua_iterator_form(form)
389
+ head = list_head(form)
390
+ return unless head.is_a?(Sym) && LuaCompat.iterator_form?(head.name)
391
+
392
+ LuaIteratorForm.new(head:, collection: form.items[1])
393
+ end
394
+
395
+ def parse_lua_pcall_args(args)
396
+ LuaPcallForm.new(callable: args[0], args: args[1..] || [])
397
+ end
398
+
399
+ def parse_lua_xpcall_args(args)
400
+ LuaXpcallForm.new(callable: args[0], handler: args[1], args: args[2..] || [])
401
+ end
402
+
403
+ def parse_sigil_form(form)
404
+ head = list_head_name(form)
405
+ return unless head
406
+
407
+ parse_sigil_args(head, form.rest)
408
+ end
409
+
410
+ def parse_sigil_args(head, args)
411
+ return unless %w[ivar cvar gvar].include?(head)
412
+
413
+ SigilForm.new(head:, name: args[0])
414
+ end
415
+
416
+ def parse_hashfn_form(form)
417
+ return unless list_head?(form, 'hashfn')
418
+
419
+ HashFnForm.new(body: form.rest)
420
+ end
421
+
422
+ def parse_macro_definition_args(args)
423
+ MacroDefinitionForm.new(name: args[0], params: args[1], body: args[2..] || [])
424
+ end
425
+
426
+ def parse_macro_definition_form(form)
427
+ return unless list_head?(form, 'macro')
428
+
429
+ parse_macro_definition_args(form.rest)
430
+ end
431
+
432
+ def parse_import_macros_args(args)
433
+ ImportMacrosForm.new(destructure: args[0], module_arg: args[1])
434
+ end
435
+
436
+ def parse_import_macros_form(form)
437
+ return unless list_head?(form, 'import-macros')
438
+
439
+ parse_import_macros_args(form.rest)
440
+ end
441
+
442
+ def parse_let_args(args)
443
+ LetForm.new(bindings: args[0], body: args[1..] || [])
444
+ end
445
+
446
+ def parse_let_form(form)
447
+ return unless list_head?(form, 'let')
448
+
449
+ parse_let_args(form.rest)
450
+ end
451
+
452
+ def parse_case_args(args)
453
+ CaseForm.new(subject: args[0], clauses: args[1..] || [])
454
+ end
455
+
456
+ def parse_case_form(form)
457
+ return unless list_head?(form, %w[case match])
458
+
459
+ parse_case_args(form.rest)
460
+ end
461
+
462
+ def parse_try_form(form)
463
+ return unless list_head?(form, 'try')
464
+
465
+ parse_try_args(form.rest)
466
+ end
467
+
468
+ def parse_where_pattern(pattern)
469
+ return unless list_head?(pattern, 'where')
470
+
471
+ WherePattern.new(inner: pattern.items[1], guards: pattern.items[2..] || [])
472
+ end
473
+
474
+ def parse_or_pattern(pattern)
475
+ return unless list_head?(pattern, 'or')
476
+
477
+ OrPattern.new(alternatives: pattern.items[1..] || [])
478
+ end
479
+
480
+ def parse_pin_pattern(pattern)
481
+ return unless list_head?(pattern, '=') && pattern.items.length == 2
482
+
483
+ PinPattern.new(name: pattern.items[1])
484
+ end
485
+
486
+ def parse_unpack_call(form)
487
+ return unless list_head?(form, 'unpack')
488
+
489
+ UnpackCall.new(value: form.items[1])
490
+ end
491
+
492
+ def parse_iteration_args(args)
493
+ IterationForm.new(bindings: args[0], body: args[1..] || [])
494
+ end
495
+
496
+ def parse_iteration_form(form)
497
+ return unless list_head?(form, %w[each collect icollect])
498
+
499
+ parse_iteration_args(form.rest)
500
+ end
501
+
502
+ def parse_accumulate_args(args)
503
+ AccumulateForm.new(bindings: args[0], body: args[1..] || [])
504
+ end
505
+
506
+ def parse_accumulate_form(form)
507
+ return unless list_head?(form, 'accumulate')
508
+
509
+ parse_accumulate_args(form.rest)
510
+ end
511
+
512
+ def parse_faccumulate_args(args)
513
+ FaccumulateForm.new(bindings: args[0], body: args[1..] || [])
514
+ end
515
+
516
+ def parse_faccumulate_form(form)
517
+ return unless list_head?(form, 'faccumulate')
518
+
519
+ parse_faccumulate_args(form.rest)
520
+ end
521
+
522
+ def parse_counted_for_args(args)
523
+ CountedForForm.new(bindings: args[0], body: args[1..] || [])
524
+ end
525
+
526
+ def parse_counted_for_form(form)
527
+ return unless list_head?(form, %w[for fcollect])
528
+
529
+ parse_counted_for_args(form.rest)
530
+ end
531
+ end
532
+ end
533
+ end
@@ -25,25 +25,22 @@ module Kapusta
25
25
  end
26
26
 
27
27
  def normalize_lua_pcall(items)
28
- fn = items[1]
29
- args = items[2..]
28
+ parsed = Language.parse_lua_pcall_args(items[1..] || [])
30
29
  List.new([
31
30
  Sym.new('try'),
32
- List.new([Sym.new('values'), true, List.new([fn, *args])]),
31
+ List.new([Sym.new('values'), true, List.new([parsed.callable, *parsed.args])]),
33
32
  List.new([Sym.new('catch'), Sym.new('StandardError'), Sym.new('e'),
34
33
  List.new([Sym.new('values'), false, Sym.new('e')])])
35
34
  ])
36
35
  end
37
36
 
38
37
  def normalize_lua_xpcall(items)
39
- fn = items[1]
40
- handler = items[2]
41
- args = items[3..]
38
+ parsed = Language.parse_lua_xpcall_args(items[1..] || [])
42
39
  List.new([
43
40
  Sym.new('try'),
44
- List.new([Sym.new('values'), true, List.new([fn, *args])]),
41
+ List.new([Sym.new('values'), true, List.new([parsed.callable, *parsed.args])]),
45
42
  List.new([Sym.new('catch'), Sym.new('StandardError'), Sym.new('e'),
46
- List.new([Sym.new('values'), false, List.new([handler, Sym.new('e')])])])
43
+ List.new([Sym.new('values'), false, List.new([parsed.handler, Sym.new('e')])])])
47
44
  ])
48
45
  end
49
46
  end
@@ -53,39 +50,37 @@ module Kapusta
53
50
 
54
51
  def emit_lua_compat_inject(iter_expr, binding_pats, body_env, env, current_scope, acc_var,
55
52
  init_code, body_forms)
56
- return unless lua_iterator_expr?(iter_expr)
53
+ parsed = Language.parse_lua_iterator_form(iter_expr)
54
+ return unless parsed
57
55
 
58
- case iter_expr.head.name
56
+ case parsed.name
59
57
  when 'ipairs'
60
- emit_lua_ipairs_inject(iter_expr, binding_pats, body_env, env, current_scope,
58
+ emit_lua_ipairs_inject(parsed, binding_pats, body_env, env, current_scope,
61
59
  acc_var, init_code, body_forms)
62
60
  when 'pairs'
63
- emit_lua_pairs_inject(iter_expr, binding_pats, body_env, env, current_scope,
61
+ emit_lua_pairs_inject(parsed, binding_pats, body_env, env, current_scope,
64
62
  acc_var, init_code, body_forms)
65
63
  end
66
64
  end
67
65
 
68
66
  def emit_lua_compat_iteration(iter_expr, binding_pats, env, current_scope, method:,
69
67
  extra_block_param: nil, &block)
70
- return unless lua_iterator_expr?(iter_expr)
68
+ parsed = Language.parse_lua_iterator_form(iter_expr)
69
+ return unless parsed
71
70
 
72
- case iter_expr.head.name
71
+ case parsed.name
73
72
  when 'ipairs'
74
- emit_lua_ipairs_iteration(iter_expr, binding_pats, env, current_scope,
73
+ emit_lua_ipairs_iteration(parsed, binding_pats, env, current_scope,
75
74
  method:, extra_block_param:, &block)
76
75
  when 'pairs'
77
- emit_lua_pairs_iteration(iter_expr, binding_pats, env, current_scope,
76
+ emit_lua_pairs_iteration(parsed, binding_pats, env, current_scope,
78
77
  method:, extra_block_param:, &block)
79
78
  end
80
79
  end
81
80
 
82
- def lua_iterator_expr?(expr)
83
- expr.is_a?(List) && expr.head.is_a?(Sym) && LuaCompat.iterator_form?(expr.head.name)
84
- end
85
-
86
- def emit_lua_ipairs_inject(iter_expr, binding_pats, body_env, env, current_scope, acc_var,
81
+ def emit_lua_ipairs_inject(iter_form, binding_pats, body_env, env, current_scope, acc_var,
87
82
  init_code, body_forms)
88
- coll_code = emit_expr(iter_expr.items[1], env, current_scope)
83
+ coll_code = emit_expr(iter_form.collection, env, current_scope)
89
84
  value_var, value_bind = bind_iteration_param(binding_pats[1], 'value', body_env)
90
85
  if ignored_pattern?(binding_pats[0])
91
86
  body_code, = emit_sequence(body_forms, body_env, current_scope, allow_method_definitions: false)
@@ -99,22 +94,22 @@ module Kapusta
99
94
  init_code, bind_code, body_code)
100
95
  end
101
96
 
102
- def emit_lua_pairs_inject(iter_expr, binding_pats, body_env, env, current_scope, acc_var,
97
+ def emit_lua_pairs_inject(iter_form, binding_pats, body_env, env, current_scope, acc_var,
103
98
  init_code, body_forms)
104
99
  key_var, key_bind = bind_iteration_param(binding_pats[0], 'key', body_env)
105
100
  value_var, value_bind = bind_iteration_param(binding_pats[1], 'value', body_env)
106
101
  bind_code = [key_bind, value_bind].compact.join("\n")
107
102
  body_code, = emit_sequence(body_forms, body_env, current_scope, allow_method_definitions: false)
108
- coll_code = emit_expr(iter_expr.items[1], env, current_scope)
103
+ coll_code = emit_expr(iter_form.collection, env, current_scope)
109
104
  inject_block(coll_code, "#{acc_var}, (#{key_var}, #{value_var})",
110
105
  init_code, bind_code, body_code)
111
106
  end
112
107
 
113
- def emit_lua_ipairs_iteration(iter_expr, binding_pats, env, current_scope, method:,
108
+ def emit_lua_ipairs_iteration(iter_form, binding_pats, env, current_scope, method:,
114
109
  extra_block_param: nil, &block)
115
110
  body_env = env.child
116
111
  value_var, value_bind = bind_iteration_param(binding_pats[1], 'value', body_env)
117
- coll_code = emit_expr(iter_expr.items[1], env, current_scope)
112
+ coll_code = emit_expr(iter_form.collection, env, current_scope)
118
113
  if ignored_pattern?(binding_pats[0])
119
114
  bind_code = value_bind || ''
120
115
  body_code = block.call(body_env)
@@ -131,14 +126,14 @@ module Kapusta
131
126
  iteration_block("#{receiver} do |#{params}|", bind_code, body_code)
132
127
  end
133
128
 
134
- def emit_lua_pairs_iteration(iter_expr, binding_pats, env, current_scope, method:,
129
+ def emit_lua_pairs_iteration(iter_form, binding_pats, env, current_scope, method:,
135
130
  extra_block_param: nil, &block)
136
131
  body_env = env.child
137
132
  key_var, key_bind = bind_iteration_param(binding_pats[0], 'key', body_env)
138
133
  value_var, value_bind = bind_iteration_param(binding_pats[1], 'value', body_env)
139
134
  bind_code = [key_bind, value_bind].compact.join("\n")
140
135
  body_code = block.call(body_env)
141
- coll_code = emit_expr(iter_expr.items[1], env, current_scope)
136
+ coll_code = emit_expr(iter_form.collection, env, current_scope)
142
137
  inner_params = "#{key_var}, #{value_var}"
143
138
  params = extra_block_param ? "(#{inner_params}), #{extra_block_param}" : inner_params
144
139
  iteration_block("#{coll_code}.#{method} do |#{params}|", bind_code, body_code)