@effekt-lang/effekt 0.18.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.
@@ -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
+ }
@@ -0,0 +1,113 @@
1
+ module union_find
2
+ import resizable_array
3
+
4
+ /// Classic mutable union-find data structure on integer indices,
5
+ /// with union-by-rank and path compression.
6
+ record UnionFind(rawElements: ResizableArray[Int])
7
+
8
+ namespace internal {
9
+ // -(rk + 1) for roots
10
+ // parent for inner nodes
11
+ def isRoot(u: UnionFind, s: Int): Bool / Exception[MissingValue] = {
12
+ with on[OutOfBounds].default { do raise(MissingValue(), "No node " ++ s.show) }
13
+ u.rawElements.get(s) < 0
14
+ }
15
+ def parent(u: UnionFind, s: Int): Int / Exception[MissingValue] = {
16
+ with on[OutOfBounds].default { do raise(MissingValue(), "No node " ++ s.show) }
17
+ val c = u.rawElements.get(s)
18
+ if (c < 0) {
19
+ do raise(MissingValue(), "Root node " ++ s.show ++ " has no parent.")
20
+ } else {
21
+ c
22
+ }
23
+ }
24
+ def rank(u: UnionFind, s: Int): Int / Exception[MissingValue] = {
25
+ with on[OutOfBounds].default { do raise(MissingValue(), "No node " ++ s.show) }
26
+ val c = u.rawElements.get(s)
27
+ if (c < 0) {
28
+ -1 * c - 1
29
+ } else {
30
+ do raise(MissingValue(), "Only root nodes have a rank.")
31
+ }
32
+ }
33
+ def setParent(u: UnionFind, child: Int, parent: Int) = {
34
+ with on[OutOfBounds].default { do raise(MissingValue(), "No node " ++ child.show) }
35
+ u.rawElements.set(child, parent)
36
+ }
37
+ def setRank(u: UnionFind, root: Int, rank: Int) = {
38
+ with on[OutOfBounds].default { do raise(MissingValue(), "No node " ++ root.show) }
39
+ u.rawElements.set(root, -1 * rank - 1)
40
+ }
41
+ }
42
+
43
+ def unionFind(): UnionFind =
44
+ UnionFind(resizableArray())
45
+
46
+ def unionFind(capacity: Int): UnionFind =
47
+ UnionFind(resizableArray(capacity))
48
+
49
+ /// Make a new set in the union-find datastructure and return its index
50
+ ///
51
+ /// O(1) amortized, worst-case O(n) if the capacity does not suffice
52
+ def makeSet(u: UnionFind): Int = {
53
+ u.rawElements.add(-1)
54
+ }
55
+
56
+ /// Find the representative of the given element.
57
+ ///
58
+ /// O(inverse-ackermann(n)) amortized
59
+ def find(u: UnionFind, s: Int): Int / Exception[MissingValue] = {
60
+ def goFind(s: Int): Int = {
61
+ if (u.internal::isRoot(s)) {
62
+ s
63
+ } else {
64
+ goFind(u.internal::parent(s))
65
+ }
66
+ }
67
+ val root = goFind(s)
68
+
69
+ def goCompress(s: Int): Unit = {
70
+ if (not(u.internal::isRoot(s))) {
71
+ val p = u.internal::parent(s)
72
+ u.internal::setParent(s, root)
73
+ goCompress(p)
74
+ }
75
+ }
76
+ goCompress(s)
77
+
78
+ root
79
+ }
80
+
81
+ /// Make it such that the sets *represented by* x and y are merged
82
+ ///
83
+ /// O(1)
84
+ def unionRoots(u: UnionFind, x: Int, y: Int): Int / Exception[MissingValue] = {
85
+ if(not(u.internal::isRoot(x) && u.internal::isRoot(y))) {
86
+ panic("unionRoots should only be called on representatives")
87
+ }
88
+
89
+ def addChild(root: Int, child: Int) = {
90
+ if (u.internal::rank(root) == u.internal::rank(child)) {
91
+ u.internal::setRank(root, u.internal::rank(root) + 1)
92
+ }
93
+ u.internal::setParent(child, root)
94
+ root
95
+ }
96
+
97
+ if (x == y) {
98
+ x
99
+ } else if (u.internal::rank(x) < u.internal::rank(y)) {
100
+ addChild(y, x)
101
+ } else {
102
+ addChild(x, y)
103
+ }
104
+ }
105
+
106
+ /// Make it so a and b are in the same set
107
+ ///
108
+ /// O(inverse-ackermann(n)) amortized
109
+ def union(u: UnionFind, a: Int, b: Int): Int / Exception[MissingValue] = {
110
+ val x = u.find(a)
111
+ val y = u.find(b)
112
+ u.unionRoots(x, y)
113
+ }
@@ -152,16 +152,45 @@ struct Pos c_bytearray_equal(const struct Pos left, const struct Pos right) {
152
152
  erasePositive(right);
153
153
  return BooleanFalse;
154
154
  }
