@effekt-lang/effekt 0.54.0 → 0.56.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/effekt_primitives.ss +10 -4
- package/libraries/common/dictionary.effekt +133 -0
- package/libraries/common/json.effekt +19 -19
- package/libraries/common/list.effekt +16 -0
- package/libraries/common/option.effekt +1 -1
- package/libraries/common/process.effekt +99 -0
- package/libraries/common/random.effekt +1 -1
- package/libraries/common/stream.effekt +109 -4
- package/libraries/common/union_find.effekt +7 -7
- package/libraries/js/effekt_builtins.js +1 -1
- package/libraries/llvm/forward-declare-c.ll +2 -2
- package/libraries/llvm/io.c +197 -2
- package/libraries/llvm/panic.c +2 -0
- package/libraries/llvm/rts.ll +16 -38
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +3 -3
- package/licenses/mit license - mit-license.html +96 -64
- package/licenses/the bsd license - bsd-license.html +96 -64
- package/licenses/the mit license - mit.html +95 -63
- package/package.json +1 -1
package/bin/effekt
CHANGED
|
Binary file
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
(cond
|
|
71
71
|
; Same object reference (fast path)
|
|
72
72
|
[(eq? obj1 obj2) #t]
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
; If both are records, compare them structurally
|
|
75
75
|
[(and (record? obj1) (record? obj2))
|
|
76
76
|
(let* ([rtd1 (record-rtd obj1)]
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
(if (not (equal_impl field1 field2))
|
|
88
88
|
(set! result #f)))))
|
|
89
89
|
#f))]
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
; For lists, compare elements recursively
|
|
92
92
|
[(and (list? obj1) (list? obj2))
|
|
93
93
|
(and (= (length obj1) (length obj2))
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
(or (null? l1)
|
|
96
96
|
(and (equal_impl (car l1) (car l2))
|
|
97
97
|
(loop (cdr l1) (cdr l2))))))]
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
; For all other types, use Scheme's built-in equal?
|
|
100
100
|
[else (equal? obj1 obj2)]))
|
|
101
101
|
|
|
@@ -134,4 +134,10 @@
|
|
|
134
134
|
(raise
|
|
135
135
|
(condition
|
|
136
136
|
(make-error)
|
|
137
|
-
(make-message-condition (string-append pos " not implemented yet" )))))
|
|
137
|
+
(make-message-condition (string-append pos " not implemented yet" )))))
|
|
138
|
+
|
|
139
|
+
(define (unreachable)
|
|
140
|
+
(raise
|
|
141
|
+
(condition
|
|
142
|
+
(make-error)
|
|
143
|
+
(make-message-condition ("should not be reachable!")))))
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
module dictionary
|
|
2
|
+
|
|
3
|
+
import array
|
|
4
|
+
import bytearray
|
|
5
|
+
import stream
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/// A trie-based dictionary for efficient key-value storage with bytearray keys.
|
|
9
|
+
/// Supports String and ByteArray keys with fast lookup and insertion.
|
|
10
|
+
type Dictionary[A] {
|
|
11
|
+
Empty()
|
|
12
|
+
Leaf(value: A)
|
|
13
|
+
Branch(children: Array[Dictionary[A]])
|
|
14
|
+
Node(value: A, children: Array[Dictionary[A]])
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/// Insert a value with a String key.
|
|
18
|
+
def insert[A](trie: Dictionary[A], key: String, value: A): Dictionary[A] =
|
|
19
|
+
insert(trie, key.fromString, value)
|
|
20
|
+
|
|
21
|
+
/// Insert a key-value pair into the dictionary with a ByteArray key.
|
|
22
|
+
def insert[A](trie: Dictionary[A], key: ByteArray, value: A): Dictionary[A] = {
|
|
23
|
+
def go(trie: Dictionary[A], i: Int): Dictionary[A] =
|
|
24
|
+
if (i < key.size) {
|
|
25
|
+
trie match {
|
|
26
|
+
case Empty() =>
|
|
27
|
+
val child = go(Empty(), i + 1)
|
|
28
|
+
val children = array(256, Empty())
|
|
29
|
+
children.unsafeSet(key.unsafeGet(i).toInt, child)
|
|
30
|
+
Branch(children)
|
|
31
|
+
case Leaf(existing) =>
|
|
32
|
+
val child = go(Empty(), i + 1)
|
|
33
|
+
val children = array(256, Empty())
|
|
34
|
+
children.unsafeSet(key.unsafeGet(i).toInt, child)
|
|
35
|
+
Node(existing, children)
|
|
36
|
+
case Branch(children) =>
|
|
37
|
+
val child = go(children.unsafeGet(key.unsafeGet(i).toInt), i + 1)
|
|
38
|
+
children.unsafeSet(key.unsafeGet(i).toInt, child)
|
|
39
|
+
Branch(children)
|
|
40
|
+
case Node(existing, children) =>
|
|
41
|
+
val child = go(children.unsafeGet(key.unsafeGet(i).toInt), i + 1)
|
|
42
|
+
children.unsafeSet(key.unsafeGet(i).toInt, child)
|
|
43
|
+
Node(existing, children)
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
trie match {
|
|
47
|
+
case Empty() => Leaf(value)
|
|
48
|
+
case Leaf(_) => Leaf(value)
|
|
49
|
+
case Node(_, children) => Node(value, children)
|
|
50
|
+
case Branch(children) => Node(value, children)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
go(trie, 0)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// Look up a value in the dictionary by string key.
|
|
57
|
+
def lookup[A](trie: Dictionary[A], key: String): Option[A] =
|
|
58
|
+
lookup(trie, key.fromString)
|
|
59
|
+
|
|
60
|
+
/// Look up a value in the dictionary by bytearray key.
|
|
61
|
+
def lookup[A](trie: Dictionary[A], key: ByteArray): Option[A] = {
|
|
62
|
+
def go(trie: Dictionary[A], i: Int): Option[A] =
|
|
63
|
+
if (i < key.size) {
|
|
64
|
+
trie match {
|
|
65
|
+
case Empty() => None()
|
|
66
|
+
case Leaf(_) => None()
|
|
67
|
+
case Branch(children) => go(children.unsafeGet(key.unsafeGet(i).toInt), i + 1)
|
|
68
|
+
case Node(_, children) => go(children.unsafeGet(key.unsafeGet(i).toInt), i + 1)
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
trie match {
|
|
72
|
+
case Empty() => None()
|
|
73
|
+
case Leaf(value) => Some(value)
|
|
74
|
+
case Branch(_) => None()
|
|
75
|
+
case Node(value, _) => Some(value)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
go(trie, 0)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// A record pairing a key with a value for use in streams.
|
|
82
|
+
record Tagged[A](key: ByteArray, value: A)
|
|
83
|
+
|
|
84
|
+
/// Collect key-value pairs from a stream of Tagged elements into a Dictionary.
|
|
85
|
+
def collect[A] { stream: () => Unit / emit[Tagged[A]] }: Dictionary[A] = {
|
|
86
|
+
var trie = Empty()
|
|
87
|
+
for[Tagged[A]] { stream() } { case Tagged(bytes, value) =>
|
|
88
|
+
trie = trie.insert(bytes, value)
|
|
89
|
+
}
|
|
90
|
+
return trie
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// Emit all values stored in the dictionary as a push stream.
|
|
94
|
+
def each[A](trie: Dictionary[A]): Unit / emit[A] =
|
|
95
|
+
trie match {
|
|
96
|
+
case Empty() => ()
|
|
97
|
+
case Leaf(value) => do emit(value)
|
|
98
|
+
case Branch(children) =>
|
|
99
|
+
each(0, children.size) { i =>
|
|
100
|
+
each(children.unsafeGet(i))
|
|
101
|
+
}
|
|
102
|
+
case Node(value, children) =>
|
|
103
|
+
do emit(value)
|
|
104
|
+
each(0, children.size) { i =>
|
|
105
|
+
each(children.unsafeGet(i))
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// Emit all key-value pairs stored in the dictionary as a push stream of Tagged elements.
|
|
110
|
+
def eachTagged[A](trie: Dictionary[A]): Unit / emit[Tagged[A]] = {
|
|
111
|
+
def fromBytesReverse(bytes: List[Byte], n: Int): ByteArray = {
|
|
112
|
+
val key = bytearray::allocate(n)
|
|
113
|
+
foreachIndex(bytes) { (i, b) => key.unsafeSet(n - 1 - i, b) }
|
|
114
|
+
key
|
|
115
|
+
}
|
|
116
|
+
def go(trie: Dictionary[A], key: List[Byte], n: Int): Unit =
|
|
117
|
+
trie match {
|
|
118
|
+
case Empty() => ()
|
|
119
|
+
case Leaf(value) => do emit(Tagged(fromBytesReverse(key, n), value))
|
|
120
|
+
case Branch(children) =>
|
|
121
|
+
each(0, children.size) { i =>
|
|
122
|
+
go(children.unsafeGet(i), Cons(i.toByte, key), n + 1)
|
|
123
|
+
}
|
|
124
|
+
case Node(value, children) =>
|
|
125
|
+
do emit(Tagged(fromBytesReverse(key, n), value))
|
|
126
|
+
each(0, children.size) { i =>
|
|
127
|
+
go(children.unsafeGet(i), Cons(i.toByte, key), n + 1)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
go(trie, Nil(), 0)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
@@ -25,12 +25,12 @@ def handleJsonBuilder[R]{b: JsonBuilder}{ body: => R / JsonBuilder }: R = try bo
|
|
|
25
25
|
def bool(v) = resume(b.bool(v))
|
|
26
26
|
def number(n) = resume(b.number(n))
|
|
27
27
|
def string(s) = resume(b.string(s))
|
|
28
|
-
def list() = resume { {c} => b.list{c} }
|
|
29
|
-
def dict() = resume { {c} => b.dict{c} }
|
|
28
|
+
def list[T]() = resume { {c} => b.list{c} }
|
|
29
|
+
def dict[T]() = resume { {c} => b.dict{c} }
|
|
30
30
|
}
|
|
31
31
|
/// Make explicitly bound JsonObjectBuilder instance implicit
|
|
32
32
|
def handleJsonObjectBuilder[R]{b: JsonObjectBuilder}{ body: => R / JsonObjectBuilder }: R = try body() with JsonObjectBuilder {
|
|
33
|
-
def field(k) = resume { {c} => b.field(k){c} }
|
|
33
|
+
def field[T](k) = resume { {c} => b.field(k){c} }
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
// --------------------------------------------------------------------------------
|
|
@@ -49,8 +49,8 @@ def encodeJsonList[R]{ body: => R / JsonBuilder }: R / emit[String] = {
|
|
|
49
49
|
def bool(v) = { c(); resume(do bool(v)) }
|
|
50
50
|
def number(n) = { c(); resume(do number(n)) }
|
|
51
51
|
def string(s) = { c(); resume(do string(s)) }
|
|
52
|
-
def list() = resume { { b } => c(); do list{b} }
|
|
53
|
-
def dict() = resume { { b } => c(); do dict{b} }
|
|
52
|
+
def list[T]() = resume { { b } => c(); do list{b} }
|
|
53
|
+
def dict[T]() = resume { { b } => c(); do dict{b} }
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
do emit("]")
|
|
@@ -66,7 +66,7 @@ def encodeJsonObject[R]{ body: => R / JsonObjectBuilder }: R / emit[String] = {
|
|
|
66
66
|
}
|
|
67
67
|
val r = encodeJson {
|
|
68
68
|
try body() with JsonObjectBuilder {
|
|
69
|
-
def field(k) = resume { {b} => c(k); b() }
|
|
69
|
+
def field[T](k) = resume { {b} => c(k); b() }
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
do emit("}")
|
|
@@ -80,8 +80,8 @@ def encodeJson[R]{ body: => R / JsonBuilder }: R / emit[String] = {
|
|
|
80
80
|
def bool(v) = { resume(do emit( if(v){ "true" } else { "false" } )) }
|
|
81
81
|
def number(n) = { resume(do emit(show(n))) }
|
|
82
82
|
def string(s) = { resume(do emit(escape(s))) }
|
|
83
|
-
def list() = resume { {b} => encodeJsonList { b() } }
|
|
84
|
-
def dict() = resume { {b} => encodeJsonObject { b() } }
|
|
83
|
+
def list[T]() = resume { {b} => encodeJsonList { b() } }
|
|
84
|
+
def dict[T]() = resume { {b} => encodeJsonObject { b() } }
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -209,7 +209,7 @@ def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} =
|
|
|
209
209
|
// --------------------------------------------------------------------------------
|
|
210
210
|
/// Ignore the generated calls to JsonObjectBuilder
|
|
211
211
|
def ignoreDict[R](){ body: => R / JsonObjectBuilder }: R = try body() with JsonObjectBuilder {
|
|
212
|
-
def field(k) = resume { {v} => ignore{v} }
|
|
212
|
+
def field[T](k) = resume { {v} => ignore{v} }
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
/// Ignore the generated calls to JsonBuilder
|
|
@@ -218,8 +218,8 @@ def ignore[R](){ body: => R / JsonBuilder }: R = try body() with JsonBuilder {
|
|
|
218
218
|
def bool(b) = resume(())
|
|
219
219
|
def null() = resume(())
|
|
220
220
|
def string(s) = resume(())
|
|
221
|
-
def list() = resume { {e} => ignore{e} }
|
|
222
|
-
def dict() = resume { {e} => ignoreDict{e} }
|
|
221
|
+
def list[T]() = resume { {e} => ignore{e} }
|
|
222
|
+
def dict[T]() = resume { {e} => ignoreDict{e} }
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
// --------------------------------------------------------------------------------
|
|
@@ -264,12 +264,12 @@ def build[R](){ body: => R / JsonBuilder }: (R, JsonValue) = {
|
|
|
264
264
|
def bool(b) = { r = Bool(b); resume(()) }
|
|
265
265
|
def null() = { r = Null(); resume(()) }
|
|
266
266
|
def string(s) = { r = String(s); resume(()) }
|
|
267
|
-
def list() = resume { {els} =>
|
|
267
|
+
def list[T]() = resume { {els} =>
|
|
268
268
|
val x = buildList {els}
|
|
269
269
|
r = List(x.second)
|
|
270
270
|
x.first
|
|
271
271
|
}
|
|
272
|
-
def dict() = resume { {fs} =>
|
|
272
|
+
def dict[T]() = resume { {fs} =>
|
|
273
273
|
val x = buildDict {fs}
|
|
274
274
|
r = Dict(x.second)
|
|
275
275
|
x.first
|
|
@@ -283,12 +283,12 @@ def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = list::ret
|
|
|
283
283
|
def bool(b) = { do emit(Bool(b)); resume(()) }
|
|
284
284
|
def null() = { do emit(Null()); resume(()) }
|
|
285
285
|
def string(s) = { do emit(String(s)); resume(()) }
|
|
286
|
-
def list() = resume { {els} =>
|
|
286
|
+
def list[T]() = resume { {els} =>
|
|
287
287
|
val x = buildList {els}
|
|
288
288
|
do emit(List(x.second))
|
|
289
289
|
x.first
|
|
290
290
|
}
|
|
291
|
-
def dict() = resume { {fs} =>
|
|
291
|
+
def dict[T]() = resume { {fs} =>
|
|
292
292
|
val x = buildDict {fs}
|
|
293
293
|
do emit(Dict(x.second))
|
|
294
294
|
x.first
|
|
@@ -297,7 +297,7 @@ def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = list::ret
|
|
|
297
297
|
}
|
|
298
298
|
def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = list::returning::collect[(String, JsonValue), R] {
|
|
299
299
|
try body() with JsonObjectBuilder {
|
|
300
|
-
def field(k) = resume { {v} =>
|
|
300
|
+
def field[T](k) = resume { {v} =>
|
|
301
301
|
val x = build{v}
|
|
302
302
|
do emit((k, x.second))
|
|
303
303
|
x.first
|
|
@@ -325,7 +325,7 @@ namespace test {
|
|
|
325
325
|
with scanner[Char]
|
|
326
326
|
|
|
327
327
|
def k = new JsonObjectBuilder {
|
|
328
|
-
def field(k){v} = { println(k); ignore{v} }
|
|
328
|
+
def field[T](k){v} = { println(k); ignore{v} }
|
|
329
329
|
}
|
|
330
330
|
def jsonBuilder[R] {e: => R / JsonBuilder}: R = {
|
|
331
331
|
def d = new JsonBuilder {
|
|
@@ -333,8 +333,8 @@ namespace test {
|
|
|
333
333
|
def bool(b) = ()
|
|
334
334
|
def null() = println("NULL")
|
|
335
335
|
def string(s) = ()
|
|
336
|
-
def list(){e} = jsonBuilder{e}
|
|
337
|
-
def dict(){e} = handleJsonObjectBuilder{k}{e}
|
|
336
|
+
def list[T](){e} = jsonBuilder{e}
|
|
337
|
+
def dict[T](){e} = handleJsonObjectBuilder{k}{e}
|
|
338
338
|
}
|
|
339
339
|
handleJsonBuilder{d}{e}
|
|
340
340
|
}
|
|
@@ -944,6 +944,22 @@ def each[A](list: List[A]): Unit / emit[A] =
|
|
|
944
944
|
each(tail)
|
|
945
945
|
}
|
|
946
946
|
|
|
947
|
+
/// Push iterator that emits all the tails (suffixes) of the given `list`.
|
|
948
|
+
///
|
|
949
|
+
/// Examples:
|
|
950
|
+
/// ```effekt
|
|
951
|
+
/// > list::collect[List[Int]] { [1, 2, 3].tails }
|
|
952
|
+
/// [[1, 2, 3], [2, 3], [3], []]
|
|
953
|
+
///
|
|
954
|
+
/// > list::collect[List[Int]] { [].tails }
|
|
955
|
+
/// [[]]
|
|
956
|
+
/// ```
|
|
957
|
+
def tails[A](list: List[A]): Unit / emit[List[A]] =
|
|
958
|
+
list match {
|
|
959
|
+
case Nil() => do emit(Nil())
|
|
960
|
+
case Cons(head, tail) => do emit(list); tails(tail)
|
|
961
|
+
}
|
|
962
|
+
|
|
947
963
|
def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
|
|
948
964
|
var l = list
|
|
949
965
|
try {
|
|
@@ -37,7 +37,7 @@ def foreach[A](self: Option[A]) { f: A => Unit }: Unit = self match {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
def value[A](self: Option[A]): A / Exception[MissingValue] = self match {
|
|
40
|
-
case None() => do raise(MissingValue(), "Trying to get the value of an empty option")
|
|
40
|
+
case None() => do raise(MissingValue(), "Trying to get the value of an empty option") match {}
|
|
41
41
|
case Some(v) => v
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
module process
|
|
2
|
+
import array
|
|
2
3
|
|
|
3
4
|
extern def exit(errorCode: Int) at io: Nothing =
|
|
4
5
|
js "(function() { process.exit(${errorCode}) })()"
|
|
@@ -7,3 +8,101 @@ extern def exit(errorCode: Int) at io: Nothing =
|
|
|
7
8
|
ret %Pos zeroinitializer
|
|
8
9
|
"""
|
|
9
10
|
chez "(exit ${errorCode})"
|
|
11
|
+
|
|
12
|
+
// Subprocesses
|
|
13
|
+
// ------------
|
|
14
|
+
|
|
15
|
+
extern jsNode """
|
|
16
|
+
const { spawn } = require('node:child_process');
|
|
17
|
+
"""
|
|
18
|
+
extern llvm """
|
|
19
|
+
declare %Pos @c_spawn_options_default()
|
|
20
|
+
declare %Pos @c_spawn_options_on_stdout(%Pos, %Pos)
|
|
21
|
+
declare %Pos @c_spawn_options_on_stderr(%Pos, %Pos)
|
|
22
|
+
declare %Pos @c_spawn_options_pipe_stdin(%Pos, %Pos)
|
|
23
|
+
declare %Pos @c_write_stream(%Pos, %Pos)
|
|
24
|
+
declare %Pos @c_close_stream(%Pos)
|
|
25
|
+
declare %Pos @c_spawn(%Pos, %Pos, %Pos)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
extern type SpawnOptions
|
|
29
|
+
extern def default() at {}: SpawnOptions =
|
|
30
|
+
jsNode """{ stdio: [0,1,2], onSpawn: (p) => {} }"""
|
|
31
|
+
llvm """
|
|
32
|
+
%opts = call %Pos @c_spawn_options_default()
|
|
33
|
+
ret %Pos %opts
|
|
34
|
+
"""
|
|
35
|
+
extern def onStdout(opts: SpawnOptions, callback: String => Unit at {io, global}) at {}: SpawnOptions =
|
|
36
|
+
jsNode """(function() {
|
|
37
|
+
let old = ${opts};
|
|
38
|
+
old.stdio[1] = 'pipe';
|
|
39
|
+
let oldSpawn = old.onSpawn;
|
|
40
|
+
old.onSpawn = function(p) {
|
|
41
|
+
oldSpawn(p);
|
|
42
|
+
p.stdout.on('data', (chunk) => {
|
|
43
|
+
$effekt.runToplevel((ks, k) => (${callback})(chunk, ks, k));
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
return old; })()"""
|
|
47
|
+
llvm """
|
|
48
|
+
%opts = call %Pos @c_spawn_options_on_stdout(%Pos ${opts}, %Pos ${callback})
|
|
49
|
+
ret %Pos %opts
|
|
50
|
+
"""
|
|
51
|
+
extern def onStderr(opts: SpawnOptions, callback: String => Unit at {io, global}) at {}: SpawnOptions =
|
|
52
|
+
jsNode """(function() {
|
|
53
|
+
let old = ${opts};
|
|
54
|
+
old.stdio[2] = 'pipe';
|
|
55
|
+
let oldSpawn = old.onSpawn;
|
|
56
|
+
old.onSpawn = function(p) {
|
|
57
|
+
oldSpawn(p);
|
|
58
|
+
p.stderr.on('data', (chunk) => {
|
|
59
|
+
$effekt.runToplevel((ks, k) => (${callback})(chunk, ks, k));
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
return old; })()"""
|
|
63
|
+
llvm """
|
|
64
|
+
%opts = call %Pos @c_spawn_options_on_stderr(%Pos ${opts}, %Pos ${callback})
|
|
65
|
+
ret %Pos %opts
|
|
66
|
+
"""
|
|
67
|
+
extern type WritableStream
|
|
68
|
+
extern def withStdin(opts: SpawnOptions, callback: WritableStream => Unit at {io, global}) at {}: SpawnOptions =
|
|
69
|
+
jsNode """(function () {
|
|
70
|
+
let old = ${opts};
|
|
71
|
+
old.stdio[0] = 'pipe'
|
|
72
|
+
let oldSpawn = old.onSpawn;
|
|
73
|
+
old.onSpawn = function(p) {
|
|
74
|
+
oldSpawn(p);
|
|
75
|
+
p.on('spawn', () => {
|
|
76
|
+
$effekt.runToplevel((ks, k) => (${callback})(p.stdin, ks, k));
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
return old;
|
|
80
|
+
})()"""
|
|
81
|
+
llvm """
|
|
82
|
+
%opts = call %Pos @c_spawn_options_pipe_stdin(%Pos ${opts}, %Pos ${callback})
|
|
83
|
+
ret %Pos %opts
|
|
84
|
+
"""
|
|
85
|
+
extern def write(to: WritableStream, s: String) at io: Unit =
|
|
86
|
+
jsNode """${to}.write(${s})"""
|
|
87
|
+
llvm """
|
|
88
|
+
call void @c_write_stream(%Pos ${to}, %Pos ${s})
|
|
89
|
+
ret %Pos zeroinitializer
|
|
90
|
+
"""
|
|
91
|
+
extern def close(to: WritableStream) at io: Unit =
|
|
92
|
+
jsNode """${to}.end()"""
|
|
93
|
+
llvm """
|
|
94
|
+
call void @c_close_stream(%Pos ${to})
|
|
95
|
+
ret %Pos zeroinitializer
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
extern def spawn(cmd: String, args: Array[String], options: SpawnOptions) at async: Int =
|
|
99
|
+
jsNode """$effekt.capture(k => {
|
|
100
|
+
let p = spawn(${cmd}, ${args}, ${options});
|
|
101
|
+
${options}.onSpawn(p);
|
|
102
|
+
p.on('close', (exitcode, sig) => { k(exitcode) });
|
|
103
|
+
return p;
|
|
104
|
+
})"""
|
|
105
|
+
llvm """
|
|
106
|
+
call void @c_spawn(%Pos ${cmd}, %Pos ${args}, %Pos ${options}, %Stack %stack)
|
|
107
|
+
ret void
|
|
108
|
+
"""
|
|
@@ -2,6 +2,7 @@ module stream
|
|
|
2
2
|
|
|
3
3
|
import effekt
|
|
4
4
|
import option
|
|
5
|
+
import exception
|
|
5
6
|
|
|
6
7
|
// Effects
|
|
7
8
|
// -------
|
|
@@ -128,6 +129,26 @@ def iterate[A](initial: A) { step: A => A }: Unit / emit[A] = {
|
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
131
|
|
|
132
|
+
/// Re-emit from the `stream` as long as the `predicate` holds.
|
|
133
|
+
/// Once the predicate fails, the stream ends.
|
|
134
|
+
///
|
|
135
|
+
/// Examples:
|
|
136
|
+
/// ```effekt
|
|
137
|
+
/// > list::collect[Int] { takeWhile[Int] { x => x < 3 } { [1, 2, 3, 4, 1].each } }
|
|
138
|
+
/// [1, 2]
|
|
139
|
+
///
|
|
140
|
+
/// > list::collect[Int] { takeWhile[Int] { x => x < 10 } { [1, 2, 3].each } }
|
|
141
|
+
/// [1, 2, 3]
|
|
142
|
+
///
|
|
143
|
+
/// > list::collect[Int] { takeWhile[Int] { x => x < 0 } { [1, 2, 3].each } }
|
|
144
|
+
/// []
|
|
145
|
+
/// ```
|
|
146
|
+
def takeWhile[A] { predicate: A => Bool } { stream: () => Unit / emit[A] }: Unit / emit[A] =
|
|
147
|
+
try { stream() } with emit[A] {
|
|
148
|
+
case value and predicate(value) => do emit(value); resume(())
|
|
149
|
+
case _ => () // end the stream
|
|
150
|
+
}
|
|
151
|
+
|
|
131
152
|
|
|
132
153
|
def sum { stream: () => Unit / emit[Int] }: Int =
|
|
133
154
|
returning::sum[Unit]{stream}.second
|
|
@@ -135,6 +156,71 @@ def sum { stream: () => Unit / emit[Int] }: Int =
|
|
|
135
156
|
def product { stream: () => Unit / emit[Int] }: Int =
|
|
136
157
|
returning::product[Unit]{stream}.second
|
|
137
158
|
|
|
159
|
+
/// Returns true if all elements in the stream satisfy the given predicate,
|
|
160
|
+
/// false otherwise. Short-circuits as soon as a non-matching element is found.
|
|
161
|
+
///
|
|
162
|
+
/// Examples:
|
|
163
|
+
/// ```effekt
|
|
164
|
+
/// > all[Int] { x => x > 0 } { [1, 2, 3].each }
|
|
165
|
+
/// true
|
|
166
|
+
///
|
|
167
|
+
/// > all[Int] { x => x < 3 } { [1, 2, 3, 4].each }
|
|
168
|
+
/// false
|
|
169
|
+
///
|
|
170
|
+
/// > all[Bool] { x => x } { [false, true, true].each }
|
|
171
|
+
/// false
|
|
172
|
+
///
|
|
173
|
+
/// > all[Int] { x => x >= 0 } { () } // empty stream
|
|
174
|
+
/// true
|
|
175
|
+
/// ```
|
|
176
|
+
def all[A] { cond: A => Bool } { stream: => Unit / emit[A] }: Bool =
|
|
177
|
+
try { stream(); true } with emit[A] {
|
|
178
|
+
case value and not(cond(value)) => false
|
|
179
|
+
case _ => resume(())
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// Returns true if any element in the stream satisfies the given predicate,
|
|
183
|
+
/// false otherwise. Short-circuits as soon as a matching element is found.
|
|
184
|
+
///
|
|
185
|
+
/// Examples:
|
|
186
|
+
/// ```effekt
|
|
187
|
+
/// > any[Int] { x => x > 0 } { [-2, -1, 0, 1, 2].each }
|
|
188
|
+
/// true
|
|
189
|
+
///
|
|
190
|
+
/// > any[Int] { x => x > 10 } { [1, 2, 3, 4].each }
|
|
191
|
+
/// false
|
|
192
|
+
///
|
|
193
|
+
/// > any[Bool] { x => x } { [false, true, false].each }
|
|
194
|
+
/// true
|
|
195
|
+
///
|
|
196
|
+
/// > any[Int] { x => x == 0 } { () } // empty stream
|
|
197
|
+
/// false
|
|
198
|
+
/// ```
|
|
199
|
+
def any[A] { cond: A => Bool } { stream: => Unit / emit[A] }: Bool =
|
|
200
|
+
try { stream(); false } with emit[A] {
|
|
201
|
+
case value and cond(value) => true
|
|
202
|
+
case _ => resume(())
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/// Count the number of emitted values that satisfy the given predicate.
|
|
206
|
+
///
|
|
207
|
+
/// Examples:
|
|
208
|
+
/// ```
|
|
209
|
+
/// > countIf[Int] { x => x > 0 } { [2, -1, 0, 1, -2].each }
|
|
210
|
+
/// 2
|
|
211
|
+
///
|
|
212
|
+
/// > countIf[Bool] { x => x } { [false, false, true, false].each }
|
|
213
|
+
/// 1
|
|
214
|
+
/// ```
|
|
215
|
+
def countIf[A] { shouldCount: A => Bool } { stream: () => Unit / emit[A] }: Int = {
|
|
216
|
+
var count = 0
|
|
217
|
+
for {stream} {
|
|
218
|
+
case x and shouldCount(x) => count = count + 1
|
|
219
|
+
case _ => ()
|
|
220
|
+
}
|
|
221
|
+
count
|
|
222
|
+
}
|
|
223
|
+
|
|
138
224
|
def source[A] { stream: () => Unit / emit[A] } { reader: () => Unit / read[A] }: Unit =
|
|
139
225
|
returning::source[A, Unit]{stream}{reader}
|
|
140
226
|
|
|
@@ -177,9 +263,9 @@ namespace internal {
|
|
|
177
263
|
|
|
178
264
|
/// Use cons to handle prod, but also emit to the outside.
|
|
179
265
|
/// The outside controls iteration, i.e., if cons aborts, the rest of prod will still be emitted.
|
|
180
|
-
///
|
|
266
|
+
///
|
|
181
267
|
/// Starts by executing cons. Will stop cons during execution if the outside stops consuming.
|
|
182
|
-
///
|
|
268
|
+
///
|
|
183
269
|
/// Example, printing all values consumed by the outside:
|
|
184
270
|
///
|
|
185
271
|
/// with teeing{ {s} => for{s}{ e => println(e) } }
|
|
@@ -215,9 +301,9 @@ def teeing[A]{ cons: { => Unit / emit[A] } => Unit }{ prod: => Unit / emit[A] }:
|
|
|
215
301
|
}
|
|
216
302
|
|
|
217
303
|
/// Binds a `teeing`-like function in the body, stopping the inner producer once all consumers in tees are done consuming.
|
|
218
|
-
///
|
|
304
|
+
///
|
|
219
305
|
/// Example of use, equivalent to `tee[A]{cns1}{cns2}{prd}`:
|
|
220
|
-
///
|
|
306
|
+
///
|
|
221
307
|
/// manyTee[A] { {tee} =>
|
|
222
308
|
/// with tee{cns1}
|
|
223
309
|
/// with tee{cns2}
|
|
@@ -253,6 +339,25 @@ def tee[A]{ cons1: { => Unit / emit[A] } => Unit }{ cons2: { => Unit / emit[A] }
|
|
|
253
339
|
}
|
|
254
340
|
}
|
|
255
341
|
|
|
342
|
+
/// Return exactly the very first element of the given stream
|
|
343
|
+
/// or raise MissingValue if no elements are emitted.
|
|
344
|
+
///
|
|
345
|
+
/// Examples:
|
|
346
|
+
/// ```effekt
|
|
347
|
+
/// > first[Int] { [1, 2, 3].each }
|
|
348
|
+
/// 1
|
|
349
|
+
/// > first[Bool] { do emit(true) }
|
|
350
|
+
/// true
|
|
351
|
+
/// > first[String] { () } // empty stream
|
|
352
|
+
/// <Exception MissingValue: code in first did not emit any values>
|
|
353
|
+
/// ```
|
|
354
|
+
def first[A] { body: => Unit / emit[A] }: A / Exception[MissingValue] =
|
|
355
|
+
try {
|
|
356
|
+
body()
|
|
357
|
+
val r: A = do raise[MissingValue](MissingValue(), "code in first did not emit any values")
|
|
358
|
+
r
|
|
359
|
+
} with emit[A] { a => a }
|
|
360
|
+
|
|
256
361
|
namespace returning {
|
|
257
362
|
|
|
258
363
|
/// Canonical handler of push streams that performs `action` for every
|
|
@@ -15,9 +15,9 @@ namespace internal {
|
|
|
15
15
|
def parent(u: UnionFind, s: Int): Int / Exception[MissingValue] = {
|
|
16
16
|
with on[OutOfBounds].default { do raise(MissingValue(), "No node " ++ s.show) }
|
|
17
17
|
val c = u.rawElements.get(s)
|
|
18
|
-
if (c < 0) {
|
|
19
|
-
do raise(MissingValue(), "Root node " ++ s.show ++ " has no parent.")
|
|
20
|
-
} else {
|
|
18
|
+
if (c < 0) {
|
|
19
|
+
do raise(MissingValue(), "Root node " ++ s.show ++ " has no parent.")
|
|
20
|
+
} else {
|
|
21
21
|
c
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -31,11 +31,11 @@ namespace internal {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
def setParent(u: UnionFind, child: Int, parent: Int) = {
|
|
34
|
-
with
|
|
34
|
+
with default[OutOfBounds, Unit] { do raise(MissingValue(), "No node " ++ child.show) }
|
|
35
35
|
u.rawElements.set(child, parent)
|
|
36
36
|
}
|
|
37
37
|
def setRank(u: UnionFind, root: Int, rank: Int) = {
|
|
38
|
-
with
|
|
38
|
+
with default[OutOfBounds, Unit] { do raise(MissingValue(), "No node " ++ root.show) }
|
|
39
39
|
u.rawElements.set(root, -1 * rank - 1)
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -66,7 +66,7 @@ def find(u: UnionFind, s: Int): Int / Exception[MissingValue] = {
|
|
|
66
66
|
}
|
|
67
67
|
val root = goFind(s)
|
|
68
68
|
|
|
69
|
-
def goCompress(s: Int): Unit = {
|
|
69
|
+
def goCompress(s: Int): Unit = {
|
|
70
70
|
if (not(u.internal::isRoot(s))) {
|
|
71
71
|
val p = u.internal::parent(s)
|
|
72
72
|
u.internal::setParent(s, root)
|
|
@@ -82,7 +82,7 @@ def find(u: UnionFind, s: Int): Int / Exception[MissingValue] = {
|
|
|
82
82
|
///
|
|
83
83
|
/// O(1)
|
|
84
84
|
def unionRoots(u: UnionFind, x: Int, y: Int): Int / Exception[MissingValue] = {
|
|
85
|
-
if(not(u.internal::isRoot(x) && u.internal::isRoot(y))) {
|
|
85
|
+
if(not(u.internal::isRoot(x) && u.internal::isRoot(y))) {
|
|
86
86
|
panic("unionRoots should only be called on representatives")
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -56,6 +56,6 @@ $effekt.println = function println$impl(str) {
|
|
|
56
56
|
|
|
57
57
|
$effekt.unit = { __unit: true }
|
|
58
58
|
|
|
59
|
-
$effekt.
|
|
59
|
+
$effekt.unreachable = function() { throw "should not be reachable!" }
|
|
60
60
|
|
|
61
61
|
$effekt.hole = function(pos) { throw pos + " not implemented yet" }
|
|
@@ -7,8 +7,8 @@ declare void @c_io_println(%Pos)
|
|
|
7
7
|
declare %Pos @c_io_readln()
|
|
8
8
|
declare %Double @c_io_random()
|
|
9
9
|
|
|
10
|
-
declare void @hole(i8*)
|
|
11
|
-
declare void @duplicated_prompt()
|
|
10
|
+
declare void @hole(i8*) cold
|
|
11
|
+
declare void @duplicated_prompt() cold
|
|
12
12
|
|
|
13
13
|
declare %Pos @c_ref_fresh(%Pos)
|
|
14
14
|
declare %Pos @c_ref_get(%Pos)
|