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.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/examples/counter.kap +1 -2
- data/examples/length-of-last-word.kap +5 -2
- data/examples/manhattan-distance.kap +9 -0
- data/examples/palindrome.kap +2 -3
- data/examples/pangram.kap +6 -5
- data/examples/pivot-index.kap +13 -0
- data/examples/subtract-product-sum.kap +14 -0
- data/lib/kapusta/compiler/emitter/bindings.rb +18 -7
- data/lib/kapusta/compiler/emitter/collections.rb +90 -61
- data/lib/kapusta/compiler/emitter/control_flow.rb +39 -66
- data/lib/kapusta/compiler/emitter/interop.rb +66 -73
- data/lib/kapusta/compiler/emitter/patterns.rb +196 -109
- data/lib/kapusta/compiler/emitter/support.rb +2 -15
- data/lib/kapusta/compiler/emitter.rb +1 -3
- data/lib/kapusta/compiler/normalizer.rb +2 -2
- data/lib/kapusta/compiler.rb +0 -1
- data/lib/kapusta/version.rb +1 -1
- data/lib/kapusta.rb +28 -4
- data/spec/examples_spec.rb +12 -0
- metadata +4 -2
- data/lib/kapusta/compiler/runtime.rb +0 -226
|
@@ -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) }
|
|
21
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
114
|
-
return unless
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
145
|
-
return unless
|
|
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
|
-
|
|
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
|
|
156
|
-
return unless name_sym.is_a?(Sym)
|
|
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
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
lines =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
30
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
40
|
-
|
|
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:
|
|
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
|
|
158
|
+
def compile_native_pattern(pattern, env, mode:, allow_pins:, state:)
|
|
48
159
|
case pattern
|
|
49
|
-
when Sym
|
|
50
|
-
|
|
51
|
-
when
|
|
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
|
-
|
|
165
|
+
compile_native_pin(pattern, env, mode:, allow_pins:)
|
|
61
166
|
elsif or_pattern?(pattern)
|
|
62
|
-
|
|
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
|
-
|
|
169
|
+
compile_native_sequence(pattern.items, env, mode:, allow_pins:, state:)
|
|
67
170
|
end
|
|
68
|
-
when nil
|
|
69
|
-
|
|
70
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
98
|
-
state[:
|
|
99
|
-
|
|
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
|
|
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
|
-
|
|
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 <<
|
|
223
|
+
parts << compile_native_pattern(items[i], env, mode:, allow_pins:, state:)
|
|
112
224
|
i += 1
|
|
113
225
|
end
|
|
114
226
|
end
|
|
115
|
-
|
|
227
|
+
parts << '*' unless has_rest
|
|
228
|
+
"[#{parts.join(', ')}]"
|
|
116
229
|
end
|
|
117
230
|
|
|
118
|
-
def
|
|
119
|
-
|
|
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
|
-
|
|
244
|
+
raise PatternNotTranslatable unless name_sym.is_a?(Sym)
|
|
123
245
|
|
|
124
246
|
binding = env.lookup_if_defined(name_sym.name)
|
|
125
|
-
|
|
247
|
+
raise PatternNotTranslatable unless binding
|
|
126
248
|
|
|
127
|
-
"
|
|
249
|
+
"^(#{binding_value_code(binding)})"
|
|
128
250
|
end
|
|
129
251
|
|
|
130
|
-
def
|
|
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
|
-
|
|
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 =
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
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('
|
|
124
|
+
gensym('thread')
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
def doto(forms)
|
|
128
128
|
value = forms.first
|
|
129
|
-
temp = gensym('
|
|
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..]])
|
data/lib/kapusta/compiler.rb
CHANGED
data/lib/kapusta/version.rb
CHANGED