kapusta 0.2.2 → 0.2.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 693c32746a17e6f3a2aa7ac2a2d23cd6e3b3d981f1ef9355a69c5cd14ea09bbe
4
- data.tar.gz: 7a500a84cbb1c8e3c1937c1fb987aa14db1b835cd63416e2f2e1d9b18309324e
3
+ metadata.gz: 1a5fcd922c54b5fa491785e6087c4fc2f8e6de1a8d94deab10fca3f775518b9b
4
+ data.tar.gz: d95f4bce9e6673104c44fc0ff281a705e796797490285cc50ef509b6b9fe5e97
5
5
  SHA512:
6
- metadata.gz: fb9f5ac51dbc88892e7d2b2527c6b94d13c972276899cdf86a76f6460e7073c0c4610cbaee2142e66638108226b9a66efe1415e42394e6c8c7abd74bd4ac0d2c
7
- data.tar.gz: 53eac046879c094bd002cb6d0e9c199dfb39680d2140dde5699f62a36a26a8cc5d928ccc02315b1cad568e9b8f383cafeddf2e26cfaa4d07db3f53a29783de7e
6
+ metadata.gz: cb5ba4420166be83a152c22bf56a12563efdb8cc64e54f230f6d299051761c5abc2329ae42dfc5e6127a721f66250b9804d6ec86a9b053dc36282599b2f8c986
7
+ data.tar.gz: 334d34e9e47bdecf6a4aa35cc6e905770efb6913fa6c2e9f90b0f3b1a95915b9044c8e3f36c81778a865f91a6d43f675318bf1ae892e19292cb4a2d2de179685
data/examples/anagram.kap CHANGED
@@ -1,8 +1,8 @@
1
1
  (fn normalize-word [word]
2
- (let [lower (word.downcase)
3
- chars (lower.chars)
4
- sorted (chars.sort)]
5
- (sorted.join)))
2
+ (-> word
3
+ (: :downcase)
4
+ (: :chars)
5
+ (: :sort)))
6
6
 
7
7
  (fn anagram? [a b]
8
8
  (= (normalize-word a)
@@ -4,7 +4,7 @@
4
4
  (if (= op "C") (do (-?> (scores.pop) (: :abs)) nil)
5
5
  (= op "D") (scores.push (* 2 (. scores -1)))
6
6
  (= op "+") (scores.push (+ (. scores -1) (. scores -2)))
7
- (scores.push (Integer op))))
7
+ (scores.push (: op :to-i))))
8
8
  scores.sum))
9
9
 
10
10
  (print (cal-points ["5" "2" "C" "D" "+"]))
@@ -1,7 +1,7 @@
1
1
  (fn require-score [s]
2
2
  (if (= s "oops")
3
3
  (raise (ArgumentError.new "not a number"))
4
- (Integer s)))
4
+ (: s :to-i)))
5
5
 
