@effekt-lang/effekt 0.62.0 → 0.64.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.
@@ -0,0 +1,74 @@
1
+ module io/env
2
+
3
+ /// Retrieve the value of an environment variable, returning `None()` if not set.
4
+ extern def getEnvironmentVariable(name: String) at io: Option[String] =
5
+ jsNode { js::getEnvironmentVariable(name) }
6
+ chez { chez::getEnvironmentVariable(name) }
7
+ llvm { llvm::getEnvironmentVariable(name) }
8
+
9
+ /// Check whether an environment variable is set.
10
+ extern def hasEnvironmentVariable(name: String) at io: Bool =
11
+ llvm { llvm::hasEnvironmentVariable(name) }
12
+ default { if (getEnvironmentVariable(name) is Some(_)) true else false }
13
+
14
+ namespace js {
15
+ extern def rawGetEnv(name: String) at io: String =
16
+ jsNode "process.env[${name}]"
17
+
18
+ def getEnvironmentVariable(name: String): Option[String] =
19
+ rawGetEnv(name).undefinedToOption
20
+ }
21
+
22
+ namespace chez {
23
+ extern def rawGetEnv(name: String) at io: String =
24
+ chez "(getenv ${name})"
25
+
26
+ def getEnvironmentVariable(name: String): Option[String] =
27
+ rawGetEnv(name).undefinedToOption // Chez: getenv returns #f when not found
28
+ }
29
+
30
+ namespace llvm {
31
+ extern llvm """
32
+ declare %Int @c_getenv_size(%Pos)
33
+ declare %Pos @c_getenv_value(%Pos, %Int)
34
+ """
35
+
36
+ extern def getEnvSize(name: String) at io: Int =
37
+ llvm """
38
+ %size = call %Int @c_getenv_size(%Pos ${name})
39
+ ret %Int %size
40
+ """
41
+
42
+ extern def getEnvValue(name: String, size: Int) at io: String =
43
+ llvm """
44
+ %val = call %Pos @c_getenv_value(%Pos ${name}, %Int ${size})
45
+ ret %Pos %val
46
+ """
47
+
48
+ def getEnvironmentVariable(name: String): Option[String] = {
49
+ val size = getEnvSize(name)
50
+ if (size >= 0) Some(getEnvValue(name, size))
51
+ else None()
52
+ }
53
+
54
+ def hasEnvironmentVariable(name: String): Bool =
55
+ getEnvSize(name) >= 0
56
+ }
57
+
58
+ namespace examples {
59
+ def main() = {
60
+ val path = getEnvironmentVariable("PATH")
61
+ println(path.isDefined) // should be defined on all platforms
62
+
63
+ val doesNotExist = getEnvironmentVariable("THIS_ENV_VAR_OUGHT_NOT_EXIST__WOW__EFFEKT_IS_AWESOME")
64
+ println(doesNotExist.show) // should not be defined
65
+ }
66
+
67
+ def _unused_other() = {
68
+ val h = getEnvironmentVariable("HOME")
69
+ h match {
70
+ case Some(value) => println(s"HOME is set to: ${value}")
71
+ case None() => println("HOME is not set")
72
+ }
73
+ }
74
+ }
@@ -42,7 +42,6 @@ def listen(listener: Listener, handler: Connection => Unit at {io, async, global
42
42
  def shutdown(listener: Listener): Unit =
43
43
  internal::shutdown(listener)
44
44
 
45
-
46
45
  def streaming[R](connection: Connection) { program: () => R / {read[ByteArray], emit[ByteArray]} }: R / Exception[IOError] =
47
46
  try {
48
47
  program()
@@ -59,58 +58,148 @@ def streaming[R](connection: Connection) { program: () => R / {read[ByteArray],
59
58
 
60
59
  namespace internal {
61
60
 
62
- extern llvm """
63
- declare void @c_tcp_connect(%Pos, %Int, %Stack)
64
- declare void @c_tcp_read(%Int, %Pos, %Int, %Int, %Stack)
65
- declare void @c_tcp_write(%Int, %Pos, %Int, %Int, %Stack)
66
- declare void @c_tcp_close(%Int, %Stack)
67
- declare void @c_tcp_bind(%Pos, %Int, %Int, %Stack)
68
- declare void @c_tcp_listen(%Int, %Pos, %Stack)
69
- declare void @c_tcp_shutdown(%Int, %Stack)
61
+ // TODO: also in `io/filesystem`, perhaps we want this in a common place?
62
+ extern js """
63
+ function toBuffer(buffer) {
64
+ return Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength);
65
+ }
66
+ """
67
+
68
+ extern jsNode """
69
+ const net = require('net');
70
+
71
+ const tcp$handles = new Map();
72
+ let tcp$next = 1;
73
+ function tcp$alloc(entry) { const h = tcp$next++; tcp$handles.set(h, entry); return h; }
74
+ function tcp$free(h) { tcp$handles.delete(h); }
75
+
76
+ function tcp$connect(host, port, callback) {
77
+ const socket = net.createConnection({ host, port });
78
+ socket.once('connect', () => { socket.pause(); callback(tcp$alloc({ socket })); });
79
+ socket.once('error', err => callback(-(err.errno || 1)));
80
+ }
81
+
82
+ function tcp$read(handle, buffer, offset, size, callback) {
83
+ const { socket } = tcp$handles.get(handle);
84
+ function cleanup() {
85
+ socket.removeListener('readable', onReadable);
86
+ socket.removeListener('end', onEnd);
87
+ socket.removeListener('error', onError);
88
+ }
89
+ function onReadable() { cleanup(); tryRead(); }
90
+ function onEnd() { cleanup(); callback(0); }
91
+ function onError(err) { cleanup(); callback(-(err.errno || 1)); }
92
+ function tryRead() {
93
+ const chunk = socket.read(size);
94
+ if (chunk !== null) { chunk.copy(toBuffer(buffer), offset); callback(chunk.length); return; }
95
+ if (socket.readableEnded) { callback(0); return; }
96
+ socket.once('readable', onReadable);
97
+ socket.once('end', onEnd);
98
+ socket.once('error', onError);
99
+ }
100
+ tryRead();
101
+ }
102
+
103
+ function tcp$write(handle, buffer, offset, size, callback) {
104
+ const { socket } = tcp$handles.get(handle);
105
+ socket.write(toBuffer(buffer).slice(offset, offset + size), err => {
106
+ if (err) callback(-(err.errno || 1)); else callback(size);
107
+ });
108
+ }
109
+
110
+ function tcp$close(handle, callback) {
111
+ const entry = tcp$handles.get(handle);
112
+ if (!entry || !entry.socket) { callback($effekt.unit); return; }
113
+ tcp$free(handle);
114
+ const { socket } = entry;
115
+ if (socket.destroyed) { callback($effekt.unit); return; }
116
+ socket.once('error', _err => {});
117
+ socket.end(() => { socket.destroy(); callback($effekt.unit); });
118
+ }
119
+
120
+ function tcp$bind(host, port) {
121
+ return tcp$alloc({ server: net.createServer(), host, port });
122
+ }
123
+
124
+ function tcp$listen(listenerHandle, handler, callback) {
125
+ const { server, host, port } = tcp$handles.get(listenerHandle);
126
+ server.once('error', err => callback(-(err.errno || 1)));
127
+ server.listen(port, host, () => {
128
+ server.removeAllListeners('error');
129
+ server.on('error', _err => {});
130
+ server.on('connection', socket => {
131
+ socket.pause();
132
+ const connHandle = tcp$alloc({ socket });
133
+ $effekt.runToplevel((ks, k) => handler(connHandle, ks, k));
134
+ });
135
+ callback(0);
136
+ });
137
+ }
138
+
139
+ function tcp$shutdown(listenerHandle, callback) {
140
+ const entry = tcp$handles.get(listenerHandle);
141
+ if (!entry || !entry.server) { callback($effekt.unit); return; }
142
+ tcp$free(listenerHandle);
143
+ entry.server.close(() => callback($effekt.unit));
144
+ }
70
145
  """
71
146
 
72
147
  extern def connect(host: String, port: Int) at async: Int =
148
+ jsNode "$effekt.capture(k => tcp$connect(${host}, ${port}, k))"
73
149
  llvm """
74
150
  call void @c_tcp_connect(%Pos ${host}, %Int ${port}, %Stack %stack)
75
151
  ret void
76
152
  """
77
153
 
78
154
  extern def read(handle: Int, buffer: ByteArray, offset: Int, size: Int) at async: Int =
155
+ jsNode "$effekt.capture(k => tcp$read(${handle}, ${buffer}, ${offset}, ${size}, k))"
79
156
  llvm """
80
157
  call void @c_tcp_read(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
81
158
  ret void
82
159
  """
83
160
 
84
161
  extern def write(handle: Int, buffer: ByteArray, offset: Int, size: Int) at async: Int =
162
+ jsNode "$effekt.capture(k => tcp$write(${handle}, ${buffer}, ${offset}, ${size}, k))"
85
163
  llvm """
86
164
  call void @c_tcp_write(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
87
165
  ret void
88
166
  """
89
167
 
90
168
  extern def close(handle: Int) at async: Unit =
169
+ jsNode "$effekt.capture(k => tcp$close(${handle}, k))"
91
170
  llvm """
92
171
  call void @c_tcp_close(%Int ${handle}, %Stack %stack)
93
172
  ret void
94
173
  """
95
174
 
96
175
  extern def bind(host: String, port: Int) at io: Int =
176
+ jsNode "tcp$bind(${host}, ${port})"
97
177
  llvm """
98
178
  %result = call %Int @c_tcp_bind(%Pos ${host}, %Int ${port})
99
179
  ret %Int %result
100
180
  """
101
181
 
102
- extern def listen(listener: Int, handler: Int => Unit at {io, async, global}) at async: Int =
182
+ extern def listen(listener: Int, handler: Connection => Unit at {io, async, global}) at async: Int =
183
+ jsNode "$effekt.capture(k => tcp$listen(${listener}, ${handler}, k))"
103
184
  llvm """
104
185
  call void @c_tcp_listen(%Int ${listener}, %Pos ${handler}, %Stack %stack)
105
186
  ret void
106
187
  """
107
188
 
108
189
  extern def shutdown(handle: Int) at async: Unit =
190
+ jsNode "$effekt.capture(k => tcp$shutdown(${handle}, k))"
109
191
  llvm """
110
192
  call void @c_tcp_shutdown(%Int ${handle}, %Stack %stack)
111
193
  ret void
112
194
  """
113
195
 
196
+ extern llvm """
197
+ declare void @c_tcp_connect(%Pos, %Int, %Stack)
198
+ declare void @c_tcp_read(%Int, %Pos, %Int, %Int, %Stack)
199
+ declare void @c_tcp_write(%Int, %Pos, %Int, %Int, %Stack)
200
+ declare void @c_tcp_close(%Int, %Stack)
201
+ declare %Int @c_tcp_bind(%Pos, %Int)
202
+ declare void @c_tcp_listen(%Int, %Pos, %Stack)
203
+ declare void @c_tcp_shutdown(%Int, %Stack)
204
+ """
114
205
  }
115
-
116
-
@@ -95,10 +95,10 @@ def readDouble(): Double / { Scan[Char], Exception[WrongFormat]} = {
95
95
  def exponent(pre: Double): Double / fail = {
96
96
  try {
97
97
  if(do peek() == 'e' || do peek() == 'E') {
98
- do skip[Char]()
98
+ skip[Char]()
99
99
  val e = do peek[Char]() match {
100
- case '+' => do skip[Char](); readDecimal()
101
- case '-' => do skip[Char](); neg(readDecimal())
100
+ case '+' => skip[Char](); readDecimal()
101
+ case '-' => skip[Char](); neg(readDecimal())
102
102
  case _ => readDecimal()
103
103
  }
104
104
  pre * pow(10.0, e)
@@ -133,9 +133,9 @@ def readQuotedString(): Unit / { Scan[Char], emit[Char], Exception[WrongFormat]
133
133
  with expect("Unexpected end of input while reading a string")
134
134
  skipWhitespace()
135
135
  expect("Expected \"") { must { readIf('"') } }
136
- while(must { read[Char]() } is c and c != '"') {
136
+ while(must { do read[Char]() } is c and c != '"') {
137
137
  c match {
138
- case '\\' => must { read[Char]() } match {
138
+ case '\\' => must { do read[Char]() } match {
139
139
  case '"' => do emit('\"')
140
140
  case '\\' => do emit('\\')
141
141
  case '/' => do emit('/')
@@ -184,7 +184,7 @@ def decodeJsonObject(): Unit / {Scan[Char], JsonObjectBuilder, Exception[WrongFo
184
184
  skipWhitespace()
185
185
  first = false
186
186
  }
187
- must { do skip() }
187
+ must { skip() }
188
188
  }
189
189
  def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
190
190
  var first = true
@@ -197,7 +197,7 @@ def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} =
197
197
  skipWhitespace()
198
198
  first = false
199
199
  }
200
- must { do skip() }
200
+ must { skip() }
201
201
  }
202
202
 
203
203
  // --------------------------------------------------------------------------------
@@ -962,6 +962,7 @@ def tails[A](list: List[A]): Unit / emit[List[A]] =
962
962
  case Cons(head, tail) => do emit(list); tails(tail)
963
963
  }
964
964
 
965
+ /// Connects `list` as a producer to a given pull stream `reader` acting as a consumer.
965
966
  def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
966
967
  var l = list
967
968
  try {
@@ -978,10 +979,14 @@ def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
978
979
  }
979
980
  }
980
981
 
982
+ /// Collects elements emitted by a push stream producer `stream` into a new list,
983
+ /// discarding the result of the producer.
981
984
  def collect[A] { stream: () => Unit / emit[A] }: List[A] =
982
985
  returning::collect[A, Unit]{stream}.second
983
986
 
984
987
  namespace returning {
988
+ /// Collects elements emitted by a push stream producer `stream` into a new list,
989
+ /// returning the result of the producer as well as the collected list.
985
990
  def collect[A, R] { stream: () => R / emit[A] }: (R, List[A]) =
986
991
  try {
987
992
  (stream(), Nil())
@@ -1012,10 +1012,14 @@ def eachKey[K, V](map: Map[K, V]): Unit / emit[K] =
1012
1012
  def eachValue[K, V](map: Map[K, V]): Unit / emit[V] =
1013
1013
  map.foreach { (_k, v) => do emit(v) }
1014
1014
 
1015
+ /// Collects `(key, value)` pairs from a push stream producer `stream` into a new `map`
1016
+ /// using the provided `compare` function, discarding the result of the producer.
1015
1017
  def collect[K, V](compare: (K, K) => Ordering at {}) { stream: () => Unit / emit[(K, V)] }: Map[K, V] =
1016
1018
  returning::collect[K, V, Unit](compare){stream}.second
1017
1019
 
1018
1020
  namespace returning {
1021
+ /// Collects `(key, value)` pairs from a push stream producer `stream` into a new `map`,
1022
+ /// using the provided `compare` function, returning the result of the producer as well as the collected map.
1019
1023
  def collect[K, V, R](compare: (K, K) => Ordering at {}) { stream: () => R / emit[(K, V)] }: (R, Map[K, V]) =
1020
1024
  try {
1021
1025
  (stream(), empty(compare))
@@ -13,6 +13,13 @@ def attempt[R] { program: () => R / fail } { fallback: () => R }: R =
13
13
  fallback()
14
14
  }
15
15
 
16
+ def onFail[R] { fallback: () => R } { program: () => R / fail }: R =
17
+ try {
18
+ program()
19
+ } with fail {
20
+ fallback()
21
+ }
22
+
16
23
  def succeeds { program: () => Unit / fail }: Bool =
17
24
  try { program(); true } with fail { false }
18
25
 
@@ -3,6 +3,7 @@ module resizable_array
3
3
  import ref
4
4
  import array
5
5
 
6
+ /// A resizable array, implemented as a wrapper around a mutable array and a size field.
6
7
  record ResizableArray[T](rawSizePtr: Ref[Int], rawContentPtr: Ref[Array[T]])
7
8
 
8
9
  // These numbers should be optimized based on benchmarking
@@ -20,11 +21,10 @@ val shrinkThreshold = 0.4
20
21
  def size[T](arr: ResizableArray[T]) = arr.rawSizePtr.get
21
22
 
22
23
  /// Allocate a new, empty dynamic array with given initial capacity
23
- def resizableArray[T](capacity: Int): ResizableArray[T] = {
24
+ def resizableArray[T](capacity: Int): ResizableArray[T] =
24
25
  ResizableArray(ref(0), ref(unsafeAllocate(capacity)))
25
- }
26
26
 
27
- /// Aallocate a new, empty dynamic array
27
+ /// Allocate a new, empty dynamic array
28
28
  def resizableArray[T](): ResizableArray[T] = resizableArray(8)
29
29
 
30
30
 
@@ -35,7 +35,7 @@ def boundsCheck[T](arr: ResizableArray[T], index: Int): Unit / Exception[OutOfBo
35
35
  }
36
36
 
37
37
 
38
- /// get the element at position index in the array
38
+ /// Get the element at position `index` in the resizable array `arr`
39
39
  ///
40
40
  /// O(1)
41
41
  def get[T](arr: ResizableArray[T], index: Int): T / Exception[OutOfBounds] = {
@@ -43,7 +43,7 @@ def get[T](arr: ResizableArray[T], index: Int): T / Exception[OutOfBounds] = {
43
43
  arr.rawContentPtr.get.get(index)
44
44
  }
45
45
 
46
- /// set the element at position index in the array
46
+ /// Set the element at position `index` in the resizable array `arr` to `value`
47
47
  ///
48
48
  /// O(1)
49
49
  def set[T](arr: ResizableArray[T], index: Int, value: T): Unit / Exception[OutOfBounds] = {
@@ -51,7 +51,7 @@ def set[T](arr: ResizableArray[T], index: Int, value: T): Unit / Exception[OutOf
51
51
  arr.rawContentPtr.get.set(index, value)
52
52
  }
53
53
 
54
- /// swap the elements at the given positions in the array
54
+ /// Swap the elements at the given positions in the resizable array `arr`
55
55
  ///
56
56
  /// O(1)
57
57
  def swap[T](arr: ResizableArray[T], index1: Int, index2: Int): Unit / Exception[OutOfBounds] = {
@@ -71,7 +71,7 @@ def setCapacity[T](arr: ResizableArray[T], capacity: Int): Unit / Exception[OutO
71
71
  do raise(OutOfBounds(), "Cannot change capacity of ResizableArray to " ++ capacity.show ++ " below size " ++ arr.size.show)
72
72
  }
73
73
  val oldRaw = arr.rawContentPtr.get
74
- if (oldRaw.size != capacity) {
74
+ if (oldRaw.size != capacity) {
75
75
  val newRaw = array::unsafeAllocate(capacity)
76
76
  oldRaw.copy(0, newRaw, 0, arr.size)
77
77
  arr.rawContentPtr.set(newRaw)
@@ -82,13 +82,13 @@ def setCapacity[T](arr: ResizableArray[T], capacity: Int): Unit / Exception[OutO
82
82
  ///
83
83
  /// O(n)
84
84
  def maybeShrink[T](arr: ResizableArray[T]): Unit / Exception[OutOfBounds] = {
85
- if(arr.size.toDouble < arr.rawContentPtr.get.size.toDouble * shrinkThreshold) {
85
+ if (arr.size.toDouble < arr.rawContentPtr.get.size.toDouble * shrinkThreshold) {
86
86
  val newCap = max(arr.size, (arr.rawContentPtr.get.size.toDouble / growFactor).ceil)
87
87
  arr.setCapacity(newCap)
88
88
  }
89
89
  }
90
90
 
91
- /// makes sure capacity is at least the given one
91
+ /// Makes sure capacity is at least the given one
92
92
  ///
93
93
  /// O(given capacity - current capacity) amortized, O(n) worst case // TODO ?
94
94
  def ensureCapacity[T](arr: ResizableArray[T], capacity: Int): Unit / Exception[OutOfBounds] = {
@@ -140,62 +140,123 @@ def popRight[T](arr: ResizableArray[T]): T / Exception[OutOfBounds] = {
140
140
  }
141
141
 
142
142
 
143
- def foreachIndex[T](arr: ResizableArray[T]){ body: (Int, T) => Unit }: Unit = {
143
+ /// Traverse the resizable array `arr`, applying the given action on every element.
144
+ def foreach[T](arr: ResizableArray[T]){ action: T => Unit }: Unit =
144
145
  each(0, arr.size) { i =>
145
- body(i, arr.rawContentPtr.get.get(i))
146
+ action(arr.rawContentPtr.get.get(i))
146
147
  }
147
- }
148
- def foreachIndex[T](arr: ResizableArray[T]){ body: (Int, T) {Label} => Unit }: Unit = {
148
+
149
+ /// Traverse the resizable array `arr`, applying the given action on every element,
150
+ /// providing a `label` for `break`/`continue`.
151
+ def foreach[T](arr: ResizableArray[T]){ action: (T) {Label} => Unit }: Unit =
149
152
  each(0, arr.size) { (i){label} =>
150
- body(i, arr.rawContentPtr.get.get(i)){label}
153
+ action(arr.rawContentPtr.get.get(i)){label}
151
154
  }
152
- }
153
- def foreach[T](arr: ResizableArray[T]){ body: T => Unit }: Unit = {
155
+
156
+ /// Traverse the resizable array `arr`, applying the given action on every element and its (zero-based) index.
157
+ def foreachIndex[T](arr: ResizableArray[T]){ action: (Int, T) => Unit }: Unit =
154
158
  each(0, arr.size) { i =>
155
- body(arr.rawContentPtr.get.get(i))
159
+ action(i, arr.rawContentPtr.get.get(i))
156
160
  }
157
- }
158
- def foreach[T](arr: ResizableArray[T]){ body: (T) {Label} => Unit }: Unit = {
161
+
162
+ /// Traverse the resizable array `arr`, applying the given action on every element and its (zero-based) index,
163
+ /// providing a `label` for `break`/`continue`.
164
+ def foreachIndex[T](arr: ResizableArray[T]){ action: (Int, T) {Label} => Unit }: Unit =
159
165
  each(0, arr.size) { (i){label} =>
160
- body(arr.rawContentPtr.get.get(i)){label}
166
+ action(i, arr.rawContentPtr.get.get(i)){label}
161
167
  }
162
- }
163
- def foreachIndexReversed[T](arr: ResizableArray[T]){ body: (Int, T){Label} => Unit }: Unit = {
168
+
169
+ /// Traverse the resizable array `arr` in reverse order, applying the given action on every element.
170
+ def foreachReversed[T](arr: ResizableArray[T]){ action: T => Unit }: Unit =
171
+ var i = arr.size - 1
172
+ while (i >= 0) {
173
+ action(arr.rawContentPtr.get.get(i))
174
+ i = i - 1
175
+ }
176
+
177
+ /// Traverse the resizable array `arr` in reverse order, applying the given action on every element,
178
+ /// providing a `label` for `break`/`continue`.
179
+ def foreachReversed[T](arr: ResizableArray[T]){ action: (T) {Label} => Unit }: Unit =
164
180
  var i = arr.size - 1
165
181
  loop { {l} =>
166
182
  if (i < 0) { l.break() }
167
- body(i, arr.rawContentPtr.get.get(i)){l}
183
+ action(arr.rawContentPtr.get.get(i)){l}
168
184
  i = i - 1
169
185
  }
170
- }
171
- def foreachIndexReversed[T](arr: ResizableArray[T]){ body: (Int, T) => Unit }: Unit = {
186
+
187
+ /// Traverse the resizable array `arr` in reverse order, applying the given action on every element and its (zero-based) index.
188
+ def foreachIndexReversed[T](arr: ResizableArray[T]){ action: (Int, T) => Unit }: Unit =
172
189
  var i = arr.size - 1
173
190
  while(i >= 0) {
174
- body(i, arr.rawContentPtr.get.get(i))
191
+ action(i, arr.rawContentPtr.get.get(i))
175
192
  i = i - 1
176
193
  }
177
- }
178
- def foreachReversed[T](arr: ResizableArray[T]){ body: (T){Label} => Unit }: Unit = {
194
+
195
+ /// Traverse the resizable array `arr` in reverse order, applying the given action on every element and its (zero-based) index,
196
+ /// providing a `label` for `break`/`continue`.
197
+ def foreachIndexReversed[T](arr: ResizableArray[T]){ action: (Int, T) {Label} => Unit }: Unit =
179
198
  var i = arr.size - 1
180
199
  loop { {l} =>
181
200
  if (i < 0) { l.break() }
182
- body(arr.rawContentPtr.get.get(i)){l}
201
+ action(i, arr.rawContentPtr.get.get(i)){l}
183
202
  i = i - 1
184
203
  }
185
- }
186
- def foreachReversed[T](arr: ResizableArray[T]){ body: T => Unit }: Unit = {
187
- var i = arr.size - 1
188
- while (i >= 0) {
189
- body(arr.rawContentPtr.get.get(i))
190
- i = i - 1
191
- }
192
- }
193
204
 
205
+
206
+ /// Create a new list from a given resizable array `arr`.
194
207
  def toList[T](arr: ResizableArray[T]): List[T] = {
195
208
  def go(i: Int, acc: List[T]): List[T] = {
196
- if (i < 0) { acc } else {
209
+ if (i < 0) {
210
+ acc
211
+ } else {
197
212
  go(i - 1, Cons(arr.rawContentPtr.get.get(i), acc))
198
213
  }
199
214
  }
215
+
200
216
  go(arr.size - 1, Nil())
217
+ }
218
+
219
+
220
+ /// Turns `arr` into a producer of a push stream
221
+ /// by emitting each contained value from 0 to length - 1.
222
+ def each[T](arr: ResizableArray[T]): Unit / emit[T] =
223
+ each(0, arr.size) { i =>
224
+ do emit(arr.rawContentPtr.get.get(i))
225
+ }
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 = {
229
+ var i = 0
230
+ try {
231
+ reader()
232
+ } with read[T] {
233
+ resume {
234
+ if (i < arr.size) {
235
+ val v = arr.rawContentPtr.get.get(i)
236
+ i = i + 1
237
+ v
238
+ } else {
239
+ do stop()
240
+ }
241
+ }
242
+ }
243
+ }
244
+
245
+ /// Collects elements emitted by a push stream producer `stream` into a new resizable array,
246
+ /// discarding the result of the producer.
247
+ def collect[T] { stream: () => Unit / emit[T] }: ResizableArray[T] =
248
+ returning::collect[T, Unit]{stream}.second
249
+
250
+ namespace returning {
251
+ /// Collects elements emitted by a push stream producer `stream` into a new resizable array,
252
+ /// returning the result of the producer as well as the collected array.
253
+ def collect[T, R] { stream: () => R / emit[T] }: (R, ResizableArray[T]) = {
254
+ val arr = resizableArray()
255
+ try {
256
+ (stream(), arr)
257
+ } with emit[T] { v =>
258
+ arr.add(v)
259
+ resume(())
260
+ }
261
+ }
201
262
  }