@effekt-lang/effekt 0.2.2 → 0.4.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.
Files changed (95) hide show
  1. package/README.md +6 -18
  2. package/bin/effekt +0 -0
  3. package/bin/effekt.sh +1 -1
  4. package/libraries/chez/common/effekt_primitives.ss +10 -2
  5. package/libraries/chez/common/immutable/cslist.effekt +13 -7
  6. package/libraries/chez/common/mutable/dict.effekt +8 -10
  7. package/libraries/chez/common/text/regex.effekt +7 -9
  8. package/libraries/common/args.effekt +68 -0
  9. package/libraries/common/array.effekt +220 -0
  10. package/libraries/common/bench.effekt +86 -0
  11. package/libraries/common/buffer.effekt +123 -0
  12. package/libraries/common/bytes.effekt +125 -0
  13. package/libraries/{js/immutable → common}/dequeue.effekt +18 -4
  14. package/libraries/common/effekt.effekt +725 -0
  15. package/libraries/common/exception.effekt +121 -0
  16. package/libraries/common/io/console.effekt +62 -0
  17. package/libraries/common/io/error.effekt +474 -0
  18. package/libraries/common/io/filesystem.effekt +274 -0
  19. package/libraries/common/io/network.effekt +81 -0
  20. package/libraries/common/io/time.effekt +12 -0
  21. package/libraries/common/io.effekt +88 -0
  22. package/libraries/{llvm/immutable → common}/list.effekt +101 -74
  23. package/libraries/common/option.effekt +72 -0
  24. package/libraries/common/process.effekt +9 -0
  25. package/libraries/common/queue.effekt +140 -0
  26. package/libraries/common/ref.effekt +60 -0
  27. package/libraries/common/result.effekt +45 -0
  28. package/libraries/{ml/immutable → common}/seq.effekt +30 -19
  29. package/libraries/common/string/tty.effekt +66 -0
  30. package/libraries/common/string.effekt +361 -0
  31. package/libraries/common/test.effekt +160 -0
  32. package/libraries/js/effekt_builtins.js +37 -6
  33. package/libraries/js/mutable/map.effekt +8 -9
  34. package/libraries/js/text/regex.effekt +7 -8
  35. package/libraries/js/unsafe/cont.effekt +2 -2
  36. package/libraries/js/web/dom.effekt +13 -13
  37. package/libraries/llvm/array.c +53 -0
  38. package/libraries/llvm/buffer.c +181 -76
  39. package/libraries/llvm/forward-declare-c.ll +27 -8
  40. package/libraries/llvm/io.c +423 -1
  41. package/libraries/llvm/main.c +6 -1
  42. package/libraries/llvm/ref.c +45 -0
  43. package/libraries/llvm/rts.ll +515 -354
  44. package/libraries/llvm/types.c +32 -0
  45. package/licenses/eclipse public license, version 2.0 - epl-2.0.html +4 -4
  46. package/licenses/mit license - mit-license.html +532 -62
  47. package/licenses/the bsd license - bsd-license.html +535 -60
  48. package/licenses/the mit license - mit.html +532 -62
  49. package/package.json +2 -2
  50. package/libraries/chez/callcc/effekt.effekt +0 -221
  51. package/libraries/chez/common/effekt_matching.ss +0 -100
  52. package/libraries/chez/common/immutable/dequeue.effekt +0 -78
  53. package/libraries/chez/common/immutable/list.effekt +0 -643
  54. package/libraries/chez/common/immutable/option.effekt +0 -39
  55. package/libraries/chez/common/immutable/result.effekt +0 -29
  56. package/libraries/chez/common/io/args.effekt +0 -20
  57. package/libraries/chez/common/mutable/array.effekt +0 -27
  58. package/libraries/chez/common/mutable/heap.effekt +0 -12
  59. package/libraries/chez/common/test.effekt +0 -59
  60. package/libraries/chez/common/text/string.effekt +0 -46
  61. package/libraries/chez/lift/effekt.effekt +0 -217
  62. package/libraries/chez/lift/effekt.ss +0 -118
  63. package/libraries/chez/monadic/effekt.effekt +0 -218
  64. package/libraries/js/effekt.effekt +0 -284
  65. package/libraries/js/effekt_matching.js +0 -79
  66. package/libraries/js/immutable/list.effekt +0 -643
  67. package/libraries/js/immutable/option.effekt +0 -49
  68. package/libraries/js/immutable/result.effekt +0 -29
  69. package/libraries/js/io/args.effekt +0 -9
  70. package/libraries/js/io/async.effekt +0 -28
  71. package/libraries/js/io/async.js +0 -2
  72. package/libraries/js/io/file.effekt +0 -50
  73. package/libraries/js/io/file_include.js +0 -13
  74. package/libraries/js/mutable/array.effekt +0 -98
  75. package/libraries/js/mutable/heap.effekt +0 -32
  76. package/libraries/js/test.effekt +0 -59
  77. package/libraries/js/text/string.effekt +0 -64
  78. package/libraries/llvm/effekt.effekt +0 -243
  79. package/libraries/llvm/immutable/option.effekt +0 -53
  80. package/libraries/llvm/immutable/result.effekt +0 -29
  81. package/libraries/llvm/io/args.effekt +0 -24
  82. package/libraries/llvm/text/string.effekt +0 -179
  83. package/libraries/ml/effekt.effekt +0 -267
  84. package/libraries/ml/effekt.sml +0 -63
  85. package/libraries/ml/immutable/dequeue.effekt +0 -81
  86. package/libraries/ml/immutable/list.effekt +0 -674
  87. package/libraries/ml/immutable/option.effekt +0 -53
  88. package/libraries/ml/immutable/result.effekt +0 -29
  89. package/libraries/ml/internal/option.effekt +0 -18
  90. package/libraries/ml/io/args.effekt +0 -41
  91. package/libraries/ml/mutable/array.effekt +0 -43
  92. package/libraries/ml/random.effekt +0 -85
  93. package/libraries/ml/test.effekt +0 -60
  94. package/libraries/ml/text/regex.effekt +0 -107
  95. package/libraries/ml/text/string.effekt +0 -81
