@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.
Files changed (41) hide show
  1. package/bin/effekt +0 -0
  2. package/libraries/chez/common/immutable/cslist.effekt +6 -6
  3. package/libraries/chez/common/mutable/dict.effekt +7 -7
  4. package/libraries/common/args.effekt +10 -10
  5. package/libraries/common/array.effekt +57 -4
  6. package/libraries/common/bench.effekt +2 -2
  7. package/libraries/common/buffer.effekt +5 -5
  8. package/libraries/common/bytearray.effekt +69 -7
  9. package/libraries/common/char.effekt +95 -8
  10. package/libraries/common/effekt.effekt +74 -71
  11. package/libraries/common/exception.effekt +1 -1
  12. package/libraries/common/io/console.effekt +4 -4
  13. package/libraries/common/io/error.effekt +12 -1
  14. package/libraries/common/io/filesystem.effekt +78 -16
  15. package/libraries/common/io/network.effekt +97 -65
  16. package/libraries/common/io/time.effekt +1 -1
  17. package/libraries/common/io.effekt +7 -8
  18. package/libraries/common/json.effekt +5 -5
  19. package/libraries/common/list.effekt +154 -18
  20. package/libraries/common/map.effekt +35 -1
  21. package/libraries/common/process.effekt +1 -1
  22. package/libraries/common/queue.effekt +3 -3
  23. package/libraries/common/random.effekt +1 -0
  24. package/libraries/common/ref.effekt +8 -4
  25. package/libraries/common/regex.effekt +4 -4
  26. package/libraries/common/seq.effekt +1 -1
  27. package/libraries/common/set.effekt +24 -1
  28. package/libraries/common/stream.effekt +2 -360
  29. package/libraries/common/string.effekt +35 -5
  30. package/libraries/common/stringbuffer.effekt +1 -0
  31. package/libraries/js/mutable/map.effekt +5 -5
  32. package/libraries/js/web/dom.effekt +11 -11
  33. package/libraries/llvm/io.c +281 -8
  34. package/libraries/llvm/main.c +1 -2
  35. package/libraries/llvm/rts.ll +14 -7
  36. package/libraries/llvm/types.c +3 -3
  37. package/licenses/eclipse public license, version 2.0 - epl-2.0.html +2 -2
  38. package/licenses/mit license - mit-license.html +48 -48
  39. package/licenses/the bsd license - bsd-license.html +50 -50
  40. package/licenses/the mit license - mit.html +48 -48
  41. package/package.json +1 -1
@@ -1,5 +1,7 @@
1
1
  module map
2
2
 
3
+ import stream
4
+
3
5
  /// Ordered finite immutable map, backed by balanced binary trees of logarithmic depth.
4
6
  record Map[K, V](tree: internal::Tree[K, V], compare: (K, K) => Ordering at {})
5
7
 
@@ -989,4 +991,36 @@ namespace internal {
989
991
 
990
992
  "[" ++ res ++ "]"
991
993
  }
992
- }
994
+ }
995
+
996
+
997
+ // Streaming
998
+ // ---------
999
+
1000
+ /// Turns a `map` into a producer of a push stream
1001
+ /// of `(key, value)` pairs by emitting each contained *in order*.
1002
+ def each[K, V](map: Map[K, V]): Unit / emit[(K, V)] =
1003
+ map.foreach { (k, v) => do emit((k, v)) }
1004
+
1005
+ /// Turns a `map` into a producer of a push stream
1006
+ /// of its keys by emitting each contained *in order*.
1007
+ def eachKey[K, V](map: Map[K, V]): Unit / emit[K] =
1008
+ map.foreach { (k, _v) => do emit(k) }
1009
+
1010
+ /// Turns a `map` into a producer of a push stream
1011
+ /// of its values by emitting each contained.
1012
+ def eachValue[K, V](map: Map[K, V]): Unit / emit[V] =
1013
+ map.foreach { (_k, v) => do emit(v) }
1014
+
1015
+ def collect[K, V](compare: (K, K) => Ordering at {}) { stream: () => Unit / emit[(K, V)] }: Map[K, V] =
1016
+ returning::collect[K, V, Unit](compare){stream}.second
1017
+
1018
+ namespace returning {
1019
+ def collect[K, V, R](compare: (K, K) => Ordering at {}) { stream: () => R / emit[(K, V)] }: (R, Map[K, V]) =
1020
+ try {
1021
+ (stream(), empty(compare))
1022
+ } with emit[(K, V)] { case (k, v) =>
1023
+ val (r, map) = resume(());
1024
+ (r, map.put(k, v))
1025
+ }
1026
+ }
@@ -1,6 +1,6 @@
1
1
  module process
