@effekt-lang/effekt 0.77.0 → 0.79.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.
- package/bin/effekt +0 -0
- package/libraries/common/array.effekt +5 -4
- package/libraries/common/bytearray.effekt +45 -26
- package/libraries/common/char.effekt +12 -5
- package/libraries/common/dictionary.effekt +243 -53
- package/libraries/common/effekt.effekt +11 -14
- package/libraries/common/io/filesystem.effekt +7 -7
- package/libraries/common/io/network.effekt +6 -6
- package/libraries/common/io/signal.effekt +10 -10
- package/libraries/common/io/time.effekt +1 -1
- package/libraries/common/io.effekt +4 -4
- package/libraries/common/list.effekt +22 -0
- package/libraries/common/map.effekt +194 -100
- package/libraries/common/process.effekt +3 -2
- package/libraries/common/random.effekt +14 -1
- package/libraries/common/set.effekt +9 -19
- package/libraries/common/string.effekt +28 -0
- package/libraries/common/stringbuffer.effekt +5 -5
- package/libraries/llvm/bytearray.c +15 -6
- package/libraries/llvm/forward-declare-c.ll +1 -0
- package/libraries/llvm/rts.ll +14 -14
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +3 -3
- package/licenses/mit license - mit-license.html +61 -53
- package/licenses/the bsd license - bsd-license.html +61 -53
- package/licenses/the mit license - mit.html +62 -54
- package/package.json +1 -1
package/bin/effekt
CHANGED
|
Binary file
|
|
@@ -63,9 +63,10 @@ extern def unsafeAllocate[T](size: Int) at global: Array[T] =
|
|
|
63
63
|
|
|
64
64
|
/// Fills a given array in-place.
|
|
65
65
|
def fill[T](arr: Array[T], filler: T): Unit = {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
val n = arr.size
|
|
67
|
+
def go(i: Int): Unit =
|
|
68
|
+
if (i < n) { arr.set(i, filler); go(i + 1) }
|
|
69
|
+
go(0)
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
/// Creates a new Array of size `size` filled with the value `init`
|
|
@@ -107,7 +108,7 @@ define void @assertInbounds(i64 %index, %Pos %arr) alwaysinline {
|
|
|
107
108
|
%in_bounds = icmp ult i64 %index, %size
|
|
108
109
|
br i1 %in_bounds, label %ok, label %out
|
|
109
110
|
out:
|
|
110
|
-
call void @exit(
|
|
111
|
+
call void @exit(i32 1)
|
|
111
112
|
unreachable
|
|
112
113
|
ok:
|
|
113
114
|
ret void
|
|
@@ -61,7 +61,7 @@ define void @assertInboundsByteArray(i64 %index, %Pos %arr) alwaysinline {
|
|
|
61
61
|
%in_bounds = icmp ult i64 %index, %size
|
|
62
62
|
br i1 %in_bounds, label %ok, label %out
|
|
63
63
|
out:
|
|
64
|
-
call void @exit(
|
|
64
|
+
call void @exit(i32 1)
|
|
65
65
|
unreachable
|
|
66
66
|
ok:
|
|
67
67
|
ret void
|
|
@@ -113,17 +113,35 @@ def build(size: Int) { index: Int => Byte }: ByteArray = {
|
|
|
113
113
|
arr
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/// Copies `length`-many bytes from `from` to `to`
|
|
117
|
+
/// starting at `start` (in `from`) and `offset` (in `to`).
|
|
118
|
+
/// The ranges may overlap, even within the same bytearray: the source bytes are
|
|
119
|
+
/// read before any are written. Panics if out of bounds.
|
|
120
|
+
///
|
|
121
|
+
/// O(N)
|
|
122
|
+
def copy(from: ByteArray, start: Int, to: ByteArray, offset: Int, length: Int): Unit =
|
|
123
|
+
if (length < 0 || start < 0 || offset < 0
|
|
124
|
+
|| start + length > from.size || offset + length > to.size) {
|
|
125
|
+
<{ "bytearray::copy: range out of bounds" }>
|
|
126
|
+
} else {
|
|
127
|
+
unsafeCopy(from, start, to, offset, length)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// `copy` without its bounds check.
|
|
131
|
+
/// Corrupts memory if misused; prefer `copy`.
|
|
132
|
+
extern def unsafeCopy(from: ByteArray, start: Int, to: ByteArray, offset: Int, length: Int) at global: Unit =
|
|
133
|
+
js "bytearray$copy(${from}, ${start}, ${to}, ${offset}, ${length})"
|
|
134
|
+
chez "(bytevector-copy! ${from} ${start} ${to} ${offset} ${length})"
|
|
135
|
+
llvm """
|
|
136
|
+
%z = call %Pos @c_bytearray_copy(%Pos ${from}, %Int ${start}, %Pos ${to}, %Int ${offset}, %Int ${length})
|
|
137
|
+
ret %Pos %z
|
|
138
|
+
"""
|
|
139
|
+
|
|
116
140
|
def resize(source: ByteArray, size: Int): ByteArray = {
|
|
117
141
|
val target = allocate(size)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
target.set(i, source.get(i))
|
|
122
|
-
go(i + 1)
|
|
123
|
-
} else {
|
|
124
|
-
target
|
|
125
|
-
}
|
|
126
|
-
go(0)
|
|
142
|
+
// safe: `min` establishes the bound for both ranges ~> no check is needed
|
|
143
|
+
unsafeCopy(source, 0, target, 0, min(source.size, size))
|
|
144
|
+
target
|
|
127
145
|
}
|
|
128
146
|
|
|
129
147
|
/// Sorts a given bytearray `arr` in-place using a counting sort,
|
|
@@ -190,6 +208,21 @@ extern def toString(arr: ByteArray) at {}: String =
|
|
|
190
208
|
chez "(utf8->string ${arr})"
|
|
191
209
|
|
|
192
210
|
extern js """
|
|
211
|
+
function bytearray$copy(src, srcOff, dst, dstOff, n) {
|
|
212
|
+
// for small `n`, it's faster to copy manually to avoid allocating `subarray`
|
|
213
|
+
if (n < 40) {
|
|
214
|
+
if (srcOff < dstOff) {
|
|
215
|
+
for (let i = n - 1; i >= 0; --i) dst[dstOff + i] = src[srcOff + i];
|
|
216
|
+
} else {
|
|
217
|
+
for (let i = 0; i < n; ++i) dst[dstOff + i] = src[srcOff + i];
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
// safe: `set` clones the source region first when both share a buffer
|
|
221
|
+
dst.set(src.subarray(srcOff, srcOff + n), dstOff);
|
|
222
|
+
}
|
|
223
|
+
return $effekt.unit;
|
|
224
|
+
}
|
|
225
|
+
|
|
193
226
|
function bytearray$set(bytes, index, value) {
|
|
194
227
|
bytes[index] = value;
|
|
195
228
|
return $effekt.unit;
|
|
@@ -242,22 +275,8 @@ extern def compareByteArrayImpl(b1: ByteArray, b2: ByteArray) at {}: Int =
|
|
|
242
275
|
"""
|
|
243
276
|
chez "(bytearray$compare ${b1} ${b2})"
|
|
244
277
|
|
|
245
|
-
def compareByteArray(b1: ByteArray, b2: ByteArray): Ordering =
|
|
246
|
-
|
|
247
|
-
if (ret == 0) {
|
|
248
|
-
Equal()
|
|
249
|
-
} else if (ret < 0) {
|
|
250
|
-
Less()
|
|
251
|
-
} else { // ret > 0
|
|
252
|
-
Greater()
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
def compareStringBytes(left: String, right: String): Ordering = {
|
|
257
|
-
val l = left.fromString
|
|
258
|
-
val r = right.fromString
|
|
259
|
-
compareByteArray(l, r)
|
|
260
|
-
}
|
|
278
|
+
def compareByteArray(b1: ByteArray, b2: ByteArray): Ordering =
|
|
279
|
+
compareByteArrayImpl(b1, b2).fromInt
|
|
261
280
|
|
|
262
281
|
|
|
263
282
|
// Streaming
|
|
@@ -144,6 +144,10 @@ extern def infixGte(x: Char, y: Char) at {}: Bool =
|
|
|
144
144
|
ret %Pos %adt_boolean
|
|
145
145
|
"""
|
|
146
146
|
|
|
147
|
+
def compareChar(x: Char, y: Char): Ordering =
|
|
148
|
+
if (x == y) Equal()
|
|
149
|
+
else if (x < y) Less()
|
|
150
|
+
else Greater()
|
|
147
151
|
|
|
148
152
|
/**
|
|
149
153
|
* Determines the number of bytes needed by a codepoint
|
|
@@ -155,18 +159,21 @@ def utf8ByteCount(codepoint: Char): Int = codepoint match {
|
|
|
155
159
|
case c and c >= '\u0080' and c <= '\u07FF' => 2
|
|
156
160
|
case c and c >= '\u0800' and c <= '\uFFFF' => 3
|
|
157
161
|
case c and c >= '\u10000' and c <= '\u10FFFF' => 4
|
|
158
|
-
case c =>
|
|
162
|
+
case c => <{ "Not a valid code point" }>
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
def utf16UnitCount(codepoint: Char): Int = codepoint match {
|
|
162
166
|
case c and c >= '\u0000' and c <= '\uFFFF' => 1
|
|
163
|
-
case c and c >= '\u10000' and c <= '\u10FFFF' =>
|
|
164
|
-
case c =>
|
|
167
|
+
case c and c >= '\u10000' and c <= '\u10FFFF' => 2
|
|
168
|
+
case c => <{ "Not a valid code point" }>
|
|
165
169
|
}
|
|
166
170
|
|
|
171
|
+
/// How many index units `c` occupies in the unit that `length` and `unsafeCharAt`
|
|
172
|
+
/// use on this backend. Adding it to a code-point boundary gives the next character.
|
|
167
173
|
extern def charWidth(c: Char) at {}: Int =
|
|
168
|
-
|
|
169
|
-
|
|
174
|
+
js { utf16UnitCount(c) }
|
|
175
|
+
chez { 1 }
|
|
176
|
+
llvm { utf8ByteCount(c) }
|
|
170
177
|
|
|
171
178
|
|
|
172
179
|
def decodeChar(): Char / {next[Byte], stop} = {
|
|
@@ -6,14 +6,40 @@ import bytearray
|
|
|
6
6
|
import stream
|
|
7
7
|
|
|
8
8
|
/// Internal trie node type.
|
|
9
|
+
///
|
|
10
|
+
/// Nodes with at most four children are `Branch1` ... `Branch4`, holding their
|
|
11
|
+
/// children behind `Ref`s so that descending along an existing path can
|
|
12
|
+
/// update in place without rebuilding the spine. A fifth child promotes the
|
|
13
|
+
/// node to the dense representation (`Branch256` / `Node256`), a full 256-slot
|
|
14
|
+
/// array indexed by the byte, whose cells are mutable themselves.
|
|
15
|
+
///
|
|
16
|
+
/// Invariant: in the sparse representations, `byte1 < byte2 < byte3 < byte4`
|
|
17
|
+
/// (bytes are unsigned), so iteration is in key order in every representation.
|
|
18
|
+
///
|
|
19
|
+
/// `Leaf`, `Node1` ... `Node4` and `Node256` carry a value at this
|
|
20
|
+
/// position; `Branch1` ... `Branch4` and `Branch256` do not.
|
|
21
|
+
///
|
|
22
|
+
/// No `Trie` value is ever shared between two live positions: insertion
|
|
23
|
+
/// mutates through `Ref`s and array cells, so aliasing a subtree would be
|
|
24
|
+
/// unsound.
|
|
9
25
|
type Trie[A] {
|
|
10
26
|
Empty()
|
|
11
27
|
Leaf(value: A)
|
|
12
|
-
|
|
13
|
-
|
|
28
|
+
Branch1(byte: Byte, child: Ref[Trie[A]])
|
|
29
|
+
Node1(value: A, byte: Byte, child: Ref[Trie[A]])
|
|
30
|
+
Branch2(byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]])
|
|
31
|
+
Node2(value: A, byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]])
|
|
32
|
+
Branch3(byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]], byte3: Byte, child3: Ref[Trie[A]])
|
|
33
|
+
Node3(value: A, byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]], byte3: Byte, child3: Ref[Trie[A]])
|
|
34
|
+
Branch4(byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]], byte3: Byte, child3: Ref[Trie[A]], byte4: Byte, child4: Ref[Trie[A]])
|
|
35
|
+
Node4(value: A, byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]], byte3: Byte, child3: Ref[Trie[A]], byte4: Byte, child4: Ref[Trie[A]])
|
|
36
|
+
Branch256(children: Array[Trie[A]])
|
|
37
|
+
Node256(value: A, children: Array[Trie[A]])
|
|
14
38
|
}
|
|
15
39
|
|
|
16
40
|
/// A mutable trie-based dictionary for efficient key-value storage with bytearray keys.
|
|
41
|
+
///
|
|
42
|
+
/// Mutating the dictionary while iterating it (`each`, `eachTagged`) is undefined.
|
|
17
43
|
type Dictionary[A] = Ref[Trie[A]]
|
|
18
44
|
|
|
19
45
|
/// A record pairing a key with a value for use in streams.
|
|
@@ -27,34 +53,104 @@ def insert[A](dictionary: Dictionary[A], key: String, value: A): Unit =
|
|
|
27
53
|
|
|
28
54
|
/// Insert a key-value pair into the dictionary with a ByteArray key.
|
|
29
55
|
def insert[A](dictionary: Dictionary[A], key: ByteArray, value: A): Unit = {
|
|
56
|
+
val n = key.size
|
|
57
|
+
|
|
58
|
+
// The rest of the key from position `i` on, as a chain of `Branch1`s
|
|
59
|
+
// ending in a `Leaf`.
|
|
60
|
+
def chain(i: Int): Trie[A] =
|
|
61
|
+
if (i < n) Branch1(key.get(i), ref(chain(i + 1)))
|
|
62
|
+
else Leaf(value)
|
|
63
|
+
|
|
64
|
+
// The four children of a `Branch4`, plus the rest of the key at `b`,
|
|
65
|
+
// as a full 256-slot array.
|
|
66
|
+
def densified(byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]], byte3: Byte, child3: Ref[Trie[A]], byte4: Byte, child4: Ref[Trie[A]], b: Byte, i: Int): Array[Trie[A]] = {
|
|
67
|
+
val children = array[Trie[A]](256, Empty())
|
|
68
|
+
children.set(byte1.toInt, child1.get)
|
|
69
|
+
children.set(byte2.toInt, child2.get)
|
|
70
|
+
children.set(byte3.toInt, child3.get)
|
|
71
|
+
children.set(byte4.toInt, child4.get)
|
|
72
|
+
children.set(b.toInt, chain(i + 1))
|
|
73
|
+
children
|
|
74
|
+
}
|
|
75
|
+
|
|
30
76
|
def go(trie: Trie[A], i: Int): Trie[A] =
|
|
31
|
-
if (i <
|
|
77
|
+
if (i < n) {
|
|
78
|
+
val b = key.get(i)
|
|
32
79
|
trie match {
|
|
33
|
-
case Empty() =>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
80
|
+
case Empty() => chain(i)
|
|
81
|
+
case Leaf(existing) => Node1(existing, b, ref(chain(i + 1)))
|
|
82
|
+
|
|
83
|
+
case Branch1(byte1, child1) =>
|
|
84
|
+
if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
|
|
85
|
+
else if (b.toInt < byte1.toInt) Branch2(b, ref(chain(i + 1)), byte1, child1)
|
|
86
|
+
else Branch2(byte1, child1, b, ref(chain(i + 1)))
|
|
87
|
+
case Node1(existing, byte1, child1) =>
|
|
88
|
+
if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
|
|
89
|
+
else if (b.toInt < byte1.toInt) Node2(existing, b, ref(chain(i + 1)), byte1, child1)
|
|
90
|
+
else Node2(existing, byte1, child1, b, ref(chain(i + 1)))
|
|
91
|
+
|
|
92
|
+
case Branch2(byte1, child1, byte2, child2) =>
|
|
93
|
+
if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
|
|
94
|
+
else if (b == byte2) { child2.set(go(child2.get, i + 1)); trie }
|
|
95
|
+
else if (b.toInt < byte1.toInt) Branch3(b, ref(chain(i + 1)), byte1, child1, byte2, child2)
|
|
96
|
+
else if (b.toInt < byte2.toInt) Branch3(byte1, child1, b, ref(chain(i + 1)), byte2, child2)
|
|
97
|
+
else Branch3(byte1, child1, byte2, child2, b, ref(chain(i + 1)))
|
|
98
|
+
case Node2(existing, byte1, child1, byte2, child2) =>
|
|
99
|
+
if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
|
|
100
|
+
else if (b == byte2) { child2.set(go(child2.get, i + 1)); trie }
|
|
101
|
+
else if (b.toInt < byte1.toInt) Node3(existing, b, ref(chain(i + 1)), byte1, child1, byte2, child2)
|
|
102
|
+
else if (b.toInt < byte2.toInt) Node3(existing, byte1, child1, b, ref(chain(i + 1)), byte2, child2)
|
|
103
|
+
else Node3(existing, byte1, child1, byte2, child2, b, ref(chain(i + 1)))
|
|
104
|
+
|
|
105
|
+
case Branch3(byte1, child1, byte2, child2, byte3, child3) =>
|
|
106
|
+
if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
|
|
107
|
+
else if (b == byte2) { child2.set(go(child2.get, i + 1)); trie }
|
|
108
|
+
else if (b == byte3) { child3.set(go(child3.get, i + 1)); trie }
|
|
109
|
+
else if (b.toInt < byte1.toInt) Branch4(b, ref(chain(i + 1)), byte1, child1, byte2, child2, byte3, child3)
|
|
110
|
+
else if (b.toInt < byte2.toInt) Branch4(byte1, child1, b, ref(chain(i + 1)), byte2, child2, byte3, child3)
|
|
111
|
+
else if (b.toInt < byte3.toInt) Branch4(byte1, child1, byte2, child2, b, ref(chain(i + 1)), byte3, child3)
|
|
112
|
+
else Branch4(byte1, child1, byte2, child2, byte3, child3, b, ref(chain(i + 1)))
|
|
113
|
+
case Node3(existing, byte1, child1, byte2, child2, byte3, child3) =>
|
|
114
|
+
if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
|
|
115
|
+
else if (b == byte2) { child2.set(go(child2.get, i + 1)); trie }
|
|
116
|
+
else if (b == byte3) { child3.set(go(child3.get, i + 1)); trie }
|
|
117
|
+
else if (b.toInt < byte1.toInt) Node4(existing, b, ref(chain(i + 1)), byte1, child1, byte2, child2, byte3, child3)
|
|
118
|
+
else if (b.toInt < byte2.toInt) Node4(existing, byte1, child1, b, ref(chain(i + 1)), byte2, child2, byte3, child3)
|
|
119
|
+
else if (b.toInt < byte3.toInt) Node4(existing, byte1, child1, byte2, child2, b, ref(chain(i + 1)), byte3, child3)
|
|
120
|
+
else Node4(existing, byte1, child1, byte2, child2, byte3, child3, b, ref(chain(i + 1)))
|
|
121
|
+
|
|
122
|
+
case Branch4(byte1, child1, byte2, child2, byte3, child3, byte4, child4) =>
|
|
123
|
+
if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
|
|
124
|
+
else if (b == byte2) { child2.set(go(child2.get, i + 1)); trie }
|
|
125
|
+
else if (b == byte3) { child3.set(go(child3.get, i + 1)); trie }
|
|
126
|
+
else if (b == byte4) { child4.set(go(child4.get, i + 1)); trie }
|
|
127
|
+
else Branch256(densified(byte1, child1, byte2, child2, byte3, child3, byte4, child4, b, i))
|
|
128
|
+
case Node4(existing, byte1, child1, byte2, child2, byte3, child3, byte4, child4) =>
|
|
129
|
+
if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
|
|
130
|
+
else if (b == byte2) { child2.set(go(child2.get, i + 1)); trie }
|
|
131
|
+
else if (b == byte3) { child3.set(go(child3.get, i + 1)); trie }
|
|
132
|
+
else if (b == byte4) { child4.set(go(child4.get, i + 1)); trie }
|
|
133
|
+
else Node256(existing, densified(byte1, child1, byte2, child2, byte3, child3, byte4, child4, b, i))
|
|
134
|
+
|
|
135
|
+
case Branch256(children) =>
|
|
136
|
+
children.set(b.toInt, go(children.get(b.toInt), i + 1)); trie
|
|
137
|
+
case Node256(_, children) =>
|
|
138
|
+
children.set(b.toInt, go(children.get(b.toInt), i + 1)); trie
|
|
51
139
|
}
|
|
52
140
|
} else {
|
|
53
141
|
trie match {
|
|
54
142
|
case Empty() => Leaf(value)
|
|
55
143
|
case Leaf(_) => Leaf(value)
|
|
56
|
-
case
|
|
57
|
-
case
|
|
144
|
+
case Branch1(byte1, child1) => Node1(value, byte1, child1)
|
|
145
|
+
case Node1(_, byte1, child1) => Node1(value, byte1, child1)
|
|
146
|
+
case Branch2(byte1, child1, byte2, child2) => Node2(value, byte1, child1, byte2, child2)
|
|
147
|
+
case Node2(_, byte1, child1, byte2, child2) => Node2(value, byte1, child1, byte2, child2)
|
|
148
|
+
case Branch3(byte1, child1, byte2, child2, byte3, child3) => Node3(value, byte1, child1, byte2, child2, byte3, child3)
|
|
149
|
+
case Node3(_, byte1, child1, byte2, child2, byte3, child3) => Node3(value, byte1, child1, byte2, child2, byte3, child3)
|
|
150
|
+
case Branch4(byte1, child1, byte2, child2, byte3, child3, byte4, child4) => Node4(value, byte1, child1, byte2, child2, byte3, child3, byte4, child4)
|
|
151
|
+
case Node4(_, byte1, child1, byte2, child2, byte3, child3, byte4, child4) => Node4(value, byte1, child1, byte2, child2, byte3, child3, byte4, child4)
|
|
152
|
+
case Branch256(children) => Node256(value, children)
|
|
153
|
+
case Node256(_, children) => Node256(value, children)
|
|
58
154
|
}
|
|
59
155
|
}
|
|
60
156
|
dictionary.set(go(dictionary.get, 0))
|
|
@@ -68,18 +164,56 @@ def lookup[A](dictionary: Dictionary[A], key: String): Option[A] =
|
|
|
68
164
|
def lookup[A](dictionary: Dictionary[A], key: ByteArray): Option[A] = {
|
|
69
165
|
def go(trie: Trie[A], i: Int): Option[A] =
|
|
70
166
|
if (i < key.size) {
|
|
167
|
+
val b = key.get(i)
|
|
71
168
|
trie match {
|
|
72
169
|
case Empty() => None()
|
|
73
170
|
case Leaf(_) => None()
|
|
74
|
-
case
|
|
75
|
-
|
|
171
|
+
case Branch1(byte1, child1) =>
|
|
172
|
+
if (b == byte1) go(child1.get, i + 1) else None()
|
|
173
|
+
case Node1(_, byte1, child1) =>
|
|
174
|
+
if (b == byte1) go(child1.get, i + 1) else None()
|
|
175
|
+
case Branch2(byte1, child1, byte2, child2) =>
|
|
176
|
+
if (b == byte1) go(child1.get, i + 1)
|
|
177
|
+
else if (b == byte2) go(child2.get, i + 1)
|
|
178
|
+
else None()
|
|
179
|
+
case Node2(_, byte1, child1, byte2, child2) =>
|
|
180
|
+
if (b == byte1) go(child1.get, i + 1)
|
|
181
|
+
else if (b == byte2) go(child2.get, i + 1)
|
|
182
|
+
else None()
|
|
183
|
+
case Branch3(byte1, child1, byte2, child2, byte3, child3) =>
|
|
184
|
+
if (b == byte1) go(child1.get, i + 1)
|
|
185
|
+
else if (b == byte2) go(child2.get, i + 1)
|
|
186
|
+
else if (b == byte3) go(child3.get, i + 1)
|
|
187
|
+
else None()
|
|
188
|
+
case Node3(_, byte1, child1, byte2, child2, byte3, child3) =>
|
|
189
|
+
if (b == byte1) go(child1.get, i + 1)
|
|
190
|
+
else if (b == byte2) go(child2.get, i + 1)
|
|
191
|
+
else if (b == byte3) go(child3.get, i + 1)
|
|
192
|
+
else None()
|
|
193
|
+
case Branch4(byte1, child1, byte2, child2, byte3, child3, byte4, child4) =>
|
|
194
|
+
if (b == byte1) go(child1.get, i + 1)
|
|
195
|
+
else if (b == byte2) go(child2.get, i + 1)
|
|
196
|
+
else if (b == byte3) go(child3.get, i + 1)
|
|
197
|
+
else if (b == byte4) go(child4.get, i + 1)
|
|
198
|
+
else None()
|
|
199
|
+
case Node4(_, byte1, child1, byte2, child2, byte3, child3, byte4, child4) =>
|
|
200
|
+
if (b == byte1) go(child1.get, i + 1)
|
|
201
|
+
else if (b == byte2) go(child2.get, i + 1)
|
|
202
|
+
else if (b == byte3) go(child3.get, i + 1)
|
|
203
|
+
else if (b == byte4) go(child4.get, i + 1)
|
|
204
|
+
else None()
|
|
205
|
+
case Branch256(children) => go(children.get(b.toInt), i + 1)
|
|
206
|
+
case Node256(_, children) => go(children.get(b.toInt), i + 1)
|
|
76
207
|
}
|
|
77
208
|
} else {
|
|
78
209
|
trie match {
|
|
79
|
-
case Empty() => None()
|
|
80
210
|
case Leaf(value) => Some(value)
|
|
81
|
-
case
|
|
82
|
-
case
|
|
211
|
+
case Node1(value, _, _) => Some(value)
|
|
212
|
+
case Node2(value, _, _, _, _) => Some(value)
|
|
213
|
+
case Node3(value, _, _, _, _, _, _) => Some(value)
|
|
214
|
+
case Node4(value, _, _, _, _, _, _, _, _) => Some(value)
|
|
215
|
+
case Node256(value, _) => Some(value)
|
|
216
|
+
case _ => None()
|
|
83
217
|
}
|
|
84
218
|
}
|
|
85
219
|
go(dictionary.get, 0)
|
|
@@ -87,48 +221,104 @@ def lookup[A](dictionary: Dictionary[A], key: ByteArray): Option[A] = {
|
|
|
87
221
|
|
|
88
222
|
/// Collect key-value pairs from a stream of Tagged elements into a fresh Dictionary.
|
|
89
223
|
def collect[A] { stream: () => Unit / emit[Tagged[A]] }: Dictionary[A] = {
|
|
90
|
-
val dictionary =
|
|
224
|
+
val dictionary = empty[A]()
|
|
91
225
|
for[Tagged[A]] { stream() } { case Tagged(bytes, value) =>
|
|
92
226
|
dictionary.insert(bytes, value)
|
|
93
227
|
}
|
|
94
|
-
|
|
228
|
+
dictionary
|
|
95
229
|
}
|
|
96
230
|
|
|
97
|
-
/// Emit all values stored in the dictionary as a push stream
|
|
231
|
+
/// Emit all values stored in the dictionary as a push stream,
|
|
232
|
+
/// in lexicographic key order (a key before its extensions).
|
|
98
233
|
def each[A](dictionary: Dictionary[A]): Unit / emit[A] = {
|
|
99
234
|
def go(trie: Trie[A]): Unit / emit[A] =
|
|
100
235
|
trie match {
|
|
101
236
|
case Empty() => ()
|
|
102
237
|
case Leaf(value) => do emit(value)
|
|
103
|
-
case
|
|
104
|
-
|
|
105
|
-
|
|
238
|
+
case Branch1(_, child1) => go(child1.get)
|
|
239
|
+
case Node1(value, _, child1) =>
|
|
240
|
+
do emit(value); go(child1.get)
|
|
241
|
+
case Branch2(_, child1, _, child2) =>
|
|
242
|
+
go(child1.get); go(child2.get)
|
|
243
|
+
case Node2(value, _, child1, _, child2) =>
|
|
244
|
+
do emit(value); go(child1.get); go(child2.get)
|
|
245
|
+
case Branch3(_, child1, _, child2, _, child3) =>
|
|
246
|
+
go(child1.get); go(child2.get); go(child3.get)
|
|
247
|
+
case Node3(value, _, child1, _, child2, _, child3) =>
|
|
248
|
+
do emit(value); go(child1.get); go(child2.get); go(child3.get)
|
|
249
|
+
case Branch4(_, child1, _, child2, _, child3, _, child4) =>
|
|
250
|
+
go(child1.get); go(child2.get); go(child3.get); go(child4.get)
|
|
251
|
+
case Node4(value, _, child1, _, child2, _, child3, _, child4) =>
|
|
252
|
+
do emit(value); go(child1.get); go(child2.get); go(child3.get); go(child4.get)
|
|
253
|
+
case Branch256(children) =>
|
|
254
|
+
each(0, 256) { i => go(children.get(i)) }
|
|
255
|
+
case Node256(value, children) =>
|
|
106
256
|
do emit(value)
|
|
107
|
-
each(0,
|
|
257
|
+
each(0, 256) { i => go(children.get(i)) }
|
|
108
258
|
}
|
|
109
259
|
go(dictionary.get)
|
|
110
260
|
}
|
|
111
261
|
|
|
112
|
-
/// Emit all key-value pairs stored in the dictionary as a push stream of
|
|
262
|
+
/// Emit all key-value pairs stored in the dictionary as a push stream of
|
|
263
|
+
/// Tagged elements, in lexicographic key order (a key before its extensions).
|
|
113
264
|
def eachTagged[A](dictionary: Dictionary[A]): Unit / emit[Tagged[A]] = {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
265
|
+
var buffer = bytearray::allocate(32)
|
|
266
|
+
// `depth` grows by exactly one per level, so `depth <= buffer.size` always
|
|
267
|
+
// holds and a single doubling suffices.
|
|
268
|
+
def push(depth: Int, b: Byte): Unit = {
|
|
269
|
+
if (depth >= buffer.size) {
|
|
270
|
+
buffer = buffer.resize(buffer.size * 2)
|
|
271
|
+
}
|
|
272
|
+
buffer.set(depth, b)
|
|
118
273
|
}
|
|
119
|
-
def
|
|
274
|
+
def emitAt(depth: Int, value: A): Unit = {
|
|
275
|
+
val key = bytearray::allocate(depth)
|
|
276
|
+
// safe: `key` has exactly `depth` bytes, and `depth` is within `buffer`'s capacity.
|
|
277
|
+
bytearray::unsafeCopy(buffer, 0, key, 0, depth)
|
|
278
|
+
do emit(Tagged(key, value))
|
|
279
|
+
}
|
|
280
|
+
def go(trie: Trie[A], depth: Int): Unit =
|
|
120
281
|
trie match {
|
|
121
282
|
case Empty() => ()
|
|
122
|
-
case Leaf(value) =>
|
|
123
|
-
case
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
283
|
+
case Leaf(value) => emitAt(depth, value)
|
|
284
|
+
case Branch1(byte1, child1) =>
|
|
285
|
+
push(depth, byte1); go(child1.get, depth + 1)
|
|
286
|
+
case Node1(value, byte1, child1) =>
|
|
287
|
+
emitAt(depth, value)
|
|
288
|
+
push(depth, byte1); go(child1.get, depth + 1)
|
|
289
|
+
case Branch2(byte1, child1, byte2, child2) =>
|
|
290
|
+
push(depth, byte1); go(child1.get, depth + 1)
|
|
291
|
+
push(depth, byte2); go(child2.get, depth + 1)
|
|
292
|
+
case Node2(value, byte1, child1, byte2, child2) =>
|
|
293
|
+
emitAt(depth, value)
|
|
294
|
+
push(depth, byte1); go(child1.get, depth + 1)
|
|
295
|
+
push(depth, byte2); go(child2.get, depth + 1)
|
|
296
|
+
case Branch3(byte1, child1, byte2, child2, byte3, child3) =>
|
|
297
|
+
push(depth, byte1); go(child1.get, depth + 1)
|
|
298
|
+
push(depth, byte2); go(child2.get, depth + 1)
|
|
299
|
+
push(depth, byte3); go(child3.get, depth + 1)
|
|
300
|
+
case Node3(value, byte1, child1, byte2, child2, byte3, child3) =>
|
|
301
|
+
emitAt(depth, value)
|
|
302
|
+
push(depth, byte1); go(child1.get, depth + 1)
|
|
303
|
+
push(depth, byte2); go(child2.get, depth + 1)
|
|
304
|
+
push(depth, byte3); go(child3.get, depth + 1)
|
|
305
|
+
case Branch4(byte1, child1, byte2, child2, byte3, child3, byte4, child4) =>
|
|
306
|
+
push(depth, byte1); go(child1.get, depth + 1)
|
|
307
|
+
push(depth, byte2); go(child2.get, depth + 1)
|
|
308
|
+
push(depth, byte3); go(child3.get, depth + 1)
|
|
309
|
+
push(depth, byte4); go(child4.get, depth + 1)
|
|
310
|
+
case Node4(value, byte1, child1, byte2, child2, byte3, child3, byte4, child4) =>
|
|
311
|
+
emitAt(depth, value)
|
|
312
|
+
push(depth, byte1); go(child1.get, depth + 1)
|
|
313
|
+
push(depth, byte2); go(child2.get, depth + 1)
|
|
314
|
+
push(depth, byte3); go(child3.get, depth + 1)
|
|
315
|
+
push(depth, byte4); go(child4.get, depth + 1)
|
|
316
|
+
case Branch256(children) =>
|
|
317
|
+
each(0, 256) { i => push(depth, i.toByte); go(children.get(i), depth + 1) }
|
|
318
|
+
case Node256(value, children) =>
|
|
319
|
+
emitAt(depth, value)
|
|
320
|
+
each(0, 256) { i => push(depth, i.toByte); go(children.get(i), depth + 1) }
|
|
132
321
|
}
|
|
133
|
-
go(dictionary.get,
|
|
134
|
-
}
|
|
322
|
+
go(dictionary.get, 0)
|
|
323
|
+
}
|
|
324
|
+
|
|
@@ -72,7 +72,7 @@ extern def readln() at async: String =
|
|
|
72
72
|
jsNode "$effekt.capture(callback => $effekt.readln(callback))"
|
|
73
73
|
llvm """
|
|
74
74
|
%result = call %Pos @c_io_readln()
|
|
75
|
-
|
|
75
|
+
notail call void @resume_Pos(%Stack %stack, %Pos %result)
|
|
76
76
|
ret void
|
|
77
77
|
"""
|
|
78
78
|
|
|
@@ -191,20 +191,25 @@ extern def random() at io: Double =
|
|
|
191
191
|
// extern io def freshGlobal[T](t: T): Ref[T] at global = js "$effekt.fresh(${t})"
|
|
192
192
|
|
|
193
193
|
|
|
194
|
-
//
|
|
194
|
+
// Comparison
|
|
195
195
|
// ========
|
|
196
196
|
|
|
197
197
|
/// Result of comparison between two objects according to some order:
|
|
198
198
|
///
|
|
199
199
|
/// - `Less()` means that the first object is *before* the first in the order,
|
|
200
|
-
/// - `Equal()` means that the two objects are the same in the order,
|
|
201
|
-
/// -
|
|
200
|
+
/// - `Equal()` means that the two objects are the same in the order, and
|
|
201
|
+
/// - `Greater()` means that the second object is *before* the first in the order.
|
|
202
202
|
type Ordering {
|
|
203
203
|
Less();
|
|
204
204
|
Equal();
|
|
205
205
|
Greater()
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
def ordering::fromInt(n: Int): Ordering =
|
|
209
|
+
if (n == 0) Equal()
|
|
210
|
+
else if (n < 0) Less()
|
|
211
|
+
else Greater()
|
|
212
|
+
|
|
208
213
|
def compareInt(n: Int, m: Int) =
|
|
209
214
|
if (n == m) Equal()
|
|
210
215
|
else if (n < m) Less()
|
|
@@ -215,16 +220,8 @@ extern def genericCompareImpl[R](x: R, y: R) at {}: Int =
|
|
|
215
220
|
|
|
216
221
|
/// Compares two values of the same type, returning an `Ordering`.
|
|
217
222
|
/// Only available on the JavaScript backend.
|
|
218
|
-
def genericCompare[R](x: R, y: R): Ordering =
|
|
219
|
-
|
|
220
|
-
if (ret == 0) {
|
|
221
|
-
Equal()
|
|
222
|
-
} else if (ret < 0) {
|
|
223
|
-
Less()
|
|
224
|
-
} else { // ret > 0
|
|
225
|
-
Greater()
|
|
226
|
-
}
|
|
227
|
-
}
|
|
223
|
+
def genericCompare[R](x: R, y: R): Ordering =
|
|
224
|
+
genericCompareImpl(x, y).fromInt
|
|
228
225
|
|
|
229
226
|
def println(o: Ordering): Unit = println(o.show)
|
|
230
227
|
|
|
@@ -245,49 +245,49 @@ namespace internal {
|
|
|
245
245
|
extern def openForReading(path: String) at async: Int =
|
|
246
246
|
jsNode "$effekt.capture(callback => open(${path}, 'r', callback))"
|
|
247
247
|
llvm """
|
|
248
|
-
call void @c_fs_open_reading(%Pos ${path}, %Stack %stack)
|
|
248
|
+
notail call void @c_fs_open_reading(%Pos ${path}, %Stack %stack)
|
|
249
249
|
ret void
|
|
250
250
|
"""
|
|
251
251
|
|
|
252
252
|
extern def openForWriting(path: String) at async: Int =
|
|
253
253
|
jsNode "$effekt.capture(callback => open(${path}, 'w', callback))"
|
|
254
254
|
llvm """
|
|
255
|
-
call void @c_fs_open_writing(%Pos ${path}, %Stack %stack)
|
|
255
|
+
notail call void @c_fs_open_writing(%Pos ${path}, %Stack %stack)
|
|
256
256
|
ret void
|
|
257
257
|
"""
|
|
258
258
|
|
|
259
259
|
extern def openForAppending(path: String) at async: Int =
|
|
260
260
|
jsNode "$effekt.capture(callback => open(${path}, 'a', callback))"
|
|
261
261
|
llvm """
|
|
262
|
-
call void @c_fs_open_appending(%Pos ${path}, %Stack %stack)
|
|
262
|
+
notail call void @c_fs_open_appending(%Pos ${path}, %Stack %stack)
|
|
263
263
|
ret void
|
|
264
264
|
"""
|
|
265
265
|
|
|
266
266
|
extern def read(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int) at async: Int =
|
|
267
267
|
jsNode "$effekt.capture(callback => read(${file}, ${buffer}, ${offset}, ${size}, ${position}, callback))"
|
|
268
268
|
llvm """
|
|
269
|
-
call void @c_fs_read(%Int ${file}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Int ${position}, %Stack %stack)
|
|
269
|
+
notail call void @c_fs_read(%Int ${file}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Int ${position}, %Stack %stack)
|
|
270
270
|
ret void
|
|
271
271
|
"""
|
|
272
272
|
|
|
273
273
|
extern def write(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int) at async: Int =
|
|
274
274
|
jsNode "$effekt.capture(callback => write(${file}, ${buffer}, ${offset}, ${size}, ${position}, callback))"
|
|
275
275
|
llvm """
|
|
276
|
-
call void @c_fs_write(%Int ${file}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Int ${position}, %Stack %stack)
|
|
276
|
+
notail call void @c_fs_write(%Int ${file}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Int ${position}, %Stack %stack)
|
|
277
277
|
ret void
|
|
278
278
|
"""
|
|
279
279
|
|
|
280
280
|
extern def close(file: Int) at async: Int =
|
|
281
281
|
jsNode "$effekt.capture(callback => close(${file}, callback))"
|
|
282
282
|
llvm """
|
|
283
|
-
call void @c_fs_close(%Int ${file}, %Stack %stack)
|
|
283
|
+
notail call void @c_fs_close(%Int ${file}, %Stack %stack)
|
|
284
284
|
ret void
|
|
285
285
|
"""
|
|
286
286
|
|
|
287
287
|
extern def mkdir(path: String) at async: Int =
|
|
288
288
|
jsNode "$effekt.capture(callback => mkdir(${path}, callback))"
|
|
289
289
|
llvm """
|
|
290
|
-
call void @c_fs_mkdir(%Pos ${path}, %Stack %stack)
|
|
290
|
+
notail call void @c_fs_mkdir(%Pos ${path}, %Stack %stack)
|
|
291
291
|
ret void
|
|
292
292
|
"""
|
|
293
293
|
|