@effekt-lang/effekt 0.2.1 → 0.2.2

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.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +36 -0
  3. package/bin/effekt +0 -0
  4. package/bin/effekt.sh +2 -0
  5. package/libraries/chez/THIRD-PARTY.txt +11 -0
  6. package/libraries/chez/callcc/datatype.ss +188 -0
  7. package/libraries/chez/callcc/effekt.effekt +221 -0
  8. package/libraries/chez/callcc/effekt.ss +31 -0
  9. package/libraries/chez/callcc/seq.ss +44 -0
  10. package/libraries/chez/callcc/seq0.ss +69 -0
  11. package/libraries/chez/callcc/tail.ss +76 -0
  12. package/libraries/chez/callcc/tail0.ss +89 -0
  13. package/libraries/chez/common/effekt_matching.ss +100 -0
  14. package/libraries/chez/common/effekt_primitives.ss +97 -0
  15. package/libraries/chez/common/immutable/cslist.effekt +31 -0
  16. package/libraries/chez/common/immutable/dequeue.effekt +78 -0
  17. package/libraries/chez/common/immutable/list.effekt +643 -0
  18. package/libraries/chez/common/immutable/option.effekt +39 -0
  19. package/libraries/chez/common/immutable/result.effekt +29 -0
  20. package/libraries/chez/common/io/args.effekt +20 -0
  21. package/libraries/chez/common/mutable/array.effekt +27 -0
  22. package/libraries/chez/common/mutable/dict.effekt +30 -0
  23. package/libraries/chez/common/mutable/heap.effekt +12 -0
  24. package/libraries/chez/common/test.effekt +59 -0
  25. package/libraries/chez/common/text/pregexp.scm +772 -0
  26. package/libraries/chez/common/text/regex.effekt +33 -0
  27. package/libraries/chez/common/text/string.effekt +46 -0
  28. package/libraries/chez/lift/effekt.effekt +217 -0
  29. package/libraries/chez/lift/effekt.ss +118 -0
  30. package/libraries/chez/monadic/effekt.effekt +218 -0
  31. package/libraries/chez/monadic/effekt.ss +84 -0
  32. package/libraries/chez/monadic/seq0.ss +76 -0
  33. package/libraries/js/effekt.effekt +284 -0
  34. package/libraries/js/effekt_builtins.js +26 -0
  35. package/libraries/js/effekt_matching.js +79 -0
  36. package/libraries/js/effekt_runtime.js +385 -0
  37. package/libraries/js/immutable/dequeue.effekt +78 -0
  38. package/libraries/js/immutable/list.effekt +643 -0
  39. package/libraries/js/immutable/option.effekt +49 -0
  40. package/libraries/js/immutable/result.effekt +29 -0
  41. package/libraries/js/io/args.effekt +9 -0
  42. package/libraries/js/io/async.effekt +28 -0
  43. package/libraries/js/io/async.js +2 -0
  44. package/libraries/js/io/file.effekt +50 -0
  45. package/libraries/js/io/file_include.js +13 -0
  46. package/libraries/js/mutable/array.effekt +98 -0
  47. package/libraries/js/mutable/heap.effekt +32 -0
  48. package/libraries/js/mutable/map.effekt +32 -0
  49. package/libraries/js/test.effekt +59 -0
  50. package/libraries/js/text/regex.effekt +34 -0
  51. package/libraries/js/text/string.effekt +64 -0
  52. package/libraries/js/unsafe/cont.effekt +11 -0
  53. package/libraries/js/web/dom.effekt +45 -0
  54. package/libraries/llvm/buffer.c +181 -0
  55. package/libraries/llvm/effekt.effekt +243 -0
  56. package/libraries/llvm/forward-declare-c.ll +24 -0
  57. package/libraries/llvm/hole.c +11 -0
  58. package/libraries/llvm/immutable/list.effekt +678 -0
  59. package/libraries/llvm/immutable/option.effekt +53 -0
  60. package/libraries/llvm/immutable/result.effekt +29 -0
  61. package/libraries/llvm/io/args.effekt +24 -0
  62. package/libraries/llvm/io.c +24 -0
  63. package/libraries/llvm/main.c +39 -0
  64. package/libraries/llvm/rts.ll +619 -0
  65. package/libraries/llvm/sanity.c +16 -0
  66. package/libraries/llvm/text/string.effekt +179 -0
  67. package/libraries/llvm/types.c +20 -0
  68. package/libraries/ml/effekt.effekt +267 -0
  69. package/libraries/ml/effekt.sml +63 -0
  70. package/libraries/ml/immutable/dequeue.effekt +81 -0
  71. package/libraries/ml/immutable/list.effekt +674 -0
  72. package/libraries/ml/immutable/option.effekt +53 -0
  73. package/libraries/ml/immutable/result.effekt +29 -0
  74. package/libraries/ml/immutable/seq.effekt +903 -0
  75. package/libraries/ml/internal/option.effekt +18 -0
  76. package/libraries/ml/io/args.effekt +41 -0
  77. package/libraries/ml/mutable/array.effekt +43 -0
  78. package/libraries/ml/random.effekt +85 -0
  79. package/libraries/ml/test.effekt +60 -0
  80. package/libraries/ml/text/regex.effekt +107 -0
  81. package/libraries/ml/text/string.effekt +81 -0
  82. package/licenses/THIRD-PARTY.txt +21 -0
  83. package/licenses/apache-2.0 - license-2.0.txt +202 -0
  84. package/licenses/eclipse public license, version 2.0 - epl-2.0.html +61 -0
  85. package/licenses/kiama-license.txt +373 -0
  86. package/licenses/kiama-readme.txt +186 -0
  87. package/licenses/mit license - mit-license.html +769 -0
  88. package/licenses/the apache software license, version 2.0 - license-2.0.txt +202 -0
  89. package/licenses/the bsd license - bsd-license.html +762 -0
  90. package/licenses/the mit license - mit.html +769 -0
  91. package/package.json +26 -18
