@effekt-lang/effekt 0.21.0 → 0.23.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
@@ -3,6 +3,7 @@ module array
3
3
  import effekt
4
4
  import exception
5
5
  import list
6
+ import option
6
7
 
7
8
  /// A mutable 0-indexed fixed-sized array.
8
9
  extern type Array[T]
@@ -18,12 +19,17 @@ extern global def allocate[T](size: Int): Array[T] =
18
19
  """
19
20
  vm "array::allocate(Int)"
20
21
 
22
+ /// Fills a given array in-place.
23
+ def fill[T](arr: Array[T], filler: T): Unit = {
24
+ arr.foreachIndex { (idx, _) =>
25
+ arr.unsafeSet(idx, filler)
26
+ }
27
+ }
28
+
21
29
  /// Creates a new Array of size `size` filled with the value `init`
22
30
  def array[T](size: Int, init: T): Array[T] = {
23
31
  val arr = allocate[T](size);
24
- each(0, size) { i =>
25
- unsafeSet(arr, i, init)
26
- };
32
+ arr.fill(init)
27
33
  arr
28
34
  }
29
35
 
@@ -81,8 +87,6 @@ extern global def unsafeSet[T](arr: Array[T], index: Int, value: T): Unit =
81
87
  """
82
88
  vm "array::unsafeSet[T](Array[T], Int, T)"
83
89
 
84
-
85
-
86
90
  // Derived operations:
87
91
 
88
92
  /// Gets the element of the `arr` at given `index` in constant time,
@@ -107,6 +111,9 @@ def build[T](size: Int) { index: Int => T }: Array[T] = {
107
111
  arr
108
112
  }
109
113
 
