@effekt-lang/effekt 0.19.0 → 0.20.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/effekt.effekt +22 -0
- package/libraries/common/heap.effekt +103 -0
- package/libraries/common/io/filesystem.effekt +49 -66
- package/libraries/common/resizable_array.effekt +235 -0
- package/libraries/common/stream.effekt +2 -2
- package/libraries/common/stringbuffer.effekt +65 -0
- package/libraries/common/union_find.effekt +113 -0
- package/libraries/llvm/forward-declare-c.ll +1 -0
- package/libraries/llvm/io.c +17 -43
- package/libraries/llvm/main.c +1 -1
- package/libraries/llvm/panic.c +20 -0
- package/libraries/llvm/rts.ll +141 -142
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +4 -4
- package/licenses/mit license - mit-license.html +77 -17
- package/licenses/the bsd license - bsd-license.html +77 -17
- package/licenses/the mit license - mit.html +76 -16
- package/package.json +1 -1
- package/libraries/llvm/hole.c +0 -11
package/bin/effekt
CHANGED
|
Binary file
|
|
@@ -736,6 +736,28 @@ def repeat(n: Int) { action: () => Unit } = each(0, n) { n => action() }
|
|
|
736
736
|
|
|
737
737
|
def repeat(n: Int) { action: () {Control} => Unit } = each(0, n) { (n) {l} => action() {l} }
|
|
738
738
|
|
|
739
|
+
|
|
740
|
+
// Splices
|
|
741
|
+
// =======
|
|
742
|
+
//
|
|
743
|
+
// The Effekt compiler translates the string interpolation
|
|
744
|
+
//
|
|
745
|
+
// "foo ${42} bar ${43}"
|
|
746
|
+
//
|
|
747
|
+
// to the following stream of `literal`s and `splice[Int]`s:
|
|
748
|
+
//
|
|
749
|
+
// do literal("foo "); do splice(42); do literal(" bar "); do splice(43)
|
|
750
|
+
//
|
|
751
|
+
// The stream is wrapped into a handler function, which defaults to `stringbuffer::s`
|
|
752
|
+
//
|
|
753
|
+
// s { ... }
|
|
754
|
+
//
|
|
755
|
+
// but can be specified by prefixing the string interpolation `custom"..."`.
|
|
756
|
+
|
|
757
|
+
effect literal(s: String): Unit
|
|
758
|
+
effect splice[A](x: A): Unit
|
|
759
|
+
|
|
760
|
+
|
|
739
761
|
namespace internal {
|
|
740
762
|
namespace boxing {
|
|
741
763
|
// For boxing polymorphic values
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
module heap
|
|
2
|
+
import resizable_array
|
|
3
|
+
|
|
4
|
+
/// Resizable 2-ary min-heap, backed by a resizable array
|
|
5
|
+
/// `cmp` defines the ordering of elements
|
|
6
|
+
record Heap[T](rawContents: ResizableArray[T], cmp: (T, T) => Ordering at {})
|
|
7
|
+
|
|
8
|
+
/// Make a new Heap with the given comparison operation
|
|
9
|
+
def heap[T](cmp: (T,T) => Ordering at {}) =
|
|
10
|
+
Heap[T](resizableArray(), cmp)
|
|
11
|
+
|
|
12
|
+
/// Make a new Heap with the given comparison operation and initial capacity
|
|
13
|
+
def heap[T](cmp: (T,T) => Ordering at {}, capacity: Int) =
|
|
14
|
+
Heap[T](resizableArray(capacity), cmp)
|
|
15
|
+
|
|
16
|
+
namespace internal {
|
|
17
|
+
def left(idx: Int) = 2 * idx + 1
|
|
18
|
+
def right(idx: Int) = 2 * idx + 2
|
|
19
|
+
def parent(idx: Int) = (idx - 1) / 2
|
|
20
|
+
|
|
21
|
+
def bubbleUp[A](heap: Heap[A], idx: Int) = {
|
|
22
|
+
val arr = heap.rawContents
|
|
23
|
+
arr.boundsCheck(idx) // idx > parent(idx), parent(parent(idx)) etc
|
|
24
|
+
|
|
25
|
+
def go(idx: Int): Unit = {
|
|
26
|
+
if (idx > 0 and (heap.cmp)(arr.unsafeGet(parent(idx)), arr.unsafeGet(idx)) is Greater()) {
|
|
27
|
+
arr.unsafeSwap(parent(idx), idx)
|
|
28
|
+
go(parent(idx))
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
go(idx)
|
|
32
|
+
}
|
|
33
|
+
def sinkDown[A](heap: Heap[A], idx: Int) = {
|
|
34
|
+
val arr = heap.rawContents
|
|
35
|
+
|
|
36
|
+
def infixLt(x: A, y: A) = {
|
|
37
|
+
if ((heap.cmp)(x,y) is Less()) { true } else { false }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
def go(idx: Int): Unit = {
|
|
41
|
+
if (right(idx) < arr.size) {
|
|
42
|
+
val v = arr.unsafeGet(idx)
|
|
43
|
+
val l = arr.unsafeGet(left(idx))
|
|
44
|
+
val r = arr.unsafeGet(right(idx))
|
|
45
|
+
if (l < v && r < v) {
|
|
46
|
+
// swap with the smaller one
|
|
47
|
+
if (l < r) {
|
|
48
|
+
arr.unsafeSwap(left(idx), idx)
|
|
49
|
+
go(left(idx))
|
|
50
|
+
} else {
|
|
51
|
+
arr.unsafeSwap(right(idx), idx)
|
|
52
|
+
go(right(idx))
|
|
53
|
+
}
|
|
54
|
+
} else if (l < v) {
|
|
55
|
+
arr.unsafeSwap(left(idx), idx)
|
|
56
|
+
go(left(idx))
|
|
57
|
+
} else if (r < v) {
|
|
58
|
+
arr.unsafeSwap(right(idx), idx)
|
|
59
|
+
go(right(idx))
|
|
60
|
+
}
|
|
61
|
+
} else if (left(idx) < arr.size) {
|
|
62
|
+
if (arr.unsafeGet(left(idx)) < arr.unsafeGet(idx)) {
|
|
63
|
+
arr.unsafeSwap(left(idx), idx)
|
|
64
|
+
go(left(idx))
|
|
65
|
+
}
|
|
66
|
+
} // else: we are at the bottom
|
|
67
|
+
}
|
|
68
|
+
go(idx)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// Insert value into heap
|
|
73
|
+
///
|
|
74
|
+
/// O(log n) worst case if capacity suffices, O(1) average
|
|
75
|
+
def insert[T](heap: Heap[T], value: T): Unit = {
|
|
76
|
+
with on[OutOfBounds].panic();
|
|
77
|
+
val idx = heap.rawContents.add(value)
|
|
78
|
+
internal::bubbleUp(heap, idx)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// find and return (but not remove) the minimal element in this heap
|
|
82
|
+
///
|
|
83
|
+
/// O(1)
|
|
84
|
+
def findMin[T](heap: Heap[T]): T / Exception[OutOfBounds] = {
|
|
85
|
+
heap.rawContents.get(0)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// find and remove the minimal element in this heap
|
|
89
|
+
///
|
|
90
|
+
/// O(log n)
|
|
91
|
+
def deleteMin[T](heap: Heap[T]): T / Exception[OutOfBounds] = {
|
|
92
|
+
val res = heap.rawContents.get(0)
|
|
93
|
+
heap.rawContents.unsafeSet(0, heap.rawContents.popRight())
|
|
94
|
+
internal::sinkDown(heap, 0)
|
|
95
|
+
res
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// Number of elements in the heap
|
|
99
|
+
///
|
|
100
|
+
/// O(1)
|
|
101
|
+
def size[T](heap: Heap[T]): Int = {
|
|
102
|
+
heap.rawContents.size
|
|
103
|
+
}
|
|
@@ -6,35 +6,13 @@ import io
|
|
|
6
6
|
import io/error
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
/// Represents the file opening modes with their respective flags
|
|
10
|
-
/// Backends rely on the order of these
|
|
11
|
-
type Mode {
|
|
12
|
-
// Most common modes
|
|
13
|
-
ReadOnly() // 'r' exception if does not exist
|
|
14
|
-
WriteOnly() // 'w' created if does not exist, truncated if exists
|
|
15
|
-
AppendOnly() // 'a' created if does not exist
|
|
16
|
-
|
|
17
|
-
// Other modes
|
|
18
|
-
ReadWrite() // 'w+' created if does not exist, truncated if exists
|
|
19
|
-
ReadAppend() // 'a+' created if does not exist
|
|
20
|
-
AppendExclusive() // 'ax' created if does not exist, fails if exists
|
|
21
|
-
ReadAppendExclusive() // 'ax+' created if does not exist, fails if exists
|
|
22
|
-
AppendSync() // 'as' created if does not exist, append in synchronous mode
|
|
23
|
-
ReadAppendSync() // 'as+' created if does not exist, append in synchronous mode
|
|
24
|
-
ReadSync() // 'rs' exception if does not exist, read in synchronous mode
|
|
25
|
-
ReadWriteSync() // 'rs+' exception if does not exist, read/write in synchronous mode
|
|
26
|
-
WriteExclusive() // 'wx' created if does not exist, truncated if exists, fails if exists
|
|
27
|
-
ReadWriteExclusive() // 'wx+' created if does not exist, truncated if exists, fails if exists
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
9
|
/// A file descriptor. Should not be inspected.
|
|
32
10
|
type File = Int
|
|
33
11
|
|
|
34
12
|
|
|
35
13
|
/// Reads a file at given path as utf8 encoded string.
|
|
36
14
|
def readFile(path: String): String / Exception[IOError] = {
|
|
37
|
-
val file =
|
|
15
|
+
val file = openForReading(path);
|
|
38
16
|
with on[IOError].finalize { close(file) }
|
|
39
17
|
|
|
40
18
|
val chunkSize = 1048576 // 1MB
|
|
@@ -59,7 +37,7 @@ def readFile(path: String): String / Exception[IOError] = {
|
|
|
59
37
|
|
|
60
38
|
/// Writes the (utf8 encoded) string `contents` into the specified file.
|
|
61
39
|
def writeFile(path: String, contents: String): Unit / Exception[IOError] = {
|
|
62
|
-
val file =
|
|
40
|
+
val file = openForWriting(path);
|
|
63
41
|
with on[IOError].finalize { close(file) }
|
|
64
42
|
|
|
65
43
|
val chunkSize = 1048576 // 1MB
|
|
@@ -75,6 +53,23 @@ def writeFile(path: String, contents: String): Unit / Exception[IOError] = {
|
|
|
75
53
|
go()
|
|
76
54
|
}
|
|
77
55
|
|
|
56
|
+
/// Appends the (utf8 encoded) string `contents` to the specified file.
|
|
57
|
+
def appendFile(path: String, contents: String): Unit / Exception[IOError] = {
|
|
58
|
+
val file = openForAppending(path);
|
|
59
|
+
with on[IOError].finalize { close(file) }
|
|
60
|
+
|
|
61
|
+
val chunkSize = 1048576 // 1MB
|
|
62
|
+
val buffer = contents.fromString
|
|
63
|
+
var offset = 0;
|
|
64
|
+
|
|
65
|
+
def go(): Unit = {
|
|
66
|
+
val n = write(file, buffer, offset, min(buffer.size - offset, chunkSize), -1)
|
|
67
|
+
offset = offset + n
|
|
68
|
+
if (offset < buffer.size) { go() }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
go()
|
|
72
|
+
}
|
|
78
73
|
|
|
79
74
|
/// An abstract interface applications can program against.
|
|
80
75
|
///
|
|
@@ -95,8 +90,14 @@ def filesystem[R] { program: => R / Files }: R / Exception[IOError] = // TODO mo
|
|
|
95
90
|
}
|
|
96
91
|
|
|
97
92
|
|
|
98
|
-
def
|
|
99
|
-
internal::checkResult(internal::
|
|
93
|
+
def openForReading(path: String): File / Exception[IOError] =
|
|
94
|
+
internal::checkResult(internal::openForReading(path))
|
|
95
|
+
|
|
96
|
+
def openForWriting(path: String): File / Exception[IOError] =
|
|
97
|
+
internal::checkResult(internal::openForWriting(path))
|
|
98
|
+
|
|
99
|
+
def openForAppending(path: String): File / Exception[IOError] =
|
|
100
|
+
internal::checkResult(internal::openForAppending(path))
|
|
100
101
|
|
|
101
102
|
def read(file: File, buffer: ByteArray, offset: Int, size: Int, position: Int): Int / Exception[IOError] =
|
|
102
103
|
internal::checkResult(internal::read(file, buffer, offset, size, position))
|
|
@@ -111,40 +112,6 @@ def close(file: File): Unit / Exception[IOError] = {
|
|
|
111
112
|
namespace internal {
|
|
112
113
|
|
|
113
114
|
extern js """
|
|
114
|
-
function modeName(mode) {
|
|
115
|
-
switch (mode.__tag) {
|
|
116
|
-
case 0: // ReadOnly()
|
|
117
|
-
return 'r';
|
|
118
|
-
case 1: // WriteOnly()
|
|
119
|
-
return 'w';
|
|
120
|
-
case 2: // AppendOnly()
|
|
121
|
-
return 'a';
|
|
122
|
-
case 3: // ReadWrite()
|
|
123
|
-
return 'w+';
|
|
124
|
-
case 4: // ReadAppend()
|
|
125
|
-
return 'a+';
|
|
126
|
-
case 5: // AppendExclusive()
|
|
127
|
-
return 'ax';
|
|
128
|
-
case 6: // ReadAppendExclusive()
|
|
129
|
-
return 'ax+';
|
|
130
|
-
case 7: // AppendSync()
|
|
131
|
-
return 'as';
|
|
132
|
-
case 8: // ReadAppendSync()
|
|
133
|
-
return 'as+';
|
|
134
|
-
case 9: // ReadSync()
|
|
135
|
-
return 'rs';
|
|
136
|
-
case 10: // ReadWriteSync()
|
|
137
|
-
return 'rs+';
|
|
138
|
-
case 11: // WriteExclusive()
|
|
139
|
-
return 'wx';
|
|
140
|
-
case 12: // ReadWriteExclusive()
|
|
141
|
-
return 'wx+';
|
|
142
|
-
default:
|
|
143
|
-
// Invalid tag value
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
115
|
/**
|
|
149
116
|
* Nodejs file operations expect buffers, but we represent buffers as typed arrays.
|
|
150
117
|
* This function converts between the two without copying.
|
|
@@ -157,8 +124,8 @@ namespace internal {
|
|
|
157
124
|
extern jsNode """
|
|
158
125
|
const fs = require("fs");
|
|
159
126
|
|
|
160
|
-
function open(path,
|
|
161
|
-
fs.open(path,
|
|
127
|
+
function open(path, flags, callback) {
|
|
128
|
+
fs.open(path, flags, (err, file) => {
|
|
162
129
|
if (err) { callback(err.errno) } else { callback(file) }
|
|
163
130
|
})
|
|
164
131
|
}
|
|
@@ -185,16 +152,32 @@ namespace internal {
|
|
|
185
152
|
"""
|
|
186
153
|
|
|
187
154
|
extern llvm """
|
|
188
|
-
declare void @
|
|
155
|
+
declare void @c_fs_open_reading(%Pos, %Stack)
|
|
156
|
+
declare void @c_fs_open_writing(%Pos, %Stack)
|
|
157
|
+
declare void @c_fs_open_appending(%Pos, %Stack)
|
|
189
158
|
declare void @c_fs_read(%Int, %Pos, %Int, %Int, %Int, %Stack)
|
|
190
159
|
declare void @c_fs_write(%Int, %Pos, %Int, %Int, %Int, %Stack)
|
|
191
160
|
declare void @c_fs_close(%Int, %Stack)
|
|
192
161
|
"""
|
|
193
162
|
|
|
194
|
-
extern async def
|
|
195
|
-
jsNode "$effekt.capture(callback => open(${path},
|
|
163
|
+
extern async def openForReading(path: String): Int =
|
|
164
|
+
jsNode "$effekt.capture(callback => open(${path}, 'r', callback))"
|
|
165
|
+
llvm """
|
|
166
|
+
call void @c_fs_open_reading(%Pos ${path}, %Stack %stack)
|
|
167
|
+
ret void
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
extern async def openForWriting(path: String): Int =
|
|
171
|
+
jsNode "$effekt.capture(callback => open(${path}, 'w', callback))"
|
|
172
|
+
llvm """
|
|
173
|
+
call void @c_fs_open_writing(%Pos ${path}, %Stack %stack)
|
|
174
|
+
ret void
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
extern async def openForAppending(path: String): Int =
|
|
178
|
+
jsNode "$effekt.capture(callback => open(${path}, 'a', callback))"
|
|
196
179
|
llvm """
|
|
197
|
-
call void @
|
|
180
|
+
call void @c_fs_open_appending(%Pos ${path}, %Stack %stack)
|
|
198
181
|
ret void
|
|
199
182
|
"""
|
|
200
183
|
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
module resizable_array
|
|
2
|
+
|
|
3
|
+
import ref
|
|
4
|
+
import array
|
|
5
|
+
|
|
6
|
+
record ResizableArray[T](rawSizePtr: Ref[Int], rawContentPtr: Ref[Array[T]])
|
|
7
|
+
|
|
8
|
+
// These numbers should be optimized based on benchmarking
|
|
9
|
+
// According to https://en.wikipedia.org/wiki/Dynamic_array most use 1.5 or 2
|
|
10
|
+
|
|
11
|
+
/// Factor by which to grow the capacity when it becomes too small
|
|
12
|
+
val growFactor = 1.5
|
|
13
|
+
/// shrink array when size / capacity falls below this threshold
|
|
14
|
+
/// should be < 1/growFactor
|
|
15
|
+
val shrinkThreshold = 0.4
|
|
16
|
+
|
|
17
|
+
/// Number of elements in the dynamic array
|
|
18
|
+
///
|
|
19
|
+
/// O(1)
|
|
20
|
+
def size[T](arr: ResizableArray[T]) = arr.rawSizePtr.get
|
|
21
|
+
|
|
22
|
+
/// Allocate a new, empty dynamic array with given initial capacity
|
|
23
|
+
def resizableArray[T](capacity: Int): ResizableArray[T] = {
|
|
24
|
+
ResizableArray(ref(0), ref(allocate(capacity)))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Allocate a new, empty dynamic array
|
|
28
|
+
def resizableArray[T](): ResizableArray[T] = resizableArray(8)
|
|
29
|
+
|
|
30
|
+
/// Throw an OutOfBounds exception if index is not a valid index into arr
|
|
31
|
+
def boundsCheck[T](arr: ResizableArray[T], index: Int): Unit / Exception[OutOfBounds] = {
|
|
32
|
+
if (index < 0 && index >= arr.size) {
|
|
33
|
+
do raise(OutOfBounds(), "Array index out of bounds: " ++ show(index))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// get the element at position index in the array
|
|
38
|
+
///
|
|
39
|
+
/// precondition: index is a valid index into the array
|
|
40
|
+
///
|
|
41
|
+
/// O(1)
|
|
42
|
+
def unsafeGet[T](arr: ResizableArray[T], index: Int): T = {
|
|
43
|
+
arr.rawContentPtr.get.unsafeGet(index)
|
|
44
|
+
}
|
|
45
|
+
/// get the element at position index in the array
|
|
46
|
+
///
|
|
47
|
+
/// O(1)
|
|
48
|
+
def get[T](arr: ResizableArray[T], index: Int): T / Exception[OutOfBounds] = {
|
|
49
|
+
arr.boundsCheck(index);
|
|
50
|
+
arr.unsafeGet(index)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// set the element at position index in the array
|
|
54
|
+
///
|
|
55
|
+
/// precondition: index is a valid index into the array
|
|
56
|
+
///
|
|
57
|
+
/// O(1)
|
|
58
|
+
def unsafeSet[T](arr: ResizableArray[T], index: Int, value: T): Unit = {
|
|
59
|
+
arr.rawContentPtr.get.unsafeSet(index, value)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// set the element at position index in the array
|
|
63
|
+
///
|
|
64
|
+
/// O(1)
|
|
65
|
+
def set[T](arr: ResizableArray[T], index: Int, value: T): Unit / Exception[OutOfBounds] = {
|
|
66
|
+
arr.boundsCheck(index);
|
|
67
|
+
arr.unsafeSet(index, value)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// swap the elements at the given positions in the array
|
|
71
|
+
///
|
|
72
|
+
/// precondition: both are valid indices into the array
|
|
73
|
+
///
|
|
74
|
+
/// O(1)
|
|
75
|
+
def unsafeSwap[T](arr: ResizableArray[T], index1: Int, index2: Int): Unit = {
|
|
76
|
+
val raw = arr.rawContentPtr.get
|
|
77
|
+
val tmp = raw.unsafeGet(index1)
|
|
78
|
+
raw.unsafeSet(index1, raw.unsafeGet(index2))
|
|
79
|
+
raw.unsafeSet(index2, tmp)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// swap the elements at the given positions in the array
|
|
83
|
+
///
|
|
84
|
+
/// O(1)
|
|
85
|
+
def swap[T](arr: ResizableArray[T], index1: Int, index2: Int): Unit / Exception[OutOfBounds] = {
|
|
86
|
+
arr.boundsCheck(max(index1, index2))
|
|
87
|
+
arr.unsafeSwap(index1, index2)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// Change the dynamic to have exactly the given capacity
|
|
91
|
+
///
|
|
92
|
+
/// precondition: given capacity is at least the size of the array
|
|
93
|
+
///
|
|
94
|
+
/// O(n)
|
|
95
|
+
def unsafeSetCapacity[T](arr: ResizableArray[T], capacity: Int): Unit = {
|
|
96
|
+
with on[OutOfBounds].panic
|
|
97
|
+
val oldRaw = arr.rawContentPtr.get
|
|
98
|
+
if (oldRaw.size != capacity) {
|
|
99
|
+
val newRaw = array::allocate(capacity)
|
|
100
|
+
oldRaw.copy(0, newRaw, 0, arr.size)
|
|
101
|
+
arr.rawContentPtr.set(newRaw)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Change the resizable array to have exactly the given capacity.
|
|
106
|
+
/// This only changes the size of the backing array, not the `size`.
|
|
107
|
+
///
|
|
108
|
+
/// O(n)
|
|
109
|
+
def setCapacity[T](arr: ResizableArray[T], capacity: Int): Unit / Exception[OutOfBounds] = {
|
|
110
|
+
if (arr.size > capacity) {
|
|
111
|
+
do raise(OutOfBounds(), "Cannot change capacity of ResizableArray to " ++ capacity.show ++ " below size " ++ arr.size.show)
|
|
112
|
+
}
|
|
113
|
+
arr.unsafeSetCapacity(capacity)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// If the shrinkThreshold is reached, shrink by growFactor, otherwise do nothing
|
|
117
|
+
///
|
|
118
|
+
/// O(n)
|
|
119
|
+
def maybeShrink[T](arr: ResizableArray[T]): Unit = {
|
|
120
|
+
if(arr.size.toDouble < arr.rawContentPtr.get.size.toDouble * shrinkThreshold) {
|
|
121
|
+
val newCap = max(arr.size, (arr.rawContentPtr.get.size.toDouble / growFactor).ceil)
|
|
122
|
+
arr.unsafeSetCapacity(newCap)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/// makes sure capacity is at least the given one
|
|
127
|
+
///
|
|
128
|
+
/// O(given capacity - current capacity) amortized, O(n) worst case // TODO ?
|
|
129
|
+
def ensureCapacity[T](arr: ResizableArray[T], capacity: Int): Unit / Exception[OutOfBounds] = {
|
|
130
|
+
if (arr.rawContentPtr.get.size < capacity) {
|
|
131
|
+
val curCapd: Double = arr.rawContentPtr.get.size.toDouble
|
|
132
|
+
val minGrowCapacity: Int = (curCapd * growFactor + 1.0).toInt
|
|
133
|
+
val newCapacity = max(capacity, minGrowCapacity)
|
|
134
|
+
arr.setCapacity(newCapacity)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/// Set the value at given position, resizing if necessary
|
|
139
|
+
/// Note: New elements might be uninitialized!!!
|
|
140
|
+
///
|
|
141
|
+
/// O(max(1,index - n)) amortized, O(n) worst case if index > capacity
|
|
142
|
+
def setResizing[T](arr: ResizableArray[T], index: Int, value: T): Unit / Exception[OutOfBounds] = {
|
|
143
|
+
if (index < 0) {
|
|
144
|
+
do raise(OutOfBounds(), "Negative index " ++ index.show)
|
|
145
|
+
}
|
|
146
|
+
if (index < arr.size) {
|
|
147
|
+
arr.rawContentPtr.get.unsafeSet(index, value)
|
|
148
|
+
}
|
|
149
|
+
ensureCapacity(arr, index + 1)
|
|
150
|
+
arr.rawContentPtr.get.unsafeSet(index, value)
|
|
151
|
+
arr.rawSizePtr.set(index + 1)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/// Add a new element at the end of the resizable array.
|
|
155
|
+
/// Return the index of the new element
|
|
156
|
+
///
|
|
157
|
+
/// O(1) amortized, O(n) worst case
|
|
158
|
+
def add[T](arr: ResizableArray[T], value: T): Int = {
|
|
159
|
+
with on[OutOfBounds].panic();
|
|
160
|
+
val idx = arr.size
|
|
161
|
+
arr.setResizing(idx, value)
|
|
162
|
+
idx
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/// Remove and return the rightmost element in the resizable array.
|
|
166
|
+
///
|
|
167
|
+
/// O(1) amortized, O(n) worst case
|
|
168
|
+
def popRight[T](arr: ResizableArray[T]): T / Exception[OutOfBounds] = {
|
|
169
|
+
arr.boundsCheck(arr.size - 1)
|
|
170
|
+
arr.rawSizePtr.set(arr.size - 1)
|
|
171
|
+
val r = arr.unsafeGet(arr.size)
|
|
172
|
+
arr.maybeShrink()
|
|
173
|
+
r
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def foreachIndex[T](arr: ResizableArray[T]){ body: (Int, T) => Unit }: Unit = {
|
|
178
|
+
each(0, arr.size) { i =>
|
|
179
|
+
body(i, arr.rawContentPtr.get.unsafeGet(i))
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
def foreachIndex[T](arr: ResizableArray[T]){ body: (Int, T) {Control} => Unit }: Unit = {
|
|
183
|
+
each(0, arr.size) { (i){label} =>
|
|
184
|
+
body(i, arr.rawContentPtr.get.unsafeGet(i)){label}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
def foreach[T](arr: ResizableArray[T]){ body: T => Unit }: Unit = {
|
|
188
|
+
each(0, arr.size) { i =>
|
|
189
|
+
body(arr.rawContentPtr.get.unsafeGet(i))
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
def foreach[T](arr: ResizableArray[T]){ body: (T) {Control} => Unit }: Unit = {
|
|
193
|
+
each(0, arr.size) { (i){label} =>
|
|
194
|
+
body(arr.rawContentPtr.get.unsafeGet(i)){label}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
def foreachIndexReversed[T](arr: ResizableArray[T]){ body: (Int, T){Control} => Unit }: Unit = {
|
|
198
|
+
var i = arr.size - 1
|
|
199
|
+
loop { {l} =>
|
|
200
|
+
if (i < 0) { l.break() }
|
|
201
|
+
body(i, arr.rawContentPtr.get.unsafeGet(i)){l}
|
|
202
|
+
i = i - 1
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
def foreachIndexReversed[T](arr: ResizableArray[T]){ body: (Int, T) => Unit }: Unit = {
|
|
206
|
+
var i = arr.size - 1
|
|
207
|
+
while(i >= 0) {
|
|
208
|
+
body(i, arr.rawContentPtr.get.unsafeGet(i))
|
|
209
|
+
i = i - 1
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
def foreachReversed[T](arr: ResizableArray[T]){ body: (T){Control} => Unit }: Unit = {
|
|
213
|
+
var i = arr.size - 1
|
|
214
|
+
loop { {l} =>
|
|
215
|
+
if (i < 0) { l.break() }
|
|
216
|
+
body(arr.rawContentPtr.get.unsafeGet(i)){l}
|
|
217
|
+
i = i - 1
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
def foreachReversed[T](arr: ResizableArray[T]){ body: T => Unit }: Unit = {
|
|
221
|
+
var i = arr.size - 1
|
|
222
|
+
while (i >= 0) {
|
|
223
|
+
body(arr.rawContentPtr.get.unsafeGet(i))
|
|
224
|
+
i = i - 1
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
def toList[T](arr: ResizableArray[T]): List[T] = {
|
|
229
|
+
def go(i: Int, acc: List[T]): List[T] = {
|
|
230
|
+
if (i < 0) { acc } else {
|
|
231
|
+
go(i - 1, Cons(arr.unsafeGet(i), acc))
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
go(arr.size - 1, Nil())
|
|
235
|
+
}
|
|
@@ -263,7 +263,7 @@ def zip[A, B] { stream1: () => Unit / emit[A] } { stream2: () => Unit / emit[B]
|
|
|
263
263
|
|
|
264
264
|
def writeFile[R](path: String) { stream: () => R / emit[Byte] }: R / Exception[IOError] = {
|
|
265
265
|
|
|
266
|
-
val file =
|
|
266
|
+
val file = openForWriting(path);
|
|
267
267
|
with on[IOError].finalize { close(file) }
|
|
268
268
|
|
|
269
269
|
val chunkSize = 1048576 // 1MB
|
|
@@ -291,7 +291,7 @@ def writeFile[R](path: String) { stream: () => R / emit[Byte] }: R / Exception[I
|
|
|
291
291
|
|
|
292
292
|
def readFile[R](path: String) { reader: () => R / read[Byte] }: R / Exception[IOError] = {
|
|
293
293
|
|
|
294
|
-
val file =
|
|
294
|
+
val file = openForReading(path);
|
|
295
295
|
with on[IOError].finalize { close(file) }
|
|
296
296
|
|
|
297
297
|
val chunkSize = 1048576 // 1MB
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module stringbuffer
|
|
2
|
+
|
|
3
|
+
import bytearray
|
|
4
|
+
|
|
5
|
+
interface StringBuffer {
|
|
6
|
+
def write(str: String): Unit
|
|
7
|
+
def flush(): String
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
def stringBuffer[A] { prog: => A / StringBuffer }: A = {
|
|
11
|
+
val initialCapacity = 128
|
|
12
|
+
var buffer = bytearray::allocate(initialCapacity)
|
|
13
|
+
// next free index to write to
|
|
14
|
+
var pos = 0
|
|
15
|
+
|
|
16
|
+
def ensureCapacity(sizeToAdd: Int): Unit = {
|
|
17
|
+
val cap = buffer.size - pos + 1
|
|
18
|
+
if (sizeToAdd <= cap) ()
|
|
19
|
+
else {
|
|
20
|
+
// Double the capacity while ensuring the required capacity
|
|
21
|
+
val newSize = max(buffer.size * 2, buffer.size + sizeToAdd)
|
|
22
|
+
buffer = buffer.resize(newSize)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try { prog() }
|
|
27
|
+
with StringBuffer {
|
|
28
|
+
def write(str) = {
|
|
29
|
+
val bytes = fromString(str)
|
|
30
|
+
ensureCapacity(bytes.size)
|
|
31
|
+
bytes.foreach { b =>
|
|
32
|
+
buffer.unsafeSet(pos, b)
|
|
33
|
+
pos = pos + 1
|
|
34
|
+
}
|
|
35
|
+
resume(())
|
|
36
|
+
}
|
|
37
|
+
def flush() = {
|
|
38
|
+
// resize buffer to strip trailing zeros that otherwise would be converted into 0x00 characters
|
|
39
|
+
val str = bytearray::resize(buffer, pos).toString()
|
|
40
|
+
// after flushing, the stringbuffer should be empty again
|
|
41
|
+
buffer = bytearray::allocate(initialCapacity)
|
|
42
|
+
resume(str)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Handler for string interpolation using a string buffer
|
|
48
|
+
def s { prog: () => Unit / { literal, splice[String] } }: String =
|
|
49
|
+
stringBuffer {
|
|
50
|
+
try { prog(); do flush() }
|
|
51
|
+
with splice[String] { x => resume(do write(x)) }
|
|
52
|
+
with literal { s => resume(do write(s)) }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
namespace examples {
|
|
56
|
+
def main() = {
|
|
57
|
+
with stringBuffer
|
|
58
|
+
do write("hello")
|
|
59
|
+
do write(", world")
|
|
60
|
+
// prints `hello, world`
|
|
61
|
+
println(do flush())
|
|
62
|
+
// prints the empty string
|
|
63
|
+
println(do flush())
|
|
64
|
+
}
|
|
65
|
+
}
|