@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
package/bin/effekt CHANGED
Binary file
@@ -5,21 +5,21 @@ import array
5
5
 
6
6
  // a chez scheme cons list
7
7
  extern type CSList[A]
8
- extern pure def cons[A](el: A, rest: CSList[A]): CSList[A] =
8
+ extern def cons[A](el: A, rest: CSList[A]) at {}: CSList[A] =
9
9
  chez "(cons ${el} ${rest})"
10
10
 
11
- extern pure def nil[A](): CSList[A] =
11
+ extern def nil[A]() at {}: CSList[A] =
12
12
  chez "(list)"
13
13
 
14
- extern pure def isEmpty[A](l: CSList[A]): Bool =
14
+ extern def isEmpty[A](l: CSList[A]) at {}: Bool =
15
15
  chez "(null? ${l})"
16
16
 
17
17
  // unsafe!
18
- extern pure def head[A](l: CSList[A]): A =
18
+ extern def head[A](l: CSList[A]) at {}: A =
19
19
  chez "(car ${l})"
20
20
 
21
21
  // unsafe!
22
- extern pure def tail[A](l: CSList[A]): CSList[A] =
22
+ extern def tail[A](l: CSList[A]) at {}: CSList[A] =
23
23
  chez "(cdr ${l})"
24
24
 
25
25
 
@@ -33,5 +33,5 @@ def fromChez[A](l: CSList[A]): List[A] =
33
33
 
34
34
  // Old From Chez
35
35
  // -------------
36
- extern pure def toArray[A](l: CSList[A]): Array[A] =
36
+ extern def toArray[A](l: CSList[A]) at {}: Array[A] =
37
37
  chez "(list->vector ${l})"
@@ -3,25 +3,25 @@ module mutable/dict
3
3
  // https://cisco.github.io/ChezScheme/csug9.5/compat.html#g148
4
4
  extern type Dict[K, V]
5
5
 
6
- extern io def emptyDict[K, V](): Dict[K, V] =
6
+ extern def emptyDict[K, V]() at io: Dict[K, V] =
7
7
  chez "(make-eq-hashtable)"
8
8
 
9
- extern io def emptyWeakDict[K, V](): Dict[K, V] =
9
+ extern def emptyWeakDict[K, V]() at io: Dict[K, V] =
10
10
  chez "(make-hash-table #t)"
11
11
 
12
- extern io def put[K, V](d: Dict[K, V], key: K, value: V): Unit =
12
+ extern def put[K, V](d: Dict[K, V], key: K, value: V) at io: Unit =
13
13
  chez "(put-hash-table! ${d} ${key} ${value})"
14
14
 
15
- extern io def get[K, V](d: Dict[K, V], key: K, default: V): V =
15
+ extern def get[K, V](d: Dict[K, V], key: K, default: V) at io: V =
16
16
  chez "(hashtable-ref ${d} ${key} ${default})"
17
17
 
18
- extern io def unsafeGet[K, V](d: Dict[K, V], key: K): V =
18
+ extern def unsafeGet[K, V](d: Dict[K, V], key: K) at io: V =
19
19
  chez "(hashtable-ref ${d} ${key} #f)"
20
20
 
21
- extern io def contains[K, V](d: Dict[K, V], key: K): Bool =
21
+ extern def contains[K, V](d: Dict[K, V], key: K) at io: Bool =
22
22
  chez "(hashtable-contains? ${d} ${key})"
23
23
 
24
- extern io def remove[K, V](d: Dict[K, V], key: K): Unit =
24
+ extern def remove[K, V](d: Dict[K, V], key: K) at io: Unit =
25
25
  chez "(remove-hash-table! ${d} ${key})"
26
26
 
27
27
  def get[K, V](d: Dict[K, V], key: K): Option[V] =
@@ -1,6 +1,6 @@
1
1
  module args
2
2
 
3
- extern io def commandLineArgs(): List[String] =
3
+ extern def commandLineArgs() at io: List[String] =
4
4
  js { js::commandLineArgs() }
