@effekt-lang/effekt 0.78.0 → 0.80.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 +1 -1
- package/libraries/common/bytearray.effekt +1 -1
- package/libraries/common/dictionary.effekt +243 -53
- package/libraries/common/effekt.effekt +1 -1
- 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/map.effekt +194 -100
- package/libraries/common/process.effekt +6 -5
- package/libraries/common/random.effekt +14 -1
- package/libraries/common/set.effekt +9 -19
- package/libraries/common/sourcepos.effekt +75 -0
- package/libraries/common/test.effekt +57 -56
- package/libraries/js/effekt_runtime.js +19 -3
- package/libraries/llvm/bytearray.c +1 -1
- package/libraries/llvm/io.c +17 -17
- package/libraries/llvm/rts.ll +30 -29
- package/libraries/llvm/types.c +3 -3
- package/licenses/mit license - mit-license.html +28 -28
- package/licenses/the bsd license - bsd-license.html +27 -27
- package/licenses/the mit license - mit.html +27 -27
- package/package.json +1 -1
|
@@ -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})"
|
|
@@ -47,7 +48,7 @@ extern def onStdout(opts: SpawnOptions, callback: String => Unit at {io, global}
|
|
|
47
48
|
};
|
|
48
49
|
return old; })()"""
|
|
49
50
|
llvm """
|
|
50
|
-
%opts = call %Pos @c_spawn_options_on_stdout(%Pos ${opts}, %
|
|
51
|
+
%opts = call %Pos @c_spawn_options_on_stdout(%Pos ${opts}, %Neg ${callback})
|
|
51
52
|
ret %Pos %opts
|
|
52
53
|
"""
|
|
53
54
|
extern def onStderr(opts: SpawnOptions, callback: String => Unit at {io, global}) at {}: SpawnOptions =
|
|
@@ -63,7 +64,7 @@ extern def onStderr(opts: SpawnOptions, callback: String => Unit at {io, global}
|
|
|
63
64
|
};
|
|
64
65
|
return old; })()"""
|
|
65
66
|
llvm """
|
|
66
|
-
%opts = call %Pos @c_spawn_options_on_stderr(%Pos ${opts}, %
|
|
67
|
+
%opts = call %Pos @c_spawn_options_on_stderr(%Pos ${opts}, %Neg ${callback})
|
|
67
68
|
ret %Pos %opts
|
|
68
69
|
"""
|
|
69
70
|
extern type WritableStream =
|
|
@@ -83,7 +84,7 @@ extern def withStdin(opts: SpawnOptions, callback: WritableStream => Unit at {io
|
|
|
83
84
|
return old;
|
|
84
85
|
})()"""
|
|
85
86
|
llvm """
|
|
86
|
-
%opts = call %Pos @c_spawn_options_pipe_stdin(%Pos ${opts}, %
|
|
87
|
+
%opts = call %Pos @c_spawn_options_pipe_stdin(%Pos ${opts}, %Neg ${callback})
|
|
87
88
|
ret %Pos %opts
|
|
88
89
|
"""
|
|
89
90
|
extern def write(to: WritableStream, s: String) at io: Unit =
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module sourcepos
|
|
2
|
+
import io/error
|
|
3
|
+
import io/filesystem
|
|
4
|
+
import stringbuffer
|
|
5
|
+
import tty
|
|
6
|
+
|
|
7
|
+
def toGccStyleString(sp: SourcePosition): String = {
|
|
8
|
+
s"${sp.file}:${sp.startLine.show()}:${sp.startColumn.show()}"
|
|
9
|
+
}
|
|
10
|
+
def toClangStyleString = toGccStyleString
|
|
11
|
+
def toMsvcStyleString(sp: SourcePosition): String = {
|
|
12
|
+
s"${sp.file}(${sp.startLine.show()},${sp.startColumn.show()})"
|
|
13
|
+
}
|
|
14
|
+
def toPythonStyleString(sp: SourcePosition): String = {
|
|
15
|
+
s"File \"${sp.file}\", line ${sp.startLine.show()}"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def getSourceLine(file: String, line: Int): String / {Exception[IOError], stop} = {
|
|
19
|
+
with stringBuffer
|
|
20
|
+
with readFileUTF8(file)
|
|
21
|
+
var currentLine = 1
|
|
22
|
+
while (currentLine < line) {
|
|
23
|
+
val c = do next()
|
|
24
|
+
if (c == '\n' || c == '\r') {
|
|
25
|
+
currentLine += 1
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
var c = do next()
|
|
29
|
+
while (c != '\n' && c != '\r') {
|
|
30
|
+
do write(c.toString)
|
|
31
|
+
c = do next()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
def getSourceText(sp: SourcePosition): String / {Exception[IOError], stop} = {
|
|
35
|
+
with stringBuffer
|
|
36
|
+
with readFileUTF8(sp.file)
|
|
37
|
+
var currentLine = 1
|
|
38
|
+
var currentColumn = 1
|
|
39
|
+
while (currentLine < sp.startLine || currentColumn < sp.startColumn) {
|
|
40
|
+
val c = do next()
|
|
41
|
+
if (c == '\n' || c == '\r') {
|
|
42
|
+
currentLine += 1
|
|
43
|
+
currentColumn = 0
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
var c = do next()
|
|
47
|
+
while (currentLine <= sp.endLine && currentColumn <= sp.endColumn) {
|
|
48
|
+
do write(c.toString)
|
|
49
|
+
if (c == '\n' || c == '\r') {
|
|
50
|
+
currentLine += 1
|
|
51
|
+
currentColumn = 0
|
|
52
|
+
}
|
|
53
|
+
c = do next()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
def getMarkedSource(sp: SourcePosition): String / Formatted = {
|
|
58
|
+
try {
|
|
59
|
+
if (sp.startLine == sp.endLine) {
|
|
60
|
+
val line = getSourceLine(sp.file, sp.startLine)
|
|
61
|
+
val mline = line.substring(0, sp.startColumn) ++ line.substring(sp.startColumn, sp.endColumn + 1).red ++ line.substring(sp.endColumn + 1)
|
|
62
|
+
return mline ++ "\n" ++ " ".repeat(sp.startColumn - 1) ++ "^".repeat(sp.endColumn - sp.startColumn)
|
|
63
|
+
} else {
|
|
64
|
+
val prefix = getSourceLine(sp.file, sp.startLine).substring(0, sp.startColumn)
|
|
65
|
+
val suffix = getSourceLine(sp.file, sp.endLine).substring(sp.endColumn + 1)
|
|
66
|
+
val text = getSourceText(sp)
|
|
67
|
+
return prefix ++ text.red ++ suffix
|
|
68
|
+
}
|
|
69
|
+
} with stop {
|
|
70
|
+
"(invalid source position)"
|
|
71
|
+
} with Exception[IOError] {
|
|
72
|
+
def raise(e, m) =
|
|
73
|
+
"(can't open source file: " ++ m ++ ")"
|
|
74
|
+
}
|
|
75
|
+
}
|