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
data/lib/kapusta.rb
CHANGED
|
@@ -12,10 +12,12 @@ module Kapusta
|
|
|
12
12
|
@loaded_kapusta_features = {}
|
|
13
13
|
|
|
14
14
|
def self.eval(source, path: '(eval)', **_opts)
|
|
15
|
+
install!
|
|
15
16
|
Compiler.run(source, path:)
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def self.dofile(path, **_opts)
|
|
20
|
+
install!
|
|
19
21
|
source = File.read(path)
|
|
20
22
|
self.eval(source, path:)
|
|
21
23
|
end
|
|
@@ -25,6 +27,7 @@ module Kapusta
|
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
def self.require(feature, relative_to: nil)
|
|
30
|
+
install!
|
|
28
31
|
feature = feature.to_s
|
|
29
32
|
local_path = resolve_require_path(feature, relative_to:)
|
|
30
33
|
|
|
@@ -35,12 +38,33 @@ module Kapusta
|
|
|
35
38
|
end
|
|
36
39
|
|
|
37
40
|
def self.install!
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
return if @installed
|
|
42
|
+
|
|
43
|
+
@installed = true
|
|
44
|
+
Kernel.module_eval do
|
|
45
|
+
alias_method :__kapusta_original_require_relative, :require_relative
|
|
46
|
+
private :__kapusta_original_require_relative
|
|
47
|
+
|
|
48
|
+
def require_relative(path)
|
|
49
|
+
kap_path = Kapusta.send(:resolve_kap_relative, path, caller_locations(1, 1).first)
|
|
50
|
+
return Kapusta.send(:require_kapusta_file, kap_path) if kap_path
|
|
51
|
+
|
|
52
|
+
__kapusta_original_require_relative(path)
|
|
53
|
+
end
|
|
41
54
|
end
|
|
42
55
|
end
|
|
43
56
|
|
|
57
|
+
def self.resolve_kap_relative(path, location)
|
|
58
|
+
return unless path.is_a?(String) && location
|
|
59
|
+
|
|
60
|
+
base_file = location.absolute_path || location.path
|
|
61
|
+
return unless base_file
|
|
62
|
+
|
|
63
|
+
full = File.expand_path(path, File.dirname(File.expand_path(base_file)))
|
|
64
|
+
candidates = full.end_with?('.kap') ? [full] : ["#{full}.kap"]
|
|
65
|
+
candidates.find { |c| File.file?(c) }
|
|
66
|
+
end
|
|
67
|
+
|
|
44
68
|
def self.resolve_require_path(feature, relative_to:)
|
|
45
69
|
return unless local_feature?(feature)
|
|
46
70
|
|
|
@@ -88,5 +112,5 @@ module Kapusta
|
|
|
88
112
|
end
|
|
89
113
|
|
|
90
114
|
private_class_method :resolve_require_path, :local_feature?, :require_base_dir,
|
|
91
|
-
:existing_feature_path, :require_kapusta_file
|
|
115
|
+
:existing_feature_path, :require_kapusta_file, :resolve_kap_relative
|
|
92
116
|
end
|
data/spec/examples_spec.rb
CHANGED
|
@@ -280,6 +280,10 @@ RSpec.describe 'examples' do
|
|
|
280
280
|
OUT
|
|
281
281
|
end
|
|
282
282
|
|
|
283
|
+
it 'pivot-index.kap' do
|
|
284
|
+
expect(run_example('pivot-index.kap')).to eq("3\n-1\n0\n")
|
|
285
|
+
end
|
|
286
|
+
|
|
283
287
|
it 'points.kap' do
|
|
284
288
|
expect(run_example('points.kap')).to eq(<<~OUT)
|
|
285
289
|
"origin"
|
|
@@ -480,6 +484,10 @@ RSpec.describe 'examples' do
|
|
|
480
484
|
expect(run_example('majority-element.kap')).to eq("3\n2\n1\n")
|
|
481
485
|
end
|
|
482
486
|
|
|
487
|
+
it 'manhattan-distance.kap' do
|
|
488
|
+
expect(run_example('manhattan-distance.kap')).to eq("14\n")
|
|
489
|
+
end
|
|
490
|
+
|
|
483
491
|
it 'plus-one.kap' do
|
|
484
492
|
expect(run_example('plus-one.kap')).to eq(<<~OUT)
|
|
485
493
|
[1, 2, 4]
|
|
@@ -488,4 +496,8 @@ RSpec.describe 'examples' do
|
|
|
488
496
|
[1, 0, 0]
|
|
489
497
|
OUT
|
|
490
498
|
end
|
|
499
|
+
|
|
500
|
+
it 'subtract-product-sum.kap' do
|
|
501
|
+
expect(run_example('subtract-product-sum.kap')).to eq("15\n21\n0\n")
|
|
502
|
+
end
|
|
491
503
|
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.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evgenii Morozov
|
|
@@ -57,6 +57,7 @@ files:
|
|
|
57
57
|
- examples/leap-year.kap
|
|
58
58
|
- examples/length-of-last-word.kap
|
|
59
59
|
- examples/majority-element.kap
|
|
60
|
+
- examples/manhattan-distance.kap
|
|
60
61
|
- examples/match.kap
|
|
61
62
|
- examples/maximum-subarray.kap
|
|
62
63
|
- examples/min-max.kap
|
|
@@ -68,6 +69,7 @@ files:
|
|
|
68
69
|
- examples/pangram.kap
|
|
69
70
|
- examples/pcall.kap
|
|
70
71
|
- examples/pipeline.kap
|
|
72
|
+
- examples/pivot-index.kap
|
|
71
73
|
- examples/plus-one.kap
|
|
72
74
|
- examples/points.kap
|
|
73
75
|
- examples/primes.kap
|
|
@@ -83,6 +85,7 @@ files:
|
|
|
83
85
|
- examples/single-number.kap
|
|
84
86
|
- examples/squares.kap
|
|
85
87
|
- examples/stack.kap
|
|
88
|
+
- examples/subtract-product-sum.kap
|
|
86
89
|
- examples/sum.kap
|
|
87
90
|
- examples/threading.kap
|
|
88
91
|
- examples/tic-tac-toe.kap
|
|
@@ -111,7 +114,6 @@ files:
|
|
|
111
114
|
- lib/kapusta/compiler/emitter/patterns.rb
|
|
112
115
|
- lib/kapusta/compiler/emitter/support.rb
|
|
113
116
|
- lib/kapusta/compiler/normalizer.rb
|
|
114
|
-
- lib/kapusta/compiler/runtime.rb
|
|
115
117
|
- lib/kapusta/env.rb
|
|
116
118
|
- lib/kapusta/error.rb
|
|
117
119
|
- lib/kapusta/formatter.rb
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Kapusta
|
|
4
|
-
module Compiler
|
|
5
|
-
module Runtime
|
|
6
|
-
HELPER_DEPENDENCIES = {
|
|
7
|
-
destructure: %i[destructure_into],
|
|
8
|
-
match_pattern: %i[match_pattern_into]
|
|
9
|
-
}.freeze
|
|
10
|
-
|
|
11
|
-
HELPER_SOURCES = {
|
|
12
|
-
qget_path: <<~RUBY.chomp,
|
|
13
|
-
def kap_qget_path(obj, keys)
|
|
14
|
-
keys.each do |key|
|
|
15
|
-
return if obj.nil?
|
|
16
|
-
|
|
17
|
-
obj = obj[key]
|
|
18
|
-
end
|
|
19
|
-
obj
|
|
20
|
-
end
|
|
21
|
-
RUBY
|
|
22
|
-
ensure_module: <<~RUBY.chomp,
|
|
23
|
-
def kap_ensure_module(holder, path)
|
|
24
|
-
segments = path.split('.')
|
|
25
|
-
last = segments.pop
|
|
26
|
-
scope = holder.is_a?(Module) ? holder : Object
|
|
27
|
-
segments.each do |segment|
|
|
28
|
-
scope =
|
|
29
|
-
if scope.const_defined?(segment, false)
|
|
30
|
-
scope.const_get(segment, false)
|
|
31
|
-
else
|
|
32
|
-
mod = Module.new
|
|
33
|
-
scope.const_set(segment, mod)
|
|
34
|
-
mod
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
if scope.const_defined?(last, false)
|
|
38
|
-
scope.const_get(last, false)
|
|
39
|
-
else
|
|
40
|
-
mod = Module.new
|
|
41
|
-
scope.const_set(last, mod)
|
|
42
|
-
mod
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
RUBY
|
|
46
|
-
ensure_class: <<~RUBY.chomp,
|
|
47
|
-
def kap_ensure_class(holder, path, super_class)
|
|
48
|
-
segments = path.split('.')
|
|
49
|
-
last = segments.pop
|
|
50
|
-
scope = holder.is_a?(Module) ? holder : Object
|
|
51
|
-
segments.each do |segment|
|
|
52
|
-
scope =
|
|
53
|
-
if scope.const_defined?(segment, false)
|
|
54
|
-
scope.const_get(segment, false)
|
|
55
|
-
else
|
|
56
|
-
mod = Module.new
|
|
57
|
-
scope.const_set(segment, mod)
|
|
58
|
-
mod
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
if scope.const_defined?(last, false)
|
|
62
|
-
scope.const_get(last, false)
|
|
63
|
-
else
|
|
64
|
-
klass = Class.new(super_class)
|
|
65
|
-
scope.const_set(last, klass)
|
|
66
|
-
klass
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
RUBY
|
|
70
|
-
destructure: <<~RUBY.chomp,
|
|
71
|
-
def kap_destructure(pattern, value)
|
|
72
|
-
bindings = {}
|
|
73
|
-
kap_destructure_into(pattern, value, bindings)
|
|
74
|
-
bindings
|
|
75
|
-
end
|
|
76
|
-
RUBY
|
|
77
|
-
destructure_into: <<~'RUBY'.chomp,
|
|
78
|
-
def kap_destructure_into(pattern, value, bindings)
|
|
79
|
-
case pattern[0]
|
|
80
|
-
when :sym
|
|
81
|
-
name = pattern[1]
|
|
82
|
-
bindings[name] = value unless name == '_'
|
|
83
|
-
when :vec
|
|
84
|
-
items = pattern[1]
|
|
85
|
-
rest_idx = items.index { |item| item.is_a?(Array) && item[0] == :rest }
|
|
86
|
-
if rest_idx
|
|
87
|
-
before = items[0...rest_idx]
|
|
88
|
-
rest_pattern = items[rest_idx][1]
|
|
89
|
-
before.each_with_index do |item, i|
|
|
90
|
-
kap_destructure_into(item, value ? value[i] : nil, bindings)
|
|
91
|
-
end
|
|
92
|
-
rest_value = value ? (value[rest_idx..] || []) : []
|
|
93
|
-
kap_destructure_into(rest_pattern, rest_value, bindings)
|
|
94
|
-
else
|
|
95
|
-
items.each_with_index do |item, i|
|
|
96
|
-
kap_destructure_into(item, value ? value[i] : nil, bindings)
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
when :hash
|
|
100
|
-
pattern[1].each do |key, subpattern|
|
|
101
|
-
kap_destructure_into(subpattern, value ? value[key] : nil, bindings)
|
|
102
|
-
end
|
|
103
|
-
when :ignore
|
|
104
|
-
nil
|
|
105
|
-
else
|
|
106
|
-
raise "unknown destructure pattern: #{pattern.inspect}"
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
RUBY
|
|
110
|
-
match_pattern: <<~RUBY.chomp,
|
|
111
|
-
def kap_match_pattern(pattern, value)
|
|
112
|
-
bindings = {}
|
|
113
|
-
[kap_match_pattern_into(pattern, value, bindings), bindings]
|
|
114
|
-
end
|
|
115
|
-
RUBY
|
|
116
|
-
match_pattern_into: <<~'RUBY'.chomp
|
|
117
|
-
def kap_match_pattern_into(pattern, value, bindings)
|
|
118
|
-
case pattern[0]
|
|
119
|
-
when :bind
|
|
120
|
-
name = pattern[1]
|
|
121
|
-
allow_nil = pattern[2]
|
|
122
|
-
return false if value.nil? && !allow_nil
|
|
123
|
-
|
|
124
|
-
bindings[name] = value
|
|
125
|
-
true
|
|
126
|
-
when :ref
|
|
127
|
-
bindings.key?(pattern[1]) && bindings[pattern[1]] == value
|
|
128
|
-
when :wild
|
|
129
|
-
true
|
|
130
|
-
when :vec
|
|
131
|
-
return false unless value.is_a?(Array) || value.respond_to?(:to_ary)
|
|
132
|
-
|
|
133
|
-
array = value.is_a?(Array) ? value : value.to_ary
|
|
134
|
-
items = pattern[1]
|
|
135
|
-
rest_idx = items.index { |item| item.is_a?(Array) && item[0] == :rest }
|
|
136
|
-
if rest_idx
|
|
137
|
-
before = items[0...rest_idx]
|
|
138
|
-
rest_pattern = items[rest_idx][1]
|
|
139
|
-
return false if array.length < before.length
|
|
140
|
-
|
|
141
|
-
before.each_with_index do |item, i|
|
|
142
|
-
return false unless kap_match_pattern_into(item, array[i], bindings)
|
|
143
|
-
end
|
|
144
|
-
kap_match_pattern_into(rest_pattern, array[rest_idx..], bindings)
|
|
145
|
-
else
|
|
146
|
-
return false unless array.length >= items.length
|
|
147
|
-
|
|
148
|
-
items.each_with_index do |item, i|
|
|
149
|
-
return false unless kap_match_pattern_into(item, array[i], bindings)
|
|
150
|
-
end
|
|
151
|
-
true
|
|
152
|
-
end
|
|
153
|
-
when :hash
|
|
154
|
-
return false unless value.is_a?(Hash)
|
|
155
|
-
|
|
156
|
-
pattern[1].each do |key, subpattern|
|
|
157
|
-
return false unless value.key?(key)
|
|
158
|
-
return false unless kap_match_pattern_into(subpattern, value[key], bindings)
|
|
159
|
-
end
|
|
160
|
-
true
|
|
161
|
-
when :lit
|
|
162
|
-
value == pattern[1]
|
|
163
|
-
when :pin
|
|
164
|
-
value == pattern[1]
|
|
165
|
-
when :or
|
|
166
|
-
pattern[1].any? do |option|
|
|
167
|
-
option_bindings = bindings.dup
|
|
168
|
-
next false unless kap_match_pattern_into(option, value, option_bindings)
|
|
169
|
-
|
|
170
|
-
bindings.replace(option_bindings)
|
|
171
|
-
true
|
|
172
|
-
end
|
|
173
|
-
else
|
|
174
|
-
raise "bad pattern: #{pattern.inspect}"
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
RUBY
|
|
178
|
-
}.transform_values(&:freeze).freeze
|
|
179
|
-
|
|
180
|
-
module_function
|
|
181
|
-
|
|
182
|
-
def helper_name(name)
|
|
183
|
-
"kap_#{name}"
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def helper_source(helpers)
|
|
187
|
-
ordered = []
|
|
188
|
-
seen = {}
|
|
189
|
-
helpers.each { |name| append_helper_source(name.to_sym, ordered, seen) }
|
|
190
|
-
return '' if ordered.empty?
|
|
191
|
-
|
|
192
|
-
[
|
|
193
|
-
ordered.map { |name| HELPER_SOURCES.fetch(name) }.join("\n\n"),
|
|
194
|
-
"private #{ordered.map { |name| ":#{helper_name(name)}" }.join(', ')}"
|
|
195
|
-
].join("\n\n")
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def append_helper_source(name, ordered, seen)
|
|
199
|
-
return if seen[name]
|
|
200
|
-
|
|
201
|
-
HELPER_DEPENDENCIES.fetch(name, []).each do |dependency|
|
|
202
|
-
append_helper_source(dependency, ordered, seen)
|
|
203
|
-
end
|
|
204
|
-
ordered << name
|
|
205
|
-
seen[name] = true
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
HELPER_SOURCES.each_value do |source|
|
|
209
|
-
module_eval(source, __FILE__, __LINE__)
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
helper_methods = []
|
|
213
|
-
|
|
214
|
-
HELPER_SOURCES.each_key do |name|
|
|
215
|
-
helper_method = :"kap_#{name}"
|
|
216
|
-
body = instance_method(helper_method)
|
|
217
|
-
define_singleton_method(helper_method, body)
|
|
218
|
-
define_singleton_method(name, body)
|
|
219
|
-
helper_methods << helper_method
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
private_class_method(*helper_methods)
|
|
223
|
-
send(:private, *helper_methods)
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
end
|