Skip to content

Commit 1442cb7

Browse files
authored
Merge pull request #494 from savi-lang/add/indexable-stuff
Improvements to `Indexable` trait
2 parents 7fa4b70 + 1ee334c commit 1442cb7

File tree

3 files changed

+98
-58
lines changed

3 files changed

+98
-58
lines changed

core/Indexable.savi

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
:: An collection of elements of type A that can be accessed via numeric index.
12
:trait box Indexable(A)
3+
:fun size USize
24
:fun "[]!"(index USize) (@->A)'aliased
35

46
//--
@@ -9,6 +11,16 @@
911
stride USize = 1
1012
) None
1113
:yields ((@->A)'aliased, USize)
14+
index = from
15+
to = to.at_most(@size)
16+
stride = stride.at_least(1)
17+
while index < to (
18+
try (
19+
value = @[index]!
20+
yield (--value, index)
21+
)
22+
index = try (index +! stride | return)
23+
)
1224

1325
:fun each(
1426
from USize = 0
@@ -95,6 +107,15 @@
95107
stride USize = 1
96108
) None
97109
:yields ((@->A)'aliased, USize)
110+
index = from.at_most(try (@size -! 1 | return))
111+
stride = stride.at_least(1)
112+
while index >= to (
113+
try (
114+
value = @[index]!
115+
yield (--value, index)
116+
)
117+
index = try (index -! stride | return)
118+
)
98119

99120
:fun reverse_each(
100121
from = USize.max_value
@@ -129,6 +150,18 @@
129150

130151
//--
131152

153+
:fun first! @->(A'aliased)
154+
@[0]!
155+
156+
:fun last! @->(A'aliased)
157+
@reverse_each_with_index -> (value, index |
158+
return value
159+
None // TODO: this should not be needed
160+
)
161+
error!
162+
163+
//--
164+
132165
:fun select(
133166
from USize = 0
134167
to = USize.max_value

spec/core/Indexable.Spec.savi

Lines changed: 59 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,122 @@
1+
// A little custom class to showcase the minimum trait implementation.
2+
// All other Indexable methods will be based on this minimal implementation.
3+
:class _ASCIILettersExample
4+
:is Indexable(String)
5+
6+
:fun size USize: 26
7+
:fun "[]!"(index USize)
8+
error! if index >= @size
9+
"\((index + 'a').format.printable_ascii)"
10+
11+
:class _ArrayWrapperExample(T val) // TODO: val constraint should not be required
12+
:is Indexable(T)
13+
:let array Array(T)
14+
:new (@array)
15+
16+
:fun size: @array.size
17+
:fun "[]!"(index USize): @array[index]!
18+
119
:class Savi.Indexable.Spec
220
:is Spec
321
:const describes: "Indexable"
422

523
:it "yields each element"
624
array Array(String) = []
7-
["foo", "bar", "baz"].each -> (string | array << string)
8-
assert: array == ["foo", "bar", "baz"]
25+
_ASCIILettersExample.new.each -> (string | array << string)
26+
assert: array == [
27+
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"
28+
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
29+
]
930

1031
:it "yields each element of a subslice"
1132
array Array(String) = []
12-
["a", "b", "c", "d", "e", "f"].each(1, 5) -> (string | array << string)
33+
_ASCIILettersExample.new.each(1, 5) -> (string | array << string)
1334
assert: array == ["b", "c", "d", "e"]
1435

1536
:it "yields each element along with the index"
1637
array_a Array(String) = []
1738
array_b Array(USize) = []
18-
["foo", "bar", "baz"].each_with_index -> (string, index |
39+
_ASCIILettersExample.new.each_with_index -> (string, index |
1940
array_a << string
2041
array_b << index
2142
)
22-
assert: array_a == ["foo", "bar", "baz"]
23-
assert: array_b == [0, 1, 2]
43+
assert: array_a == [
44+
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"
45+
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
46+
]
47+
assert: array_b == [
48+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
49+
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
50+
]
2451

2552
:it "yields each element, in reverse"
2653
array Array(String) = []
27-
["foo", "bar", "baz"].reverse_each -> (string | array << string)
28-
assert: array == ["baz", "bar", "foo"]
54+
_ASCIILettersExample.new.reverse_each -> (string | array << string)
55+
assert: array == [
56+
"z", "y", "x", "w", "v", "u", "t", "s", "r", "q", "p", "o", "n"
57+
"m", "l", "k", "j", "i", "h", "g", "f", "e", "d", "c", "b", "a"
58+
]
2959

3060
:it "yields each element, in reverse, along with the index"
3161
array_a Array(String) = []
3262
array_b Array(USize) = []
33-
["foo", "bar", "baz"].reverse_each_with_index -> (string, index |
63+
_ASCIILettersExample.new.reverse_each_with_index -> (string, index |
3464
array_a << string
3565
array_b << index
3666
)
37-
assert: array_b == [2, 1, 0]
38-
assert:
39-
array_a == ["baz", "bar", "foo"]
40-
41-
:it "yields each element, stopping early if the criteria is met"
42-
array Array(String) = []
43-
early_stop = ["foo", "bar", "baz"].each_until -> (string |
44-
array << string
45-
string == "bar"
46-
)
47-
assert: early_stop
48-
assert: array == ["foo", "bar"]
49-
50-
array.clear
51-
early_stop = ["foo", "bar", "baz"].each_until -> (string |
52-
array << string
53-
string == "bard"
54-
)
55-
assert: early_stop.is_false
56-
assert: array == ["foo", "bar", "baz"]
57-
58-
:it "yields each element of a subslice, stopping early if the criteria is met"
59-
array Array(String) = []
60-
early_stop = ["a", "b", "c", "d", "e", "f"].each_until(1, 5) -> (string |
61-
array << string
62-
string == "d"
63-
)
64-
assert: early_stop
65-
assert: array == ["b", "c", "d"]
66-
67-
array.clear
68-
early_stop = ["a", "b", "c", "d", "e", "f"].each_until(1, 5) -> (string |
69-
array << string
70-
string == "z"
71-
)
72-
assert: early_stop.is_false
73-
assert: array == ["b", "c", "d", "e"]
67+
assert: array_a == [
68+
"z", "y", "x", "w", "v", "u", "t", "s", "r", "q", "p", "o", "n"
69+
"m", "l", "k", "j", "i", "h", "g", "f", "e", "d", "c", "b", "a"
70+
]
71+
assert: array_b == [
72+
25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13
73+
12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
74+
]
7475

7576
:it "returns True if any element meets the criteria"
76-
array Array(U8) = [11, 22, 33, 44, 36, 27, 18]
77+
array = _ArrayWrapperExample(U8).new([11, 22, 33, 44, 36, 27, 18])
7778
assert: array.has_any -> (num | num > 30)
7879
assert: array.has_any -> (num | num > 50).is_false
7980

8081
:it "returns True if all elements meet the criteria"
81-
array Array(U8) = [11, 22, 33, 44, 36, 27, 18]
82+
array = _ArrayWrapperExample(U8).new([11, 22, 33, 44, 36, 27, 18])
8283
assert: array.has_all -> (num | num > 10)
8384
assert: array.has_all -> (num | num > 30).is_false
8485

8586
:it "finds the first element that meets the criteria"
86-
array Array(U8) = [11, 22, 33, 44, 36, 27, 18]
87+
array = _ArrayWrapperExample(U8).new([11, 22, 33, 44, 36, 27, 18])
8788
assert: array.find! -> (num | num > 30) == 33
8889
assert error: array.find! -> (num | num > 50)
8990

9091
:it "finds the first index that meets the criteria"
91-
array Array(U8) = [11, 22, 33, 44, 36, 27, 18]
92+
array = _ArrayWrapperExample(U8).new([11, 22, 33, 44, 36, 27, 18])
9293
assert: array.find_index! -> (num | num > 30) == 2
9394
assert error: array.find_index! -> (num | num > 50)
9495

9596
:it "finds, starting from the end, the first element that meets the criteria"
96-
array Array(U8) = [11, 22, 33, 44, 36, 27, 18]
97+
array = _ArrayWrapperExample(U8).new([11, 22, 33, 44, 36, 27, 18])
9798
assert: array.reverse_find! -> (num | num > 30) == 36
9899
assert error: array.reverse_find! -> (num | num > 50)
99100

100101
:it "finds, starting from the end, the first index that meets the criteria"
101-
array Array(U8) = [11, 22, 33, 44, 36, 27, 18]
102+
array = _ArrayWrapperExample(U8).new([11, 22, 33, 44, 36, 27, 18])
102103
assert: array.reverse_find_index! -> (num | num > 30) == 4
103104
assert error: array.reverse_find_index! -> (num | num > 50)
104105

105106
:it "selects those elements that meet the criteria"
106-
array Array(U8) = [11, 22, 33, 44, 36, 27, 18]
107+
array = _ArrayWrapperExample(U8).new([11, 22, 33, 44, 36, 27, 18])
107108
selected = array.select -> (num | num < 30)
108109
assert: selected == [11, 22, 27, 18]
109110

110111
:it "rejects those elements that do not meet the criteria"
111-
array Array(U8) = [1, 2, 3, 4, 5]
112+
array = _ArrayWrapperExample(U8).new([1, 2, 3, 4, 5])
112113
odds = array.reject -> (num | num % 2 == 0)
113114
assert: odds == [1, 3, 5]
114115

115116
:it "rejects nothing from an empty array"
116-
array Array(U8) = []
117-
assert: array.reject -> (num | num % 2 == 0) == array
117+
array = _ArrayWrapperExample(U8).new([])
118+
assert: array.reject -> (num | num % 2 == 0) == []
118119

119120
:it "rejects nothing if criteria is always false"
120-
array Array(U8) = [1, 2, 3]
121-
assert: array.reject -> (num | False) == array
121+
array = _ArrayWrapperExample(U8).new([1, 2, 3])
122+
assert: array.reject -> (num | False) == [1, 2, 3]

src/savi/compiler/populate.cr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,17 @@ class Savi::Compiler::Populate
205205
visitor = self
206206
params = f.params.try(&.accept(ctx, visitor))
207207
ret = f.ret.try(&.accept(ctx, visitor))
208+
error_out = f.error_out.try(&.accept(ctx, visitor))
209+
yield_out = f.yield_out.try(&.accept(ctx, visitor))
210+
yield_in = f.yield_in.try(&.accept(ctx, visitor))
208211
body = f.body.try(&.accept(ctx, visitor))
209212

210213
f = f.dup
211214
f.params = params
212215
f.ret = ret
216+
f.error_out = error_out
217+
f.yield_out = yield_out
218+
f.yield_in = yield_in
213219
f.body = body
214220
f
215221
}

0 commit comments

Comments
 (0)