@@ -0,0 +1,9 @@
1
+ module io/args
2
+
3
+ import effekt
4
+ import immutable/list
5
+ import mutable/array
6
+
7
+ extern io def nativeArgs(): Array[String] = "process.argv.slice(2)"
8
+
9
+ def commandLineArgs(): List[String] = nativeArgs().toList
@@ -0,0 +1,28 @@
1
+ module io/async
2
+
3
+ extern include "async.js"
4
+
5
+ extern type Promise[T]
6
+
7
+ interface AsyncIO {
8
+ def read(path: String): Promise[String]
9
+ def await[T](p: Promise[T]): T
10
+ }
11
+
12
+ def asyncIO[R](p: () => R / AsyncIO at {io}): R = try {
13
+ p()
14
+ } with AsyncIO {
15
+ def read(path) = resume(readPromise(path))
16
+ def await[T](p) = awaitPromise(p, resume)
17
+ }
18
+
19
+ def block[T](p: Promise[T]) = do await(p)
20
+
21
+ extern io def readPromise(path: String): Promise[String] =
22
+ "fsPromises.readFile(${path}, 'utf8')"
23
+
24
+ extern io def awaitPromise[T, R](p: Promise[T], cont: T => R at {io}): R =
25
+ "${p}.then(res => (${cont})(res).run())"
26
+
27
+ extern io def fork(process: () => Unit at {io}): Unit =
28
+ "setTimeout(() => (${process})().run(), 0)"
@@ -0,0 +1,2 @@
1
+ const fs = require("fs")
2
+ const fsPromises = fs.promises;
@@ -0,0 +1,50 @@
1
+ module io/file
2
+
3
+ effect IOException[A](msg: String): A
4
+
5
+ extern include "file_include.js"
6
+
7
+ extern type FileDescriptor
8
+
9
+ extern type Buffer
10
+
11
+ type Result[A] {
12
+ Error(msg: String);
13
+ Success(a: A)
14
+ }
15
+
16
+ extern type Promise[T]
17
+
18
+ extern io def readPromise(path: String): Promise[String] =
19
+ "$effekt.delayed(() => fsPromises.readFile(${path}, 'utf8'))"
20
+
21
+ def await[T](p: Promise[T]): T / { IOException } =
22
+ try { awaitImpl(p){exc} } with exc : IOException[A] {
23
+ msg => do IOException(msg)
24
+ }
25
+
26
+ extern io def awaitImpl[T](p: Promise[T]){exc: IOException}: T =
27
+ "$effekt.callcc(k => ${p}.then(res => k($effekt.pure(res)), err => k(${box { (msg: String) => do IOException[T](msg) }}(err.message)))).then(c => c)"
28
+
29
+ def open(path: String, mode: String): FileDescriptor / { IOException } =
30
+ try { openImpl(path, mode){exc} } with exc : IOException[A] {
31
+ msg => do IOException(msg)
32
+ }
33
+
34
+ extern io def openImpl(path: String, mode: String){exc: IOException}: FileDescriptor =
35
+ "$effekt.delayed(() => { try { return $effekt.pure(fs.openSync(${path}, ${mode})); } catch (err) { return ${box { (msg: String) => do IOException[FileDescriptor](msg) }}(err.message); }}).then(c => c)"
36
+
37
+ extern io def close(fd: FileDescriptor): Unit =
38
+ "$effekt.delayed(() => fs.closeSync(${fd}))"
39
+
40
+ // uses the asynchronous API of node
41
+ extern io def readFile(path: String): Result[String] =
42
+ "$effekt.callcc(k => fs.readFile(${path}, 'utf8', (err, data) => k(err ? ${box Error}(err.message) : ${box Success}(data))))"
43
+
44
+ def read(path: String): String / { IOException } =
45
+ getResult(readFile(path))
46
+
47
+ def getResult[R](r: Result[R]): R / { IOException } = r match {
48
+ case Error(msg) => do IOException(msg)
49
+ case Success(r) => r
50
+ }
@@ -0,0 +1,13 @@
1
+ const fs = require("fs")
2
+ const fsPromises = fs.promises;
3
+
4
+ function readAsyncHelper(path, f) {
5
+ return $effekt.callcc(k => {
6
+ fs.readFile(path, 'utf8', (err, data) => {
7
+ // change f, so it returns true
8
+ const comp = f(err ? Error(err.message) : Success(data)).then(x => $effekt.pure(true))
9
+ k(comp); // restore the stack, since `f` might use effects
10
+ });
11
+ return k($effekt.pure(false))
12
+ }).then(x => x).then(abort => abort ? $effekt.callcc(k => $effekt.pure(null)) : $effekt.pure(null))
13
+ }
@@ -0,0 +1,98 @@
1
+ module mutable/array
2
+
3
+ import immutable/option
4
+ import immutable/list
5
+
6
+ // A mutable 0-based indexed array, backed by a JavaScript array.
7
+ extern type Array[T]
8
+
9
+ def emptyArray[T](): Array[T] = emptyArray(0)
10
+
11
+ // Creates a new Array of size `size` filled with the value `default`
12
+ def fill[T](size: Int, default: T): Array[T] = {
13
+ val arr = emptyArray[T](size)
14
+ each(0, arr.size()) { i =>
15
+ put(arr, i, default)
16
+ }
17
+ return arr;
18
+ }
19
+
20
+ // Builds a new Array of size `size` from a computation `index` which gets an index
21
+ // and returns a value that will be on that position in the resulting array
22
+ def build[T](size: Int) { index: Int => T }: Array[T] = {
23
+ val arr = emptyArray[T](size)
24
+ each(0, arr.size()) { i =>
25
+ put(arr, i, index(i))
26
+ }
27
+ return arr
28
+ }
29
+
30
+ // Converts a List `list` to an Array
31
+ def fromList[T](list: List[T]): Array[T] = {
32
+ val listSize = list.size();
33
+ val arr = emptyArray(listSize);
34
+
35
+ foreachIndex(list) { (i, head) =>
36
+ put(arr, i, head)
37
+ }
38
+ return arr;
39
+ }
40
+
41
+ extern pure def emptyArray[T](initialSize: Int): Array[T] =
42
+ "(new Array(${initialSize}))"
43
+
44
+ def get[T](arr: Array[T], index: Int): Option[T] =
45
+ arr.unsafeGet(index).undefinedToOption
46
+
47
+ extern pure def size[T](arr: Array[T]): Int =
48
+ "${arr}.length"
49
+
50
+ extern io def unsafeGet[T](arr: Array[T], index: Int): T =
51
+ "${arr}[${index}]"
52
+
53
+ extern """
54
+ function put(arr, index, value) {
55
+ arr[index] = value;
56
+ return $effekt.unit
57
+ }
58
+ """
59
+
60
+ extern io def put[T](arr: Array[T], index: Int, value: T): Unit =
61
+ "put(${arr}, ${index}, ${value})"
62
+
63
+ extern pure def copy[T](arr: Array[T]): Array[T] =
64
+ "[...${arr}]"
65
+
66
+ // Utility functions:
67
+
68
+ def toList[T](arr: Array[T]): List[T] = {
69
+ var i = arr.size - 1;
70
+ var l = Nil[T]()
71
+ while (i >= 0) {
72
+ l = Cons(unsafeGet(arr, i), l)
73
+ i = i - 1
74
+ }
75
+ l
76
+ }
77
+
78
+ def foreach[T](arr: Array[T]){ action: T => Unit / Control }: Unit = {
79
+ each(0, arr.size) { i =>
80
+ val x: T = arr.unsafeGet(i)
81
+ action(x)
82
+ }
83
+ }
84
+
85
+ def foreachIndex[T](arr: Array[T]){ action: (Int, T) => Unit / Control }: Unit = {
86
+ each(0, arr.size) { i =>
87
+ val x: T = arr.unsafeGet(i)
88
+ action(i, x)
89
+ }
90
+ }
91
+
92
+ def sum(list: Array[Int]): Int = {
93
+ var acc = 0
94
+ list.foreach { x =>
95
+ acc = acc + x
96
+ }
97
+ acc
98
+ }
@@ -0,0 +1,32 @@
1
+ module mutable/heap
2
+
3
+ extern """
4
+ var heap$data = {};
5
+ var heap$pointer = 0;
6
+
7
+ function heap$fresh(init) {
8
+ const ref = heap$pointer++;
9
+ heap$data[ref] = init;
10
+ return ref
11
+ }
12
+
13
+ function heap$put(ref, value) {
14
+ heap$data[ref] = value;
15
+ return null
16
+ }
17
+
18
+ function get$impl(ref) {
19
+ return heap$data[ref];
20
+ }
21
+ """
22
+
23
+ extern type HeapRef[T]
24
+
25
+ extern io def fresh[T](init: T): HeapRef[T] =
26
+ "heap$fresh(${init})"
27
+
28
+ extern io def put[T](ref: HeapRef[T], value: T): Unit =
29
+ "heap$put(${ref}, ${value})"
30
+
31
+ extern io def get[T](ref: HeapRef[T]): T =
32
+ "heap$data[${ref}]"
@@ -0,0 +1,32 @@
1
+ module mutable/map
2
+
3
+ import mutable/array
4
+ import immutable/option
5
+
6
+ // A mutable map, backed by a JavaScript Map.
7
+ extern type Map[K, V]
8
+
9
+ extern pure def emptyMap[K, V](): Map[K, V] =
10
+ "new Map()"
11
+
12
+ def get[K, V](m: Map[K, V], key: K): Option[V] =
13
+ m.unsafeGet(key).undefinedToOption
14
+
15
+ extern pure def unsafeGet[K, V](m: Map[K, V], key: K): V =
16
+ "${m}.get(${key})"
17
+
18
+ extern """
19
+ function map$update(m, key, value) {
20
+ m.set(key, value);
21
+ return $effekt.unit
22
+ }
23
+ """
24
+
25
+ extern io def update[K, V](m: Map[K, V], key: K, value: V): Unit =
26
+ "map$update(${m}, ${key}, ${value})"
27
+
28
+ extern pure def values[K, V](map: Map[K, V]): Array[V] =
29
+ "Array.from(${map}.values())"
30
+
31
+ extern pure def keys[K, V](map: Map[K, V]): Array[K] =
32
+ "Array.from(${map}.keys())"
@@ -0,0 +1,59 @@
1
+ import text/string
2
+
3
+ interface Assertion {
4
+ def assert(condition: Boolean, msg: String): Unit
5
+ }
6
+
7
+ def assertTrue(condition: Boolean, msg: String) =
8
+ do assert(condition, msg)
9
+
10
+ def assertTrue(condition: Boolean) =
11
+ do assert(condition, "Assertion failed")
12
+
13
+ def assertFalse(condition: Boolean, msg: String) =
14
+ do assert(not(condition), msg)
15
+
16
+ def assertFalse(condition: Boolean) =
17
+ do assert(not(condition), "Assertion failed")
18
+
19
+ def assertEqual[A](obtained: A, expected: A, msg: String) =
20
+ do assert(obtained == expected, msg)
21
+
22
+ def assertEqual[A](obtained: A, expected: A) =
23
+ do assert(obtained == expected, "Obtained: " ++ obtained.show ++ "\n but expected:" ++ expected.show)
24
+
25
+ interface Test {
26
+ def success(name: String): Unit
27
+ def failure(name: String, msg: String): Unit
28
+ }
29
+
30
+ def test(name: String) { body: => Unit / Assertion } =
31
+ try { body(); do success(name) } with Assertion {
32
+ def assert(condition, msg) = if (condition) resume(()) else do failure(name, msg)
33
+ }
34
+
35
+ def suite(name: String) { body: => Unit / Test }: Boolean = {
36
+ println("Running suite: " ++ name);
37
+ var failed = 0
38
+ var passed = 0
39
+
40
+ try { body() } with Test {
41
+ def success(name) = {
42
+ passed = passed + 1
43
+ println(ANSI_GREEN ++ "+ " ++ name ++ "" ++ ANSI_RESET);
44
+ resume(())
45
+ }
46
+ def failure(name, msg) = {
47
+ failed = failed + 1
48
+ println(ANSI_RED ++ "- (FAIL) " ++ name ++ "\n " ++ msg ++ ANSI_RESET);
49
+ resume(())
50
+ }
51
+ }
52
+ if (failed > 0) {
53
+ println(ANSI_RED ++ "Some tests failed (" ++ passed.show ++ " passed, " ++ failed.show ++ " failed)" ++ ANSI_RESET)
54
+ false
55
+ } else {
56
+ println(ANSI_GREEN ++ "All tests passed (" ++ passed.show ++ " passed)" ++ ANSI_RESET)
57
+ true
58
+ }
59
+ }
@@ -0,0 +1,34 @@
1
+ module text/regex
2
+
3
+ import text/string
4
+ import immutable/option
5
+
6
+ extern type Regex
7
+
8
+ record Match(matched: String, index: Int)
9
+
10
+ extern pure def regex(str: String): Regex =
11
+ "new RegExp(${str})"
12
+
13
+ def exec(reg: Regex, str: String): Option[Match] =
14
+ reg.unsafeExec(str).undefinedToOption match {
15
+ case None() => None()
16
+ case Some(v) => Some(Match(v.matched, v.index))
17
+ }
18
+
19
+ // internal representation { matched: String, index: Int }
20
+ extern type RegexMatch
21
+ extern pure def matched(r: RegexMatch): String = "${r}.matched"
22
+ extern pure def index(r: RegexMatch): Int = "${r}.index"
23
+
24
+ extern """
25
+ function regex$exec(reg, str) {
26
+ var res = reg.exec(str);
27
+ if (res === null) { return undefined }
28
+ else { return { matched: res[0], index: res.index } }
29
+ }
30
+ """
31
+
32
+ // internals
33
+ extern io def unsafeExec(reg: Regex, str: String): RegexMatch =
34
+ "regex$exec(${reg}, ${str})"
@@ -0,0 +1,64 @@
1
+ module text/string
2
+
3
+ import immutable/option
4
+ import mutable/array
5
+
6
+ def charAt(str: String, index: Int): Option[String] =
7
+ str.unsafeCharAt(index).undefinedToOption
8
+
9
+ extern pure def length(str: String): Int =
10
+ "${str}.length"
11
+
12
+ extern pure def repeat(str: String, n: Int): String =
13
+ "${str}.repeat(${n})"
14
+
15
+ extern pure def unsafeSubstring(str: String, from: Int, to: Int): String =
16
+ "${str}.substring(${from}, ${to})"
17
+
18
+ def substring(str: String, from: Int, to: Int): String =
19
+ if (from >= to) { "" }
20
+ else {
21
+ str.unsafeSubstring(from, to)
22
+ }
23
+
24
+ def substring(str: String, from: Int): String =
25
+ str.unsafeSubstring(from, str.length)
26
+
27
+ extern pure def split(str: String, sep: String): Array[String] =
28
+ "${str}.split(${sep})"
29
+
30
+ extern pure def trim(str: String): String =
31
+ "${str}.trim()"
32
+
33
+ def toInt(str: String): Option[Int] =
34
+ str.unsafeToInt.undefinedToOption
35
+
36
+ extern pure def unsafeCharAt(str: String, n: Int): String =
37
+ "${str}[${n}]"
38
+
39
+ extern pure def unsafeToInt(str: String): Int =
40
+ "(Number.isNaN(parseInt(${str})) ? undefined : parseInt(${str}))"
41
+
42
+ def indexOf(str: String, sub: String): Option[Int] = {
43
+ val index = str.unsafeIndexOf(sub)
44
+ if (index < 0) { None() } else { Some(index) }
45
+ }
46
+
47
+ def lastIndexOf(str: String, sub: String): Option[Int] = {
48
+ val index = str.unsafeLastIndexOf(sub)
49
+ if (index < 0) { None() } else { Some(index) }
50
+ }
51
+
52
+ extern pure def unsafeIndexOf(str: String, sub: String): Int = "${str}.indexOf(${sub})"
53
+ extern pure def unsafeLastIndexOf(str: String, sub: String): Int = "${str}.lastIndexOf(${sub})"
54
+
55
+ extern pure def startsWith(str: String, prefix: String): Boolean =
56
+ "${str}.startsWith(${prefix})"
57
+
58
+ extern pure def endsWith(str: String, suffix: String): Boolean =
59
+ "${str}.endsWith(${suffix})"
60
+
61
+ // ANSI escape codes
62
+ val ANSI_GREEN = "\u001b[32m"
63
+ val ANSI_RED = "\u001b[31m"
64
+ val ANSI_RESET = "\u001b[0m"
@@ -0,0 +1,11 @@
1
+ module unsafe/cont
2
+
3
+ // The type of a captured continuation
4
+ extern type Cont[A, B]
5
+
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}"
8
+
9
+ // This is unsafe if the continuation left the scope of the
10
+ // capabilities it closed over
11
+ extern control def apply[A, B](k: Cont[A, B], a: A): B / {} = "${k}(${a})"
@@ -0,0 +1,45 @@
1
+ module web/dom
2
+
3
+ extern type Node
4
+
5
+ // can be null
6
+ extern pure def getElementById(id: String): Node =
7
+ "document.getElementById(${id})"
8
+
9
+ extern pure def createElement(tag: String): Node =
10
+ "document.createElement(${tag})"
11
+
12
+ extern pure def createTextNode(text: String): Node =
13
+ "document.createTextNode(${text})"
14
+
15
+ extern pure def setAttribute(node: Node, key: String, value: String): Node =
16
+ "${node}.setAttribute(${key}, ${value})"
17
+
18
+ // the string return type is wrong here...
19
+ extern pure def getAttribute(node: Node, key: String): String =
20
+ "${node}.getAttribute(${key})"
21
+
22
+ extern pure def appendChild(node: Node, child: Node): Node =
23
+ "${node}.appendChild(${child})"
24
+
25
+ extern pure def innerHTML(node: Node, contents: String): Node =
26
+ "(function() { ${node}.innerHTML = ${contents}; return ${node} })()"
27
+
28
+ extern type IdleDeadline
29
+
30
+ extern pure def requestIdleCallback(callback: (IdleDeadline) => Unit at {io}): Unit =
31
+ "window.requestIdleCallback(deadline => ${callback}(deadline).run())"
32
+
33
+ extern pure def didDimeout(deadline: IdleDeadline): Boolean =
34
+ "${deadline}.didTimeout"
35
+
36
+ extern pure def timeRemaining(deadline: IdleDeadline): Double =
37
+ "${deadline}.timeRemaining()"
38
+
39
+ extern pure def onClick(node: Node, handler: () => Unit at {io}): Node =
40
+ "(function() { ${node}.onclick = () => ${handler}().run(); return ${node} })()"
41
+
42
+ extern def sleep(n: Int): Unit =
43
+ "$effekt.callcc(k => window.setTimeout(() => k(null), ${n}))"
44
+
45
+ def clear(node: Node) = node.innerHTML("")
@@ -0,0 +1,181 @@
1
+ #ifndef EFFEKT_BUFFER_C
2
+ #define EFFEKT_BUFFER_C
3
+
4
+ // NOTE: Buffers are NOT 0-terminated!
5
+
6
+ #include "stdlib.h"
7
+ #include <stdint.h>
8
+
9
+ // TODO It may be performance-advantageous to implement this C file's semantics
10
+ // in LLVM, since the linker cannot realistically be asked to satisfactorily
11
+ // inline.
12
+ // Alternatively, one could drop gcc and combine Effekt-compiler-generated LLVM
13
+ // with clang-generated LLVM from this file's entire translation unit.
14
+
15
+
16
+ // Eight bytes for the reference counter.
17
+ // E.g. capacity could later also be stored in the header.
18
+ #define BUFFER_HEADER_WIDTH (8)
19
+
20
+ uint64_t c_buffer_length(const struct Pos buffer) {
21
+ return buffer.tag;
22
+ }
23
+
24
+ // NOTE: This assumes a homogenously used byte order.
25
+ uint64_t *c_buffer_refcount(const struct Pos buffer) {
26
+ return (uint64_t *) (buffer.obj + 0);
27
+ }
28
+
29
+ uint8_t *c_buffer_bytes(const struct Pos buffer) {
30
+ return (uint8_t *) (buffer.obj + BUFFER_HEADER_WIDTH);
31
+ }
32
+
33
+
34
+ struct Pos c_buffer_construct(const uint64_t n, const uint8_t *data) {
35
+ uint8_t *obj = malloc(BUFFER_HEADER_WIDTH + n * sizeof *obj);
36
+ ASSERT_NON_NULL(obj)
37
+
38
+ // reference count (a reference count of zero means one sole owner)
39
+ for (uint64_t j = 0; j < BUFFER_HEADER_WIDTH; ++j)
40
+ obj[j] = 0;
41
+
42
+ // data
43
+ for (uint64_t j = 0; j < n; ++j)
44
+ obj[BUFFER_HEADER_WIDTH + j] = data[j];
45
+
46
+ return (struct Pos) {
47
+ .tag = n,
48
+ .obj = obj,
49
+ };
50
+ }
51
+
52
+ void c_buffer_destruct(const struct Pos buffer) {
53
+ free(buffer.obj);
54
+ }
55
+
56
+
57
+ void c_buffer_refcount_increment(const struct Pos buffer) {
58
+ if (DEBUG_REFCOUNT) {
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
+ }
64
+
65
+ (*c_buffer_refcount(buffer))++;
66
+ }
67
+
68
+ void c_buffer_refcount_decrement(const struct Pos buffer) {
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
+ }
75
+
76
+ if (!(*c_buffer_refcount(buffer))--)
77
+ c_buffer_destruct(buffer);
78
+ }
79
+
80
+
81
+ struct Pos c_buffer_construct_zeroed(const uint64_t n) {
82
+ uint8_t *zeroes = calloc(n, sizeof *zeroes);
83
+ ASSERT_NON_NULL(zeroes)
84
+ const struct Pos buffer = c_buffer_construct(n, zeroes);
85
+ free(zeroes);
86
+ return buffer;
87
+ }
88
+
89
+ void c_buffer_truncate(struct Pos buffer, const uint64_t n) {
90
+ if (n > c_buffer_length(buffer))
91
+ return;
92
+ buffer.tag = n;
93
+ }
94
+
95
+ struct Pos c_buffer_copy(const struct Pos buffer) {
96
+ return c_buffer_construct(c_buffer_length(buffer), c_buffer_bytes(buffer));
97
+ }
98
+
99
+
100
+ // incurs a malloc: the returned pointer needs to be managed
101
+ char *c_buffer_as_null_terminated_string(const struct Pos buffer) {
102
+ // Zero runes are represented as non-minimal utf8 in the null-terminated
103
+ // string. As such, a count reveals the necessary number of bytes.
104
+ uint64_t zero_runes = 0;
105
+ for (uint64_t j = 0; j < c_buffer_length(buffer); ++j)
106
+ zero_runes += !c_buffer_bytes(buffer)[j];
107
+
108
+ const uint64_t n = c_buffer_length(buffer) + zero_runes;
109
+ char *buf = (char *) malloc((n+1) * sizeof *buf);
110
+ ASSERT_NON_NULL(buf)
111
+
112
+ uint64_t i = 0;
113
+ for (uint64_t j = 0; j < c_buffer_length(buffer); ++j) {
114
+ buf[i++] = c_buffer_bytes(buffer)[j];
115
+ if (!buf[i]) {
116
+ buf[i] = 0xc0; // 0b110.00000
117
+ buf[i++] = 0x80; // 0b10.000000
118
+ }
119
+ }
120
+
121
+ // null-terminated
122
+ buf[n] = '\00';
123
+
124
+ return buf;
125
+ }
126
+
127
+ struct Pos c_buffer_construct_from_null_terminated_string(const char *data_nt) {
128
+ uint64_t n = 0;
129
+ while (data_nt[++n]);
130
+
131
+ return c_buffer_construct(n, (uint8_t *) data_nt);
132
+ }
133
+
134
+
135
+ struct Pos c_buffer_concatenate(const struct Pos left, const struct Pos right) {
136
+ const struct Pos concatenated = c_buffer_construct_zeroed(
137
+ c_buffer_length(left) + c_buffer_length(right));
138
+ for (uint64_t j = 0; j < c_buffer_length(concatenated); ++j)
139
+ c_buffer_bytes(concatenated)[j]
140
+ = j < c_buffer_length(left)
141
+ ? c_buffer_bytes(left)[j]
142
+ : c_buffer_bytes(right)[j - c_buffer_length(left)];
143
+ return concatenated;
144
+ }
145
+
146
+ struct Pos c_buffer_eq(const struct Pos left, const struct Pos right) {
147
+ uint64_t left_len = c_buffer_length(left);
148
+ uint64_t right_len = c_buffer_length(right);
149
+ if(left_len != right_len) return BooleanFalse;
150
+ for(uint64_t j = 0; j < left_len; ++j) {
151
+ if(c_buffer_bytes(left)[j] != c_buffer_bytes(right)[j]) {
152
+ return BooleanFalse;
153
+ }
154
+ }
155
+ return BooleanTrue;
156
+ }
157
+
158
+ struct Pos c_buffer_substring(const struct Pos str, uint64_t start, uint64_t end) {
159
+ const struct Pos substr = c_buffer_construct_zeroed(end - start);
160
+ for (uint64_t j = 0; j < c_buffer_length(substr); ++j) {
161
+ c_buffer_bytes(substr)[j] = c_buffer_bytes(str)[start+j];
162
+ }
163
+ return substr;
164
+ }
165
+
166
+ struct Pos c_buffer_show_Int(const Int n) {
167
+ char str[24];
168
+ sprintf(str, "%" PRId64, n);
169
+ return c_buffer_construct_from_null_terminated_string(str);
170
+ }
171
+ struct Pos c_buffer_show_Double(const Double x) {
172
+ char str[64]; // TODO is this large enough? Possibly use snprintf first
173
+ sprintf(str, "%g", x);
174
+ return c_buffer_construct_from_null_terminated_string(str);
175
+ }
176
+
177
+ uint64_t c_buffer_index(const struct Pos str, const uint64_t index) {
178
+ return c_buffer_bytes(str)[index];
179
+ }
180
+
181
+ #endif