@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
package/README.md
CHANGED
|
@@ -11,26 +11,14 @@ aims to be significantly more lightweight in its concepts.
|
|
|
11
11
|
Also, Effekt comes with no warranty and there are (probably) many bugs -- If this does not discourage you, feel free to
|
|
12
12
|
play with it and give us your feedback :)
|
|
13
13
|
|
|
14
|
-
##
|
|
15
|
-
|
|
16
|
-
You need to have Java (>= 11) and Node (>= 12) and npm installed.
|
|
17
|
-
|
|
18
|
-
The recommended way of installing Effekt is by running:
|
|
14
|
+
## Examples
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
npm install -g https://github.com/effekt-lang/effekt/releases/latest/download/effekt.tgz
|
|
22
|
-
```
|
|
16
|
+
If you are looking for examples of the Effekt language, we invite you to look at the [casestudies](https://github.com/effekt-lang/effekt/tree/master/examples/casestudies) and the [language tour](https://effekt-lang.org/tour).
|
|
23
17
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
npm install -g effekt.tgz
|
|
28
|
-
```
|
|
18
|
+
## Installation
|
|
29
19
|
|
|
30
|
-
|
|
20
|
+
Please navigate to the [getting-started guide](https://effekt-lang.org/docs/getting-started#installing-effekt) on our [website](https://effekt-lang.org) for detailed instructions of how to install Effekt.
|
|
31
21
|
|
|
32
|
-
|
|
33
|
-
> effekt
|
|
34
|
-
```
|
|
22
|
+
## Contributing
|
|
35
23
|
|
|
36
|
-
|
|
24
|
+
Thanks for considering to contribute to the Effekt compiler! For setting up your local environment, please check out our [contributing guide](https://effekt-lang.org/contributing.html) on the [website](https://effekt-lang.org).
|
package/bin/effekt
CHANGED
|
Binary file
|
package/bin/effekt.sh
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
java -jar "$(npm root -g)/effekt/bin/effekt" $@
|
|
2
|
+
java -jar "$(npm root -g)/@effekt-lang/effekt/bin/effekt" $@
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[(number? obj) (show-number obj)]
|
|
4
4
|
[(string? obj) obj]
|
|
5
5
|
[(boolean? obj) (if obj "true" "false")]
|
|
6
|
+
[(char? obj) (show-char obj)]
|
|
6
7
|
; [(record? obj)
|
|
7
8
|
; (let* ([rtd (record-rtd obj)]
|
|
8
9
|
; [name (symbol->string (record-type-name rtd))])
|
|
@@ -21,6 +22,9 @@
|
|
|
21
22
|
(define (show-number n)
|
|
22
23
|
(if (integer? n) (number->string (exact n)) (number->string n)))
|
|
23
24
|
|
|
25
|
+
(define (show-char c)
|
|
26
|
+
(string c))
|
|
27
|
+
|
|
24
28
|
; here we use eval to find the show function defined with the record...
|
|
25
29
|
; (define (show-record rec)
|
|
26
30
|
; (let* ([rtd (record-rtd rec)]
|
|
@@ -56,8 +60,8 @@
|
|
|
56
60
|
|
|
57
61
|
|
|
58
62
|
|
|
59
|
-
(define (println_impl
|
|
60
|
-
(display
|
|
63
|
+
(define (println_impl str)
|
|
64
|
+
(display str)
|
|
61
65
|
(newline))
|
|
62
66
|
|
|
63
67
|
(define (equal_impl obj1 obj2)
|
|
@@ -79,6 +83,10 @@
|
|
|
79
83
|
(define (seconds diff)
|
|
80
84
|
(+ (time-second diff) (/ (time-nanosecond diff) 1000000000.0)))
|
|
81
85
|
|
|
86
|
+
(define (timestamp)
|
|
87
|
+
(let ([t (current-time)])
|
|
88
|
+
(+ (* (time-second t) 1000000000) (time-nanosecond t))))
|
|
89
|
+
|
|
82
90
|
(define (measure block warmup iterations)
|
|
83
91
|
(define (run n)
|
|
84
92
|
(if (<= n 0)
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
module immutable/cslist
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import list
|
|
4
|
+
import array
|
|
4
5
|
|
|
5
6
|
// a chez scheme cons list
|
|
6
7
|
extern type CSList[A]
|
|
7
8
|
extern pure def cons[A](el: A, rest: CSList[A]): CSList[A] =
|
|
8
|
-
"(cons ${el} ${rest})"
|
|
9
|
+
chez "(cons ${el} ${rest})"
|
|
9
10
|
|
|
10
11
|
extern pure def nil[A](): CSList[A] =
|
|
11
|
-
"(list)"
|
|
12
|
+
chez "(list)"
|
|
12
13
|
|
|
13
|
-
extern pure def isEmpty[A](l: CSList[A]):
|
|
14
|
-
"(null? ${l})"
|
|
14
|
+
extern pure def isEmpty[A](l: CSList[A]): Bool =
|
|
15
|
+
chez "(null? ${l})"
|
|
15
16
|
|
|
16
17
|
// unsafe!
|
|
17
18
|
extern pure def head[A](l: CSList[A]): A =
|
|
18
|
-
"(car ${l})"
|
|
19
|
+
chez "(car ${l})"
|
|
19
20
|
|
|
20
21
|
// unsafe!
|
|
21
22
|
extern pure def tail[A](l: CSList[A]): CSList[A] =
|
|
22
|
-
"(cdr ${l})"
|
|
23
|
+
chez "(cdr ${l})"
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
def toChez[A](l: List[A]): CSList[A] = l match {
|
|
@@ -29,3 +30,8 @@ def toChez[A](l: List[A]): CSList[A] = l match {
|
|
|
29
30
|
|
|
30
31
|
def fromChez[A](l: CSList[A]): List[A] =
|
|
31
32
|
if (l.isEmpty) Nil() else Cons(l.head, l.tail.fromChez)
|
|
33
|
+
|
|
34
|
+
// Old From Chez
|
|
35
|
+
// -------------
|
|
36
|
+
extern pure def toArray[A](l: CSList[A]): Array[A] =
|
|
37
|
+
chez "(list->vector ${l})"
|
|
@@ -1,30 +1,28 @@
|
|
|
1
1
|
module mutable/dict
|
|
2
2
|
|
|
3
|
-
import immutable/option
|
|
4
|
-
|
|
5
3
|
// https://cisco.github.io/ChezScheme/csug9.5/compat.html#g148
|
|
6
4
|
extern type Dict[K, V]
|
|
7
5
|
|
|
8
6
|
extern io def emptyDict[K, V](): Dict[K, V] =
|
|
9
|
-
"(make-eq-hashtable)"
|
|
7
|
+
chez "(make-eq-hashtable)"
|
|
10
8
|
|
|
11
9
|
extern io def emptyWeakDict[K, V](): Dict[K, V] =
|
|
12
|
-
"(make-hash-table #t)"
|
|
10
|
+
chez "(make-hash-table #t)"
|
|
13
11
|
|
|
14
12
|
extern io def put[K, V](d: Dict[K, V], key: K, value: V): Unit =
|
|
15
|
-
"(put-hash-table! ${d} ${key} ${value})"
|
|
13
|
+
chez "(put-hash-table! ${d} ${key} ${value})"
|
|
16
14
|
|
|
17
15
|
extern io def get[K, V](d: Dict[K, V], key: K, default: V): V =
|
|
18
|
-
"(hashtable-ref ${d} ${key} ${default})"
|
|
16
|
+
chez "(hashtable-ref ${d} ${key} ${default})"
|
|
19
17
|
|
|
20
18
|
extern io def unsafeGet[K, V](d: Dict[K, V], key: K): V =
|
|
21
|
-
"(hashtable-ref ${d} ${key} #f)"
|
|
19
|
+
chez "(hashtable-ref ${d} ${key} #f)"
|
|
22
20
|
|
|
23
|
-
extern io def contains[K, V](d: Dict[K, V], key: K):
|
|
24
|
-
"(hashtable-contains? ${d} ${key})"
|
|
21
|
+
extern io def contains[K, V](d: Dict[K, V], key: K): Bool =
|
|
22
|
+
chez "(hashtable-contains? ${d} ${key})"
|
|
25
23
|
|
|
26
24
|
extern io def remove[K, V](d: Dict[K, V], key: K): Unit =
|
|
27
|
-
"(remove-hash-table! ${d} ${key})"
|
|
25
|
+
chez "(remove-hash-table! ${d} ${key})"
|
|
28
26
|
|
|
29
27
|
def get[K, V](d: Dict[K, V], key: K): Option[V] =
|
|
30
28
|
undefinedToOption(unsafeGet(d, key))
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
module text/regex
|
|
2
2
|
|
|
3
|
-
import immutable/option
|
|
4
3
|
import immutable/cslist
|
|
5
|
-
import mutable/array
|
|
6
4
|
|
|
7
|
-
extern include "pregexp.scm"
|
|
5
|
+
extern include chez "pregexp.scm"
|
|
8
6
|
|
|
9
7
|
extern type Regex
|
|
10
8
|
|
|
11
9
|
record Match(matched: String, index: Int)
|
|
12
10
|
|
|
13
11
|
extern pure def regex(str: String): Regex =
|
|
14
|
-
"(pregexp ${str})"
|
|
12
|
+
chez "(pregexp ${str})"
|
|
15
13
|
|
|
16
14
|
def exec(reg: Regex, str: String): Option[Match] = {
|
|
17
15
|
val matched = reg.unsafeMatchString(str)
|
|
@@ -20,14 +18,14 @@ def exec(reg: Regex, str: String): Option[Match] = {
|
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
extern pure def unsafeMatchIndex(reg: Regex, str: String): Int =
|
|
23
|
-
"(let ([m (pregexp-match-positions ${reg} ${str})]) (if m (car (car m)) #f))"
|
|
21
|
+
chez "(let ([m (pregexp-match-positions ${reg} ${str})]) (if m (car (car m)) #f))"
|
|
24
22
|
|
|
25
23
|
// we ignore the captures for now and only return the whole match
|
|
26
24
|
extern pure def unsafeMatchString(reg: Regex, str: String): String =
|
|
27
|
-
"(let ([m (pregexp-match ${reg} ${str})]) (if m (car m) #f))"
|
|
25
|
+
chez "(let ([m (pregexp-match ${reg} ${str})]) (if m (car m) #f))"
|
|
28
26
|
|
|
29
27
|
extern pure def split(reg: Regex, str: String): CSList[String] =
|
|
30
|
-
"(pregexp-split ${reg} ${str})"
|
|
28
|
+
chez "(pregexp-split ${reg} ${str})"
|
|
31
29
|
|
|
32
|
-
def split(str: String, sep: String): Array[String] =
|
|
33
|
-
|
|
30
|
+
// def split(str: String, sep: String): Array[String] =
|
|
31
|
+
// toArray(split(sep.regex, str))
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module args
|
|
2
|
+
|
|
3
|
+
extern io def commandLineArgs(): List[String] =
|
|
4
|
+
js { js::commandLineArgs() }
|
|
5
|
+
chez { chez::commandLineArgs() }
|
|
6
|
+
llvm { llvm::commandLineArgs() }
|
|
7
|
+
|
|
8
|
+
namespace js {
|
|
9
|
+
extern type Args // = Array[String]
|
|
10
|
+
|
|
11
|
+
extern io def nativeArgs(): Args = js "process.argv.slice(1)"
|
|
12
|
+
|
|
13
|
+
extern io def argCount(args: Args): Int = js "${args}.length"
|
|
14
|
+
|
|
15
|
+
extern io def argument(args: Args, i: Int): String = js "${args}[${i}]"
|
|
16
|
+
|
|
17
|
+
def commandLineArgs(): List[String] = {
|
|
18
|
+
def toList(args: Args, start: Int, acc: List[String]): List[String] =
|
|
19
|
+
if (start < 1) { acc }
|
|
20
|
+
else args.toList(start - 1, Cons(args.argument(start), acc))
|
|
21
|
+
|
|
22
|
+
val args = nativeArgs()
|
|
23
|
+
args.toList(args.argCount - 1, Nil())
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
namespace chez {
|
|
28
|
+
extern type Args // = CSList[String]
|
|
29
|
+
|
|
30
|
+
extern io def nativeArgs(): Args =
|
|
31
|
+
chez "(cdr (command-line))"
|
|
32
|
+
|
|
33
|
+
extern pure def isEmpty(l: Args): Bool =
|
|
34
|
+
chez "(null? ${l})"
|
|
35
|
+
|
|
36
|
+
extern pure def head(l: Args): String =
|
|
37
|
+
chez "(car ${l})"
|
|
38
|
+
|
|
39
|
+
extern pure def tail(l: Args): Args =
|
|
40
|
+
chez "(cdr ${l})"
|
|
41
|
+
|
|
42
|
+
def toList(l: Args): List[String] =
|
|
43
|
+
if (l.isEmpty) Nil() else Cons(l.head, l.tail.toList)
|
|
44
|
+
|
|
45
|
+
def commandLineArgs(): List[String] = nativeArgs().toList
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
namespace llvm {
|
|
49
|
+
extern io def argCount(): Int =
|
|
50
|
+
llvm """
|
|
51
|
+
%c = call %Int @c_get_argc()
|
|
52
|
+
ret %Int %c
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
extern io def argument(i: Int): String =
|
|
56
|
+
llvm """
|
|
57
|
+
%s = call %Pos @c_get_arg(%Int ${i})
|
|
58
|
+
ret %Pos %s
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def commandLineArgs(): List[String] = {
|
|
62
|
+
def toList(start: Int, acc: List[String]): List[String] =
|
|
63
|
+
if (start < 1) { acc }
|
|
64
|
+
else toList(start - 1, Cons(argument(start), acc))
|
|
65
|
+
|
|
66
|
+
toList(argCount() - 1, Nil())
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
module array
|
|
2
|
+
|
|
3
|
+
import effekt
|
|
4
|
+
import exception
|
|
5
|
+
import list
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A mutable 0-indexed fixed-sized array.
|
|
9
|
+
*/
|
|
10
|
+
extern type Array[T]
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Allocates a new array of size `size`, keeping its values _undefined_.
|
|
14
|
+
* Prefer using `array` constructor instead to ensure that values are defined.
|
|
15
|
+
*/
|
|
16
|
+
extern global def allocate[T](size: Int): Array[T] =
|
|
17
|
+
js "(new Array(${size}))"
|
|
18
|
+
chez "(make-vector ${size})" // creates an array filled with 0s on CS
|
|
19
|
+
llvm """
|
|
20
|
+
%z = call %Pos @c_array_new(%Int ${size})
|
|
21
|
+
ret %Pos %z
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Creates a new Array of size `size` filled with the value `init`
|
|
26
|
+
*/
|
|
27
|
+
def array[T](size: Int, init: T): Array[T] = {
|
|
28
|
+
val arr = allocate[T](size);
|
|
29
|
+
each(0, size) { i =>
|
|
30
|
+
unsafeSet(arr, i, init)
|
|
31
|
+
};
|
|
32
|
+
arr
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Converts a List `list` to an Array
|
|
37
|
+
*/
|
|
38
|
+
def fromList[T](list: List[T]): Array[T] = {
|
|
39
|
+
val listSize = list.size();
|
|
40
|
+
val arr = allocate(listSize);
|
|
41
|
+
|
|
42
|
+
foreachIndex(list) { (i, head) =>
|
|
43
|
+
arr.unsafeSet(i, head)
|
|
44
|
+
}
|
|
45
|
+
return arr;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Gets the length of the array in constant time.
|
|
50
|
+
*/
|
|
51
|
+
extern pure def size[T](arr: Array[T]): Int =
|
|
52
|
+
js "${arr}.length"
|
|
53
|
+
chez "(vector-length ${arr})"
|
|
54
|
+
llvm """
|
|
55
|
+
%z = call %Int @c_array_size(%Pos ${arr})
|
|
56
|
+
ret %Int %z
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Gets the element of the `arr` at given `index` in constant time.
|
|
61
|
+
* Unchecked Precondition: `index` is in bounds (0 ≤ index < arr.size)
|
|
62
|
+
*
|
|
63
|
+
* Prefer using `get` instead.
|
|
64
|
+
*/
|
|
65
|
+
extern global def unsafeGet[T](arr: Array[T], index: Int): T =
|
|
66
|
+
js "${arr}[${index}]"
|
|
67
|
+
chez "(vector-ref ${arr} ${index})"
|
|
68
|
+
llvm """
|
|
69
|
+
%z = call %Pos @c_array_get(%Pos ${arr}, %Int ${index})
|
|
70
|
+
ret %Pos %z
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
extern js """
|
|
74
|
+
function array$set(arr, index, value) {
|
|
75
|
+
arr[index] = value;
|
|
76
|
+
return $effekt.unit
|
|
77
|
+
}
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Sets the element of the `arr` at given `index` to `value` in constant time.
|
|
82
|
+
* Unchecked Precondition: `index` is in bounds (0 ≤ index < arr.size)
|
|
83
|
+
*
|
|
84
|
+
* Prefer using `set` instead.
|
|
85
|
+
*/
|
|
86
|
+
extern global def unsafeSet[T](arr: Array[T], index: Int, value: T): Unit =
|
|
87
|
+
js "array$set(${arr}, ${index}, ${value})"
|
|
88
|
+
chez "(begin (vector-set! ${arr} ${index} ${value}) #f)"
|
|
89
|
+
llvm """
|
|
90
|
+
%z = call %Pos @c_array_set(%Pos ${arr}, %Int ${index}, %Pos ${value})
|
|
91
|
+
ret %Pos %z
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Creates a copy of `arr`
|
|
96
|
+
*/
|
|
97
|
+
def copy[T](arr: Array[T]): Array[T] = {
|
|
98
|
+
with on[OutOfBounds].default { <> }; // should not happen
|
|
99
|
+
val len = arr.size;
|
|
100
|
+
val newArray = allocate[T](len);
|
|
101
|
+
copy[T](arr, 0, newArray, 0, len);
|
|
102
|
+
newArray
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Copies `length`-many elements from `from` to `to`
|
|
107
|
+
* starting at `start` (in `from`) and `offset` (in `to`)
|
|
108
|
+
*/
|
|
109
|
+
def copy[T](from: Array[T], start: Int, to: Array[T], offset: Int, length: Int): Unit / Exception[OutOfBounds] = {
|
|
110
|
+
val startValid = start >= 0 && start + length <= from.size
|
|
111
|
+
val offsetValid = offset >= 0 && offset + length <= to.size
|
|
112
|
+
|
|
113
|
+
def go(i: Int, j: Int, length: Int): Unit =
|
|
114
|
+
if (length > 0) {
|
|
115
|
+
to.unsafeSet(j, from.unsafeGet(i))
|
|
116
|
+
go(i + 1, j + 1, length - 1)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (startValid && offsetValid) go(start, offset, length)
|
|
120
|
+
else do raise(OutOfBounds(), "Array index out of bounds, when copying")
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Derived operations:
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Gets the element of the `arr` at given `index` in constant time,
|
|
127
|
+
* throwing an `Exception[OutOfBounds]` unless `0 ≤ index < arr.size`.
|
|
128
|
+
*/
|
|
129
|
+
def get[T](arr: Array[T], index: Int): T / Exception[OutOfBounds] =
|
|
130
|
+
if (index >= 0 && index < arr.size) arr.unsafeGet(index)
|
|
131
|
+
else do raise(OutOfBounds(), "Array index out of bounds: " ++ show(index))
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Sets the element of the `arr` at given `index` to `value` in constant time,
|
|
135
|
+
* throwing an `Exception[OutOfBounds]` unless `0 ≤ index < arr.size`.
|
|
136
|
+
*/
|
|
137
|
+
def set[T](arr: Array[T], index: Int, value: T): Unit / Exception[OutOfBounds] =
|
|
138
|
+
if (index >= 0 && index < arr.size) unsafeSet(arr, index, value)
|
|
139
|
+
else do raise(OutOfBounds(), "Array index out of bounds: " ++ show(index))
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Builds a new Array of size `size` from a computation `index` which gets an index
|
|
143
|
+
* and returns a value that will be on that position in the resulting array
|
|
144
|
+
*/
|
|
145
|
+
def build[T](size: Int) { index: Int => T }: Array[T] = {
|
|
146
|
+
val arr = allocate[T](size);
|
|
147
|
+
each(0, size) { i =>
|
|
148
|
+
unsafeSet(arr, i, index(i))
|
|
149
|
+
};
|
|
150
|
+
arr
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Utility functions:
|
|
154
|
+
|
|
155
|
+
def toList[T](arr: Array[T]): List[T] = {
|
|
156
|
+
var i = arr.size - 1;
|
|
157
|
+
var l = Nil[T]()
|
|
158
|
+
while (i >= 0) {
|
|
159
|
+
l = Cons(arr.unsafeGet(i), l)
|
|
160
|
+
i = i - 1
|
|
161
|
+
}
|
|
162
|
+
l
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
def foreach[T](arr: Array[T]){ action: T => Unit }: Unit =
|
|
166
|
+
each(0, arr.size) { i =>
|
|
167
|
+
val x: T = arr.unsafeGet(i)
|
|
168
|
+
action(x)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
def foreach[T](arr: Array[T]){ action: (T) {Control} => Unit }: Unit =
|
|
172
|
+
each(0, arr.size) { (i) {label} =>
|
|
173
|
+
val x: T = arr.unsafeGet(i)
|
|
174
|
+
action(x) {label}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
def foreachIndex[T](arr: Array[T]){ action: (Int, T) => Unit }: Unit =
|
|
178
|
+
each(0, arr.size) { i =>
|
|
179
|
+
val x: T = arr.unsafeGet(i)
|
|
180
|
+
action(i, x)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
def foreachIndex[T](arr: Array[T]){ action: (Int, T) {Control} => Unit }: Unit =
|
|
184
|
+
each(0, arr.size) { (i) {label} =>
|
|
185
|
+
val x: T = arr.unsafeGet(i)
|
|
186
|
+
action(i, x) {label}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
def sum(list: Array[Int]): Int = {
|
|
190
|
+
var acc = 0
|
|
191
|
+
list.foreach { x =>
|
|
192
|
+
acc = acc + x
|
|
193
|
+
}
|
|
194
|
+
acc
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Show Instances
|
|
198
|
+
// --------------
|
|
199
|
+
|
|
200
|
+
def show[A](arr: Array[A]) { showA: A => String }: String = {
|
|
201
|
+
var output = "Array("
|
|
202
|
+
val lastIndex = arr.size - 1
|
|
203
|
+
|
|
204
|
+
arr.foreachIndex { (index, a) =>
|
|
205
|
+
if (index == lastIndex) output = output ++ showA(a)
|
|
206
|
+
else output = output ++ showA(a) ++ ", "
|
|
207
|
+
}
|
|
208
|
+
output = output ++ ")"
|
|
209
|
+
|
|
210
|
+
output
|
|
211
|
+
}
|
|
212
|
+
def show(l: Array[Int]): String = show(l) { e => show(e) }
|
|
213
|
+
def show(l: Array[Double]): String = show(l) { e => show(e) }
|
|
214
|
+
def show(l: Array[Bool]): String = show(l) { e => show(e) }
|
|
215
|
+
def show(l: Array[String]): String = show(l) { e => e }
|
|
216
|
+
|
|
217
|
+
def println(l: Array[Int]): Unit = println(show(l))
|
|
218
|
+
def println(l: Array[Double]): Unit = println(show(l))
|
|
219
|
+
def println(l: Array[Bool]): Unit = println(show(l))
|
|
220
|
+
def println(l: Array[String]): Unit = println(show(l))
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module bench
|
|
2
|
+
|
|
3
|
+
type Nanos = Int
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The current time (since UNIX Epoch) in nanoseconds.
|
|
7
|
+
*
|
|
8
|
+
* The actual precision varies across the different backends.
|
|
9
|
+
* - js: Milliseconds
|
|
10
|
+
* - chez: Microseconds
|
|
11
|
+
*/
|
|
12
|
+
extern io def timestamp(): Nanos =
|
|
13
|
+
js "Date.now() * 1000000"
|
|
14
|
+
chez "(timestamp)"
|
|
15
|
+
llvm """
|
|
16
|
+
%time_ptr = alloca { i64, i64 }
|
|
17
|
+
call i32 @clock_gettime(i32 0, ptr %time_ptr)
|
|
18
|
+
%time = load {i64, i64}, ptr %time_ptr
|
|
19
|
+
%time_seconds = extractvalue {i64, i64} %time, 0
|
|
20
|
+
%time_nanoseconds = extractvalue {i64, i64} %time, 1
|
|
21
|
+
%time_seconds_nanoseconds = mul nsw i64 %time_seconds, 1000000000
|
|
22
|
+
%result = add nsw i64 %time_seconds_nanoseconds, %time_nanoseconds
|
|
23
|
+
ret %Int %result
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
extern llvm """
|
|
27
|
+
declare i32 @clock_gettime(i32, ptr)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
// This should not be needed when using NodeJS 16.x or newer.
|
|
31
|
+
extern jsNode """
|
|
32
|
+
const { performance } = require('node:perf_hooks');
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* High-precision timestamp in nanoseconds that should be for measurements.
|
|
37
|
+
*
|
|
38
|
+
* This timestamp should only be used for **relative** measurements,
|
|
39
|
+
* as gives no guarantees on the absolute time (unlike a UNIX timestamp).
|
|
40
|
+
*/
|
|
41
|
+
extern io def relativeTimestamp(): Nanos =
|
|
42
|
+
js "Math.round(performance.now() * 1000000)"
|
|
43
|
+
default { timestamp() }
|
|
44
|
+
|
|
45
|
+
type Duration = Int
|
|
46
|
+
|
|
47
|
+
namespace Duration {
|
|
48
|
+
def diff(fromNanos: Nanos, toNanos: Nanos): Duration = toNanos - fromNanos
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Runs the block and returns the time in nanoseconds
|
|
53
|
+
*/
|
|
54
|
+
def timed { block: => Unit }: Duration = {
|
|
55
|
+
val before = relativeTimestamp()
|
|
56
|
+
block()
|
|
57
|
+
val after = relativeTimestamp()
|
|
58
|
+
Duration::diff(before, after)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
def measure(warmup: Int, iterations: Int) { block: => Unit }: Unit = {
|
|
62
|
+
def run(n: Int, report: Bool): Unit = {
|
|
63
|
+
if (n <= 0) { () } else {
|
|
64
|
+
val time = timed { block() };
|
|
65
|
+
if (report) { println(time) } else { () };
|
|
66
|
+
run(n - 1, report)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
run(warmup, false)
|
|
70
|
+
run(iterations, true)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Takes a duration in nanoseconds and formats it in milliseconds with precision of two decimal digits.
|
|
75
|
+
*/
|
|
76
|
+
def formatMs(nanos: Nanos): String = {
|
|
77
|
+
val micros = nanos / 1000000
|
|
78
|
+
val sub = (nanos.mod(1000000).toDouble / 10000.0).round
|
|
79
|
+
|
|
80
|
+
if (sub == 100) {
|
|
81
|
+
// overflow because of rounding
|
|
82
|
+
(micros + 1).show ++ ".0ms"
|
|
83
|
+
} else {
|
|
84
|
+
micros.show ++ "." ++ sub.show ++ "ms"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
module buffer
|
|
2
|
+
|
|
3
|
+
import array
|
|
4
|
+
|
|
5
|
+
// TODO (in Effekt compiler)
|
|
6
|
+
// - [ ] fix allocating into actually global region
|
|
7
|
+
// - [ ] fix exceptions on objects
|
|
8
|
+
// - [X] allow omitting braces after `at` for singleton regions
|
|
9
|
+
|
|
10
|
+
record BufferOverflow()
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Fixed-size ring buffers (queues) where one can write
|
|
14
|
+
* elements to the head and read from the tail.
|
|
15
|
+
*/
|
|
16
|
+
interface Buffer[T] {
|
|
17
|
+
/**
|
|
18
|
+
* The remaining capacity
|
|
19
|
+
*/
|
|
20
|
+
def capacity(): Int
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Is this buffer full?
|
|
24
|
+
*/
|
|
25
|
+
def full?(): Bool
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Is this buffer empty?
|
|
29
|
+
*/
|
|
30
|
+
def empty?(): Bool
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Read an element from the tail of the buffer
|
|
34
|
+
*/
|
|
35
|
+
def read(): Option[T]
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Write an element to the head of the buffer
|
|
39
|
+
*/
|
|
40
|
+
def write(el: T): Unit / Exception[BufferOverflow]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
def emptyBuffer[T](capacity: Int): Buffer[T] at {global} = {
|
|
44
|
+
if (capacity == 1) refBuffer()
|
|
45
|
+
else arrayBuffer(capacity)
|
|
46
|
+
}
|
|
47
|
+
def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
|
|
48
|
+
// TODO allocate buffer (and array) into a region r.
|
|
49
|
+
val contents = emptyArray[T](initialCapacity)
|
|
50
|
+
var head in global = 0
|
|
51
|
+
var tail in global = 0
|
|
52
|
+
|
|
53
|
+
def size(): Int =
|
|
54
|
+
if (tail >= head) { tail - head }
|
|
55
|
+
else { initialCapacity - head + tail }
|
|
56
|
+
|
|
57
|
+
def capacity(): Int = initialCapacity - size()
|
|
58
|
+
|
|
59
|
+
def buffer = new Buffer[T] {
|
|
60
|
+
def capacity() = capacity()
|
|
61
|
+
def full?() = capacity() <= 0
|
|
62
|
+
def empty?() = size() <= 0
|
|
63
|
+
def read() = {
|
|
64
|
+
if (buffer.empty?) None()
|
|
65
|
+
else {
|
|
66
|
+
val result: T = contents.remove(head).getOrElse { <> };
|
|
67
|
+
head = mod(head + 1, initialCapacity)
|
|
68
|
+
Some(result)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
def write(el: T) = {
|
|
72
|
+
if (buffer.full?) <> // raise(BufferOverflow())
|
|
73
|
+
|
|
74
|
+
contents.put(tail, el)
|
|
75
|
+
tail = mod(tail + 1, initialCapacity)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
buffer
|
|
79
|
+
}
|
|
80
|
+
def refBuffer[T](): Buffer[T] at {global} = {
|
|
81
|
+
var content: Option[T] in global = None()
|
|
82
|
+
new Buffer[T] {
|
|
83
|
+
def capacity() = if (content.isEmpty) 1 else 0
|
|
84
|
+
def full?() = content.isDefined
|
|
85
|
+
def empty?() = isEmpty(content)
|
|
86
|
+
def read() = {
|
|
87
|
+
val res = content
|
|
88
|
+
content = None()
|
|
89
|
+
res
|
|
90
|
+
}
|
|
91
|
+
def write(el: T) = content match {
|
|
92
|
+
case Some(v) =>
|
|
93
|
+
<> // do raise(BufferOverflow(), "Cannot read element from buffer")
|
|
94
|
+
case None() =>
|
|
95
|
+
content = Some(el)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
namespace examples {
|
|
101
|
+
def main() = ignore[BufferOverflow] {
|
|
102
|
+
// Buffer with capacity 1
|
|
103
|
+
def b = emptyBuffer[Int](1);
|
|
104
|
+
println(b.capacity);
|
|
105
|
+
println(b.full?);
|
|
106
|
+
|
|
107
|
+
b.write(17);
|
|
108
|
+
println(b.read());
|
|
109
|
+
|
|
110
|
+
// buffer with capacity 3
|
|
111
|
+
def ringbuffer = emptyBuffer[Int](3);
|
|
112
|
+
ringbuffer.write(1);
|
|
113
|
+
ringbuffer.write(2);
|
|
114
|
+
println(ringbuffer.read());
|
|
115
|
+
ringbuffer.write(3);
|
|
116
|
+
println(ringbuffer.read());
|
|
117
|
+
println(ringbuffer.read());
|
|
118
|
+
ringbuffer.write(4);
|
|
119
|
+
ringbuffer.write(5);
|
|
120
|
+
println(ringbuffer.read());
|
|
121
|
+
println(ringbuffer.read());
|
|
122
|
+
}
|
|
123
|
+
}
|