@@ -0,0 +1,274 @@
1
+ module io/filesystem
2
+
3
+ import bytes
4
+
5
+ import io
6
+ import io/error
7
+
8
+
9
+ /**
10
+ * Represents the file opening modes with their respective flags
11
+ * Backends rely on the order of these
12
+ */
13
+ type Mode {
14
+ // Most common modes
15
+ ReadOnly() // 'r' exception if does not exist
16
+ WriteOnly() // 'w' created if does not exist, truncated if exists
17
+ AppendOnly() // 'a' created if does not exist
18
+
19
+ // Other modes
20
+ ReadWrite() // 'w+' created if does not exist, truncated if exists
21
+ ReadAppend() // 'a+' created if does not exist
22
+ AppendExclusive() // 'ax' created if does not exist, fails if exists
23
+ ReadAppendExclusive() // 'ax+' created if does not exist, fails if exists
24
+ AppendSync() // 'as' created if does not exist, append in synchronous mode
25
+ ReadAppendSync() // 'as+' created if does not exist, append in synchronous mode
26
+ ReadSync() // 'rs' exception if does not exist, read in synchronous mode
27
+ ReadWriteSync() // 'rs+' exception if does not exist, read/write in synchronous mode
28
+ WriteExclusive() // 'wx' created if does not exist, truncated if exists, fails if exists
29
+ ReadWriteExclusive() // 'wx+' created if does not exist, truncated if exists, fails if exists
30
+ }
31
+
32
+
33
+ /**
34
+ * A file descriptor. Should not be inspected.
35
+ */
36
+ type File = Int
37
+
38
+
39
+ /**
40
+ * Reads a file at given path as utf8 encoded string.
41
+ */
42
+ def readFile(path: String): String / Exception[IOError] = {
43
+ val fd = open(path, ReadOnly());
44
+ with on[IOError].finalize { close(fd) }
45
+
46
+ val readSize = 1048576 // 1MB
47
+ var size = readSize
48
+ var buffer = bytes(size)
49
+ var offset = 0;
50
+
51
+ def go(): String = {
52
+ read(fd, buffer.slice(offset, min(readSize, size - offset)), -1) match {
53
+ case 0 =>
54
+ buffer.slice(0, offset).toUTF8
55
+ case n and n < 0 => panic("Error!")
56
+ case n =>
57
+ offset = offset + n
58
+
59
+ // we are at maximum capacity
60
+ if (n == readSize && (offset + readSize) > size) {
61
+ val newSize = size * 2
62
+ val newBuffer = bytes(newSize)
63
+ copy(buffer, newBuffer, 0, 0, size)
64
+ buffer = newBuffer
65
+ size = newSize
66
+ }
67
+ go()
68
+ }
69
+ }
70
+
71
+ go()
72
+ }
73
+
74
+ /**
75
+ * Writes the (utf8 encoded) string `contents` into the specified file.
76
+ */
77
+ def writeFile(path: String, contents: String): Unit / Exception[IOError] = {
78
+ val fd = open(path, WriteOnly());
79
+ with on[IOError].finalize { close(fd) }
80
+
81
+ val writeSize = 1048576 // 1MB
82
+
83
+ // this induces a memcpy that is not strictly necessary, since we use the buffer read-only
84
+ val buffer = contents.fromUTF8
85
+ val size = buffer.size
86
+
87
+ var offset = 0;
88
+ def remaining() = size - offset
89
+
90
+ def go(): Unit =
91
+ write(fd, buffer.slice(offset, min(writeSize, remaining())), -1) match {
92
+ case 0 => ()
93
+ case n and n < 0 => panic("Error!")
94
+ case n =>
95
+ offset = offset + n;
96
+ if (remaining() > 0) go()
97
+ }
98
+
99
+ go()
100
+ }
101
+
102
+
103
+ /**
104
+ * An abstract interface applications can program against.
105
+ *
106
+ * Can be interpreted with the `filesystem` handler, or virtualized etc.
107
+ *
108
+ * See example below.
109
+ */
110
+ interface Files {
111
+ def readFile(path: String): String
112
+ def writeFile(path: String, contents: String): Unit
113
+ }
114
+
115
+ /**
116
+ * Using the file system via node.js
117
+ */
118
+ def filesystem[R] { program: => R / Files }: R / Exception[IOError] = // TODO move Exception to be bidirectional
119
+ try { program() }
120
+ with Files {
121
+ def readFile(path) = resume(readFile(path))
122
+ def writeFile(path, contents) = resume(writeFile(path, contents))
123
+ }
124
+
125
+
126
+ def open(path: String, mode: Mode): File / Exception[IOError] =
127
+ internal::checkResult(internal::open(path, mode))
128
+
129
+ def read(fd: File, buffer: Bytes, offset: Int): Int / Exception[IOError] =
130
+ internal::checkResult(internal::read(fd, buffer, offset))
131
+
132
+ def write(fd: File, buffer: Bytes, offset: Int): Int / Exception[IOError] =
133
+ internal::checkResult(internal::write(fd, buffer, offset))
134
+
135
+ def close(fd: File): Unit / Exception[IOError] = {
136
+ internal::checkResult(internal::close(fd)); ()
137
+ }
138
+
139
+ namespace internal {
140
+
141
+ extern js """
142
+ function modeName(mode) {
143
+ switch (mode.__tag) {
144
+ case 0: // ReadOnly()
145
+ return 'r';
146
+ case 1: // WriteOnly()
147
+ return 'w';
148
+ case 2: // AppendOnly()
149
+ return 'a';
150
+ case 3: // ReadWrite()
151
+ return 'w+';
152
+ case 4: // ReadAppend()
153
+ return 'a+';
154
+ case 5: // AppendExclusive()
155
+ return 'ax';
156
+ case 6: // ReadAppendExclusive()
157
+ return 'ax+';
158
+ case 7: // AppendSync()
159
+ return 'as';
160
+ case 8: // ReadAppendSync()
161
+ return 'as+';
162
+ case 9: // ReadSync()
163
+ return 'rs';
164
+ case 10: // ReadWriteSync()
165
+ return 'rs+';
166
+ case 11: // WriteExclusive()
167
+ return 'wx';
168
+ case 12: // ReadWriteExclusive()
169
+ return 'wx+';
170
+ default:
171
+ // Invalid tag value
172
+ return null;
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Nodejs file operations expect buffers, but we represent buffers as typed arrays.
178
+ * This function converts between the two without copying.
179
+ */
180
+ function toBuffer(buffer) {
181
+ return Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength)
182
+ }
183
+ """
184
+
185
+ extern jsNode """
186
+ const fs = require("fs");
187
+
188
+ function open(path, mode, callback) {
189
+ fs.open(path, modeName(mode), (err, fd) => {
190
+ if (err) { callback(err.errno) } else { callback(fd) }
191
+ })
192
+ }
193
+
194
+ function read(fd, buffer, offset, callback) {
195
+ let position = offset === -1 ? null : offset;
196
+ fs.read(fd, toBuffer(buffer), 0, buffer.length, position, (err, bytesRead) => {
197
+ if (err) { callback(err.errno) } else { callback(bytesRead) }
198
+ })
199
+ }
200
+
201
+ function write(fd, buffer, offset, callback) {
202
+ let position = offset === -1 ? null : offset;
203
+ fs.write(fd, toBuffer(buffer), 0, buffer.length, position, (err, bytesWritten) => {
204
+ if (err) { callback(err.errno) } else { callback(bytesWritten) }
205
+ })
206
+ }
207
+
208
+ function close(fd, callback) {
209
+ fs.close(fd, (err) => {
210
+ if (err) { callback(err.errno) } else { callback(0) }
211
+ })
212
+ }
213
+ """
214
+
215
+ extern llvm """
216
+ declare void @c_fs_open(%Pos, %Pos, %Stack)
217
+ declare void @c_fs_read(%Int, %Pos, %Int, %Stack)
218
+ declare void @c_fs_write(%Int, %Pos, %Int, %Stack)
219
+ declare void @c_fs_close(%Int, %Stack)
220
+ """
221
+
222
+ extern async def open(path: String, mode: Mode): Int =
223
+ jsNode "$effekt.callcc(callback => open(${path}, ${mode}, callback))"
224
+ llvm """
225
+ call void @c_fs_open(%Pos ${path}, %Pos ${mode}, %Stack %stack)
226
+ ret void
227
+ """
228
+
229
+ extern async def read(fd: Int, buffer: Bytes, offset: Int): Int =
230
+ jsNode "$effekt.callcc(callback => read(${fd}, ${buffer}, ${offset}, callback))"
231
+ llvm """
232
+ call void @c_fs_read(%Int ${fd}, %Pos ${buffer}, %Int ${offset}, %Stack %stack)
233
+ ret void
234
+ """
235
+
236
+ extern async def write(fd: Int, buffer: Bytes, offset: Int): Int =
237
+ jsNode "$effekt.callcc(callback => write(${fd}, ${buffer}, ${offset}, callback))"
238
+ llvm """
239
+ call void @c_fs_write(%Int ${fd}, %Pos ${buffer}, %Int ${offset}, %Stack %stack)
240
+ ret void
241
+ """
242
+
243
+ extern async def close(fd: Int): Int =
244
+ jsNode "$effekt.callcc(callback => close(${fd}, callback))"
245
+ llvm """
246
+ call void @c_fs_close(%Int ${fd}, %Stack %stack)
247
+ ret void
248
+ """
249
+
250
+ def checkResult(result: Int): Int / Exception[IOError] =
251
+ if (result < 0) {
252
+ val ioError = fromNumber(internal::errorNumber(result));
253
+ do raise[IOError](ioError, message(ioError))
254
+ } else {
255
+ result
256
+ }
257
+ }
258
+
259
+ namespace examples {
260
+
261
+ // Example illustrating using abstract files interface
262
+ // and interpreting it into asynchronous IO
263
+ def copy(from: String, to: String): Unit / Files = {
264
+ val contents = do readFile(from)
265
+ do writeFile(to, contents)
266
+ }
267
+
268
+ def main() = {
269
+ with on[IOError].panic;
270
+ with filesystem;
271
+
272
+ copy("README.md", "out/test.md")
273
+ }
274
+ }
@@ -0,0 +1,81 @@
1
+ module io/network
2
+
3
+ import bytes
4
+ import io
5
+
6
+ namespace js {
7
+ extern jsNode """
8
+ const net = require('node:net');
9
+
10
+ function listen(server, port, host, listener) {
11
+ server.listen(port, host);
12
+ server.on('connection', listener);
13
+ }
14
+ """
15
+
16
+ extern type JSServer // = net.Server
17
+ extern type JSSocket // = net.Socket
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) => (${listener})(socket).run())"
22
+
23
+ extern async def send(socket: JSSocket, data: Bytes): Unit =
24
+ jsNode "$effekt.callcc(callback => ${socket}.write(${data}, callback))"
25
+
26
+ extern async def receive(socket: JSSocket): Bytes =
27
+ jsNode "$effekt.callcc(callback => ${socket}.once('data', callback))"
28
+
29
+ extern async def end(socket: JSSocket): Unit =
30
+ jsNode "$effekt.callcc(callback => ${socket}.end(callback))"
31
+ }
32
+
33
+ interface Socket {
34
+ def send(message: Bytes): Unit
35
+ def receive(): Bytes
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
+ }
56
+
57
+
58
+ namespace examples {
59
+ def helloWorldApp(): Unit / Socket = {
60
+ val request = do receive();
61
+
62
+ println("Received a request: " ++ request.toUTF8)
63
+
64
+ if (request.toUTF8.startsWith("GET /")) {
65
+ do send(fromUTF8("HTTP/1.1 200 OK\r\n\r\nHello from Effekt!"))
66
+ } else {
67
+ do send("HTTP/1.1 400 Bad Request\r\n\r\n".fromUTF8)
68
+ }
69
+ do end()
70
+ }
71
+
72
+ // A server that just shows "Hello from Effekt!" on localhost:8080
73
+ def main() = {
74
+ val port = 8080
75
+ println("Starting server on http://localhost:" ++ port.show)
76
+
77
+ server("localhost", port, box {
78
+ helloWorldApp()
79
+ })
80
+ }
81
+ }
@@ -0,0 +1,12 @@
1
+ module io/time
2
+
3
+ extern llvm """
4
+ declare void @c_timer_start(%Int, %Stack)
5
+ """
6
+
7
+ extern async def wait(millis: Int): Unit =
8
+ js "$effekt.callcc(callback => setTimeout(() => callback($effekt.unit), ${millis}))"
9
+ llvm """
10
+ call void @c_timer_start(%Int ${millis}, %Stack %stack)
11
+ ret void
12
+ """
@@ -0,0 +1,88 @@
1
+ module io
2
+
3
+ import ref
4
+ import queue
5
+
6
+ // Event Loop
7
+ // ----------
8
+
9
+ type Task[T] = () => T at {io, async, global}
10
+
11
+ extern llvm """
12
+ declare void @c_yield(%Stack)
13
+ """
14
+
15
+ extern async def spawn(task: Task[Unit]): Unit =
16
+ js "$effekt.callcc(callback => { setTimeout(() => callback($effekt.unit), 0); ${task}().run()})"
17
+ llvm """
18
+ call void @c_yield(%Stack %stack)
19
+ call void @run(%Neg ${task})
20
+ ret void
21
+ """
22
+
23
+ extern async def yield(): Unit =
24
+ js "$effekt.callcc(callback => setTimeout(() => callback($effekt.unit), 0))"
25
+ llvm """
26
+ call void @c_yield(%Stack %stack)
27
+ ret void
28
+ """
29
+
30
+ extern async def abort(): Nothing =
31
+ js "$effekt.callcc(callback => { return })"
32
+ llvm """
33
+ call void @eraseStack(%Stack %stack)
34
+ ret void
35
+ """
36
+
37
+
38
+ // Promises
39
+ // --------
40
+
41
+ extern type Promise[T]
42
+ // = js "{resolve: ƒ, promise: Promise}"
43
+ // = llvm "{tag: 0, obj: Promise*}"
44
+
45
+ def promise[T](task: Task[T]): Promise[T] = {
46
+ val p = promise::make[T]();
47
+ spawn(box { p.resolve(task()) });
48
+ return p
49
+ }
50
+
51
+ extern llvm """
52
+ declare %Pos @c_promise_make()
53
+ declare void @c_promise_resolve(%Pos, %Pos)
54
+ declare void @c_promise_await(%Pos, %Neg)
55
+ """
56
+
57
+ extern async def await[T](promise: Promise[T]): T =
58
+ js "$effekt.callcc(callback => ${promise}.promise.then(res => callback(res)))"
59
+ llvm """
60
+ call void @c_promise_await(%Pos ${promise}, %Stack %stack)
61
+ ret void
62
+ """
63
+
64
+ extern async def resolve[T](promise: Promise[T], value: T): Unit =
65
+ js "$effekt.callcc(callback => { ${promise}.resolve(${value}); callback($effekt.unit)})"
66
+ llvm """
67
+ call void @c_promise_resolve(%Pos ${promise}, %Pos ${value}, %Stack %stack)
68
+ ret void
69
+ """
70
+
71
+ namespace promise {
72
+ extern js """
73
+ function promise$make() {
74
+ let resolve;
75
+ const promise = new Promise((res, rej) => {
76
+ resolve = res;
77
+ });
78
+ return { resolve: resolve, promise: promise };
79
+ }
80
+ """
81
+
82
+ extern io def make[T](): Promise[T] =
83
+ js "promise$make()"
84
+ llvm """
85
+ %promise = call %Pos @c_promise_make()
86
+ ret %Pos %promise
87
+ """
88
+ }