@effekt-lang/effekt 0.56.0 → 0.57.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.
@@ -2,6 +2,7 @@ module bytearray
2
2
 
3
3
  import effekt
4
4
  import stream
5
+ import control
5
6
 
6
7
  /**
7
8
  * A memory managed, mutable, fixed-length array of bytes.
@@ -22,12 +23,12 @@ extern type ByteArray
22
23
  // = js "Uint8Array"
23
24
  // = chez "bytevector"
24
25
 
25
- /// Allocates a new bytearray with the given `size`, its values are undefined.
26
+ /// Allocates a new bytearray with the given `size`, its values are zero bytes.
26
27
  extern def allocate(size: Int) at global: ByteArray =
27
28
  js "(new Uint8Array(${size}))"
28
29
  llvm """
29
30
  %object_size = add i64 ${size}, 16
30
- %object_alloc = tail call noalias ptr @malloc(i64 noundef %object_size)
31
+ %object_alloc = tail call noalias ptr @calloc(i64 %object_size, i64 1)
31
32
  store i64 0, ptr %object_alloc, align 8
32
33
  %object_data_ptr = getelementptr inbounds i8, ptr %object_alloc, i64 8
33
34
  store ptr @bytearray_erase_noop, ptr %object_data_ptr, align 8
@@ -48,9 +49,30 @@ extern def size(arr: ByteArray) at {}: Int =
48
49
  chez "(bytevector-length ${arr})"
49
50
  vm "bytearray::size(ByteArray)"
50
51
 
51
- extern def unsafeGet(arr: ByteArray, index: Int) at global: Byte =
52
- js "(${arr})[${index}]"
52
+ extern js """
53
+ function bytearray$get(arr, index) {
54
+ if (index < 0 || index >= arr.length) throw 'Index out of bounds';
55
+ return arr[index];
56
+ }
57
+ """
58
+
59
+ extern llvm """
60
+ define void @assertInboundsByteArray(i64 %index, %Pos %arr) alwaysinline {
61
+ %size = extractvalue %Pos %arr, 0
62
+ %in_bounds = icmp ult i64 %index, %size
63
+ br i1 %in_bounds, label %ok, label %out
64
+ out:
65
+ call void @exit(i64 1)
66
+ unreachable
67
+ ok:
68
+ ret void
69
+ }
70
+ """
71
+
72
+ extern def get(arr: ByteArray, index: Int) at global: Byte =
73
+ js "bytearray$get(${arr}, ${index})"
53
74
  llvm """
75
+ call void @assertInboundsByteArray(i64 ${index}, %Pos ${arr})
54
76
  %arr_ptr = extractvalue %Pos ${arr}, 1
55
77
  %arr_data_ptr = getelementptr inbounds i8, ptr %arr_ptr, i64 16
56
78
  %element_ptr = getelementptr inbounds i8, ptr %arr_data_ptr, i64 ${index}
@@ -59,11 +81,12 @@ extern def unsafeGet(arr: ByteArray, index: Int) at global: Byte =
59
81
  ret i8 %element
60
82
  """
61
83
  chez "(bytevector-u8-ref ${arr} ${index})"
62
- vm "bytearray::unsafeGet(ByteArray, Int)"
84
+ vm "bytearray::get(ByteArray, Int)"
63
85
 
64
- extern def unsafeSet(arr: ByteArray, index: Int, value: Byte) at global: Unit =
86
+ extern def set(arr: ByteArray, index: Int, value: Byte) at global: Unit =
65
87
  js "bytearray$set(${arr}, ${index}, ${value})"
66
88
  llvm """
89
+ call void @assertInboundsByteArray(i64 ${index}, %Pos ${arr})
67
90
  %arr_ptr = extractvalue %Pos ${arr}, 1
68
91
  %arr_data_ptr = getelementptr inbounds i8, ptr %arr_ptr, i64 16
69
92
  %element_ptr = getelementptr inbounds i8, ptr %arr_data_ptr, i64 ${index}
@@ -72,13 +95,13 @@ extern def unsafeSet(arr: ByteArray, index: Int, value: Byte) at global: Unit =
72
95
  ret %Pos zeroinitializer
73
96
  """
74
97
  chez "(bytevector-u8-set! ${arr} ${index} ${value})"
75
- vm "bytearray::unsafeSet(ByteArray, Int, Byte)"
98
+ vm "bytearray::set(ByteArray, Int, Byte)"
76
99
 
77
100
  /// Creates a new ByteArray of size `size` filled with the value `init`
78
101
  def bytearray(size: Int, init: Byte): ByteArray = {
79
102
  val arr = allocate(size);
80
103
  each(0, size) { i =>
81
- unsafeSet(arr, i, init)
104
+ set(arr, i, init)
82
105
  };
83
106
  arr
84
107
  }
@@ -88,7 +111,7 @@ def resize(source: ByteArray, size: Int): ByteArray = {
88
111
  val n = min(source.size, target.size)
89
112
  def go(i: Int): ByteArray =
90
113
  if (i < n) {
91
- target.unsafeSet(i, source.unsafeGet(i))
114
+ target.set(i, source.get(i))
92
115
  go(i + 1)
93
116
  } else {
94
117
  target
@@ -98,22 +121,22 @@ def resize(source: ByteArray, size: Int): ByteArray = {
98
121
 
99
122
  def foreach(arr: ByteArray){ action: Byte => Unit }: Unit =
100
123
  each(0, arr.size) { i =>
101
- action(arr.unsafeGet(i))
124
+ action(arr.get(i))
102
125
  }
103
126
 
104
- def foreach(arr: ByteArray){ action: (Byte) {Control} => Unit }: Unit =
127
+ def foreach(arr: ByteArray){ action: (Byte) {Label} => Unit }: Unit =
105
128
  each(0, arr.size) { (i) {label} =>
106
- action(arr.unsafeGet(i)) {label}
129
+ action(arr.get(i)) {label}
107
130
  }
108
131
 
109
132
  def foreachIndex(arr: ByteArray){ action: (Int, Byte) => Unit }: Unit =
110
133
  each(0, arr.size) { i =>
111
- action(i, arr.unsafeGet(i))
134
+ action(i, arr.get(i))
112
135
  }
113
136
 
114
- def foreachIndex(arr: ByteArray){ action: (Int, Byte) {Control} => Unit }: Unit =
137
+ def foreachIndex(arr: ByteArray){ action: (Int, Byte) {Label} => Unit }: Unit =
115
138
  each(0, arr.size) { (i) {label} =>
116
- action(i, arr.unsafeGet(i)) {label}
139
+ action(i, arr.get(i)) {label}
117
140
  }
118
141
 
119
142
  extern def fromString(str: String) at {}: ByteArray =
@@ -214,7 +237,7 @@ def each(bytes: ByteArray): Unit / emit[Byte] = {
214
237
  val n = bytes.size
215
238
  def go(i: Int): Unit = {
216
239
  if (i < n) {
217
- do emit(bytes.unsafeGet(i))
240
+ do emit(bytes.get(i))
218
241
  go(i + 1)
219
242
  }
220
243
  }
@@ -230,7 +253,7 @@ def feed[R](bytes: ByteArray) { reader: () => R / read[Byte] }: R = {
230
253
  if (i < bytes.size) {
231
254
  val c = i
232
255
  i = c + 1
233
- bytes.unsafeGet(c)
256
+ bytes.get(c)
234
257
  } else {
235
258
  do stop()
236
259
  }
@@ -249,7 +272,7 @@ namespace returning {
249
272
  (stream(), a.resize(i))
250
273
  } with emit[Byte] { (v) =>
251
274
  if (i >= a.size) { a = a.resize(2 * a.size) }
252
- a.unsafeSet(i, v)
275
+ a.set(i, v)
253
276
  i = i + 1
254
277
  resume(())
255
278
  }
@@ -0,0 +1,27 @@
1
+ module control
2
+
3
+ import effekt
4
+
5
+ interface Label {
6
+ def break(): Unit
7
+ def continue(): Unit
8
+ }
9
+
10
+ def loop { f: {Label} => Unit }: Unit = try {
11
+ def go(): Unit = { f {label}; go() }
12
+ go()
13
+ } with label: Label {
14
+ def break() = ()
15
+ def continue() = loop { f }
16
+ }
17
+
18
+ /// Calls provided action repeatedly with support for breaking. `start` is inclusive, `end` is not.
19
+ def each(start: Int, end: Int) { action: (Int) {Label} => Unit } = {
20
+ var i = start;
21
+ loop { {l} =>
22
+ if (i < end) { val el = i; i = i + 1; action(el){l} }
23
+ else { l.break() }
24
+ }
25
+ }
26
+
27
+ def repeat(n: Int) { action: () {Label} => Unit } = each(0, n) { (n) {l} => action() {l} }
@@ -26,20 +26,20 @@ def insert[A](trie: Dictionary[A], key: ByteArray, value: A): Dictionary[A] = {
26
26
  case Empty() =>
27
27
  val child = go(Empty(), i + 1)
28
28
  val children = array(256, Empty())
29
- children.unsafeSet(key.unsafeGet(i).toInt, child)
29
+ children.set(key.get(i).toInt, child)
30
30
  Branch(children)
31
31
  case Leaf(existing) =>
32
32
  val child = go(Empty(), i + 1)
33
33
  val children = array(256, Empty())
34
- children.unsafeSet(key.unsafeGet(i).toInt, child)
34
+ children.set(key.get(i).toInt, child)
35
35
  Node(existing, children)
36
36
  case Branch(children) =>
37
- val child = go(children.unsafeGet(key.unsafeGet(i).toInt), i + 1)
38
- children.unsafeSet(key.unsafeGet(i).toInt, child)
37
+ val child = go(children.get(key.get(i).toInt), i + 1)
38
+ children.set(key.get(i).toInt, child)
39
39
  Branch(children)
40
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)
41
+ val child = go(children.get(key.get(i).toInt), i + 1)
42
+ children.set(key.get(i).toInt, child)
43
43
  Node(existing, children)
44
44
  }
45
45
  } else {
@@ -64,8 +64,8 @@ def lookup[A](trie: Dictionary[A], key: ByteArray): Option[A] = {
64
64
  trie match {
65
65
  case Empty() => None()
66
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)
67
+ case Branch(children) => go(children.get(key.get(i).toInt), i + 1)
68
+ case Node(_, children) => go(children.get(key.get(i).toInt), i + 1)
69
69
  }
70
70
  } else {
71
71
  trie match {
@@ -97,12 +97,12 @@ def each[A](trie: Dictionary[A]): Unit / emit[A] =
97
97
  case Leaf(value) => do emit(value)
98
98
  case Branch(children) =>
99
99
  each(0, children.size) { i =>
100
- each(children.unsafeGet(i))
100
+ each(children.get(i))
101
101
  }
102
102
  case Node(value, children) =>
103
103
  do emit(value)
104
104
  each(0, children.size) { i =>
105
- each(children.unsafeGet(i))
105
+ each(children.get(i))
106
106
  }
107
107
  }
108
108
 
@@ -110,7 +110,7 @@ def each[A](trie: Dictionary[A]): Unit / emit[A] =
110
110
  def eachTagged[A](trie: Dictionary[A]): Unit / emit[Tagged[A]] = {
111
111
  def fromBytesReverse(bytes: List[Byte], n: Int): ByteArray = {
112
112
  val key = bytearray::allocate(n)
113
- foreachIndex(bytes) { (i, b) => key.unsafeSet(n - 1 - i, b) }
113
+ foreachIndex(bytes) { (i, b) => key.set(n - 1 - i, b) }
114
114
  key
115
115
  }
116
116
  def go(trie: Dictionary[A], key: List[Byte], n: Int): Unit =
@@ -119,12 +119,12 @@ def eachTagged[A](trie: Dictionary[A]): Unit / emit[Tagged[A]] = {
119
119
  case Leaf(value) => do emit(Tagged(fromBytesReverse(key, n), value))
120
120
  case Branch(children) =>
121
121
  each(0, children.size) { i =>
122
- go(children.unsafeGet(i), Cons(i.toByte, key), n + 1)
122
+ go(children.get(i), Cons(i.toByte, key), n + 1)
123
123
  }
124
124
  case Node(value, children) =>
125
125
  do emit(Tagged(fromBytesReverse(key, n), value))
126
126
  each(0, children.size) { i =>
127
- go(children.unsafeGet(i), Cons(i.toByte, key), n + 1)
127
+ go(children.get(i), Cons(i.toByte, key), n + 1)
128
128
  }
129
129
  }
130
130
  go(trie, Nil(), 0)
@@ -732,23 +732,37 @@ type OneOrBoth[A, B] {
732
732
 
733
733
  // Control Flow
734
734
  // ============
735
- interface Control {
736
- def break(): Unit
737
- def continue(): Unit
738
- }
739
735
 
740
- def loop { f: () => Unit }: Unit = {
741
- def go(): Unit = { f(); go() }
736
+ /// Repeat `action` forever
737
+ def forever { action: () => Unit }: Nothing = {
738
+ def go(): Nothing = { action(); go() }
742
739
  go()
743
740
  }
744
741
 
745
- def loop { f: {Control} => Unit }: Unit = try {
746
- def go(): Unit = { f {label}; go() }
747
- go()
748
- } with label: Control {
749
- def break() = ()
750
- def continue() = loop { f }
751
- }
742
+ /// Signal that we should stop.
743
+ effect stop(): Nothing
744
+
745
+ /// When the program stops, then to here.
746
+ def boundary { program: () => Unit / stop }: Unit =
747
+ try { program() } with stop { () }
748
+
749
+ /// Repeat `action` until `stop` is thrown.
750
+ def loop { action: () => Unit / stop }: Unit =
751
+ try {
752
+ def go(): Unit = { action(); go() }
753
+ go()
754
+ } with stop {
755
+ ()
756
+ }
757
+
758
+ /// Repeat `action` until `stop` is thrown, supplying the iteration count.
759
+ def loop { action: Int => Unit / stop }: Unit =
760
+ try {
761
+ def go(i: Int): Unit = { action(i); go(i + 1) }
762
+ go(0)
763
+ } with stop {
764
+ ()
765
+ }
752
766
 
753
767
  /// Calls provided action repeatedly. `start` is inclusive, `end` is not.
754
768
  def each(start: Int, end: Int) { action: (Int) => Unit } = {
@@ -758,19 +772,8 @@ def each(start: Int, end: Int) { action: (Int) => Unit } = {
758
772
  loop(start)
759
773
  }
760
774
 
761
- /// Calls provided action repeatedly with support for breaking. `start` is inclusive, `end` is not.
762
- def each(start: Int, end: Int) { action: (Int) {Control} => Unit } = {
763
- var i = start;
764
- loop { {l} =>
765
- if (i < end) { val el = i; i = i + 1; action(el){l} }
766
- else { l.break() }
767
- }
768
- }
769
-
770
775
  def repeat(n: Int) { action: () => Unit } = each(0, n) { n => action() }
771
776
 
772
- def repeat(n: Int) { action: () {Control} => Unit } = each(0, n) { (n) {l} => action() {l} }
773
-
774
777
  // NOTE: This is emitted by the close hole code action: do not remove unless you also adjust the code action
775
778
  /// Scopes a local computation
776
779
  def locally[R] { p: => R } : R = p()
@@ -23,8 +23,8 @@ namespace internal {
23
23
  arr.boundsCheck(idx) // idx > parent(idx), parent(parent(idx)) etc
24
24
 
25
25
  def go(idx: Int): Unit = {
26
- if (idx > 0 and (heap.cmp)(arr.unsafeGet(parent(idx)), arr.unsafeGet(idx)) is Greater()) {
27
- arr.unsafeSwap(parent(idx), idx)
26
+ if (idx > 0 and (heap.cmp)(arr.get(parent(idx)), arr.get(idx)) is Greater()) {
27
+ arr.swap(parent(idx), idx)
28
28
  go(parent(idx))
29
29
  }
30
30
  }
@@ -39,28 +39,28 @@ namespace internal {
39
39
 
40
40
  def go(idx: Int): Unit = {
41
41
  if (right(idx) < arr.size) {
42
- val v = arr.unsafeGet(idx)
43
- val l = arr.unsafeGet(left(idx))
44
- val r = arr.unsafeGet(right(idx))
42
+ val v = arr.get(idx)
43
+ val l = arr.get(left(idx))
44
+ val r = arr.get(right(idx))
45
45
  if (l < v && r < v) {
46
46
  // swap with the smaller one
47
47
  if (l < r) {
48
- arr.unsafeSwap(left(idx), idx)
48
+ arr.swap(left(idx), idx)
49
49
  go(left(idx))
50
50
  } else {
51
- arr.unsafeSwap(right(idx), idx)
51
+ arr.swap(right(idx), idx)
52
52
  go(right(idx))
53
53
  }
54
54
  } else if (l < v) {
55
- arr.unsafeSwap(left(idx), idx)
55
+ arr.swap(left(idx), idx)
56
56
  go(left(idx))
57
57
  } else if (r < v) {
58
- arr.unsafeSwap(right(idx), idx)
58
+ arr.swap(right(idx), idx)
59
59
  go(right(idx))
60
60
  }
61
61
  } else if (left(idx) < arr.size) {
62
- if (arr.unsafeGet(left(idx)) < arr.unsafeGet(idx)) {
63
- arr.unsafeSwap(left(idx), idx)
62
+ if (arr.get(left(idx)) < arr.get(idx)) {
63
+ arr.swap(left(idx), idx)
64
64
  go(left(idx))
65
65
  }
66
66
  } // else: we are at the bottom
@@ -79,6 +79,7 @@ def insert[T](heap: Heap[T], value: T): Unit = {
79
79
  }
80
80
 
81
81
  /// find and return (but not remove) the minimal element in this heap
82
+ /// panics when heap is empty
82
83
  ///
83
84
  /// O(1)
84
85
  def findMin[T](heap: Heap[T]): T / Exception[OutOfBounds] = {
@@ -86,13 +87,20 @@ def findMin[T](heap: Heap[T]): T / Exception[OutOfBounds] = {
86
87
  }
87
88
 
88
89
  /// find and remove the minimal element in this heap
90
+ /// panics when heap is empty
89
91
  ///
90
92
  /// O(log n)
91
93
  def deleteMin[T](heap: Heap[T]): T / Exception[OutOfBounds] = {
92
- val res = heap.rawContents.get(0)
93
- heap.rawContents.unsafeSet(0, heap.rawContents.popRight())
94
- internal::sinkDown(heap, 0)
95
- res
94
+ val arr = heap.rawContents
95
+ val res = arr.get(0)
96
+ if (arr.size == 1) {
97
+ arr.popRight()
98
+ res
99
+ } else {
100
+ arr.set(0, arr.popRight())
101
+ internal::sinkDown(heap, 0)
102
+ res
103
+ }
96
104
  }
97
105
 
98
106
  /// Number of elements in the heap
@@ -102,7 +102,7 @@ def writeFile[R](path: String) { stream: () => R / emit[Byte] }: R / Exception[I
102
102
  push(0, buffer.size)
103
103
  offset = 0
104
104
  }
105
- buffer.unsafeSet(offset, byte)
105
+ buffer.set(offset, byte)
106
106
  offset = offset + 1
107
107
  resume(())
108
108
  }
@@ -135,7 +135,7 @@ def readFile[R](path: String) { reader: () => R / read[Byte] }: R / Exception[IO
135
135
  pull()
136
136
  offset = 0
137
137
  }
138
- val byte = buffer.unsafeGet(offset)
138
+ val byte = buffer.get(offset)
139
139
  offset = offset + 1
140
140
  return byte
141
141
  }
@@ -2,8 +2,9 @@ module list
2
2
 
3
3
  import effekt
4
4
  import option
5
- import exception
5
+ import control
6
6
  import stream
7
+ import exception
7
8
 
8
9
  /// Immutable linked list for finite sequences of elements.
9
10
  type List[A] {
@@ -123,7 +124,7 @@ def foreach[A](l: List[A]) { f: (A) => Unit } : Unit = l match {
123
124
  /// Traverse a list, applying the given action on every element.
124
125
  ///
125
126
  /// O(N)
126
- def foreach[A](l: List[A]) { f: (A) {Control} => Unit } : Unit = {
127
+ def foreach[A](l: List[A]) { f: (A) {Label} => Unit } : Unit = {
127
128
  var remainder = l
128
129
  loop { {label} =>
129
130
  remainder match {
@@ -151,7 +152,7 @@ def foreachIndex[A](list: List[A]){ f: (Int, A) => Unit }: Unit = {
151
152
  /// Traverse a list, applying the given action on every element and its (zero-based) index.
152
153
  ///
153
154
  /// O(N)
154
- def foreachIndex[A](list: List[A]){ f: (Int, A) {Control} => Unit }: Unit = {
155
+ def foreachIndex[A](list: List[A]){ f: (Int, A) {Label} => Unit }: Unit = {
155
156
  var remainder = list
156
157
  var i = -1
157
158
  loop { {label} =>
@@ -49,7 +49,7 @@ def emptyQueue[T](initialCapacity: Int): Queue[T] at global = {
49
49
  val oldSize = capacity.get
50
50
  val newSize = capacity.get * 2
51
51
  val oldContents = contents.get
52
- val newContents = array::allocate[Option[T]](newSize)
52
+ val newContents = array[Option[T]](newSize, None())
53
53
 
54
54
  if (head.get < tail.get) {
55
55
  // The queue does not wrap around; direct copy is possible.
@@ -93,12 +93,12 @@ def emptyQueue[T](initialCapacity: Int): Queue[T] at global = {
93
93
  resizeTo(size.get + 1)
94
94
  head.set(mod(head.get - 1 + capacity.get, capacity.get))
95
95
  size.set(size.get + 1)
96
- contents.get.unsafeSet(head.get, Some(el))
96
+ contents.get.set(head.get, Some(el))
97
97
  }
98
98
 
99
99
  def pushBack(el: T) = {
100
100
  resizeTo(size.get + 1)
101
- contents.get.unsafeSet(tail.get, Some(el))
101
+ contents.get.set(tail.get, Some(el))
102
102
  size.set(size.get + 1)
103
103
  tail.set(mod(tail.get + 1, capacity.get))
104
104
  }
@@ -30,12 +30,12 @@ extern type Ref[T]
30
30
 
31
31
  /// Allocates a new reference, keeping its value _undefined_.
32
32
  /// Prefer using `ref` constructor instead to ensure that the value is defined.
33
- extern def allocate[T]() at global: Ref[T] =
33
+ extern def unsafeAllocate[T]() at global: Ref[T] =
34
34
  js "{ value: undefined }"
35
35
  chez "(box #f)"
36
36
  llvm """
37
37
  ; sizeof Header + sizeof Pos = 32
38
- %ref = tail call noalias ptr @malloc(i64 noundef 32)
38
+ %ref = tail call noalias ptr @calloc(i64 noundef 32, i64 1)
39
39
  %refEraser = getelementptr ptr, ptr %ref, i64 1
40
40
  %fieldTag = getelementptr ptr, ptr %ref, i64 2
41
41
  %fieldData_pointer = getelementptr ptr, ptr %ref, i64 3