kapusta 0.1.4 → 0.1.5

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: c136e4c7cc9c5dde0ced8c828ab80882900b278db86010986eb7271a7b45309e
4
- data.tar.gz: 46ff912715738e99c454727224a5e19f6d1635fbfccb1ade3820a9a51a996d0d
3
+ metadata.gz: 52369f7a4cd658385e7ddc69f552477d94b55340cb9f796ac635550a7def6e6a
4
+ data.tar.gz: 6d673c9c387b52b0e4cd9e55b8ae2cf1d0a7e2c5f741dc7705f405b0ddaf25c6
5
5
  SHA512:
6
- metadata.gz: 9d36ad79125c72a9e804f10ce9e0e5983e852b428f1a1a3718e21ef9394c66f39e57bec366ed81257bd5e62439429cc1cd93039710404db2c8dc0f3d9c09c44a
7
- data.tar.gz: 0cb236654de0a57184387087c5a7f47dc49d548c1fed0167638439c071b83cda592cd0346f68ffc086c74afccff0b1f08c90ec977238213b9672fa8244052b27
6
+ metadata.gz: 53510507ba41a4d67ec73184eaef665fee182220407046eb9db64cf05cae7d28a315b5b8cf4ace6071b359a83801bd85d7e45ce5e4aefd3c608fef614f116fee
7
+ data.tar.gz: ff1b88fc11dc10ccffc1459686b17754bd39dc6a941756d7cab304253aec3b63bc36967c9edcf50cf4c29eadee3ae2afb0467383b6428dcb74f41c452173ab4a
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ SOURCE_DIR="$ROOT_DIR/examples"
6
+ TARGET_DIR="$ROOT_DIR/examples-compiled"
7
+
8
+ mkdir -p "$TARGET_DIR"
9
+
10
+ shopt -s nullglob
11
+ kap_files=("$SOURCE_DIR"/*.kap)
12
+
13
+ if ((${#kap_files[@]} == 0)); then
14
+ printf 'No .kap files found in %s\n' "$SOURCE_DIR" >&2
15
+ exit 0
16
+ fi
17
+
18
+ for kap_file in "${kap_files[@]}"; do
19
+ name="$(basename "$kap_file" .kap)"
20
+ ruby_file="$TARGET_DIR/$name.rb"
21
+
22
+ "$ROOT_DIR/exe/kapusta" --compile "$kap_file" > "$ruby_file"
23
+ printf 'Compiled %s -> %s\n' "$kap_file" "$ruby_file"
24
+ done
@@ -57,6 +57,31 @@ module Kapusta
57
57
  pattern.is_a?(Vec) && pattern.items.all? { |item| item.is_a?(Sym) && !item.dotted? && item.name != '&' }
58
58
  end
59
59
 
60
+ def emit_definition_form(form, env, current_scope)
61
+ return [emit_method_definition(form, env), env] unless current_scope == :toplevel
62
+
63
+ emit_toplevel_method_definition(form, env)
64
+ end
65
+
66
+ def emit_toplevel_method_definition(form, env)
67
+ name_sym = form.items[1]
68
+ pattern = form.items[2]
69
+ body = form.items[3..]
70
+ return [nil, env] if name_sym.dotted?
71
+ return [nil, env] unless simple_parameter_pattern?(pattern)
72
+
73
+ ruby_name = direct_method_definition_name(name_sym)
74
+ return [nil, env] unless ruby_name
75
+ return [nil, env] if captures_outer_binding?(body, env, pattern_names(pattern))
76
+
77
+ env.define(name_sym.name, Env::MethodBinding.new(ruby_name))
78
+ definition = emit_direct_method_definition(name_sym, pattern, body, env)
79
+ if needs_toplevel_method_bridge?(ruby_name)
80
+ definition = join_code(definition, emit_toplevel_method_bridge(ruby_name))
81
+ end
82
+ [definition, env]
83
+ end
84
+
60
85
  def emit_named_fn_assignment(form, env, current_scope)
61
86
  name_sym = form.items[1]
62
87
  ruby_name = define_local(env, name_sym.name)
@@ -70,6 +95,9 @@ module Kapusta
70
95
  name_sym = form.items[1]
71
96
  pattern = form.items[2]
72
97
  body = form.items[3..]
98
+ direct_definition = emit_direct_method_definition(name_sym, pattern, body, env)
99
+ return direct_definition if direct_definition
100
+
73
101
  block_header, body_code = emit_method_body(pattern, body, env)
74
102
 
75
103
  if name_sym.name.start_with?('self.')
@@ -89,6 +117,48 @@ module Kapusta
89
117
  end
90
118
  end
91
119
 
120
+ def emit_direct_method_definition(name_sym, pattern, body, env)
121
+ return unless simple_parameter_pattern?(pattern)
122
+
123
+ ruby_name = direct_method_definition_name(name_sym)
124
+ return unless ruby_name
125
+ return if captures_outer_binding?(body, env, pattern_names(pattern))
126
+
127
+ body_env = env.child
128
+ params = pattern.items.map { |sym| define_local(body_env, sym.name, shadow: true) }
129
+ body_code, = emit_sequence(body, body_env, :toplevel, allow_method_definitions: false)
130
+ header = params.empty? ? "def #{ruby_name}" : "def #{ruby_name}(#{params.join(', ')})"
131
+ [
132
+ header,
133
+ indent(body_code),
134
+ 'end'
135
+ ].join("\n")
136
+ end
137
+
138
+ def direct_method_definition_name(name_sym)
139
+ source_name = name_sym.name
140
+ if source_name.start_with?('self.')
141
+ method_name = Kapusta.kebab_to_snake(source_name.delete_prefix('self.'))
142
+ return unless direct_method_name?(method_name)
143
+
144
+ "self.#{method_name}"
145
+ else
146
+ method_name = Kapusta.kebab_to_snake(source_name)
147
+ return unless direct_method_name?(method_name)
148
+
149
+ method_name
150
+ end
151
+ end
152
+
153
+ def needs_toplevel_method_bridge?(ruby_name)
154
+ %w[context describe example it specify].include?(ruby_name)
155
+ end
156
+
157
+ def emit_toplevel_method_bridge(ruby_name)
158
+ method_name = ruby_name.to_sym.inspect
159
+ "define_singleton_method(#{method_name}, Object.instance_method(#{method_name}).bind(self))"
160
+ end
161
+
92
162
  def emit_method_body(pattern, body, env)
93
163
  return emit_simple_method_body(pattern, body, env) if simple_parameter_pattern?(pattern)
94
164
 
@@ -108,6 +178,33 @@ module Kapusta
108
178
  [params.empty? ? 'do' : "do |#{params.join(', ')}|", body_code]
109
179
  end
110
180
 
181
+ def captures_outer_binding?(forms, env, local_names)
182
+ forms.any? { |form| form_captures_outer_binding?(form, env, local_names) }
183
+ end
184
+
185
+ def form_captures_outer_binding?(form, env, local_names)
186
+ case form
187
+ when Sym
188
+ sym_captures_outer_binding?(form, env, local_names)
189
+ when Vec, List
190
+ form.items.any? { |item| form_captures_outer_binding?(item, env, local_names) }
191
+ when HashLit
192
+ form.pairs.any? do |key, value|
193
+ form_captures_outer_binding?(key, env, local_names) ||
194
+ form_captures_outer_binding?(value, env, local_names)
195
+ end
196
+ else
197
+ false
198
+ end
199
+ end
200
+
201
+ def sym_captures_outer_binding?(sym, env, local_names)
202
+ name = sym.dotted? ? sym.segments.first : sym.name
203
+ return false if local_names.include?(name) || !env.defined?(name)
204
+
205
+ !method_binding?(env.lookup(name))
206
+ end
207
+
111
208
  def emit_let(args, env, current_scope)
112
209
  binding_code, body_code = emit_let_parts(args, env, current_scope, result: true)
113
210
  <<~RUBY.chomp
@@ -171,7 +268,10 @@ module Kapusta
171
268
  if target.is_a?(Sym) && !target.dotted?
172
269
  ruby_name =
173
270
  if env.defined?(target.name)
174
- env.lookup(target.name)
271
+ binding = env.lookup(target.name)
272
+ raise Error, "cannot set method binding: #{target.name}" if method_binding?(binding)
273
+
274
+ binding
175
275
  else
176
276
  define_local(env, target.name)
177
277
  end
@@ -194,7 +294,10 @@ module Kapusta
194
294
  base_code, segments = multisym_base(target.segments, env)
195
295
  runtime_call(:set_method_path, base_code, segments.inspect, value_code)
196
296
  else
197
- "#{env.lookup(target.name)} = #{value_code}"
297
+ binding = env.lookup(target.name)
298
+ raise Error, "cannot set method binding: #{target.name}" if method_binding?(binding)
299
+
300
+ "#{binding} = #{value_code}"
198
301
  end
199
302
  when List
200
303
  head = target.head
@@ -36,7 +36,7 @@ module Kapusta
36
36
  if head.is_a?(Sym)
37
37
  return emit_special(head.name, args, env, current_scope) if special_form?(head.name)
38
38
  return emit_multisym_call(head, args, env, current_scope) if head.dotted?
39
- return emit_callable_call(env.lookup(head.name), args, env, current_scope) if env.defined?(head.name)
39
+ return emit_bound_call(env.lookup(head.name), args, env, current_scope) if env.defined?(head.name)
40
40
 
41
41
  return emit_self_call(head.name, args, env, current_scope)
42
42
  end
@@ -68,9 +68,7 @@ module Kapusta
68
68
  when '.' then emit_lookup(args, env, current_scope)
69
69
  when '?.' then emit_safe_lookup(args, env, current_scope)
70
70
  when ':' then emit_colon(args, env, current_scope)
71
- when '..' then runtime_call(:concat, args.map do |arg|
72
- emit_expr(arg, env, current_scope)
73
- end)
71
+ when '..' then emit_concat(args, env, current_scope)
74
72
  when 'length' then "(#{emit_expr(args[0], env, current_scope)}).length"
75
73
  when 'require' then emit_require(args[0], env, current_scope)
76
74
  when 'module' then emit_module_expr(args, env)
@@ -95,13 +93,31 @@ module Kapusta
95
93
  when '*' then emit_reduce(args, env, current_scope, '1', :*)
96
94
  when '/' then emit_div(args, env, current_scope)
97
95
  when '%' then args.map { |arg| parenthesize(emit_expr(arg, env, current_scope)) }.join(' % ')
98
- when 'print' then runtime_call(:print_values, *args.map do |arg|
99
- emit_expr(arg, env, current_scope)
100
- end)
96
+ when 'print' then emit_print(args, env, current_scope)
101
97
  else
102
98
  raise Error, "unknown special form: #{name}"
103
99
  end
104
100
  end
101
+
102
+ def emit_concat(args, env, current_scope)
103
+ return '""' if args.empty?
104
+
105
+ args.map { |arg| emit_string_part(arg, env, current_scope) }.join(' + ')
106
+ end
107
+
108
+ def emit_print(args, env, current_scope)
109
+ return '$stdout.puts("")' if args.empty?
110
+
111
+ values = args.map { |arg| emit_string_part(arg, env, current_scope) }
112
+ output = values.length == 1 ? values.first : "[#{values.join(', ')}].join(\"\\t\")"
113
+ "$stdout.puts(#{output})"
114
+ end
115
+
116
+ def emit_string_part(arg, env, current_scope)
117
+ return arg.inspect if arg.is_a?(String)
118
+
119
+ runtime_call(:stringify, emit_expr(arg, env, current_scope))
120
+ end
105
121
  end
106
122
  end
107
123
  end
@@ -63,7 +63,7 @@ module Kapusta
63
63
 
64
64
  def emit_direct_module_header(name_sym, body)
65
65
  const_name = simple_constant_name(name_sym)
66
- return nil unless const_name
66
+ return unless const_name
67
67
 
68
68
  [
69
69
  "module #{const_name}",
@@ -94,7 +94,7 @@ module Kapusta
94
94
 
95
95
  def emit_direct_class_header(name_sym, supers, body)
96
96
  const_name = simple_constant_name(name_sym)
97
- return nil unless const_name && supers.nil?
97
+ return unless const_name && supers.nil?
98
98
 
99
99
  [
100
100
  "class #{const_name}",
@@ -105,7 +105,7 @@ module Kapusta
105
105
  end
106
106
 
107
107
  def simple_constant_name(name_sym)
108
- return nil unless name_sym.is_a?(Sym) && name_sym.name.match?(/\A[A-Z]\w*\z/)
108
+ return unless name_sym.is_a?(Sym) && name_sym.name.match?(/\A[A-Z]\w*\z/)
109
109
 
110
110
  name_sym.name
111
111
  end
@@ -212,6 +212,22 @@ module Kapusta
212
212
  runtime_call(:call, callee_code, "[#{positional.join(', ')}]", kwargs, block)
213
213
  end
214
214
 
215
+ def emit_bound_call(binding, args, env, current_scope)
216
+ return emit_self_method_binding_call(binding, args, env, current_scope) if method_binding?(binding)
217
+
218
+ emit_callable_call(binding, args, env, current_scope)
219
+ end
220
+
221
+ def emit_self_method_binding_call(binding, args, env, current_scope)
222
+ positional = args.map { |arg| emit_expr(arg, env, current_scope) }
223
+ emit_direct_self_method_call(binding.ruby_name, positional)
224
+ end
225
+
226
+ def emit_direct_self_method_call(method_name, positional)
227
+ args = positional.join(', ')
228
+ args.empty? ? "#{method_name}()" : "#{method_name}(#{args})"
229
+ end
230
+
215
231
  def emit_direct_callable_call(callee_code, positional)
216
232
  rendered_args = positional.join(', ')
217
233
  suffix = rendered_args.empty? ? '.call' : ".call(#{rendered_args})"
@@ -285,7 +301,7 @@ module Kapusta
285
301
  name = sym.name
286
302
  return 'self' if name == 'self'
287
303
  return 'Float::INFINITY' if name == 'math.huge'
288
- return env.lookup(name) if env.defined?(name)
304
+ return binding_value_code(env.lookup(name)) if env.defined?(name)
289
305
  return emit_multisym_value(sym, env) if sym.dotted?
290
306
  return 'ARGV' if name == 'ARGV'
291
307
  return name if name.match?(/\A[A-Z]/)
@@ -311,7 +327,7 @@ module Kapusta
311
327
  if segments[0] == 'self'
312
328
  ['self', segments[1..]]
313
329
  elsif env.defined?(segments[0])
314
- [env.lookup(segments[0]), segments[1..]]
330
+ [binding_value_code(env.lookup(segments[0])), segments[1..]]
315
331
  else
316
332
  idx = 0
317
333
  const_path = []
@@ -91,7 +91,7 @@ module Kapusta
91
91
  if state[:bound_names].key?(name)
92
92
  "[:ref, #{name.inspect}]"
93
93
  elsif prefer_pin && mode == :match && env.defined?(name)
94
- "[:pin, #{env.lookup(name)}]"
94
+ "[:pin, #{binding_value_code(env.lookup(name))}]"
95
95
  else
96
96
  state[:bound_names][name] = true
97
97
  state[:binding_names] << name
@@ -121,7 +121,7 @@ module Kapusta
121
121
  raise Error, "bad pin pattern: #{pattern.inspect}" unless name_sym.is_a?(Sym)
122
122
  raise Error, "cannot pin undefined name: #{name_sym.name}" unless env.defined?(name_sym.name)
123
123
 
124
- "[:pin, #{env.lookup(name_sym.name)}]"
124
+ "[:pin, #{binding_value_code(env.lookup(name_sym.name))}]"
125
125
  end
126
126
 
127
127
  def emit_or_match_pattern(pattern, env, mode:, allow_pins:, state:)
@@ -63,9 +63,14 @@ module Kapusta
63
63
  end
64
64
 
65
65
  def emit_form_in_sequence(form, env, current_scope, allow_method_definitions:, result_needed: true)
66
- if allow_method_definitions && method_definition_form?(form) && %i[module class].include?(current_scope)
67
- [emit_method_definition(form, env), env]
68
- elsif named_function_form?(form)
66
+ if allow_method_definitions &&
67
+ method_definition_form?(form) &&
68
+ %i[toplevel module class].include?(current_scope)
69
+ code, env = emit_definition_form(form, env, current_scope)
70
+ return [code, env] if code
71
+ end
72
+
73
+ if named_function_form?(form)
69
74
  emit_named_fn_assignment(form, env, current_scope)
70
75
  elsif local_form?(form)
71
76
  code, env = emit_local_form(form, env, current_scope)
@@ -219,6 +224,14 @@ module Kapusta
219
224
  "#{runtime_helper(name)}(#{rendered_args.join(', ')})"
220
225
  end
221
226
 
227
+ def method_binding?(binding)
228
+ binding.is_a?(Env::MethodBinding)
229
+ end
230
+
231
+ def binding_value_code(binding)
232
+ method_binding?(binding) ? "method(#{binding.ruby_name.to_sym.inspect})" : binding
233
+ end
234
+
222
235
  def parse_counted_for_bindings(bindings, env, current_scope)
223
236
  name_sym = bindings[0]
224
237
  loop_env = env.child
@@ -74,7 +74,7 @@ module Kapusta
74
74
  end
75
75
 
76
76
  def wrap_do(forms)
77
- return nil if forms.empty?
77
+ return if forms.empty?
78
78
  return forms.first if forms.length == 1
79
79
 
80
80
  List.new([Sym.new('do'), *forms])
@@ -4,6 +4,7 @@ module Kapusta
4
4
  module Compiler
5
5
  module Runtime
6
6
  HELPER_DEPENDENCIES = {
7
+ stringify: %i[repr],
7
8
  print_values: %i[stringify],
8
9
  concat: %i[stringify],
9
10
  method_path_value: %i[kebab_to_snake],
@@ -63,33 +64,30 @@ module Kapusta
63
64
  end
64
65
  end
65
66
  RUBY
66
- stringify: <<~'RUBY'.chomp,
67
+ stringify: <<~RUBY.chomp,
67
68
  def kap_stringify(value)
68
- render = nil
69
- render = lambda do |item|
70
- case item
71
- when nil then 'nil'
72
- when true then 'true'
73
- when false then 'false'
74
- when String, Symbol then item.inspect
75
- when Array
76
- "[#{item.map { |child| render.call(child) }.join(', ')}]"
77
- when Hash
78
- "{#{item.map { |key, child| "#{render.call(key)}=>#{render.call(child)}" }.join(', ')}}"
79
- else
80
- item.inspect
81
- end
82
- end
83
-
84
69
  case value
85
70
  when nil then 'nil'
86
- when true then 'true'
87
- when false then 'false'
88
- when Array, Hash then render.call(value)
71
+ when Array, Hash then kap_repr(value)
89
72
  else value.to_s
90
73
  end
91
74
  end
92
75
  RUBY
76
+ repr: <<~'RUBY'.chomp,
77
+ def kap_repr(value)
78
+ case value
79
+ when nil then 'nil'
80
+ when true, false then value.to_s
81
+ when String, Symbol then value.inspect
82
+ when Array
83
+ "[#{value.map { |item| kap_repr(item) }.join(', ')}]"
84
+ when Hash
85
+ "{#{value.map { |key, item| "#{kap_repr(key)}=>#{kap_repr(item)}" }.join(', ')}}"
86
+ else
87
+ value.inspect
88
+ end
89
+ end
90
+ RUBY
93
91
  print_values: <<~'RUBY'.chomp,
94
92
  def kap_print_values(*values)
95
93
  $stdout.puts(values.map { |value| kap_stringify(value) }.join("\t"))
@@ -109,7 +107,7 @@ module Kapusta
109
107
  qget_path: <<~RUBY.chomp,
110
108
  def kap_qget_path(obj, keys)
111
109
  keys.each do |key|
112
- return nil if obj.nil?
110
+ return if obj.nil?
113
111
 
114
112
  obj = obj[key]
115
113
  end
@@ -367,10 +365,13 @@ module Kapusta
367
365
 
368
366
  HELPER_SOURCES.each_key do |name|
369
367
  helper_method = :"kap_#{name}"
370
- define_singleton_method(name, instance_method(helper_method))
368
+ body = instance_method(helper_method)
369
+ define_singleton_method(helper_method, body)
370
+ define_singleton_method(name, body)
371
371
  helper_methods << helper_method
372
372
  end
373
373
 
374
+ private_class_method(*helper_methods)
374
375
  send(:private, *helper_methods)
375
376
  end
376
377
  end
data/lib/kapusta/env.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Kapusta
4
4
  class Env
5
+ MethodBinding = Struct.new(:ruby_name)
6
+
5
7
  def initialize(parent = nil)
6
8
  @parent = parent
7
9
  @vars = {}
@@ -26,11 +28,11 @@ module Kapusta
26
28
  end
27
29
 
28
30
  def ruby_name_defined?(name)
29
- @vars.value?(name) || @parent&.ruby_name_defined?(name)
31
+ @vars.any? { |_source_name, value| binding_ruby_name(value) == name } || @parent&.ruby_name_defined?(name)
30
32
  end
31
33
 
32
34
  def local_ruby_name_defined?(name)
33
- @vars.value?(name)
35
+ @vars.any? { |_source_name, value| binding_ruby_name(value) == name }
34
36
  end
35
37
 
36
38
  def set_existing!(name, value)
@@ -46,5 +48,11 @@ module Kapusta
46
48
  def child
47
49
  Env.new(self)
48
50
  end
51
+
52
+ private
53
+
54
+ def binding_ruby_name(value)
55
+ value.respond_to?(:ruby_name) ? value.ruby_name : value
56
+ end
49
57
  end
50
58
  end
@@ -177,15 +177,15 @@ module Kapusta
177
177
  when Sym
178
178
  form.name
179
179
  when Vec
180
- return nil if contains_comments?(form.items)
180
+ return if contains_comments?(form.items)
181
181
 
182
182
  "[#{form.items.map { |item| flat_render(item) }.join(' ')}]"
183
183
  when HashLit
184
- return nil if contains_comments?(form.entries)
184
+ return if contains_comments?(form.entries)
185
185
 
186
186
  "{#{form.pairs.map { |key, value| flat_hash_pair(key, value) }.join(' ')}}"
187
187
  when List
188
- return nil if contains_comments?(form.items)
188
+ return if contains_comments?(form.items)
189
189
  return "##{flat_render(semantic_items(form.items)[1])}" if hashfn_literal?(form)
190
190
 
191
191
  "(#{form.items.map { |item| flat_render(item) }.join(' ')})"
@@ -548,13 +548,12 @@ module Kapusta
548
548
 
549
549
  def render_hanging_pairwise_vec(vec)
550
550
  pairs = vec.items.each_slice(2).to_a
551
- rendered_pairs = pairs.map do |pair|
552
- left, right = pair
553
- return nil unless pair.length == 2
551
+ return unless pairs.all? { |pair| pair.length == 2 }
554
552
 
553
+ rendered_pairs = pairs.map do |left, right|
555
554
  render_binding_pair(left, right)
556
555
  end
557
- return nil if rendered_pairs.any?(&:nil?)
556
+ return if rendered_pairs.any?(&:nil?)
558
557
 
559
558
  lines = ["[#{rendered_pairs.first}"]
560
559
  continuation = ' ' * '(let ['.length
@@ -609,7 +608,7 @@ module Kapusta
609
608
  def render_pair(left, right, indent)
610
609
  left_rendered = flat_render(left) || render(left, indent)
611
610
  right_rendered = flat_render(right) || render(right, indent)
612
- return nil unless single_line?(left_rendered) && single_line?(right_rendered)
611
+ return unless single_line?(left_rendered) && single_line?(right_rendered)
613
612
 
614
613
  pair = "#{left_rendered} #{right_rendered}"
615
614
  fits?(pair, indent) ? pair : nil
@@ -617,12 +616,12 @@ module Kapusta
617
616
 
618
617
  def render_binding_pair(left, right)
619
618
  left_rendered = flat_render(left)
620
- return nil unless left_rendered
619
+ return unless left_rendered
621
620
 
622
621
  right_rendered = render(right, '(let ['.length + left_rendered.length + 1)
623
622
  first_line, *rest = right_rendered.lines(chomp: true)
624
623
  pair = "#{left_rendered} #{first_line}"
625
- return nil unless pair.length <= MAX_WIDTH
624
+ return unless pair.length <= MAX_WIDTH
626
625
 
627
626
  return pair if rest.empty?
628
627
 
@@ -221,9 +221,9 @@ module Kapusta
221
221
  def parse_atom(token)
222
222
  return true if token == 'true'
223
223
  return false if token == 'false'
224
- return nil if token == 'nil'
225
- return token.to_i if token.match?(/\A-?\d+\z/)
226
- return token.to_f if token.match?(/\A-?\d+\.\d+\z/)
224
+ return if token == 'nil'
225
+ return Integer(token, 10) if token.match?(/\A-?\d+\z/)
226
+ return Float(token) if token.match?(/\A-?\d+\.\d+\z/)
227
227
 
228
228
  if token.start_with?(':') && token.length > 1
229
229
  Kapusta.kebab_to_snake(token[1..]).to_sym
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kapusta
4
- VERSION = '0.1.4'
4
+ VERSION = '0.1.5'
5
5
  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.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgenii Morozov
@@ -22,6 +22,7 @@ files:
22
22
  - Gemfile
23
23
  - README.md
24
24
  - Rakefile
25
+ - bin/compile-examples
25
26
  - bin/console
26
27
  - bin/setup
27
28
  - examples/accumulator.kap