@effekt-lang/effekt 0.46.0 → 0.48.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/chez/common/immutable/cslist.effekt +6 -6
- package/libraries/chez/common/mutable/dict.effekt +7 -7
- package/libraries/common/args.effekt +10 -10
- package/libraries/common/array.effekt +57 -4
- package/libraries/common/bench.effekt +2 -2
- package/libraries/common/buffer.effekt +5 -5
- package/libraries/common/bytearray.effekt +69 -7
- package/libraries/common/char.effekt +95 -8
- package/libraries/common/effekt.effekt +74 -71
- package/libraries/common/exception.effekt +1 -1
- package/libraries/common/io/console.effekt +4 -4
- package/libraries/common/io/error.effekt +12 -1
- package/libraries/common/io/filesystem.effekt +78 -16
- package/libraries/common/io/network.effekt +97 -65
- package/libraries/common/io/time.effekt +1 -1
- package/libraries/common/io.effekt +7 -8
- package/libraries/common/json.effekt +5 -5
- package/libraries/common/list.effekt +154 -18
- package/libraries/common/map.effekt +35 -1
- package/libraries/common/process.effekt +1 -1
- package/libraries/common/queue.effekt +3 -3
- package/libraries/common/random.effekt +1 -0
- package/libraries/common/ref.effekt +8 -4
- package/libraries/common/regex.effekt +4 -4
- package/libraries/common/seq.effekt +1 -1
- package/libraries/common/set.effekt +24 -1
- package/libraries/common/stream.effekt +2 -360
- package/libraries/common/string.effekt +35 -5
- package/libraries/common/stringbuffer.effekt +1 -0
- package/libraries/js/mutable/map.effekt +5 -5
- package/libraries/js/web/dom.effekt +11 -11
- package/libraries/llvm/io.c +281 -8
- package/libraries/llvm/main.c +1 -2
- package/libraries/llvm/rts.ll +14 -7
- package/libraries/llvm/types.c +3 -3
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +2 -2
- package/licenses/mit license - mit-license.html +48 -48
- package/licenses/the bsd license - bsd-license.html +50 -50
- package/licenses/the mit license - mit.html +48 -48
- package/package.json +1 -1
|
@@ -71,6 +71,77 @@ def appendFile(path: String, contents: String): Unit / Exception[IOError] = {
|
|
|
71
71
|
go()
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
def writeFileUTF8[R](path: String) { stream: () => R / emit[Char] }: R / Exception[IOError] =
|
|
75
|
+
writeFile(path) { encodeUTF8 { stream() } }
|
|
76
|
+
|
|
77
|
+
def readFileUTF8[R](path: String) { reader: () => R / read[Char] }: R / Exception[IOError] =
|
|
78
|
+
readFile(path) { decodeUTF8 { reader() } }
|
|
79
|
+
|
|
80
|
+
def writeFile[R](path: String) { stream: () => R / emit[Byte] }: R / Exception[IOError] = {
|
|
81
|
+
|
|
82
|
+
val file = openForWriting(path);
|
|
83
|
+
with on[IOError].finalize { close(file) }
|
|
84
|
+
|
|
85
|
+
val chunkSize = 1048576 // 1MB
|
|
86
|
+
val buffer = bytearray::allocate(chunkSize)
|
|
87
|
+
var offset = 0
|
|
88
|
+
|
|
89
|
+
def push(i: Int, n: Int): Unit = {
|
|
90
|
+
val r = write(file, buffer, i, n, -1)
|
|
91
|
+
if (r < n) {
|
|
92
|
+
push(i + r, n - r)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
val r = stream()
|
|
98
|
+
push(0, offset)
|
|
99
|
+
return r
|
|
100
|
+
} with emit[Byte] { (byte) =>
|
|
101
|
+
if (offset >= buffer.size) {
|
|
102
|
+
push(0, buffer.size)
|
|
103
|
+
offset = 0
|
|
104
|
+
}
|
|
105
|
+
buffer.unsafeSet(offset, byte)
|
|
106
|
+
offset = offset + 1
|
|
107
|
+
resume(())
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
def readFile[R](path: String) { reader: () => R / read[Byte] }: R / Exception[IOError] = {
|
|
112
|
+
|
|
113
|
+
val file = openForReading(path);
|
|
114
|
+
with on[IOError].finalize { close(file) }
|
|
115
|
+
|
|
116
|
+
val chunkSize = 1048576 // 1MB
|
|
117
|
+
val buffer = bytearray::allocate(chunkSize)
|
|
118
|
+
var offset = 0
|
|
119
|
+
var length = 0
|
|
120
|
+
|
|
121
|
+
def pull(): Unit / stop = {
|
|
122
|
+
read(file, buffer, 0, chunkSize, -1) match {
|
|
123
|
+
case 0 =>
|
|
124
|
+
do stop()
|
|
125
|
+
case n =>
|
|
126
|
+
length = n
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
reader()
|
|
132
|
+
} with read[Byte] {
|
|
133
|
+
resume {
|
|
134
|
+
if (offset >= length) {
|
|
135
|
+
pull()
|
|
136
|
+
offset = 0
|
|
137
|
+
}
|
|
138
|
+
val byte = buffer.unsafeGet(offset)
|
|
139
|
+
offset = offset + 1
|
|
140
|
+
return byte
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
74
145
|
/// An abstract interface applications can program against.
|
|
75
146
|
///
|
|
76
147
|
/// Can be interpreted with the `filesystem` handler, or virtualized etc.
|
|
@@ -171,64 +242,55 @@ namespace internal {
|
|
|
171
242
|
declare void @c_fs_mkdir(%Pos, %Stack)
|
|
172
243
|
"""
|
|
173
244
|
|
|
174
|
-
extern
|
|
245
|
+
extern def openForReading(path: String) at async: Int =
|
|
175
246
|
jsNode "$effekt.capture(callback => open(${path}, 'r', callback))"
|
|
176
247
|
llvm """
|
|
177
248
|
call void @c_fs_open_reading(%Pos ${path}, %Stack %stack)
|
|
178
249
|
ret void
|
|
179
250
|
"""
|
|
180
251
|
|
|
181
|
-
extern
|
|
252
|
+
extern def openForWriting(path: String) at async: Int =
|
|
182
253
|
jsNode "$effekt.capture(callback => open(${path}, 'w', callback))"
|
|
183
254
|
llvm """
|
|
184
255
|
call void @c_fs_open_writing(%Pos ${path}, %Stack %stack)
|
|
185
256
|
ret void
|
|
186
257
|
"""
|
|
187
258
|
|
|
188
|
-
extern
|
|
259
|
+
extern def openForAppending(path: String) at async: Int =
|
|
189
260
|
jsNode "$effekt.capture(callback => open(${path}, 'a', callback))"
|
|
190
261
|
llvm """
|
|
191
262
|
call void @c_fs_open_appending(%Pos ${path}, %Stack %stack)
|
|
192
263
|
ret void
|
|
193
264
|
"""
|
|
194
265
|
|
|
195
|
-
|
|
196
|
-
extern async def read(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int): Int =
|
|
266
|
+
extern def read(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int) at async: Int =
|
|
197
267
|
jsNode "$effekt.capture(callback => read(${file}, ${buffer}, ${offset}, ${size}, ${position}, callback))"
|
|
198
268
|
llvm """
|
|
199
269
|
call void @c_fs_read(%Int ${file}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Int ${position}, %Stack %stack)
|
|
200
270
|
ret void
|
|
201
271
|
"""
|
|
202
272
|
|
|
203
|
-
|
|
204
|
-
extern async def write(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int): Int =
|
|
273
|
+
extern def write(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int) at async: Int =
|
|
205
274
|
jsNode "$effekt.capture(callback => write(${file}, ${buffer}, ${offset}, ${size}, ${position}, callback))"
|
|
206
275
|
llvm """
|
|
207
276
|
call void @c_fs_write(%Int ${file}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Int ${position}, %Stack %stack)
|
|
208
277
|
ret void
|
|
209
278
|
"""
|
|
210
279
|
|
|
211
|
-
extern
|
|
280
|
+
extern def close(file: Int) at async: Int =
|
|
212
281
|
jsNode "$effekt.capture(callback => close(${file}, callback))"
|
|
213
282
|
llvm """
|
|
214
283
|
call void @c_fs_close(%Int ${file}, %Stack %stack)
|
|
215
284
|
ret void
|
|
216
285
|
"""
|
|
217
286
|
|
|
218
|
-
extern
|
|
287
|
+
extern def mkdir(path: String) at async: Int =
|
|
219
288
|
jsNode "$effekt.capture(callback => mkdir(${path}, callback))"
|
|
220
289
|
llvm """
|
|
221
290
|
call void @c_fs_mkdir(%Pos ${path}, %Stack %stack)
|
|
222
291
|
ret void
|
|
223
292
|
"""
|
|
224
293
|
|
|
225
|
-
def checkResult(result: Int): Int / Exception[IOError] =
|
|
226
|
-
if (result < 0) {
|
|
227
|
-
val ioError = fromNumber(internal::errorNumber(result));
|
|
228
|
-
do raise[IOError](ioError, message(ioError))
|
|
229
|
-
} else {
|
|
230
|
-
result
|
|
231
|
-
}
|
|
232
294
|
}
|
|
233
295
|
|
|
234
296
|
namespace examples {
|
|
@@ -1,84 +1,116 @@
|
|
|
1
1
|
module io/network
|
|
2
2
|
|
|
3
|
+
import stream
|
|
3
4
|
import bytearray
|
|
4
|
-
import io
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
extern jsNode """
|
|
8
|
-
const net = require('node:net');
|
|
6
|
+
import io/error
|
|
9
7
|
|
|
10
|
-
function listen(server, port, host, listener) {
|
|
11
|
-
server.listen(port, host);
|
|
12
|
-
server.on('connection', listener);
|
|
13
|
-
}
|
|
14
|
-
"""
|
|
15
8
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
extern io def server(): JSServer =
|
|
19
|
-
jsNode "net.createServer()"
|
|
20
|
-
extern io def listen(server: JSServer, port: Int, host: String, listener: JSSocket => Unit at {io, async, global}): Unit =
|
|
21
|
-
jsNode "listen(${server}, ${port}, ${host}, (socket) => $effekt.runToplevel((ks, k) => (${listener})(socket, ks, k)))"
|
|
9
|
+
/// A TCP handle. Should not be inspected.
|
|
10
|
+
type Connection = Int
|
|
22
11
|
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
/// Reads data from a TCP connection into a buffer at the given offset.
|
|
13
|
+
def read(handle: Connection, buffer: ByteArray, offset: Int, size: Int): Int / Exception[IOError] =
|
|
14
|
+
internal::checkResult(internal::read(handle, buffer, offset, size))
|
|
25
15
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
extern async def end(socket: JSSocket): Unit =
|
|
30
|
-
jsNode "$effekt.capture(k => ${socket}.end(k))"
|
|
31
|
-
}
|
|
16
|
+
/// Writes data from a buffer at the given offset to a TCP connection.
|
|
17
|
+
def write(handle: Connection, buffer: ByteArray, offset: Int, size: Int): Int / Exception[IOError] =
|
|
18
|
+
internal::checkResult(internal::write(handle, buffer, offset, size))
|
|
32
19
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def end(): Unit
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
def server(host: String, port: Int, handler: () => Unit / Socket at {io, async, global}): Unit = {
|
|
40
|
-
val server = js::server();
|
|
41
|
-
js::listen(server, port, host, box { socket =>
|
|
42
|
-
println("New connection")
|
|
43
|
-
spawn(box {
|
|
44
|
-
try handler()
|
|
45
|
-
with Socket {
|
|
46
|
-
def send(message) =
|
|
47
|
-
resume(js::send(socket, message))
|
|
48
|
-
def receive() =
|
|
49
|
-
resume(js::receive(socket))
|
|
50
|
-
def end() =
|
|
51
|
-
resume(js::end(socket))
|
|
52
|
-
}
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
}
|
|
20
|
+
/// Establishes a TCP connection to the specified host and port.
|
|
21
|
+
def connect(host: String, port: Int): Connection / Exception[IOError] =
|
|
22
|
+
internal::checkResult(internal::connect(host, port))
|
|
56
23
|
|
|
24
|
+
/// Closes a TCP connection and releases associated resources.
|
|
25
|
+
def close(handle: Connection): Unit =
|
|
26
|
+
internal::close(handle)
|
|
57
27
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
val request = do receive().toString;
|
|
28
|
+
/// A TCP listener. Should not be inspected.
|
|
29
|
+
type Listener = Int
|
|
61
30
|
|
|
62
|
-
|
|
31
|
+
/// Creates a TCP listener bound to the specified host and port.
|
|
32
|
+
def bind(host: String, port: Int): Listener / Exception[IOError] =
|
|
33
|
+
internal::checkResult(internal::bind(host, port))
|
|
63
34
|
|
|
64
|
-
|
|
65
|
-
|
|
35
|
+
/// Starts listening for incoming connections and handles them with the provided handler function.
|
|
36
|
+
/// Runs until `shutdown` is called on this `Listener`.
|
|
37
|
+
def listen(listener: Listener, handler: Connection => Unit at {io, async, global}): Unit / Exception[IOError] = {
|
|
38
|
+
internal::checkResult(internal::listen(listener, handler)); ()
|
|
39
|
+
}
|
|
66
40
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
41
|
+
/// Stops a TCP listener and releases associated resources.
|
|
42
|
+
def shutdown(listener: Listener): Unit =
|
|
43
|
+
internal::shutdown(listener)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def streaming[R](connection: Connection) { program: () => R / {read[ByteArray], emit[ByteArray]} }: R / Exception[IOError] =
|
|
47
|
+
try {
|
|
48
|
+
program()
|
|
49
|
+
} with emit[ByteArray] { bytes =>
|
|
50
|
+
write(connection, bytes, 0, bytes.size)
|
|
51
|
+
resume(())
|
|
52
|
+
} with read[ByteArray] { () =>
|
|
53
|
+
val bytes = bytearray::allocate(4096)
|
|
54
|
+
read(connection, bytes, 0, bytes.size) match {
|
|
55
|
+
case r and r == bytes.size => resume { return bytes }
|
|
56
|
+
case r => resume { bytes.resize(r) }
|
|
71
57
|
}
|
|
72
|
-
do end()
|
|
73
58
|
}
|
|
74
59
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
60
|
+
namespace internal {
|
|
61
|
+
|
|
62
|
+
extern llvm """
|
|
63
|
+
declare void @c_tcp_connect(%Pos, %Int, %Stack)
|
|
64
|
+
declare void @c_tcp_read(%Int, %Pos, %Int, %Int, %Stack)
|
|
65
|
+
declare void @c_tcp_write(%Int, %Pos, %Int, %Int, %Stack)
|
|
66
|
+
declare void @c_tcp_close(%Int, %Stack)
|
|
67
|
+
declare void @c_tcp_bind(%Pos, %Int, %Int, %Stack)
|
|
68
|
+
declare void @c_tcp_listen(%Int, %Pos, %Stack)
|
|
69
|
+
declare void @c_tcp_shutdown(%Int, %Stack)
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
extern def connect(host: String, port: Int) at async: Int =
|
|
73
|
+
llvm """
|
|
74
|
+
call void @c_tcp_connect(%Pos ${host}, %Int ${port}, %Stack %stack)
|
|
75
|
+
ret void
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
extern def read(handle: Int, buffer: ByteArray, offset: Int, size: Int) at async: Int =
|
|
79
|
+
llvm """
|
|
80
|
+
call void @c_tcp_read(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
|
|
81
|
+
ret void
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
extern def write(handle: Int, buffer: ByteArray, offset: Int, size: Int) at async: Int =
|
|
85
|
+
llvm """
|
|
86
|
+
call void @c_tcp_write(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
|
|
87
|
+
ret void
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
extern def close(handle: Int) at async: Unit =
|
|
91
|
+
llvm """
|
|
92
|
+
call void @c_tcp_close(%Int ${handle}, %Stack %stack)
|
|
93
|
+
ret void
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
extern def bind(host: String, port: Int) at io: Int =
|
|
97
|
+
llvm """
|
|
98
|
+
%result = call %Int @c_tcp_bind(%Pos ${host}, %Int ${port})
|
|
99
|
+
ret %Int %result
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
extern def listen(listener: Int, handler: Int => Unit at {io, async, global}) at async: Int =
|
|
103
|
+
llvm """
|
|
104
|
+
call void @c_tcp_listen(%Int ${listener}, %Pos ${handler}, %Stack %stack)
|
|
105
|
+
ret void
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
extern def shutdown(handle: Int) at async: Unit =
|
|
109
|
+
llvm """
|
|
110
|
+
call void @c_tcp_shutdown(%Int ${handle}, %Stack %stack)
|
|
111
|
+
ret void
|
|
112
|
+
"""
|
|
79
113
|
|
|
80
|
-
server("localhost", port, box {
|
|
81
|
-
helloWorldApp()
|
|
82
|
-
})
|
|
83
|
-
}
|
|
84
114
|
}
|
|
115
|
+
|
|
116
|
+
|
|
@@ -4,7 +4,7 @@ extern llvm """
|
|
|
4
4
|
declare void @c_timer_start(%Int, %Stack)
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
extern
|
|
7
|
+
extern def wait(millis: Int) at async: Unit =
|
|
8
8
|
js "$effekt.capture(k => setTimeout(() => k($effekt.unit), ${millis}))"
|
|
9
9
|
llvm """
|
|
10
10
|
call void @c_timer_start(%Int ${millis}, %Stack %stack)
|
|
@@ -12,23 +12,22 @@ extern llvm """
|
|
|
12
12
|
declare void @c_yield(%Stack)
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
extern
|
|
15
|
+
extern def spawn(task: Task[Unit]) at async: Unit =
|
|
16
16
|
js "$effekt.capture(k => { setTimeout(() => k($effekt.unit), 0); return $effekt.run(${task}) })"
|
|
17
17
|
llvm """
|
|
18
18
|
call void @c_yield(%Stack %stack)
|
|
19
|
-
|
|
20
|
-
call void @run(%Neg %unboxed)
|
|
19
|
+
call void @run(%Pos ${task})
|
|
21
20
|
ret void
|
|
22
21
|
"""
|
|
23
22
|
|
|
24
|
-
extern
|
|
23
|
+
extern def yield() at async: Unit =
|
|
25
24
|
js "$effekt.capture(k => setTimeout(() => k($effekt.unit), 0))"
|
|
26
25
|
llvm """
|
|
27
26
|
call void @c_yield(%Stack %stack)
|
|
28
27
|
ret void
|
|
29
28
|
"""
|
|
30
29
|
|
|
31
|
-
extern
|
|
30
|
+
extern def abort() at async: Nothing =
|
|
32
31
|
js "$effekt.capture(k => undefined)"
|
|
33
32
|
llvm """
|
|
34
33
|
call void @eraseStack(%Stack %stack)
|
|
@@ -55,14 +54,14 @@ extern llvm """
|
|
|
55
54
|
declare void @c_promise_await(%Pos, %Neg)
|
|
56
55
|
"""
|
|
57
56
|
|
|
58
|
-
extern
|
|
57
|
+
extern def await[T](promise: Promise[T]) at async: T =
|
|
59
58
|
js "$effekt.capture(k => ${promise}.promise.then(k))"
|
|
60
59
|
llvm """
|
|
61
60
|
call void @c_promise_await(%Pos ${promise}, %Stack %stack)
|
|
62
61
|
ret void
|
|
63
62
|
"""
|
|
64
63
|
|
|
65
|
-
extern
|
|
64
|
+
extern def resolve[T](promise: Promise[T], value: T) at async: Unit =
|
|
66
65
|
js "$effekt.capture(k => { ${promise}.resolve(${value}); return k($effekt.unit) })"
|
|
67
66
|
llvm """
|
|
68
67
|
call void @c_promise_resolve(%Pos ${promise}, %Pos ${value}, %Stack %stack)
|
|
@@ -80,7 +79,7 @@ namespace promise {
|
|
|
80
79
|
}
|
|
81
80
|
"""
|
|
82
81
|
|
|
83
|
-
extern
|
|
82
|
+
extern def make[T]() at io: Promise[T] =
|
|
84
83
|
js "promise$make()"
|
|
85
84
|
llvm """
|
|
86
85
|
%promise = call %Pos @c_promise_make()
|
|
@@ -165,7 +165,7 @@ def decodeJson(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
|
|
|
165
165
|
case 'f' => expectString("false"); do bool(false)
|
|
166
166
|
case '-' => do number(readDouble())
|
|
167
167
|
case d and d.isDigit => do number(readDouble())
|
|
168
|
-
case '"' => do string(
|
|
168
|
+
case '"' => do string(collect { readQuotedString() })
|
|
169
169
|
case '{' => do dict{ decodeJsonObject() }
|
|
170
170
|
case '[' => do list{ decodeJsonList() }
|
|
171
171
|
case _ => println("Unexpected " ++ c.toString); <>
|
|
@@ -179,7 +179,7 @@ def decodeJsonObject(): Unit / {Scan[Char], JsonObjectBuilder, Exception[WrongFo
|
|
|
179
179
|
skipWhitespace()
|
|
180
180
|
while(do peek[Char]() is c and (c != '}')) {
|
|
181
181
|
if (not(first)) { expectString(",") }
|
|
182
|
-
val k
|
|
182
|
+
val k = string::collect { readQuotedString() }
|
|
183
183
|
skipWhitespace()
|
|
184
184
|
expectString(":")
|
|
185
185
|
do field(k){
|
|
@@ -277,7 +277,7 @@ def build[R](){ body: => R / JsonBuilder }: (R, JsonValue) = {
|
|
|
277
277
|
}
|
|
278
278
|
(x, r)
|
|
279
279
|
}
|
|
280
|
-
def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = returning::
|
|
280
|
+
def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = list::returning::collect[JsonValue, R] {
|
|
281
281
|
try body() with JsonBuilder {
|
|
282
282
|
def number(n) = { do emit(Number(n)); resume(()) }
|
|
283
283
|
def bool(b) = { do emit(Bool(b)); resume(()) }
|
|
@@ -295,7 +295,7 @@ def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = returning
|
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
|
-
def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = returning::
|
|
298
|
+
def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = list::returning::collect[(String, JsonValue), R] {
|
|
299
299
|
try body() with JsonObjectBuilder {
|
|
300
300
|
def field(k) = resume { {v} =>
|
|
301
301
|
val x = build{v}
|
|
@@ -315,7 +315,7 @@ namespace test {
|
|
|
315
315
|
// Read quoted string
|
|
316
316
|
feed("\"\ta\n\ra\"") {
|
|
317
317
|
with scanner[Char]
|
|
318
|
-
println(
|
|
318
|
+
println(string::collect { readQuotedString() })
|
|
319
319
|
}
|
|
320
320
|
|
|
321
321
|
println("")
|
|
@@ -3,6 +3,7 @@ module list
|
|
|
3
3
|
import effekt
|
|
4
4
|
import option
|
|
5
5
|
import exception
|
|
6
|
+
import stream
|
|
6
7
|
|
|
7
8
|
/// Immutable linked list for finite sequences of elements.
|
|
8
9
|
type List[A] {
|
|
@@ -167,9 +168,52 @@ def foreachIndex[A](list: List[A]){ f: (Int, A) {Control} => Unit }: Unit = {
|
|
|
167
168
|
/// Map a function `f` over elements in a given list.
|
|
168
169
|
///
|
|
169
170
|
/// O(N)
|
|
170
|
-
def map[A, B](l: List[A]) { f: A => B }
|
|
171
|
-
var acc = Nil
|
|
172
|
-
l.foreach { el =>
|
|
171
|
+
def map[A, B](l: List[A]) { f: A => B }: List[B] = {
|
|
172
|
+
var acc: List[B] = Nil()
|
|
173
|
+
l.foreach { el =>
|
|
174
|
+
acc = Cons(f(el), acc)
|
|
175
|
+
}
|
|
176
|
+
acc.reverse
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/// Map a function `f` over elements and their indices in a given list.
|
|
180
|
+
///
|
|
181
|
+
/// O(N)
|
|
182
|
+
def mapWithIndex[A, B](l: List[A]) { f: (Int, A) => B }: List[B] = {
|
|
183
|
+
var acc: List[B] = Nil()
|
|
184
|
+
l.foreachIndex { (i, el) =>
|
|
185
|
+
acc = Cons(f(i, el), acc)
|
|
186
|
+
}
|
|
187
|
+
acc.reverse
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/// Map a function `f` over elements in a given list,
|
|
191
|
+
/// keeping only the elements for which the function returned `true`,
|
|
192
|
+
/// discarding the elements for which the function returned `false`.
|
|
193
|
+
///
|
|
194
|
+
/// O(N)
|
|
195
|
+
def filter[A](l: List[A]) { shouldKeep: A => Bool }: List[A] = {
|
|
196
|
+
var acc: List[A] = Nil()
|
|
197
|
+
l.foreach { a =>
|
|
198
|
+
if (shouldKeep(a)) {
|
|
199
|
+
acc = Cons(a, acc)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
acc.reverse
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/// Map a function `f` over elements and their indices in a given list,
|
|
206
|
+
/// keeping only the elements for which the function returned `true`,
|
|
207
|
+
/// discarding the elements for which the function returned `false`.
|
|
208
|
+
///
|
|
209
|
+
/// O(N)
|
|
210
|
+
def filterWithIndex[A](l: List[A]) { shouldKeep: (Int, A) => Bool }: List[A] = {
|
|
211
|
+
var acc: List[A] = Nil()
|
|
212
|
+
l.foreachIndex { (i, a) =>
|
|
213
|
+
if (shouldKeep(i, a)) {
|
|
214
|
+
acc = Cons(a, acc)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
173
217
|
acc.reverse
|
|
174
218
|
}
|
|
175
219
|
|
|
@@ -178,13 +222,28 @@ def map[A, B](l: List[A]) { f: A => B } : List[B] = {
|
|
|
178
222
|
/// discarding the elements for which the function returned `None()`.
|
|
179
223
|
///
|
|
180
224
|
/// O(N)
|
|
181
|
-
def
|
|
182
|
-
var acc = Nil
|
|
225
|
+
def collectSome[A, B](l: List[A]) { f : A => Option[B] }: List[B] = {
|
|
226
|
+
var acc: List[B] = Nil()
|
|
183
227
|
l.foreach { a =>
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
case
|
|
187
|
-
|
|
228
|
+
f(a) match {
|
|
229
|
+
case None() => ()
|
|
230
|
+
case Some(b) => acc = Cons(b, acc)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
acc.reverse
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/// Map a function `f` over elements and their indices in a given list,
|
|
237
|
+
/// keeping only the elements for which the function returned `Some(...)`,
|
|
238
|
+
/// discarding the elements for which the function returned `None()`.
|
|
239
|
+
///
|
|
240
|
+
/// O(N)
|
|
241
|
+
def collectWithIndex[A, B](l: List[A]) { f: (Int, A) => Option[B] }: List[B] = {
|
|
242
|
+
var acc: List[B] = Nil()
|
|
243
|
+
l.foreachIndex { (i, a) =>
|
|
244
|
+
f(i, a) match {
|
|
245
|
+
case None() => ()
|
|
246
|
+
case Some(b) => acc = Cons(b, acc)
|
|
188
247
|
}
|
|
189
248
|
}
|
|
190
249
|
acc.reverse
|
|
@@ -205,21 +264,55 @@ def flatMap[A, B](l: List[A]) { f : A => List[B] }: List[B] = {
|
|
|
205
264
|
/// Check if predicate is true for all elements of the given list.
|
|
206
265
|
///
|
|
207
266
|
/// O(N)
|
|
208
|
-
def all[A](list: List[A]) { predicate: A => Bool }: Bool =
|
|
267
|
+
def all[A](list: List[A]) { predicate: A => Bool }: Bool =
|
|
209
268
|
list match {
|
|
210
269
|
case Cons(x, xs) => predicate(x) && all(xs) { predicate }
|
|
211
270
|
case Nil() => true
|
|
212
271
|
}
|
|
213
|
-
}
|
|
214
272
|
|
|
215
273
|
/// Check if predicate is true for at least one element of the given list.
|
|
216
274
|
///
|
|
217
275
|
/// O(N)
|
|
218
|
-
def any[A](list: List[A]) { predicate: A => Bool }: Bool =
|
|
276
|
+
def any[A](list: List[A]) { predicate: A => Bool }: Bool =
|
|
219
277
|
list match {
|
|
220
278
|
case Cons(x, xs) => predicate(x) || any(xs) { predicate }
|
|
221
279
|
case Nil() => false
|
|
222
280
|
}
|
|
281
|
+
|
|
282
|
+
/// Check if the list contains the given `element`,
|
|
283
|
+
/// comparing for equality with the given `eq` function.
|
|
284
|
+
///
|
|
285
|
+
/// O(N)
|
|
286
|
+
def contains[A](list: List[A], element: A) { eq: (A, A) => Bool }: Bool =
|
|
287
|
+
list.any { x => x.eq(element) }
|
|
288
|
+
|
|
289
|
+
/// Find the first index of the given `element`,
|
|
290
|
+
/// comparing for equality with the given `eq` function.
|
|
291
|
+
///
|
|
292
|
+
/// O(N)
|
|
293
|
+
def indexOf[A](list: List[A], element: A) { eq: (A, A) => Bool }: Option[Int] = {
|
|
294
|
+
var result = None()
|
|
295
|
+
list.foreachIndex { (i, a) {label} =>
|
|
296
|
+
if (element.eq(a)) {
|
|
297
|
+
result = Some(i)
|
|
298
|
+
label.break()
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
result
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/// Find the last index of the given `element`,
|
|
305
|
+
/// comparing for equality with the given `eq` function.
|
|
306
|
+
///
|
|
307
|
+
/// O(N)
|
|
308
|
+
def lastIndexOf[A](list: List[A], element: A) { eq: (A, A) => Bool }: Option[Int] = {
|
|
309
|
+
var result = None()
|
|
310
|
+
list.foreachIndex { (i, a) {label} =>
|
|
311
|
+
if (element.eq(a)) {
|
|
312
|
+
result = Some(i)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
result
|
|
223
316
|
}
|
|
224
317
|
|
|
225
318
|
/// Count how many elements of a list satisfy the given predicate.
|
|
@@ -297,10 +390,11 @@ def reverse[A](l: List[A]): List[A] = {
|
|
|
297
390
|
/// ```
|
|
298
391
|
///
|
|
299
392
|
/// O(|l|)
|
|
300
|
-
def reverseOnto[A](l: List[A], other: List[A]): List[A] =
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
393
|
+
def reverseOnto[A](l: List[A], other: List[A]): List[A] =
|
|
394
|
+
l match {
|
|
395
|
+
case Nil() => other
|
|
396
|
+
case Cons(a, rest) => rest.reverseOnto(Cons(a, other))
|
|
397
|
+
}
|
|
304
398
|
|
|
305
399
|
/// Concatenate list `l` with list `other`:
|
|
306
400
|
///
|
|
@@ -347,7 +441,7 @@ def join[A](lists: List[List[A]]): List[A] = {
|
|
|
347
441
|
/// ```
|
|
348
442
|
///
|
|
349
443
|
/// O(N)
|
|
350
|
-
def join[A](lists: List[List[A]], between: List[A]): List[A] =
|
|
444
|
+
def join[A](lists: List[List[A]], between: List[A]): List[A] =
|
|
351
445
|
lists match {
|
|
352
446
|
case Nil() => Nil()
|
|
353
447
|
case Cons(firstList, restOfLists) =>
|
|
@@ -355,7 +449,6 @@ def join[A](lists: List[List[A]], between: List[A]): List[A] = {
|
|
|
355
449
|
restOfLists.flatMap { list => between.append(list)}
|
|
356
450
|
)
|
|
357
451
|
}
|
|
358
|
-
}
|
|
359
452
|
|
|
360
453
|
/// Take the first `n` elements of a given list.
|
|
361
454
|
///
|
|
@@ -836,3 +929,46 @@ def println(l: List[Int]): Unit = println(show(l))
|
|
|
836
929
|
def println(l: List[Double]): Unit = println(show(l))
|
|
837
930
|
def println(l: List[Bool]): Unit = println(show(l))
|
|
838
931
|
def println(l: List[String]): Unit = println(show(l))
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
// Streaming
|
|
935
|
+
// ---------
|
|
936
|
+
|
|
937
|
+
/// Turns a `list` into a producer of a push stream
|
|
938
|
+
/// by emitting each contained value left-to-right.
|
|
939
|
+
def each[A](list: List[A]): Unit / emit[A] =
|
|
940
|
+
list match {
|
|
941
|
+
case Nil() => ()
|
|
942
|
+
case Cons(head, tail) =>
|
|
943
|
+
do emit(head)
|
|
944
|
+
each(tail)
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
|
|
948
|
+
var l = list
|
|
949
|
+
try {
|
|
950
|
+
reader()
|
|
951
|
+
} with read[T] {
|
|
952
|
+
resume {
|
|
953
|
+
l match {
|
|
954
|
+
case Nil() => do stop()
|
|
955
|
+
case Cons(value, rest) =>
|
|
956
|
+
l = rest
|
|
957
|
+
return value
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
def collect[A] { stream: () => Unit / emit[A] }: List[A] =
|
|
964
|
+
returning::collect[A, Unit]{stream}.second
|
|
965
|
+
|
|
966
|
+
namespace returning {
|
|
967
|
+
def collect[A, R] { stream: () => R / emit[A] }: (R, List[A]) =
|
|
968
|
+
try {
|
|
969
|
+
(stream(), Nil())
|
|
970
|
+
} with emit[A] { (v) =>
|
|
971
|
+
val (r, vs) = resume(());
|
|
972
|
+
(r, Cons(v, vs))
|
|
973
|
+
}
|
|
974
|
+
}
|