@effekt-lang/effekt 0.64.0 → 0.66.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 CHANGED
Binary file
@@ -47,6 +47,7 @@
47
47
  [r (diff-ref realDiff)]
48
48
  [oldValue (ref-value r)])
49
49
  (ref-value-set! r (diff-value realDiff))
50
+ (ref-generation-set! r (diff-generation realDiff))
50
51
  (set-box! n (make-diff r oldValue (ref-generation r) currentDiff))
51
52
  (applyDiffs currentDiff (cdr diffs)))]))
52
53
 
@@ -60,10 +61,11 @@
60
61
  [store (snap-store snap)]
61
62
  [storeRoot (store-root store)])
62
63
  (reroot snapRoot storeRoot)
63
- (store-root-set! store snapRoot)))
64
+ (store-root-set! store snapRoot)
65
+ (store-generation-set! store (+ (snap-generation snap) 1))))
64
66
 
65
67
  ; Cont a = a, MetaCont -> #
66
- ; Prompt = Int
68
+ ; Prompt = Symbol
67
69
 
68
70
  ; MetaCont = Cont * Prompt * Store | Snapshot * MetaCont?
69
71
  ; Holds a "copy" of k, as well as its prompt and store
@@ -102,14 +104,8 @@
102
104
  ; Ref | MetaCont -> void
103
105
  (define (deallocate _) (void))
104
106
 
105
- (define _prompt 1)
106
- (define (new-prompt)
107
- (let ([prompt _prompt])
108
- (set! _prompt (+ prompt 1))
109
- prompt))
110
-
111
107
  (define (top-level-k x _) x)