114
+ /// Resizes an array to a given size.
115
+ ///
116
+ /// O(N + M)
110
117
  def resize[T](source: Array[T], size: Int): Array[T] = {
111
118
  val target = allocate(size)
112
119
  val n = min(source.size, target.size)
@@ -147,6 +154,27 @@ def copy[T](from: Array[T], start: Int, to: Array[T], offset: Int, length: Int):
147
154
 
148
155
  // Utility functions:
149
156
 
157
+ /// Swap the value at index i with that at index j.
158
+ /// Warning: undefined behaviour if the indices are out bound.
159
+ ///
160
+ /// O(1)
161
+ def unsafeSwap[A](arr: Array[A], i: Int, j: Int): Unit = {
162
+ val tmp = arr.unsafeGet(i)
163
+ arr.unsafeSet(i, arr.unsafeGet(j))
164
+ arr.unsafeSet(j, tmp)
165
+ }
166
+
167
+ /// Swap the value at index i with that at index j.
168
+ ///
169
+ /// O(1)
170
+ def swap[A](arr: Array[A], i: Int, j: Int): Unit / Exception[OutOfBounds] = {
171
+ if (i < 0 || i >= arr.size || j < 0 || j >= arr.size) do raise(OutOfBounds(), "Index out of bounds")
172
+ arr.unsafeSwap(i, j)
173
+ }
174
+
175
+ /// Converts the given array into a list.
176
+ ///
177
+ /// O(N)
150
178
  def toList[T](arr: Array[T]): List[T] = {
151
179
  var i = arr.size - 1;
152
180
  var l = Nil[T]()
@@ -157,34 +185,380 @@ def toList[T](arr: Array[T]): List[T] = {
157
185
  l
158
186
  }
159
187
 
188
+ /// Traverse an array, applying the given action on every element.
189
+ ///
190
+ /// O(N)
160
191
  def foreach[T](arr: Array[T]){ action: T => Unit }: Unit =
161
192
  each(0, arr.size) { i =>
162
193
  action(arr.unsafeGet(i))
163
194
  }
164
195
 
196
+ /// Traverse an array, applying the given action on every element.
197
+ ///
198
+ /// O(N)
165
199
  def foreach[T](arr: Array[T]){ action: (T) {Control} => Unit }: Unit =
166
200
  each(0, arr.size) { (i) {label} =>
167
201
  action(arr.unsafeGet(i)) {label}
168
202
  }
169
203
 
204
+ /// Traverse an array, applying the given action on every element and its (zero-based) index.
205
+ ///
206
+ /// O(N)
170
207
  def foreachIndex[T](arr: Array[T]){ action: (Int, T) => Unit }: Unit =
171
208
  each(0, arr.size) { i =>
172
209
  action(i, arr.unsafeGet(i))
173
210
  }
174
211
 
212
+ /// Traverse an array, applying the given action on every element and its (zero-based) index.
213
+ ///
214
+ /// O(N)
175
215
  def foreachIndex[T](arr: Array[T]){ action: (Int, T) {Control} => Unit }: Unit =
176
216
  each(0, arr.size) { (i) {label} =>
177
217
  action(i, arr.unsafeGet(i)) {label}
178
218
  }
179
219
 
180
- def sum(list: Array[Int]): Int = {
220
+ /// Sum the elements of the array.
221
+ ///
222
+ /// O(N)
223
+ def sum(arr: Array[Int]): Int = {
181
224
  var acc = 0
182
- list.foreach { x =>
225
+ arr.foreach { x =>
183
226
  acc = acc + x
184
227
  }
185
228
  acc
186
229
  }
187
230
 
231
+ /// Checks whether two given arrays are equal with respect to an equalty funciton defined on their element.
232
+ ///
233
+ /// O(N)
234
+ def equals[A](this: Array[A], other: Array[A]) { eq: (A, A) => Bool }: Bool = {
235
+ if (this.size != other.size) false
236
+ else {
237
+ var res = true
238
+ this.foreachIndex { (idx, elem) {label} =>
239
+ if (not(eq(elem, other.unsafeGet(idx)))) {
240
+ res = false
241
+ label.break()
242
+ }
243
+ }
244
+ res
245
+ }
246
+ }
247
+
248
+ /// Map a function `f` over the elements in a given array.
249
+ /// Modifies the array in-place.
250
+ ///
251
+ /// O(N)
252
+ def map[A](arr: Array[A]) { f: A => A }: Unit = {
253
+ arr.foreachIndex { (idx, elem) =>
254
+ arr.unsafeSet(idx, f(arr.unsafeGet(idx)))
255
+ }
256
+ }
257
+
258
+ /// Map a function over elements.
259
+ /// Allocates a new array of the same size.
260
+ ///
261
+ /// O(N)
262
+ def mapped[A, B](arr: Array[A]) { f: A => B }: Array[B] = {
263
+ val res = allocate(arr.size)
264
+ arr.foreachIndex { (idx, elem) =>
265
+ res.unsafeSet(idx, f(arr.unsafeGet(idx)))
266
+ }
267
+ res
268
+ }
269
+
270
+ /// Reverses an array in-place.
271
+ ///
272
+ /// O(N)
273
+ def reverse[A](arr: Array[A]): Unit = {
274
+ val size = arr.size()
275
+ val mid = size / 2
276
+
277
+ // Swap elements from start and end, moving towards the middle
278
+ each(0, mid) { i =>
279
+ val temp = arr.unsafeGet(i)
280
+ val j = size - 1 - i
281
+ val other = arr.unsafeGet(j)
282
+ arr.unsafeSet(i, other)
283
+ arr.unsafeSet(j, temp)
284
+ }
285
+ }
286
+ /// Creates a new array with elements in reverse order.
287
+ ///
288
+ /// O(N)
289
+ def reversed[A](arr: Array[A]): Array[A] = {
290
+ val size = arr.size()
291
+ val result = allocate[A](size)
292
+
293
+ // Copy elements in reverse order to new array
294
+ each(0, size) { i =>
295
+ val value = arr.unsafeGet(i)
296
+ result.unsafeSet(size - 1 - i, value)
297
+ }
298
+ result
299
+ }
300
+
301
+ /// Fold an array using `f`, starting from the left given a starting value.
302
+ ///
303
+ /// O(N)
304
+ def foldLeft[A, B](arr: Array[A], init: B) { f: (B, A) => B }: B = {
305
+ var acc = init;
306
+ arr.foreach { x => acc = f(acc, x) };
307
+ acc
308
+ }
309
+
310
+ /// Fold an array using `f`, starting from the right given a starting value.
311
+ ///
312
+ /// O(N)
313
+ def foldRight[A, B](arr: Array[A], init: B) { f: (A, B) => B }: B = {
314
+ var acc = init;
315
+ arr.reversed.foreach { x => acc = f(x, acc) };
316
+ acc
317
+ }
318
+
319
+ /// Filters a given array with respect to a given predicate such that a
320
+ /// new array containing all the elements for which the predicate
321
+ /// holds is allocated.
322
+ ///
323
+ /// O(N)
324
+ def filtered[A](arr: Array[A]) { shouldKeep: A => Bool }: Array[A] = {
325
+ val res = arr.copy()
326
+ var idx = 0
327
+ arr.foreach { x =>
328
+ if (shouldKeep(x)) {
329
+ res.unsafeSet(idx, x)
330
+ idx = idx + 1
331
+ }
332
+ }
333
+ res.resize(idx)
334
+ }
335
+
336
+ /// Check if predicate is true for all elements.
337
+ ///
338
+ /// O(N)
339
+ def all[A](arr: Array[A]) { predicate: A => Bool }: Bool = {
340
+ var result = true
341
+ arr.foreach { (x) {label} =>
342
+ if (not(predicate(x))) {
343
+ result = false
344
+ label.break()
345
+ }
346
+ }
347
+ result
348
+ }
349
+
350
+ /// Check if predicate is true for at least one element.
351
+ ///
352
+ /// O(N)
353
+ def any[A](arr: Array[A]) { predicate: A => Bool }: Bool = {
354
+ not(arr.all { x => not(predicate(x)) })
355
+ }
356
+
357
+ /// Count elements satisfying the predicate.
358
+ ///
359
+ /// O(N)
360
+ def count[A](arr: Array[A]) { predicate: A => Bool }: Int = {
361
+ var count = 0
362
+ arr.foreach { x =>
363
+ if (predicate(x)) count = count + 1
364
+ }
365
+ count
366
+ }
367
+
368
+ /// Take first n elements.
369
+ /// Returns a new array.
370
+ ///
371
+ /// O(N)
372
+ def take[A](arr: Array[A], n: Int): Array[A] = {
373
+ val len = min(max(0, n), arr.size)
374
+ val result = allocate[A](len)
375
+ each(0, len) { i =>
376
+ result.unsafeSet(i, arr.unsafeGet(i))
377
+ }
378
+ result
379
+ }
380
+
381
+ // Drop first n elements.
382
+ /// Returns a new array.
383
+ ///
384
+ /// O(N)
385
+ def drop[A](arr: Array[A], n: Int): Array[A] = {
386
+ val start = min(max(0, n), arr.size)
387
+ val len = arr.size - start
388
+ val result = allocate[A](len)
389
+ each(0, len) { i =>
390
+ result.unsafeSet(i, arr.unsafeGet(i + start))
391
+ }
392
+ result
393
+ }
394
+
395
+ /// Return a slice from start (inclusive) to end (exclusive).
396
+ /// Returns a new array.
397
+ ///
398
+ /// O(N)
399
+ def sliced[A](arr: Array[A], start: Int, end: Int): Array[A] = {
400
+ val validStart = min(max(0, start), arr.size)
401
+ val validEnd = min(max(validStart, end), arr.size)
402
+ val len = validEnd - validStart
403
+ val result = allocate[A](len)
404
+ each(0, len) { i =>
405
+ result.unsafeSet(i, arr.unsafeGet(i + validStart))
406
+ }
407
+ result
408
+ }
409
+
410
+ /// Zip two arrays into an array of pairs.
411
+ /// Length is minimum of input lengths.
412
+ ///
413
+ /// O(min(N, M))
414
+ def zipped[A, B](this: Array[A], other: Array[B]): Array[(A, B)] = {
415
+ val len = min(this.size, other.size)
416
+ val result = allocate[(A, B)](len)
417
+ each(0, len) { i =>
418
+ result.unsafeSet(i, (this.unsafeGet(i), other.unsafeGet(i)))
419
+ }
420
+ result
421
+ }
422
+
423
+ /// Combine two arrays using given function.
424
+ /// Length is minimum of input lengths.
425
+ ///
426
+ /// O(min(N, M))
427
+ def zippedWith[A, B, C](this: Array[A], other: Array[B]) { f: (A, B) => C }: Array[C] = {
428
+ val len = min(this.size, other.size)
429
+ val result = allocate[C](len)
430
+ each(0, len) { i =>
431
+ result.unsafeSet(i, f(this.unsafeGet(i), other.unsafeGet(i)))
432
+ }
433
+ result
434
+ }
435
+
436
+ /// Produce a pair of arrays from an array of pairs.
437
+ /// Allocates two new arrays as a result.
438
+ ///
439
+ /// O(N)
440
+ def unzipped[A, B](arr: Array[(A, B)]): (Array[A], Array[B]) = {
441
+ val arr1 = allocate(arr.size)
442
+ val arr2 = allocate(arr.size)
443
+ arr.foreachIndex { (idx, tpl) =>
444
+ val (x, y) = tpl
445
+ arr1.unsafeSet(idx, x)
446
+ arr2.unsafeSet(idx, y)
447
+ }
448
+ (arr1, arr2)
449
+ }
450
+
451
+ /// Partition a given array into two arrays.
452
+ /// The left array contains the elements that satsify the predicate,
453
+ /// the right array contains the elements that do not.
454
+ ///
455
+ /// O(N)
456
+ def partioned[A](arr: Array[A]) { pred: A => Bool }: (Array[A], Array[A]) = {
457
+ val tcases = arr.copy()
458
+ val fcases = arr.copy()
459
+ var tidx = 0
460
+ var fidx = 0
461
+ arr.foreach { x =>
462
+ if (pred(x)) {
463
+ tcases.unsafeSet(tidx, x)
464
+ tidx = tidx + 1
465
+ } else {
466
+ fcases.unsafeSet(fidx, x)
467
+ fidx = fidx + 1
468
+ }
469
+ }
470
+ (tcases.resize(tidx), fcases.resize(fidx))
471
+ }
472
+
473
+ /// Get the _first_ index of the last occurance of `elem`, if there is any.
474
+ ///
475
+ /// O(N)
476
+ def indexOf[A](arr: Array[A], toFind: A) { eq: (A, A) => Bool }: Option[Int] = {
477
+ var found = None[Int]()
478
+ arr.foreachIndex { (idx, elem) {label} =>
479
+ if (eq(elem, toFind) && found.isEmpty) {
480
+ found = Some(idx)
481
+ label.break()
482
+ }
483
+ }
484
+ found
485
+ }
486
+
487
+ /// Get the _last_ index of the last occurance of `elem`, if there is any.
488
+ ///
489
+ /// O(N)
490
+ def lastIndexOf[A](arr: Array[A], toFind: A) { eq: (A, A) => Bool }: Option[Int] = {
491
+ var lastFound = None[Int]()
492
+ arr.foreachIndex { (idx, elem) =>
493
+ if (eq(elem, toFind)) {
494
+ lastFound = Some(idx)
495
+ }
496
+ }
497
+ lastFound
498
+ }
499
+
500
+ /// Perform binary search to find the index of a given element.
501
+ ///
502
+ /// O(log N)
503
+ def binarySearch[A](arr: Array[A], toFind: A) { ord: (A, A) => Ordering }: Option[Int] = {
504
+ def binarySearch(lowerIdx: Int, upperIdx: Int): Option[Int] = {
505
+ if (lowerIdx > upperIdx) None()
506
+ else {
507
+ val mid = lowerIdx + (upperIdx - lowerIdx) / 2
508
+ val current = arr.unsafeGet(mid)
509
+ ord(current, toFind) match {
510
+ case Less() => binarySearch(mid + 1, upperIdx)
511
+ case Greater() => binarySearch(lowerIdx, mid - 1)
512
+ case Equal() => Some(mid)
513
+ }
514
+ }
515
+ }
516
+ binarySearch(0, arr.size - 1)
517
+ }
518
+
519
+ /// Sort an array in-place using provided comparison function.
520
+ /// Note: sort is unstable since it uses QuickSort (unlike list::sortBy)
521
+ ///
522
+ /// O(N log N) average case
523
+ def sort[A](arr: Array[A]) { ord: (A, A) => Ordering }: Unit = {
524
+ def partition(low: Int, high: Int): Int = {
525
+ val pivot = arr.unsafeGet(high)
526
+ var i = low - 1
527
+
528
+ each(low, high) { j =>
529
+ ord(arr.unsafeGet(j), pivot) match {
530
+ case Less() =>
531
+ i = i + 1
532
+ unsafeSwap(arr, i, j)
533
+ case _ => ()
534
+ }
535
+ }
536
+ unsafeSwap(arr, i + 1, high)
537
+ i + 1
538
+ }
539
+
540
+ def quickSort(low: Int, high: Int): Unit = {
541
+ if (low < high) {
542
+ val par = partition(low, high)
543
+ quickSort(low, par - 1)
544
+ quickSort(par + 1, high)
545
+ }
546
+ }
547
+
548
+ quickSort(0, arr.size - 1)
549
+ }
550
+
551
+ /// Sort an array using provided comparison function.
552
+ /// A newly allocated sorted array is returned as result, i.e., the sorting is not in-place.
553
+ /// Note: sort is unstable since it uses QuickSort (unlike list::sortBy)
554
+ ///
555
+ /// O(N log N) average case
556
+ def sorted[A](arr: Array[A]) { ord: (A, A) => Ordering }: Array[A] = {
557
+ val res = arr.copy()
558
+ res.sort { ord }
559
+ res
560
+ }
561
+
188
562
  // Show Instances
189
563
  // --------------
190
564
 
@@ -41,6 +41,15 @@ extern global def unsafeSet(arr: ByteArray, index: Int, value: Byte): Unit =
41
41
  """
42
42
  chez "(bytevector-u8-set! ${arr} ${index} ${value})"
43
43
 
44
+ /// Creates a new ByteArray of size `size` filled with the value `init`
45
+ def bytearray(size: Int, init: Byte): ByteArray = {
46
+ val arr = allocate(size);
47
+ each(0, size) { i =>
48
+ unsafeSet(arr, i, init)
49
+ };
50
+ arr
51
+ }
52
+
44
53
  def resize(source: ByteArray, size: Int): ByteArray = {
45
54
  val target = allocate(size)
46
55
  val n = min(source.size, target.size)
@@ -50,7 +50,7 @@ def wrongFormat(msg: String): Nothing / Exception[WrongFormat] =
50
50
  /**
51
51
  * A fatal runtime error that cannot be caught
52
52
  */
53
- extern io def panic[R](msg: String): R =
53
+ extern io def panic(msg: String): Nothing =
54
54
  js "(function() { throw ${msg} })()"
55
55
  chez "(raise ${msg})"
56
56
  llvm """
@@ -109,6 +109,10 @@ def close(file: File): Unit / Exception[IOError] = {
109
109
  internal::checkResult(internal::close(file)); ()
110
110
  }
111
111
 
112
+ def mkdir(path: String): Unit / Exception[IOError] = {
113
+ internal::checkResult(internal::mkdir(path)); ()
114
+ }
115
+
112
116
  namespace internal {
113
117
 
114
118
  extern js """
@@ -149,6 +153,12 @@ namespace internal {
149
153
  if (err) { callback(err.errno) } else { callback(0) }
150
154
  })
151
155
  }
156
+
157
+ function mkdir(path, callback) {
158
+ fs.mkdir(path, (err) => {
159
+ if (err) { callback(err.errno) } else { callback(0) }
160
+ })
161
+ }
152
162
  """
153
163
 
154
164
  extern llvm """
@@ -158,6 +168,7 @@ namespace internal {
158
168
  declare void @c_fs_read(%Int, %Pos, %Int, %Int, %Int, %Stack)
159
169
  declare void @c_fs_write(%Int, %Pos, %Int, %Int, %Int, %Stack)
160
170
  declare void @c_fs_close(%Int, %Stack)
171
+ declare void @c_fs_mkdir(%Pos, %Stack)
161
172
  """
162
173
 
163
174
  extern async def openForReading(path: String): Int =
@@ -204,6 +215,13 @@ namespace internal {
204
215
  ret void
205
216
  """
206
217
 
218
+ extern async def mkdir(path: String): Int =
219
+ jsNode "$effekt.capture(callback => mkdir(${path}, callback))"
220
+ llvm """
221
+ call void @c_fs_mkdir(%Pos ${path}, %Stack %stack)
222
+ ret void
223
+ """
224
+
207
225
  def checkResult(result: Int): Int / Exception[IOError] =
208
226
  if (result < 0) {
209
227
  val ioError = fromNumber(internal::errorNumber(result));
@@ -29,7 +29,7 @@ def resizableArray[T](): ResizableArray[T] = resizableArray(8)
29
29
 
30
30
  /// Throw an OutOfBounds exception if index is not a valid index into arr
31
31
  def boundsCheck[T](arr: ResizableArray[T], index: Int): Unit / Exception[OutOfBounds] = {
32
- if (index < 0 && index >= arr.size) {
32
+ if (index < 0 || index >= arr.size) {
33
33
  do raise(OutOfBounds(), "Array index out of bounds: " ++ show(index))
34
34
  }
35
35
  }
@@ -282,7 +282,10 @@ def writeFile[R](path: String) { stream: () => R / emit[Byte] }: R / Exception[I
282
282
  push(0, offset)
283
283
  return r
284
284
  } with emit[Byte] { (byte) =>
285
- if (offset >= buffer.size) { push(0, buffer.size) }
285
+ if (offset >= buffer.size) {
286
+ push(0, buffer.size)
287
+ offset = 0
288
+ }
286
289
  buffer.unsafeSet(offset, byte)
287
290
  offset = offset + 1
288
291
  resume(())
@@ -304,7 +307,6 @@ def readFile[R](path: String) { reader: () => R / read[Byte] }: R / Exception[IO
304
307
  case 0 =>
305
308
  do stop()
306
309
  case n =>
307
- offset = 0
308
310
  length = n
309
311
  }
310
312
  }
@@ -313,7 +315,10 @@ def readFile[R](path: String) { reader: () => R / read[Byte] }: R / Exception[IO
313
315
  reader()
314
316
  } with read[Byte] {
315
317
  resume {
316
- if (offset >= length) { pull() }
318
+ if (offset >= length) {
319
+ pull()
320
+ offset = 0
321
+ }
317
322
  val byte = buffer.unsafeGet(offset)
318
323
  offset = offset + 1
319
324
  return byte
@@ -0,0 +1 @@
1
+
@@ -71,7 +71,7 @@ void c_fs_open(String path, int flags, Stack stack) {
71
71
  uv_fs_t* request = malloc(sizeof(uv_fs_t));
72
72
  request->data = stack;
73
73
 
74
- int result = uv_fs_open(uv_default_loop(), request, path_str, flags, 0666, c_resume_int_fs);
74
+ int result = uv_fs_open(uv_default_loop(), request, path_str, flags, 0777, c_resume_int_fs);
75
75
 
76
76
  if (result < 0) {
77
77
  uv_fs_req_cleanup(request);
@@ -79,7 +79,7 @@ void c_fs_open(String path, int flags, Stack stack) {
79
79
  resume_Int(stack, result);
80
80
  }
81
81
 
82
- // We must free the string either way, since libuv copies it into the request
82
+ // Free the string since libuv copies it into the request
83
83
  free(path_str);
84
84
 
85
85
  return;
@@ -147,6 +147,29 @@ void c_fs_close(Int file, Stack stack) {
147
147
  }
148
148
  }
149
149
 
150
+ void c_fs_mkdir(String path, Stack stack) {
151
+
152
+ // Convert the Effekt String to a null-terminated string
153
+ char* path_str = c_bytearray_into_nullterminated_string(path);
154
+ erasePositive((struct Pos) path);
155
+
156
+ uv_fs_t* request = malloc(sizeof(uv_fs_t));
157
+ request->data = stack;
158
+
159
+ // Perform the mkdir operation
160
+ int result = uv_fs_mkdir(uv_default_loop(), request, path_str, 0777, c_resume_int_fs);
161
+
162
+ if (result < 0) {
163
+ uv_fs_req_cleanup(request);
164
+ free(request);
165
+ resume_Int(stack, result);
166
+ }
167
+
168
+ // Free the string since libuv copies it into the request
169
+ free(path_str);
170
+
171
+ return;
172
+ }
150
173
 
151
174
  /**
152
175
  * Maps the libuv error code to a stable (platform independent) numeric value.