2
2
 
3
- extern io def exit(errorCode: Int): Nothing =
3
+ extern def exit(errorCode: Int) at io: Nothing =
4
4
  js "(function() { process.exit(${errorCode}) })()"
5
5
  llvm """
6
6
  call void @exit(i64 ${errorCode})
@@ -20,10 +20,10 @@ interface Queue[T] {
20
20
  }
21
21
 
22
22
 
23
- def emptyQueue[T](): Queue[T] at {global} =
23
+ def emptyQueue[T](): Queue[T] at global =
24
24
  emptyQueue[T](64)
25
25
 
26
- def emptyQueue[T](initialCapacity: Int): Queue[T] at {global} = {
26
+ def emptyQueue[T](initialCapacity: Int): Queue[T] at global = {
27
27
 
28
28
  val contents = ref(array[Option[T]](initialCapacity, None()))
29
29
  val head = ref(0)
@@ -103,7 +103,7 @@ def emptyQueue[T](initialCapacity: Int): Queue[T] at {global} = {
103
103
  tail.set(mod(tail.get + 1, capacity.get))
104
104
  }
105
105
  }
106
- queue
106
+ box queue
107
107
  }
108
108
 
109
109
  namespace examples {
@@ -1,6 +1,7 @@
1
1
  module random
2
2
 
3
3
  import stream
4
+ import io/filesystem
4
5
  import io/error
5
6
 
6
7
  /// Infinite pull stream of random bytes.
@@ -30,7 +30,7 @@ extern type Ref[T]
30
30
 
31
31
  /// Allocates a new reference, keeping its value _undefined_.
32
32
  /// Prefer using `ref` constructor instead to ensure that the value is defined.
33
- extern global def allocate[T](): Ref[T] =
33
+ extern def allocate[T]() at global: Ref[T] =
34
34
  js "{ value: undefined }"
35
35
  chez "(box #f)"
36
36
  llvm """
@@ -50,7 +50,7 @@ extern global def allocate[T](): Ref[T] =
50
50
  """
51
51
 
52
52
  /// Creates a new reference with the initial value `init`.
53
- extern global def ref[T](init: T): Ref[T] =
53
+ extern def ref[T](init: T) at global: Ref[T] =
54
54
  js "{ value: ${init} }"
55
55
  chez "(box ${init})"
56
56
  llvm """
@@ -72,7 +72,7 @@ extern global def ref[T](init: T): Ref[T] =
72
72
  vm "ref::ref[T](T)"
73
73
 
74
74
  /// Gets the referenced element of the `ref` in constant time.
75
- extern global def get[T](ref: Ref[T]): T =
75
+ extern def get[T](ref: Ref[T]) at global: T =
76
76
  js "${ref}.value"
77
77
  chez "(unbox ${ref})"
78
78
  llvm """
@@ -88,7 +88,7 @@ llvm """
88
88
  vm "ref::get[T](Ref[T])"
89
89
 
90
90
  /// Sets the referenced element of the `ref` to `value` in constant time.
91
- extern global def set[T](ref: Ref[T], value: T): Unit =
91
+ extern def set[T](ref: Ref[T], value: T) at global: Unit =
92
92
  js "set$impl(${ref}, ${value})"