155
- for (uint64_t j = 0; j < left_size; ++j) {
156
- if (c_bytearray_data(left)[j] != c_bytearray_data(right)[j]) {
157
- erasePositive(left);
158
- erasePositive(right);
159
- return BooleanFalse;
160
- }
155
+
156
+ int cmp = memcmp(c_bytearray_data(left), c_bytearray_data(right), left_size);
157
+ erasePositive(left);
158
+ erasePositive(right);
159
+ return (cmp == 0 ? BooleanTrue : BooleanFalse);
160
+ }
161
+
162
+ Int c_bytearray_compare(const struct Pos left, const struct Pos right) {
163
+ uint64_t left_size = left.tag;
164
+ uint64_t right_size = right.tag;
165
+ uint64_t min_size = (left_size < right_size) ? left_size : right_size;
166
+
167
+ uint8_t* left_data = c_bytearray_data(left);
168
+ uint8_t* right_data = c_bytearray_data(right);
169
+
170
+ if (left_data == right_data) {
171
+ erasePositive(left);
172
+ erasePositive(right);
173
+ return 0;
161
174
  }
175
+
176
+ int cmp = memcmp(left_data, right_data, min_size);
177
+
162
178
  erasePositive(left);
163
179
  erasePositive(right);
164
- return BooleanTrue;
180
+
181
+ if (cmp < 0) {
182
+ return -1;
183
+ } else if (cmp > 0) {
184
+ return 1;
185
+ }
186
+
187
+ if (left_size < right_size) {
188
+ return -1;
189
+ } else if (left_size > right_size) {
190
+ return 1;
191
+ }
192
+
193
+ return 0;
165
194
  }
166
195
 
167
196
  // TODO deprecate
@@ -9,6 +9,7 @@ declare void @c_io_println_Double(%Double)
9
9
  declare void @c_io_println_String(%Pos)
10
10
 
11
11
  declare void @hole()
12
+ declare void @duplicated_prompt()
12
13
 
13
14
  declare %Pos @c_ref_fresh(%Pos)
14
15
  declare %Pos @c_ref_get(%Pos)
@@ -37,6 +38,7 @@ declare %Pos @c_bytearray_show_Double(double)
37
38
 
38
39
  declare %Pos @c_bytearray_concatenate(%Pos, %Pos)
39
40
  declare %Pos @c_bytearray_equal(%Pos, %Pos)
41
+ declare %Int @c_bytearray_compare(%Pos, %Pos)
40
42
 
41
43
  declare %Pos @c_bytearray_substring(%Pos, i64, i64)
42
44
  declare %Int @c_bytearray_character_at(%Pos, i64)
@@ -62,50 +62,12 @@ void c_resume_int_fs(uv_fs_t* request) {
62
62
  resume_Int(stack, result);
63
63
  }
64
64
 
