@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.
- package/bin/effekt +0 -0
- package/libraries/common/array.effekt +5 -4
- package/libraries/common/bytearray.effekt +45 -26
- package/libraries/common/char.effekt +12 -5
- package/libraries/common/dictionary.effekt +243 -53
- package/libraries/common/effekt.effekt +11 -14
- package/libraries/common/io/filesystem.effekt +7 -7
- package/libraries/common/io/network.effekt +6 -6
- package/libraries/common/io/signal.effekt +10 -10
- package/libraries/common/io/time.effekt +1 -1
- package/libraries/common/io.effekt +4 -4
- package/libraries/common/list.effekt +22 -0
- package/libraries/common/map.effekt +194 -100
- package/libraries/common/process.effekt +3 -2
- package/libraries/common/random.effekt +14 -1
- package/libraries/common/set.effekt +9 -19
- package/libraries/common/string.effekt +28 -0
- package/libraries/common/stringbuffer.effekt +5 -5
- package/libraries/llvm/bytearray.c +15 -6
- package/libraries/llvm/forward-declare-c.ll +1 -0
- package/libraries/llvm/rts.ll +14 -14
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +3 -3
- package/licenses/mit license - mit-license.html +61 -53
- package/licenses/the bsd license - bsd-license.html +61 -53
- package/licenses/the mit license - mit.html +62 -54
- package/package.json +1 -1
|
@@ -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(
|
|
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(
|
|
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::
|
|
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::
|
|
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
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
|
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(
|
|
614
|
-
//
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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
|
-
//
|
|
628
|
-
def
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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
|
-
//
|
|
637
|
-
//
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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
|
-
|
|
674
|
-
|
|
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),
|
|
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
|
-
|
|
692
|
-
|
|
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
|
-
|
|
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 = (
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
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::
|
|
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
|
-
|
|
264
|
-
|
|
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,
|
|
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 ++ "'")
|