@effekt-lang/effekt 0.2.2 → 0.4.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/README.md +6 -18
- package/bin/effekt +0 -0
- package/bin/effekt.sh +1 -1
- package/libraries/chez/common/effekt_primitives.ss +10 -2
- package/libraries/chez/common/immutable/cslist.effekt +13 -7
- package/libraries/chez/common/mutable/dict.effekt +8 -10
- package/libraries/chez/common/text/regex.effekt +7 -9
- package/libraries/common/args.effekt +68 -0
- package/libraries/common/array.effekt +220 -0
- package/libraries/common/bench.effekt +86 -0
- package/libraries/common/buffer.effekt +123 -0
- package/libraries/common/bytes.effekt +125 -0
- package/libraries/{js/immutable → common}/dequeue.effekt +18 -4
- package/libraries/common/effekt.effekt +725 -0
- package/libraries/common/exception.effekt +121 -0
- package/libraries/common/io/console.effekt +62 -0
- package/libraries/common/io/error.effekt +474 -0
- package/libraries/common/io/filesystem.effekt +274 -0
- package/libraries/common/io/network.effekt +81 -0
- package/libraries/common/io/time.effekt +12 -0
- package/libraries/common/io.effekt +88 -0
- package/libraries/{llvm/immutable → common}/list.effekt +101 -74
- package/libraries/common/option.effekt +72 -0
- package/libraries/common/process.effekt +9 -0
- package/libraries/common/queue.effekt +140 -0
- package/libraries/common/ref.effekt +60 -0
- package/libraries/common/result.effekt +45 -0
- package/libraries/{ml/immutable → common}/seq.effekt +30 -19
- package/libraries/common/string/tty.effekt +66 -0
- package/libraries/common/string.effekt +361 -0
- package/libraries/common/test.effekt +160 -0
- package/libraries/js/effekt_builtins.js +37 -6
- package/libraries/js/mutable/map.effekt +8 -9
- package/libraries/js/text/regex.effekt +7 -8
- package/libraries/js/unsafe/cont.effekt +2 -2
- package/libraries/js/web/dom.effekt +13 -13
- package/libraries/llvm/array.c +53 -0
- package/libraries/llvm/buffer.c +181 -76
- package/libraries/llvm/forward-declare-c.ll +27 -8
- package/libraries/llvm/io.c +423 -1
- package/libraries/llvm/main.c +6 -1
- package/libraries/llvm/ref.c +45 -0
- package/libraries/llvm/rts.ll +515 -354
- package/libraries/llvm/types.c +32 -0
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +4 -4
- package/licenses/mit license - mit-license.html +532 -62
- package/licenses/the bsd license - bsd-license.html +535 -60
- package/licenses/the mit license - mit.html +532 -62
- package/package.json +2 -2
- package/libraries/chez/callcc/effekt.effekt +0 -221
- package/libraries/chez/common/effekt_matching.ss +0 -100
- package/libraries/chez/common/immutable/dequeue.effekt +0 -78
- package/libraries/chez/common/immutable/list.effekt +0 -643
- package/libraries/chez/common/immutable/option.effekt +0 -39
- package/libraries/chez/common/immutable/result.effekt +0 -29
- package/libraries/chez/common/io/args.effekt +0 -20
- package/libraries/chez/common/mutable/array.effekt +0 -27
- package/libraries/chez/common/mutable/heap.effekt +0 -12
- package/libraries/chez/common/test.effekt +0 -59
- package/libraries/chez/common/text/string.effekt +0 -46
- package/libraries/chez/lift/effekt.effekt +0 -217
- package/libraries/chez/lift/effekt.ss +0 -118
- package/libraries/chez/monadic/effekt.effekt +0 -218
- package/libraries/js/effekt.effekt +0 -284
- package/libraries/js/effekt_matching.js +0 -79
- package/libraries/js/immutable/list.effekt +0 -643
- package/libraries/js/immutable/option.effekt +0 -49
- package/libraries/js/immutable/result.effekt +0 -29
- package/libraries/js/io/args.effekt +0 -9
- package/libraries/js/io/async.effekt +0 -28
- package/libraries/js/io/async.js +0 -2
- package/libraries/js/io/file.effekt +0 -50
- package/libraries/js/io/file_include.js +0 -13
- package/libraries/js/mutable/array.effekt +0 -98
- package/libraries/js/mutable/heap.effekt +0 -32
- package/libraries/js/test.effekt +0 -59
- package/libraries/js/text/string.effekt +0 -64
- package/libraries/llvm/effekt.effekt +0 -243
- package/libraries/llvm/immutable/option.effekt +0 -53
- package/libraries/llvm/immutable/result.effekt +0 -29
- package/libraries/llvm/io/args.effekt +0 -24
- package/libraries/llvm/text/string.effekt +0 -179
- package/libraries/ml/effekt.effekt +0 -267
- package/libraries/ml/effekt.sml +0 -63
- package/libraries/ml/immutable/dequeue.effekt +0 -81
- package/libraries/ml/immutable/list.effekt +0 -674
- package/libraries/ml/immutable/option.effekt +0 -53
- package/libraries/ml/immutable/result.effekt +0 -29
- package/libraries/ml/internal/option.effekt +0 -18
- package/libraries/ml/io/args.effekt +0 -41
- package/libraries/ml/mutable/array.effekt +0 -43
- package/libraries/ml/random.effekt +0 -85
- package/libraries/ml/test.effekt +0 -60
- package/libraries/ml/text/regex.effekt +0 -107
- package/libraries/ml/text/string.effekt +0 -81
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
function
|
|
1
|
+
$effekt.show = function(obj) {
|
|
2
2
|
if (!!obj && !!obj.__reflect) {
|
|
3
3
|
const meta = obj.__reflect()
|
|
4
|
-
return meta.__name + "(" + meta.__data.map(show
|
|
4
|
+
return meta.__name + "(" + meta.__data.map($effekt.show).join(", ") + ")"
|
|
5
5
|
}
|
|
6
6
|
else if (!!obj && obj.__unit) {
|
|
7
7
|
return "()";
|
|
@@ -10,7 +10,7 @@ function show$impl(obj) {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
function
|
|
13
|
+
$effekt.equals = function(obj1, obj2) {
|
|
14
14
|
if (!!obj1.__equals) {
|
|
15
15
|
return obj1.__equals(obj2)
|
|
16
16
|
} else {
|
|
@@ -18,9 +18,40 @@ function equals$impl(obj1, obj2) {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
function compare$prim(n1, n2) {
|
|
22
|
+
if (n1 == n2) { return 0; }
|
|
23
|
+
else if (n1 > n2) { return 1; }
|
|
24
|
+
else { return -1; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
$effekt.compare = function(obj1, obj2) {
|
|
28
|
+
if ($effekt.equals(obj1, obj2)) { return 0; }
|
|
29
|
+
|
|
30
|
+
if (!!obj1 && !!obj2) {
|
|
31
|
+
if (!!obj1.__reflect && !!obj2.__reflect) {
|
|
32
|
+
const tagOrdering = compare$prim(obj1.__tag, obj2.__tag)
|
|
33
|
+
if (tagOrdering != 0) { return tagOrdering; }
|
|
34
|
+
|
|
35
|
+
const meta1 = obj1.__reflect().__data
|
|
36
|
+
const meta2 = obj2.__reflect().__data
|
|
37
|
+
|
|
38
|
+
const lengthOrdering = compare$prim(meta1.length, meta2.length)
|
|
39
|
+
if (lengthOrdering != 0) { return lengthOrdering; }
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < meta1.length; i++) {
|
|
42
|
+
const contentOrdering = $effekt.compare(meta1[i], meta2[i])
|
|
43
|
+
if (contentOrdering != 0) { return contentOrdering; }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return compare$prim(obj1, obj2);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
$effekt.println = function println$impl(str) {
|
|
54
|
+
console.log(str); return $effekt.unit;
|
|
24
55
|
}
|
|
25
56
|
|
|
26
57
|
$effekt.unit = { __unit: true }
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
module mutable/map
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import immutable/option
|
|
3
|
+
import array
|
|
5
4
|
|
|
6
5
|
// A mutable map, backed by a JavaScript Map.
|
|
7
6
|
extern type Map[K, V]
|
|
8
7
|
|
|
9
8
|
extern pure def emptyMap[K, V](): Map[K, V] =
|
|
10
|
-
"new Map()"
|
|
9
|
+
js "new Map()"
|
|
11
10
|
|
|
12
11
|
def get[K, V](m: Map[K, V], key: K): Option[V] =
|
|
13
|
-
m.unsafeGet(key)
|
|
12
|
+
undefinedToOption(m.unsafeGet(key))
|
|
14
13
|
|
|
15
14
|
extern pure def unsafeGet[K, V](m: Map[K, V], key: K): V =
|
|
16
|
-
"${m}.get(${key})"
|
|
15
|
+
js "${m}.get(${key})"
|
|
17
16
|
|
|
18
|
-
extern """
|
|
17
|
+
extern js """
|
|
19
18
|
function map$update(m, key, value) {
|
|
20
19
|
m.set(key, value);
|
|
21
20
|
return $effekt.unit
|
|
@@ -23,10 +22,10 @@ function map$update(m, key, value) {
|
|
|
23
22
|
"""
|
|
24
23
|
|
|
25
24
|
extern io def update[K, V](m: Map[K, V], key: K, value: V): Unit =
|
|
26
|
-
"map$update(${m}, ${key}, ${value})"
|
|
25
|
+
js "map$update(${m}, ${key}, ${value})"
|
|
27
26
|
|
|
28
27
|
extern pure def values[K, V](map: Map[K, V]): Array[V] =
|
|
29
|
-
"Array.from(${map}.values())"
|
|
28
|
+
js "Array.from(${map}.values())"
|
|
30
29
|
|
|
31
30
|
extern pure def keys[K, V](map: Map[K, V]): Array[K] =
|
|
32
|
-
"Array.from(${map}.keys())"
|
|
31
|
+
js "Array.from(${map}.keys())"
|
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
module text/regex
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import immutable/option
|
|
3
|
+
import string
|
|
5
4
|
|
|
6
5
|
extern type Regex
|
|
7
6
|
|
|
8
7
|
record Match(matched: String, index: Int)
|
|
9
8
|
|
|
10
9
|
extern pure def regex(str: String): Regex =
|
|
11
|
-
"new RegExp(${str})"
|
|
10
|
+
js "new RegExp(${str})"
|
|
12
11
|
|
|
13
12
|
def exec(reg: Regex, str: String): Option[Match] =
|
|
14
|
-
|
|
13
|
+
undefinedToOption(reg.unsafeExec(str)) match {
|
|
15
14
|
case None() => None()
|
|
16
15
|
case Some(v) => Some(Match(v.matched, v.index))
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
// internal representation { matched: String, index: Int }
|
|
20
19
|
extern type RegexMatch
|
|
21
|
-
extern pure def matched(r: RegexMatch): String = "${r}.matched"
|
|
22
|
-
extern pure def index(r: RegexMatch): Int = "${r}.index"
|
|
20
|
+
extern pure def matched(r: RegexMatch): String = js "${r}.matched"
|
|
21
|
+
extern pure def index(r: RegexMatch): Int = js "${r}.index"
|
|
23
22
|
|
|
24
|
-
extern """
|
|
23
|
+
extern js """
|
|
25
24
|
function regex$exec(reg, str) {
|
|
26
25
|
var res = reg.exec(str);
|
|
27
26
|
if (res === null) { return undefined }
|
|
@@ -31,4 +30,4 @@ function regex$exec(reg, str) {
|
|
|
31
30
|
|
|
32
31
|
// internals
|
|
33
32
|
extern io def unsafeExec(reg: Regex, str: String): RegexMatch =
|
|
34
|
-
"regex$exec(${reg}, ${str})"
|
|
33
|
+
js "regex$exec(${reg}, ${str})"
|
|
@@ -4,8 +4,8 @@ module unsafe/cont
|
|
|
4
4
|
extern type Cont[A, B]
|
|
5
5
|
|
|
6
6
|
// we mark this as {io} since pure functions should not take block parameters.
|
|
7
|
-
extern io def cont[A, B] { r: A => B / {} }: Cont[A, B] = "${box r}"
|
|
7
|
+
extern io def cont[A, B] { r: A => B / {} }: Cont[A, B] = js "${box r}"
|
|
8
8
|
|
|
9
9
|
// This is unsafe if the continuation left the scope of the
|
|
10
10
|
// capabilities it closed over
|
|
11
|
-
extern
|
|
11
|
+
extern async def apply[A, B](k: Cont[A, B], a: A): B / {} = js "${k}(${a})"
|
|
@@ -4,42 +4,42 @@ extern type Node
|
|
|
4
4
|
|
|
5
5
|
// can be null
|
|
6
6
|
extern pure def getElementById(id: String): Node =
|
|
7
|
-
"document.getElementById(${id})"
|
|
7
|
+
js "document.getElementById(${id})"
|
|
8
8
|
|
|
9
9
|
extern pure def createElement(tag: String): Node =
|
|
10
|
-
"document.createElement(${tag})"
|
|
10
|
+
js "document.createElement(${tag})"
|
|
11
11
|
|
|
12
12
|
extern pure def createTextNode(text: String): Node =
|
|
13
|
-
"document.createTextNode(${text})"
|
|
13
|
+
js "document.createTextNode(${text})"
|
|
14
14
|
|
|
15
15
|
extern pure def setAttribute(node: Node, key: String, value: String): Node =
|
|
16
|
-
"${node}.setAttribute(${key}, ${value})"
|
|
16
|
+
js "${node}.setAttribute(${key}, ${value})"
|
|
17
17
|
|
|
18
18
|
// the string return type is wrong here...
|
|
19
19
|
extern pure def getAttribute(node: Node, key: String): String =
|
|
20
|
-
"${node}.getAttribute(${key})"
|
|
20
|
+
js "${node}.getAttribute(${key})"
|
|
21
21
|
|
|
22
22
|
extern pure def appendChild(node: Node, child: Node): Node =
|
|
23
|
-
"${node}.appendChild(${child})"
|
|
23
|
+
js "${node}.appendChild(${child})"
|
|
24
24
|
|
|
25
25
|
extern pure def innerHTML(node: Node, contents: String): Node =
|
|
26
|
-
"(function() { ${node}.innerHTML = ${contents}; return ${node} })()"
|
|
26
|
+
js "(function() { ${node}.innerHTML = ${contents}; return ${node} })()"
|
|
27
27
|
|
|
28
28
|
extern type IdleDeadline
|
|
29
29
|
|
|
30
30
|
extern pure def requestIdleCallback(callback: (IdleDeadline) => Unit at {io}): Unit =
|
|
31
|
-
"window.requestIdleCallback(deadline => ${callback}(deadline).run())"
|
|
31
|
+
js "window.requestIdleCallback(deadline => ${callback}(deadline).run())"
|
|
32
32
|
|
|
33
|
-
extern pure def didDimeout(deadline: IdleDeadline):
|
|
34
|
-
"${deadline}.didTimeout"
|
|
33
|
+
extern pure def didDimeout(deadline: IdleDeadline): Bool =
|
|
34
|
+
js "${deadline}.didTimeout"
|
|
35
35
|
|
|
36
36
|
extern pure def timeRemaining(deadline: IdleDeadline): Double =
|
|
37
|
-
"${deadline}.timeRemaining()"
|
|
37
|
+
js "${deadline}.timeRemaining()"
|
|
38
38
|
|
|
39
39
|
extern pure def onClick(node: Node, handler: () => Unit at {io}): Node =
|
|
40
|
-
"(function() { ${node}.onclick = () => ${handler}().run(); return ${node} })()"
|
|
40
|
+
js "(function() { ${node}.onclick = () => ${handler}().run(); return ${node} })()"
|
|
41
41
|
|
|
42
42
|
extern def sleep(n: Int): Unit =
|
|
43
|
-
"$effekt.callcc(k => window.setTimeout(() => k(null), ${n}))"
|
|
43
|
+
js "$effekt.callcc(k => window.setTimeout(() => k(null), ${n}))"
|
|
44
44
|
|
|
45
45
|
def clear(node: Node) = node.innerHTML("")
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#ifndef EFFEKT_ARRAY_C
|
|
2
|
+
#define EFFEKT_ARRAY_C
|
|
3
|
+
|
|
4
|
+
/** We represent arrays like positive types.
|
|
5
|
+
* The tag is 0 and the obj points to memory with the following layout:
|
|
6
|
+
*
|
|
7
|
+
* +--[ Header ]--+------+------------+
|
|
8
|
+
* | Rc | Eraser | Size | Fields ... |
|
|
9
|
+
* +--------------+------+------------+
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
void c_array_erase_fields(void *envPtr) {
|
|
13
|
+
uint64_t *sizePtr = envPtr;
|
|
14
|
+
struct Pos *dataPtr = envPtr + sizeof(uint64_t);
|
|
15
|
+
uint64_t size = *sizePtr;
|
|
16
|
+
for (uint64_t i = 0; i < size; i++) {
|
|
17
|
+
erasePositive(dataPtr[i]);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
struct Pos c_array_new(const Int size) {
|
|
22
|
+
void *objPtr = calloc(sizeof(struct Header) + sizeof(uint64_t) + size * sizeof(struct Pos), 1);
|
|
23
|
+
struct Header *headerPtr = objPtr;
|
|
24
|
+
uint64_t *sizePtr = objPtr + sizeof(struct Header);
|
|
25
|
+
*headerPtr = (struct Header) { .rc = 0, .eraser = c_array_erase_fields, };
|
|
26
|
+
*sizePtr = size;
|
|
27
|
+
return (struct Pos) {
|
|
28
|
+
.tag = 0,
|
|
29
|
+
.obj = objPtr,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Int c_array_size(const struct Pos arr) {
|
|
34
|
+
uint64_t *sizePtr = arr.obj + sizeof(struct Header);
|
|
35
|
+
return *sizePtr;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
struct Pos c_array_get(const struct Pos arr, const Int index) {
|
|
39
|
+
struct Pos *dataPtr = arr.obj + sizeof(struct Header) + sizeof(uint64_t);
|
|
40
|
+
struct Pos element = dataPtr[index];
|
|
41
|
+
sharePositive(element);
|
|
42
|
+
return element;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
struct Pos c_array_set(const struct Pos arr, const Int index, const struct Pos value) {
|
|
46
|
+
struct Pos *dataPtr = arr.obj + sizeof(struct Header) + sizeof(uint64_t);
|
|
47
|
+
struct Pos element = dataPtr[index];
|
|
48
|
+
erasePositive(element);
|
|
49
|
+
dataPtr[index] = value;
|
|
50
|
+
return Unit;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#endif
|
package/libraries/llvm/buffer.c
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
#ifndef EFFEKT_BUFFER_C
|
|
2
2
|
#define EFFEKT_BUFFER_C
|
|
3
3
|
|
|
4
|
-
// NOTE: Buffers are NOT 0-terminated!
|
|
5
|
-
|
|
6
4
|
#include "stdlib.h"
|
|
7
5
|
#include <stdint.h>
|
|
6
|
+
#include <string.h> // For memcopy
|
|
7
|
+
|
|
8
|
+
/** We represent buffers like positive types.
|
|
9
|
+
*
|
|
10
|
+
* - The field `tag` contains offset (32bit) and length (32bit), which is equal to
|
|
11
|
+
* the tag (64bit) in Pos;
|
|
12
|
+
* - The field `obj` points to memory with the following layout:
|
|
13
|
+
*
|
|
14
|
+
* +--[ Header ]--+--------------+
|
|
15
|
+
* | Rc | Eraser | Contents ... |
|
|
16
|
+
* +--------------+--------------+
|
|
17
|
+
*
|
|
18
|
+
* The eraser is always a null pointer since it does not need to do anything.
|
|
19
|
+
*/
|
|
8
20
|
|
|
9
21
|
// TODO It may be performance-advantageous to implement this C file's semantics
|
|
10
22
|
// in LLVM, since the linker cannot realistically be asked to satisfactorily
|
|
@@ -13,113 +25,123 @@
|
|
|
13
25
|
// with clang-generated LLVM from this file's entire translation unit.
|
|
14
26
|
|
|
15
27
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
28
|
+
struct Pos c_buffer(const uint32_t offset, const uint32_t length, void* obj) {
|
|
29
|
+
return (struct Pos) {
|
|
30
|
+
.tag = ((uint64_t)offset << 32) | (uint32_t)length,
|
|
31
|
+
.obj = obj,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
19
34
|
|
|
20
|
-
|
|
21
|
-
|
|
35
|
+
void c_buffer_erase_noop(void *envPtr) { (void)envPtr; }
|
|
36
|
+
|
|
37
|
+
uint64_t c_buffer_offset(const struct Pos buffer) {
|
|
38
|
+
return (uint32_t)(buffer.tag >> 32); // Extract offset from the upper 32 bits
|
|
22
39
|
}
|
|
23
40
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
41
|
+
// Function to get the length from Pos
|
|
42
|
+
// WARNING: This does not erase the Pos argument!
|
|
43
|
+
uint64_t c_buffer_length(const struct Pos buffer) {
|
|
44
|
+
return (uint32_t)(buffer.tag & 0xFFFFFFFF); // Extract length from the lower 32 bits
|
|
27
45
|
}
|
|
28
46
|
|
|
29
47
|
uint8_t *c_buffer_bytes(const struct Pos buffer) {
|
|
30
|
-
return (uint8_t *) (buffer.obj +
|
|
48
|
+
return (uint8_t *) (buffer.obj + sizeof(struct Header) + c_buffer_offset(buffer));
|
|
31
49
|
}
|
|
32
50
|
|
|
33
|
-
|
|
34
51
|
struct Pos c_buffer_construct(const uint64_t n, const uint8_t *data) {
|
|
35
|
-
uint8_t *obj = malloc(
|
|
36
|
-
ASSERT_NON_NULL(obj)
|
|
52
|
+
uint8_t *obj = (uint8_t *)malloc(sizeof(struct Header) + n * sizeof *obj);
|
|
53
|
+
ASSERT_NON_NULL(obj);
|
|
37
54
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
obj[j] = 0;
|
|
55
|
+
struct Header *header = (void*) obj;
|
|
56
|
+
*header = (struct Header) { .rc = 0, .eraser = c_buffer_erase_noop, };
|
|
41
57
|
|
|
42
|
-
// data
|
|
43
|
-
|
|
44
|
-
obj[BUFFER_HEADER_WIDTH + j] = data[j];
|
|
58
|
+
// Copy data
|
|
59
|
+
memcpy(obj + sizeof(struct Header), data, n);
|
|
45
60
|
|
|
46
|
-
return (
|
|
47
|
-
.tag = n,
|
|
48
|
-
.obj = obj,
|
|
49
|
-
};
|
|
61
|
+
return c_buffer(0, n, obj);
|
|
50
62
|
}
|
|
51
63
|
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
struct Pos c_buffer_construct_zeroed(const uint64_t n) {
|
|
65
|
+
uint8_t *zeroes = calloc(sizeof(struct Header) + n, sizeof *zeroes);
|
|
66
|
+
ASSERT_NON_NULL(zeroes)
|
|
67
|
+
|
|
68
|
+
struct Header *header = (void*) zeroes;
|
|
69
|
+
*header = (struct Header) { .rc = 0, .eraser = c_buffer_erase_noop, };
|
|
70
|
+
|
|
71
|
+
return c_buffer(0, n, zeroes);
|
|
54
72
|
}
|
|
55
73
|
|
|
74
|
+
struct Pos c_buffer_construct_uninitialized(const uint64_t n) {
|
|
75
|
+
uint8_t *data = malloc(sizeof(struct Header) + n * sizeof *data);
|
|
76
|
+
ASSERT_NON_NULL(data)
|
|
56
77
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
fprintf(stderr, "c_buffer_refcount_increment((struct Pos) "
|
|
60
|
-
"{ .tag = %" PRIu64 ", .obj = %p }): from %" PRIu64 "\n",
|
|
61
|
-
buffer.tag, buffer.obj, *(c_buffer_refcount(buffer)));
|
|
62
|
-
fflush(stderr);
|
|
63
|
-
}
|
|
78
|
+
struct Header *header = (void*) data;
|
|
79
|
+
*header = (struct Header) { .rc = 0, .eraser = c_buffer_erase_noop, };
|
|
64
80
|
|
|
65
|
-
(
|
|
81
|
+
return c_buffer(0, n, data);
|
|
66
82
|
}
|
|
67
83
|
|
|
68
|
-
|
|
69
|
-
if (DEBUG_REFCOUNT) {
|
|
70
|
-
fprintf(stderr, "c_buffer_refcount_decrement((struct Pos) "
|
|
71
|
-
"{ .tag = %" PRIu64 ", .obj = %p }): from %" PRIu64 "\n",
|
|
72
|
-
buffer.tag, buffer.obj, *(c_buffer_refcount(buffer)));
|
|
73
|
-
fflush(stderr);
|
|
74
|
-
}
|
|
84
|
+
struct Pos c_buffer_slice(struct Pos buffer, const uint64_t offset, const uint64_t length) {
|
|
75
85
|
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
}
|
|
86
|
+
if ((c_buffer_offset(buffer) + offset + length) > c_buffer_length(buffer))
|
|
87
|
+
return buffer;
|
|
79
88
|
|
|
89
|
+
return c_buffer(c_buffer_offset(buffer) + offset, length, buffer.obj);
|
|
90
|
+
}
|
|
80
91
|
|
|
81
|
-
struct Pos
|
|
82
|
-
|
|
83
|
-
ASSERT_NON_NULL(zeroes)
|
|
84
|
-
const struct Pos buffer = c_buffer_construct(n, zeroes);
|
|
85
|
-
free(zeroes);
|
|
86
|
-
return buffer;
|
|
92
|
+
struct Pos c_buffer_clone(const struct Pos buffer) {
|
|
93
|
+
return c_buffer_construct(c_buffer_length(buffer), c_buffer_bytes(buffer));
|
|
87
94
|
}
|
|
88
95
|
|
|
89
|
-
void
|
|
90
|
-
|
|
96
|
+
void c_buffer_copy(const struct Pos from, struct Pos to, uint64_t startFrom, uint64_t startTo, uint64_t length) {
|
|
97
|
+
// Check bounds
|
|
98
|
+
if ((startFrom + length > c_buffer_length(from)) || (startTo + length > c_buffer_length(to))) {
|
|
91
99
|
return;
|
|
92
|
-
|
|
93
|
-
}
|
|
100
|
+
}
|
|
94
101
|
|
|
95
|
-
|
|
96
|
-
|
|
102
|
+
// Get pointers to the source and destination slices
|
|
103
|
+
uint8_t *from_bytes = c_buffer_bytes(from) + startFrom;
|
|
104
|
+
uint8_t *to_bytes = c_buffer_bytes(to) + startTo;
|
|
105
|
+
|
|
106
|
+
// Copy bytes from `from` to `to`
|
|
107
|
+
memcpy(to_bytes, from_bytes, length);
|
|
97
108
|
}
|
|
98
109
|
|
|
99
110
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Converts a buffer to a null-terminated string, handling zero bytes by encoding them as non-minimal UTF-8 sequences.
|
|
113
|
+
*
|
|
114
|
+
* Note: the returned pointer needs to be managed manually!
|
|
115
|
+
*
|
|
116
|
+
* @param buffer The buffer to convert.
|
|
117
|
+
* @return A null-terminated string representing the contents of the buffer.
|
|
118
|
+
*/
|
|
119
|
+
char* c_buffer_as_null_terminated_string(const struct Pos buffer) {
|
|
120
|
+
uint64_t length = c_buffer_length(buffer);
|
|
104
121
|
uint64_t zero_runes = 0;
|
|
105
|
-
|
|
106
|
-
|
|
122
|
+
uint8_t* bytes = c_buffer_bytes(buffer);
|
|
123
|
+
|
|
124
|
+
// Count zero runes
|
|
125
|
+
for (uint64_t j = 0; j < length; ++j) {
|
|
126
|
+
zero_runes += !bytes[j];
|
|
127
|
+
}
|
|
107
128
|
|
|
108
|
-
|
|
109
|
-
|
|
129
|
+
// Allocate buffer for the null-terminated string
|
|
130
|
+
uint64_t n = length + zero_runes;
|
|
131
|
+
char *buf = (char *) malloc((n + 1) * sizeof(*buf));
|
|
110
132
|
ASSERT_NON_NULL(buf)
|
|
111
133
|
|
|
112
134
|
uint64_t i = 0;
|
|
113
|
-
for (uint64_t j = 0; j <
|
|
114
|
-
buf[i++] =
|
|
115
|
-
if (
|
|
116
|
-
buf[i]
|
|
117
|
-
buf[i++] = 0x80; //
|
|
135
|
+
for (uint64_t j = 0; j < length; ++j) {
|
|
136
|
+
buf[i++] = bytes[j];
|
|
137
|
+
if (buf[i-1] == 0) {
|
|
138
|
+
buf[i-1] = 0xc0; // 0b11000000
|
|
139
|
+
buf[i++] = 0x80; // 0b10000000
|
|
118
140
|
}
|
|
119
141
|
}
|
|
120
142
|
|
|
121
|
-
//
|
|
122
|
-
buf[n] = '\
|
|
143
|
+
// Null-terminate the string
|
|
144
|
+
buf[n] = '\0';
|
|
123
145
|
|
|
124
146
|
return buf;
|
|
125
147
|
}
|
|
@@ -140,18 +162,29 @@ struct Pos c_buffer_concatenate(const struct Pos left, const struct Pos right) {
|
|
|
140
162
|
= j < c_buffer_length(left)
|
|
141
163
|
? c_buffer_bytes(left)[j]
|
|
142
164
|
: c_buffer_bytes(right)[j - c_buffer_length(left)];
|
|
165
|
+
|
|
166
|
+
erasePositive(left);
|
|
167
|
+
erasePositive(right);
|
|
143
168
|
return concatenated;
|
|
144
169
|
}
|
|
145
170
|
|
|
146
171
|
struct Pos c_buffer_eq(const struct Pos left, const struct Pos right) {
|
|
147
172
|
uint64_t left_len = c_buffer_length(left);
|
|
148
173
|
uint64_t right_len = c_buffer_length(right);
|
|
149
|
-
if(left_len != right_len)
|
|
150
|
-
|
|
151
|
-
|
|
174
|
+
if (left_len != right_len) {
|
|
175
|
+
erasePositive(left);
|
|
176
|
+
erasePositive(right);
|
|
177
|
+
return BooleanFalse;
|
|
178
|
+
}
|
|
179
|
+
for (uint64_t j = 0; j < left_len; ++j) {
|
|
180
|
+
if (c_buffer_bytes(left)[j] != c_buffer_bytes(right)[j]) {
|
|
181
|
+
erasePositive(left);
|
|
182
|
+
erasePositive(right);
|
|
152
183
|
return BooleanFalse;
|
|
153
184
|
}
|
|
154
185
|
}
|
|
186
|
+
erasePositive(left);
|
|
187
|
+
erasePositive(right);
|
|
155
188
|
return BooleanTrue;
|
|
156
189
|
}
|
|
157
190
|
|
|
@@ -160,6 +193,7 @@ struct Pos c_buffer_substring(const struct Pos str, uint64_t start, uint64_t end
|
|
|
160
193
|
for (uint64_t j = 0; j < c_buffer_length(substr); ++j) {
|
|
161
194
|
c_buffer_bytes(substr)[j] = c_buffer_bytes(str)[start+j];
|
|
162
195
|
}
|
|
196
|
+
erasePositive(str);
|
|
163
197
|
return substr;
|
|
164
198
|
}
|
|
165
199
|
|
|
@@ -168,14 +202,85 @@ struct Pos c_buffer_show_Int(const Int n) {
|
|
|
168
202
|
sprintf(str, "%" PRId64, n);
|
|
169
203
|
return c_buffer_construct_from_null_terminated_string(str);
|
|
170
204
|
}
|
|
205
|
+
|
|
206
|
+
struct Pos c_buffer_show_Byte(const uint8_t n) {
|
|
207
|
+
char str[4]; // Byte values range from 0 to 255, 3 characters + null terminator
|
|
208
|
+
sprintf(str, "%" PRIu8, n);
|
|
209
|
+
return c_buffer_construct_from_null_terminated_string(str);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
struct Pos c_buffer_show_Char(const uint64_t n) {
|
|
213
|
+
char str[5] = {0}; // Max 4 bytes for UTF-8 + 1 for null terminator
|
|
214
|
+
unsigned char *buf = (unsigned char *)str;
|
|
215
|
+
|
|
216
|
+
if (n < 0x80) { // 1-byte sequence
|
|
217
|
+
buf[0] = (unsigned char)n;
|
|
218
|
+
} else if (n < 0x800) { // 2-byte sequence
|
|
219
|
+
buf[0] = (unsigned char)(0xC0 | (n >> 6));
|
|
220
|
+
buf[1] = (unsigned char)(0x80 | (n & 0x3F));
|
|
221
|
+
} else if (n < 0x10000) { // 3-byte sequence
|
|
222
|
+
buf[0] = (unsigned char)(0xE0 | (n >> 12));
|
|
223
|
+
buf[1] = (unsigned char)(0x80 | ((n >> 6) & 0x3F));
|
|
224
|
+
buf[2] = (unsigned char)(0x80 | (n & 0x3F));
|
|
225
|
+
} else if (n < 0x110000) { // 4-byte sequence
|
|
226
|
+
buf[0] = (unsigned char)(0xF0 | (n >> 18));
|
|
227
|
+
buf[1] = (unsigned char)(0x80 | ((n >> 12) & 0x3F));
|
|
228
|
+
buf[2] = (unsigned char)(0x80 | ((n >> 6) & 0x3F));
|
|
229
|
+
buf[3] = (unsigned char)(0x80 | (n & 0x3F));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return c_buffer_construct_from_null_terminated_string(str);
|
|
233
|
+
}
|
|
234
|
+
|
|
171
235
|
struct Pos c_buffer_show_Double(const Double x) {
|
|
172
236
|
char str[64]; // TODO is this large enough? Possibly use snprintf first
|
|
173
237
|
sprintf(str, "%g", x);
|
|
174
238
|
return c_buffer_construct_from_null_terminated_string(str);
|
|
175
239
|
}
|
|
176
240
|
|
|
177
|
-
uint64_t c_buffer_index(const struct Pos
|
|
178
|
-
return c_buffer_bytes(
|
|
241
|
+
uint64_t c_buffer_index(const struct Pos buffer, const uint64_t index) {
|
|
242
|
+
return c_buffer_bytes(buffer)[index];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
void c_buffer_set(struct Pos buffer, const uint64_t index, uint8_t value) {
|
|
246
|
+
c_buffer_bytes(buffer)[index] = value;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
uint32_t c_buffer_character_at(const struct Pos buffer, const uint64_t index) {
|
|
250
|
+
const uint8_t *bytes = c_buffer_bytes(buffer);
|
|
251
|
+
uint8_t first_byte = bytes[index];
|
|
252
|
+
uint32_t character = 0;
|
|
253
|
+
|
|
254
|
+
uint32_t length = c_buffer_length(buffer);
|
|
255
|
+
|
|
256
|
+
if (first_byte < 0x80) {
|
|
257
|
+
// Single-byte character (0xxxxxxx)
|
|
258
|
+
character = first_byte;
|
|
259
|
+
} else if ((first_byte & 0xE0) == 0xC0) {
|
|
260
|
+
// Two-byte character (110xxxxx 10xxxxxx)
|
|
261
|
+
if (index + 1 < length) {
|
|
262
|
+
character = ((first_byte & 0x1F) << 6) |
|
|
263
|
+
(bytes[index + 1] & 0x3F);
|
|
264
|
+
}
|
|
265
|
+
} else if ((first_byte & 0xF0) == 0xE0) {
|
|
266
|
+
// Three-byte character (1110xxxx 10xxxxxx 10xxxxxx)
|
|
267
|
+
if (index + 2 < length) {
|
|
268
|
+
character = ((first_byte & 0x0F) << 12) |
|
|
269
|
+
((bytes[index + 1] & 0x3F) << 6) |
|
|
270
|
+
(bytes[index + 2] & 0x3F);
|
|
271
|
+
}
|
|
272
|
+
} else if ((first_byte & 0xF8) == 0xF0) {
|
|
273
|
+
// Four-byte character (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx)
|
|
274
|
+
if (index + 3 < length) {
|
|
275
|
+
character = ((first_byte & 0x07) << 18) |
|
|
276
|
+
((bytes[index + 1] & 0x3F) << 12) |
|
|
277
|
+
((bytes[index + 2] & 0x3F) << 6) |
|
|
278
|
+
(bytes[index + 3] & 0x3F);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
erasePositive(buffer);
|
|
283
|
+
return character;
|
|
179
284
|
}
|
|
180
285
|
|
|
181
286
|
#endif
|
|
@@ -8,17 +8,36 @@ declare void @c_io_println_Int(%Int)
|
|
|
8
8
|
declare void @c_io_println_Double(%Double)
|
|
9
9
|
declare void @c_io_println_String(%Pos)
|
|
10
10
|
|
|
11
|
-
declare %Pos @c_buffer_construct(i64,
|
|
12
|
-
declare void @c_buffer_refcount_increment(%Pos)
|
|
13
|
-
declare void @c_buffer_refcount_decrement(%Pos)
|
|
11
|
+
declare %Pos @c_buffer_construct(i64, ptr)
|
|
14
12
|
declare %Pos @c_buffer_concatenate(%Pos, %Pos)
|
|
15
|
-
|
|
16
|
-
declare
|
|
13
|
+
declare ptr @c_buffer_as_null_terminated_string(%Pos)
|
|
14
|
+
declare %Pos @c_buffer_construct_from_null_terminated_string(ptr)
|
|
15
|
+
declare ptr @c_buffer_construct_uninitialized(i64)
|
|
16
|
+
declare ptr @c_buffer_bytes(ptr)
|
|
17
|
+
declare void @c_buffer_copy(%Pos, %Pos, i64, i64, i64)
|
|
18
|
+
declare %Pos @c_buffer_clone(%Pos)
|
|
17
19
|
|
|
18
20
|
declare %Pos @c_buffer_eq(%Pos, %Pos)
|
|
19
21
|
declare %Pos @c_buffer_substring(%Pos, i64, i64)
|
|
20
22
|
declare i64 @c_buffer_length(%Pos)
|
|
21
23
|
declare i64 @c_buffer_toInt(%Pos)
|
|
22
|
-
declare %Pos @c_buffer_show_Int(
|
|
23
|
-
declare %Pos @
|
|
24
|
-
declare %
|
|
24
|
+
declare %Pos @c_buffer_show_Int(i64)
|
|
25
|
+
declare %Pos @c_buffer_show_Char(i32)
|
|
26
|
+
declare %Pos @c_buffer_show_Byte(i8)
|
|
27
|
+
declare %Pos @c_buffer_show_Double(double)
|
|
28
|
+
declare %Int @c_buffer_index(%Pos, i64)
|
|
29
|
+
declare void @c_buffer_set(%Pos, i64, i8)
|
|
30
|
+
declare %Int @c_buffer_character_at(%Pos, i64)
|
|
31
|
+
|
|
32
|
+
declare %Pos @c_buffer_slice(%Pos, i64, i64)
|
|
33
|
+
|
|
34
|
+
declare void @hole()
|
|
35
|
+
|
|
36
|
+
declare %Pos @c_ref_fresh(%Pos)
|
|
37
|
+
declare %Pos @c_ref_get(%Pos)
|
|
38
|
+
declare %Pos @c_ref_set(%Pos, %Pos)
|
|
39
|
+
|
|
40
|
+
declare %Pos @c_array_new(%Int)
|
|
41
|
+
declare %Int @c_array_size(%Pos)
|
|
42
|
+
declare %Pos @c_array_get(%Pos, %Int)
|
|
43
|
+
declare %Pos @c_array_set(%Pos, %Int, %Pos)
|