@effekt-lang/effekt 0.3.0 → 0.5.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 (44) hide show
  1. package/README.md +6 -18
  2. package/bin/effekt +0 -0
  3. package/libraries/common/args.effekt +0 -27
  4. package/libraries/common/array.effekt +34 -63
  5. package/libraries/common/bench.effekt +40 -22
  6. package/libraries/common/buffer.effekt +8 -20
  7. package/libraries/common/bytes.effekt +5 -15
  8. package/libraries/common/dequeue.effekt +5 -5
  9. package/libraries/common/effekt.effekt +51 -92
  10. package/libraries/common/exception.effekt +0 -1
  11. package/libraries/common/io/console.effekt +30 -36
  12. package/libraries/common/io/error.effekt +131 -30
  13. package/libraries/common/io/filesystem.effekt +262 -0
  14. package/libraries/common/io/network.effekt +30 -54
  15. package/libraries/common/io/time.effekt +9 -12
  16. package/libraries/common/io.effekt +54 -124
  17. package/libraries/common/list.effekt +345 -327
  18. package/libraries/common/process.effekt +0 -1
  19. package/libraries/common/queue.effekt +1 -3
  20. package/libraries/common/ref.effekt +11 -24
  21. package/libraries/common/result.effekt +6 -14
  22. package/libraries/common/seq.effekt +28 -49
  23. package/libraries/common/string/tty.effekt +66 -0
  24. package/libraries/common/string.effekt +7 -49
  25. package/libraries/common/test.effekt +96 -34
  26. package/libraries/js/unsafe/cont.effekt +1 -1
  27. package/libraries/llvm/io.c +258 -378
  28. package/libraries/llvm/main.c +3 -0
  29. package/libraries/llvm/rts.ll +57 -38
  30. package/libraries/llvm/types.c +10 -1
  31. package/licenses/eclipse public license, version 2.0 - epl-2.0.html +4 -4
  32. package/licenses/mit license - mit-license.html +21 -20
  33. package/licenses/the bsd license - bsd-license.html +20 -21
  34. package/licenses/the mit license - mit.html +22 -21
  35. package/package.json +1 -1
  36. package/libraries/chez/lift/effekt.ss +0 -118
  37. package/libraries/common/io/files.effekt +0 -282
  38. package/libraries/js/io.js +0 -143
  39. package/libraries/llvm/io.ll +0 -19
  40. package/libraries/ml/effekt.sml +0 -65
  41. package/libraries/ml/internal/mllist.effekt +0 -9
  42. package/libraries/ml/internal/mloption.effekt +0 -18
  43. package/libraries/ml/random.effekt +0 -85
  44. package/libraries/ml/text/regex.effekt +0 -102
@@ -1,18 +1,16 @@
1
1
  module io/error
2
2
 
