kapusta 0.5.0 → 0.7.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 +24 -6
- data/bin/fennel-parity +2 -0
- data/examples/classify-wallet.kap +11 -0
- data/examples/power-of-three.kap +12 -0
- data/exe/kapusta-ls +14 -0
- data/kapusta.gemspec +2 -2
- data/lib/kapusta/compiler/emitter/bindings.rb +38 -4
- data/lib/kapusta/compiler/emitter/collections.rb +51 -59
- data/lib/kapusta/compiler/emitter/control_flow.rb +24 -2
- data/lib/kapusta/compiler/emitter/expressions.rb +0 -2
- data/lib/kapusta/compiler/emitter/interop.rb +2 -1
- data/lib/kapusta/compiler/emitter/patterns.rb +52 -4
- data/lib/kapusta/compiler/emitter/support.rb +1 -1
- data/lib/kapusta/compiler/emitter.rb +1 -1
- data/lib/kapusta/compiler/lua_compat.rb +149 -0
- data/lib/kapusta/compiler/macro_expander.rb +2 -0
- data/lib/kapusta/compiler/normalizer.rb +4 -19
- data/lib/kapusta/compiler.rb +4 -2
- data/lib/kapusta/errors.rb +3 -2
- data/lib/kapusta/formatter.rb +4 -0
- data/lib/kapusta/lsp.rb +258 -0
- data/lib/kapusta/reader.rb +0 -2
- data/lib/kapusta/version.rb +1 -1
- data/spec/examples_errors_spec.rb +125 -0
- data/spec/lsp_spec.rb +83 -0
- metadata +8 -1
|
@@ -27,6 +27,26 @@ RSpec.describe 'examples-errors' do
|
|
|
27
27
|
.to eq("accumulate-missing-iterator.kap:5:3: expected initial value and iterator binding table\n")
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
it 'auto-gensym-outside-quasiquote.kap' do
|
|
31
|
+
expect(run_error_example('auto-gensym-outside-quasiquote.kap'))
|
|
32
|
+
.to eq("auto-gensym-outside-quasiquote.kap: auto-gensym x# outside quasiquote\n")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'bad-multisym.kap' do
|
|
36
|
+
expect(run_error_example('bad-multisym.kap'))
|
|
37
|
+
.to eq("bad-multisym.kap:1:8: bad multisym: unbound.foo\n")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'bad-set-target.kap' do
|
|
41
|
+
expect(run_error_example('bad-set-target.kap'))
|
|
42
|
+
.to eq("bad-set-target.kap:2:1: bad set target: 1\n")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'bad-shorthand.kap' do
|
|
46
|
+
expect(run_error_example('bad-shorthand.kap'))
|
|
47
|
+
.to eq("bad-shorthand.kap:2:14: bad shorthand\n")
|
|
48
|
+
end
|
|
49
|
+
|
|
30
50
|
it 'call-empty-form.kap' do
|
|
31
51
|
expect(run_error_example('call-empty-form.kap'))
|
|
32
52
|
.to eq("call-empty-form.kap:7:8: expected a function, macro, or special to call\n")
|
|
@@ -37,16 +57,31 @@ RSpec.describe 'examples-errors' do
|
|
|
37
57
|
.to eq("call-literal-number.kap:6:14: cannot call literal value 1\n")
|
|
38
58
|
end
|
|
39
59
|
|
|
60
|
+
it 'cannot-set-method-binding.kap' do
|
|
61
|
+
expect(run_error_example('cannot-set-method-binding.kap'))
|
|
62
|
+
.to eq("cannot-set-method-binding.kap:2:1: cannot set method binding: foo\n")
|
|
63
|
+
end
|
|
64
|
+
|
|
40
65
|
it 'case-no-patterns.kap' do
|
|
41
66
|
expect(run_error_example('case-no-patterns.kap'))
|
|
42
67
|
.to eq("case-no-patterns.kap:3:5: expected at least one pattern/body pair\n")
|
|
43
68
|
end
|
|
44
69
|
|
|
70
|
+
it 'case-no-subject.kap' do
|
|
71
|
+
expect(run_error_example('case-no-subject.kap'))
|
|
72
|
+
.to eq("case-no-subject.kap:1:1: missing subject\n")
|
|
73
|
+
end
|
|
74
|
+
|
|
45
75
|
it 'case-odd-pattern-body.kap' do
|
|
46
76
|
expect(run_error_example('case-odd-pattern-body.kap'))
|
|
47
77
|
.to eq("case-odd-pattern-body.kap:2:3: expected even number of pattern/body pairs\n")
|
|
48
78
|
end
|
|
49
79
|
|
|
80
|
+
it 'case-unsupported.kap' do
|
|
81
|
+
expect(run_error_example('case-unsupported.kap'))
|
|
82
|
+
.to eq("case-unsupported.kap:1:1: case/match clauses use patterns this compiler cannot translate\n")
|
|
83
|
+
end
|
|
84
|
+
|
|
50
85
|
it 'destructure-literal-number.kap' do
|
|
51
86
|
expect(run_error_example('destructure-literal-number.kap'))
|
|
52
87
|
.to eq("destructure-literal-number.kap:5:3: could not destructure literal\n")
|
|
@@ -122,6 +157,16 @@ RSpec.describe 'examples-errors' do
|
|
|
122
157
|
.to eq("import-macros-missing-module.kap:4:1: import-macros is not yet supported\n")
|
|
123
158
|
end
|
|
124
159
|
|
|
160
|
+
it 'invalid-class-name.kap' do
|
|
161
|
+
expect(run_error_example('invalid-class-name.kap'))
|
|
162
|
+
.to eq("invalid-class-name.kap: invalid class name: lowercase\n")
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'invalid-module-name.kap' do
|
|
166
|
+
expect(run_error_example('invalid-module-name.kap'))
|
|
167
|
+
.to eq("invalid-module-name.kap: invalid module name: lowercase\n")
|
|
168
|
+
end
|
|
169
|
+
|
|
125
170
|
it 'let-odd-bindings.kap' do
|
|
126
171
|
expect(run_error_example('let-odd-bindings.kap'))
|
|
127
172
|
.to eq("let-odd-bindings.kap:2:3: expected even number of name/value bindings\n")
|
|
@@ -142,6 +187,16 @@ RSpec.describe 'examples-errors' do
|
|
|
142
187
|
.to eq("local-without-value.kap:6:3: local: expected name and value\n")
|
|
143
188
|
end
|
|
144
189
|
|
|
190
|
+
it 'macro-name-must-be-symbol.kap' do
|
|
191
|
+
expect(run_error_example('macro-name-must-be-symbol.kap'))
|
|
192
|
+
.to eq("macro-name-must-be-symbol.kap: macro name must be a symbol\n")
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it 'macro-params-must-be-vector.kap' do
|
|
196
|
+
expect(run_error_example('macro-params-must-be-vector.kap'))
|
|
197
|
+
.to eq("macro-params-must-be-vector.kap:1:12: macro params must be a vector\n")
|
|
198
|
+
end
|
|
199
|
+
|
|
145
200
|
it 'macro-unsafe-bind.kap' do
|
|
146
201
|
expect(run_error_example('macro-unsafe-bind.kap'))
|
|
147
202
|
.to eq("macro-unsafe-bind.kap:13:8: macro tried to bind unsafe without gensym\n")
|
|
@@ -152,16 +207,46 @@ RSpec.describe 'examples-errors' do
|
|
|
152
207
|
.to eq("macro-vararg-with-operator.kap:5:3: tried to use vararg with operator\n")
|
|
153
208
|
end
|
|
154
209
|
|
|
210
|
+
it 'macros-entry-must-be-fn.kap' do
|
|
211
|
+
expect(run_error_example('macros-entry-must-be-fn.kap'))
|
|
212
|
+
.to eq("macros-entry-must-be-fn.kap: macros entry value must be a fn form, got 1\n")
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'macros-entry-params-must-be-vector.kap' do
|
|
216
|
+
expect(run_error_example('macros-entry-params-must-be-vector.kap'))
|
|
217
|
+
.to eq("macros-entry-params-must-be-vector.kap:1:19: macros entry params must be a vector\n")
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it 'macros-expects-hash.kap' do
|
|
221
|
+
expect(run_error_example('macros-expects-hash.kap'))
|
|
222
|
+
.to eq("macros-expects-hash.kap: macros expects a hash literal\n")
|
|
223
|
+
end
|
|
224
|
+
|
|
155
225
|
it 'match-no-patterns.kap' do
|
|
156
226
|
expect(run_error_example('match-no-patterns.kap'))
|
|
157
227
|
.to eq("match-no-patterns.kap:3:5: expected at least one pattern/body pair\n")
|
|
158
228
|
end
|
|
159
229
|
|
|
230
|
+
it 'match-no-subject.kap' do
|
|
231
|
+
expect(run_error_example('match-no-subject.kap'))
|
|
232
|
+
.to eq("match-no-subject.kap:1:1: missing subject\n")
|
|
233
|
+
end
|
|
234
|
+
|
|
160
235
|
it 'mismatched-brackets.kap' do
|
|
161
236
|
expect(run_error_example('mismatched-brackets.kap'))
|
|
162
237
|
.to eq("mismatched-brackets.kap:4:19: unexpected closing delimiter ')'\n")
|
|
163
238
|
end
|
|
164
239
|
|
|
240
|
+
it 'nested-quasiquote.kap' do
|
|
241
|
+
expect(run_error_example('nested-quasiquote.kap'))
|
|
242
|
+
.to eq("nested-quasiquote.kap: nested quasiquote is not supported\n")
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
it 'odd-forms-in-hash.kap' do
|
|
246
|
+
expect(run_error_example('odd-forms-in-hash.kap'))
|
|
247
|
+
.to eq("odd-forms-in-hash.kap:1:9: odd number of forms in hash\n")
|
|
248
|
+
end
|
|
249
|
+
|
|
165
250
|
it 'only-rest-param.kap' do
|
|
166
251
|
expect(run_error_example('only-rest-param.kap'))
|
|
167
252
|
.to eq("only-rest-param.kap:4:1: expected rest argument before last parameter\n")
|
|
@@ -177,6 +262,11 @@ RSpec.describe 'examples-errors' do
|
|
|
177
262
|
.to eq("rest-not-last.kap:6:3: expected rest argument before last parameter\n")
|
|
178
263
|
end
|
|
179
264
|
|
|
265
|
+
it 'set-immutable-let.kap' do
|
|
266
|
+
expect(run_error_example('set-immutable-let.kap'))
|
|
267
|
+
.to eq("set-immutable-let.kap:2:3: expected var counter\n")
|
|
268
|
+
end
|
|
269
|
+
|
|
180
270
|
it 'set-immutable-local.kap' do
|
|
181
271
|
expect(run_error_example('set-immutable-local.kap'))
|
|
182
272
|
.to eq("set-immutable-local.kap:8:3: expected var counter\n")
|
|
@@ -212,11 +302,46 @@ RSpec.describe 'examples-errors' do
|
|
|
212
302
|
.to eq("unclosed-table.kap:4:21: unexpected closing delimiter ']'\n")
|
|
213
303
|
end
|
|
214
304
|
|
|
305
|
+
it 'undefined-symbol.kap' do
|
|
306
|
+
expect(run_error_example('undefined-symbol.kap'))
|
|
307
|
+
.to eq("undefined-symbol.kap:1:8: undefined symbol: missing-symbol\n")
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
it 'unexpected-eof.kap' do
|
|
311
|
+
expect(run_error_example('unexpected-eof.kap'))
|
|
312
|
+
.to eq("unexpected-eof.kap:1:2: unexpected eof\n")
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
it 'unexpected-vararg.kap' do
|
|
316
|
+
expect(run_error_example('unexpected-vararg.kap'))
|
|
317
|
+
.to eq("unexpected-vararg.kap:2:10: unexpected vararg\n")
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
it 'unknown-special-form.kap' do
|
|
321
|
+
expect(run_error_example('unknown-special-form.kap'))
|
|
322
|
+
.to eq("unknown-special-form.kap:1:1: unknown special form: catch\n")
|
|
323
|
+
end
|
|
324
|
+
|
|
215
325
|
it 'unquote-outside-quote.kap' do
|
|
216
326
|
expect(run_error_example('unquote-outside-quote.kap'))
|
|
217
327
|
.to eq("unquote-outside-quote.kap:5:10: cannot emit form: ,x\n")
|
|
218
328
|
end
|
|
219
329
|
|
|
330
|
+
it 'unquote-splice-outside-list.kap' do
|
|
331
|
+
expect(run_error_example('unquote-splice-outside-list.kap'))
|
|
332
|
+
.to eq("unquote-splice-outside-list.kap: unquote-splice must appear inside a quoted list/vec\n")
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
it 'unterminated-string.kap' do
|
|
336
|
+
expect(run_error_example('unterminated-string.kap'))
|
|
337
|
+
.to eq("unterminated-string.kap:1:8: unterminated string\n")
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
it 'vararg-not-last.kap' do
|
|
341
|
+
expect(run_error_example('vararg-not-last.kap'))
|
|
342
|
+
.to eq("vararg-not-last.kap:1:1: expected vararg as last parameter\n")
|
|
343
|
+
end
|
|
344
|
+
|
|
220
345
|
it 'var-without-value.kap' do
|
|
221
346
|
expect(run_error_example('var-without-value.kap'))
|
|
222
347
|
.to eq("var-without-value.kap:6:3: var: expected name and value\n")
|
data/spec/lsp_spec.rb
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'kapusta/lsp'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'stringio'
|
|
7
|
+
|
|
8
|
+
RSpec.describe Kapusta::LSP do
|
|
9
|
+
def frame(payload)
|
|
10
|
+
body = JSON.generate(payload)
|
|
11
|
+
"Content-Length: #{body.bytesize}\r\n\r\n#{body}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def parse_responses(stdout)
|
|
15
|
+
messages = []
|
|
16
|
+
rest = stdout.dup
|
|
17
|
+
while (m = rest.match(/\AContent-Length: (\d+)\r\n\r\n/))
|
|
18
|
+
len = Integer(m[1], 10)
|
|
19
|
+
messages << JSON.parse(rest[m[0].length, len])
|
|
20
|
+
rest = rest[(m[0].length + len)..]
|
|
21
|
+
end
|
|
22
|
+
messages
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def run(*frames)
|
|
26
|
+
input = StringIO.new(frames.join)
|
|
27
|
+
output = StringIO.new
|
|
28
|
+
log = StringIO.new
|
|
29
|
+
described_class.new(input:, output:, log:).run
|
|
30
|
+
parse_responses(output.string)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'advertises diagnostics and formatting capabilities on initialize' do
|
|
34
|
+
responses = run(frame(jsonrpc: '2.0', id: 1, method: 'initialize', params: {}))
|
|
35
|
+
capabilities = responses.first.dig('result', 'capabilities')
|
|
36
|
+
|
|
37
|
+
expect(capabilities).to include('textDocumentSync', 'documentFormattingProvider')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'publishes diagnostics for invalid source' do
|
|
41
|
+
responses = run(
|
|
42
|
+
frame(jsonrpc: '2.0', id: 1, method: 'initialize', params: {}),
|
|
43
|
+
frame(jsonrpc: '2.0', method: 'textDocument/didOpen',
|
|
44
|
+
params: { textDocument: { uri: 'file:///x.kap', version: 1, text: '(let [x 1] (+ x ()))' } })
|
|
45
|
+
)
|
|
46
|
+
diagnostics = responses.last.dig('params', 'diagnostics')
|
|
47
|
+
|
|
48
|
+
expect(diagnostics).not_to be_empty
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'publishes no diagnostics for valid source' do
|
|
52
|
+
responses = run(
|
|
53
|
+
frame(jsonrpc: '2.0', id: 1, method: 'initialize', params: {}),
|
|
54
|
+
frame(jsonrpc: '2.0', method: 'textDocument/didOpen',
|
|
55
|
+
params: { textDocument: { uri: 'file:///x.kap', version: 1, text: '(print "hi")' } })
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
expect(responses.last.dig('params', 'diagnostics')).to be_empty
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'returns a TextEdit for formatting' do
|
|
62
|
+
responses = run(
|
|
63
|
+
frame(jsonrpc: '2.0', id: 1, method: 'initialize', params: {}),
|
|
64
|
+
frame(jsonrpc: '2.0', method: 'textDocument/didOpen',
|
|
65
|
+
params: { textDocument: { uri: 'file:///x.kap', version: 1, text: "(fn greet [x] (print x))\n" } }),
|
|
66
|
+
frame(jsonrpc: '2.0', id: 2, method: 'textDocument/formatting',
|
|
67
|
+
params: { textDocument: { uri: 'file:///x.kap' } })
|
|
68
|
+
)
|
|
69
|
+
edits = responses.find { |m| m['id'] == 2 }['result']
|
|
70
|
+
|
|
71
|
+
expect(edits).to be_an(Array).and(be_one)
|
|
72
|
+
expect(edits.first).to include('range', 'newText')
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'rejects requests sent before initialize' do
|
|
76
|
+
responses = run(
|
|
77
|
+
frame(jsonrpc: '2.0', id: 1, method: 'textDocument/formatting',
|
|
78
|
+
params: { textDocument: { uri: 'file:///x.kap' } })
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
expect(responses.first.dig('error', 'code')).to eq(-32_002)
|
|
82
|
+
end
|
|
83
|
+
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.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evgenii Morozov
|
|
@@ -13,6 +13,7 @@ description: Kapusta is a Lisp for the Ruby runtime.
|
|
|
13
13
|
executables:
|
|
14
14
|
- kapfmt
|
|
15
15
|
- kapusta
|
|
16
|
+
- kapusta-ls
|
|
16
17
|
extensions: []
|
|
17
18
|
extra_rdoc_files: []
|
|
18
19
|
files:
|
|
@@ -37,6 +38,7 @@ files:
|
|
|
37
38
|
- examples/block-sort.kap
|
|
38
39
|
- examples/blocks-and-kwargs.kap
|
|
39
40
|
- examples/calc.kap
|
|
41
|
+
- examples/classify-wallet.kap
|
|
40
42
|
- examples/climbing-stairs.kap
|
|
41
43
|
- examples/contains-duplicate.kap
|
|
42
44
|
- examples/counter.kap
|
|
@@ -80,6 +82,7 @@ files:
|
|
|
80
82
|
- examples/pivot-index.kap
|
|
81
83
|
- examples/plus-one.kap
|
|
82
84
|
- examples/points.kap
|
|
85
|
+
- examples/power-of-three.kap
|
|
83
86
|
- examples/primes.kap
|
|
84
87
|
- examples/raindrops.kap
|
|
85
88
|
- examples/record.kap
|
|
@@ -109,6 +112,7 @@ files:
|
|
|
109
112
|
- examples/zoo-animal-inheritance-2.kap
|
|
110
113
|
- exe/kapfmt
|
|
111
114
|
- exe/kapusta
|
|
115
|
+
- exe/kapusta-ls
|
|
112
116
|
- kapusta.gemspec
|
|
113
117
|
- lib/kapusta.rb
|
|
114
118
|
- lib/kapusta/ast.rb
|
|
@@ -122,12 +126,14 @@ files:
|
|
|
122
126
|
- lib/kapusta/compiler/emitter/interop.rb
|
|
123
127
|
- lib/kapusta/compiler/emitter/patterns.rb
|
|
124
128
|
- lib/kapusta/compiler/emitter/support.rb
|
|
129
|
+
- lib/kapusta/compiler/lua_compat.rb
|
|
125
130
|
- lib/kapusta/compiler/macro_expander.rb
|
|
126
131
|
- lib/kapusta/compiler/normalizer.rb
|
|
127
132
|
- lib/kapusta/env.rb
|
|
128
133
|
- lib/kapusta/error.rb
|
|
129
134
|
- lib/kapusta/errors.rb
|
|
130
135
|
- lib/kapusta/formatter.rb
|
|
136
|
+
- lib/kapusta/lsp.rb
|
|
131
137
|
- lib/kapusta/reader.rb
|
|
132
138
|
- lib/kapusta/support.rb
|
|
133
139
|
- lib/kapusta/version.rb
|
|
@@ -135,6 +141,7 @@ files:
|
|
|
135
141
|
- spec/examples_errors_spec.rb
|
|
136
142
|
- spec/examples_spec.rb
|
|
137
143
|
- spec/formatter_spec.rb
|
|
144
|
+
- spec/lsp_spec.rb
|
|
138
145
|
- spec/spec_helper.rb
|
|
139
146
|
homepage: https://github.com/evmorov/kapusta
|
|
140
147
|
licenses:
|