93
93
  chez "(set-box! ${ref} ${value})"
94
94
  llvm """
@@ -104,3 +104,7 @@ extern global def set[T](ref: Ref[T], value: T): Unit =
104
104
  ret %Pos zeroinitializer
105
105
  """
106
106
  vm "ref::set[T](Ref[T], T)"
107
+
108
+ /// Modifies the inner contents of the `ref` with a function `f`
109
+ def map[T](ref: Ref[T]) { f: T => T }: Unit =
110
+ ref.set(f(ref.get))
@@ -8,7 +8,7 @@ extern type Regex
8
8
 
9
9
  record Match(matched: String, index: Int)
10
10
 
11
- extern pure def regex(str: String): Regex =
11
+ extern def regex(str: String) at {}: Regex =
12
12
  js "new RegExp(${str})"
13
13
  chez "(pregexp ${str})"
14
14
  vm "regex::regex(String)"
@@ -29,12 +29,12 @@ namespace internal {
29
29
  // js: { matched: String, index: Int } | undefined
30
30
  // vm: scala.util.matching.Regex.Match | null
31
31
 
32
- extern pure def matched(r: RegexMatch): String =
32
+ extern def matched(r: RegexMatch) at {}: String =
33
33
  js "${r}.matched"
34
34
  chez "(vector-ref ${r} 0)"
35
35
  vm "regex::matched(RegexMatch)"
36
36
 
37
- extern pure def index(r: RegexMatch): Int =
37
+ extern def index(r: RegexMatch) at {}: Int =
38
38
  js "${r}.index"
39
39
  chez "(vector-ref ${r} 1)"
40
40
  vm "regex::index(RegexMatch)"
@@ -57,7 +57,7 @@ namespace internal {
57
57
  #f))))
58
58
  """
59
59
 
60
- extern io def exec(reg: Regex, str: String): RegexMatch =
60
+ extern def exec(reg: Regex, str: String) at io: RegexMatch =
61
61
  js "regex$exec(${reg}, ${str})"
62
62
  chez "(regex-exec ${reg} ${str})"
63
63
  vm "regex::exec(Regex, String)"
@@ -875,7 +875,7 @@ def map[A, B](seq: Seq[A]) { f: (A) {Control} => B }: Seq[B] = {
875
875
 
876
876
  // def iterate[A, R](seq: Seq[A]) { prog: {Iterator[A]} => R }: R = {
877
877
 
878
- // var k: Unit => Unit at {} = fun(unit: Unit) { () }
878
+ // var k: Unit => Unit at {} = box { () => () }
879
879
  // var v: Option[A] = None()
880
880
 
881
881
  // try { seq.each { el => do yield(el) } } with Generator[A] {
@@ -1,6 +1,7 @@
1
1
  module set
2
2
 
3
3
  import map
4
+ import stream
4
5
 
5
6
  /// Ordered finite immutable set, backed by balanced binary trees of logarithmic depth.
6
7
  record Set[A](tree: internal::Tree[A, Unit], compare: (A, A) => Ordering at {})
@@ -231,4 +232,26 @@ def intersection[A](s1: Set[A], s2: Set[A], compare: (A, A) => Ordering at {}):
231
232
  ///
232
233
  /// O(???)
233
234
  def intersection[A](s1: Set[A], s2: Set[A]): Set[A] =
234
- s1.intersection(s2, s1.compare)
235
+ s1.intersection(s2, s1.compare)
236
+
237
+
238
+ // Streaming
239
+ // ---------
240
+
241
+ /// Turns a `set` into a producer of a push stream
242
+ /// by emitting each contained *in order*.
243
+ def each[A](set: Set[A]): Unit / emit[A] =
244
+ set.foreach { x => do emit(x) }
245
+
246
+ def collect[A](compare: (A, A) => Ordering at {}) { stream: () => Unit / emit[A] }: Set[A] =
247
+ returning::collect[A, Unit](compare){stream}.second
248
+
249
+ namespace returning {
250
+ def collect[A, R](compare: (A, A) => Ordering at {}) { stream: () => R / emit[A] }: (R, Set[A]) =
251
+ try {
252
+ (stream(), empty(compare))
253
+ } with emit[A] { (v) =>
254
+ val (r, set) = resume(());
255
+ (r, set.insert(v))
256
+ }
257
+ }
@@ -1,12 +1,7 @@
1
1
  module stream
2
2
 
3
- import array
4
- import bytearray
5
- import map
6
- import set
7
-
8
- import io/filesystem
9
- import io/error
3
+ import effekt
4
+ import option
10
5
 
11
6
  // Effects
12
7
  // -------
@@ -53,68 +48,6 @@ def fix[T] { one: T => Unit / emit[T] } { stream: => Unit / emit[T] }: Unit =
53
48
  resume(())
54
49
  }
55
50
 
56
- /// Turns a `list` into a producer of a push stream
57
- /// by emitting each contained value left-to-right.
58
- def each[A](list: List[A]): Unit / emit[A] =
59
- list match {
60
- case Nil() => ()
61
- case Cons(head, tail) =>
62
- do emit(head)
63
- each(tail)
64
- }
65
-
66
- /// Turns an `array` into a producer of a push stream
67
- /// by emitting each contained value from 0 to length - 1.
68
- def each[T](array: Array[T]): Unit / emit[T] = {
69
- val n = array.size
70
- def go(i: Int): Unit = {
71
- if (i < n) {
72
- do emit(array.unsafeGet(i))
73
- go(i + 1)
74
- }
75
- }
76
- go(0)
77
- }
78
-
79
- /// Turns `bytes` into a producer of a push stream
80
- /// by emitting each contained value from 0 to length - 1.
81
- def each(bytes: ByteArray): Unit / emit[Byte] = {
82
- val n = bytes.size
83
- def go(i: Int): Unit = {
84
- if (i < n) {
85
- do emit(bytes.unsafeGet(i))
86
- go(i + 1)
87
- }
88
- }
89
- go(0)
90
- }
91
-
92
- /// Turns a `map` into a producer of a push stream
93
- /// of `(key, value)` pairs by emitting each contained *in order*.
94
- def each[K, V](map: Map[K, V]): Unit / emit[(K, V)] =
95
- map.foreach { (k, v) => do emit((k, v)) }
96
-
97
- /// Turns a `map` into a producer of a push stream
98
- /// of its keys by emitting each contained *in order*.
99
- def eachKey[K, V](map: Map[K, V]): Unit / emit[K] =
100
- map.foreach { (k, _v) => do emit(k) }
101
-
102
- /// Turns a `map` into a producer of a push stream
103
- /// of its values by emitting each contained.
104
- def eachValue[K, V](map: Map[K, V]): Unit / emit[V] =
105
- map.foreach { (_k, v) => do emit(v) }
106
-
107
- /// Turns a `set` into a producer of a push stream
108
- /// by emitting each contained *in order*.
109
- def each[A](set: Set[A]): Unit / emit[A] =
110
- set.foreach { x => do emit(x) }
111
-
112
- // not option
113
- // not dequeue
114
- // not queue
115
- // not result
116
- // not seq
117
-
118
51
 
119
52
  def boundary { program: () => Unit / stop }: Unit = {
120
53
  returning::boundary[Unit]{program}
@@ -202,87 +135,6 @@ def sum { stream: () => Unit / emit[Int] }: Int =
202
135
  def product { stream: () => Unit / emit[Int] }: Int =
203
136
  returning::product[Unit]{stream}.second
204
137
 
205
- def collectList[A] { stream: () => Unit / emit[A] }: List[A] =
206
- returning::collectList[A, Unit]{stream}.second
207
-
208
- def collectArray[A] { stream: () => Unit / emit[A] }: Array[A] =
209
- returning::collectArray[A, Unit]{stream}.second
210
-
211
- def collectBytes { stream: () => Unit / emit[Byte] }: ByteArray =
212
- returning::collectBytes[Unit]{stream}.second
213
-
214
- def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
215
- var l = list
216
- try {
217
- reader()
218
- } with read[T] {
219
- resume {
220
- l match {
221
- case Nil() => do stop()
222
- case Cons(value, rest) =>
223
- l = rest
224
- return value
225
- }
226
- }
227
- }
228
- }
229
-
230
- def collectMap[K, V, R](compare: (K, K) => Ordering at {}) { stream: () => R / emit[(K, V)] }: (R, Map[K, V]) =
231
- try {
232
- (stream(), map::empty(compare))
233
- } with emit[(K, V)] { case (k, v) =>
234
- val (r, map) = resume(());
235
- (r, map.put(k, v))
236
- }
237
-
238
- def collectMap[K, V](compare: (K, K) => Ordering at {}) { stream: () => Any / emit[(K, V)] }: Map[K, V] =
239
- collectMap[K, V, Any](compare){stream}.second
240
-
241
- def collectSet[A, R](compare: (A, A) => Ordering at {}) { stream: () => R / emit[A] }: (R, Set[A]) =
242
- try {
243
- (stream(), set::empty(compare))
244
- } with emit[A] { (v) =>
245
- val (r, set) = resume(());
246
- (r, set.insert(v))
247
- }
248
-
249
- def collectSet[A](compare: (A, A) => Ordering at {}) { stream: () => Any / emit[A] }: Set[A] =
250
- collectSet[A, Any](compare){stream}.second
251
-
252
- def feed[T, R](array: Array[T]) { reader: () => R / read[T] }: R = {
253
- var i = 0
254
- try {
255
- reader()
256
- } with read[T] {
257
- resume {
258
- if (i < array.size) {
259
- val c = i
260
- i = c + 1
261
- array.unsafeGet(c)
262
- } else {
263
- do stop()
264
- }
265
- }
266
- }
267
- }
268
-
269
- def feed[R](bytes: ByteArray) { reader: () => R / read[Byte] }: R = {
270
- var i = 0
271
- try {
272
- reader()
273
- } with read[Byte] {
274
- resume {
275
- if (i < bytes.size) {
276
- val c = i
277
- i = c + 1
278
- bytes.unsafeGet(c)
279
- } else {
280
- do stop()
281
- }
282
- }
283
- }
284
- }
285
-
286
138
  def source[A] { stream: () => Unit / emit[A] } { reader: () => Unit / read[A] }: Unit =
287
139
  returning::source[A, Unit]{stream}{reader}
288
140
 
@@ -319,155 +171,6 @@ def zipLongest[A, B] { stream1: () => Unit / emit[A] } { stream2: () => Unit / e
319
171
  }
320
172
  }
321
173
 
322
- def writeFile[R](path: String) { stream: () => R / emit[Byte] }: R / Exception[IOError] = {
323
-
324
- val file = openForWriting(path);
325
- with on[IOError].finalize { close(file) }
326
-
327
- val chunkSize = 1048576 // 1MB
328
- val buffer = bytearray::allocate(chunkSize)
329
- var offset = 0
330
-
331
- def push(i: Int, n: Int): Unit = {
332
- val r = write(file, buffer, i, n, -1)
333
- if (r < n) {
334
- push(i + r, n - r)
335
- }
336
- }
337
-
338
- try {
339
- val r = stream()
340
- push(0, offset)
341
- return r
342
- } with emit[Byte] { (byte) =>
343
- if (offset >= buffer.size) {
344
- push(0, buffer.size)
345
- offset = 0
346
- }
347
- buffer.unsafeSet(offset, byte)
348
- offset = offset + 1
349
- resume(())
350
- }
351
- }
352
-
353
- def readFile[R](path: String) { reader: () => R / read[Byte] }: R / Exception[IOError] = {
354
-
355
- val file = openForReading(path);
356
- with on[IOError].finalize { close(file) }
357
-
358
- val chunkSize = 1048576 // 1MB
359
- val buffer = bytearray::allocate(chunkSize)
360
- var offset = 0
361
- var length = 0
362
-
363
- def pull(): Unit / stop = {
364
- read(file, buffer, 0, chunkSize, -1) match {
365
- case 0 =>
366
- do stop()
367
- case n =>
368
- length = n
369
- }
370
- }
371
-
372
- try {
373
- reader()
374
- } with read[Byte] {
375
- resume {
376
- if (offset >= length) {
377
- pull()
378
- offset = 0
379
- }
380
- val byte = buffer.unsafeGet(offset)
381
- offset = offset + 1
382
- return byte
383
- }
384
- }
385
- }
386
-
387
- def decodeChar(): Char / {read[Byte], stop} = {
388
- val b = do read().toInt
389
- if (b < 128) {
390
- b.toChar
391
- } else if (b < 224) {
392
- val part1 = bitwiseShl(bitwiseAnd(b, 31), 6)
393
- val part2 = bitwiseAnd(do read().toInt, 63)
394
- bitwiseOr(part1, part2).toChar
395
- } else if (b < 240) {
396
- val part1 = bitwiseShl(bitwiseAnd(b, 15), 12)
397
- val part2 = bitwiseShl(bitwiseAnd(do read().toInt, 63), 6)
398
- val part3 = bitwiseAnd(do read().toInt, 63)
399
- bitwiseOr(bitwiseOr(part1, part2), part3).toChar
400
- } else {
401
- val part1 = bitwiseShl(bitwiseAnd(b, 7), 18)
402
- val part2 = bitwiseShl(bitwiseAnd(do read().toInt, 63), 12)
403
- val part3 = bitwiseShl(bitwiseAnd(do read().toInt, 63), 6)
404
- val part4 = bitwiseAnd(do read().toInt, 63)
405
- bitwiseOr(bitwiseOr(bitwiseOr(part1, part2), part3), part4).toChar
406
- }
407
- }
408
-
409
- def encodeChar(char: Char): Unit / emit[Byte] = {
410
- val code = char.toInt
411
- if (code < 128) {
412
- do emit(code.toByte)
413
- } else if (code < 2048) {
414
- do emit(bitwiseOr(192, bitwiseShr(code, 6)).toByte)
415
- do emit(bitwiseOr(128, bitwiseAnd(code, 63)).toByte)
416
- } else if (code < 65536) {
417
- do emit(bitwiseOr(224, bitwiseShr(code, 12)).toByte)
418
- do emit(bitwiseOr(128, bitwiseAnd(bitwiseShr(code, 6), 63)).toByte)
419
- do emit(bitwiseOr(128, bitwiseAnd(code, 63)).toByte)
420
- } else {
421
- do emit(bitwiseOr(240, bitwiseShr(code, 18)).toByte)
422
- do emit(bitwiseOr(128, bitwiseAnd(bitwiseShr(code, 12), 63)).toByte)
423
- do emit(bitwiseOr(128, bitwiseAnd(bitwiseShr(code, 6), 63)).toByte)
424
- do emit(bitwiseOr(128, bitwiseAnd(code, 63)).toByte)
425
- }
426
- }
427
-
428
- def decodeUTF8[R] { reader: () => R / read[Char] }: R / read[Byte] =
429
- try {
430
- reader()
431
- } with read[Char] {
432
- resume { decodeChar() }
433
- }
434
-
435
- def encodeUTF8[R] { stream: () => R / emit[Char] }: R / emit[Byte] =
436
- try {
437
- stream()
438
- } with emit[Char] { char =>
439
- resume(encodeChar(char))
440
- }
441
-
442
- def writeFileUTF8[R](path: String) { stream: () => R / emit[Char] }: R / Exception[IOError] =
443
- writeFile(path) { encodeUTF8 { stream() } }
444
-
445
- def readFileUTF8[R](path: String) { reader: () => R / read[Char] }: R / Exception[IOError] =
446
- readFile(path) { decodeUTF8 { reader() } }
447
-
448
- def feed[R](string: String) { reader: () => R / read[Char] } =
449
- feed(string.fromString) {
450
- decodeUTF8 {
451
- reader()
452
- }
453
- }
454
-
455
- def each(string: String): Unit / emit[Char] =
456
- feed(string) {
457
- exhaustively {
458
- do emit[Char](do read[Char]())
459
- }
460
- }
461
-
462
- def collectString { stream: () => Unit / emit[Char] }: String =
463
- returning::collectString[Unit]{stream}.second
464
-
465
- def writeLine { body: () => Unit / emit[Char] }: Unit / emit[Char] =
466
- returning::writeLine[Unit]{body}
467
-
468
- def readLine { body: () => Unit / read[Char] }: Unit / {read[Char], stop} =
469
- returning::readLine[Unit]{body}
470
-
471
174
  namespace internal {
472
175
  effect snapshot(): Unit
473
176
  }
@@ -640,67 +343,6 @@ def product[R] { stream: () => R / emit[Int] }: (R, Int) = {
640
343
  }
641
344
  }
642
345
 
643
- def collectList[A, R] { stream: () => R / emit[A] }: (R, List[A]) =
644
- try {
645
- (stream(), Nil())
646
- } with emit[A] { (v) =>
647
- val (r, vs) = resume(());
648
- (r, Cons(v, vs))
649
- }
650
-
651
- def collectArray[A, R] { stream: () => R / emit[A] }: (R, Array[A]) = {
652
- var i = 0
653
- var a = array::allocate(1)
654
- try {
655
- (stream(), a.resize(i))
656
- } with emit[A] { (v) =>
657
- if (i >= a.size) { a = a.resize(2 * a.size) }
658
- a.unsafeSet(i, v)
659
- i = i + 1
660
- resume(())
661
- }
662
- }
663
-
664
- def collectBytes[R] { stream: () => R / emit[Byte] }: (R, ByteArray) = {
665
- var i = 0
666
- var a = bytearray::allocate(1)
667
- try {
668
- (stream(), a.resize(i))
669
- } with emit[Byte] { (v) =>
670
- if (i >= a.size) { a = a.resize(2 * a.size) }
671
- a.unsafeSet(i, v)
672
- i = i + 1
673
- resume(())
674
- }
675
- }
676
-
677
- def collectString[R] { stream: () => R / emit[Char] }: (R, String) = {
678
- val (result, bytes) = collectBytes[R] { encodeUTF8 { stream } }
679
- (result, bytes.toString)
680
- }
681
-
682
- def writeLine[R] { body: () => R / emit[Char] }: R / emit[Char] = {
683
- val result = body()
684
- do emit('\n')
685
- return result
686
- }
687
-
688
- def readLine[R] { body: () => R / read[Char] }: R / {read[Char], stop} = {
689
- var stopped = false
690
- try {
691
- body()
692
- } with read[Char] {
693
- if(stopped){
694
- resume { do stop() }
695
- } else {
696
- do read[Char] match {
697
- case '\n' => stopped = true; resume { do stop() }
698
- case char => resume { return char }
699
- }
700
- }
701
- }
702
- }
703
-
704
346
  def source[A, R] { stream: () => Unit / emit[A] } { reader: () => R / read[A] }: R = {
705
347
  var next = box { None() }
706
348
  next = box {
@@ -2,10 +2,12 @@ module string
2
2
 
3
3
  import effekt
4
4
  import option
5
+ import stream
5
6
  import list
6
7
  import exception
7
8
  import result
8
9
  import char
10
+ import bytearray
9
11
 
10
12
  // TODO
11
13
  // - [ ] handle unicode codepoints (that can span two indices) correctly
@@ -111,7 +113,7 @@ def join(strings: List[String], sep: String): String = {
111
113
  }
112
114
  }
113
115
 
114
- extern pure def trim(str: String): String =
116
+ extern def trim(str: String) at {}: String =
115
117
  js "${str}.trim()"
116
118
 
117
119
  // Parsing
@@ -169,7 +171,7 @@ def toInt(str: String, base: Int): Int / Exception[WrongFormat] = {
169
171
  }
170
172
 
171
173
  /// Native versions of toInt (unused right now)
172
- extern pure def unsafeToInt(str: String): Int =
174
+ extern def unsafeToInt(str: String) at {}: Int =
173
175
  js "(Number.isNaN(parseInt(${str})) ? undefined : parseInt(${str}))"
174
176
  chez "(string->number ${str})"
175
177
 
@@ -203,7 +205,7 @@ def lastIndexOf(str: String, sub: String, from: Int): Option[Int] = {
203
205
 
204
206
  // TODO more efficient (native) implementations could be integrated as follows
205
207
 
206
- // extern pure def indexOf(str: String, sub: String): Option[Int] =
208
+ // extern def indexOf(str: String, sub: String) at {}: Option[Int] =
207
209
  // //js { nativeIndexOf(str, sub) }
208
210
  // default { indexOf(str, sub, 0) }
209
211
 
@@ -212,7 +214,7 @@ def lastIndexOf(str: String, sub: String, from: Int): Option[Int] = {
212
214
  // if (index < 0) { None() } else { Some(index) }
213
215
  // }
214
216
 
215
- // extern pure def unsafeIndexOf(str: String, sub: String): Int =
217
+ // extern def unsafeIndexOf(str: String, sub: String) at {}: Int =
216
218
  // js "${str}.indexOf(${sub})"
217
219
 
218
220
  def charAt(str: String, index: Int): Char / Exception[OutOfBounds] =
@@ -221,7 +223,7 @@ def charAt(str: String, index: Int): Char / Exception[OutOfBounds] =
221
223
  else
222
224
  unsafeCharAt(str, index)
223
225
 
224
- extern pure def unsafeCharAt(str: String, n: Int): Char =
226
+ extern def unsafeCharAt(str: String, n: Int) at {}: Char =
225
227
  js "${str}.codePointAt(${n})"
226
228
  chez "(char->integer (string-ref ${str} ${n}))"
227
229
  llvm """
@@ -229,3 +231,31 @@ extern pure def unsafeCharAt(str: String, n: Int): Char =
229
231
  ret %Int %x
230
232
  """
231
233
  vm "string::unsafeCharAt(String, Int)"
234
+
235
+
236
+ // Streaming
237
+ // ---------
238
+
239
+ def feed[R](string: String) { reader: () => R / read[Char] } =
240
+ feed(string.fromString) {
241
+ decodeUTF8 {
242
+ reader()
243
+ }
244
+ }
245
+
246
+ def each(string: String): Unit / emit[Char] =
247
+ feed(string) {
248
+ exhaustively {
249
+ do emit[Char](do read[Char]())
250
+ }
251
+ }
252
+
253
+ def collect { stream: () => Unit / emit[Char] }: String =
254
+ returning::collect[Unit]{stream}.second
255
+
256
+ namespace returning {
257
+ def collect[R] { stream: () => R / emit[Char] }: (R, String) = {
258
+ val (result, bytes) = bytearray::returning::collect[R] { encodeUTF8 { stream } }
259
+ (result, bytes.toString)
260
+ }
261
+ }
@@ -1,5 +1,6 @@
1
1
  module stringbuffer
2
2
 
3
+ import effekt
3
4
  import bytearray
4
5
 
5
6
  interface StringBuffer {