kapusta 0.10.0 → 0.11.1
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 +2 -0
- data/examples/accumulator.kap +2 -0
- data/examples/bank-account.kap +2 -0
- data/examples/bst-iterator.kap +52 -0
- data/examples/circle.kap +2 -0
- data/examples/counter.kap +2 -0
- data/examples/hit-counter.kap +2 -0
- data/examples/module-header.kap +4 -2
- data/examples/mruby-runtime-examples.txt +3 -0
- data/examples/parking-system.kap +2 -0
- data/examples/recent-counter.kap +17 -0
- data/examples/scopes.kap +2 -0
- data/examples/signal-harvest.kap +16 -0
- data/examples/stack.kap +2 -0
- data/examples/valid-parentheses-1.kap +2 -0
- data/lib/kapusta/compiler/emitter/bindings.rb +1 -4
- data/lib/kapusta/compiler/emitter/control_flow.rb +25 -30
- data/lib/kapusta/compiler/emitter/expressions.rb +2 -0
- data/lib/kapusta/compiler/emitter/interop.rb +23 -15
- data/lib/kapusta/compiler/emitter/patterns.rb +29 -52
- data/lib/kapusta/compiler/emitter/support.rb +106 -44
- data/lib/kapusta/compiler/macro_expander.rb +4 -12
- data/lib/kapusta/compiler/macro_lowerer.rb +4 -12
- data/lib/kapusta/compiler/normalizer.rb +9 -17
- data/lib/kapusta/compiler.rb +2 -2
- data/lib/kapusta/errors.rb +4 -0
- data/lib/kapusta/formatter.rb +1 -1
- data/lib/kapusta/lsp/definition.rb +17 -0
- data/lib/kapusta/lsp/rename.rb +3 -1
- data/lib/kapusta/lsp/scope_walker.rb +79 -46
- data/lib/kapusta/lsp/workspace_index.rb +2 -13
- data/lib/kapusta/lsp.rb +17 -16
- data/lib/kapusta/support.rb +8 -0
- data/lib/kapusta/version.rb +1 -1
- data/spec/examples_errors_spec.rb +25 -0
- data/spec/examples_spec.rb +21 -0
- data/spec/lsp_spec.rb +71 -3
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 85bcd0988a353fb851e5ff955c2b5196f39193e1c3c8fb6ca4a111d6260e774d
|
|
4
|
+
data.tar.gz: 80efbc138038947b866eaac96ef21d5cdccd04a461a25f2e17cbd7ce925ceaec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 16d02e652b9221990ac027423e677a8f27c285d582c6bfb368f585140305e1c48dff5add4a8491a7996b5be99e34efd761e7fb280d8932fc6548622939b166b4
|
|
7
|
+
data.tar.gz: 8ab10f9991b7029d9366e47d434a9e9166568b870369f1fe585007fbe2cf9b750c84e8eb3b8fb015db255977eef80fd3255750fa99006d577a9564b640d84319
|
data/README.md
CHANGED
|
@@ -110,6 +110,8 @@ Kapusta keeps most core Fennel forms. The main differences come from Ruby's runt
|
|
|
110
110
|
Kapusta-specific additions:
|
|
111
111
|
|
|
112
112
|
- `module` and `class` for Ruby host structure, including file-header forms
|
|
113
|
+
- `(end)` closes a bodyless file-header
|
|
114
|
+
- `(defn name ...)` or `(fn class.name ..)`
|
|
113
115
|
- `ivar` or `@var` / `cvar` or `@@var` / `gvar` or `$var`
|
|
114
116
|
- `try` / `catch` / `finally` plus `raise` for exceptions
|
|
115
117
|
- `(ruby "...")` raw host escape hatch
|
data/examples/accumulator.kap
CHANGED
data/examples/bank-account.kap
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
(class TreeNode)
|
|
2
|
+
|
|
3
|
+
(fn initialize [val left right]
|
|
4
|
+
(set @val val)
|
|
5
|
+
(set @left left)
|
|
6
|
+
(set @right right))
|
|
7
|
+
|
|
8
|
+
(fn val [] @val)
|
|
9
|
+
(fn left [] @left)
|
|
10
|
+
(fn right [] @right)
|
|
11
|
+
|
|
12
|
+
(end)
|
|
13
|
+
|
|
14
|
+
(class BSTIterator)
|
|
15
|
+
|
|
16
|
+
(fn initialize [root]
|
|
17
|
+
(set @stack [])
|
|
18
|
+
(self.push-left root))
|
|
19
|
+
|
|
20
|
+
(fn push-left [node]
|
|
21
|
+
(var n node)
|
|
22
|
+
(while n
|
|
23
|
+
(let [stack @stack]
|
|
24
|
+
(stack.push n))
|
|
25
|
+
(set n (n.left))))
|
|
26
|
+
|
|
27
|
+
(fn next []
|
|
28
|
+
(let [stack @stack
|
|
29
|
+
node (stack.pop)]
|
|
30
|
+
(self.push-left (node.right))
|
|
31
|
+
(node.val)))
|
|
32
|
+
|
|
33
|
+
(fn has-next? []
|
|
34
|
+
(let [stack @stack]
|
|
35
|
+
(not (stack.empty?))))
|
|
36
|
+
|
|
37
|
+
(end)
|
|
38
|
+
|
|
39
|
+
(let [root (TreeNode.new 7
|
|
40
|
+
(TreeNode.new 3 nil nil)
|
|
41
|
+
(TreeNode.new 15
|
|
42
|
+
(TreeNode.new 9 nil nil)
|
|
43
|
+
(TreeNode.new 20 nil nil)))
|
|
44
|
+
it (BSTIterator.new root)]
|
|
45
|
+
(print (it.next))
|
|
46
|
+
(print (it.next))
|
|
47
|
+
(print (it.has-next?))
|
|
48
|
+
(print (it.next))
|
|
49
|
+
(print (it.has-next?))
|
|
50
|
+
(print (it.next))
|
|
51
|
+
(print (it.next))
|
|
52
|
+
(print (it.has-next?)))
|
data/examples/circle.kap
CHANGED
data/examples/counter.kap
CHANGED
data/examples/hit-counter.kap
CHANGED
data/examples/module-header.kap
CHANGED
|
@@ -9,6 +9,7 @@ best-time-to-buy-sell-stock
|
|
|
9
9
|
binary-search
|
|
10
10
|
binary-to-decimal
|
|
11
11
|
block-sort
|
|
12
|
+
bst-iterator
|
|
12
13
|
calc
|
|
13
14
|
circle
|
|
14
15
|
classify-wallet
|
|
@@ -65,6 +66,7 @@ points
|
|
|
65
66
|
power-of-three
|
|
66
67
|
primes
|
|
67
68
|
raindrops
|
|
69
|
+
recent-counter
|
|
68
70
|
record
|
|
69
71
|
reverse-integer
|
|
70
72
|
roman-to-integer
|
|
@@ -72,6 +74,7 @@ ruby-eval
|
|
|
72
74
|
safe-lookup
|
|
73
75
|
scopes
|
|
74
76
|
shapes
|
|
77
|
+
signal-harvest
|
|
75
78
|
single-number
|
|
76
79
|
squares
|
|
77
80
|
stack
|
data/examples/parking-system.kap
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
(class RecentCounter)
|
|
2
|
+
(fn initialize [] (set @pings []))
|
|
3
|
+
(fn ping [t]
|
|
4
|
+
(let [pings @pings]
|
|
5
|
+
(pings.push t)
|
|
6
|
+
(while (< (. pings 0) (- t 3000))
|
|
7
|
+
(pings.shift))
|
|
8
|
+
(length pings)))
|
|
9
|
+
(defn warm [history]
|
|
10
|
+
(let [c (RecentCounter.new)]
|
|
11
|
+
(each [_ t (ipairs history)] (c.ping t))
|
|
12
|
+
c))
|
|
13
|
+
(end)
|
|
14
|
+
|
|
15
|
+
(let [c (RecentCounter.warm [100 200 300])]
|
|
16
|
+
(print (c.ping 3001))
|
|
17
|
+
(print (c.ping 3002)))
|
data/examples/scopes.kap
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
(module SignalHarvest)
|
|
2
|
+
|
|
3
|
+
(local SCREEN-W 1280)
|
|
4
|
+
(local TARGET-SCORE 36)
|
|
5
|
+
|
|
6
|
+
(defn cell-width [columns]
|
|
7
|
+
(/ SCREEN-W columns))
|
|
8
|
+
|
|
9
|
+
(defn win? [score]
|
|
10
|
+
(>= score TARGET-SCORE))
|
|
11
|
+
|
|
12
|
+
(end)
|
|
13
|
+
|
|
14
|
+
(print (SignalHarvest.cell-width 32))
|
|
15
|
+
(print (SignalHarvest.win? 40))
|
|
16
|
+
(print (SignalHarvest.win? 12))
|
data/examples/stack.kap
CHANGED
|
@@ -148,10 +148,7 @@ module Kapusta
|
|
|
148
148
|
return unless ruby_name
|
|
149
149
|
return if captures_outer_binding?(body, env, pattern_names(pattern))
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
params = pattern.items.map { |sym| define_local(body_env, sym.name, shadow: true) }
|
|
153
|
-
body_code, = emit_sequence(body, body_env, :toplevel,
|
|
154
|
-
allow_method_definitions: false, result: false)
|
|
151
|
+
params, body_code = build_simple_block_parts(pattern, body, env, :toplevel)
|
|
155
152
|
header = params.empty? ? "def #{ruby_name}" : "def #{ruby_name}(#{params.join(', ')})"
|
|
156
153
|
[
|
|
157
154
|
header,
|
|
@@ -111,27 +111,30 @@ module Kapusta
|
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
def try_emit_native_case(value_var, clauses, env, current_scope, mode)
|
|
114
|
+
arms = collect_case_arms(clauses) do |pattern, body, where_guards|
|
|
115
|
+
try_native_arm(pattern, body, where_guards, env, current_scope, mode)
|
|
116
|
+
end
|
|
117
|
+
return unless arms
|
|
118
|
+
|
|
119
|
+
arms << ['else', indent('nil')].join("\n") unless wildcard_last?(clauses)
|
|
120
|
+
["case #{value_var}", *arms, 'end'].join("\n")
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def collect_case_arms(clauses)
|
|
114
124
|
arms = []
|
|
115
125
|
i = 0
|
|
116
126
|
while i < clauses.length
|
|
117
127
|
pattern = clauses[i]
|
|
118
128
|
body = clauses[i + 1]
|
|
119
|
-
inner, where_guards =
|
|
120
|
-
[pattern.items[1], pattern.items[2..]]
|
|
121
|
-
else
|
|
122
|
-
[pattern, []]
|
|
123
|
-
end
|
|
129
|
+
inner, where_guards = extract_pattern_and_guards(pattern)
|
|
124
130
|
sub_patterns = or_pattern?(inner) ? inner.items[1..] : [inner]
|
|
125
|
-
sub_arms = sub_patterns.map
|
|
126
|
-
try_native_arm(sub, body, where_guards, env, current_scope, mode)
|
|
127
|
-
end
|
|
131
|
+
sub_arms = sub_patterns.map { |sub| yield sub, body, where_guards }
|
|
128
132
|
return if sub_arms.any?(&:nil?)
|
|
129
133
|
|
|
130
134
|
arms.concat(sub_arms)
|
|
131
135
|
i += 2
|
|
132
136
|
end
|
|
133
|
-
arms
|
|
134
|
-
["case #{value_var}", *arms, 'end'].join("\n")
|
|
137
|
+
arms
|
|
135
138
|
end
|
|
136
139
|
|
|
137
140
|
def try_native_arm(pattern, body, where_guards, env, current_scope, mode)
|
|
@@ -149,25 +152,11 @@ module Kapusta
|
|
|
149
152
|
end
|
|
150
153
|
|
|
151
154
|
def try_emit_compat_case(value_var, clauses, env, current_scope, mode)
|
|
152
|
-
arms =
|
|
153
|
-
|
|
154
|
-
while i < clauses.length
|
|
155
|
-
pattern = clauses[i]
|
|
156
|
-
body = clauses[i + 1]
|
|
157
|
-
inner, where_guards = if where_pattern?(pattern)
|
|
158
|
-
[pattern.items[1], pattern.items[2..]]
|
|
159
|
-
else
|
|
160
|
-
[pattern, []]
|
|
161
|
-
end
|
|
162
|
-
sub_patterns = or_pattern?(inner) ? inner.items[1..] : [inner]
|
|
163
|
-
sub_arms = sub_patterns.map do |sub|
|
|
164
|
-
try_compat_arm(sub, body, where_guards, value_var, env, current_scope, mode)
|
|
165
|
-
end
|
|
166
|
-
return if sub_arms.any?(&:nil?)
|
|
167
|
-
|
|
168
|
-
arms.concat(sub_arms)
|
|
169
|
-
i += 2
|
|
155
|
+
arms = collect_case_arms(clauses) do |pattern, body, where_guards|
|
|
156
|
+
try_compat_arm(pattern, body, where_guards, value_var, env, current_scope, mode)
|
|
170
157
|
end
|
|
158
|
+
return unless arms
|
|
159
|
+
|
|
171
160
|
emit_compat_case_lines(arms)
|
|
172
161
|
end
|
|
173
162
|
|
|
@@ -176,6 +165,12 @@ module Kapusta
|
|
|
176
165
|
last_pattern.is_a?(Sym) && last_pattern.name == '_'
|
|
177
166
|
end
|
|
178
167
|
|
|
168
|
+
def extract_pattern_and_guards(pattern)
|
|
169
|
+
return [pattern, []] unless where_pattern?(pattern)
|
|
170
|
+
|
|
171
|
+
[pattern.items[1], pattern.items[2..]]
|
|
172
|
+
end
|
|
173
|
+
|
|
179
174
|
def try_compat_arm(pattern, body, where_guards, value_var, env, current_scope, mode)
|
|
180
175
|
allow_pins = !where_guards.empty? && mode == :case
|
|
181
176
|
arm_env = env.child
|
|
@@ -185,11 +180,11 @@ module Kapusta
|
|
|
185
180
|
where_guard_codes = where_guards.map { |g| emit_expr(g, arm_env, current_scope) }
|
|
186
181
|
if where_guard_codes.empty?
|
|
187
182
|
guard_codes = plan[:conditions]
|
|
188
|
-
prelude
|
|
183
|
+
prelude = plan[:prelude]
|
|
189
184
|
else
|
|
190
185
|
prelude_guards = plan[:prelude].map { |line| "begin #{line}; true end" }
|
|
191
186
|
guard_codes = plan[:conditions] + prelude_guards + where_guard_codes
|
|
192
|
-
prelude
|
|
187
|
+
prelude = []
|
|
193
188
|
end
|
|
194
189
|
body_code = emit_expr(body, arm_env, current_scope)
|
|
195
190
|
[guard_codes, prelude, body_code]
|
|
@@ -83,6 +83,8 @@ module Kapusta
|
|
|
83
83
|
when 'require' then emit_require(args[0], env, current_scope)
|
|
84
84
|
when 'module' then emit_module_expr(args, env)
|
|
85
85
|
when 'class' then emit_class_expr(args, env)
|
|
86
|
+
when 'end' then emit_error!(:end_outside_header)
|
|
87
|
+
when 'defn' then emit_error!(:defn_outside_header)
|
|
86
88
|
when 'try' then emit_try(args, env, current_scope)
|
|
87
89
|
when 'raise' then emit_raise(args, env, current_scope)
|
|
88
90
|
when 'ivar' then "@#{Kapusta.kebab_to_snake(args[0].name)}"
|
|
@@ -102,7 +102,7 @@ module Kapusta
|
|
|
102
102
|
segments = constant_segments(name_sym)
|
|
103
103
|
return unless segments
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
build_nested_module(segments, body)
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
def emit_class_wrapper(name_sym, supers, env, body)
|
|
@@ -118,7 +118,7 @@ module Kapusta
|
|
|
118
118
|
return unless segments
|
|
119
119
|
|
|
120
120
|
super_code = class_super_code(supers, env)
|
|
121
|
-
|
|
121
|
+
build_nested_class(segments, super_code, body)
|
|
122
122
|
end
|
|
123
123
|
|
|
124
124
|
def constant_segments(name_sym)
|
|
@@ -483,20 +483,28 @@ module Kapusta
|
|
|
483
483
|
Kapusta.kebab_to_snake(name).gsub(/[^a-zA-Z0-9_]/, '_')
|
|
484
484
|
end
|
|
485
485
|
|
|
486
|
+
SIMPLE_EXPRESSION_PATTERNS = [
|
|
487
|
+
/\A[a-z_]\w*\z/, # local
|
|
488
|
+
/\A@@?[a-z_]\w*\z/, # @ivar / @@cvar
|
|
489
|
+
/\A\$[a-zA-Z_]\w*\z/, # $gvar
|
|
490
|
+
/\A[A-Z]\w*(?:::[A-Z]\w*)*\z/, # Constant / A::B::C
|
|
491
|
+
/\A[a-z_]\w*[!?=]?\([^()\n]*\)\z/, # bare call foo(...)
|
|
492
|
+
/\A-?\d+(?:\.\d+)?\z/, # number
|
|
493
|
+
/\A[a-z_]\w*(?:\.[a-z_]\w*[!?=]?(?:\([^()\n]*\))?|\[[^\[\]]*\])+\z/, # local + .m/[k] chain
|
|
494
|
+
/\A[A-Z]\w*(?:::[A-Z]\w*)*(?:\.[a-z_]\w*[!?=]?(?:\([^()\n]*\))?|\[[^\[\]]*\])+\z/, # const + chain
|
|
495
|
+
/\A:[a-zA-Z_]\w*[!?=]?\z/, # :symbol
|
|
496
|
+
/\A"(?:[^"\\]|\\.)*"\z/, # "string"
|
|
497
|
+
/\A'(?:[^'\\]|\\.)*'\z/, # 'string'
|
|
498
|
+
/\A\[[^\[\]\n]*\]\z/ # [flat, array]
|
|
499
|
+
].freeze
|
|
500
|
+
private_constant :SIMPLE_EXPRESSION_PATTERNS
|
|
501
|
+
|
|
502
|
+
SIMPLE_EXPRESSION_KEYWORDS = %w[nil true false self].freeze
|
|
503
|
+
private_constant :SIMPLE_EXPRESSION_KEYWORDS
|
|
504
|
+
|
|
486
505
|
def simple_expression?(code)
|
|
487
|
-
code.match?(
|
|
488
|
-
|
|
489
|
-
code.match?(/\A\$[a-zA-Z_]\w*\z/) ||
|
|
490
|
-
code.match?(/\A[A-Z]\w*(?:::[A-Z]\w*)*\z/) ||
|
|
491
|
-
code.match?(/\A[a-z_]\w*[!?=]?\([^()\n]*\)\z/) ||
|
|
492
|
-
code.match?(/\A-?\d+(?:\.\d+)?\z/) ||
|
|
493
|
-
code.match?(/\A[a-z_]\w*(?:\.[a-z_]\w*[!?=]?(?:\([^()\n]*\))?|\[[^\[\]]*\])+\z/) ||
|
|
494
|
-
code.match?(/\A[A-Z]\w*(?:::[A-Z]\w*)*(?:\.[a-z_]\w*[!?=]?(?:\([^()\n]*\))?|\[[^\[\]]*\])+\z/) ||
|
|
495
|
-
code.match?(/\A:[a-zA-Z_]\w*[!?=]?\z/) ||
|
|
496
|
-
code.match?(/\A"(?:[^"\\]|\\.)*"\z/) ||
|
|
497
|
-
code.match?(/\A'(?:[^'\\]|\\.)*'\z/) ||
|
|
498
|
-
code.match?(/\A\[[^\[\]\n]*\]\z/) ||
|
|
499
|
-
%w[nil true false self].include?(code) ||
|
|
506
|
+
SIMPLE_EXPRESSION_PATTERNS.any? { |re| code.match?(re) } ||
|
|
507
|
+
SIMPLE_EXPRESSION_KEYWORDS.include?(code) ||
|
|
500
508
|
negation_simple?(code)
|
|
501
509
|
end
|
|
502
510
|
|
|
@@ -56,20 +56,13 @@ module Kapusta
|
|
|
56
56
|
parts = []
|
|
57
57
|
deferred = []
|
|
58
58
|
current_env = env
|
|
59
|
-
items
|
|
60
|
-
|
|
61
|
-
while i < items.length
|
|
62
|
-
if items[i].is_a?(Sym) && items[i].name == '&'
|
|
63
|
-
sub = items[i + 1]
|
|
64
|
-
raise PatternNotTranslatable unless sub.is_a?(Sym)
|
|
65
|
-
|
|
59
|
+
each_pattern_item(pattern.items) do |kind, sub|
|
|
60
|
+
if kind == :rest
|
|
66
61
|
parts << native_rest_target(sub, current_env)
|
|
67
|
-
i += 2
|
|
68
62
|
else
|
|
69
|
-
code, current_env, follow_up = native_destructure_target(
|
|
63
|
+
code, current_env, follow_up = native_destructure_target(sub, current_env, allow_follow_up: true)
|
|
70
64
|
parts << code
|
|
71
65
|
deferred << follow_up if follow_up
|
|
72
|
-
i += 1
|
|
73
66
|
end
|
|
74
67
|
end
|
|
75
68
|
if deferred.empty?
|
|
@@ -122,17 +115,13 @@ module Kapusta
|
|
|
122
115
|
inner = []
|
|
123
116
|
current = env
|
|
124
117
|
deferred = []
|
|
125
|
-
items
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if items[i].is_a?(Sym) && items[i].name == '&'
|
|
129
|
-
inner << native_rest_target(items[i + 1], current)
|
|
130
|
-
i += 2
|
|
118
|
+
each_pattern_item(pattern.items) do |kind, sub|
|
|
119
|
+
if kind == :rest
|
|
120
|
+
inner << native_rest_target(sub, current)
|
|
131
121
|
else
|
|
132
|
-
code, current, follow_up = native_destructure_target(
|
|
122
|
+
code, current, follow_up = native_destructure_target(sub, current)
|
|
133
123
|
inner << code
|
|
134
124
|
deferred << follow_up if follow_up
|
|
135
|
-
i += 1
|
|
136
125
|
end
|
|
137
126
|
end
|
|
138
127
|
raise PatternNotTranslatable unless deferred.empty?
|
|
@@ -225,36 +214,26 @@ module Kapusta
|
|
|
225
214
|
state[:conditions] << "#{value_code}.length >= #{min_length}"
|
|
226
215
|
|
|
227
216
|
index = 0
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if rest_pattern_marker?(items, i)
|
|
231
|
-
sub = items[i + 1]
|
|
217
|
+
each_pattern_item(items) do |kind, sub|
|
|
218
|
+
if kind == :rest
|
|
232
219
|
raise PatternNotTranslatable unless sub.is_a?(Sym)
|
|
233
220
|
|
|
234
221
|
unless sub.name == '_'
|
|
235
222
|
ruby = define_local(arm_env, sub.name)
|
|
236
223
|
state[:prelude] << "#{ruby} = #{value_code}[#{index}..]"
|
|
237
224
|
end
|
|
238
|
-
i += 2
|
|
239
225
|
else
|
|
240
|
-
compile_compat_pattern(
|
|
226
|
+
compile_compat_pattern(sub, "#{value_code}[#{index}]", env, arm_env,
|
|
241
227
|
mode:, allow_pins:, state:)
|
|
242
228
|
index += 1
|
|
243
|
-
i += 1
|
|
244
229
|
end
|
|
245
230
|
end
|
|
246
231
|
end
|
|
247
232
|
|
|
248
233
|
def compat_sequence_min_length(items)
|
|
249
234
|
count = 0
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if rest_pattern_marker?(items, i)
|
|
253
|
-
i += 2
|
|
254
|
-
else
|
|
255
|
-
count += 1
|
|
256
|
-
i += 1
|
|
257
|
-
end
|
|
235
|
+
each_pattern_item(items) do |kind, _sub|
|
|
236
|
+
count += 1 if kind == :item
|
|
258
237
|
end
|
|
259
238
|
count
|
|
260
239
|
end
|
|
@@ -346,11 +325,9 @@ module Kapusta
|
|
|
346
325
|
def compile_native_sequence(items, env, mode:, allow_pins:, state:)
|
|
347
326
|
parts = []
|
|
348
327
|
has_rest = false
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if rest_pattern_marker?(items, i)
|
|
328
|
+
each_pattern_item(items) do |kind, sub|
|
|
329
|
+
if kind == :rest
|
|
352
330
|
has_rest = true
|
|
353
|
-
sub = items[i + 1]
|
|
354
331
|
raise PatternNotTranslatable unless sub.is_a?(Sym)
|
|
355
332
|
|
|
356
333
|
if sub.name == '_'
|
|
@@ -360,10 +337,8 @@ module Kapusta
|
|
|
360
337
|
state[:binding_names] << sub.name
|
|
361
338
|
parts << "*#{sanitize_local(sub.name)}"
|
|
362
339
|
end
|
|
363
|
-
i += 2
|
|
364
340
|
else
|
|
365
|
-
parts << compile_native_pattern(
|
|
366
|
-
i += 1
|
|
341
|
+
parts << compile_native_pattern(sub, env, mode:, allow_pins:, state:)
|
|
367
342
|
end
|
|
368
343
|
end
|
|
369
344
|
parts << '*' unless has_rest
|
|
@@ -464,16 +439,8 @@ module Kapusta
|
|
|
464
439
|
pattern.name == '_' ? [] : [pattern.name]
|
|
465
440
|
when Vec
|
|
466
441
|
names = []
|
|
467
|
-
items
|
|
468
|
-
|
|
469
|
-
while i < items.length
|
|
470
|
-
if items[i].is_a?(Sym) && items[i].name == '&'
|
|
471
|
-
names.concat(pattern_names(items[i + 1]))
|
|
472
|
-
i += 2
|
|
473
|
-
else
|
|
474
|
-
names.concat(pattern_names(items[i]))
|
|
475
|
-
i += 1
|
|
476
|
-
end
|
|
442
|
+
each_pattern_item(pattern.items) do |_kind, sub|
|
|
443
|
+
names.concat(pattern_names(sub))
|
|
477
444
|
end
|
|
478
445
|
names
|
|
479
446
|
when HashLit
|
|
@@ -504,8 +471,18 @@ module Kapusta
|
|
|
504
471
|
name.length > 1 && (name.start_with?('?') || name.start_with?('_'))
|
|
505
472
|
end
|
|
506
473
|
|
|
507
|
-
def
|
|
508
|
-
|
|
474
|
+
def each_pattern_item(items)
|
|
475
|
+
i = 0
|
|
476
|
+
while i < items.length
|
|
477
|
+
item = items[i]
|
|
478
|
+
if item.is_a?(Sym) && item.name == '&'
|
|
479
|
+
yield :rest, items[i + 1]
|
|
480
|
+
i += 2
|
|
481
|
+
else
|
|
482
|
+
yield :item, item
|
|
483
|
+
i += 1
|
|
484
|
+
end
|
|
485
|
+
end
|
|
509
486
|
end
|
|
510
487
|
end
|
|
511
488
|
end
|