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
@@ -0,0 +1,294 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'fileutils'
5
+ require 'tmpdir'
6
+
7
+ RSpec.describe 'Kapusta require' do
8
+ it 'returns cached final values from .kap relative requires resolved from the current .kap file' do
9
+ Dir.mktmpdir('kapusta-require-local') do |dir|
10
+ app_dir = File.join(dir, 'app')
11
+ other_dir = File.join(dir, 'other')
12
+ FileUtils.mkdir_p([app_dir, other_dir])
13
+ counter_path = File.join(dir, 'loads.txt')
14
+
15
+ File.write(File.join(app_dir, 'args.kap'), <<~KAP)
16
+ (ruby #{"File.write(#{counter_path.inspect}, 'x', mode: 'a')".inspect})
17
+
18
+ (fn parse [argv]
19
+ (argv.join ","))
20
+
21
+ {: parse}
22
+ KAP
23
+
24
+ File.write(File.join(app_dir, 'main.kap'), <<~KAP)
25
+ (local args (require "./args"))
26
+ (local again (require "./args"))
27
+
28
+ [((: args :parse) ["alpha" "beta"])
29
+ ((: again :parse) ["gamma"])]
30
+ KAP
31
+
32
+ result = Dir.chdir(other_dir) do
33
+ Kapusta.dofile(File.join(app_dir, 'main.kap'))
34
+ end
35
+
36
+ expect(result).to eq(['alpha,beta', 'gamma'])
37
+ expect(File.read(counter_path)).to eq('x')
38
+ end
39
+ end
40
+
41
+ it 'calls functions on required .kap module maps with colon syntax' do
42
+ Dir.mktmpdir('kapusta-require-local-map-call') do |dir|
43
+ File.write(File.join(dir, 'probe.kap'), <<~KAP)
44
+ (fn parse-size [output]
45
+ (.. "size:" output))
46
+
47
+ {: parse-size}
48
+ KAP
49
+
50
+ File.write(File.join(dir, 'main.kap'), <<~KAP)
51
+ (local probe (require "./probe"))
52
+
53
+ (probe:parse-size "42 120")
54
+ KAP
55
+
56
+ expect(Kapusta.dofile(File.join(dir, 'main.kap'))).to eq('size:42 120')
57
+ end
58
+ end
59
+
60
+ it 'keeps dotted calls on required .kap module maps as method calls' do
61
+ Dir.mktmpdir('kapusta-require-local-map-dotted-call') do |dir|
62
+ File.write(File.join(dir, 'probe.kap'), <<~KAP)
63
+ (fn parse-size [output]
64
+ (.. "size:" output))
65
+
66
+ {: parse-size}
67
+ KAP
68
+
69
+ main_path = File.join(dir, 'main.kap')
70
+ File.write(main_path, <<~KAP)
71
+ (local probe (require "./probe"))
72
+
73
+ (probe.parse-size "42 120")
74
+ KAP
75
+
76
+ expect(Kapusta.compile(File.read(main_path), path: main_path)).to eq(<<~RUBY)
77
+ probe = require_relative "probe"
78
+ probe.parse_size("42 120")
79
+ RUBY
80
+ end
81
+ end
82
+
83
+ it 'compiles local aliases for .kap module requires to plain Ruby require and constant assignment' do
84
+ Dir.mktmpdir('kapusta-require-module-local-alias') do |dir|
85
+ app_dir = File.join(dir, 'app')
86
+ FileUtils.mkdir_p(app_dir)
87
+ File.write(File.join(app_dir, 'search.kap'), <<~KAP)
88
+ (module App.Search)
89
+
90
+ (defn active? [state]
91
+ true)
92
+
93
+ (end)
94
+ KAP
95
+ main_path = File.join(dir, 'main.kap')
96
+ File.write(main_path, <<~KAP)
97
+ (local search (require :app.search))
98
+ (search.active? {})
99
+ KAP
100
+
101
+ expect(Kapusta.compile(File.read(main_path), path: main_path)).to eq(<<~RUBY)
102
+ require_relative "app/search"
103
+ search = App::Search
104
+ search.active?({})
105
+ RUBY
106
+ end
107
+ end
108
+
109
+ it 'keeps nested module require aliases isolated when names repeat' do
110
+ Dir.mktmpdir('kapusta-require-module-nested-alias') do |dir|
111
+ app_dir = File.join(dir, 'app')
112
+ FileUtils.mkdir_p(app_dir)
113
+
114
+ File.write(File.join(app_dir, 'base.kap'), <<~KAP)
115
+ (module App.Base)
116
+
117
+ (defn value []
118
+ "base")
119
+
120
+ (end)
121
+ KAP
122
+
123
+ File.write(File.join(app_dir, 'inner.kap'), <<~KAP)
124
+ (module App.Inner)
125
+
126
+ (local mod (require "./base"))
127
+
128
+ (defn value []
129
+ (.. "inner:" (mod.value)))
130
+
131
+ (end)
132
+ KAP
133
+
134
+ File.write(File.join(app_dir, 'outer.kap'), <<~KAP)
135
+ (module App.Outer)
136
+
137
+ (local mod (require "./inner"))
138
+
139
+ (defn value []
140
+ (.. "outer:" (mod.value)))
141
+
142
+ (end)
143
+ KAP
144
+
145
+ main_path = File.join(dir, 'main.kap')
146
+ File.write(main_path, <<~KAP)
147
+ (local mod (require :app.outer))
148
+ (mod.value)
149
+ KAP
150
+
151
+ expect(Kapusta.dofile(main_path)).to eq('outer:inner:base')
152
+ end
153
+ end
154
+
155
+ it 'compiles class instances created after relative requires' do
156
+ Dir.mktmpdir('kapusta-require-class-instance') do |dir|
157
+ app_dir = File.join(dir, 'app')
158
+ platform_dir = File.join(dir, 'platform')
159
+ FileUtils.mkdir_p([app_dir, platform_dir])
160
+
161
+ File.write(File.join(platform_dir, 'browser.kap'), <<~KAP)
162
+ (class RequireClassInstance.Browser)
163
+
164
+ (fn open [url]
165
+ url)
166
+
167
+ (end)
168
+ KAP
169
+
170
+ commands_path = File.join(app_dir, 'commands.kap')
171
+ File.write(commands_path, <<~KAP)
172
+ (class App.Commands)
173
+
174
+ (fn open [url]
175
+ (let [browser (do (require "../platform/browser") (RequireClassInstance.Browser.new))]
176
+ (browser.open url)))
177
+
178
+ (end)
179
+ KAP
180
+
181
+ expect(Kapusta.compile(File.read(commands_path), path: commands_path)).to eq(<<~RUBY)
182
+ module App
183
+ class Commands
184
+ def open(url)
185
+ browser = begin
186
+ require_relative "../platform/browser"
187
+ RequireClassInstance::Browser.new
188
+ end
189
+ browser.open(url)
190
+ end
191
+ end
192
+ end
193
+ RUBY
194
+ end
195
+ end
196
+
197
+ it 'keeps class instances isolated when required modules repeat local names' do
198
+ Dir.mktmpdir('kapusta-require-class-instance-isolation') do |dir|
199
+ app_dir = File.join(dir, 'app')
200
+ platform_dir = File.join(dir, 'platform')
201
+ FileUtils.mkdir_p([app_dir, platform_dir])
202
+
203
+ File.write(File.join(platform_dir, 'browser.kap'), <<~KAP)
204
+ (class RequireClassInstance.Browser)
205
+
206
+ (fn initialize [name]
207
+ (set @name name))
208
+
209
+ (fn open [url]
210
+ (.. @name ":" url))
211
+
212
+ (end)
213
+ KAP
214
+
215
+ File.write(File.join(app_dir, 'inner.kap'), <<~KAP)
216
+ (module App.Inner)
217
+
218
+ (defn open [url]
219
+ (let [browser (do (require "../platform/browser")
220
+ (RequireClassInstance.Browser.new "inner-browser"))]
221
+ (browser.open (.. "inner:" url))))
222
+
223
+ (end)
224
+ KAP
225
+
226
+ File.write(File.join(app_dir, 'outer.kap'), <<~KAP)
227
+ (module App.Outer)
228
+
229
+ (require "./inner")
230
+
231
+ (defn open [url]
232
+ (let [browser (do (require "../platform/browser")
233
+ (RequireClassInstance.Browser.new "outer-browser"))]
234
+ (.. (browser.open (.. "outer:" url)) "|" (App.Inner.open url))))
235
+
236
+ (end)
237
+ KAP
238
+
239
+ main_path = File.join(dir, 'main.kap')
240
+ File.write(main_path, <<~KAP)
241
+ (require :app.outer)
242
+ (App.Outer.open "x")
243
+ KAP
244
+
245
+ expect(Kapusta.dofile(main_path)).to eq('outer-browser:outer:x|inner-browser:inner:x')
246
+ end
247
+ end
248
+
249
+ it 'delegates relative requires to Ruby for .rb files' do
250
+ Dir.mktmpdir('kapusta-require-local-ruby') do |dir|
251
+ mod_name = "KapustaRequireRelativeRubyFeature#{rand(1_000_000)}"
252
+ File.write(File.join(dir, 'feature.rb'), <<~RUBY)
253
+ module #{mod_name}
254
+ VALUE = 7
255
+ end
256
+ RUBY
257
+ File.write(File.join(dir, 'main.kap'), <<~KAP)
258
+ (require "./feature")
259
+ (ruby "#{mod_name}::VALUE")
260
+ KAP
261
+
262
+ expect(Kapusta.dofile(File.join(dir, 'main.kap'))).to eq(7)
263
+ end
264
+ end
265
+
266
+ it 'returns cached final values from .kap module-path requires' do
267
+ Dir.mktmpdir('kapusta-require-module') do |dir|
268
+ app_dir = File.join(dir, 'app')
269
+ FileUtils.mkdir_p(app_dir)
270
+ counter_path = File.join(dir, 'loads.txt')
271
+
272
+ File.write(File.join(app_dir, 'args.kap'), <<~KAP)
273
+ (ruby #{"File.write(#{counter_path.inspect}, 'x', mode: 'a')".inspect})
274
+
275
+ (fn parse [argv]
276
+ (argv.join ":"))
277
+
278
+ {: parse}
279
+ KAP
280
+
281
+ begin
282
+ $LOAD_PATH.unshift(dir)
283
+ first = Kapusta.require(:'app.args')
284
+ second = Kapusta.require(:'app.args')
285
+
286
+ expect(first[:parse].call(%w[left right])).to eq('left:right')
287
+ expect(second).to equal(first)
288
+ expect(File.read(counter_path)).to eq('x')
289
+ ensure
290
+ $LOAD_PATH.delete(dir)
291
+ end
292
+ end
293
+ end
294
+ 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.13.2
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgenii Morozov
@@ -31,6 +31,7 @@ files:
31
31
  - examples/ackermann.kap
32
32
  - examples/anagram.kap
33
33
  - examples/anonymous-greeter.kap
34
+ - examples/app/args.kap
34
35
  - examples/arrange-coins.kap
35
36
  - examples/array-sign.kap
36
37
  - examples/bank-account.kap
@@ -49,8 +50,8 @@ files:
49
50
  - examples/contains-duplicate.kap
50
51
  - examples/convert-temperature.kap
51
52
  - examples/count-effects.kap
53
+ - examples/count-items-matching-rule.kap
52
54
  - examples/counter.kap
53
- - examples/describe.kap
54
55
  - examples/destructure.kap
55
56
  - examples/divisibility-stats.kap
56
57
  - examples/doto-hygiene.kap
@@ -66,6 +67,7 @@ files:
66
67
  - examples/files.kap
67
68
  - examples/fizzbuzz.kap
68
69
  - examples/gcd.kap
70
+ - examples/good-pairs.kap
69
71
  - examples/greet.kap
70
72
  - examples/happy-number.kap
71
73
  - examples/hashfn.kap
@@ -73,6 +75,7 @@ files:
73
75
  - examples/import-helpers.kapm
74
76
  - examples/kwargs.kap
75
77
  - examples/leap-year.kap
78
+ - examples/left-right-difference.kap
76
79
  - examples/length-of-last-word.kap
77
80
  - examples/macros-dbg.kap
78
81
  - examples/macros-import-helpers.kap
@@ -89,6 +92,7 @@ files:
89
92
  - examples/max-achievable.kap
90
93
  - examples/maximum-subarray.kap
91
94
  - examples/min-max.kap
95
+ - examples/minimum-start-value.kap
92
96
  - examples/module-header.kap
93
97
  - examples/move-zeroes.kap
94
98
  - examples/mruby-runtime-examples.txt
@@ -109,12 +113,18 @@ files:
109
113
  - examples/power-of-three.kap
110
114
  - examples/primes.kap
111
115
  - examples/raindrops.kap
116
+ - examples/range-width.kap
112
117
  - examples/recent-counter.kap
113
118
  - examples/record.kap
114
119
  - examples/regex.kap
120
+ - examples/require-local-args.kap
121
+ - examples/require-local.kap
122
+ - examples/require-module-local.kap
123
+ - examples/require-module.kap
115
124
  - examples/reverse-integer.kap
116
125
  - examples/roman-to-integer.kap
117
126
  - examples/ruby-eval.kap
127
+ - examples/running-sum.kap
118
128
  - examples/safe-lookup.kap
119
129
  - examples/scopes.kap
120
130
  - examples/shapes.kap
@@ -125,6 +135,7 @@ files:
125
135
  - examples/stack.kap
126
136
  - examples/subtract-product-sum.kap
127
137
  - examples/sum.kap
138
+ - examples/summary-ranges.kap
128
139
  - examples/thread-styles.kap
129
140
  - examples/threading.kap
130
141
  - examples/tic-tac-toe.kap
@@ -132,6 +143,7 @@ files:
132
143
  - examples/two-sum-hash.kap
133
144
  - examples/two-sum.kap
134
145
  - examples/ugly-number.kap
146
+ - examples/underground-system.kap
135
147
  - examples/underscore-patterns.kap
136
148
  - examples/use_bank_account.rb
137
149
  - examples/valid-parentheses-1.kap
@@ -155,6 +167,7 @@ files:
155
167
  - lib/kapusta/compiler/emitter/patterns.rb
156
168
  - lib/kapusta/compiler/emitter/simple_expression.rb
157
169
  - lib/kapusta/compiler/emitter/support.rb
170
+ - lib/kapusta/compiler/language.rb
158
171
  - lib/kapusta/compiler/lua_compat.rb
159
172
  - lib/kapusta/compiler/macro_expander.rb
160
173
  - lib/kapusta/compiler/macro_gensym.rb
@@ -165,6 +178,10 @@ files:
165
178
  - lib/kapusta/error.rb
166
179
  - lib/kapusta/errors.rb
167
180
  - lib/kapusta/formatter.rb
181
+ - lib/kapusta/formatter/ast_helpers.rb
182
+ - lib/kapusta/formatter/cli.rb
183
+ - lib/kapusta/formatter/line_helpers.rb
184
+ - lib/kapusta/formatter/validator.rb
168
185
  - lib/kapusta/lsp.rb
169
186
  - lib/kapusta/lsp/definition.rb
170
187
  - lib/kapusta/lsp/diagnostics.rb
@@ -181,6 +198,7 @@ files:
181
198
  - spec/examples_spec.rb
182
199
  - spec/formatter_spec.rb
183
200
  - spec/lsp_spec.rb
201
+ - spec/require_spec.rb
184
202
  - spec/spec_helper.rb
185
203
  homepage: https://github.com/evmorov/kapusta
186
204
  licenses:
@@ -1,9 +0,0 @@
1
- (fn describe [x]
2
- (case x
3
- 0 "zero"
4
- 1 "one"
5
- (where n (< n 0)) "negative"
6
- _ "many"))
7
-
8
- (each [_ n (ipairs [-3 0 1 2 99])]
9
- (print n (describe n)))