@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
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
|
|
8
|
+
extern def cons[A](el: A, rest: CSList[A]) at {}: CSList[A] =
|
|
9
9
|
chez "(cons ${el} ${rest})"
|
|
10
10
|
|
|
11
|
-
extern
|
|
11
|
+
extern def nil[A]() at {}: CSList[A] =
|
|
12
12
|
chez "(list)"
|
|
13
13
|
|
|
14
|
-
extern
|
|
14
|
+
extern def isEmpty[A](l: CSList[A]) at {}: Bool =
|
|
15
15
|
chez "(null? ${l})"
|
|
16
16
|
|
|
17
17
|
// unsafe!
|
|
18
|
-
extern
|
|
18
|
+
extern def head[A](l: CSList[A]) at {}: A =
|
|
19
19
|
chez "(car ${l})"
|
|
20
20
|
|
|
21
21
|
// unsafe!
|
|
22
|
-
extern
|
|
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
|
|
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
|
|
6
|
+
extern def emptyDict[K, V]() at io: Dict[K, V] =
|
|
7
7
|
chez "(make-eq-hashtable)"
|
|
8
8
|
|
|
9
|
-
extern
|
|
9
|
+
extern def emptyWeakDict[K, V]() at io: Dict[K, V] =
|
|
10
10
|
chez "(make-hash-table #t)"
|
|
11
11
|
|
|
12
|
-
extern
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
12
|
+
extern def nativeArgs() at io: Args = js "process.argv.slice(1)"
|
|
13
13
|
|
|
14
|
-
extern
|
|
14
|
+
extern def argCount(args: Args) at io: Int = js "${args}.length"
|
|
15
15
|
|
|
16
|
-
extern
|
|
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
|
|
31
|
+
extern def nativeArgs() at io: Args =
|
|
32
32
|
chez "(cdr (command-line))"
|
|
33
33
|
|
|
34
|
-
extern
|
|
34
|
+
extern def isEmpty(l: Args) at {}: Bool =
|
|
35
35
|
chez "(null? ${l})"
|
|
36
36
|
|
|
37
|
-
extern
|
|
37
|
+
extern def head(l: Args) at {}: String =
|
|
38
38
|
chez "(car ${l})"
|
|
39
39
|
|
|
40
|
-
extern
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
+
}
|