@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
|
@@ -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
|
// -------
|
|
@@ -44,7 +45,7 @@ def fix[T] { one: T => Unit / emit[T] } { stream: => Unit / emit[T] }: Unit =
|
|
|
44
45
|
try {
|
|
45
46
|
stream()
|
|
46
47
|
} with emit[T] { value =>
|
|
47
|
-
fix
|
|
48
|
+
fix {one} { one(value) }
|
|
48
49
|
resume(())
|
|
49
50
|
}
|
|
50
51
|
|
|
@@ -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
|
|
@@ -5,7 +5,7 @@ import array
|
|
|
5
5
|
// A mutable map, backed by a JavaScript Map.
|
|
6
6
|
extern type Map[K, V]
|
|
7
7
|
|
|
8
|
-
extern def emptyMap[K, V]() at
|
|
8
|
+
extern def emptyMap[K, V]() at io: Map[K, V] =
|
|
9
9
|
js "new Map()"
|
|
10
10
|
|
|
11
11
|
def get[K, V](m: Map[K, V], key: K): Option[V] =
|
|
@@ -28,4 +28,4 @@ extern def values[K, V](map: Map[K, V]) at {}: Array[V] =
|
|
|
28
28
|
js "Array.from(${map}.values())"
|
|
29
29
|
|
|
30
30
|
extern def keys[K, V](map: Map[K, V]) at {}: Array[K] =
|
|
31
|
-
js "Array.from(${map}.keys())"
|
|
31
|
+
js "Array.from(${map}.keys())"
|
package/libraries/llvm/io.c
CHANGED
|
@@ -572,154 +572,254 @@ void c_yield(Stack stack) {
|
|
|
572
572
|
c_timer_start(0, stack);
|
|
573
573
|
}
|
|
574
574
|
|
|
575
|
-
|
|
576
|
-
// Promises
|
|
575
|
+
// Signals
|
|
577
576
|
// --------
|
|
578
577
|
|
|
579
|
-
typedef enum {
|
|
580
|
-
|
|
581
|
-
typedef struct Listeners {
|
|
582
|
-
Stack head;
|
|
583
|
-
struct Listeners* tail;
|
|
584
|
-
} Listeners;
|
|
578
|
+
typedef enum { INITIAL, NOTIFIED } signal_state_t;
|
|
585
579
|
|
|
586
580
|
typedef struct {
|
|
587
581
|
uint64_t rc;
|
|
588
582
|
void* eraser;
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
// }
|
|
594
|
-
union {
|
|
595
|
-
struct Pos value;
|
|
596
|
-
Listeners listeners;
|
|
597
|
-
} payload;
|
|
598
|
-
} Promise;
|
|
599
|
-
|
|
600
|
-
void c_promise_erase_listeners(void *envPtr) {
|
|
601
|
-
// envPtr points to a Promise _after_ the eraser, so let's adjust it to point to the promise.
|
|
602
|
-
Promise *promise = (Promise*) (envPtr - offsetof(Promise, state));
|
|
603
|
-
promise_state_t state = promise->state;
|
|
604
|
-
|
|
605
|
-
Stack head;
|
|
606
|
-
Listeners* tail;
|
|
607
|
-
Listeners* current;
|
|
608
|
-
|
|
609
|
-
switch (state) {
|
|
610
|
-
case UNRESOLVED:
|
|
611
|
-
head = promise->payload.listeners.head;
|
|
612
|
-
tail = promise->payload.listeners.tail;
|
|
613
|
-
if (head != NULL) {
|
|
614
|
-
// Erase head
|
|
615
|
-
eraseStack(head);
|
|
616
|
-
// Erase tail
|
|
617
|
-
current = tail;
|
|
618
|
-
while (current != NULL) {
|
|
619
|
-
head = current->head;
|
|
620
|
-
tail = current->tail;
|
|
621
|
-
free(current);
|
|
622
|
-
eraseStack(head);
|
|
623
|
-
current = tail;
|
|
624
|
-
};
|
|
625
|
-
};
|
|
626
|
-
break;
|
|
627
|
-
case RESOLVED:
|
|
628
|
-
erasePositive(promise->payload.value);
|
|
629
|
-
break;
|
|
630
|
-
}
|
|
631
|
-
}
|
|
583
|
+
signal_state_t state;
|
|
584
|
+
struct Pos value;
|
|
585
|
+
Stack stack;
|
|
586
|
+
} Signal;
|
|
632
587
|
|
|
633
|
-
void
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
c_promise_resume_listeners(tail, value);
|
|
639
|
-
sharePositive(value);
|
|
640
|
-
resume_Pos(head, value);
|
|
641
|
-
}
|
|
588
|
+
void c_signal_erase(void *envPtr) {
|
|
589
|
+
// envPtr points to a Signal _after_ the eraser, so let's adjust it to point to the beginning.
|
|
590
|
+
Signal *signal = (Signal*) (envPtr - offsetof(Signal, state));
|
|
591
|
+
erasePositive(signal->value);
|
|
592
|
+
if (signal->stack != NULL) { eraseStack(signal->stack); }
|
|
642
593
|
}
|
|
643
594
|
|
|
644
|
-
|
|
645
|
-
|
|
595
|
+
struct Pos c_signal_make() {
|
|
596
|
+
Signal* f = (Signal*)malloc(sizeof(Signal));
|
|
646
597
|
|
|
647
|
-
|
|
648
|
-
|
|
598
|
+
f->rc = 0;
|
|
599
|
+
f->eraser = c_signal_erase;
|
|
600
|
+
f->state = INITIAL;
|
|
601
|
+
f->value = (struct Pos) { .tag = 0, .obj = NULL, };
|
|
602
|
+
f->stack = NULL;
|
|
649
603
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
head = p->payload.listeners.head;
|
|
653
|
-
tail = p->payload.listeners.tail;
|
|
654
|
-
|
|
655
|
-
p->state = RESOLVED;
|
|
656
|
-
p->payload.value = value;
|
|
657
|
-
resume_Pos(stack, Unit);
|
|
604
|
+
return (struct Pos) { .tag = 0, .obj = f, };
|
|
605
|
+
}
|
|
658
606
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
607
|
+
void c_signal_notify(struct Pos signal, struct Pos value, Stack stack) {
|
|
608
|
+
Signal* f = (Signal*)signal.obj;
|
|
609
|
+
switch (f->state) {
|
|
610
|
+
case INITIAL: {
|
|
611
|
+
f->state = NOTIFIED;
|
|
612
|
+
f->value = value;
|
|
613
|
+
f->stack = stack;
|
|
614
|
+
erasePositive(signal);
|
|
666
615
|
break;
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
616
|
+
}
|
|
617
|
+
case NOTIFIED: {
|
|
618
|
+
struct Pos other_value = f->value;
|
|
619
|
+
f->value = (struct Pos) { .tag = 0, .obj = NULL, };
|
|
620
|
+
Stack other_stack = f->stack;
|
|
621
|
+
f->stack = NULL;
|
|
622
|
+
erasePositive(signal);
|
|
623
|
+
resume_Pos(other_stack, value);
|
|
624
|
+
resume_Pos(stack, other_value);
|
|
673
625
|
break;
|
|
626
|
+
}
|
|
674
627
|
}
|
|
675
|
-
// TODO stack overflow?
|
|
676
|
-
// We need to erase the promise now, since we consume it.
|
|
677
|
-
erasePositive(promise);
|
|
678
628
|
}
|
|
679
629
|
|
|
680
|
-
|
|
681
|
-
|
|
630
|
+
// Subprocesses
|
|
631
|
+
// ------------
|
|
682
632
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
633
|
+
struct Pos c_spawn_options_default() {
|
|
634
|
+
uv_process_options_t* options = (uv_process_options_t*)malloc(sizeof(uv_process_options_t));
|
|
635
|
+
memset(options, 0, sizeof(uv_process_options_t));
|
|
636
|
+
uv_stdio_container_t* stdio = (uv_stdio_container_t*)malloc(3*sizeof(uv_stdio_container_t));
|
|
637
|
+
stdio[0].flags = UV_INHERIT_FD;
|
|
638
|
+
stdio[0].data.fd = 0;
|
|
639
|
+
stdio[1].flags = UV_INHERIT_FD;
|
|
640
|
+
stdio[1].data.fd = 1;
|
|
641
|
+
stdio[2].flags = UV_INHERIT_FD;
|
|
642
|
+
stdio[2].data.fd = 2;
|
|
643
|
+
options->stdio = stdio;
|
|
644
|
+
options->stdio_count = 3;
|
|
645
|
+
return (struct Pos) { .tag = 0, .obj = options, };
|
|
646
|
+
}
|
|
687
647
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
648
|
+
static void subproc_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
|
|
649
|
+
(void)handle;
|
|
650
|
+
buf->base = malloc(suggested_size);
|
|
651
|
+
buf->len = suggested_size;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
typedef struct {
|
|
655
|
+
struct Pos handler;
|
|
656
|
+
} subproc_stream_cb_closure_t;
|
|
657
|
+
void subproc_stream_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
|
|
658
|
+
subproc_stream_cb_closure_t* clos = (subproc_stream_cb_closure_t*)(stream->data);
|
|
659
|
+
if(nread >= 0) {
|
|
660
|
+
struct Pos chunk = c_bytearray_construct(nread, (uint8_t*)(buf->base));
|
|
661
|
+
if (buf->base) free(buf->base);
|
|
662
|
+
run_Pos(clos->handler, chunk);
|
|
663
|
+
} else {
|
|
664
|
+
uv_read_stop(stream);
|
|
665
|
+
erasePositive(clos->handler);
|
|
666
|
+
free(clos);
|
|
667
|
+
if (buf->base) free(buf->base);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
struct Pos subproc_options_on_stream(struct Pos opts, size_t id, uv_stdio_flags flags, struct Pos callback) {
|
|
671
|
+
uv_process_options_t* options = (uv_process_options_t*)opts.obj;
|
|
672
|
+
|
|
673
|
+
options->stdio[id].flags = UV_CREATE_PIPE | flags;
|
|
674
|
+
uv_pipe_t* pipe = (uv_pipe_t*)malloc(sizeof(uv_pipe_t));
|
|
675
|
+
|
|
676
|
+
uv_pipe_init(uv_default_loop(), pipe, 1);
|
|
677
|
+
options->stdio[id].data.stream = (uv_stream_t*)pipe;
|
|
678
|
+
|
|
679
|
+
subproc_stream_cb_closure_t* clos = (subproc_stream_cb_closure_t*)malloc(sizeof(subproc_stream_cb_closure_t));
|
|
680
|
+
clos->handler = callback;
|
|
681
|
+
pipe->data = clos;
|
|
682
|
+
|
|
683
|
+
return opts;
|
|
684
|
+
}
|
|
685
|
+
struct Pos c_spawn_options_on_stdout(struct Pos opts, struct Pos callback) {
|
|
686
|
+
return subproc_options_on_stream(opts, 1, UV_WRITABLE_PIPE, callback);
|
|
687
|
+
}
|
|
688
|
+
struct Pos c_spawn_options_on_stderr(struct Pos opts, struct Pos callback) {
|
|
689
|
+
return subproc_options_on_stream(opts, 2, UV_WRITABLE_PIPE, callback);
|
|
690
|
+
}
|
|
691
|
+
struct Pos c_spawn_options_pipe_stdin(struct Pos opts, struct Pos callback) {
|
|
692
|
+
return subproc_options_on_stream(opts, 0, UV_READABLE_PIPE, callback);
|
|
710
693
|
}
|
|
711
694
|
|
|
712
|
-
struct Pos c_promise_make() {
|
|
713
|
-
Promise* promise = (Promise*)malloc(sizeof(Promise));
|
|
714
695
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
696
|
+
typedef struct {
|
|
697
|
+
char** args;
|
|
698
|
+
Stack k;
|
|
699
|
+
uv_process_options_t* opts;
|
|
700
|
+
} subproc_proc_data_t;
|
|
701
|
+
void c_close_stream_callback_exit(uv_handle_t* req) {
|
|
702
|
+
free(req);
|
|
703
|
+
}
|
|
704
|
+
void subproc_on_close(uv_handle_t* handle) {
|
|
705
|
+
subproc_proc_data_t* pd = (subproc_proc_data_t*)((uv_process_t*)handle)->data;
|
|
706
|
+
uv_process_options_t* opts = (uv_process_options_t*)(pd->opts);
|
|
707
|
+
if(opts->stdio[0].flags & UV_CREATE_PIPE){
|
|
708
|
+
uv_close((uv_handle_t*)(opts->stdio[0].data.stream), c_close_stream_callback_exit);
|
|
709
|
+
}
|
|
710
|
+
if(opts->stdio[1].flags & UV_CREATE_PIPE){
|
|
711
|
+
uv_close((uv_handle_t*)(opts->stdio[1].data.stream), c_close_stream_callback_exit);
|
|
712
|
+
}
|
|
713
|
+
if(opts->stdio[2].flags & UV_CREATE_PIPE){
|
|
714
|
+
uv_close((uv_handle_t*)(opts->stdio[2].data.stream), c_close_stream_callback_exit);
|
|
715
|
+
}
|
|
716
|
+
// TODO shutdown pipes for stdio
|
|
717
|
+
free(opts->stdio);
|
|
718
|
+
free(opts);
|
|
719
|
+
free(pd);
|
|
720
|
+
free(handle);
|
|
721
|
+
}
|
|
722
|
+
void subproc_on_exit(uv_process_t* proc, int64_t exit_status, int term_signal) {
|
|
723
|
+
(void)term_signal; (void)exit_status;
|
|
724
|
+
subproc_proc_data_t* pd = (subproc_proc_data_t*)proc->data;
|
|
725
|
+
if (pd->args) {
|
|
726
|
+
for(int i = 0; pd->args[i] != NULL; i++) {
|
|
727
|
+
free(pd->args[i]);
|
|
728
|
+
}
|
|
729
|
+
free(pd->args);
|
|
730
|
+
}
|
|
731
|
+
Stack k = pd->k;
|
|
732
|
+
uv_close((uv_handle_t*)proc, subproc_on_close);
|
|
733
|
+
resume_Int(k, exit_status);
|
|
734
|
+
}
|
|
720
735
|
|
|
721
|
-
|
|
736
|
+
void c_close_stream_callback(uv_shutdown_t* req, int status) {
|
|
737
|
+
if(status != 0){
|
|
738
|
+
// TODO error handling
|
|
739
|
+
}
|
|
740
|
+
free(req);
|
|
741
|
+
}
|
|
742
|
+
void c_close_stream(struct Pos stream) {
|
|
743
|
+
uv_stream_t* str = (uv_stream_t*)stream.obj;
|
|
744
|
+
uv_shutdown_t* req = (uv_shutdown_t*)malloc(sizeof(uv_shutdown_t));
|
|
745
|
+
uv_shutdown(req, str, c_close_stream_callback);
|
|
722
746
|
}
|
|
723
747
|
|
|
748
|
+
typedef struct {
|
|
749
|
+
struct Pos buffer;
|
|
750
|
+
uv_buf_t buf;
|
|
751
|
+
} subproc_write_req_data_t;
|
|
752
|
+
void c_write_stream_callback(uv_write_t* req, int status) {
|
|
753
|
+
if(status != 0){
|
|
754
|
+
// TODO error handling
|
|
755
|
+
}
|
|
756
|
+
subproc_write_req_data_t* data = (subproc_write_req_data_t*)(req->data);
|
|
757
|
+
erasePositive(data->buffer);
|
|
758
|
+
free(data);
|
|
759
|
+
free(req);
|
|
760
|
+
}
|
|
761
|
+
void c_write_stream(struct Pos stream, struct Pos buffer) {
|
|
762
|
+
uv_stream_t* str = (uv_stream_t*)stream.obj;
|
|
763
|
+
uv_write_t* req = (uv_write_t*)malloc(sizeof(uv_write_t));
|
|
764
|
+
memset(req, 0, sizeof(uv_write_t));
|
|
765
|
+
subproc_write_req_data_t* data = (subproc_write_req_data_t*)malloc(sizeof(subproc_write_req_data_t));
|
|
766
|
+
data->buffer = buffer;
|
|
767
|
+
uv_buf_t buf = uv_buf_init((char*)(c_bytearray_data(buffer)), buffer.tag);
|
|
768
|
+
req->data = data;
|
|
769
|
+
data->buf = buf;
|
|
770
|
+
uv_write(req, str, &(data->buf), 1, c_write_stream_callback);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
void c_spawn(struct Pos cmd, struct Pos args, struct Pos options, Stack stack) {
|
|
774
|
+
uv_process_options_t* opts = (uv_process_options_t*)options.obj;
|
|
775
|
+
uv_process_t* proc = (uv_process_t*)malloc(sizeof(uv_process_t));
|
|
776
|
+
|
|
777
|
+
// command
|
|
778
|
+
char* cmd_s = c_bytearray_into_nullterminated_string(cmd);
|
|
779
|
+
erasePositive(cmd);
|
|
780
|
+
opts->file = cmd_s;
|
|
781
|
+
|
|
782
|
+
// args
|
|
783
|
+
int _argc = (int)((uint64_t*)args.obj)[2];
|
|
784
|
+
char** _args = (char**)malloc((2 + _argc) * sizeof(char*));
|
|
785
|
+
struct Pos* _argv = (struct Pos*)(((uint64_t*)args.obj) + 3);
|
|
786
|
+
_args[0] = cmd_s;
|
|
787
|
+
for(int i = 0; i < _argc; i++) {
|
|
788
|
+
_args[i + 1] = c_bytearray_into_nullterminated_string(_argv[i]);
|
|
789
|
+
}
|
|
790
|
+
erasePositive(args);
|
|
791
|
+
_args[_argc + 1] = NULL;
|
|
792
|
+
opts->args = _args;
|
|
793
|
+
// callback to free _args
|
|
794
|
+
opts->exit_cb = subproc_on_exit;
|
|
795
|
+
subproc_proc_data_t* pd = (subproc_proc_data_t*)malloc(sizeof(subproc_proc_data_t));
|
|
796
|
+
pd->args = _args;
|
|
797
|
+
pd->k = stack;
|
|
798
|
+
proc->data = pd;
|
|
799
|
+
|
|
800
|
+
// spawn process
|
|
801
|
+
int err = uv_spawn(uv_default_loop(), proc, opts);
|
|
802
|
+
|
|
803
|
+
if(opts->stdio[0].flags & UV_CREATE_PIPE) {
|
|
804
|
+
uv_stream_t* stream = (uv_stream_t*)(opts->stdio[0].data.stream);
|
|
805
|
+
subproc_stream_cb_closure_t* clos = (subproc_stream_cb_closure_t*)(stream->data);
|
|
806
|
+
struct Pos str = (struct Pos) { .tag = 0, .obj = stream, };
|
|
807
|
+
run_Pos(clos->handler, str);
|
|
808
|
+
erasePositive(clos->handler);
|
|
809
|
+
free(clos);
|
|
810
|
+
}
|
|
811
|
+
if(opts->stdio[1].flags & UV_CREATE_PIPE)
|
|
812
|
+
uv_read_start((uv_stream_t*)(opts->stdio[1].data.stream), subproc_alloc_cb, subproc_stream_cb);
|
|
813
|
+
if(opts->stdio[2].flags & UV_CREATE_PIPE)
|
|
814
|
+
uv_read_start((uv_stream_t*)(opts->stdio[2].data.stream), subproc_alloc_cb, subproc_stream_cb);
|
|
815
|
+
pd->opts = opts;
|
|
816
|
+
|
|
817
|
+
erasePositive(options);
|
|
818
|
+
if(err) {
|
|
819
|
+
printf("%s\n", uv_strerror(err));
|
|
820
|
+
free(_args);
|
|
821
|
+
free(proc);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
724
824
|
|
|
725
825
|
#endif
|