6
6
  (fn parse-score [s]
7
7
  (try (require-score s)
data/examples/pcall.kap CHANGED
@@ -1,6 +1,9 @@
1
- (let [[ok value] (pcall Integer "12")
2
- [bad-ok error] (pcall Integer "oops")
3
- [handled-ok handled] (xpcall Integer (fn [e] (e.message)) "oops")]
1
+ (fn parse-int [s]
2
+ (: Kernel :Integer s))
3
+
4
+ (let [[ok value] (pcall parse-int "12")
5
+ [bad-ok error] (pcall parse-int "oops")
6
+ [handled-ok handled] (xpcall parse-int (fn [e] (e.message)) "oops")]
4
7
  (print ok)
5
8
  (print value)
6
9
  (print bad-ok)
@@ -7,7 +7,7 @@
7
7
  (when (div? n 5) (add-drop "Plang"))
8
8
  (when (div? n 7) (add-drop "Plong"))
9
9
  (if (empty? drops)
10
- (tostring n)
10
+ (: n :to-s)
11
11
  (drops.join)))
12
12
 
13
13
  (print (raindrops 15))
@@ -0,0 +1,7 @@
1
+ (fn single-number [nums]
2
+ (accumulate [acc 0 _ n (ipairs nums)]
3
+ (: acc :^ n)))
4
+
5
+ (print (single-number [2 2 1]))
6
+ (print (single-number [4 1 2 1 2]))
7
+ (print (single-number [1]))
data/kapusta.gemspec CHANGED
@@ -7,13 +7,13 @@ Gem::Specification.new do |spec|
7
7
  spec.version = Kapusta::VERSION
8
8
  spec.authors = ['Evgenii Morozov']
9
9
  spec.homepage = 'https://github.com/evmorov/kapusta'
10
+ spec.license = 'MIT'
10
11
 
11
12
  spec.summary = 'A Lisp for the Ruby runtime'
12
13
  spec.description = 'Kapusta is a Lisp for the Ruby runtime.'
13
14
  spec.required_ruby_version = '>= 3.1'
14
15
 
15
16
  spec.metadata['rubygems_mfa_required'] = 'true'
16
- spec.metadata['homepage_uri'] = spec.homepage
17
17
  spec.metadata['source_code_uri'] = spec.homepage
18
18
  spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
19
19
 
@@ -242,7 +242,7 @@ module Kapusta
242
242
  body_code, = emit_sequence(body, child_env, current_scope,
243
243
  allow_method_definitions: false,
244
244
  result:)
245
- [binding_codes.join("\n"), body_code]
245
+ [binding_codes.reject(&:empty?).join("\n"), body_code]
246
246
  end
247
247
 
248
248
  def join_code(*chunks)
@@ -258,7 +258,7 @@ module Kapusta
258
258
  ["#{ruby_name} = #{value_code}\nnil", env]
259
259
  else
260
260
  bind_code, env = emit_pattern_bind(target, value_code, env)
261
- ["#{bind_code}\nnil", env]
261
+ [join_code(bind_code, 'nil'), env]
262
262
  end
263
263
  end
264
264
 
@@ -156,7 +156,7 @@ module Kapusta
156
156
 
157
157
  code, current_env = emit_pattern_bind(pattern, value_code, current_env)
158
158
  code
159
- end.compact
159
+ end.compact.reject(&:empty?)
160
160
  [codes.join("\n"), current_env]
161
161
  end
162
162
 
@@ -21,11 +21,17 @@ module Kapusta
21
21
  runtime_call(:qget_path, object_code, "[#{keys}]")
22
22
  end
23
23
 
24
+ BINARY_OPERATOR_METHODS = %w[<=> ** << >> & | ^ === =~].freeze
25
+ private_constant :BINARY_OPERATOR_METHODS
26
+
24
27
  def emit_colon(args, env, current_scope)
25
28
  receiver = emit_expr(args[0], env, current_scope)
26
29
  method_form = args[1]
27
30
  positional, kwargs, block_form = split_call_args(args[2..], env, current_scope)
28
31
  literal_name = method_form if method_form.is_a?(Symbol) || method_form.is_a?(String)
32
+ if literal_name && binary_operator_call?(literal_name.to_s, positional, kwargs, block_form)
33
+ return emit_binary_operator_call(receiver, literal_name.to_s, positional[0])
34
+ end
29
35
  if literal_name && direct_method_name?(literal_name.to_s)
30
36
  return emit_direct_method_call(receiver, Kapusta.kebab_to_snake(literal_name.to_s),
31
37
  positional, kwargs, block_form, env, current_scope)
@@ -37,6 +43,15 @@ module Kapusta
37
43
  "#{parenthesize(receiver)}.public_send(#{parts})"
38
44
  end
39
45
 
46
+ def binary_operator_call?(name, positional, kwargs, block_form)
47
+ BINARY_OPERATOR_METHODS.include?(name) &&
48
+ positional.length == 1 && !kwargs && !block_form
49
+ end
50
+
51
+ def emit_binary_operator_call(receiver, operator, arg_code)
52
+ "#{parenthesize(receiver)} #{operator} #{parenthesize(arg_code)}"
53
+ end
54
+
40
55
  def emit_require(arg, env, current_scope)
41
56
  path_code =
42
57
  case arg
@@ -218,6 +233,9 @@ module Kapusta
218
233
  def emit_compare(args, env, current_scope, operator)
219
234
  values = args.map { |arg| emit_expr(arg, env, current_scope) }
220
235
  return 'true' if values.length <= 1
236
+ if (nil_pred = nil_predicate(args, values, operator, negate: false))
237
+ return nil_pred
238
+ end
221
239
 
222
240
  (0...(values.length - 1)).map do |i|
223
241
  "#{parenthesize(values[i])} #{operator} #{parenthesize(values[i + 1])}"
@@ -227,12 +245,27 @@ module Kapusta
227
245
  def emit_compare_any(args, env, current_scope, operator)
228
246
  values = args.map { |arg| emit_expr(arg, env, current_scope) }
229
247
  return 'false' if values.length <= 1
248
+ if (nil_pred = nil_predicate(args, values, operator, negate: true))
249
+ return nil_pred
250
+ end
230
251
 
231
252
  (0...(values.length - 1)).map do |i|
232
253
  "#{parenthesize(values[i])} #{operator} #{parenthesize(values[i + 1])}"
233
254
  end.join(' || ')
234
255
  end
235
256
 
257
+ def nil_predicate(args, values, operator, negate:)
258
+ return unless args.length == 2
259
+ return unless (operator == '==' && !negate) || (operator == '!=' && negate)
260
+
261
+ nil_idx = args.find_index(&:nil?)
262
+ return unless nil_idx
263
+
264
+ other = values[1 - nil_idx]
265
+ receiver = simple_expression?(other) ? other : parenthesize(other)
266
+ "#{'!' if negate}#{receiver}.nil?"
267
+ end
268
+
236
269
  def emit_reduce(args, env, current_scope, empty_value, operator)
237
270
  return empty_value if args.empty?
238
271
 
@@ -292,6 +325,9 @@ module Kapusta
292
325
  else
293
326
  receiver = emit_method_path(base_code, segments[0...-1])
294
327
  positional, kwargs, block_form = split_call_args(args, env, current_scope)
328
+ if binary_operator_call?(segments.last, positional, kwargs, block_form)
329
+ return emit_binary_operator_call(receiver, segments.last, positional[0])
330
+ end
295
331
  if direct_method_name?(segments.last)
296
332
  return emit_direct_method_call(receiver, Kapusta.kebab_to_snake(segments.last),
297
333
  positional, kwargs, block_form, env, current_scope)
@@ -328,10 +364,23 @@ module Kapusta
328
364
 
329
365
  def emit_self_call(name, args, env, current_scope)
330
366
  positional, kwargs, block_form = split_call_args(args, env, current_scope)
367
+ snake = Kapusta.kebab_to_snake(name)
368
+ if direct_method_name?(snake)
369
+ return emit_direct_self_call(snake, positional, kwargs, block_form, env, current_scope)
370
+ end
371
+
331
372
  block = emit_block_proc(block_form, env, current_scope)
332
- method_name = Kapusta.kebab_to_snake(name).to_sym.inspect
373
+ method_name = snake.to_sym.inspect
333
374
  parts = build_call_args([method_name, *positional], kwargs, block)
334
- "send(#{parts})"
375
+ "public_send(#{parts})"
376
+ end
377
+
378
+ def emit_direct_self_call(method_name, positional, kwargs, block_form, env, current_scope)
379
+ attached = block_form && emit_attached_block(block_form, env, current_scope)
380
+ block = block_form && !attached ? emit_block_proc(block_form, env, current_scope) : nil
381
+ parts = build_call_args(positional, kwargs, block)
382
+ call = parts.empty? ? "#{method_name}()" : "#{method_name}(#{parts})"
383
+ attached ? "#{call} #{attached}" : call
335
384
  end
336
385
 
337
386
  def split_call_args(args, env, current_scope)
@@ -8,7 +8,7 @@ module Kapusta
8
8
 
9
9
  def emit_pattern_bind(pattern, value_code, env)
10
10
  if pattern.is_a?(Sym)
11
- return ['nil', env] if pattern.name == '_'
11
+ return ['', env] if pattern.name == '_'
12
12
 
13
13
  ruby_name = define_local(env, pattern)
14
14
  ["#{ruby_name} = #{value_code}", env]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kapusta
4
- VERSION = '0.2.2'
4
+ VERSION = '0.2.3'
5
5
  end
@@ -399,6 +399,10 @@ RSpec.describe 'examples' do
399
399
  expect(run_example('shapes.kap')).to eq("78.5\n9\n8\n0\n")
400
400
  end
401
401
 
402
+ it 'single-number.kap' do
403
+ expect(run_example('single-number.kap')).to eq("1\n4\n1\n")
404
+ end
405
+
402
406
  it 'squares.kap' do
403
407
  expect(run_example('squares.kap')).to eq("1\n4\n9\n16\n25\n")
404
408
  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.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgenii Morozov
@@ -80,6 +80,7 @@ files:
80
80
  - examples/safe-lookup.kap
81
81
  - examples/scopes.kap
82
82
  - examples/shapes.kap
83
+ - examples/single-number.kap
83
84
  - examples/squares.kap
84
85
  - examples/stack.kap
85
86
  - examples/sum.kap
@@ -123,10 +124,10 @@ files:
123
124
  - spec/reader_spec.rb
124
125
  - spec/spec_helper.rb
125
126
  homepage: https://github.com/evmorov/kapusta
126
- licenses: []
127
+ licenses:
128
+ - MIT
127
129
  metadata:
128
130
  rubygems_mfa_required: 'true'
129
- homepage_uri: https://github.com/evmorov/kapusta
130
131
  source_code_uri: https://github.com/evmorov/kapusta
131
132
  bug_tracker_uri: https://github.com/evmorov/kapusta/issues
132
133
  rdoc_options: []