@effekt-lang/effekt 0.53.0 → 0.55.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/cps/effekt.ss +169 -0
- package/libraries/common/effekt.effekt +1 -1
- package/libraries/common/io/channel.effekt +51 -0
- package/libraries/common/io/promise.effekt +53 -0
- package/libraries/common/io/signal.effekt +55 -0
- package/libraries/common/io/time.effekt +1 -1
- package/libraries/common/io.effekt +1 -57
- package/libraries/common/list.effekt +16 -0
- package/libraries/common/process.effekt +99 -0
- package/libraries/common/stream.effekt +110 -5
- package/libraries/js/mutable/map.effekt +2 -2
- package/libraries/llvm/io.c +224 -124
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +41 -66
- package/licenses/mit license - mit-license.html +90 -90
- package/licenses/the bsd license - bsd-license.html +91 -91
- package/licenses/the mit license - mit.html +90 -90
- package/package.json +1 -1
package/bin/effekt
CHANGED
|
Binary file
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
; Value = Any
|
|
2
|
+
; Generation = Int
|
|
3
|
+
|
|
4
|
+
; Ref = Store * Value * Generation
|
|
5
|
+
(define-record-type ref (fields store (mutable value) (mutable generation)))
|
|
6
|
+
|
|
7
|
+
; Node = box MEM | box Diff
|
|
8
|
+
|
|
9
|
+
; MEM
|
|
10
|
+
; Means the correct values are in MEMory
|
|
11
|
+
(define-record-type mem (fields))
|
|
12
|
+
|
|
13
|
+
; Diff = Ref * Value * Generation * Node
|
|
14
|
+
; Notes the DIFFerences between root and the current state in MEMory
|
|
15
|
+
(define-record-type diff (fields ref value generation root))
|
|
16
|
+
|
|
17
|
+
; Store = Node * Generation
|
|
18
|
+
(define-record-type store (fields (mutable root) (mutable generation)))
|
|
19
|
+
|
|
20
|
+
; Snapshot = Store * Node * Generation
|
|
21
|
+
(define-record-type snap (fields store root generation))
|
|
22
|
+
|
|
23
|
+
; -> Store
|
|
24
|
+
(define (create-store) (make-store (box (make-mem)) 0))
|
|
25
|
+
|
|
26
|
+
; Store -> Snapshot
|
|
27
|
+
(define (snapshot store)
|
|
28
|
+
(let* ([sGen (store-generation store)]
|
|
29
|
+
[snap (make-snap store (store-root store) sGen)])
|
|
30
|
+
(store-generation-set! store (+ sGen 1))
|
|
31
|
+
snap))
|
|
32
|
+
|
|
33
|
+
; Node, [box Diff] -> [box Diff]
|
|
34
|
+
(define (collectDiffs n acc)
|
|
35
|
+
(let ([unboxedN (unbox n)])
|
|
36
|
+
(cond
|
|
37
|
+
[(mem? unboxedN) acc]
|
|
38
|
+
[else (collectDiffs (diff-root unboxedN) (cons n acc))])))
|
|
39
|
+
|
|
40
|
+
; Node, [Diff] -> void
|
|
41
|
+
(define (applyDiffs n diffs)
|
|
42
|
+
(cond
|
|
43
|
+
[(null? diffs) (set-box! n (make-mem))]
|
|
44
|
+
[else
|
|
45
|
+
(let* ([currentDiff (car diffs)]
|
|
46
|
+
[realDiff (unbox currentDiff)]
|
|
47
|
+
[r (diff-ref realDiff)]
|
|
48
|
+
[oldValue (ref-value r)])
|
|
49
|
+
(ref-value-set! r (diff-value realDiff))
|
|
50
|
+
(set-box! n (make-diff r oldValue (ref-generation r) currentDiff))
|
|
51
|
+
(applyDiffs currentDiff (cdr diffs)))]))
|
|
52
|
+
|
|
53
|
+
; Node, Node -> void
|
|
54
|
+
(define (reroot newRoot oldRoot)
|
|
55
|
+
(applyDiffs oldRoot (collectDiffs newRoot '())))
|
|
56
|
+
|
|
57
|
+
; Store, Snapshot -> void
|
|
58
|
+
(define (restore snap)
|
|
59
|
+
(let* ([snapRoot (snap-root snap)]
|
|
60
|
+
[store (snap-store snap)]
|
|
61
|
+
[storeRoot (store-root store)])
|
|
62
|
+
(reroot snapRoot storeRoot)
|
|
63
|
+
(store-root-set! store snapRoot)))
|
|
64
|
+
|
|
65
|
+
; Cont a = a, MetaCont -> #
|
|
66
|
+
; Prompt = Int
|
|
67
|
+
|
|
68
|
+
; MetaCont = Cont * Prompt * Store | Snapshot * MetaCont?
|
|
69
|
+
; Holds a "copy" of k, as well as its prompt and store
|
|
70
|
+
(define-record-type meta-cont
|
|
71
|
+
(fields (mutable cont) prompt (mutable store) (mutable rest)))
|
|
72
|
+
|
|
73
|
+
; Value, MetaCont -> Ref
|
|
74
|
+
(define (var init ks)
|
|
75
|
+
(let ([store (meta-cont-store ks)])
|
|
76
|
+
(make-ref store init (store-generation store))))
|
|
77
|
+
|
|
78
|
+
; Ref -> Value
|
|
79
|
+
(define (get ref) (ref-value ref))
|
|
80
|
+
|
|
81
|
+
; Ref, Value -> void
|
|
82
|
+
(define (put ref value)
|
|
83
|
+
(let* ([rGen (ref-generation ref)]
|
|
84
|
+
[store (ref-store ref)]
|
|
85
|
+
[sGen (store-generation store)])
|
|
86
|
+
(if (= rGen sGen)
|
|
87
|
+
(ref-value-set! ref value)
|
|
88
|
+
(let ([oldVal (ref-value ref)]
|
|
89
|
+
[newRoot (box (make-mem))]
|
|
90
|
+
[oldRoot (store-root store)])
|
|
91
|
+
(ref-value-set! ref value)
|
|
92
|
+
(ref-generation-set! ref sGen)
|
|
93
|
+
(set-box! oldRoot (make-diff ref oldVal rGen newRoot))
|
|
94
|
+
(store-root-set! store newRoot)))))
|
|
95
|
+
|
|
96
|
+
; MetaCont -> MetaCont
|
|
97
|
+
(define (create-region ks) ks)
|
|
98
|
+
|
|
99
|
+
; Value, MetaCont -> Ref
|
|
100
|
+
(define allocate var)
|
|
101
|
+
|
|
102
|
+
; Ref | MetaCont -> void
|
|
103
|
+
(define (deallocate _) (void))
|
|
104
|
+
|
|
105
|
+
(define _prompt 1)
|
|
106
|
+
(define (new-prompt)
|
|
107
|
+
(let ([prompt _prompt])
|
|
108
|
+
(set! _prompt (+ prompt 1))
|
|
109
|
+
prompt))
|
|
110
|
+
|
|
111
|
+
(define (top-level-k x _) x)
|
|
112
|
+
(define top-level-ks (make-meta-cont top-level-k 0 (create-store) '()))
|
|
113
|
+
|
|
114
|
+
; a, MetaCont -> #
|
|
115
|
+
(define (return x ks)
|
|
116
|
+
(let* ([new-ks (meta-cont-rest ks)]
|
|
117
|
+
[k (meta-cont-cont new-ks)])
|
|
118
|
+
(k x new-ks)))
|
|
119
|
+
|
|
120
|
+
; Program a b = a, Cont b, MetaCont -> #
|
|
121
|
+
|
|
122
|
+
; Program Prompt b, MetaCont, Cont b -> #
|
|
123
|
+
(define (reset prog ks k)
|
|
124
|
+
(let ([prompt (new-prompt)])
|
|
125
|
+
(meta-cont-cont-set! ks k)
|
|
126
|
+
(prog prompt (make-meta-cont return prompt (create-store) ks) return)))
|
|
127
|
+
|
|
128
|
+
; MetaCont, Prompt -> MetaCont * MetaCont
|
|
129
|
+
(define (split-stack ks p)
|
|
130
|
+
(define (worker above below)
|
|
131
|
+
(let ([new-below (meta-cont-rest below)]
|
|
132
|
+
[snap (snapshot (meta-cont-store below))])
|
|
133
|
+
(meta-cont-store-set! below snap)
|
|
134
|
+
(meta-cont-rest-set! below above)
|
|
135
|
+
(if (= (meta-cont-prompt below) p)
|
|
136
|
+
(values below new-below)
|
|
137
|
+
(worker below new-below))))
|
|
138
|
+
(worker '() ks))
|
|
139
|
+
|
|
140
|
+
; Prompt, Program MetaCont b, MetaCont, Cont b -> #
|
|
141
|
+
(define (shift p prog ks k)
|
|
142
|
+
(meta-cont-cont-set! ks k)
|
|
143
|
+
(let-values ([(c underC) (split-stack ks p)])
|
|
144
|
+
(prog c underC (meta-cont-cont underC))))
|
|
145
|
+
|
|
146
|
+
; MetaCont, MetaCont -> MetaCont
|
|
147
|
+
(define (rewind cont ks)
|
|
148
|
+
(if (null? cont)
|
|
149
|
+
ks
|
|
150
|
+
(let* ([snap (meta-cont-store cont)]
|
|
151
|
+
[next (meta-cont-rest cont)]
|
|
152
|
+
[newKs (make-meta-cont (meta-cont-cont cont)
|
|
153
|
+
(meta-cont-prompt cont)
|
|
154
|
+
(snap-store snap)
|
|
155
|
+
ks)])
|
|
156
|
+
(restore snap)
|
|
157
|
+
(rewind next newKs))))
|
|
158
|
+
|
|
159
|
+
; Block b = Cont b, MetaCont -> #
|
|
160
|
+
|
|
161
|
+
; MetaCont, Block, MetaCont, Cont -> #
|
|
162
|
+
(define (resume cont block ks k)
|
|
163
|
+
(meta-cont-cont-set! ks k)
|
|
164
|
+
(let ([rewinded (rewind cont ks)])
|
|
165
|
+
(block rewinded (meta-cont-cont rewinded))))
|
|
166
|
+
|
|
167
|
+
; Block b -> b
|
|
168
|
+
(define (run-top-level p)
|
|
169
|
+
(p top-level-ks top-level-k))
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module io/channel
|
|
2
|
+
|
|
3
|
+
import io
|
|
4
|
+
import io/signal
|
|
5
|
+
|
|
6
|
+
record Node[A, B](this: Signal[A, B], rest: Ref[Option[Node[A, B]]])
|
|
7
|
+
|
|
8
|
+
type Sender[A, B] = Ref[Node[A, B]]
|
|
9
|
+
type Receiver[A, B] = Ref[Node[A, B]]
|
|
10
|
+
|
|
11
|
+
/// Creates a new rendezvous channel and returns the receiving and the sending end.
|
|
12
|
+
/// Every send must be matched by exactly one receive upon which two values are exchanged.
|
|
13
|
+
def channel[A, B](): (Sender[A, B], Receiver[A, B]) = {
|
|
14
|
+
val node = Node(signal(), ref(None()))
|
|
15
|
+
(ref(node), ref(node))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// Sends a value to a channel and blocks until a receiver arrives.
|
|
19
|
+
def send[A, B](sender: Sender[A, B], value: A): B = {
|
|
20
|
+
val Node(this, rest) = sender.get()
|
|
21
|
+
rest.get() match {
|
|
22
|
+
case None() =>
|
|
23
|
+
val node = Node(signal(), ref(None()))
|
|
24
|
+
rest.set(Some(node))
|
|
25
|
+
sender.set(node)
|
|
26
|
+
case Some(node) =>
|
|
27
|
+
sender.set(node)
|
|
28
|
+
}
|
|
29
|
+
// TODO avoid yield
|
|
30
|
+
yield()
|
|
31
|
+
this.unsafeFire(value)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// Receives a value from a channel and blocks until a sender arrives.
|
|
35
|
+
def receive[A, B](receiver: Receiver[A, B], value: B): A = {
|
|
36
|
+
val Node(this, rest) = receiver.get()
|
|
37
|
+
rest.get() match {
|
|
38
|
+
case None() =>
|
|
39
|
+
val node = Node(signal(), ref(None()))
|
|
40
|
+
rest.set(Some(node))
|
|
41
|
+
receiver.set(node)
|
|
42
|
+
case Some(node) =>
|
|
43
|
+
receiver.set(node)
|
|
44
|
+
}
|
|
45
|
+
// TODO avoid yield
|
|
46
|
+
yield()
|
|
47
|
+
this.unsafeWait(value)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// TODO streaming
|
|
51
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module io/promise
|
|
2
|
+
|
|
3
|
+
import io
|
|
4
|
+
import io/signal
|
|
5
|
+
|
|
6
|
+
// Promises
|
|
7
|
+
// --------
|
|
8
|
+
|
|
9
|
+
type State[T] {
|
|
10
|
+
Pending(signals: List[Signal[T, Unit]])
|
|
11
|
+
Resolved(value: T)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type Promise[T] = Ref[State[T]]
|
|
15
|
+
|
|
16
|
+
namespace promise {
|
|
17
|
+
/// Creates a pending promise that must be resolved exactly once
|
|
18
|
+
/// and can be awaited multiple times.
|
|
19
|
+
def make[T](): Promise[T] =
|
|
20
|
+
ref(Pending(Nil()))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// Spawns a task and immediately returns a promise that upon completion
|
|
24
|
+
/// of the task will resolve to its result.
|
|
25
|
+
def promise[T](task: () => T at {io, async, global}): Promise[T] = {
|
|
26
|
+
val p = promise::make[T]();
|
|
27
|
+
spawn(box { p.resolve(task()) });
|
|
28
|
+
return p
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Awaits and blocks until the given promise is resolved.
|
|
32
|
+
def await[T](promise: Promise[T]): T =
|
|
33
|
+
promise.get match {
|
|
34
|
+
case Resolved(value) =>
|
|
35
|
+
return value
|
|
36
|
+
case Pending(signals) =>
|
|
37
|
+
val signal = signal()
|
|
38
|
+
promise.set(Pending(Cons(signal, signals)))
|
|
39
|
+
// TODO perhaps yield to avoid stack overflow
|
|
40
|
+
signal.unsafeWait(())
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Resolves the given promise, unblocking all awaiting tasks.
|
|
44
|
+
def resolve[T](promise: Promise[T], value: T): Unit =
|
|
45
|
+
promise.get match {
|
|
46
|
+
case Resolved(value) =>
|
|
47
|
+
panic("ERROR: Promise already resolved")
|
|
48
|
+
case Pending(signals) =>
|
|
49
|
+
promise.set(Resolved(value))
|
|
50
|
+
// TODO perhaps yield to avoid stack overflow
|
|
51
|
+
signals.reverse.foreach { signal => unsafeFire(signal, value) }
|
|
52
|
+
}
|
|
53
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module io/signal
|
|
2
|
+
|
|
3
|
+
// Signals
|
|
4
|
+
// -------
|
|
5
|
+
|
|
6
|
+
/// Must be fired exactly once with an A receiving a B,
|
|
7
|
+
/// and must be waited for exactly once with a B receiving an A.
|
|
8
|
+
extern type Signal[A, B]
|
|
9
|
+
|
|
10
|
+
extern def signal[A, B]() at global: Signal[A, B] =
|
|
11
|
+
js "signal$make()"
|
|
12
|
+
llvm """
|
|
13
|
+
%signal = call %Pos @c_signal_make()
|
|
14
|
+
ret %Pos %signal
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
extern def unsafeFire[A, B](signal: Signal[A, B], value: A) at async: B =
|
|
18
|
+
js "$effekt.capture(callback => signal$notify(${signal}, ${value}, callback))"
|
|
19
|
+
llvm """
|
|
20
|
+
call void @c_signal_notify(%Pos ${signal}, %Pos ${value}, %Stack %stack)
|
|
21
|
+
ret void
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
extern def unsafeWait[A, B](signal: Signal[A, B], value: B) at async: A =
|
|
25
|
+
js "$effekt.capture(callback => signal$notify(${signal}, ${value}, callback))"
|
|
26
|
+
llvm """
|
|
27
|
+
call void @c_signal_notify(%Pos ${signal}, %Pos ${value}, %Stack %stack)
|
|
28
|
+
ret void
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
extern llvm """
|
|
32
|
+
declare %Pos @c_signal_make()
|
|
33
|
+
declare void @c_signal_notify(%Pos, %Pos, %Stack)
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
extern js """
|
|
37
|
+
function signal$make() {
|
|
38
|
+
return { state: 0, value: null, stack: null };
|
|
39
|
+
}
|
|
40
|
+
function signal$notify(signal, value, stack) {
|
|
41
|
+
if (signal.state === 0) {
|
|
42
|
+
signal.state = 1;
|
|
43
|
+
signal.value = value;
|
|
44
|
+
signal.stack = stack;
|
|
45
|
+
} else {
|
|
46
|
+
const otherValue = signal.value;
|
|
47
|
+
const otherStack = signal.stack;
|
|
48
|
+
signal.value = null;
|
|
49
|
+
signal.stack = null;
|
|
50
|
+
|
|
51
|
+
otherStack(value);
|
|
52
|
+
stack(otherValue);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
"""
|
|
@@ -4,7 +4,7 @@ extern llvm """
|
|
|
4
4
|
declare void @c_timer_start(%Int, %Stack)
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
extern def
|
|
7
|
+
extern def sleep(millis: Int) at async: Unit =
|
|
8
8
|
js "$effekt.capture(k => setTimeout(() => k($effekt.unit), ${millis}))"
|
|
9
9
|
llvm """
|
|
10
10
|
call void @c_timer_start(%Int ${millis}, %Stack %stack)
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
module io
|
|
2
2
|
|
|
3
|
-
import ref
|
|
4
|
-
import queue
|
|
5
3
|
|
|
6
4
|
// Event Loop
|
|
7
5
|
// ----------
|
|
8
6
|
|
|
9
|
-
type Task[T] = () => T at {io, async, global}
|
|
10
|
-
|
|
11
7
|
extern llvm """
|
|
12
8
|
declare void @c_yield(%Stack)
|
|
13
9
|
"""
|
|
14
10
|
|
|
15
|
-
extern def spawn(task:
|
|
11
|
+
extern def spawn[T](task: () => T at {io, async, global}) at async: Unit =
|
|
16
12
|
js "$effekt.capture(k => { setTimeout(() => k($effekt.unit), 0); return $effekt.run(${task}) })"
|
|
17
13
|
llvm """
|
|
18
14
|
call void @c_yield(%Stack %stack)
|
|
@@ -34,55 +30,3 @@ extern def abort() at async: Nothing =
|
|
|
34
30
|
ret void
|
|
35
31
|
"""
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
// Promises
|
|
39
|
-
// --------
|
|
40
|
-
|
|
41
|
-
extern type Promise[T]
|
|
42
|
-
// = js "{resolve: ƒ, promise: Promise}"
|
|
43
|
-
// = llvm "{tag: 0, obj: Promise*}"
|
|
44
|
-
|
|
45
|
-
def promise[T](task: Task[T]): Promise[T] = {
|
|
46
|
-
val p = promise::make[T]();
|
|
47
|
-
spawn(box { p.resolve(task()) });
|
|
48
|
-
return p
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
extern llvm """
|
|
52
|
-
declare %Pos @c_promise_make()
|
|
53
|
-
declare void @c_promise_resolve(%Pos, %Pos)
|
|
54
|
-
declare void @c_promise_await(%Pos, %Neg)
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
extern def await[T](promise: Promise[T]) at async: T =
|
|
58
|
-
js "$effekt.capture(k => ${promise}.promise.then(k))"
|
|
59
|
-
llvm """
|
|
60
|
-
call void @c_promise_await(%Pos ${promise}, %Stack %stack)
|
|
61
|
-
ret void
|
|
62
|
-
"""
|
|
63
|
-
|
|
64
|
-
extern def resolve[T](promise: Promise[T], value: T) at async: Unit =
|
|
65
|
-
js "$effekt.capture(k => { ${promise}.resolve(${value}); return k($effekt.unit) })"
|
|
66
|
-
llvm """
|
|
67
|
-
call void @c_promise_resolve(%Pos ${promise}, %Pos ${value}, %Stack %stack)
|
|
68
|
-
ret void
|
|
69
|
-
"""
|
|
70
|
-
|
|
71
|
-
namespace promise {
|
|
72
|
-
extern js """
|
|
73
|
-
function promise$make() {
|
|
74
|
-
let resolve;
|
|
75
|
-
const promise = new Promise((res, rej) => {
|
|
76
|
-
resolve = res;
|
|
77
|
-
});
|
|
78
|
-
return { resolve: resolve, promise: promise };
|
|
79
|
-
}
|
|
80
|
-
"""
|
|
81
|
-
|
|
82
|
-
extern def make[T]() at io: Promise[T] =
|
|
83
|
-
js "promise$make()"
|
|
84
|
-
llvm """
|
|
85
|
-
%promise = call %Pos @c_promise_make()
|
|
86
|
-
ret %Pos %promise
|
|
87
|
-
"""
|
|
88
|
-
}
|
|
@@ -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 {
|
|
@@ -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
|
+
"""
|