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.
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
- @install ||= begin
39
- Kernel.require 'rubygems'
40
- true
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
@@ -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.2.3
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