65
- int modeFlags(struct Pos mode) {
66
- switch (mode.tag) {
67
- case 0: // ReadOnly()
68
- return UV_FS_O_RDONLY;
69
- case 1: // WriteOnly()
70
- return UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_TRUNC;
71
- case 2: // AppendOnly()
72
- return UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_APPEND;
73
- case 3: // ReadWrite()
74
- return UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_TRUNC;
75
- case 4: // ReadAppend()
76
- return UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_APPEND;
77
- case 5: // AppendExclusive()
78
- return UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_APPEND | UV_FS_O_EXCL;
79
- case 6: // ReadAppendExclusive()
80
- return UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_APPEND | UV_FS_O_EXCL;
81
- case 7: // AppendSync()
82
- return UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_APPEND | UV_FS_O_SYNC;
83
- case 8: // ReadAppendSync()
84
- return UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_APPEND | UV_FS_O_SYNC;
85
- case 9: // ReadSync()
86
- return UV_FS_O_RDONLY | UV_FS_O_SYNC;
87
- case 10: // ReadWriteSync()
88
- return UV_FS_O_RDWR | UV_FS_O_SYNC;
89
- case 11: // WriteExclusive()
90
- return UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_TRUNC | UV_FS_O_EXCL;
91
- case 12: // ReadWriteExclusive()
92
- return UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_TRUNC | UV_FS_O_EXCL;
93
- default:
94
- // Invalid tag value
95
- return -1;
96
- }
97
- }
98
-
99
- void c_fs_open(String path, struct Pos mode, Stack stack) {
65
+ void c_fs_open(String path, int flags, Stack stack) {
100
66
 
101
67
  // Convert the Effekt String to a 0-terminated string
102
68
  char* path_str = c_bytearray_into_nullterminated_string(path);
103
69
  erasePositive((struct Pos) path);
104
70
 
105
- // Convert the Effekt String representing the opening mode to libuv flags
106
- int flags = modeFlags(mode);
107
- erasePositive((struct Pos) mode);
108
-
109
71
  uv_fs_t* request = malloc(sizeof(uv_fs_t));
110
72
  request->data = stack;
111
73
 
@@ -123,6 +85,18 @@ void c_fs_open(String path, struct Pos mode, Stack stack) {
123
85
  return;
124
86
  }
125
87
 
88
+ void c_fs_open_reading(String path, Stack stack) {
89
+ c_fs_open(path, UV_FS_O_RDONLY, stack);
90
+ }
91
+
92
+ void c_fs_open_writing(String path, Stack stack) {
93
+ c_fs_open(path, UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_TRUNC, stack);
94
+ }
95
+
96
+ void c_fs_open_appending(String path, Stack stack) {
97
+ c_fs_open(path, UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_APPEND, stack);
98
+ }
99
+
126
100
  void c_fs_read(Int file, struct Pos buffer, Int offset, Int size, Int position, Stack stack) {
127
101
 
128
102
  uv_buf_t buf = uv_buf_init((char*)(c_bytearray_data(buffer) + offset), size);
@@ -177,11 +151,11 @@ void c_fs_close(Int file, Stack stack) {
177
151
  /**
178
152
  * Maps the libuv error code to a stable (platform independent) numeric value.
179
153
  *
180
- * Tries to use most common errno integer values, but introduces fresh values (> 200)
181
- * for those without common errno values.
154
+ * Tries to use most common error number integer values, but introduces fresh values (> 200)
155
+ * for those without common error number values.
182
156
  */
183
- Int c_error_number(Int errno) {
184
- switch (errno) {
157
+ Int c_error_number(Int error) {
158
+ switch (error) {
185
159
  case UV_EPERM: return 1; // EPERM
186
160
  case UV_ENOENT: return 2; // ENOENT
187
161
  case UV_ESRCH: return 3; // ESRCH
@@ -12,7 +12,7 @@
12
12
  #include "types.c"
13
13
  #include "bytearray.c"
14
14
  #include "io.c"
15
- #include "hole.c"
15
+ #include "panic.c"
16
16
  #include "ref.c"
17
17
  #include "array.c"
18
18
 
@@ -40,4 +40,5 @@ int main(int argc, char *argv[]) {
40
40
  uv_loop_t *loop = uv_default_loop();
41
41
  uv_run(loop, UV_RUN_DEFAULT);
42
42
  uv_loop_close(loop);
43
+ return 0;
43
44
  }
@@ -0,0 +1,20 @@
1
+ #ifndef EFFEKT_PANIC_C
2
+ #define EFFEKT_PANIC_C
3
+
4
+ #include <stdio.h>
5
+
6
+ // TODO:
7
+ // this should _morally_ be using `stderr`, but we don't tee it in tests
8
+ // see PR #823 & issue #815 for context
9
+
10
+ void hole() {
11
+ printf("PANIC: Reached a hole in the program\n");
12
+ exit(1);
13
+ }
14
+
15
+ void duplicated_prompt() {
16
+ printf("PANIC: Continuation invoked itself\n");
17
+ exit(1);
18
+ }
19
+
20
+ #endif