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.
@@ -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.5.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: