kapusta 0.2.3 → 0.3.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.
@@ -17,8 +17,9 @@ module Kapusta
17
17
 
18
18
  def emit_safe_lookup(args, env, current_scope)
19
19
  object_code = emit_expr(args[0], env, current_scope)
20
- keys = args[1..].map { |arg| emit_expr(arg, env, current_scope) }.join(', ')
21
- runtime_call(:qget_path, object_code, "[#{keys}]")
20
+ keys = args[1..].map { |arg| emit_expr(arg, env, current_scope) }
21
+ receiver = simple_expression?(object_code) ? object_code : parenthesize(object_code)
22
+ keys.reduce(receiver) { |acc, key| "#{acc}&.[](#{key})" }
22
23
  end
23
24
 
24
25
  BINARY_OPERATOR_METHODS = %w[<=> ** << >> & | ^ === =~].freeze
@@ -53,35 +54,27 @@ module Kapusta
53
54
  end
54
55
 
55
56
  def emit_require(arg, env, current_scope)
56
- path_code =
57
- case arg
58
- when Sym then arg.name.inspect
59
- when Symbol then arg.to_s.inspect
60
- when String then arg.inspect
61
- else "(#{emit_expr(arg, env, current_scope)}).to_s"
62
- end
63
- if kapusta_require?(arg)
64
- return [
65
- 'unless defined?(Kapusta)',
66
- indent('require "kapusta"'),
67
- 'end',
68
- "Kapusta.require(#{path_code}, relative_to: #{@path.inspect})"
69
- ].join("\n")
57
+ literal = require_path_literal(arg)
58
+ if literal&.match?(%r{\A\.\.?/})
59
+ cleaned = literal.delete_suffix('.kap').sub(%r{\A\./}, '')
60
+ return "require_relative #{cleaned.inspect}"
70
61
  end
71
62
 
63
+ path_code =
64
+ if literal
65
+ literal.inspect
66
+ else
67
+ "(#{emit_expr(arg, env, current_scope)}).to_s"
68
+ end
72
69
  "require #{path_code}"
73
70
  end
74
71
 
75
- def kapusta_require?(arg)
76
- path =
77
- case arg
78
- when Sym then arg.name
79
- when Symbol then arg.to_s
80
- when String then arg
81
- end
82
- return false unless path
83
-
84
- path.end_with?('.kap') || path.start_with?('./', '../') || File.absolute_path?(path)
72
+ def require_path_literal(arg)
73
+ case arg
74
+ when Sym then arg.name
75
+ when Symbol then arg.to_s
76
+ when String then arg
77
+ end
85
78
  end
86
79
 
87
80
  def emit_module_expr(args, env)
@@ -97,65 +90,65 @@ module Kapusta
97
90
  end
98
91
 
99
92
  def emit_module_wrapper(name_sym, body)
100
- mod_var = temp('module')
101
- [
102
- '(-> do',
103
- indent("#{mod_var} = #{runtime_call(:ensure_module, 'self', name_sym.name.inspect)}"),
104
- indent("#{mod_var}.module_eval do"),
105
- indent(body, 2),
106
- indent('end'),
107
- indent(mod_var),
108
- 'end).call'
109
- ].join("\n")
93
+ segments = constant_segments(name_sym)
94
+ emit_error!("invalid module name: #{name_sym.name}") unless segments
95
+ inner = build_nested_module(segments, body)
96
+ ['(-> do', indent(inner), indent(segments.join('::')), 'end).call'].join("\n")
110
97
  end
111
98
 
112
99
  def emit_direct_module_header(name_sym, body)
113
- const_name = simple_constant_name(name_sym)
114
- return unless const_name
100
+ segments = constant_segments(name_sym)
101
+ return unless segments
115
102
 
116
- [
117
- "module #{const_name}",
118
- indent(body),
119
- 'end',
120
- const_name
121
- ].join("\n")
103
+ [build_nested_module(segments, body), segments.join('::')].join("\n")
122
104
  end
123
105
 
124
106
  def emit_class_wrapper(name_sym, supers, env, body)
125
- klass_var = temp('class')
126
- super_code =
127
- if supers.is_a?(Vec) && !supers.items.empty?
128
- emit_expr(supers.items.first, env, :toplevel)
129
- else
130
- 'Object'
131
- end
132
- [
133
- '(-> do',
134
- indent("#{klass_var} = #{runtime_call(:ensure_class, 'self', name_sym.name.inspect, super_code)}"),
135
- indent("#{klass_var}.class_eval do"),
136
- indent(body, 2),
137
- indent('end'),
138
- indent(klass_var),
139
- 'end).call'
140
- ].join("\n")
107
+ segments = constant_segments(name_sym)
108
+ emit_error!("invalid class name: #{name_sym.name}") unless segments
109
+ super_code = class_super_code(supers, env)
110
+ inner = build_nested_class(segments, super_code, body)
111
+ ['(-> do', indent(inner), indent(segments.join('::')), 'end).call'].join("\n")
141
112
  end
142
113
 
143
- def emit_direct_class_header(name_sym, supers, body)
144
- const_name = simple_constant_name(name_sym)
145
- return unless const_name && supers.nil?
114
+ def emit_direct_class_header(name_sym, supers, body, env)
115
+ segments = constant_segments(name_sym)
116
+ return unless segments
146
117
 
147
- [
148
- "class #{const_name}",
149
- indent(body),
150
- 'end',
151
- const_name
152
- ].join("\n")
118
+ super_code = class_super_code(supers, env)
119
+ [build_nested_class(segments, super_code, body), segments.join('::')].join("\n")
153
120
  end
154
121
 
155
- def simple_constant_name(name_sym)
156
- return unless name_sym.is_a?(Sym) && name_sym.name.match?(/\A[A-Z]\w*\z/)
122
+ def constant_segments(name_sym)
123
+ return unless name_sym.is_a?(Sym)
157
124
 
158
- name_sym.name
125
+ segments = name_sym.dotted? ? name_sym.segments : [name_sym.name]
126
+ return unless segments.all? { |s| s.match?(/\A[A-Z]\w*\z/) }
127
+
128
+ segments
129
+ end
130
+
131
+ def class_super_code(supers, env)
132
+ return unless supers.is_a?(Vec) && !supers.items.empty?
133
+
134
+ emit_expr(supers.items.first, env, :toplevel)
135
+ end
136
+
137
+ def build_nested_module(segments, body)
138
+ inner = ["module #{segments.last}", indent(body), 'end'].join("\n")
139
+ wrap_in_modules(segments[0...-1], inner)
140
+ end
141
+
142
+ def build_nested_class(segments, super_code, body)
143
+ header = super_code ? "class #{segments.last} < #{super_code}" : "class #{segments.last}"
144
+ inner = [header, indent(body), 'end'].join("\n")
145
+ wrap_in_modules(segments[0...-1], inner)
146
+ end
147
+
148
+ def wrap_in_modules(parents, inner)
149
+ parents.reverse.reduce(inner) do |acc, mod_name|
150
+ ["module #{mod_name}", indent(acc), 'end'].join("\n")
151
+ end
159
152
  end
160
153
 
161
154
  def emit_try(args, env, current_scope)
@@ -491,7 +484,7 @@ module Kapusta
491
484
  code.match?(/\A\$[a-zA-Z_]\w*\z/) ||
492
485
  code.match?(/\A[A-Z]\w*(?:::[A-Z]\w*)*\z/) ||
493
486
  code.match?(/\A[a-z_]\w*[!?=]?\([^()\n]*\)\z/) ||
494
- code.match?(/\A\d+(?:\.\d+)?\z/) ||
487
+ code.match?(/\A-?\d+(?:\.\d+)?\z/) ||
495
488
  code.match?(/\A[a-z_]\w*(?:\.[a-z_]\w*[!?=]?(?:\([^()\n]*\))?|\[[^\[\]]*\])+\z/) ||
496
489
  code.match?(/\A[A-Z]\w*(?:::[A-Z]\w*)*(?:\.[a-z_]\w*[!?=]?(?:\([^()\n]*\))?|\[[^\[\]]*\])+\z/) ||
497
490
  code.match?(/\A:[a-zA-Z_]\w*[!?=]?\z/) ||
@@ -11,174 +11,261 @@ module Kapusta
11
11
  return ['', env] if pattern.name == '_'
12
12
 
13
13
  ruby_name = define_local(env, pattern)
14
- ["#{ruby_name} = #{value_code}", env]
14
+ return ["#{ruby_name} = #{value_code}", env]
15
+ end
16
+
17
+ native = try_emit_native_pattern_bind(pattern, value_code, env)
18
+ return native if native
19
+
20
+ emit_error!("destructure pattern this compiler cannot translate: #{pattern.inspect}")
21
+ end
22
+
23
+ def try_emit_native_pattern_bind(pattern, value_code, env)
24
+ case pattern
25
+ when Vec
26
+ try_emit_native_vec_bind(pattern, value_code, env)
27
+ when HashLit
28
+ try_emit_native_hash_bind(pattern, value_code, env)
29
+ end
30
+ rescue PatternNotTranslatable
31
+ nil
32
+ end
33
+
34
+ def try_emit_native_vec_bind(pattern, value_code, env)
35
+ parts = []
36
+ deferred = []
37
+ current_env = env
38
+ items = pattern.items
39
+ i = 0
40
+ while i < items.length
41
+ if items[i].is_a?(Sym) && items[i].name == '&'
42
+ sub = items[i + 1]
43
+ raise PatternNotTranslatable unless sub.is_a?(Sym)
44
+
45
+ parts << native_rest_target(sub, current_env)
46
+ i += 2
47
+ else
48
+ code, current_env, follow_up = native_destructure_target(items[i], current_env, allow_follow_up: true)
49
+ parts << code
50
+ deferred << follow_up if follow_up
51
+ i += 1
52
+ end
53
+ end
54
+ if deferred.empty?
55
+ ["#{parts.join(', ')} = #{value_code}", current_env]
15
56
  else
16
- bindings_var = temp('bindings')
17
- current_env = env
18
- lines = [
19
- "#{bindings_var} = #{runtime_call(:destructure, emit_pattern(pattern), value_code)}"
20
- ]
21
- pattern_names(pattern).each do |name|
22
- ruby_name = define_local(current_env, name)
23
- lines << "#{ruby_name} = #{bindings_var}.fetch(#{name.inspect})"
57
+ arr_var = simple_expression?(value_code) ? value_code : temp('array')
58
+ lines = []
59
+ lines << "#{arr_var} = #{value_code}" unless arr_var == value_code
60
+ lines << "#{parts.join(', ')} = #{arr_var}"
61
+ deferred.each do |follow_up|
62
+ sub_code, current_env = follow_up.call(current_env)
63
+ lines << sub_code
24
64
  end
25
65
  [lines.join("\n"), current_env]
26
66
  end
27
67
  end
28
68
 
29
- def emit_bindings_from_match(binding_names, bindings_var, env)
30
- current_env = env
69
+ def try_emit_native_hash_bind(pattern, value_code, env)
70
+ pairs = pattern.pairs
71
+ raise PatternNotTranslatable if pairs.empty?
72
+
73
+ temp_var = simple_expression?(value_code) ? value_code : temp('hash')
31
74
  lines = []
32
- binding_names.each do |name|
33
- ruby_name = define_local(current_env, name)
34
- lines << "#{ruby_name} = #{bindings_var}.fetch(#{name.inspect})"
75
+ lines << "#{temp_var} = #{value_code}" unless temp_var == value_code
76
+ current_env = env
77
+ pairs.each do |key, sub|
78
+ access = "#{temp_var}[#{key.inspect}]"
79
+ if sub.is_a?(Sym)
80
+ raise PatternNotTranslatable if sub.name == '_'
81
+
82
+ bind_name = sub.name.start_with?('?') ? sub.name.delete_prefix('?') : sub.name
83
+ ruby_name = define_local(current_env, bind_name)
84
+ lines << "#{ruby_name} = #{access}"
85
+ else
86
+ sub_code, current_env = try_emit_native_pattern_bind(sub, access, current_env) ||
87
+ raise(PatternNotTranslatable)
88
+ lines << sub_code
89
+ end
35
90
  end
36
91
  [lines.join("\n"), current_env]
37
92
  end
38
93
 
39
- def pattern_match_plan(pattern, env, mode:, allow_pins:)
40
- state = { bound_names: {}, binding_names: [] }
94
+ def native_destructure_target(pattern, env, allow_follow_up: false)
95
+ case pattern
96
+ when Sym
97
+ return ['_', env, nil] if pattern.name == '_'
98
+
99
+ bind_name = pattern.name.start_with?('?') ? pattern.name.delete_prefix('?') : pattern.name
100
+ ruby_name = define_local(env, bind_name)
101
+ [ruby_name, env, nil]
102
+ when Vec
103
+ inner = []
104
+ current = env
105
+ deferred = []
106
+ items = pattern.items
107
+ i = 0
108
+ while i < items.length
109
+ if items[i].is_a?(Sym) && items[i].name == '&'
110
+ inner << native_rest_target(items[i + 1], current)
111
+ i += 2
112
+ else
113
+ code, current, follow_up = native_destructure_target(items[i], current)
114
+ inner << code
115
+ deferred << follow_up if follow_up
116
+ i += 1
117
+ end
118
+ end
119
+ raise PatternNotTranslatable unless deferred.empty?
120
+
121
+ ["(#{inner.join(', ')})", current, nil]
122
+ when HashLit
123
+ raise PatternNotTranslatable unless allow_follow_up
124
+
125
+ slot = temp('slot')
126
+ follow_up = lambda do |outer_env|
127
+ try_emit_native_hash_bind(pattern, slot, outer_env) || raise(PatternNotTranslatable)
128
+ end
129
+ [slot, env, follow_up]
130
+ else
131
+ raise PatternNotTranslatable
132
+ end
133
+ end
134
+
135
+ def native_rest_target(sym, env)
136
+ raise PatternNotTranslatable unless sym.is_a?(Sym)
137
+
138
+ return '*' if sym.name == '_'
139
+
140
+ bind_name = sym.name.start_with?('?') ? sym.name.delete_prefix('?') : sym.name
141
+ "*#{define_local(env, bind_name)}"
142
+ end
143
+
144
+ class PatternNotTranslatable < StandardError; end
145
+
146
+ def native_pattern_plan(pattern, env, mode:, allow_pins:)
147
+ state = { bound_names: {}, binding_names: [], guards: [] }
148
+ ruby_pattern = compile_native_pattern(pattern, env, mode:, allow_pins:, state:)
41
149
  {
42
- pattern: emit_match_pattern(pattern, env, mode:, allow_pins:, state:),
150
+ pattern: ruby_pattern,
151
+ guards: state[:guards],
43
152
  bindings: state[:binding_names]
44
153
  }
154
+ rescue PatternNotTranslatable
155
+ nil
45
156
  end
46
157
 
47
- def emit_match_pattern(pattern, env, mode:, allow_pins:, state:)
158
+ def compile_native_pattern(pattern, env, mode:, allow_pins:, state:)
48
159
  case pattern
49
- when Sym
50
- emit_symbol_match_pattern(pattern, env, mode:, state:)
51
- when Vec
52
- emit_sequence_match_pattern(pattern.items, env, mode:, allow_pins:, state:)
53
- when HashLit
54
- pairs = pattern.pairs.map do |key, value|
55
- "[#{key.inspect}, #{emit_match_pattern(value, env, mode:, allow_pins:, state:)}]"
56
- end
57
- "[:hash, [#{pairs.join(', ')}]]"
160
+ when Sym then compile_native_symbol(pattern, env, mode:, state:)
161
+ when Vec then compile_native_sequence(pattern.items, env, mode:, allow_pins:, state:)
162
+ when HashLit then compile_native_hash(pattern, env, mode:, allow_pins:, state:)
58
163
  when List
59
164
  if pin_pattern?(pattern)
60
- emit_pin_match_pattern(pattern, env, mode:, allow_pins:)
165
+ compile_native_pin(pattern, env, mode:, allow_pins:)
61
166
  elsif or_pattern?(pattern)
62
- emit_or_match_pattern(pattern, env, mode:, allow_pins:, state:)
63
- elsif where_pattern?(pattern)
64
- emit_error!('`where` is only valid as a case/match clause head')
167
+ compile_native_or(pattern, env, mode:, allow_pins:, state:)
65
168
  else
66
- emit_sequence_match_pattern(pattern.items, env, mode:, allow_pins:, state:)
169
+ compile_native_sequence(pattern.items, env, mode:, allow_pins:, state:)
67
170
  end
68
- when nil
69
- '[:lit, nil]'
70
- when Symbol, String, Numeric, true, false
71
- "[:lit, #{pattern.inspect}]"
72
- else
73
- emit_error!("bad pattern: #{pattern.inspect}")
171
+ when nil then 'nil'
172
+ when Symbol, String, Numeric, true, false then pattern.inspect
173
+ else raise PatternNotTranslatable
74
174
  end
75
175
  end
76
176
 
77
- def emit_symbol_match_pattern(pattern, env, mode:, state:)
177
+ def compile_native_symbol(pattern, env, mode:, state:)
78
178
  name = pattern.name
179
+ return '_' if name == '_'
79
180
 
80
- if name == '_'
81
- '[:wild]'
82
- elsif nil_allowing_pattern_name?(name)
181
+ if nil_allowing_pattern_name?(name)
83
182
  bind_name = name.start_with?('?') ? name.delete_prefix('?') : name
84
- emit_named_match_pattern(bind_name, env, mode:, state:, allow_nil: true, prefer_pin: false)
85
- else
86
- emit_named_match_pattern(name, env, mode:, state:, allow_nil: false, prefer_pin: true)
87
- end
88
- end
183
+ raise PatternNotTranslatable if state[:bound_names].key?(bind_name)
89
184
 
90
- def emit_named_match_pattern(name, env, mode:, state:, allow_nil:, prefer_pin:)
91
- binding = prefer_pin && mode == :match ? env.lookup_if_defined(name) : nil
92
- if state[:bound_names].key?(name)
93
- "[:ref, #{name.inspect}]"
94
- elsif binding
95
- "[:pin, #{binding_value_code(binding)}]"
185
+ state[:bound_names][bind_name] = true
186
+ state[:binding_names] << bind_name
187
+ sanitize_local(bind_name)
96
188
  else
97
- state[:bound_names][name] = true
98
- state[:binding_names] << name
99
- "[:bind, #{name.inspect}, #{allow_nil}]"
189
+ binding = mode == :match ? env.lookup_if_defined(name) : nil
190
+ if state[:bound_names].key?(name)
191
+ raise PatternNotTranslatable
192
+ elsif binding
193
+ "^(#{binding_value_code(binding)})"
194
+ else
195
+ state[:bound_names][name] = true
196
+ state[:binding_names] << name
197
+ ruby = sanitize_local(name)
198
+ state[:guards] << "!#{ruby}.nil?"
199
+ ruby
200
+ end
100
201
  end
101
202
  end
102
203
 
103
- def emit_sequence_match_pattern(items, env, mode:, allow_pins:, state:)
204
+ def compile_native_sequence(items, env, mode:, allow_pins:, state:)
104
205
  parts = []
206
+ has_rest = false
105
207
  i = 0
106
208
  while i < items.length
107
209
  if rest_pattern_marker?(items, i)
108
- parts << "[:rest, #{emit_match_pattern(items[i + 1], env, mode:, allow_pins:, state:)}]"
210
+ has_rest = true
211
+ sub = items[i + 1]
212
+ raise PatternNotTranslatable unless sub.is_a?(Sym)
213
+
214
+ if sub.name == '_'
215
+ parts << '*'
216
+ else
217
+ state[:bound_names][sub.name] = true
218
+ state[:binding_names] << sub.name
219
+ parts << "*#{sanitize_local(sub.name)}"
220
+ end
109
221
  i += 2
110
222
  else
111
- parts << emit_match_pattern(items[i], env, mode:, allow_pins:, state:)
223
+ parts << compile_native_pattern(items[i], env, mode:, allow_pins:, state:)
112
224
  i += 1
113
225
  end
114
226
  end
115
- "[:vec, [#{parts.join(', ')}]]"
227
+ parts << '*' unless has_rest
228
+ "[#{parts.join(', ')}]"
116
229
  end
117
230
 
118
- def emit_pin_match_pattern(pattern, env, mode:, allow_pins:)
119
- emit_error!('pin patterns are only supported inside `case` guards') unless allow_pins && mode == :case
231
+ def compile_native_hash(pattern, env, mode:, allow_pins:, state:)
232
+ pairs = pattern.pairs.map do |key, value|
233
+ raise PatternNotTranslatable unless key.is_a?(Symbol)
234
+
235
+ "#{key}: #{compile_native_pattern(value, env, mode:, allow_pins:, state:)}"
236
+ end
237
+ "{#{pairs.join(', ')}}"
238
+ end
239
+
240
+ def compile_native_pin(pattern, env, mode:, allow_pins:)
241
+ raise PatternNotTranslatable unless allow_pins && mode == :case
120
242
 
121
243
  name_sym = pattern.items[1]
122
- emit_error!("bad pin pattern: #{pattern.inspect}") unless name_sym.is_a?(Sym)
244
+ raise PatternNotTranslatable unless name_sym.is_a?(Sym)
123
245
 
124
246
  binding = env.lookup_if_defined(name_sym.name)
125
- emit_error!("cannot pin undefined name: #{name_sym.name}") unless binding
247
+ raise PatternNotTranslatable unless binding
126
248
 
127
- "[:pin, #{binding_value_code(binding)}]"
249
+ "^(#{binding_value_code(binding)})"
128
250
  end
129
251
 
130
- def emit_or_match_pattern(pattern, env, mode:, allow_pins:, state:)
131
- initial_names = state[:binding_names].length
252
+ def compile_native_or(pattern, env, mode:, allow_pins:, state:)
132
253
  initial_bound = state[:bound_names].dup
133
- canonical_names = nil
254
+ initial_names = state[:binding_names].length
255
+ initial_guards = state[:guards].length
134
256
  variants = pattern.items[1..].map do |subpattern|
135
257
  alt_state = {
136
258
  bound_names: initial_bound.dup,
137
- binding_names: state[:binding_names].dup
259
+ binding_names: state[:binding_names].dup,
260
+ guards: state[:guards].dup
138
261
  }
139
- compiled = emit_match_pattern(subpattern, env, mode:, allow_pins:, state: alt_state)
140
- alt_names = alt_state[:binding_names][initial_names..]
141
- canonical_names ||= alt_names
142
- emit_error!('all `or` patterns must bind the same names') if canonical_names.sort != alt_names.sort
262
+ compiled = compile_native_pattern(subpattern, env, mode:, allow_pins:, state: alt_state)
263
+ raise PatternNotTranslatable if alt_state[:binding_names].length > initial_names
264
+ raise PatternNotTranslatable if alt_state[:guards].length > initial_guards
143
265
 
144
266
  compiled
145
267
  end
146
-
147
- canonical_names.each do |name|
148
- state[:bound_names][name] = true
149
- state[:binding_names] << name
150
- end
151
- "[:or, [#{variants.join(', ')}]]"
152
- end
153
-
154
- def emit_pattern(pattern)
155
- case pattern
156
- when Sym
157
- pattern.name == '_' ? '[:sym, "_"]' : "[:sym, #{pattern.name.inspect}]"
158
- when Vec
159
- parts = []
160
- items = pattern.items
161
- i = 0
162
- while i < items.length
163
- if items[i].is_a?(Sym) && items[i].name == '&'
164
- parts << "[:rest, #{emit_pattern(items[i + 1])}]"
165
- i += 2
166
- else
167
- parts << emit_pattern(items[i])
168
- i += 1
169
- end
170
- end
171
- "[:vec, [#{parts.join(', ')}]]"
172
- when HashLit
173
- pairs = pattern.pairs.map { |key, value| "[#{key.inspect}, #{emit_pattern(value)}]" }
174
- "[:hash, [#{pairs.join(', ')}]]"
175
- when nil
176
- '[:nil]'
177
- when Symbol, String, Numeric, true, false
178
- "[:lit, #{pattern.inspect}]"
179
- else
180
- emit_error!("bad pattern: #{pattern.inspect}")
181
- end
268
+ "(#{variants.join(' | ')})"
182
269
  end
183
270
 
184
271
  def pattern_names(pattern)
@@ -62,7 +62,7 @@ module Kapusta
62
62
  else
63
63
  name_sym, supers, = split_class_args(form.items[1..])
64
64
  body = emit_forms_with_headers(remaining_forms, env, :class, result: false)
65
- emit_direct_class_header(name_sym, supers, body) || emit_class_wrapper(name_sym, supers, env, body)
65
+ emit_direct_class_header(name_sym, supers, body, env) || emit_class_wrapper(name_sym, supers, env, body)
66
66
  end
67
67
  end
68
68
 
@@ -128,7 +128,7 @@ module Kapusta
128
128
  return [emit_for_statement(form.rest, env, current_scope), env]
129
129
  when 'each'
130
130
  code = emit_each_statement(form.rest, env, current_scope)
131
- return ["#{code}\nnil", env] if result_needed
131
+ return ["#{code}\nnil", env] if result_needed && current_scope != :toplevel
132
132
 
133
133
  return [code, env]
134
134
  end
@@ -219,19 +219,6 @@ module Kapusta
219
219
  source_name.is_a?(GeneratedSym)
220
220
  end
221
221
 
222
- def runtime_helper(name)
223
- helper = name.to_sym
224
- @runtime_helpers << helper unless @runtime_helpers.include?(helper)
225
- Runtime.helper_name(helper)
226
- end
227
-
228
- def runtime_call(name, *args)
229
- rendered_args = args.map do |arg|
230
- arg.is_a?(Array) ? "[#{arg.join(', ')}]" : arg || 'nil'
231
- end
232
- "#{runtime_helper(name)}(#{rendered_args.join(', ')})"
233
- end
234
-
235
222
  def method_binding?(binding)
236
223
  binding.is_a?(Env::MethodBinding)
237
224
  end
@@ -27,14 +27,12 @@ module Kapusta
27
27
  def initialize(path:)
28
28
  @path = path
29
29
  @temp_index = 0
30
- @runtime_helpers = []
31
30
  end
32
31
 
33
32
  def emit_file(forms)
34
33
  env = Env.new
35
34
  body = emit_forms_with_headers(forms, env, :toplevel)
36
- helpers = Runtime.helper_source(@runtime_helpers)
37
- [helpers, body].reject(&:empty?).join("\n\n") << "\n"
35
+ "#{body}\n"
38
36
  end
39
37
  end
40
38
  end
@@ -121,12 +121,12 @@ module Kapusta
121
121
  end
122
122
 
123
123
  def thread_temp
124
- gensym('kap_thread')
124
+ gensym('thread')
125
125
  end
126
126
 
127
127
  def doto(forms)
128
128
  value = forms.first
129
- temp = gensym('kap_doto')
129
+ temp = gensym('doto')
130
130
  body = forms[1..].map do |form|
131
131
  if form.is_a?(List)
132
132
  List.new([form.items[0], temp, *form.items[1..]])
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'error'
4
- require_relative 'compiler/runtime'
5
4
  require_relative 'compiler/normalizer'
6
5
  require_relative 'compiler/emitter'
7
6
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kapusta
4
- VERSION = '0.2.3'
4
+ VERSION = '0.3.0'
5
5
  end