@effekt-lang/effekt 0.77.0 → 0.79.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.
@@ -147,28 +147,28 @@ namespace internal {
147
147
  extern def connect(host: String, port: Int) at async: Int =
148
148
  jsNode "$effekt.capture(k => tcp$connect(${host}, ${port}, k))"
149
149
  llvm """
150
- call void @c_tcp_connect(%Pos ${host}, %Int ${port}, %Stack %stack)
150
+ notail call void @c_tcp_connect(%Pos ${host}, %Int ${port}, %Stack %stack)
151
151
  ret void
152
152
  """
153
153
 
154
154
  extern def read(handle: Int, buffer: ByteArray, offset: Int, size: Int) at async: Int =
155
155
  jsNode "$effekt.capture(k => tcp$read(${handle}, ${buffer}, ${offset}, ${size}, k))"
156
156
  llvm """
157
- call void @c_tcp_read(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
157
+ notail call void @c_tcp_read(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
158
158
  ret void
159
159
  """
160
160
 
161
161
  extern def write(handle: Int, buffer: ByteArray, offset: Int, size: Int) at async: Int =
162
162
  jsNode "$effekt.capture(k => tcp$write(${handle}, ${buffer}, ${offset}, ${size}, k))"
163
163
  llvm """
164
- call void @c_tcp_write(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
164
+ notail call void @c_tcp_write(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
165
165
  ret void
166
166
  """
167
167
 
168
168
  extern def close(handle: Int) at async: Unit =
169
169
  jsNode "$effekt.capture(k => tcp$close(${handle}, k))"
170
170
  llvm """
171
- call void @c_tcp_close(%Int ${handle}, %Stack %stack)
171
+ notail call void @c_tcp_close(%Int ${handle}, %Stack %stack)
172
172
  ret void
173
173
  """
174
174
 
@@ -182,14 +182,14 @@ namespace internal {
182
182
  extern def listen(listener: Int, handler: Connection => Unit at {io, async, global}) at async: Int =
183
183
  jsNode "$effekt.capture(k => tcp$listen(${listener}, ${handler}, k))"
184
184
  llvm """
185
- call void @c_tcp_listen(%Int ${listener}, %Pos ${handler}, %Stack %stack)
185
+ notail call void @c_tcp_listen(%Int ${listener}, %Pos ${handler}, %Stack %stack)
186
186
  ret void
187
187
  """
188
188
 
189
189
  extern def shutdown(handle: Int) at async: Unit =
190
190
  jsNode "$effekt.capture(k => tcp$shutdown(${handle}, k))"
191
191
  llvm """
192
- call void @c_tcp_shutdown(%Int ${handle}, %Stack %stack)
192
+ notail call void @c_tcp_shutdown(%Int ${handle}, %Stack %stack)
193
193
  ret void
194
194
  """
195
195
 
@@ -74,18 +74,18 @@ extern def fire[A, B](signal: Signal[A, B], value: A) at async: B =
74
74
  store i64 2, ptr %statePtr
75
75
  store %Stack %stack, ptr %stackPtr
76
76
  store %Pos ${value}, ptr %valuePtr
77
- call void @erasePositive(%Pos ${signal})
77
+ notail call void @erasePositive(%Pos ${signal})
78
78
  ret void
79
79
  fire:
80
80
  %otherStack = load %Stack, ptr %stackPtr
81
81
  %otherValue = load %Pos, ptr %valuePtr
82
82
  store i64 3, ptr %statePtr
83
- call void @erasePositive(%Pos ${signal})
84
- call void @resume_Pos(%Stack %otherStack, %Pos ${value})
85
- call void @resume_Pos(%Stack %stack, %Pos %otherValue)
83
+ notail call void @erasePositive(%Pos ${signal})
84
+ notail call void @resume_Pos(%Stack %otherStack, %Pos ${value})
85
+ notail call void @resume_Pos(%Stack %stack, %Pos %otherValue)
86
86
  ret void
87
87
  panic:
88
- call void @exit(i64 1)
88
+ notail call void @exit(i32 1)
89
89
  ret void
90
90
  """
91
91
 
@@ -102,18 +102,18 @@ extern def wait[A, B](signal: Signal[A, B], value: B) at async: A =
102
102
  store i64 1, ptr %statePtr
103
103
  store %Stack %stack, ptr %stackPtr
104
104
  store %Pos ${value}, ptr %valuePtr
105
- call void @erasePositive(%Pos ${signal})
105
+ notail call void @erasePositive(%Pos ${signal})
106
106
  ret void
107
107
  wait:
108
108
  %otherStack = load %Stack, ptr %stackPtr
109
109
  %otherValue = load %Pos, ptr %valuePtr
110
110
  store i64 3, ptr %statePtr
111
- call void @erasePositive(%Pos ${signal})
112
- call void @resume_Pos(%Stack %otherStack, %Pos ${value})
113
- call void @resume_Pos(%Stack %stack, %Pos %otherValue)
111
+ notail call void @erasePositive(%Pos ${signal})
112
+ notail call void @resume_Pos(%Stack %otherStack, %Pos ${value})
113
+ notail call void @resume_Pos(%Stack %stack, %Pos %otherValue)
114
114
  ret void
115
115
  panic:
116
- call void @exit(i64 1)
116
+ notail call void @exit(i32 1)
117
117
  ret void
118
118
  """
119
119
 
@@ -7,6 +7,6 @@ extern llvm """
7
7
  extern def sleep(millis: Int) at async: Unit =
8
8
  js "$effekt.capture(k => setTimeout(() => k($effekt.unit), ${millis}))"
9
9
  llvm """
10
- call void @c_timer_start(%Int ${millis}, %Stack %stack)
10
+ notail call void @c_timer_start(%Int ${millis}, %Stack %stack)
11
11
  ret void
12
12
  """
@@ -11,22 +11,22 @@ extern llvm """
11
11
  extern def spawn[T](task: () => T at {io, async, global}) at async: Unit =
12
12
  js "$effekt.capture(k => { setTimeout(() => k($effekt.unit), 0); return $effekt.run(${task}) })"
13
13
  llvm """
14
- call void @c_yield(%Stack %stack)
15
- call void @run(%Pos ${task})
14
+ notail call void @c_yield(%Stack %stack)
15
+ notail call void @run(%Pos ${task})
16
16
  ret void
17
17
  """
18
18
 
19
19
  extern def yield() at async: Unit =
20
20
  js "$effekt.capture(k => setTimeout(() => k($effekt.unit), 0))"
21
21
  llvm """
22
- call void @c_yield(%Stack %stack)
22
+ notail call void @c_yield(%Stack %stack)
23
23
  ret void
24
24
  """
25
25
 
26
26
  extern def abort() at async: Nothing =
27
27
  js "$effekt.capture(k => undefined)"
28
28
  llvm """
29
- call void @eraseStack(%Stack %stack)
29
+ notail call void @eraseStack(%Stack %stack)
30
30
  ret void
31
31
  """
32
32
 
@@ -820,6 +820,28 @@ def partition[A](l: List[A]) { pred: A => Bool }: (List[A], List[A]) = {
820
820
  (lefts.reverse, rights.reverse)
821
821
  }
822
822
 
823
+ /// List functions that we discourage to use, but should still be available
824
+ namespace slow {
825
+ /// Find the first element in a list which satisfies a given predicate.
826
+ /// If there is no such element, returns None.
827
+ ///
828
+ /// Examples:
829
+ /// ```
830
+ /// > [].slow::find{ x => x.mod(2) == 0 }
831
+ /// None()
832
+ /// > [1, 3, 6].slow::find{ x => x.mod(2) == 0 }
833
+ /// Some(6)
834
+ /// > [2, 4, 6].slow::find{ x => x.mod(2) == 0 }
835
+ /// Some(2)
836
+ /// ```
837
+ ///
838
+ /// O(N)
839
+ def find[A](list: List[A]){ pred: A => Bool }: Option[A] = list match {
840
+ case Cons(x, xs) => if (pred(x)) Some(x) else find(xs){pred}
841
+ case Nil() => None()
842
+ }
843
+ }
844
+
823
845
  /// Utilities for sorting, see 'sortBy' for more details.
824
846
  namespace sort {
825
847
  /// Splits the given list into monotonic segments (so a list of lists).
@@ -80,19 +80,13 @@ def get[K, V](m: Map[K, V], k: K): Option[V] = internal::get(m.tree, m.compare,
80
80
  ///
81
81
  /// O(log N)
82
82
  def getOrElse[K, V](m: Map[K, V], k: K) { default: => V }: V =
83
- internal::get(m.tree, m.compare, k) match {
84
- case None() => default()
85
- case Some(v) => v
86
- }
83
+ internal::getOrElse(m.tree, m.compare, k) {default}
87
84
 
88
85
  /// Check if map `m` contains a key `k`.
89
86
  ///
90
87
  /// O(log N)
91
88
  def contains[K, V](m: Map[K, V], k: K): Bool =
92
- internal::get(m.tree, m.compare, k) match {
93
- case None() => false
94
- case Some(_) => true
95
- }
89
+ internal::contains(m.tree, m.compare, k)
96
90
 
97
91
  /// Get minimum in the map `m`.
98
92
  ///
@@ -354,15 +348,10 @@ def collect[K, V](?compare: (K, K) => Ordering at {}) { stream: () => Unit / emi
354
348
  namespace returning {
355
349
  /// Collects `(key, value)` pairs from a push stream producer `stream` into a new `map`,
356
350
  /// using the provided `compare` function, returning the result of the producer as well as the collected map.
357
- def collect[K, V, R](?compare: (K, K) => Ordering at {}) { stream: () => R / emit[(K, V)] }: (R, Map[K, V]) =
358
- var map = map::empty[K, V](compare)
359
- val r = try {
360
- stream()
361
- } with emit[(K, V)] { case (k, v) =>
362
- map = map.put(k, v)
363
- resume(())
364
- }
365
- (r, map)
351
+ def collect[K, V, R](?compare: (K, K) => Ordering at {}) { stream: () => R / emit[(K, V)] }: (R, Map[K, V]) = {
352
+ val (r, tree) = internal::collect[K, V, R](compare) {stream}
353
+ (r, Map(tree, compare))
354
+ }
366
355
  }
367
356
 
368
357
  // Internal tree representation
@@ -417,6 +406,33 @@ namespace internal {
417
406
  case Bin(size, _, _, _, _) => size
418
407
  }
419
408
 
409
+ /// Look up `k` in the tree `m`, handing the value it is bound to to `found`,
410
+ /// or running `missing` if there is none.
411
+ ///
412
+ /// O(log N)
413
+ def getWith[K, V, R](m: Tree[K, V], compare: (K, K) => Ordering at {}, k: K) { found: V => R } { missing: => R }: R =
414
+ m match {
415
+ case Tip() => missing()
416
+ case Bin(_, k2, v, l, r) =>
417
+ compare(k, k2) match {
418
+ case Less() => getWith(l, compare, k) {found} {missing}
419
+ case Greater() => getWith(r, compare, k) {found} {missing}
420
+ case Equal() => found(v)
421
+ }
422
+ }
423
+
424
+ /// Whether the tree `m` binds the key `k`.
425
+ ///
426
+ /// O(log N)
427
+ def contains[K, V](m: Tree[K, V], compare: (K, K) => Ordering at {}, k: K): Bool =
428
+ getWith(m, compare, k) { _v => true } { false }
429
+
430
+ /// The value bound to `k` in the tree `m`, or `default` if there is none.
431
+ ///
432
+ /// O(log N)
433
+ def getOrElse[K, V](m: Tree[K, V], compare: (K, K) => Ordering at {}, k: K) { default: => V }: V =
434
+ getWith(m, compare, k) { v => v } {default}
435
+
420
436
  /// Insert a new key `k` and value `v` into the tree `m`.
421
437
  /// If the key `k` is already present in `m`, its associated value is replaced with `v`.
422
438
  ///
@@ -451,15 +467,7 @@ namespace internal {
451
467
  ///
452
468
  /// O(log N)
453
469
  def get[K, V](m: Tree[K, V], compare: (K, K) => Ordering at {}, k: K): Option[V] =
454
- m match {
455
- case Tip() => None()
456
- case Bin(size, k2, v, l, r) =>
457
- compare(k, k2) match {
458
- case Less() => get(l, compare, k)
459
- case Greater() => get(r, compare, k)
460
- case Equal() => Some(v)
461
- }
462
- }
470
+ getWith(m, compare, k) { v => Some(v) } { None() }
463
471
 
464
472
  /// Get minimum in the tree `m`.
465
473
  ///
@@ -600,98 +608,184 @@ namespace internal {
600
608
  go(m)
601
609
  }
602
610
 
611
+ /// A balanced subtree waiting to be joined, and the key separating it from the pairs collected after it.
612
+ ///
613
+ /// A list of these is the *right* spine of the tree being built.
614
+ /// Their sizes strictly increase towards the back, so the list is the digits of a binary counter.
615
+ record Waiting[K, V](size: Int, key: K, value: V, tree: Tree[K, V])
616
+
617
+ /// Collect `(key, value)` pairs from a push stream producer `stream` into a tree,
618
+ /// returning the result of the producer as well.
619
+ /// Consider using `map::fromList` if you know the full list.
620
+ ///
621
+ /// O(N) if the stream is sorted by key (in _either_ direction),
622
+ /// O(N log N) otherwise
623
+ def collect[K, V, R](compare: (K, K) => Ordering at {}) { stream: () => R / emit[(K, V)] }: (R, Tree[K, V]) = {
624
+ // Note that `fromList` is always better since it knows the whole list of pairs in advance,
625
+ // whereas we have to be streaming here. Otherwise the functions are very similar...
626
+
627
+ // The lowest digit of the binary counter is a lone pair, kept as the leaf it will one day become. :)
628
+ var lowest: Tree[K, V] = Tip()
629
+ var spine: List[Waiting[K, V]] = Nil()
630
+ var tree = Tip()
631
+ // which way the current run is going; is `Equal` once a monotone run is over
632
+ var direction: Ordering = Less()
633
+
634
+ // How the currently collected pair compares with the one collected last.
635
+ def compareLast(k: K): Ordering =
636
+ lowest match {
637
+ case Bin(_, previous, _, _, _) => compare(previous, k)
638
+ case Tip() => spine match {
639
+ case Nil() => Less()
640
+ case Cons(Waiting(_, previous, _, _), _) => compare(previous, k)
641
+ }
642
+ }
643
+
644
+ // Give the pair collected last a new value, overwriting the existing one.
645
+ def rebind(v: V): Unit =
646
+ lowest match {
647
+ case Bin(_, k, _, _, _) =>
648
+ lowest = singleton(k, v)
649
+ case Tip() => spine match {
650
+ case Nil() => ()
651
+ case Cons(Waiting(size, k, _, tree), more) =>
652
+ spine = Cons(Waiting(size, k, v, tree), more)
653
+ }
654
+ }
655
+
656
+ // Increment the binary counter: carry while the next subtree has the same size.
657
+ def extend(k: K, v: V): Unit = {
658
+ def carry(size: Int, subtree: Tree[K, V], rest: List[Waiting[K, V]]): List[Waiting[K, V]] =
659
+ rest match {
660
+ case Nil() => [Waiting(size, k, v, subtree)]
661
+ case Cons(Waiting(s, k2, v2, t), more) =>
662
+ if (s == size) {
663
+ val merged = if (direction is Greater()) bin(k2, v2, subtree, t) else bin(k2, v2, t, subtree)
664
+ carry(2 * size + 1, merged, more)
665
+ } else {
666
+ Cons(Waiting(size, k, v, subtree), rest)
667
+ }
668
+ }
669
+
670
+ lowest match {
671
+ case Tip() => lowest = singleton(k, v)
672
+ case Bin(_, _, _, _, _) =>
673
+ val leaf = lowest
674
+ lowest = Tip()
675
+ spine = carry(1, leaf, spine)
676
+ }
677
+ }
678
+
679
+ def joinSpineWith(spine: List[Waiting[K, V]], whole: Tree[K, V]): Tree[K, V] =
680
+ spine match {
681
+ case Nil() => whole
682
+ case Cons(Waiting(_, k, v, subtree), more) =>
683
+ val joined = if (direction is Greater()) link(k, v, whole, subtree) else link(k, v, subtree, whole)
684
+ more.joinSpineWith(joined)
685
+ }
686
+
687
+ val result = try {
688
+ stream()
689
+ } with emit[(K, V)] { case (k, v) =>
690
+ // as long as the keys all keep going the same way, we can build a tree in O(N) time
691
+ direction match {
692
+ // the run is over, so we have to insert each new pair without a shortcut
693
+ case Equal() => tree = tree.put(compare, k, v)
694
+ case _ =>
695
+ (compareLast(k), direction) match {
696
+ case (Equal(), _) => rebind(v)
697
+ case (Less(), Less()) => extend(k, v)
698
+ case (Greater(), Greater()) => extend(k, v)
699
+ case (ordering, _) =>
700
+ // the new key goes against the run:
701
+ if (spine is Nil()) {
702
+ // - if it's the first pair, we just set the direction
703
+ direction = ordering
704
+ extend(k, v)
705
+ } else {
706
+ // - if it's not the first pair, we have to join up the run and use the slow path
707
+ tree = spine.joinSpineWith(lowest).put(compare, k, v)
708
+ direction = Equal()
709
+ }
710
+ }
711
+ }
712
+
713
+ resume(())
714
+ }
715
+
716
+ (result, if (direction is Equal()) tree else spine.joinSpineWith(lowest))
717
+ }
718
+
603
719
  /// Create a tree from a list of (key, value) pairs.
604
720
  /// If the list contains more than one value for the same key,
605
721
  /// only the last value is used in the tree.
606
722
  ///
607
- /// O(N) if the list is sorted by key,
723
+ /// O(N) if the list is sorted by key (in _either_ direction),
608
724
  /// O(N log N) otherwise
609
- def fromList[K, V](pairs: List[(K, V)], compare: (K, K) => Ordering at {}): Tree[K, V] = {
725
+ def fromList[K, V](pairs: List[(K, V)], compare: (K, K) => Ordering at {}): Tree[K, V] =
610
726
  pairs match {
611
727
  case Nil() => Tip()
612
728
  case Cons((k, v), Nil()) => singleton(k, v)
613
- case Cons((k, v), rest) =>
614
- // TODO: this function should really, **really** get inlined!
615
- def notOrdered(k: K, pairs: List[(K, V)]) = {
616
- pairs match {
617
- case Nil() => false
618
- case Cons((k2, _), _) => // k >= k2
619
- compare(k, k2) match {
620
- case Less() => false
621
- case Greater() => true
622
- case Equal() => true
729
+ case Cons(_, _) =>
730
+ // the first pair not yet taken, and whether the run repeated a key
731
+ var rest = pairs
732
+ var repeated = false
733
+ // which way the current run is going; is `Equal` once a monotone run is over
734
+ var direction: Ordering = Equal()
735
+
736
+ // How many distinct keys the non-decreasing run at the front of `ps` holds.
737
+ def runLength(ps: List[(K, V)], count: Int): Int =
738
+ ps match {
739
+ case Nil() => count
740
+ case Cons((k, _), tail) => tail match { // note: tail is effectivelly an @-pattern!
741
+ case Nil() => count + 1
742
+ case Cons((next, _), _) => (compare(k, next), direction) match {
743
+ case (Less() , Less() ) => runLength(tail, count + 1)
744
+ case (Greater(), Greater()) => runLength(tail, count + 1)
745
+ case (Equal(), _) => repeated = true; runLength(tail, count)
746
+
747
+ // the first comparison that is not `Equal` settles which way we go
748
+ case (ordering, Equal()) => direction = ordering; runLength(tail, count + 1)
749
+ case _ => count + 1
623
750
  }
751
+ }
624
752
  }
625
- }
626
753
 
627
- // Naive insertion, used for the worst-case scenario when the list is not sorted by key
628
- def insertMany(m: Tree[K, V], pairs: List[(K, V)]) = {
629
- var treeSoFar = m
630
- pairs.foreach { case (k, v) =>
631
- treeSoFar = treeSoFar.put(compare, k, v)
754
+ // Advance `rest` past any repeats of `k`, giving back the last binding of it.
755
+ def lastBinding(k: K, v: V): V =
756
+ if (rest is Cons((next, value), tail) and compare(k, next) is Equal()) {
757
+ rest = tail
758
+ lastBinding(k, value)
759
+ } else {
760
+ v
632
761
  }
633
- treeSoFar
634
- }
635
762
 
636
- // Returns a triple `(tree, xs, ys)`
637
- //
638
- // Invariant: At least one of `xs`, `ys` is empty.
639
- // Moreover, if `ys` is nonempty, its keys are **not** ordered!
640
- // Otherwise, all of the seen keys have been ordered so far.
641
- //
642
- // TODO: Possibly use a better type to encode the invariant?
643
- def create(level: Int, pairs: List[(K, V)]): (Tree[K, V], List[(K, V)], List[(K, V)]) = {
644
- pairs match {
645
- case Nil() => (Tip(), [], [])
646
- case Cons((k, v), rest) =>
647
- if (level == 1) {
648
- val one = singleton(k, v)
649
- if (notOrdered(k, rest)) {
650
- (one, [], rest)
651
- } else {
652
- (one, rest, [])
653
- }
654
- } else {
655
- val res = create(level.bitwiseShr(1), pairs)
656
- res match {
657
- case (_, Nil(), _) => res
658
- case (l, Cons((k2, v2), Nil()), zs) => (l.putMax(k2, v2), [], zs)
659
- case (l, Cons((k2, v2), rest2), _) =>
660
- // @-pattern
661
- val xs = Cons((k2, v2), rest2)
662
-
663
- if (notOrdered(k2, rest2)) { (l, [], xs) }
664
- else {
665
- val (r, zs, ws) = create(level.bitwiseShr(1), rest2);
666
- (link(k2, v2, l, r), zs, ws)
667
- }
668
- }
669
- }
763
+ // Shape `count` distinct keys from `rest` into a balanced tree, leaving `rest` on the first pair not taken.
764
+ // The order is established already, so knowing the size lays the tree out from the root down, one node per key.
765
+ def fromSorted(count: Int): Tree[K, V] =
766
+ if (count <= 0) Tip() else {
767
+ val half = (count - 1) / 2
768
+ val front = half.fromSorted
769
+ rest match {
770
+ case Nil() => <{ "fromList: fewer pairs than counted" }>
771
+ case Cons((k, v), tail) =>
772
+ rest = tail
773
+ val value = if (repeated) lastBinding(k, v) else v
774
+ val back = (count - 1 - half).fromSorted
775
+ if (direction is Greater()) bin(k, value, back, front) else bin(k, value, front, back)
776
+ }
670
777
  }
671
- }
672
778
 
673
- def go(level: Int, m: Tree[K, V], pairs: List[(K, V)]): Tree[K, V] = {
674
- pairs match {
779
+ // Whatever is left, once the run has stopped, goes through `put`.
780
+ def insertMany(m: Tree[K, V], ps: List[(K, V)]): Tree[K, V] =
781
+ ps match {
675
782
  case Nil() => m
676
- case Cons((k, v), Nil()) => m.putMax(k, v)
677
- case Cons((k, v), rest) =>
678
- if (notOrdered(k, rest)) { insertMany(m, pairs) }
679
- else {
680
- val l = m; // m is the left subtree here
681
- val cr = create(level, rest)
682
- cr match {
683
- case (r, xs, Nil()) => go(level.bitwiseShl(1), link(k, v, l, r), xs)
684
- case (r, Nil(), ys) => insertMany(link(k, v, l, r), ys)
685
- case _ => <{ "create: go: cannot happen, invariant broken!" }>
686
- }
687
- }
783
+ case Cons((k, v), tail) => m.put(compare, k, v).insertMany(tail)
688
784
  }
689
- }
690
785
 
691
- if (notOrdered(k, rest)) { insertMany(singleton(k, v), rest) }
692
- else { go(1, singleton(k, v), rest) }
786
+ val sorted = runLength(pairs, 0).fromSorted
787
+ sorted.insertMany(rest)
693
788
  }
694
- }
695
789
 
696
790
  /// Remove a key `k` from a tree `m`.
697
791
  /// If `k` is not in `m`, `m` is returned.
@@ -1053,4 +1147,4 @@ namespace internal {
1053
1147
 
1054
1148
  "[" ++ res ++ "]"
1055
1149
  }
1056
- }
1150
+ }
@@ -4,7 +4,8 @@ import array
4
4
  extern def exit(errorCode: Int) at io: Nothing =
5
5
  js "(function() { process.exit(${errorCode}) })()"
6
6
  llvm """
7
- call void @exit(i64 ${errorCode})
7
+ %exitCode = trunc i64 ${errorCode} to i32
8
+ call void @exit(i32 %exitCode)
8
9
  ret %Pos zeroinitializer
9
10
  """
10
11
  chez "(exit ${errorCode})"
@@ -107,6 +108,6 @@ extern def spawn(cmd: String, args: Array[String], options: SpawnOptions) at asy
107
108
  return p;
108
109
  })"""
109
110
  llvm """
110
- call void @c_spawn(%Pos ${cmd}, %Pos ${args}, %Pos ${options}, %Stack %stack)
111
+ notail call void @c_spawn(%Pos ${cmd}, %Pos ${args}, %Pos ${options}, %Stack %stack)
111
112
  ret void
112
113
  """
@@ -114,11 +114,24 @@ def randomInt32s(): Unit / {emit[Int], random} =
114
114
 
115
115
  /// `max` is _inclusive_!
116
116
  def randomInt(min: Int, max: Int): Int / random = {
117
+ // How many bytes it takes to represent `range` distinct values.
118
+ // Each bound is a power of two, so it's exact even as a JS 'number'.
119
+ def bytesFor(range: Int): Int =
120
+ if (range <= 1) 0
121
+ else if (range <= 256) 1
122
+ else if (range <= 65536) 2
123
+ else if (range <= 16777216) 3
124
+ else if (range <= 4294967296) 4
125
+ else if (range <= 1099511627776) 5
126
+ else if (range <= 281474976710656) 6
127
+ else if (range <= 72057594037927936) 7
128
+ else 8
129
+
117
130
  if (min > max) {
118
131
  randomInt(max, min)
119
132
  } else {
120
133
  val range = max - min + 1
121
- val bytesNeeded = (log(range.toDouble) / log(256.0)).ceil
134
+ val bytesNeeded = bytesFor(range)
122
135
 
123
136
  var result = 0
124
137
  repeat(bytesNeeded) {
@@ -79,22 +79,19 @@ def toMap[K, V](keys: Set[K]) { valueOf: K => V }: Map[K, V] = {
79
79
 
80
80
  /// Create a set from a given list and a pure, first-class comparison function.
81
81
  ///
82
- /// O(N log N)
83
82
  /// O(N) if the list is sorted
84
- def fromList[A](list: List[A], ?compare: (A, A) => Ordering at {}): Set[A] = {
85
- val tree: internal::Tree[A, Unit] = internal::fromList(list.map { k => (k, ()) }, compare)
86
- Set(tree, compare)
87
- }
83
+ /// O(N log N)
84
+ def fromList[A](list: List[A], ?compare: (A, A) => Ordering at {}): Set[A] =
85
+ set::collect(compare) { list.each }
88
86
 
89
87
  /// Create a set from a given list with a generic comparison function.
90
88
  /// Works only on the JavaScript backends!
91
89
  ///
92
- /// O(N log N)
93
90
  /// O(N) if the list is sorted
91
+ /// O(N log N)
94
92
  def fromListGeneric[A](list: List[A]): Set[A] = {
95
93
  val compare: (A, A) => Ordering at {} = box { (left, right) => genericCompare(left, right) }
96
- val tree: internal::Tree[A, Unit] = internal::fromList(list.map { k => (k, ()) }, compare)
97
- Set(tree, compare)
94
+ set::collect(compare) { list.each }
98
95
  }
99
96
 
100
97
  /// Traverse all elements in order, running the function `action` on each element.
@@ -150,10 +147,7 @@ def any[A](s: Set[A]) { predicate: A => Bool }: Bool = {
150
147
  ///
151
148
  /// O(log N)
152
149
  def contains[A](s: Set[A], a: A): Bool =
153
- internal::get(s.tree, s.compare, a) match {
154
- case None() => false
155
- case Some(v) => true
156
- }
150
+ internal::contains(s.tree, s.compare, a)
157
151
 
158
152
 
159
153
  /// Check if set `s1` is a subset of set `s2`.
@@ -260,13 +254,9 @@ namespace returning {
260
254
  /// Collects elements emitted by a push stream producer `stream` into a new set
261
255
  /// using provided `compare` function, returning the result of the producer as well as the collected set.
262
256
  def collect[A, R](?compare: (A, A) => Ordering at {}) { stream: () => R / emit[A] }: (R, Set[A]) = {
263
- var set = set::empty(compare)
264
- val r = try {
265
- stream()
266
- } with emit[A] { (v) =>
267
- set = set.insert(v)
268
- resume(())
257
+ val (r, tree) = internal::collect[A, Unit, R](compare) {
258
+ stream::returning::for[A, R] {stream} { (v: A) => do emit((v, ())) }
269
259
  }
270
- (r, set)
260
+ (r, Set(tree, compare))
271
261
  }
272
262
  }
@@ -210,6 +210,28 @@ namespace internal {
210
210
  do emit(c.utf8ByteCount)
211
211
  }
212
212
  }
213
+
214
+ def compareCodePoints(left: String, right: String): Ordering = {
215
+ val ln = left.length
216
+ val rn = right.length
217
+ def go(i: Int, j: Int): Ordering =
218
+ if (i >= ln) { if (j >= rn) Equal() else Less() }
219
+ else if (j >= rn) Greater()
220
+ else {
221
+ val a = unsafeCharAt(left, i)
222
+ val b = unsafeCharAt(right, j)
223
+ compareChar(a, b) match {
224
+ case Equal() =>
225
+ // equal code points have equal widths, so take the step only once
226
+ val w = a.charWidth
227
+ go(i + w, j + w)
228
+ case Less() => Less()
229
+ case Greater() => Greater()
230
+ }
231
+ }
232
+
233
+ go(0, 0)
234
+ }
213
235
  }
214
236
 
215
237
  /// Returns the number of bytes that the given `str` would take up in a UTF-8 encoding.
@@ -235,6 +257,12 @@ extern def utf8ByteLength(str: String) at {io, global}: Int =
235
257
  // extern def unsafeIndexOf(str: String, sub: String) at {}: Int =
236
258
  // js "${str}.indexOf(${sub})"
237
259
 
260
+ /// Compares two strings lexicographically by code point.
261
+ /// Identical on every backend.
262
+ extern def compareStringBytes(left: String, right: String) at {}: Ordering =
263
+ llvm { compareByteArray(fromString(left), fromString(right)) }
264
+ default { internal::compareCodePoints(left, right) }
265
+
238
266
  def charAt(str: String, index: Int): Char / Exception[OutOfBounds] =
239
267
  if (index < 0 || index >= length(str))
240
268
  do raise(OutOfBounds(), "Index out of bounds: " ++ show(index) ++ " in string: '" ++ str ++ "'")