5
5
  chez { chez::commandLineArgs() }
6
6
  llvm { llvm::commandLineArgs() }
@@ -9,11 +9,11 @@ extern io def commandLineArgs(): List[String] =
9
9
  namespace js {
10
10
  extern type Args // = Array[String]
11
11
 
12
- extern io def nativeArgs(): Args = js "process.argv.slice(1)"
12
+ extern def nativeArgs() at io: Args = js "process.argv.slice(1)"
13
13
 
14
- extern io def argCount(args: Args): Int = js "${args}.length"
14
+ extern def argCount(args: Args) at io: Int = js "${args}.length"
15
15
 
16
- extern io def argument(args: Args, i: Int): String = js "${args}[${i}]"
16
+ extern def argument(args: Args, i: Int) at io: String = js "${args}[${i}]"
17
17
 
18
18
  def commandLineArgs(): List[String] = {
19
19
  def toList(args: Args, start: Int, acc: List[String]): List[String] =
@@ -28,16 +28,16 @@ namespace js {
28
28
  namespace chez {
29
29
  extern type Args // = CSList[String]
30
30
 
31
- extern io def nativeArgs(): Args =
31
+ extern def nativeArgs() at io: Args =
32
32
  chez "(cdr (command-line))"
33
33
 
34
- extern pure def isEmpty(l: Args): Bool =
34
+ extern def isEmpty(l: Args) at {}: Bool =
35
35
  chez "(null? ${l})"
36
36
 
37
- extern pure def head(l: Args): String =
37
+ extern def head(l: Args) at {}: String =
38
38
  chez "(car ${l})"
39
39
 
40
- extern pure def tail(l: Args): Args =
40
+ extern def tail(l: Args) at {}: Args =
41
41
  chez "(cdr ${l})"
42
42
 
43
43
  def toList(l: Args): List[String] =
@@ -47,13 +47,13 @@ namespace chez {
47
47
  }
48
48
 
49
49
  namespace llvm {
50
- extern io def argCount(): Int =
50
+ extern def argCount() at io: Int =
51
51
  llvm """
52
52
  %c = call %Int @c_get_argc()
53
53
  ret %Int %c
54
54
  """
55
55
 
56
- extern io def argument(i: Int): String =
56
+ extern def argument(i: Int) at io: String =
57
57
  llvm """
58
58
  %s = call %Pos @c_get_arg(%Int ${i})
59
59
  ret %Pos %s
@@ -4,6 +4,7 @@ import effekt
4
4
  import exception
5
5
  import list
6
6
  import option
7
+ import stream
7
8
 
8
9
  /// A mutable 0-indexed fixed-sized array.
9
10
  extern type Array[T]
@@ -42,7 +43,7 @@ exit:
42
43
 
43
44
  /// Allocates a new array of size `size`, keeping its values _undefined_.
44
45
  /// Prefer using `array` constructor instead to ensure that values are defined.
45
- extern global def allocate[T](size: Int): Array[T] =
46
+ extern def allocate[T](size: Int) at global: Array[T] =
46
47
  js "(new Array(${size}))"
47
48
  chez "(make-vector ${size})" // creates an array filled with 0s on CS
48
49
  llvm """
@@ -85,7 +86,7 @@ def fromList[T](list: List[T]): Array[T] = {
85
86
  }
86
87
 
87
88
  /// Gets the length of the array in constant time.
88
- extern pure def size[T](arr: Array[T]): Int =
89
+ extern def size[T](arr: Array[T]) at {}: Int =
89
90
  js "${arr}.length"
90
91
  chez "(vector-length ${arr})"
91
92
  llvm """
@@ -102,7 +103,7 @@ extern pure def size[T](arr: Array[T]): Int =
102
103
  /// Unchecked Precondition: `index` is in bounds (0 ≤ index < arr.size)
103
104
  ///
104
105
  /// Prefer using `get` instead.
105
- extern global def unsafeGet[T](arr: Array[T], index: Int): T =
106
+ extern def unsafeGet[T](arr: Array[T], index: Int) at global: T =
106
107
  js "${arr}[${index}]"
107
108
  chez "(vector-ref ${arr} ${index})"
108
109
  llvm """
@@ -129,7 +130,7 @@ extern js """
129
130
  /// Unchecked Precondition: `index` is in bounds (0 ≤ index < arr.size)
130
131
  ///
131
132
  /// Prefer using `set` instead.
132
- extern global def unsafeSet[T](arr: Array[T], index: Int, value: T): Unit =
133
+ extern def unsafeSet[T](arr: Array[T], index: Int, value: T) at global: Unit =
133
134
  js "array$set(${arr}, ${index}, ${value})"
134
135
  chez "(begin (vector-set! ${arr} ${index} ${value}) #f)"
135
136
  llvm """
@@ -641,3 +642,55 @@ def println(l: Array[Int]): Unit = println(show(l))
641
642
  def println(l: Array[Double]): Unit = println(show(l))
642
643
  def println(l: Array[Bool]): Unit = println(show(l))
643
644
  def println(l: Array[String]): Unit = println(show(l))
645
+
646
+
647
+ // Streaming
648
+ // ---------
649
+
650
+ /// Turns an `array` into a producer of a push stream
651
+ /// by emitting each contained value from 0 to length - 1.
652
+ def each[T](array: Array[T]): Unit / emit[T] = {
653
+ val n = array.size
654
+ def go(i: Int): Unit = {
655
+ if (i < n) {
656
+ do emit(array.unsafeGet(i))
657
+ go(i + 1)
658
+ }
659
+ }
660
+ go(0)
661
+ }
662
+
663
+ def feed[T, R](array: Array[T]) { reader: () => R / read[T] }: R = {
664
+ var i = 0
665
+ try {
666
+ reader()
667
+ } with read[T] {
668
+ resume {
669
+ if (i < array.size) {
670
+ val c = i
671
+ i = c + 1
672
+ array.unsafeGet(c)
673
+ } else {
674
+ do stop()
675
+ }
676
+ }
677
+ }
678
+ }
679
+
680
+ def collect[A] { stream: () => Unit / emit[A] }: Array[A] =
681
+ returning::collect[A, Unit]{stream}.second
682
+
683
+ namespace returning {
684
+ def collect[A, R] { stream: () => R / emit[A] }: (R, Array[A]) = {
685
+ var i = 0
686
+ var a = allocate(1)
687
+ try {
688
+ (stream(), a.resize(i))
689
+ } with emit[A] { (v) =>
690
+ if (i >= a.size) { a = a.resize(2 * a.size) }
691
+ a.unsafeSet(i, v)
692
+ i = i + 1
693
+ resume(())
694
+ }
695
+ }
696
+ }
@@ -7,7 +7,7 @@ type Nanos = Int
7
7
  /// The actual precision varies across the different backends.
8
8
  /// - js: Milliseconds
9
9
  /// - chez: Microseconds
10
- extern io def timestamp(): Nanos =
10
+ extern def timestamp() at io: Nanos =
11
11
  js "Date.now() * 1000000"
12
12
  chez "(timestamp)"
13
13
  llvm """
@@ -34,7 +34,7 @@ extern jsNode """
34
34
  ///
35
35
  /// This timestamp should only be used for **relative** measurements,
36
36
  /// as gives no guarantees on the absolute time (unlike a UNIX timestamp).
37
- extern io def relativeTimestamp(): Nanos =
37
+ extern def relativeTimestamp() at io: Nanos =
38
38
  js "Math.round(performance.now() * 1000000)"
39
39
  default { timestamp() }
40
40
 
@@ -27,11 +27,11 @@ interface Buffer[T] {
27
27
  def write(el: T): Unit / Exception[BufferOverflow]
28
28
  }
29
29
 
30
- def emptyBuffer[T](capacity: Int): Buffer[T] at {global} = {
30
+ def emptyBuffer[T](capacity: Int): Buffer[T] at global = {
31
31
  if (capacity == 1) refBuffer()
32
32
  else arrayBuffer(capacity)
33
33
  }
34
- def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
34
+ def arrayBuffer[T](initialCapacity: Int): Buffer[T] at global = {
35
35
  // TODO allocate buffer (and array) into a region r.
36
36
  val contents = array::allocate[T](initialCapacity)
37
37
  val head = ref(0)
@@ -62,11 +62,11 @@ def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
62
62
  tail.set(mod(tail.get + 1, initialCapacity))
63
63
  }
64
64
  }
65
- buffer
65
+ box buffer
66
66
  }
67
- def refBuffer[T](): Buffer[T] at {global} = {
67
+ def refBuffer[T](): Buffer[T] at global = {
68
68
  val content = ref(None())
69
- new Buffer[T] {
69
+ box new Buffer[T] {
70
70
  def capacity() = if (content.get.isEmpty) 1 else 0
71
71
  def full?() = content.get.isDefined
72
72
  def empty?() = isEmpty(content.get)
@@ -1,5 +1,8 @@
1
1
  module bytearray
2
2
 
3
+ import effekt
4
+ import stream
5
+
3
6
  /**
4
7
  * A memory managed, mutable, fixed-length array of bytes.
5
8
  *
@@ -20,7 +23,7 @@ extern type ByteArray
20
23
  // = chez "bytevector"
21
24
 
22
25
  /// Allocates a new bytearray with the given `size`, its values are undefined.
23
- extern global def allocate(size: Int): ByteArray =
26
+ extern def allocate(size: Int) at global: ByteArray =
24
27
  js "(new Uint8Array(${size}))"
25
28
  llvm """
26
29
  %object_size = add i64 ${size}, 16
@@ -33,8 +36,9 @@ extern global def allocate(size: Int): ByteArray =
33
36
  ret %Pos %ret_object2
34
37
  """
35
38
  chez "(make-bytevector ${size})"
39
+ vm "bytearray::allocate(Int)"
36
40
 
37
- extern pure def size(arr: ByteArray): Int =
41
+ extern def size(arr: ByteArray) at {}: Int =
38
42
  js "${arr}.length"
39
43
  llvm """
40
44
  %size = extractvalue %Pos ${arr}, 0
@@ -42,8 +46,9 @@ extern pure def size(arr: ByteArray): Int =
42
46
  ret i64 %size
43
47
  """
44
48
  chez "(bytevector-length ${arr})"
49
+ vm "bytearray::size(ByteArray)"
45
50
 
46
- extern global def unsafeGet(arr: ByteArray, index: Int): Byte =
51
+ extern def unsafeGet(arr: ByteArray, index: Int) at global: Byte =
47
52
  js "(${arr})[${index}]"
48
53
  llvm """
49
54
  %arr_ptr = extractvalue %Pos ${arr}, 1
@@ -54,8 +59,9 @@ extern global def unsafeGet(arr: ByteArray, index: Int): Byte =
54
59
  ret i8 %element
55
60
  """
56
61
  chez "(bytevector-u8-ref ${arr} ${index})"
62
+ vm "bytearray::unsafeGet(ByteArray, Int)"
57
63
 
58
- extern global def unsafeSet(arr: ByteArray, index: Int, value: Byte): Unit =
64
+ extern def unsafeSet(arr: ByteArray, index: Int, value: Byte) at global: Unit =
59
65
  js "bytearray$set(${arr}, ${index}, ${value})"
60
66
  llvm """
61
67
  %arr_ptr = extractvalue %Pos ${arr}, 1
@@ -66,6 +72,7 @@ extern global def unsafeSet(arr: ByteArray, index: Int, value: Byte): Unit =
66
72
  ret %Pos zeroinitializer
67
73
  """
68
74
  chez "(bytevector-u8-set! ${arr} ${index} ${value})"
75
+ vm "bytearray::unsafeSet(ByteArray, Int, Byte)"
69
76
 
70
77
  /// Creates a new ByteArray of size `size` filled with the value `init`
71
78
  def bytearray(size: Int, init: Byte): ByteArray = {
@@ -109,20 +116,22 @@ def foreachIndex(arr: ByteArray){ action: (Int, Byte) {Control} => Unit }: Unit
109
116
  action(i, arr.unsafeGet(i)) {label}
110
117
  }
111
118
 
112
- extern pure def fromString(str: String): ByteArray =
119
+ extern def fromString(str: String) at {}: ByteArray =
113
120
  js "(new TextEncoder().encode(${str}))"
114
121
  llvm """
115
122
  ret %Pos ${str}
116
123
  """
117
124
  chez "(string->utf8 ${str})"
125
+ vm "bytearray::fromString(String)"
118
126
 
119
- extern pure def toString(arr: ByteArray): String =
127
+ extern def toString(arr: ByteArray) at {}: String =
120
128
  js "(new TextDecoder('utf-8').decode(${arr}))"
121
129
  // assuming the buffer is already in UTF-8
122
130
  llvm """
123
131
  ret %Pos ${arr}
124
132
  """
125
133
  chez "(utf8->string ${arr})"
134
+ vm "bytearray::toString(ByteArray)"
126
135
 
127
136
  extern js """
128
137
  function bytearray$set(bytes, index, value) {
@@ -169,13 +178,14 @@ extern chez """
169
178
  [else (loop (+ i 1))]))))
170
179
  """
171
180
 
172
- extern pure def compareByteArrayImpl(b1: ByteArray, b2: ByteArray): Int =
181
+ extern def compareByteArrayImpl(b1: ByteArray, b2: ByteArray) at {}: Int =
173
182
  js "bytearray$compare(${b1}, ${b2})"
174
183
  llvm """
175
184
  %x = call %Int @c_bytearray_compare(%Pos ${b1}, %Pos ${b2})
176
185
  ret %Int %x
177
186
  """
178
187
  chez "(bytearray$compare ${b1} ${b2})"
188
+ vm "bytearray::compare(ByteArray, ByteArray)"
179
189
 
180
190
  def compareByteArray(b1: ByteArray, b2: ByteArray): Ordering = {
181
191
  val ret = compareByteArrayImpl(b1, b2)
@@ -193,3 +203,55 @@ def compareStringBytes(left: String, right: String): Ordering = {
193
203
  val r = right.fromString
194
204
  compareByteArray(l, r)
195
205
  }
206
+
207
+
208
+ // Streaming
209
+ // ---------
210
+
211
+ /// Turns `bytes` into a producer of a push stream
212
+ /// by emitting each contained value from 0 to length - 1.
213
+ def each(bytes: ByteArray): Unit / emit[Byte] = {
214
+ val n = bytes.size
215
+ def go(i: Int): Unit = {
216
+ if (i < n) {
217
+ do emit(bytes.unsafeGet(i))
218
+ go(i + 1)
219
+ }
220
+ }
221
+ go(0)
222
+ }
223
+
224
+ def feed[R](bytes: ByteArray) { reader: () => R / read[Byte] }: R = {
225
+ var i = 0
226
+ try {
227
+ reader()
228
+ } with read[Byte] {
229
+ resume {
230
+ if (i < bytes.size) {
231
+ val c = i
232
+ i = c + 1
233
+ bytes.unsafeGet(c)
234
+ } else {
235
+ do stop()
236
+ }
237
+ }
238
+ }
239
+ }
240
+
241
+ def collect { stream: () => Unit / emit[Byte] }: ByteArray =
242
+ returning::collect[Unit]{stream}.second
243
+
244
+ namespace returning {
245
+ def collect[R] { stream: () => R / emit[Byte] }: (R, ByteArray) = {
246
+ var i = 0
247
+ var a = allocate(1)
248
+ try {
249
+ (stream(), a.resize(i))
250
+ } with emit[Byte] { (v) =>
251
+ if (i >= a.size) { a = a.resize(2 * a.size) }
252
+ a.unsafeSet(i, v)
253
+ i = i + 1
254
+ resume(())
255
+ }
256
+ }
257
+ }
@@ -5,6 +5,7 @@ import effekt
5
5
  import exception
6
6
  import option
7
7
  import result
8
+ import stream
8
9
 
9
10
 
10
11
  /// Checks if the given character is an ASCII whitespace
@@ -86,7 +87,7 @@ def isAlphabetic(c: Char): Bool = isLower(c) || isUpper(c)
86
87
  // Chez: ?
87
88
  // LLVM: i64 representing utf-8 (varying length 1-4 bytes)
88
89
 
89
- extern pure def toString(ch: Char): String =
90
+ extern def toString(ch: Char) at {}: String =
90
91
  js "String.fromCodePoint(${ch})"
91
92
  chez "(string (integer->char ${ch}))"
92
93
  llvm """
@@ -95,20 +96,20 @@ extern pure def toString(ch: Char): String =
95
96
  """
96
97
 
97
98
  // Since we currently represent Char by integers in all backends, we could reuse comparison
98
- extern pure def toInt(ch: Char): Int =
99
+ extern def toInt(ch: Char) at {}: Int =
99
100
  js "${ch}"
100
101
  chez "${ch}"
101
102
  llvm "ret %Int ${ch}"
102
103
  vm "string::toInt(Char)"
103
104
 
104
- extern pure def toChar(codepoint: Int): Char =
105
+ extern def toChar(codepoint: Int) at {}: Char =
105
106
  js "${codepoint}"
106
107
  chez "${codepoint}"
107
108
  llvm "ret %Int ${codepoint}"
108
109
  vm "string::toChar(Int)"
109
110
 
110
111
 
111
- extern pure def infixLt(x: Char, y: Char): Bool =
112
+ extern def infixLt(x: Char, y: Char) at {}: Bool =
112
113
  js "(${x} < ${y})"
113
114
  chez "(< ${x} ${y})"
114
115
  llvm """
@@ -119,7 +120,7 @@ extern pure def infixLt(x: Char, y: Char): Bool =
119
120
  """
120
121
  vm "string::infixLt(Char, Char)"
121
122
 
122
- extern pure def infixLte(x: Char, y: Char): Bool =
123
+ extern def infixLte(x: Char, y: Char) at {}: Bool =
123
124
  js "(${x} <= ${y})"
124
125
  chez "(<= ${x} ${y})"
125
126
  llvm """
@@ -130,7 +131,7 @@ extern pure def infixLte(x: Char, y: Char): Bool =
130
131
  """
131
132
  vm "string::infixLte(Char, Char)"
132
133
 
133
- extern pure def infixGt(x: Char, y: Char): Bool =
134
+ extern def infixGt(x: Char, y: Char) at {}: Bool =
134
135
  js "(${x} > ${y})"
135
136
  chez "(> ${x} ${y})"
136
137
  llvm """
@@ -141,7 +142,7 @@ extern pure def infixGt(x: Char, y: Char): Bool =
141
142
  """
142
143
  vm "string::infixGt(Char, Char)"
143
144
 
144
- extern pure def infixGte(x: Char, y: Char): Bool =
145
+ extern def infixGte(x: Char, y: Char) at {}: Bool =
145
146
  js "(${x} >= ${y})"
146
147
  chez "(>= ${x} ${y})"
147
148
  llvm """
@@ -172,6 +173,92 @@ def utf16UnitCount(codepoint: Char): Int = codepoint match {
172
173
  case c => panic("Not a valid code point")
173
174
  }
174
175
 
175
- extern pure def charWidth(c: Char): Int =
176
+ extern def charWidth(c: Char) at {}: Int =
176
177
  // JavaScript strings are UTF-16 where every unicode character after 0xffff takes two units
177
178
  js "(${c} > 0xffff) ? 2 : 1"
179
+
180
+
181
+ def writeLine { body: () => Unit / emit[Char] }: Unit / emit[Char] =
182
+ returning::writeLine[Unit]{body}
183
+
184
+ def readLine { body: () => Unit / read[Char] }: Unit / {read[Char], stop} =
185
+ returning::readLine[Unit]{body}
186
+
187
+ namespace returning {
188
+ def writeLine[R] { body: () => R / emit[Char] }: R / emit[Char] = {
189
+ val result = body()
190
+ do emit('\n')
191
+ return result
192
+ }
193
+
194
+ def readLine[R] { body: () => R / read[Char] }: R / {read[Char], stop} = {
195
+ var stopped = false
196
+ try {
197
+ body()
198
+ } with read[Char] {
199
+ if(stopped){
200
+ resume { do stop() }
201
+ } else {
202
+ do read[Char] match {
203
+ case '\n' => stopped = true; resume { do stop() }
204
+ case char => resume { return char }
205
+ }
206
+ }
207
+ }
208
+ }
209
+ }
210
+
211
+ def decodeChar(): Char / {read[Byte], stop} = {
212
+ val b = do read().toInt
213
+ if (b < 128) {
214
+ b.toChar
215
+ } else if (b < 224) {
216
+ val part1 = bitwiseShl(bitwiseAnd(b, 31), 6)
217
+ val part2 = bitwiseAnd(do read().toInt, 63)
218
+ bitwiseOr(part1, part2).toChar
219
+ } else if (b < 240) {
220
+ val part1 = bitwiseShl(bitwiseAnd(b, 15), 12)
221
+ val part2 = bitwiseShl(bitwiseAnd(do read().toInt, 63), 6)
222
+ val part3 = bitwiseAnd(do read().toInt, 63)
223
+ bitwiseOr(bitwiseOr(part1, part2), part3).toChar
224
+ } else {
225
+ val part1 = bitwiseShl(bitwiseAnd(b, 7), 18)
226
+ val part2 = bitwiseShl(bitwiseAnd(do read().toInt, 63), 12)
227
+ val part3 = bitwiseShl(bitwiseAnd(do read().toInt, 63), 6)
228
+ val part4 = bitwiseAnd(do read().toInt, 63)
229
+ bitwiseOr(bitwiseOr(bitwiseOr(part1, part2), part3), part4).toChar
230
+ }
231
+ }
232
+
233
+ def encodeChar(char: Char): Unit / emit[Byte] = {
234
+ val code = char.toInt
235
+ if (code < 128) {
236
+ do emit(code.toByte)
237
+ } else if (code < 2048) {
238
+ do emit(bitwiseOr(192, bitwiseShr(code, 6)).toByte)
239
+ do emit(bitwiseOr(128, bitwiseAnd(code, 63)).toByte)
240
+ } else if (code < 65536) {
241
+ do emit(bitwiseOr(224, bitwiseShr(code, 12)).toByte)
242
+ do emit(bitwiseOr(128, bitwiseAnd(bitwiseShr(code, 6), 63)).toByte)
243
+ do emit(bitwiseOr(128, bitwiseAnd(code, 63)).toByte)
244
+ } else {
245
+ do emit(bitwiseOr(240, bitwiseShr(code, 18)).toByte)
246
+ do emit(bitwiseOr(128, bitwiseAnd(bitwiseShr(code, 12), 63)).toByte)
247
+ do emit(bitwiseOr(128, bitwiseAnd(bitwiseShr(code, 6), 63)).toByte)
248
+ do emit(bitwiseOr(128, bitwiseAnd(code, 63)).toByte)
249
+ }
250
+ }
251
+
252
+ def decodeUTF8[R] { reader: () => R / read[Char] }: R / read[Byte] =
253
+ try {
254
+ reader()
255
+ } with read[Char] {
256
+ resume { decodeChar() }
257
+ }
258
+
259
+ def encodeUTF8[R] { stream: () => R / emit[Char] }: R / emit[Byte] =
260
+ try {
261
+ stream()
262
+ } with emit[Char] { char =>
263
+ resume(encodeChar(char))
264
+ }