@effekt-lang/effekt 0.62.0 → 0.64.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 +40 -40
- package/libraries/common/bytearray.effekt +39 -0
- package/libraries/common/bytestream.effekt +337 -0
- package/libraries/common/effekt.effekt +29 -2
- package/libraries/common/io/env.effekt +74 -0
- package/libraries/common/io/network.effekt +101 -12
- package/libraries/common/json.effekt +7 -7
- package/libraries/common/list.effekt +5 -0
- package/libraries/common/map.effekt +4 -0
- package/libraries/common/partial.effekt +7 -0
- package/libraries/common/resizable_array.effekt +99 -38
- package/libraries/common/scanner.effekt +27 -30
- package/libraries/common/set.effekt +4 -0
- package/libraries/common/stream.effekt +46 -2
- package/libraries/common/test.effekt +10 -0
- package/libraries/llvm/io.c +39 -0
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +2 -2
- package/licenses/mit license - mit-license.html +51 -43
- package/licenses/the bsd license - bsd-license.html +52 -43
- package/licenses/the mit license - mit.html +52 -44
- package/package.json +1 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module io/env
|
|
2
|
+
|
|
3
|
+
/// Retrieve the value of an environment variable, returning `None()` if not set.
|
|
4
|
+
extern def getEnvironmentVariable(name: String) at io: Option[String] =
|
|
5
|
+
jsNode { js::getEnvironmentVariable(name) }
|
|
6
|
+
chez { chez::getEnvironmentVariable(name) }
|
|
7
|
+
llvm { llvm::getEnvironmentVariable(name) }
|
|
8
|
+
|
|
9
|
+
/// Check whether an environment variable is set.
|
|
10
|
+
extern def hasEnvironmentVariable(name: String) at io: Bool =
|
|
11
|
+
llvm { llvm::hasEnvironmentVariable(name) }
|
|
12
|
+
default { if (getEnvironmentVariable(name) is Some(_)) true else false }
|
|
13
|
+
|
|
14
|
+
namespace js {
|
|
15
|
+
extern def rawGetEnv(name: String) at io: String =
|
|
16
|
+
jsNode "process.env[${name}]"
|
|
17
|
+
|
|
18
|
+
def getEnvironmentVariable(name: String): Option[String] =
|
|
19
|
+
rawGetEnv(name).undefinedToOption
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
namespace chez {
|
|
23
|
+
extern def rawGetEnv(name: String) at io: String =
|
|
24
|
+
chez "(getenv ${name})"
|
|
25
|
+
|
|
26
|
+
def getEnvironmentVariable(name: String): Option[String] =
|
|
27
|
+
rawGetEnv(name).undefinedToOption // Chez: getenv returns #f when not found
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
namespace llvm {
|
|
31
|
+
extern llvm """
|
|
32
|
+
declare %Int @c_getenv_size(%Pos)
|
|
33
|
+
declare %Pos @c_getenv_value(%Pos, %Int)
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
extern def getEnvSize(name: String) at io: Int =
|
|
37
|
+
llvm """
|
|
38
|
+
%size = call %Int @c_getenv_size(%Pos ${name})
|
|
39
|
+
ret %Int %size
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
extern def getEnvValue(name: String, size: Int) at io: String =
|
|
43
|
+
llvm """
|
|
44
|
+
%val = call %Pos @c_getenv_value(%Pos ${name}, %Int ${size})
|
|
45
|
+
ret %Pos %val
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def getEnvironmentVariable(name: String): Option[String] = {
|
|
49
|
+
val size = getEnvSize(name)
|
|
50
|
+
if (size >= 0) Some(getEnvValue(name, size))
|
|
51
|
+
else None()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
def hasEnvironmentVariable(name: String): Bool =
|
|
55
|
+
getEnvSize(name) >= 0
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
namespace examples {
|
|
59
|
+
def main() = {
|
|
60
|
+
val path = getEnvironmentVariable("PATH")
|
|
61
|
+
println(path.isDefined) // should be defined on all platforms
|
|
62
|
+
|
|
63
|
+
val doesNotExist = getEnvironmentVariable("THIS_ENV_VAR_OUGHT_NOT_EXIST__WOW__EFFEKT_IS_AWESOME")
|
|
64
|
+
println(doesNotExist.show) // should not be defined
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
def _unused_other() = {
|
|
68
|
+
val h = getEnvironmentVariable("HOME")
|
|
69
|
+
h match {
|
|
70
|
+
case Some(value) => println(s"HOME is set to: ${value}")
|
|
71
|
+
case None() => println("HOME is not set")
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -42,7 +42,6 @@ def listen(listener: Listener, handler: Connection => Unit at {io, async, global
|
|
|
42
42
|
def shutdown(listener: Listener): Unit =
|
|
43
43
|
internal::shutdown(listener)
|
|
44
44
|
|
|
45
|
-
|
|
46
45
|
def streaming[R](connection: Connection) { program: () => R / {read[ByteArray], emit[ByteArray]} }: R / Exception[IOError] =
|
|
47
46
|
try {
|
|
48
47
|
program()
|
|
@@ -59,58 +58,148 @@ def streaming[R](connection: Connection) { program: () => R / {read[ByteArray],
|
|
|
59
58
|
|
|
60
59
|
namespace internal {
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
61
|
+
// TODO: also in `io/filesystem`, perhaps we want this in a common place?
|
|
62
|
+
extern js """
|
|
63
|
+
function toBuffer(buffer) {
|
|
64
|
+
return Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
65
|
+
}
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
extern jsNode """
|
|
69
|
+
const net = require('net');
|
|
70
|
+
|
|
71
|
+
const tcp$handles = new Map();
|
|
72
|
+
let tcp$next = 1;
|
|
73
|
+
function tcp$alloc(entry) { const h = tcp$next++; tcp$handles.set(h, entry); return h; }
|
|
74
|
+
function tcp$free(h) { tcp$handles.delete(h); }
|
|
75
|
+
|
|
76
|
+
function tcp$connect(host, port, callback) {
|
|
77
|
+
const socket = net.createConnection({ host, port });
|
|
78
|
+
socket.once('connect', () => { socket.pause(); callback(tcp$alloc({ socket })); });
|
|
79
|
+
socket.once('error', err => callback(-(err.errno || 1)));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function tcp$read(handle, buffer, offset, size, callback) {
|
|
83
|
+
const { socket } = tcp$handles.get(handle);
|
|
84
|
+
function cleanup() {
|
|
85
|
+
socket.removeListener('readable', onReadable);
|
|
86
|
+
socket.removeListener('end', onEnd);
|
|
87
|
+
socket.removeListener('error', onError);
|
|
88
|
+
}
|
|
89
|
+
function onReadable() { cleanup(); tryRead(); }
|
|
90
|
+
function onEnd() { cleanup(); callback(0); }
|
|
91
|
+
function onError(err) { cleanup(); callback(-(err.errno || 1)); }
|
|
92
|
+
function tryRead() {
|
|
93
|
+
const chunk = socket.read(size);
|
|
94
|
+
if (chunk !== null) { chunk.copy(toBuffer(buffer), offset); callback(chunk.length); return; }
|
|
95
|
+
if (socket.readableEnded) { callback(0); return; }
|
|
96
|
+
socket.once('readable', onReadable);
|
|
97
|
+
socket.once('end', onEnd);
|
|
98
|
+
socket.once('error', onError);
|
|
99
|
+
}
|
|
100
|
+
tryRead();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function tcp$write(handle, buffer, offset, size, callback) {
|
|
104
|
+
const { socket } = tcp$handles.get(handle);
|
|
105
|
+
socket.write(toBuffer(buffer).slice(offset, offset + size), err => {
|
|
106
|
+
if (err) callback(-(err.errno || 1)); else callback(size);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function tcp$close(handle, callback) {
|
|
111
|
+
const entry = tcp$handles.get(handle);
|
|
112
|
+
if (!entry || !entry.socket) { callback($effekt.unit); return; }
|
|
113
|
+
tcp$free(handle);
|
|
114
|
+
const { socket } = entry;
|
|
115
|
+
if (socket.destroyed) { callback($effekt.unit); return; }
|
|
116
|
+
socket.once('error', _err => {});
|
|
117
|
+
socket.end(() => { socket.destroy(); callback($effekt.unit); });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function tcp$bind(host, port) {
|
|
121
|
+
return tcp$alloc({ server: net.createServer(), host, port });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function tcp$listen(listenerHandle, handler, callback) {
|
|
125
|
+
const { server, host, port } = tcp$handles.get(listenerHandle);
|
|
126
|
+
server.once('error', err => callback(-(err.errno || 1)));
|
|
127
|
+
server.listen(port, host, () => {
|
|
128
|
+
server.removeAllListeners('error');
|
|
129
|
+
server.on('error', _err => {});
|
|
130
|
+
server.on('connection', socket => {
|
|
131
|
+
socket.pause();
|
|
132
|
+
const connHandle = tcp$alloc({ socket });
|
|
133
|
+
$effekt.runToplevel((ks, k) => handler(connHandle, ks, k));
|
|
134
|
+
});
|
|
135
|
+
callback(0);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function tcp$shutdown(listenerHandle, callback) {
|
|
140
|
+
const entry = tcp$handles.get(listenerHandle);
|
|
141
|
+
if (!entry || !entry.server) { callback($effekt.unit); return; }
|
|
142
|
+
tcp$free(listenerHandle);
|
|
143
|
+
entry.server.close(() => callback($effekt.unit));
|
|
144
|
+
}
|
|
70
145
|
"""
|
|
71
146
|
|
|
72
147
|
extern def connect(host: String, port: Int) at async: Int =
|
|
148
|
+
jsNode "$effekt.capture(k => tcp$connect(${host}, ${port}, k))"
|
|
73
149
|
llvm """
|
|
74
150
|
call void @c_tcp_connect(%Pos ${host}, %Int ${port}, %Stack %stack)
|
|
75
151
|
ret void
|
|
76
152
|
"""
|
|
77
153
|
|
|
78
154
|
extern def read(handle: Int, buffer: ByteArray, offset: Int, size: Int) at async: Int =
|
|
155
|
+
jsNode "$effekt.capture(k => tcp$read(${handle}, ${buffer}, ${offset}, ${size}, k))"
|
|
79
156
|
llvm """
|
|
80
157
|
call void @c_tcp_read(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
|
|
81
158
|
ret void
|
|
82
159
|
"""
|
|
83
160
|
|
|
84
161
|
extern def write(handle: Int, buffer: ByteArray, offset: Int, size: Int) at async: Int =
|
|
162
|
+
jsNode "$effekt.capture(k => tcp$write(${handle}, ${buffer}, ${offset}, ${size}, k))"
|
|
85
163
|
llvm """
|
|
86
164
|
call void @c_tcp_write(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
|
|
87
165
|
ret void
|
|
88
166
|
"""
|
|
89
167
|
|
|
90
168
|
extern def close(handle: Int) at async: Unit =
|
|
169
|
+
jsNode "$effekt.capture(k => tcp$close(${handle}, k))"
|
|
91
170
|
llvm """
|
|
92
171
|
call void @c_tcp_close(%Int ${handle}, %Stack %stack)
|
|
93
172
|
ret void
|
|
94
173
|
"""
|
|
95
174
|
|
|
96
175
|
extern def bind(host: String, port: Int) at io: Int =
|
|
176
|
+
jsNode "tcp$bind(${host}, ${port})"
|
|
97
177
|
llvm """
|
|
98
178
|
%result = call %Int @c_tcp_bind(%Pos ${host}, %Int ${port})
|
|
99
179
|
ret %Int %result
|
|
100
180
|
"""
|
|
101
181
|
|
|
102
|
-
extern def listen(listener: Int, handler:
|
|
182
|
+
extern def listen(listener: Int, handler: Connection => Unit at {io, async, global}) at async: Int =
|
|
183
|
+
jsNode "$effekt.capture(k => tcp$listen(${listener}, ${handler}, k))"
|
|
103
184
|
llvm """
|
|
104
185
|
call void @c_tcp_listen(%Int ${listener}, %Pos ${handler}, %Stack %stack)
|
|
105
186
|
ret void
|
|
106
187
|
"""
|
|
107
188
|
|
|
108
189
|
extern def shutdown(handle: Int) at async: Unit =
|
|
190
|
+
jsNode "$effekt.capture(k => tcp$shutdown(${handle}, k))"
|
|
109
191
|
llvm """
|
|
110
192
|
call void @c_tcp_shutdown(%Int ${handle}, %Stack %stack)
|
|
111
193
|
ret void
|
|
112
194
|
"""
|
|
113
195
|
|
|
196
|
+
extern llvm """
|
|
197
|
+
declare void @c_tcp_connect(%Pos, %Int, %Stack)
|
|
198
|
+
declare void @c_tcp_read(%Int, %Pos, %Int, %Int, %Stack)
|
|
199
|
+
declare void @c_tcp_write(%Int, %Pos, %Int, %Int, %Stack)
|
|
200
|
+
declare void @c_tcp_close(%Int, %Stack)
|
|
201
|
+
declare %Int @c_tcp_bind(%Pos, %Int)
|
|
202
|
+
declare void @c_tcp_listen(%Int, %Pos, %Stack)
|
|
203
|
+
declare void @c_tcp_shutdown(%Int, %Stack)
|
|
204
|
+
"""
|
|
114
205
|
}
|
|
115
|
-
|
|
116
|
-
|
|
@@ -95,10 +95,10 @@ def readDouble(): Double / { Scan[Char], Exception[WrongFormat]} = {
|
|
|
95
95
|
def exponent(pre: Double): Double / fail = {
|
|
96
96
|
try {
|
|
97
97
|
if(do peek() == 'e' || do peek() == 'E') {
|
|
98
|
-
|
|
98
|
+
skip[Char]()
|
|
99
99
|
val e = do peek[Char]() match {
|
|
100
|
-
case '+' =>
|
|
101
|
-
case '-' =>
|
|
100
|
+
case '+' => skip[Char](); readDecimal()
|
|
101
|
+
case '-' => skip[Char](); neg(readDecimal())
|
|
102
102
|
case _ => readDecimal()
|
|
103
103
|
}
|
|
104
104
|
pre * pow(10.0, e)
|
|
@@ -133,9 +133,9 @@ def readQuotedString(): Unit / { Scan[Char], emit[Char], Exception[WrongFormat]
|
|
|
133
133
|
with expect("Unexpected end of input while reading a string")
|
|
134
134
|
skipWhitespace()
|
|
135
135
|
expect("Expected \"") { must { readIf('"') } }
|
|
136
|
-
while(must { read[Char]() } is c and c != '"') {
|
|
136
|
+
while(must { do read[Char]() } is c and c != '"') {
|
|
137
137
|
c match {
|
|
138
|
-
case '\\' => must { read[Char]() } match {
|
|
138
|
+
case '\\' => must { do read[Char]() } match {
|
|
139
139
|
case '"' => do emit('\"')
|
|
140
140
|
case '\\' => do emit('\\')
|
|
141
141
|
case '/' => do emit('/')
|
|
@@ -184,7 +184,7 @@ def decodeJsonObject(): Unit / {Scan[Char], JsonObjectBuilder, Exception[WrongFo
|
|
|
184
184
|
skipWhitespace()
|
|
185
185
|
first = false
|
|
186
186
|
}
|
|
187
|
-
must {
|
|
187
|
+
must { skip() }
|
|
188
188
|
}
|
|
189
189
|
def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
|
|
190
190
|
var first = true
|
|
@@ -197,7 +197,7 @@ def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} =
|
|
|
197
197
|
skipWhitespace()
|
|
198
198
|
first = false
|
|
199
199
|
}
|
|
200
|
-
must {
|
|
200
|
+
must { skip() }
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
// --------------------------------------------------------------------------------
|
|
@@ -962,6 +962,7 @@ def tails[A](list: List[A]): Unit / emit[List[A]] =
|
|
|
962
962
|
case Cons(head, tail) => do emit(list); tails(tail)
|
|
963
963
|
}
|
|
964
964
|
|
|
965
|
+
/// Connects `list` as a producer to a given pull stream `reader` acting as a consumer.
|
|
965
966
|
def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
|
|
966
967
|
var l = list
|
|
967
968
|
try {
|
|
@@ -978,10 +979,14 @@ def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
|
|
|
978
979
|
}
|
|
979
980
|
}
|
|
980
981
|
|
|
982
|
+
/// Collects elements emitted by a push stream producer `stream` into a new list,
|
|
983
|
+
/// discarding the result of the producer.
|
|
981
984
|
def collect[A] { stream: () => Unit / emit[A] }: List[A] =
|
|
982
985
|
returning::collect[A, Unit]{stream}.second
|
|
983
986
|
|
|
984
987
|
namespace returning {
|
|
988
|
+
/// Collects elements emitted by a push stream producer `stream` into a new list,
|
|
989
|
+
/// returning the result of the producer as well as the collected list.
|
|
985
990
|
def collect[A, R] { stream: () => R / emit[A] }: (R, List[A]) =
|
|
986
991
|
try {
|
|
987
992
|
(stream(), Nil())
|
|
@@ -1012,10 +1012,14 @@ def eachKey[K, V](map: Map[K, V]): Unit / emit[K] =
|
|
|
1012
1012
|
def eachValue[K, V](map: Map[K, V]): Unit / emit[V] =
|
|
1013
1013
|
map.foreach { (_k, v) => do emit(v) }
|
|
1014
1014
|
|
|
1015
|
+
/// Collects `(key, value)` pairs from a push stream producer `stream` into a new `map`
|
|
1016
|
+
/// using the provided `compare` function, discarding the result of the producer.
|
|
1015
1017
|
def collect[K, V](compare: (K, K) => Ordering at {}) { stream: () => Unit / emit[(K, V)] }: Map[K, V] =
|
|
1016
1018
|
returning::collect[K, V, Unit](compare){stream}.second
|
|
1017
1019
|
|
|
1018
1020
|
namespace returning {
|
|
1021
|
+
/// Collects `(key, value)` pairs from a push stream producer `stream` into a new `map`,
|
|
1022
|
+
/// using the provided `compare` function, returning the result of the producer as well as the collected map.
|
|
1019
1023
|
def collect[K, V, R](compare: (K, K) => Ordering at {}) { stream: () => R / emit[(K, V)] }: (R, Map[K, V]) =
|
|
1020
1024
|
try {
|
|
1021
1025
|
(stream(), empty(compare))
|
|
@@ -13,6 +13,13 @@ def attempt[R] { program: () => R / fail } { fallback: () => R }: R =
|
|
|
13
13
|
fallback()
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
def onFail[R] { fallback: () => R } { program: () => R / fail }: R =
|
|
17
|
+
try {
|
|
18
|
+
program()
|
|
19
|
+
} with fail {
|
|
20
|
+
fallback()
|
|
21
|
+
}
|
|
22
|
+
|
|
16
23
|
def succeeds { program: () => Unit / fail }: Bool =
|
|
17
24
|
try { program(); true } with fail { false }
|
|
18
25
|
|
|
@@ -3,6 +3,7 @@ module resizable_array
|
|
|
3
3
|
import ref
|
|
4
4
|
import array
|
|
5
5
|
|
|
6
|
+
/// A resizable array, implemented as a wrapper around a mutable array and a size field.
|
|
6
7
|
record ResizableArray[T](rawSizePtr: Ref[Int], rawContentPtr: Ref[Array[T]])
|
|
7
8
|
|
|
8
9
|
// These numbers should be optimized based on benchmarking
|
|
@@ -20,11 +21,10 @@ val shrinkThreshold = 0.4
|
|
|
20
21
|
def size[T](arr: ResizableArray[T]) = arr.rawSizePtr.get
|
|
21
22
|
|
|
22
23
|
/// Allocate a new, empty dynamic array with given initial capacity
|
|
23
|
-
def resizableArray[T](capacity: Int): ResizableArray[T] =
|
|
24
|
+
def resizableArray[T](capacity: Int): ResizableArray[T] =
|
|
24
25
|
ResizableArray(ref(0), ref(unsafeAllocate(capacity)))
|
|
25
|
-
}
|
|
26
26
|
|
|
27
|
-
///
|
|
27
|
+
/// Allocate a new, empty dynamic array
|
|
28
28
|
def resizableArray[T](): ResizableArray[T] = resizableArray(8)
|
|
29
29
|
|
|
30
30
|
|
|
@@ -35,7 +35,7 @@ def boundsCheck[T](arr: ResizableArray[T], index: Int): Unit / Exception[OutOfBo
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
///
|
|
38
|
+
/// Get the element at position `index` in the resizable array `arr`
|
|
39
39
|
///
|
|
40
40
|
/// O(1)
|
|
41
41
|
def get[T](arr: ResizableArray[T], index: Int): T / Exception[OutOfBounds] = {
|
|
@@ -43,7 +43,7 @@ def get[T](arr: ResizableArray[T], index: Int): T / Exception[OutOfBounds] = {
|
|
|
43
43
|
arr.rawContentPtr.get.get(index)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
///
|
|
46
|
+
/// Set the element at position `index` in the resizable array `arr` to `value`
|
|
47
47
|
///
|
|
48
48
|
/// O(1)
|
|
49
49
|
def set[T](arr: ResizableArray[T], index: Int, value: T): Unit / Exception[OutOfBounds] = {
|
|
@@ -51,7 +51,7 @@ def set[T](arr: ResizableArray[T], index: Int, value: T): Unit / Exception[OutOf
|
|
|
51
51
|
arr.rawContentPtr.get.set(index, value)
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
///
|
|
54
|
+
/// Swap the elements at the given positions in the resizable array `arr`
|
|
55
55
|
///
|
|
56
56
|
/// O(1)
|
|
57
57
|
def swap[T](arr: ResizableArray[T], index1: Int, index2: Int): Unit / Exception[OutOfBounds] = {
|
|
@@ -71,7 +71,7 @@ def setCapacity[T](arr: ResizableArray[T], capacity: Int): Unit / Exception[OutO
|
|
|
71
71
|
do raise(OutOfBounds(), "Cannot change capacity of ResizableArray to " ++ capacity.show ++ " below size " ++ arr.size.show)
|
|
72
72
|
}
|
|
73
73
|
val oldRaw = arr.rawContentPtr.get
|
|
74
|
-
if (oldRaw.size != capacity) {
|
|
74
|
+
if (oldRaw.size != capacity) {
|
|
75
75
|
val newRaw = array::unsafeAllocate(capacity)
|
|
76
76
|
oldRaw.copy(0, newRaw, 0, arr.size)
|
|
77
77
|
arr.rawContentPtr.set(newRaw)
|
|
@@ -82,13 +82,13 @@ def setCapacity[T](arr: ResizableArray[T], capacity: Int): Unit / Exception[OutO
|
|
|
82
82
|
///
|
|
83
83
|
/// O(n)
|
|
84
84
|
def maybeShrink[T](arr: ResizableArray[T]): Unit / Exception[OutOfBounds] = {
|
|
85
|
-
if(arr.size.toDouble < arr.rawContentPtr.get.size.toDouble * shrinkThreshold) {
|
|
85
|
+
if (arr.size.toDouble < arr.rawContentPtr.get.size.toDouble * shrinkThreshold) {
|
|
86
86
|
val newCap = max(arr.size, (arr.rawContentPtr.get.size.toDouble / growFactor).ceil)
|
|
87
87
|
arr.setCapacity(newCap)
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
///
|
|
91
|
+
/// Makes sure capacity is at least the given one
|
|
92
92
|
///
|
|
93
93
|
/// O(given capacity - current capacity) amortized, O(n) worst case // TODO ?
|
|
94
94
|
def ensureCapacity[T](arr: ResizableArray[T], capacity: Int): Unit / Exception[OutOfBounds] = {
|
|
@@ -140,62 +140,123 @@ def popRight[T](arr: ResizableArray[T]): T / Exception[OutOfBounds] = {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
/// Traverse the resizable array `arr`, applying the given action on every element.
|
|
144
|
+
def foreach[T](arr: ResizableArray[T]){ action: T => Unit }: Unit =
|
|
144
145
|
each(0, arr.size) { i =>
|
|
145
|
-
|
|
146
|
+
action(arr.rawContentPtr.get.get(i))
|
|
146
147
|
}
|
|
147
|
-
|
|
148
|
-
|
|
148
|
+
|
|
149
|
+
/// Traverse the resizable array `arr`, applying the given action on every element,
|
|
150
|
+
/// providing a `label` for `break`/`continue`.
|
|
151
|
+
def foreach[T](arr: ResizableArray[T]){ action: (T) {Label} => Unit }: Unit =
|
|
149
152
|
each(0, arr.size) { (i){label} =>
|
|
150
|
-
|
|
153
|
+
action(arr.rawContentPtr.get.get(i)){label}
|
|
151
154
|
}
|
|
152
|
-
|
|
153
|
-
|
|
155
|
+
|
|
156
|
+
/// Traverse the resizable array `arr`, applying the given action on every element and its (zero-based) index.
|
|
157
|
+
def foreachIndex[T](arr: ResizableArray[T]){ action: (Int, T) => Unit }: Unit =
|
|
154
158
|
each(0, arr.size) { i =>
|
|
155
|
-
|
|
159
|
+
action(i, arr.rawContentPtr.get.get(i))
|
|
156
160
|
}
|
|
157
|
-
|
|
158
|
-
|
|
161
|
+
|
|
162
|
+
/// Traverse the resizable array `arr`, applying the given action on every element and its (zero-based) index,
|
|
163
|
+
/// providing a `label` for `break`/`continue`.
|
|
164
|
+
def foreachIndex[T](arr: ResizableArray[T]){ action: (Int, T) {Label} => Unit }: Unit =
|
|
159
165
|
each(0, arr.size) { (i){label} =>
|
|
160
|
-
|
|
166
|
+
action(i, arr.rawContentPtr.get.get(i)){label}
|
|
161
167
|
}
|
|
162
|
-
|
|
163
|
-
|
|
168
|
+
|
|
169
|
+
/// Traverse the resizable array `arr` in reverse order, applying the given action on every element.
|
|
170
|
+
def foreachReversed[T](arr: ResizableArray[T]){ action: T => Unit }: Unit =
|
|
171
|
+
var i = arr.size - 1
|
|
172
|
+
while (i >= 0) {
|
|
173
|
+
action(arr.rawContentPtr.get.get(i))
|
|
174
|
+
i = i - 1
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/// Traverse the resizable array `arr` in reverse order, applying the given action on every element,
|
|
178
|
+
/// providing a `label` for `break`/`continue`.
|
|
179
|
+
def foreachReversed[T](arr: ResizableArray[T]){ action: (T) {Label} => Unit }: Unit =
|
|
164
180
|
var i = arr.size - 1
|
|
165
181
|
loop { {l} =>
|
|
166
182
|
if (i < 0) { l.break() }
|
|
167
|
-
|
|
183
|
+
action(arr.rawContentPtr.get.get(i)){l}
|
|
168
184
|
i = i - 1
|
|
169
185
|
}
|
|
170
|
-
|
|
171
|
-
|
|
186
|
+
|
|
187
|
+
/// Traverse the resizable array `arr` in reverse order, applying the given action on every element and its (zero-based) index.
|
|
188
|
+
def foreachIndexReversed[T](arr: ResizableArray[T]){ action: (Int, T) => Unit }: Unit =
|
|
172
189
|
var i = arr.size - 1
|
|
173
190
|
while(i >= 0) {
|
|
174
|
-
|
|
191
|
+
action(i, arr.rawContentPtr.get.get(i))
|
|
175
192
|
i = i - 1
|
|
176
193
|
}
|
|
177
|
-
|
|
178
|
-
|
|
194
|
+
|
|
195
|
+
/// Traverse the resizable array `arr` in reverse order, applying the given action on every element and its (zero-based) index,
|
|
196
|
+
/// providing a `label` for `break`/`continue`.
|
|
197
|
+
def foreachIndexReversed[T](arr: ResizableArray[T]){ action: (Int, T) {Label} => Unit }: Unit =
|
|
179
198
|
var i = arr.size - 1
|
|
180
199
|
loop { {l} =>
|
|
181
200
|
if (i < 0) { l.break() }
|
|
182
|
-
|
|
201
|
+
action(i, arr.rawContentPtr.get.get(i)){l}
|
|
183
202
|
i = i - 1
|
|
184
203
|
}
|
|
185
|
-
}
|
|
186
|
-
def foreachReversed[T](arr: ResizableArray[T]){ body: T => Unit }: Unit = {
|
|
187
|
-
var i = arr.size - 1
|
|
188
|
-
while (i >= 0) {
|
|
189
|
-
body(arr.rawContentPtr.get.get(i))
|
|
190
|
-
i = i - 1
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
204
|
|
|
205
|
+
|
|
206
|
+
/// Create a new list from a given resizable array `arr`.
|
|
194
207
|
def toList[T](arr: ResizableArray[T]): List[T] = {
|
|
195
208
|
def go(i: Int, acc: List[T]): List[T] = {
|
|
196
|
-
if (i < 0) {
|
|
209
|
+
if (i < 0) {
|
|
210
|
+
acc
|
|
211
|
+
} else {
|
|
197
212
|
go(i - 1, Cons(arr.rawContentPtr.get.get(i), acc))
|
|
198
213
|
}
|
|
199
214
|
}
|
|
215
|
+
|
|
200
216
|
go(arr.size - 1, Nil())
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
/// Turns `arr` into a producer of a push stream
|
|
221
|
+
/// by emitting each contained value from 0 to length - 1.
|
|
222
|
+
def each[T](arr: ResizableArray[T]): Unit / emit[T] =
|
|
223
|
+
each(0, arr.size) { i =>
|
|
224
|
+
do emit(arr.rawContentPtr.get.get(i))
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/// Connects `arr` as a producer to a given pull stream `reader` acting as a consumer.
|
|
228
|
+
def feed[R, T](arr: ResizableArray[T]) { reader: () => R / read[T] }: R = {
|
|
229
|
+
var i = 0
|
|
230
|
+
try {
|
|
231
|
+
reader()
|
|
232
|
+
} with read[T] {
|
|
233
|
+
resume {
|
|
234
|
+
if (i < arr.size) {
|
|
235
|
+
val v = arr.rawContentPtr.get.get(i)
|
|
236
|
+
i = i + 1
|
|
237
|
+
v
|
|
238
|
+
} else {
|
|
239
|
+
do stop()
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/// Collects elements emitted by a push stream producer `stream` into a new resizable array,
|
|
246
|
+
/// discarding the result of the producer.
|
|
247
|
+
def collect[T] { stream: () => Unit / emit[T] }: ResizableArray[T] =
|
|
248
|
+
returning::collect[T, Unit]{stream}.second
|
|
249
|
+
|
|
250
|
+
namespace returning {
|
|
251
|
+
/// Collects elements emitted by a push stream producer `stream` into a new resizable array,
|
|
252
|
+
/// returning the result of the producer as well as the collected array.
|
|
253
|
+
def collect[T, R] { stream: () => R / emit[T] }: (R, ResizableArray[T]) = {
|
|
254
|
+
val arr = resizableArray()
|
|
255
|
+
try {
|
|
256
|
+
(stream(), arr)
|
|
257
|
+
} with emit[T] { v =>
|
|
258
|
+
arr.add(v)
|
|
259
|
+
resume(())
|
|
260
|
+
}
|
|
261
|
+
}
|
|
201
262
|
}
|