3
- /**
4
- * Errors defined by libuv that can occur in IO operations.
5
- *
6
- * In addition to the named error codes, we also assign each error a
7
- * (positive) integer value that is stable across the different platforms.
8
- *
9
- * For the mapping of IOError and code values, see function fromCode.
10
- *
11
- * While stable, this integer value is supposed to only be used internally.
12
- * All user-facing operations should use values of type IOError, instead.
13
- *
14
- * see: https://docs.libuv.org/en/v1.x/errors.html
15
- */
3
+ /// Errors defined by libuv that can occur in IO operations.
4
+ ///
5
+ /// In addition to the named error codes, we also assign each error a
6
+ /// (positive) integer value that is stable across the different platforms.
7
+ ///
8
+ /// For the mapping of IOError and code values, see function fromCode.
9
+ ///
10
+ /// While stable, this integer value is supposed to only be used internally.
11
+ /// All user-facing operations should use values of type IOError, instead.
12
+ ///
13
+ /// see: https://docs.libuv.org/en/v1.x/errors.html
16
14
  type IOError {
17
15
  E2BIG();
18
16
  EACCES();
@@ -97,11 +95,9 @@ type IOError {
97
95
  EUNATCH()
98
96
  }
99
97
 
100
- /**
101
- * Corresponding error message for a libuv error
102
- *
103
- * see: https://docs.libuv.org/en/v1.x/errors.html
104
- */
98
+ /// Corresponding error message for a libuv error
99
+ ///
100
+ /// see: https://docs.libuv.org/en/v1.x/errors.html
105
101
  def message(error: IOError): String = error match {
106
102
  case E2BIG() => "argument list too long"
107
103
  case EACCES() => "permission denied"
@@ -186,11 +182,9 @@ def message(error: IOError): String = error match {
186
182
  case EUNATCH() => "protocol driver not attached"
187
183
  }
188
184
 
189
- /**
190
- * Corresponding name of a libuv error
191
- *
192
- * see: https://docs.libuv.org/en/v1.x/errors.html
193
- */
185
+ /// Corresponding name of a libuv error
186
+ ///
187
+ /// see: https://docs.libuv.org/en/v1.x/errors.html
194
188
  def name(error: IOError): String = error match {
195
189
  case E2BIG() => "E2BIG"
196
190
  case EACCES() => "EACCES"
@@ -275,12 +269,10 @@ def name(error: IOError): String = error match {
275
269
  case EUNATCH() => "EUNATCH"
276
270
  }
277
271
 
278
- /**
279
- * Translates a integer code value to an IOError.
280
- *
281
- * See documentation of type IOError for further information.
282
- */
283
- def fromCode(code: Int): IOError = code match {
272
+ /// Translates a integer code value to an IOError.
273
+ ///
274
+ /// See documentation of type IOError for further information.
275
+ def fromNumber(number: Int): IOError = number match {
284
276
  case 1 => EPERM()
285
277
  case 2 => ENOENT()
286
278
  case 3 => ESRCH()
@@ -361,5 +353,114 @@ def fromCode(code: Int): IOError = code match {
361
353
  case 219 => EUNATCH()
362
354
  case 220 => ESHUTDOWN()
363
355
 
364
- case _ => panic("Not a valid error code: " ++ show(code))
356
+ case _ => panic("Not a valid error code: " ++ show(number))
357
+ }
358
+
359
+ namespace internal {
360
+
361
+ extern llvm """
362
+ declare %Int @c_error_number(%Int)
363
+ """
364
+
365
+ extern jsNode """
366
+ const os = require('node:os');
367
+
368
+ /**
369
+ * Maps the error code to a Effekt-stable (platform independent) numeric value.
370
+ *
371
+ * Tries to use most common errno integer values, but introduces fresh values (> 200)
372
+ * for those without common errno values.
373
+ */
374
+ function errorNumber(errno) {
375
+ const errnoMap = {
376
+ [os.constants.errno.EPERM]: 1,
377
+ [os.constants.errno.ENOENT]: 2,
378
+ [os.constants.errno.ESRCH]: 3,
379
+ [os.constants.errno.EINTR]: 4,
380
+ [os.constants.errno.EIO]: 5,
381
+ [os.constants.errno.ENXIO]: 6,
382
+ [os.constants.errno.E2BIG]: 7,
383
+ [os.constants.errno.EBADF]: 9,
384
+ [os.constants.errno.EAGAIN]: 11,
385
+ [os.constants.errno.ENOMEM]: 12,
386
+ [os.constants.errno.EACCES]: 13,
387
+ [os.constants.errno.EFAULT]: 14,
388
+ [os.constants.errno.EBUSY]: 16,
389
+ [os.constants.errno.EEXIST]: 17,
390
+ [os.constants.errno.EXDEV]: 18,
391
+ [os.constants.errno.ENODEV]: 19,
392
+ [os.constants.errno.ENOTDIR]: 20,
393
+ [os.constants.errno.EISDIR]: 21,
394
+ [os.constants.errno.EINVAL]: 22,
395
+ [os.constants.errno.ENFILE]: 23,
396
+ [os.constants.errno.EMFILE]: 24,
397
+ [os.constants.errno.ENOTTY]: 25,
398
+ [os.constants.errno.ETXTBSY]: 26,
399
+ [os.constants.errno.EFBIG]: 27,
400
+ [os.constants.errno.ENOSPC]: 28,
401
+ [os.constants.errno.ESPIPE]: 29,
402
+ [os.constants.errno.EROFS]: 30,
403
+ [os.constants.errno.EMLINK]: 31,
404
+ [os.constants.errno.EPIPE]: 32,
405
+ [os.constants.errno.ERANGE]: 34,
406
+ [os.constants.errno.ENAMETOOLONG]: 36,
407
+ [os.constants.errno.ELOOP]: 40,
408
+ [os.constants.errno.EOVERFLOW]: 75,
409
+ [os.constants.errno.EFTYPE]: 79,
410
+ [os.constants.errno.EILSEQ]: 84,
411
+ [os.constants.errno.ENOTSOCK]: 88,
412
+ [os.constants.errno.EDESTADDRREQ]: 89,
413
+ [os.constants.errno.EMSGSIZE]: 90,
414
+ [os.constants.errno.EPROTOTYPE]: 91,
415
+ [os.constants.errno.ENOPROTOOPT]: 92,
416
+ [os.constants.errno.EPROTONOSUPPORT]: 93,
417
+ [os.constants.errno.ESOCKTNOSUPPORT]: 94,
418
+ [os.constants.errno.ENOTSUP]: 95,
419
+ [os.constants.errno.EAFNOSUPPORT]: 97,
420
+ [os.constants.errno.EADDRINUSE]: 98,
421
+ [os.constants.errno.EADDRNOTAVAIL]: 99,
422
+ [os.constants.errno.ENETDOWN]: 100,
423
+ [os.constants.errno.ENETUNREACH]: 101,
424
+ [os.constants.errno.ECONNABORTED]: 103,
425
+ [os.constants.errno.ECONNRESET]: 104,
426
+ [os.constants.errno.ENOBUFS]: 105,
427
+ [os.constants.errno.EISCONN]: 106,
428
+ [os.constants.errno.ENOTCONN]: 107,
429
+ [os.constants.errno.ETIMEDOUT]: 110,
430
+ [os.constants.errno.ECONNREFUSED]: 111,
431
+ [os.constants.errno.EHOSTUNREACH]: 113,
432
+ [os.constants.errno.EALREADY]: 114,
433
+ [os.constants.errno.ECANCELED]: 125,
434
+ [os.constants.errno.EAI_ADDRFAMILY]: 200,
435
+ [os.constants.errno.EAI_AGAIN]: 201,
436
+ [os.constants.errno.EAI_BADFLAGS]: 202,
437
+ [os.constants.errno.EAI_BADHINTS]: 203,
438
+ [os.constants.errno.EAI_CANCELED]: 204,
439
+ [os.constants.errno.EAI_FAIL]: 205,
440
+ [os.constants.errno.EAI_FAMILY]: 206,
441
+ [os.constants.errno.EAI_MEMORY]: 207,
442
+ [os.constants.errno.EAI_NODATA]: 208,
443
+ [os.constants.errno.EAI_NONAME]: 209,
444
+ [os.constants.errno.EAI_OVERFLOW]: 210,
445
+ [os.constants.errno.EAI_PROTOCOL]: 211,
446
+ [os.constants.errno.EAI_SERVICE]: 212,
447
+ [os.constants.errno.EAI_SOCKTYPE]: 213,
448
+ [os.constants.errno.ECHARSET]: 215,
449
+ [os.constants.errno.ENONET]: 216,
450
+ [os.constants.errno.UNKNOWN]: 217,
451
+ [os.constants.errno.EOF]: 218,
452
+ [os.constants.errno.EUNATCH]: 219,
453
+ [os.constants.errno.ESHUTDOWN]: 220
454
+ };
455
+ return errnoMap[-errno] || -1; // Default to -1 for unknown error names
456
+ }
457
+ """
458
+
459
+ extern pure def errorNumber(errno: Int): Int =
460
+ jsNode "errorNumber(${errno})"
461
+ llvm """
462
+ %z = call %Int @c_error_number(%Int ${errno})
463
+ ret %Int %z
464
+ """
465
+
365
466
  }
@@ -0,0 +1,262 @@
1
+ module io/filesystem
2
+
3
+ import bytes
4
+
5
+ import io
6
+ import io/error
7
+
8
+
9
+ /// Represents the file opening modes with their respective flags
10
+ /// Backends rely on the order of these
11
+ type Mode {
12
+ // Most common modes
13
+ ReadOnly() // 'r' exception if does not exist
14
+ WriteOnly() // 'w' created if does not exist, truncated if exists
15
+ AppendOnly() // 'a' created if does not exist
16
+
17
+ // Other modes
18
+ ReadWrite() // 'w+' created if does not exist, truncated if exists
19
+ ReadAppend() // 'a+' created if does not exist
20
+ AppendExclusive() // 'ax' created if does not exist, fails if exists
21
+ ReadAppendExclusive() // 'ax+' created if does not exist, fails if exists
22
+ AppendSync() // 'as' created if does not exist, append in synchronous mode
23
+ ReadAppendSync() // 'as+' created if does not exist, append in synchronous mode
24
+ ReadSync() // 'rs' exception if does not exist, read in synchronous mode
25
+ ReadWriteSync() // 'rs+' exception if does not exist, read/write in synchronous mode
26
+ WriteExclusive() // 'wx' created if does not exist, truncated if exists, fails if exists
27
+ ReadWriteExclusive() // 'wx+' created if does not exist, truncated if exists, fails if exists
28
+ }
29
+
30
+
31
+ /// A file descriptor. Should not be inspected.
32
+ type File = Int
33
+
34
+
35
+ /// Reads a file at given path as utf8 encoded string.
36
+ def readFile(path: String): String / Exception[IOError] = {
37
+ val fd = open(path, ReadOnly());
38
+ with on[IOError].finalize { close(fd) }
39
+
40
+ val readSize = 1048576 // 1MB
41
+ var size = readSize
42
+ var buffer = bytes(size)
43
+ var offset = 0;
44
+
45
+ def go(): String = {
46
+ read(fd, buffer.slice(offset, min(readSize, size - offset)), -1) match {
47
+ case 0 =>
48
+ buffer.slice(0, offset).toUTF8
49
+ case n and n < 0 => panic("Error!")
50
+ case n =>
51
+ offset = offset + n
52
+
53
+ // we are at maximum capacity
54
+ if (n == readSize && (offset + readSize) > size) {
55
+ val newSize = size * 2
56
+ val newBuffer = bytes(newSize)
57
+ copy(buffer, newBuffer, 0, 0, size)
58
+ buffer = newBuffer
59
+ size = newSize
60
+ }
61
+ go()
62
+ }
63
+ }
64
+
65
+ go()
66
+ }
67
+
68
+ /// Writes the (utf8 encoded) string `contents` into the specified file.
69
+ def writeFile(path: String, contents: String): Unit / Exception[IOError] = {
70
+ val fd = open(path, WriteOnly());
71
+ with on[IOError].finalize { close(fd) }
72
+
73
+ val writeSize = 1048576 // 1MB
74
+
75
+ // this induces a memcpy that is not strictly necessary, since we use the buffer read-only
76
+ val buffer = contents.fromUTF8
77
+ val size = buffer.size
78
+
79
+ var offset = 0;
80
+ def remaining() = size - offset
81
+
82
+ def go(): Unit =
83
+ write(fd, buffer.slice(offset, min(writeSize, remaining())), -1) match {
84
+ case 0 => ()
85
+ case n and n < 0 => panic("Error!")
86
+ case n =>
87
+ offset = offset + n;
88
+ if (remaining() > 0) go()
89
+ }
90
+
91
+ go()
92
+ }
93
+
94
+
95
+ /// An abstract interface applications can program against.
96
+ ///
97
+ /// Can be interpreted with the `filesystem` handler, or virtualized etc.
98
+ ///
99
+ /// See example below.
100
+ interface Files {
101
+ def readFile(path: String): String
102
+ def writeFile(path: String, contents: String): Unit
103
+ }
104
+
105
+ /// Using the file system via node.js
106
+ def filesystem[R] { program: => R / Files }: R / Exception[IOError] = // TODO move Exception to be bidirectional
107
+ try { program() }
108
+ with Files {
109
+ def readFile(path) = resume(readFile(path))
110
+ def writeFile(path, contents) = resume(writeFile(path, contents))
111
+ }
112
+
113
+
114
+ def open(path: String, mode: Mode): File / Exception[IOError] =
115
+ internal::checkResult(internal::open(path, mode))
116
+
117
+ def read(fd: File, buffer: Bytes, offset: Int): Int / Exception[IOError] =
118
+ internal::checkResult(internal::read(fd, buffer, offset))
119
+
120
+ def write(fd: File, buffer: Bytes, offset: Int): Int / Exception[IOError] =
121
+ internal::checkResult(internal::write(fd, buffer, offset))
122
+
123
+ def close(fd: File): Unit / Exception[IOError] = {
124
+ internal::checkResult(internal::close(fd)); ()
125
+ }
126
+
127
+ namespace internal {
128
+
129
+ extern js """
130
+ function modeName(mode) {
131
+ switch (mode.__tag) {
132
+ case 0: // ReadOnly()
133
+ return 'r';
134
+ case 1: // WriteOnly()
135
+ return 'w';
136
+ case 2: // AppendOnly()
137
+ return 'a';
138
+ case 3: // ReadWrite()
139
+ return 'w+';
140
+ case 4: // ReadAppend()
141
+ return 'a+';
142
+ case 5: // AppendExclusive()
143
+ return 'ax';
144
+ case 6: // ReadAppendExclusive()
145
+ return 'ax+';
146
+ case 7: // AppendSync()
147
+ return 'as';
148
+ case 8: // ReadAppendSync()
149
+ return 'as+';
150
+ case 9: // ReadSync()
151
+ return 'rs';
152
+ case 10: // ReadWriteSync()
153
+ return 'rs+';
154
+ case 11: // WriteExclusive()
155
+ return 'wx';
156
+ case 12: // ReadWriteExclusive()
157
+ return 'wx+';
158
+ default:
159
+ // Invalid tag value
160
+ return null;
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Nodejs file operations expect buffers, but we represent buffers as typed arrays.
166
+ * This function converts between the two without copying.
167
+ */
168
+ function toBuffer(buffer) {
169
+ return Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength)
170
+ }
171
+ """
172
+
173
+ extern jsNode """
174
+ const fs = require("fs");
175
+
176
+ function open(path, mode, callback) {
177
+ fs.open(path, modeName(mode), (err, fd) => {
178
+ if (err) { callback(err.errno) } else { callback(fd) }
179
+ })
180
+ }
181
+
182
+ function read(fd, buffer, offset, callback) {
183
+ let position = offset === -1 ? null : offset;
184
+ fs.read(fd, toBuffer(buffer), 0, buffer.length, position, (err, bytesRead) => {
185
+ if (err) { callback(err.errno) } else { callback(bytesRead) }
186
+ })
187
+ }
188
+
189
+ function write(fd, buffer, offset, callback) {
190
+ let position = offset === -1 ? null : offset;
191
+ fs.write(fd, toBuffer(buffer), 0, buffer.length, position, (err, bytesWritten) => {
192
+ if (err) { callback(err.errno) } else { callback(bytesWritten) }
193
+ })
194
+ }
195
+
196
+ function close(fd, callback) {
197
+ fs.close(fd, (err) => {
198
+ if (err) { callback(err.errno) } else { callback(0) }
199
+ })
200
+ }
201
+ """
202
+
203
+ extern llvm """
204
+ declare void @c_fs_open(%Pos, %Pos, %Stack)
205
+ declare void @c_fs_read(%Int, %Pos, %Int, %Stack)
206
+ declare void @c_fs_write(%Int, %Pos, %Int, %Stack)
207
+ declare void @c_fs_close(%Int, %Stack)
208
+ """
209
+
210
+ extern async def open(path: String, mode: Mode): Int =
211
+ jsNode "$effekt.callcc(callback => open(${path}, ${mode}, callback))"
212
+ llvm """
213
+ call void @c_fs_open(%Pos ${path}, %Pos ${mode}, %Stack %stack)
214
+ ret void
215
+ """
216
+
217
+ extern async def read(fd: Int, buffer: Bytes, offset: Int): Int =
218
+ jsNode "$effekt.callcc(callback => read(${fd}, ${buffer}, ${offset}, callback))"
219
+ llvm """
220
+ call void @c_fs_read(%Int ${fd}, %Pos ${buffer}, %Int ${offset}, %Stack %stack)
221
+ ret void
222
+ """
223
+
224
+ extern async def write(fd: Int, buffer: Bytes, offset: Int): Int =
225
+ jsNode "$effekt.callcc(callback => write(${fd}, ${buffer}, ${offset}, callback))"
226
+ llvm """
227
+ call void @c_fs_write(%Int ${fd}, %Pos ${buffer}, %Int ${offset}, %Stack %stack)
228
+ ret void
229
+ """
230
+
231
+ extern async def close(fd: Int): Int =
232
+ jsNode "$effekt.callcc(callback => close(${fd}, callback))"
233
+ llvm """
234
+ call void @c_fs_close(%Int ${fd}, %Stack %stack)
235
+ ret void
236
+ """
237
+
238
+ def checkResult(result: Int): Int / Exception[IOError] =
239
+ if (result < 0) {
240
+ val ioError = fromNumber(internal::errorNumber(result));
241
+ do raise[IOError](ioError, message(ioError))
242
+ } else {
243
+ result
244
+ }
245
+ }
246
+
247
+ namespace examples {
248
+
249
+ // Example illustrating using abstract files interface
250
+ // and interpreting it into asynchronous IO
251
+ def copy(from: String, to: String): Unit / Files = {
252
+ val contents = do readFile(from)
253
+ do writeFile(to, contents)
254
+ }
255
+
256
+ def main() = {
257
+ with on[IOError].panic;
258
+ with filesystem;
259
+
260
+ copy("README.md", "out/test.md")
261
+ }
262
+ }
@@ -1,11 +1,10 @@
1
1
  module io/network
2
2
 
3
3
  import bytes
4
- import queue
5
4
  import io
6
5
 
7
6
  namespace js {
8
- extern js """
7
+ extern jsNode """
9
8
  const net = require('node:net');
10
9
 
11
10
  function listen(server, port, host, listener) {
@@ -17,18 +16,18 @@ namespace js {
17
16
  extern type JSServer // = net.Server
18
17
  extern type JSSocket // = net.Socket
19
18
  extern io def server(): JSServer =
20
- js "net.createServer()"
21
- extern io def listen(server: JSServer, port: Int, host: String, listener: JSSocket => Unit at {io, global}): Unit =
22
- js "listen(${server}, ${port}, ${host}, (socket) => (${listener})(socket).run())"
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())"
23
22
 
24
- extern io def send(socket: JSSocket, data: Bytes, callback: () => Unit at {io, global}): Unit =
25
- js "${socket}.write(${data}, () => (${callback})().run())"
23
+ extern async def send(socket: JSSocket, data: Bytes): Unit =
24
+ jsNode "$effekt.callcc(callback => ${socket}.write(${data}, callback))"
26
25
 
27
- extern io def receive(socket: JSSocket, callback: Bytes => Unit at {io, global}): Unit =
28
- js "${socket}.once('data', (data) => (${callback})(data).run())"
26
+ extern async def receive(socket: JSSocket): Bytes =
27
+ jsNode "$effekt.callcc(callback => ${socket}.once('data', callback))"
29
28
 
30
- extern io def end(socket: JSSocket, callback: () => Unit at {io, global}): Unit =
31
- js "${socket}.end(() => (${callback})().run())"
29
+ extern async def end(socket: JSSocket): Unit =
30
+ jsNode "$effekt.callcc(callback => ${socket}.end(callback))"
32
31
  }
33
32
 
34
33
  interface Socket {
@@ -37,44 +36,22 @@ interface Socket {
37
36
  def end(): Unit
38
37
  }
39
38
 
40
- def server(host: String, port: Int, handler: () => Unit / { Socket, IO } at {io, global}): Unit / IO = {
39
+ def server(host: String, port: Int, handler: () => Unit / Socket at {io, async, global}): Unit = {
41
40
  val server = js::server();
42
- def connections: Queue[js::JSSocket] = emptyQueue()
43
41
  js::listen(server, port, host, box { socket =>
44
- connections.pushFront(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
+ })
45
54
  })
46
-
47
- // TODO Instead of polling, we should use Channels
48
- loop {
49
- connections.popBack match {
50
- case Some(socket) =>
51
- println("New connection")
52
- do promise(box {
53
- try { handler() } with Socket {
54
- def send(message) = {
55
- do performIO[Unit](box { k =>
56
- js::send(socket, message, box { k(()) })
57
- });
58
- resume(())
59
- }
60
- def receive() = {
61
- resume(do performIO[Bytes](box { k =>
62
- js::receive(socket, k)
63
- }))
64
- }
65
- def end() = {
66
- do performIO[Unit](box { k =>
67
- js::end(socket, box { k(()) })
68
- });
69
- resume(())
70
- }
71
- }
72
- });
73
- ()
74
- case None() =>
75
- do yield()
76
- }
77
- }
78
55
  }
79
56
 
80
57
 
@@ -82,12 +59,12 @@ namespace examples {
82
59
  def helloWorldApp(): Unit / Socket = {
83
60
  val request = do receive();
84
61
 
85
- println("Received a request: " ++ request.show)
62
+ println("Received a request: " ++ request.toUTF8)
86
63
 
87
- if (request.show.startsWith("GET /")) {
88
- do send(toBytes("HTTP/1.1 200 OK\r\n\r\nHello from Effekt!"))
64
+ if (request.toUTF8.startsWith("GET /")) {
65
+ do send(fromUTF8("HTTP/1.1 200 OK\r\n\r\nHello from Effekt!"))
89
66
  } else {
90
- do send("HTTP/1.1 400 Bad Request\r\n\r\n".toBytes)
67
+ do send("HTTP/1.1 400 Bad Request\r\n\r\n".fromUTF8)
91
68
  }
92
69
  do end()
93
70
  }
@@ -96,10 +73,9 @@ namespace examples {
96
73
  def main() = {
97
74
  val port = 8080
98
75
  println("Starting server on http://localhost:" ++ port.show)
99
- eventloop(box {
100
- server("localhost", port, box {
101
- helloWorldApp()
102
- })
76
+
77
+ server("localhost", port, box {
78
+ helloWorldApp()
103
79
  })
104
80
  }
105
81
  }
@@ -1,15 +1,12 @@
1
1
  module io/time
2
2
 
3
- import io
3
+ extern llvm """
4
+ declare void @c_timer_start(%Int, %Stack)
5
+ """
4
6
 
5
- def wait(millis: Int): Unit / AsyncIO =
6
- do performIO(box { k => callback::wait(millis, box { () => k(()) }) })
7
-
8
- namespace callback {
9
- extern io def wait(millis: Int, onTimeout: () => Unit at {io, global}): Unit =
10
- js "setTimeout(() => (${onTimeout})().run(), ${millis})"
11
- llvm """
12
- call void @c_timer_wait(i64 ${millis}, %Neg ${onTimeout})
13
- ret %Pos zeroinitializer
14
- """
15
- }
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
+ """