112
- (define top-level-ks (make-meta-cont top-level-k 0 (create-store) '()))
108
+ (define top-level-ks (make-meta-cont top-level-k (gensym "toplevel") (create-store) '()))
113
109
 
114
110
  ; a, MetaCont -> #
115
111
  (define (return x ks)
@@ -121,7 +117,7 @@
121
117
 
122
118
  ; Program Prompt b, MetaCont, Cont b -> #
123
119
  (define (reset prog ks k)
124
- (let ([prompt (new-prompt)])
120
+ (let ([prompt (gensym)])
125
121
  (meta-cont-cont-set! ks k)
126
122
  (prog prompt (make-meta-cont return prompt (create-store) ks) return)))
127
123
 
@@ -132,7 +128,7 @@
132
128
  [snap (snapshot (meta-cont-store below))])
133
129
  (meta-cont-store-set! below snap)
134
130
  (meta-cont-rest-set! below above)
135
- (if (= (meta-cont-prompt below) p)
131
+ (if (symbol=? (meta-cont-prompt below) p)
136
132
  (values below new-below)
137
133
  (worker below new-below))))
138
134
  (worker '() ks))
@@ -166,4 +162,4 @@
166
162
 
167
163
  ; Block b -> b
168
164
  (define (run-top-level p)
169
- (p top-level-ks top-level-k))
165
+ (p top-level-ks top-level-k))
@@ -677,18 +677,27 @@ def feed[T, R](array: Array[T]) { reader: () => R / read[T] }: R = {
677
677
  }
678
678
  }
679
679
 
680
+ /// Collects elements emitted by a push stream producer `stream` into a new array,
681
+ /// discarding the result of the producer.
682
+ /// The provided capacity is a hint for the expected number of elements.
683
+ /// It must be strictly larger than zero.
684
+ def collect[A](capacity: Int) { stream: () => Unit / emit[A] }: Array[A] =
685
+ returning::collect[A, Unit](capacity){stream}.second
686
+
680
687
  /// Collects elements emitted by a push stream producer `stream` into a new array,
681
688
  /// discarding the result of the producer.
682
689
  def collect[A] { stream: () => Unit / emit[A] }: Array[A] =
683
- returning::collect[A, Unit]{stream}.second
690
+ collect[A](4){stream}
684
691
 
685
692
  namespace returning {
686
693
 
687
694
  /// Collects elements emitted by a push stream producer `stream` into a new array,
688
695
  /// returning the result of the producer as well as the collected array.
689
- def collect[A, R] { stream: () => R / emit[A] }: (R, Array[A]) = {
696
+ /// The provided capacity is a hint for the expected number of elements.
697
+ /// It must be strictly larger than zero.
698
+ def collect[A, R](capacity: Int) { stream: () => R / emit[A] }: (R, Array[A]) = {
690
699
  var i = 0
691
- var a = unsafeAllocate(1)
700
+ var a = unsafeAllocate(capacity)
692
701
  try {
693
702
  (stream(), a.resize(i))
694
703
  } with emit[A] { (v) =>
@@ -698,4 +707,10 @@ namespace returning {
698
707
  resume(())
699
708
  }
700
709
  }
710
+
711
+ /// Collects elements emitted by a push stream producer `stream` into a new array,
712
+ /// returning the result of the producer as well as the collected array.
713
+ def collect[A, R] { stream: () => R / emit[A] }: (R, Array[A]) =
714
+ returning::collect[A, R](4){stream}
715
+
701
716
  }
@@ -300,13 +300,17 @@ def feed[R](bytes: ByteArray) { reader: () => R / read[Byte] }: R = {
300
300
  }
301
301
  }
302
302
 
303
+ def collect(capacity: Int) { stream: () => Unit / emit[Byte] }: ByteArray =
304
+ returning::collect[Unit](capacity){stream}.second
305
+
303
306
  def collect { stream: () => Unit / emit[Byte] }: ByteArray =
304
- returning::collect[Unit]{stream}.second
307
+ collect(4){stream}
305
308
 
306
309
  namespace returning {
307
- def collect[R] { stream: () => R / emit[Byte] }: (R, ByteArray) = {
310
+
311
+ def collect[R](capacity: Int) { stream: () => R / emit[Byte] }: (R, ByteArray) = {
308
312
  var i = 0
309
- var a = allocate(1)
313
+ var a = allocate(capacity)
310
314
  try {
311
315
  (stream(), a.resize(i))
312
316
  } with emit[Byte] { (v) =>
@@ -316,4 +320,8 @@ namespace returning {
316
320
  resume(())
317
321
  }
318
322
  }
323
+
324
+ def collect[R] { stream: () => R / emit[Byte] }: (R, ByteArray) =
325
+ returning::collect(4){stream}
326
+
319
327
  }
@@ -139,16 +139,20 @@ extern def show[R](value: R) at {}: String =
139
139
  chez "(show_impl ${value})"
140
140
  llvm "ret %Pos poison"
141
141
 
142
+ extern def genericShow[R](value: R) at {}: String =
143
+ js "$effekt.show(${value})"
144
+ chez "(show_impl ${value})"
145
+
142
146
  // Strings
143
147
  // =======
144
- extern def infixConcat(s1: String, s2: String) at {}: String =
148
+ extern def infixPlusPlus(s1: String, s2: String) at {}: String =
145
149
  js "${s1} + ${s2}"
146
150
  chez "(string-append ${s1} ${s2})"
147
151
  llvm """
148
152
  %spz = call %Pos @c_bytearray_concatenate(%Pos ${s1}, %Pos ${s2})
149
153
  ret %Pos %spz
150
154
  """
151
- vm "effekt::infixConcat(String, String)"
155
+ vm "effekt::infixPlusPlus(String, String)"
152
156
 
153
157
  extern def length(str: String) at {}: Int =
154
158
  js "${str}.length"
@@ -231,13 +235,10 @@ def println(o: Ordering): Unit = println(o.show)
231
235
  // ==============
232
236
 
233
237
  /// Structural equality: Not available in the LLVM backend
234
- extern def equals[R](x: R, y: R) at {}: Bool =
238
+ extern def genericEquals[R](x: R, y: R) at {}: Bool =
235
239
  js "$effekt.equals(${x}, ${y})"
236
240
  chez "(equal_impl ${x} ${y})"
237
241
 
238
- def differsFrom[R](x: R, y: R): Bool =
239
- not(equals(x, y))
240
-
241
242
  // TODO fix this in the pattern matching compiler
242
243
  extern def infixEq(x: Unit, y: Unit) at {}: Bool =
243
244
  default { true }
@@ -345,29 +346,29 @@ extern def infixNeq(x: Byte, y: Byte) at {}: Bool =
345
346
 
346
347
  // Math ops
347
348
  // ========
348
- extern def infixAdd(x: Int, y: Int) at {}: Int =
349
+ extern def infixPlus(x: Int, y: Int) at {}: Int =
349
350
  js "(${x} + ${y})"
350
351
  chez "(+ ${x} ${y})"
351
352
  llvm "%z = add %Int ${x}, ${y} ret %Int %z"
352
- vm "effekt::infixAdd(Int, Int)"
353
+ vm "effekt::infixPlus(Int, Int)"
353
354
 
354
- extern def infixMul(x: Int, y: Int) at {}: Int =
355
+ extern def infixStar(x: Int, y: Int) at {}: Int =
355
356
  js "(${x} * ${y})"
356
357
  chez "(* ${x} ${y})"
357
358
  llvm "%z = mul %Int ${x}, ${y} ret %Int %z"
358
- vm "effekt::infixMul(Int, Int)"
359
+ vm "effekt::infixStar(Int, Int)"
359
360
 
360
- extern def infixDiv(x: Int, y: Int) at {}: Int =
361
+ extern def infixSlash(x: Int, y: Int) at {}: Int =
361
362
  js "Math.floor(${x} / ${y})"
362
363
  chez "(floor (/ ${x} ${y}))"
363
364
  llvm "%z = sdiv %Int ${x}, ${y} ret %Int %z"
364
- vm "effekt::infixDiv(Int, Int)"
365
+ vm "effekt::infixSlash(Int, Int)"
365
366
 
366
- extern def infixSub(x: Int, y: Int) at {}: Int =
367
+ extern def infixMinus(x: Int, y: Int) at {}: Int =
367
368
  js "(${x} - ${y})"
368
369
  chez "(- ${x} ${y})"
369
370
  llvm "%z = sub %Int ${x}, ${y} ret %Int %z"
370
- vm "effekt::infixSub(Int, Int)"
371
+ vm "effekt::infixMinus(Int, Int)"
371
372
 
372
373
  extern def mod(x: Int, y: Int) at {}: Int =
373
374
  js "(${x} % ${y})"
@@ -375,29 +376,29 @@ extern def mod(x: Int, y: Int) at {}: Int =
375
376
  llvm "%z = srem %Int ${x}, ${y} ret %Int %z"
376
377
  vm "effekt::mod(Int, Int)"
377
378
 
378
- extern def infixAdd(x: Double, y: Double) at {}: Double =
379
+ extern def infixPlus(x: Double, y: Double) at {}: Double =
379
380
  js "(${x} + ${y})"
380
381
  chez "(+ ${x} ${y})"
381
382
  llvm "%z = fadd %Double ${x}, ${y} ret %Double %z"
382
- vm "effekt::infixAdd(Double, Double)"
383
+ vm "effekt::infixPlus(Double, Double)"
383
384
 
384
- extern def infixMul(x: Double, y: Double) at {}: Double =
385
+ extern def infixStar(x: Double, y: Double) at {}: Double =
385
386
  js "(${x} * ${y})"
386
387
  chez "(* ${x} ${y})"
387
388
  llvm "%z = fmul %Double ${x}, ${y} ret %Double %z"
388
- vm "effekt::infixMul(Double, Double)"
389
+ vm "effekt::infixStar(Double, Double)"
389
390
 
390
- extern def infixSub(x: Double, y: Double) at {}: Double =
391
+ extern def infixMinus(x: Double, y: Double) at {}: Double =
391
392
  js "(${x} - ${y})"
392
393
  chez "(- ${x} ${y})"
393
394
  llvm "%z = fsub %Double ${x}, ${y} ret %Double %z"
394
- vm "effekt::infixSub(Double, Double)"
395
+ vm "effekt::infixMinus(Double, Double)"
395
396
 
396
- extern def infixDiv(x: Double, y: Double) at {}: Double =
397
+ extern def infixSlash(x: Double, y: Double) at {}: Double =
397
398
  js "(${x} / ${y})"
398
399
  chez "(fl/ ${x} ${y})"
399
400
  llvm "%z = fdiv %Double ${x}, ${y} ret %Double %z"
400
- vm "effekt::infixDiv(Double, Double)"
401
+ vm "effekt::infixSlash(Double, Double)"
401
402
 
402
403
  extern def cos(x: Double) at {}: Double =
403
404
  js "Math.cos(${x})"
@@ -661,10 +662,10 @@ extern def not(b: Bool) at {}: Bool =
661
662
  """
662
663
  vm "effekt::not(Bool)"
663
664
 
664
- def infixOr { first: => Bool } { second: => Bool }: Bool =
665
+ def infixBarBar { first: => Bool } { second: => Bool }: Bool =
665
666
  if (first()) true else second()
666
667
 
667
- def infixAnd { first: => Bool } { second: => Bool }: Bool =
668
+ def infixAmpAmp { first: => Bool } { second: => Bool }: Bool =
668
669
  if (first()) second() else false
669
670
 
670
671
 
@@ -3,29 +3,34 @@ module io/channel
3
3
  import io
4
4
  import io/signal
5
5
 
6
- record Node[A, B](this: Signal[A, B], rest: Ref[Option[Node[A, B]]])
6
+ type Node[A, B] {
7
+ Nope()
8
+ Node(this: Signal[A, B], rest: Ref[Node[A, B]])
9
+ }
7
10
 
8
- type Sender[A, B] = Ref[Node[A, B]]
9
- type Receiver[A, B] = Ref[Node[A, B]]
11
+ type Sender[A, B] = Ref[Ref[Node[A, B]]]
12
+ type Receiver[A, B] = Ref[Ref[Node[A, B]]]
10
13
 
11
14
  /// Creates a new rendezvous channel and returns the receiving and the sending end.
12
15
  /// Every send must be matched by exactly one receive upon which two values are exchanged.
13
16
  def channel[A, B](): (Sender[A, B], Receiver[A, B]) = {
14
- val node = Node(signal(), ref(None()))
17
+ val node = ref(Nope())
15
18
  (ref(node), ref(node))
16
19
  }
17
20
 
18
21
  /// Sends a value to a channel and blocks until a receiver arrives.
19
22
  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)
23
+ val current = sender.get()
24
+ val (this, rest) = current.get() match {
25
+ case Nope() =>
26
+ val this = signal()
27
+ val rest = ref(Nope())
28
+ current.set(Node(this, rest))
29
+ (this, rest)
30
+ case Node(this, rest) =>
31
+ (this, rest)
28
32
  }
33
+ sender.set(rest)
29
34
  // TODO avoid yield
30
35
  yield()
31
36
  this.unsafeFire(value)
@@ -33,15 +38,17 @@ def send[A, B](sender: Sender[A, B], value: A): B = {
33
38
 
34
39
  /// Receives a value from a channel and blocks until a sender arrives.
35
40
  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)
41
+ val current = receiver.get()
42
+ val (this, rest) = current.get() match {
43
+ case Nope() =>
44
+ val this = signal()
45
+ val rest = ref(Nope())
46
+ current.set(Node(this, rest))
47
+ (this, rest)
48
+ case Node(this, rest) =>
49
+ (this, rest)
44
50
  }
51
+ receiver.set(rest)
45
52
  // TODO avoid yield
46
53
  yield()
47
54
  this.unsafeWait(value)
@@ -419,6 +419,18 @@ def reverseOnto[A](l: List[A], other: List[A]): List[A] =
419
419
  def append[A](l: List[A], other: List[A]): List[A] =
420
420
  l.reverse.reverseOnto(other)
421
421
 
422
+ /// `l ++ other` is an alias for `l.append(other)`.
423
+ ///
424
+ /// Example:
425
+ /// ```
426
+ /// > [1, 2, 3] ++ [4, 5, 6]
427
+ /// [1, 2, 3, 4, 5, 6]
428
+ /// ```
429
+ ///
430
+ /// O(N)
431
+ def infixPlusPlus[A](l: List[A], other: List[A]): List[A] =
432
+ append(l, other)
433
+
422
434
  /// Flatten a list of lists into a single list.
423
435
  ///
424
436
  /// Examples:
@@ -43,7 +43,7 @@ extern def unsafeAllocate[T]() at global: Ref[T] =
43
43
  store i64 0, ptr %ref, align 8
44
44
  store ptr @c_ref_erase_field, ptr %refEraser, align 8
45
45
  store i64 0, ptr %fieldTag, align 8
46
- store ptr null, ptr %fieldData_pointer, align 8
46
+ store ptr null, ptr %fieldData_pointer, align 8
47
47
 
48
48
  %refWrap = insertvalue %Pos { i64 0, ptr poison }, ptr %ref, 1
49
49
  ret %Pos %refWrap
@@ -99,7 +99,7 @@ extern def set[T](ref: Ref[T], value: T) at global: Unit =
99
99
 
100
100
  tail call void @erasePositive(%Pos %field)
101
101
  store %Pos ${value}, ptr %field_pointer
102
- tail call void @erasePositive(%Pos ${ref})
102
+ tail call void @erasePositive(%Pos ${ref})
103
103
 
104
104
  ret %Pos zeroinitializer
105
105
  """
@@ -107,4 +107,8 @@ extern def set[T](ref: Ref[T], value: T) at global: Unit =
107
107
 
108
108
  /// Modifies the inner contents of the `ref` with a function `f`
109
109
  def map[T](ref: Ref[T]) { f: T => T }: Unit =
110
- ref.set(f(ref.get))
110
+ ref.set(f(ref.get))
111
+
112
+ /// `r := v` serves as an alias for `r.set(v)`
113
+ def infixColonEq[T](ref: Ref[T], v: T): Unit =
114
+ ref.set(v)
@@ -251,12 +251,20 @@ def each(string: String): Unit / emit[Char] =
251
251
  }
252
252
  }
253
253
 
254
+ def collect(capacity: Int) { stream: () => Unit / emit[Char] }: String =
255
+ returning::collect[Unit](capacity){stream}.second
256
+
254
257
  def collect { stream: () => Unit / emit[Char] }: String =
255
- returning::collect[Unit]{stream}.second
258
+ collect(4){stream}
256
259
 
257
260
  namespace returning {
258
- def collect[R] { stream: () => R / emit[Char] }: (R, String) = {
259
- val (result, bytes) = bytearray::returning::collect[R] { encodeUTF8 { stream } }
261
+
262
+ def collect[R](capacity: Int) { stream: () => R / emit[Char] }: (R, String) = {
263
+ val (result, bytes) = bytearray::returning::collect[R](capacity) { encodeUTF8 { stream } }
260
264
  (result, bytes.toString)
261
265
  }
266
+
267
+ def collect[R] { stream: () => R / emit[Char] }: (R, String) =
268
+ returning::collect(4){stream}
269
+
262
270
  }
@@ -46,10 +46,10 @@ def assert(obtained: String, expected: String): Unit / { Assertion, Formatted }
46
46
  assertEqual(obtained, expected) { (x, y) => x == y } { x => show(x) }
47
47
 
48
48
  def assertEqual[A](obtained: A, expected: A, msg: String): Unit / Assertion =
49
- assertEqual(obtained, expected, msg) { (x, y) => x.equals(y) }
49
+ assertEqual(obtained, expected, msg) { (x, y) => x.genericEquals(y) }
50
50
 
51
51
  def assertEqual[A](obtained: A, expected: A): Unit / { Assertion, Formatted } =
52
- assertEqual(obtained, expected) { (x, y) => x.equals(y) } { x => x.show }
52
+ assertEqual(obtained, expected) { (x, y) => x.genericEquals(y) } { x => x.genericShow }
53
53
 
54
54
  def assertEqual[A](obtained: A, expected: A, msg: String) { equals: (A, A) => Bool }: Unit / Assertion =
55
55
  do assert(equals(obtained, expected), msg)
@@ -1,43 +1,60 @@
1
- // Complexity of state:
2
- //
3
- // get: O(1)
4
- // set: O(1)
5
- // capture: O(1)
6
- // restore: O(|write operations since capture|)
1
+
2
+ // Sentinel: a node whose .value is Mem is the current root.
7
3
  const Mem = null
8
4
 
9
- function Arena() {
10
- const s = {
11
- root: { value: Mem },
12
- generation: 0,
13
- fresh: (v) => {
14
- const r = {
15
- value: v,
16
- generation: s.generation,
17
- store: s,
18
- set: (v) => {
19
- const s = r.store
20
- const r_gen = r.generation
21
- const s_gen = s.generation
22
-
23
- if (r_gen == s_gen) {
24
- r.value = v;
25
- } else {
26
- const root = { value: Mem }
27
- // update store
28
- s.root.value = { ref: r, value: r.value, generation: r_gen, root: root }
29
- s.root = root
30
- r.value = v
31
- r.generation = s_gen
32
- }
33
- }
34
- };
35
- return r
36
- },
37
- // not implemented
38
- newRegion: () => s
39
- };
40
- return s
5
+ // reusable buffer for rerooting
6
+ const _rerootPath = []
7
+
8
+ /**
9
+ * A mutable reference inside an `Arena`.
10
+ */
11
+ class Ref {
12
+ constructor(v, s) {
13
+ this.value = v;
14
+ this.generation = s.generation;
15
+ this.store = s;
16
+ }
17
+
18
+ set(v) {
19
+ const s = this.store;
20
+ const r_gen = this.generation;
21
+ const s_gen = s.generation;
22
+ if (r_gen === s_gen) {
23
+ this.value = v;
24
+ } else {
25
+ const root = { value: Mem };
26
+ s.root.value = { ref: this, value: this.value, generation: r_gen, root };
27
+
28
+ s.root = root;
29
+ this.value = v;
30
+ this.generation = s_gen;
31
+ }
32
+ }
33
+ }
34
+
35
+ /**
36
+ * A snapshottable arena: a bag of `Ref`s whose collective state can be
37
+ * captured in O(1) and restored in O(#writes since capture).
38
+ */
39
+ class Arena {
40
+ constructor() {
41
+ this.root = { value: Mem };
42
+ this.generation = 0;
43
+ }
44
+
45
+ /**
46
+ * Allocate a new reference with initial value v.
47
+ */
48
+ fresh(v) {
49
+ return new Ref(v, this);
50
+ }
51
+
52
+ /**
53
+ * Region support (not implemented!).
54
+ */
55
+ newRegion() {
56
+ return this;
57
+ }
41
58
  }
42
59
 
43
60
  function snapshot(s) {
@@ -46,34 +63,43 @@ function snapshot(s) {
46
63
  return snap
47
64
  }
48
65
 
49
- function reroot(n) {
50
- if (n.value === Mem) return;
51
-
52
- const diff = n.value
53
- const r = diff.ref
54
- const v = diff.value
55
- const g = diff.generation
56
- const n2 = diff.root
57
- reroot(n2)
58
- n.value = Mem
59
- n2.value = { ref: r, value: r.value, generation: r.generation, root: n}
60
- r.value = v
61
- r.generation = g
66
+ function reroot(target) {
67
+ // 1. Walk from target toward the Mem node, collecting the path into `_rerootPath`
68
+ _rerootPath.length = 0
69
+ let cur = target
70
+ while (cur.value !== Mem) {
71
+ _rerootPath.push(cur)
72
+ cur = cur.value.root
73
+ }
74
+ // cur is now Mem (the current root)
75
+
76
+ // 2. Walk back from current root toward target,
77
+ // reverse each edge, restore each ref.
78
+ for (let i = _rerootPath.length - 1; i >= 0; i--) {
79
+ const node = _rerootPath[i]
80
+ const diff = node.value // forward diff
81
+ const r = diff.ref
82
+ cur.value = { ref: r, value: r.value, generation: r.generation, root: node }
83
+ r.value = diff.value // restore ref's old value
84
+ r.generation = diff.generation // ... and old generation
85
+ node.value = Mem
86
+ cur = node
87
+ }
62
88
  }
63
89
 
64
90
  function restore(store, snap) {
65
- // linear in the number of modifications...
66
- reroot(snap.root)
67
- store.root = snap.root
91
+ if (snap.root.value !== Mem) {
92
+ // fast path: continuation is resumed immediately with no writes in between
93
+ reroot(snap.root)
94
+ store.root = snap.root
95
+ }
68
96
  store.generation = snap.generation + 1
69
97
  }
70
98
 
71
99
  // Common Runtime
72
100
  // --------------
73
- let _prompt = 1;
74
-
75
101
  const TOPLEVEL_K = (x, ks) => { throw { computationIsDone: true, result: x } }
76
- const TOPLEVEL_KS = { prompt: 0, arena: Arena(), rest: null }
102
+ const TOPLEVEL_KS = { stack: null, prompt: Symbol("toplevel"), arena: new Arena(), rest: null }
77
103
 
78
104
  function THUNK(f) {
79
105
  f.thunk = true
@@ -92,9 +118,9 @@ const RETURN = (x, ks) => ks.rest.stack(x, ks.rest)
92
118
 
93
119
  // HANDLE(ks, ks, (p, ks, k) => { STMT })
94
120
  function RESET(prog, ks, k) {
95
- const prompt = _prompt++;
121
+ const prompt = Symbol(); // gensym
96
122
  const rest = { stack: k, prompt: ks.prompt, arena: ks.arena, rest: ks.rest }
97
- return prog(prompt, { prompt, arena: Arena([]), rest }, RETURN)
123
+ return prog(prompt, { stack: null, prompt, arena: new Arena(), rest }, RETURN)
98
124
  }
99
125
 
100
126
  function SHIFT(p, body, ks, k) {