@effekt-lang/effekt 0.66.0 → 0.67.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.
@@ -974,12 +974,12 @@ def tails[A](list: List[A]): Unit / emit[List[A]] =
974
974
  case Cons(head, tail) => do emit(list); tails(tail)
975
975
  }
976
976
 
977
- /// Connects `list` as a producer to a given pull stream `reader` acting as a consumer.
978
- def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
977
+ /// Connects `list` as a producer to a given pull stream `driver` acting as a consumer.
978
+ def feed[T, R](list: List[T]) { driver: () => R / next[T] }: R = {
979
979
  var l = list
980
980
  try {
981
- reader()
982
- } with read[T] {
981
+ driver()
982
+ } with next[T] {
983
983
  resume {
984
984
  l match {
985
985
  case Nil() => do stop()
@@ -25,7 +25,9 @@ extern llvm """
25
25
  declare %Pos @c_spawn(%Pos, %Pos, %Pos)
26
26
  """
27
27
 
28
- extern type SpawnOptions
28
+ extern type SpawnOptions =
29
+ jsNode "{ stdio: list[int], onSpawn: fn => {} }"
30
+ llvm "%Pos"
29
31
  extern def default() at {}: SpawnOptions =
30
32
  jsNode """{ stdio: [0,1,2], onSpawn: (p) => {} }"""
31
33
  llvm """
@@ -64,7 +66,9 @@ extern def onStderr(opts: SpawnOptions, callback: String => Unit at {io, global}
64
66
  %opts = call %Pos @c_spawn_options_on_stderr(%Pos ${opts}, %Pos ${callback})
65
67
  ret %Pos %opts
66
68
  """
67
- extern type WritableStream
69
+ extern type WritableStream =
70
+ jsNode "stream.Writable"
71
+ llvm "%Pos"
68
72
  extern def withStdin(opts: SpawnOptions, callback: WritableStream => Unit at {io, global}) at {}: SpawnOptions =
69
73
  jsNode """(function () {
70
74
  let old = ${opts};
@@ -1,6 +1,9 @@
1
1
  module random
2
2
 
3
3
  import stream
4
+ import array
5
+ import option
6
+ import exception
4
7
  import io/filesystem
5
8
  import io/error
6
9
 
@@ -38,7 +41,7 @@ def minstd(seed: Int): Unit / emit[Byte] = {
38
41
  if (result < 0) result + m else result
39
42
  }
40
43
 
41
- while (true) {
44
+ forever {
42
45
  state = nextInt()
43
46
  val b = state.mod(256).toByte
44
47
  do emit(b)
@@ -73,7 +76,7 @@ def devurandom { randomnessReader: () => Unit / random }: Unit / Exception[IOErr
73
76
  try {
74
77
  with readFile("/dev/urandom")
75
78
  try randomnessReader() with random {
76
- resume(do read())
79
+ resume(do next())
77
80
  }
78
81
  } with stop {
79
82
  do raise(io::error::EOF(), "Unexpected EOF when reading /dev/urandom!")
@@ -88,14 +91,14 @@ def devurandom { randomnessReader: () => Unit / random }: Unit / Exception[IOErr
88
91
 
89
92
  def randomByte(): Byte / random = do random()
90
93
  def randomBytes(): Unit / {emit[Byte], random} =
91
- while (true) do emit(do random())
94
+ forever { do emit(do random()) }
92
95
 
93
96
  def randomBool(): Bool / random = {
94
97
  val b = do random()
95
98
  b.toInt.mod(2) == 1
96
99
  }
97
100
  def randomBools(): Unit / {emit[Bool], random} =
98
- while (true) do emit(randomBool())
101
+ forever { do emit(randomBool()) }
99
102
 
100
103
  def randomInt32(): Int / random = {
101
104
  var result = 0
@@ -107,7 +110,7 @@ def randomInt32(): Int / random = {
107
110
  result.mod(1.bitwiseShl(31)).abs * if (signBit) 1 else -1
108
111
  }
109
112
  def randomInt32s(): Unit / {emit[Int], random} =
110
- while (true) do emit(randomInt32())
113
+ forever { do emit(randomInt32()) }
111
114
 
112
115
  /// `max` is _inclusive_!
113
116
  def randomInt(min: Int, max: Int): Int / random = {
@@ -126,9 +129,10 @@ def randomInt(min: Int, max: Int): Int / random = {
126
129
  min + (abs(result).mod(range))
127
130
  }
128
131
  }
132
+
129
133
  /// `max` is _inclusive_!
130
134
  def randomInts(min: Int, max: Int): Unit / {emit[Int], random} =
131
- while (true) do emit(randomInt(min, max))
135
+ forever { do emit(randomInt(min, max)) }
132
136
 
133
137
 
134
138
  /// Random double between 0.0 and 1.0
@@ -136,7 +140,25 @@ def randomDouble(): Double / random =
136
140
  (randomInt32().toDouble / 1.bitwiseShl(31).toDouble).abs
137
141
  // This is not perfect, but it will do for now.
138
142
  def randomDoubles(): Unit / {emit[Double], random} =
139
- while (true) do emit(randomDouble())
143
+ forever { do emit(randomDouble()) }
144
+
145
+ /// Shuffle an array in-place using the Fisher-Yates algorithm.
146
+ def shuffle[A](arr: Array[A]): Unit / random = {
147
+ val n = arr.size
148
+ each(0, n - 1) { i =>
149
+ arr.swap(i, randomInt(i, n - 1))
150
+ }
151
+ }
152
+
153
+ /// Pick a random element from an array.
154
+ ///
155
+ /// Panics when called on an empty array.
156
+ def pick[A](arr: Array[A]): A / random =
157
+ if (arr.size == 0) {
158
+ panic("Pick from an empty array")
159
+ } else {
160
+ arr.get(randomInt(0, arr.size - 1))
161
+ }
140
162
 
141
163
 
142
164
  namespace examples {
@@ -18,7 +18,10 @@ define void @c_ref_erase_field(ptr %0) {
18
18
  """
19
19
 
20
20
  /// Global, mutable references
21
- extern type Ref[T]
21
+ extern type Ref[T] =
22
+ js "{ value: T }"
23
+ chez "box"
24
+ llvm "%Pos"
22
25
 
23
26
  /** We represent references like positive types in LLVM
24
27
  * The tag is 0 and the obj points to memory with the following layout:
@@ -4,7 +4,9 @@ import string
4
4
 
5
5
  extern include chez "text/pregexp.scm"
6
6
 
7
- extern type Regex
7
+ extern type Regex =
8
+ js "RegExp"
9
+ chez "pregexp"
8
10
 
9
11
  record Match(matched: String, index: Int)
10
12
 
@@ -25,9 +27,9 @@ namespace internal {
25
27
 
26
28
  /// The type RegexMatch is used internally to represent platform
27
29
  /// dependent results of matching a regex.
28
- extern type RegexMatch
29
- // js: { matched: String, index: Int } | undefined
30
- // vm: scala.util.matching.Regex.Match | null
30
+ extern type RegexMatch =
31
+ js "{ matched: String, index: Int } | undefined"
32
+ vm "scala.util.matching.Regex.Match | null"
31
33
 
32
34
  extern def matched(r: RegexMatch) at {}: String =
33
35
  js "${r}.matched"
@@ -224,12 +224,12 @@ def each[T](arr: ResizableArray[T]): Unit / emit[T] =
224
224
  do emit(arr.rawContentPtr.get.get(i))
225
225
  }
226
226
 
227
- /// Connects `arr` as a producer to a given pull stream `reader` acting as a consumer.
228
- def feed[R, T](arr: ResizableArray[T]) { reader: () => R / read[T] }: R = {
227
+ /// Connects `arr` as a producer to a given pull stream `driver` acting as a consumer.
228
+ def feed[R, T](arr: ResizableArray[T]) { driver: () => R / next[T] }: R = {
229
229
  var i = 0
230
230
  try {
231
- reader()
232
- } with read[T] {
231
+ driver()
232
+ } with next[T] {
233
233
  resume {
234
234
  if (i < arr.size) {
235
235
  val v = arr.rawContentPtr.get.get(i)
@@ -3,19 +3,10 @@ module scanner
3
3
  import stream
4
4
 
5
5
 
6
- /// Return the next character, not advancing.
7
- /// That is, this does not change what any future calls on the Scanner return.
8
- effect peek[A](): A / stop
9
-
10
- effect Scan[A] = {read[A], peek[A]}
6
+ effect Scan[A] = {next[A], peek[A]}
11
7
 
12
8
  /// Advance the pull stream to the next element, discarding the current element.
13
- def skip[A](): Unit / {read[A], stop} = { do read(); () }
14
-
15
-
16
- /// Run a scanner by reading from an input stream, discarding its output.
17
- def scanner[A] { scanner: () => Unit / Scan[A] }: Unit / read[A] =
18
- returning::scanner[A, Unit]{scanner}
9
+ def skip[A](): Unit / {next[A], stop} = { do next(); () }
19
10
 
20
11
  def expect(message: String) { scanner: () => Unit / fail }: Unit / Exception[WrongFormat] =
21
12
  returning::expect[Unit](message){scanner}
@@ -23,7 +14,7 @@ def expect(message: String) { scanner: () => Unit / fail }: Unit / Exception[Wro
23
14
  /// If conversion is successful read the next token, otherwise stop.
24
15
  def readWhen[A, B] { convert: A => B / fail }: B / { Scan[A], stop } = {
25
16
  val t = attempt { convert(do peek()) } { do stop() };
26
- do read()
17
+ do next()
27
18
  return t
28
19
  }
29
20
 
@@ -31,7 +22,7 @@ def readWhen[A, B] { convert: A => B / fail }: B / { Scan[A], stop } = {
31
22
  def readIf[A] { predicate: A => Bool }: A / { Scan[A], stop } = {
32
23
  val t = do peek()
33
24
  if (predicate(t)) {
34
- do read()
25
+ do next()
35
26
  return t
36
27
  } else {
37
28
  do stop()
@@ -92,10 +83,10 @@ def until[A, R] { predicate: A => Bool } { scanner: () => R / Scan[A] }: R / { S
92
83
  resume {
93
84
  if (stopped) { do stop() } else { do peek() }
94
85
  }
95
- } with read[A] {
86
+ } with next[A] {
96
87
  resume {
97
88
  if (stopped) { do stop() } else {
98
- val t = do read()
89
+ val t = do next()
99
90
  boundary {
100
91
  if (predicate(do peek())) { stopped = true; skip() }
101
92
  }
@@ -166,38 +157,42 @@ def readInteger(): Int / { Scan[Char], fail } =
166
157
  case _ => do fail()
167
158
  }
168
159
 
169
- namespace returning {
170
160
 
171
- /// Run a scanner by reading from an input stream.
172
- def scanner[A, R] { scanner: () => R / Scan[A] }: R / read[A] = {
173
- def upstream(): A / stop = do read()
174
- var last = None()
175
- try {
176
- scanner()
177
- } with peek[A] { () =>
178
- resume {
179
- last match {
180
- case None() =>
181
- val t = upstream()
182
- last = Some(t)
183
- return t
184
- case Some(t) =>
185
- return t
186
- }
161
+ /// Read a decimal floating-point number with optional fraction and exponent.
162
+ def readDouble(): Double / { Scan[Char], fail} = {
163
+ def exponent(pre: Double): Double / fail = {
164
+ try {
165
+ if(do peek() == 'e' || do peek() == 'E') {
166
+ skip[Char]()
167
+ val e = do peek[Char]() match {
168
+ case '+' => skip[Char](); readDecimal()
169
+ case '-' => skip[Char](); neg(readDecimal())
170
+ case _ => readDecimal()
171
+ }
172
+ pre * pow(10.0, e)
173
+ } else { pre }
174
+ } with stop {
175
+ pre
187
176
  }
188
- } with read[A] {
189
- resume {
190
- last match {
191
- case None() =>
192
- upstream()
193
- case Some(t) =>
194
- last = None()
195
- return t
177
+ }
178
+ def fraction(pre: Int): Double / fail = {
179
+ if (succeeds { must { readIf('.') } }) {
180
+ var b = if (pre >= 0) 0.1 else -0.1
181
+ var r = pre.toDouble
182
+ while (optionally[Int] { must { readDigit() } } is Some(d)) {
183
+ r = r + b * d.toDouble
184
+ b = b * 0.1
196
185
  }
186
+ exponent(r)
187
+ } else {
188
+ exponent(pre.toDouble)
197
189
  }
198
190
  }
191
+ fraction(readInteger())
199
192
  }
200
193
 
194
+ namespace returning {
195
+
201
196
  def expect[R](message: String) { scanner: () => R / fail }: R / Exception[WrongFormat] =
202
197
  try { scanner() } with fail { wrongFormat(message) }
203
198
 
@@ -217,7 +212,7 @@ namespace test {
217
212
  with on[WrongFormat].panic
218
213
  with expect("this grammar")
219
214
  with feed(input)
220
- with scanner[Char]
215
+ with peeking[Char]
221
216
 
222
217
  skipWhitespace()
223
218
  println(readInteger().show)
@@ -7,13 +7,13 @@ import partial
7
7
  // Effects
8
8
  // -------
9
9
 
10
- /// Describes push streams by emitting values of type `A`.
11
- effect emit[A](value: A): Unit
10
+ /// The effect for emitting an item of type `A` in a push stream.
11
+ effect emit[A](item: A): Unit
12
12
 
13
- /// Describes pull streams by reading values of type `A`.
13
+ /// The effect for getting the next item of type `A` from a pull stream.
14
14
  ///
15
- /// The producer can decide to `stop` emitting values.
16
- effect read[A](): A / stop
15
+ /// Uses the `stop` effect to signal the end of the stream.
16
+ effect next[A](): A / stop
17
17
 
18
18
 
19
19
  /// Like `for[A, R]`, but ignores the result of the stream, and consequently
@@ -30,11 +30,11 @@ def for[A] { stream: () => Unit / emit[A] } { action: A => Unit }: Unit =
30
30
  def map[A, B] { function: A => B } { stream: () => Unit / emit[A] }: Unit / emit[B] =
31
31
  returning::map[A, B, Unit]{function}{stream}
32
32
 
33
- /// Runs the given action whenever the given reader reads an element.
33
+ /// Runs the given action whenever the given driver reads an element.
34
34
  ///
35
- /// e.g. var i = -1; with tap { i = i + 1; i }; reader()
36
- def tap[A] { action: () => A / stop } { reader: () => Unit / read[A] }: Unit =
37
- returning::tap[A, Unit]{action}{reader}
35
+ /// e.g. var i = -1; with tap { i = i + 1; i }; driver()
36
+ def tap[A] { action: () => A / stop } { driver: () => Unit / next[A] }: Unit =
37
+ returning::tap[A, Unit]{action}{driver}
38
38
 
39
39
  /// Runs the stream as a message queue,
40
40
  /// handling each of its elements that can produce new elements ("messages"),
@@ -42,8 +42,8 @@ def tap[A] { action: () => A / stop } { reader: () => Unit / read[A] }: Unit =
42
42
  def fix[T] { one: T => Unit / emit[T] } { stream: => Unit / emit[T] }: Unit =
43
43
  try {
44
44
  stream()
45
- } with emit[T] { value =>
46
- fix {one} { one(value) }
45
+ } with emit[T] { item =>
46
+ fix {one} { one(item) }
47
47
  resume(())
48
48
  }
49
49
 
@@ -58,7 +58,7 @@ def some[A] { action: () => A / stop }: Unit / { emit[A], fail } = {
58
58
 
59
59
 
60
60
  /// In Effekt lower bounds are inclusive and upper bounds are exclusive
61
- record Indexed[A](index: Int, value: A)
61
+ record Indexed[A](index: Int, item: A)
62
62
 
63
63
  def range(lower: Int, upper: Int): Unit / emit[Int] =
64
64
  if (lower < upper) {
@@ -116,7 +116,7 @@ def iterate[A](initial: A) { step: A => A }: Unit / emit[A] = {
116
116
  /// ```
117
117
  def takeWhile[A] { predicate: A => Bool } { stream: () => Unit / emit[A] }: Unit / emit[A] =
118
118
  try { stream() } with emit[A] {
119
- case value and predicate(value) => do emit(value); resume(())
119
+ case item and predicate(item) => do emit(item); resume(())
120
120
  case _ => () // end the stream
121
121
  }
122
122
 
@@ -146,7 +146,7 @@ def product { stream: () => Unit / emit[Int] }: Int =
146
146
  /// ```
147
147
  def all[A] { cond: A => Bool } { stream: => Unit / emit[A] }: Bool =
148
148
  try { stream(); true } with emit[A] {
149
- case value and not(cond(value)) => false
149
+ case item and not(cond(item)) => false
150
150
  case _ => resume(())
151
151
  }
152
152
 
@@ -169,7 +169,7 @@ def all[A] { cond: A => Bool } { stream: => Unit / emit[A] }: Bool =
169
169
  /// ```
170
170
  def any[A] { cond: A => Bool } { stream: => Unit / emit[A] }: Bool =
171
171
  try { stream(); false } with emit[A] {
172
- case value and cond(value) => true
172
+ case item and cond(item) => true
173
173
  case _ => resume(())
174
174
  }
175
175
 
@@ -196,8 +196,8 @@ def countIf[A] { shouldCount: A => Bool } { stream: () => Unit / emit[A] }: Int
196
196
  def count[A] { stream: () => Unit / emit[A] }: Int =
197
197
  countIf[A] { _ => true } {stream}
198
198
 
199
- def source[A] { stream: () => Unit / emit[A] } { reader: () => Unit / read[A] }: Unit =
200
- returning::source[A, Unit]{stream}{reader}
199
+ def source[A] { stream: () => Unit / emit[A] } { driver: () => Unit / next[A] }: Unit =
200
+ returning::source[A, Unit]{stream}{driver}
201
201
 
202
202
  /// Combines two streams together calling action on pairs in lockstep.
203
203
  /// Terminates when either of them terminates.
@@ -206,7 +206,7 @@ def zip[A, B] { stream1: () => Unit / emit[A] } { stream2: () => Unit / emit[B]
206
206
  with source[B] { stream2 }
207
207
 
208
208
  loop {
209
- action(do read(), do read())
209
+ action(do next[A](), do next[B]())
210
210
  }
211
211
  }
212
212
 
@@ -218,15 +218,15 @@ def zipLongest[A, B] { stream1: () => Unit / emit[A] } { stream2: () => Unit / e
218
218
 
219
219
  loop {
220
220
  try {
221
- val a = do read[A]()
221
+ val a = do next[A]()
222
222
  try {
223
- val b = do read[B]()
223
+ val b = do next[B]()
224
224
  action(Both(a, b))
225
225
  } with stop { () =>
226
226
  action(This(a))
227
227
  }
228
228
  } with stop{ () =>
229
- val b = do read[B]()
229
+ val b = do next[B]()
230
230
  action(That(b))
231
231
  }
232
232
  }
@@ -328,8 +328,8 @@ def tee[A]{ cons1: { => Unit / emit[A] } => Unit }{ cons2: { => Unit / emit[A] }
328
328
  /// ```
329
329
  def first[A] { stream: () => Unit / emit[A] }: A / fail = {
330
330
  try {
331
- def body(): A = { stream(); do fail() }
332
- body()
331
+ stream()
332
+ do fail().absurd
333
333
  } with emit[A] { a => a }
334
334
  }
335
335
 
@@ -338,8 +338,8 @@ def first[A] { stream: () => Unit / emit[A] }: A / fail = {
338
338
  def nth[A](n: Int){ stream: () => Unit / emit[A] }: A / fail = {
339
339
  var m = n
340
340
  try {
341
- def body(): A = { stream(); do fail() }
342
- body()
341
+ stream()
342
+ do fail().absurd
343
343
  } with emit[A] { a =>
344
344
  if (m == 0) {
345
345
  return a
@@ -350,6 +350,16 @@ def nth[A](n: Int){ stream: () => Unit / emit[A] }: A / fail = {
350
350
  }
351
351
  }
352
352
 
353
+ // Peeking
354
+ // -------
355
+
356
+ /// Return the next item without advancing.
357
+ effect peek[A](): A / stop
358
+
359
+ /// Provide the ability to peek when consuming a stream.
360
+ def peeking[A] { peeker: () => Unit / {next[A], peek[A]} }: Unit / next[A] =
361
+ returning::peeking[A, Unit]{peeker}
362
+
353
363
  // Counting and padding
354
364
  // --------------------
355
365
 
@@ -380,25 +390,25 @@ def tracking[A]{ body: => Unit / { emit[A], getPos } }: Unit / emit[A] =
380
390
  namespace returning {
381
391
 
382
392
  /// Canonical handler of push streams that performs `action` for every
383
- /// value emitted by `stream`.
393
+ /// item emitted by `stream`.
384
394
  def for[A, R] { stream: () => R / emit[A] } { action: A => Unit }: R =
385
395
  try {
386
396
  stream()
387
- } with emit[A] { value =>
388
- resume(action(value))
397
+ } with emit[A] { item =>
398
+ resume(action(item))
389
399
  }
390
400
 
391
401
  def map[A, B, R] { function: A => B } { stream: () => R / emit[A] }: R / emit[B] =
392
402
  try {
393
403
  stream()
394
- } with emit[A] { value =>
395
- resume(do emit(function(value)))
404
+ } with emit[A] { item =>
405
+ resume(do emit(function(item)))
396
406
  }
397
407
 
398
- def tap[A, R] { action: () => A / stop } { reader: () => R / read[A] }: R =
408
+ def tap[A, R] { action: () => A / stop } { driver: () => R / next[A] }: R =
399
409
  try {
400
- reader()
401
- } with read[A] { () =>
410
+ driver()
411
+ } with next[A] { () =>
402
412
  resume { action() }
403
413
  }
404
414
 
@@ -460,7 +470,7 @@ def product[R] { stream: () => R / emit[Int] }: (R, Int) = {
460
470
  }
461
471
  }
462
472
 
463
- def source[A, R] { stream: () => Unit / emit[A] } { reader: () => R / read[A] }: R = {
473
+ def source[A, R] { stream: () => Unit / emit[A] } { driver: () => R / next[A] }: R = {
464
474
  var next = box { None() }
465
475
  next = box {
466
476
  try {
@@ -473,12 +483,42 @@ def source[A, R] { stream: () => Unit / emit[A] } { reader: () => R / read[A] }:
473
483
  }
474
484
  }
475
485
  try {
476
- reader()
477
- } with read[A] {
486
+ driver()
487
+ } with next[A] {
478
488
  resume {
479
489
  next().getOrElse { do stop() }
480
490
  }
481
491
  }
482
492
  }
483
493
 
494
+ /// Provide the ability to peek when consuming a stream.
495
+ def peeking[A, R] { peeker: () => R / {next[A], peek[A]} }: R / next[A] = {
496
+ def upstream(): A / stop = do next()
497
+ var last = None()
498
+ try {
499
+ peeker()
500
+ } with peek[A] { () =>
501
+ resume {
502
+ last match {
503
+ case None() =>
504
+ val t = upstream()
505
+ last = Some(t)
506
+ return t
507
+ case Some(t) =>
508
+ return t
509
+ }
510
+ }
511
+ } with next[A] {
512
+ resume {
513
+ last match {
514
+ case None() =>
515
+ upstream()
516
+ case Some(t) =>
517
+ last = None()
518
+ return t
519
+ }
520
+ }
521
+ }
522
+ }
523
+
484
524
  }
@@ -237,17 +237,13 @@ extern def unsafeCharAt(str: String, n: Int) at {}: Char =
237
237
  // Streaming
238
238
  // ---------
239
239
 
240
- def feed[R](string: String) { reader: () => R / read[Char] } =
241
- feed(string.fromString) {
242
- decodeUTF8 {
243
- reader()
244
- }
245
- }
240
+ def feed[R](string: String) { driver: () => R / next[Char] } =
241
+ feed(string.fromString) { decodeUTF8 { driver() } }
246
242
 
247
243
  def each(string: String): Unit / emit[Char] =
248
244
  feed(string) {
249
245
  loop {
250
- do emit(do read())
246
+ do emit(do next())
251
247
  }
252
248
  }
253
249
 
@@ -576,61 +576,6 @@ void c_yield(Stack stack) {
576
576
  c_timer_start(0, stack);
577
577
  }
578
578
 
579
- // Signals
580
- // --------
581
-
582
- typedef enum { INITIAL, NOTIFIED } signal_state_t;
583
-
584
- typedef struct {
585
- uint64_t rc;
586
- void* eraser;
587
- signal_state_t state;
588
- struct Pos value;
589
- Stack stack;
590
- } Signal;
591
-
592
- void c_signal_erase(void *envPtr) {
593
- // envPtr points to a Signal _after_ the eraser, so let's adjust it to point to the beginning.
594
- Signal *signal = (Signal*) (envPtr - offsetof(Signal, state));
595
- erasePositive(signal->value);
596
- if (signal->stack != NULL) { eraseStack(signal->stack); }
597
- }
598
-
599
- struct Pos c_signal_make() {
600
- Signal* f = (Signal*)malloc(sizeof(Signal));
601
-
602
- f->rc = 0;
603
- f->eraser = c_signal_erase;
604
- f->state = INITIAL;
605
- f->value = (struct Pos) { .tag = 0, .obj = NULL, };
606
- f->stack = NULL;
607
-
608
- return (struct Pos) { .tag = 0, .obj = f, };
609
- }
610
-
611
- void c_signal_notify(struct Pos signal, struct Pos value, Stack stack) {
612
- Signal* f = (Signal*)signal.obj;
613
- switch (f->state) {
614
- case INITIAL: {
615
- f->state = NOTIFIED;
616
- f->value = value;
617
- f->stack = stack;
618
- erasePositive(signal);
619
- break;
620
- }
621
- case NOTIFIED: {
622
- struct Pos other_value = f->value;
623
- f->value = (struct Pos) { .tag = 0, .obj = NULL, };
624
- Stack other_stack = f->stack;
625
- f->stack = NULL;
626
- erasePositive(signal);
627
- resume_Pos(other_stack, value);
628
- resume_Pos(stack, other_value);
629
- break;
630
- }
631
- }
632
- }
633
-
634
579
  // Subprocesses
635
580
  // ------------
636
581