@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.
- package/bin/effekt +0 -0
- package/libraries/common/buffer.effekt +3 -3
- package/libraries/common/bytearray.effekt +64 -1
- package/libraries/common/effekt.effekt +45 -10
- package/libraries/common/heap.effekt +103 -0
- package/libraries/common/io/filesystem.effekt +49 -66
- package/libraries/common/io/network.effekt +8 -5
- package/libraries/common/json.effekt +30 -22
- package/libraries/common/map.effekt +992 -0
- package/libraries/common/regex.effekt +64 -0
- package/libraries/common/resizable_array.effekt +235 -0
- package/libraries/common/set.effekt +234 -0
- package/libraries/common/stream.effekt +47 -2
- package/libraries/common/string.effekt +7 -0
- package/libraries/common/stringbuffer.effekt +65 -0
- package/libraries/common/union_find.effekt +113 -0
- package/libraries/llvm/bytearray.c +36 -7
- package/libraries/llvm/forward-declare-c.ll +2 -0
- package/libraries/llvm/io.c +17 -43
- package/libraries/llvm/main.c +2 -1
- package/libraries/llvm/panic.c +20 -0
- package/libraries/llvm/rts.ll +141 -142
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +4 -4
- package/licenses/mit license - mit-license.html +82 -18
- package/licenses/the bsd license - bsd-license.html +81 -17
- package/licenses/the mit license - mit.html +81 -17
- package/package.json +1 -1
- package/libraries/chez/common/text/regex.effekt +0 -31
- package/libraries/js/text/regex.effekt +0 -33
- package/libraries/llvm/hole.c +0 -11
package/bin/effekt
CHANGED
|
Binary file
|
|
@@ -34,7 +34,7 @@ def emptyBuffer[T](capacity: Int): Buffer[T] at {global} = {
|
|
|
34
34
|
}
|
|
35
35
|
def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
|
|
36
36
|
// TODO allocate buffer (and array) into a region r.
|
|
37
|
-
val contents =
|
|
37
|
+
val contents = array::allocate[T](initialCapacity)
|
|
38
38
|
var head in global = 0
|
|
39
39
|
var tail in global = 0
|
|
40
40
|
|
|
@@ -51,7 +51,7 @@ def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
|
|
|
51
51
|
def read() = {
|
|
52
52
|
if (buffer.empty?) None()
|
|
53
53
|
else {
|
|
54
|
-
val result: T = contents.
|
|
54
|
+
val result: T = contents.unsafeGet(head);
|
|
55
55
|
head = mod(head + 1, initialCapacity)
|
|
56
56
|
Some(result)
|
|
57
57
|
}
|
|
@@ -59,7 +59,7 @@ def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
|
|
|
59
59
|
def write(el: T) = {
|
|
60
60
|
if (buffer.full?) <> // raise(BufferOverflow())
|
|
61
61
|
|
|
62
|
-
contents.
|
|
62
|
+
contents.unsafeSet(tail, el)
|
|
63
63
|
tail = mod(tail + 1, initialCapacity)
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -6,7 +6,7 @@ module bytearray
|
|
|
6
6
|
extern type ByteArray
|
|
7
7
|
// = llvm "%Pos"
|
|
8
8
|
// = js "Uint8Array"
|
|
9
|
-
|
|
9
|
+
// = chez "bytevector"
|
|
10
10
|
|
|
11
11
|
/// Allocates a new bytearray with the given `size`, its values are undefined.
|
|
12
12
|
extern global def allocate(size: Int): ByteArray =
|
|
@@ -15,6 +15,7 @@ extern global def allocate(size: Int): ByteArray =
|
|
|
15
15
|
%arr = call %Pos @c_bytearray_new(%Int ${size})
|
|
16
16
|
ret %Pos %arr
|
|
17
17
|
"""
|
|
18
|
+
chez "(make-bytevector ${size})"
|
|
18
19
|
|
|
19
20
|
extern pure def size(arr: ByteArray): Int =
|
|
20
21
|
js "${arr}.length"
|
|
@@ -22,6 +23,7 @@ extern pure def size(arr: ByteArray): Int =
|
|
|
22
23
|
%size = call %Int @c_bytearray_size(%Pos ${arr})
|
|
23
24
|
ret %Int %size
|
|
24
25
|
"""
|
|
26
|
+
chez "(bytevector-length ${arr})"
|
|
25
27
|
|
|
26
28
|
extern global def unsafeGet(arr: ByteArray, index: Int): Byte =
|
|
27
29
|
js "(${arr})[${index}]"
|
|
@@ -29,6 +31,7 @@ extern global def unsafeGet(arr: ByteArray, index: Int): Byte =
|
|
|
29
31
|
%byte = call %Byte @c_bytearray_get(%Pos ${arr}, %Int ${index})
|
|
30
32
|
ret %Byte %byte
|
|
31
33
|
"""
|
|
34
|
+
chez "(bytevector-u8-ref ${arr} ${index})"
|
|
32
35
|
|
|
33
36
|
extern global def unsafeSet(arr: ByteArray, index: Int, value: Byte): Unit =
|
|
34
37
|
js "bytearray$set(${arr}, ${index}, ${value})"
|
|
@@ -36,6 +39,7 @@ extern global def unsafeSet(arr: ByteArray, index: Int, value: Byte): Unit =
|
|
|
36
39
|
%z = call %Pos @c_bytearray_set(%Pos ${arr}, %Int ${index}, %Byte ${value})
|
|
37
40
|
ret %Pos %z
|
|
38
41
|
"""
|
|
42
|
+
chez "(bytevector-u8-set! ${arr} ${index} ${value})"
|
|
39
43
|
|
|
40
44
|
def resize(source: ByteArray, size: Int): ByteArray = {
|
|
41
45
|
val target = allocate(size)
|
|
@@ -75,6 +79,7 @@ extern pure def fromString(str: String): ByteArray =
|
|
|
75
79
|
llvm """
|
|
76
80
|
ret %Pos ${str}
|
|
77
81
|
"""
|
|
82
|
+
chez "(string->utf8 ${str})"
|
|
78
83
|
|
|
79
84
|
extern pure def toString(arr: ByteArray): String =
|
|
80
85
|
js "(new TextDecoder('utf-8').decode(${arr}))"
|
|
@@ -82,10 +87,68 @@ extern pure def toString(arr: ByteArray): String =
|
|
|
82
87
|
llvm """
|
|
83
88
|
ret %Pos ${arr}
|
|
84
89
|
"""
|
|
90
|
+
chez "(utf8->string ${arr})"
|
|
85
91
|
|
|
86
92
|
extern js """
|
|
87
93
|
function bytearray$set(bytes, index, value) {
|
|
88
94
|
bytes[index] = value;
|
|
89
95
|
return $effekt.unit;
|
|
90
96
|
}
|
|
97
|
+
|
|
98
|
+
function bytearray$compare(arr1, arr2) {
|
|
99
|
+
const len = Math.min(arr1.length, arr2.length);
|
|
100
|
+
|
|
101
|
+
for (let i = 0; i < len; i++) {
|
|
102
|
+
if (arr1[i] !== arr2[i]) {
|
|
103
|
+
return arr1[i] < arr2[i] ? -1 : 1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (arr1.length !== arr2.length) {
|
|
108
|
+
return arr1.length < arr2.length ? -1 : 1;
|
|
109
|
+
} else {
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
extern chez """
|
|
116
|
+
(define (bytearray$compare b1 b2)
|
|
117
|
+
(let ([len1 (bytevector-length b1)]
|
|
118
|
+
[len2 (bytevector-length b2)]
|
|
119
|
+
[minlen (min (bytevector-length b1) (bytevector-length b2))])
|
|
120
|
+
(let loop ([i 0])
|
|
121
|
+
(cond
|
|
122
|
+
[(= i minlen)
|
|
123
|
+
(cond [(< len1 len2) -1]
|
|
124
|
+
[(> len1 len2) 1]
|
|
125
|
+
[else 0])]
|
|
126
|
+
[(< (bytevector-u8-ref b1 i) (bytevector-u8-ref b2 i)) -1]
|
|
127
|
+
[(> (bytevector-u8-ref b1 i) (bytevector-u8-ref b2 i)) 1]
|
|
128
|
+
[else (loop (+ i 1))]))))
|
|
91
129
|
"""
|
|
130
|
+
|
|
131
|
+
extern pure def compareByteArrayImpl(b1: ByteArray, b2: ByteArray): Int =
|
|
132
|
+
js "bytearray$compare(${b1}, ${b2})"
|
|
133
|
+
llvm """
|
|
134
|
+
%x = call %Int @c_bytearray_compare(%Pos ${b1}, %Pos ${b2})
|
|
135
|
+
ret %Int %x
|
|
136
|
+
"""
|
|
137
|
+
chez "(bytearray$compare ${b1} ${b2})"
|
|
138
|
+
|
|
139
|
+
def compareByteArray(b1: ByteArray, b2: ByteArray): Ordering = {
|
|
140
|
+
val ret = compareByteArrayImpl(b1, b2)
|
|
141
|
+
if (ret == 0) {
|
|
142
|
+
Equal()
|
|
143
|
+
} else if (ret < 0) {
|
|
144
|
+
Less()
|
|
145
|
+
} else { // ret > 0
|
|
146
|
+
Greater()
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
def compareStringBytes(left: String, right: String): Ordering = {
|
|
151
|
+
val l = left.fromString
|
|
152
|
+
val r = right.fromString
|
|
153
|
+
compareByteArray(l, r)
|
|
154
|
+
}
|
|
@@ -90,6 +90,7 @@ extern pure def show(value: Char): String =
|
|
|
90
90
|
|
|
91
91
|
extern pure def show(value: Byte): String =
|
|
92
92
|
js "'' + ${value}"
|
|
93
|
+
chez "(string ${value})"
|
|
93
94
|
llvm """
|
|
94
95
|
%z = call %Pos @c_bytearray_show_Byte(i8 ${value})
|
|
95
96
|
ret %Pos %z
|
|
@@ -166,17 +167,24 @@ type Ordering {
|
|
|
166
167
|
Greater()
|
|
167
168
|
}
|
|
168
169
|
|
|
170
|
+
def compareInt(n: Int, m: Int) =
|
|
171
|
+
if (n == m) Equal()
|
|
172
|
+
else if (n < m) Less()
|
|
173
|
+
else Greater()
|
|
174
|
+
|
|
169
175
|
extern pure def genericCompareImpl[R](x: R, y: R): Int =
|
|
170
176
|
js "$effekt.compare(${x}, ${y})"
|
|
171
177
|
|
|
172
178
|
/// Compares two values of the same type, returning an `Ordering`.
|
|
173
179
|
/// Only available on the JavaScript backend.
|
|
174
180
|
def genericCompare[R](x: R, y: R): Ordering = {
|
|
175
|
-
genericCompareImpl(x, y)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
181
|
+
val ret = genericCompareImpl(x, y)
|
|
182
|
+
if (ret == 0) {
|
|
183
|
+
Equal()
|
|
184
|
+
} else if (ret < 0) {
|
|
185
|
+
Less()
|
|
186
|
+
} else { // ret > 0
|
|
187
|
+
Greater()
|
|
180
188
|
}
|
|
181
189
|
}
|
|
182
190
|
|
|
@@ -390,7 +398,7 @@ extern pure def log(x: Double): Double =
|
|
|
390
398
|
extern pure def log1p(x: Double): Double =
|
|
391
399
|
js "Math.log1p(${x})"
|
|
392
400
|
chez "(log (+ ${x} 1))"
|
|
393
|
-
llvm "%z = call %Double @log1p(double ${x}) ret %Double %z"
|
|
401
|
+
llvm "%z = call %Double @log1p(double ${x}) ret %Double %z"
|
|
394
402
|
|
|
395
403
|
extern pure def exp(x: Double): Double =
|
|
396
404
|
js "Math.exp(${x})"
|
|
@@ -571,19 +579,19 @@ extern pure def infixGte(x: Double, y: Double): Bool =
|
|
|
571
579
|
// TODO do we really need those? if yes, move to string.effekt
|
|
572
580
|
extern pure def infixLt(x: String, y: String): Bool =
|
|
573
581
|
js "(${x} < ${y})"
|
|
574
|
-
chez "(
|
|
582
|
+
chez "(string<? ${x} ${y})"
|
|
575
583
|
|
|
576
584
|
extern pure def infixLte(x: String, y: String): Bool =
|
|
577
585
|
js "(${x} <= ${y})"
|
|
578
|
-
chez "(
|
|
586
|
+
chez "(string<=? ${x} ${y})"
|
|
579
587
|
|
|
580
588
|
extern pure def infixGt(x: String, y: String): Bool =
|
|
581
589
|
js "(${x} > ${y})"
|
|
582
|
-
chez "(
|
|
590
|
+
chez "(string>? ${x} ${y})"
|
|
583
591
|
|
|
584
592
|
extern pure def infixGte(x: String, y: String): Bool =
|
|
585
593
|
js "(${x} >= ${y})"
|
|
586
|
-
chez "(
|
|
594
|
+
chez "(string>=? ${x} ${y})"
|
|
587
595
|
|
|
588
596
|
|
|
589
597
|
// Boolean operations
|
|
@@ -647,10 +655,13 @@ extern pure def bitwiseXor(x: Int, y: Int): Int =
|
|
|
647
655
|
extern pure def toByte(n: Int): Byte =
|
|
648
656
|
js "${n}"
|
|
649
657
|
llvm "%z = trunc %Int ${n} to %Byte ret %Byte %z"
|
|
658
|
+
chez "${n}"
|
|
650
659
|
|
|
651
660
|
extern pure def toInt(n: Byte): Int =
|
|
652
661
|
js "${n}"
|
|
653
662
|
llvm "%z = zext %Byte ${n} to %Int ret %Int %z"
|
|
663
|
+
vm "effekt::toInt(Byte)"
|
|
664
|
+
chez "${n}"
|
|
654
665
|
|
|
655
666
|
|
|
656
667
|
// Undefined and Null
|
|
@@ -666,11 +677,13 @@ extern pure def toInt(n: Byte): Int =
|
|
|
666
677
|
extern pure def undefined[A](): A =
|
|
667
678
|
js "undefined"
|
|
668
679
|
chez "#f"
|
|
680
|
+
vm "effekt::undefined()"
|
|
669
681
|
|
|
670
682
|
/// Is an FFI value undefined?
|
|
671
683
|
extern pure def isUndefined[A](value: A): Bool =
|
|
672
684
|
js "(${value} === undefined || ${value} === null)"
|
|
673
685
|
chez "(eq? ${value} #f)"
|
|
686
|
+
vm "effekt::isUndefined[A](A)"
|
|
674
687
|
|
|
675
688
|
|
|
676
689
|
// Tuples
|
|
@@ -723,6 +736,28 @@ def repeat(n: Int) { action: () => Unit } = each(0, n) { n => action() }
|
|
|
723
736
|
|
|
724
737
|
def repeat(n: Int) { action: () {Control} => Unit } = each(0, n) { (n) {l} => action() {l} }
|
|
725
738
|
|
|
739
|
+
|
|
740
|
+
// Splices
|
|
741
|
+
// =======
|
|
742
|
+
//
|
|
743
|
+
// The Effekt compiler translates the string interpolation
|
|
744
|
+
//
|
|
745
|
+
// "foo ${42} bar ${43}"
|
|
746
|
+
//
|
|
747
|
+
// to the following stream of `literal`s and `splice[Int]`s:
|
|
748
|
+
//
|
|
749
|
+
// do literal("foo "); do splice(42); do literal(" bar "); do splice(43)
|
|
750
|
+
//
|
|
751
|
+
// The stream is wrapped into a handler function, which defaults to `stringbuffer::s`
|
|
752
|
+
//
|
|
753
|
+
// s { ... }
|
|
754
|
+
//
|
|
755
|
+
// but can be specified by prefixing the string interpolation `custom"..."`.
|
|
756
|
+
|
|
757
|
+
effect literal(s: String): Unit
|
|
758
|
+
effect splice[A](x: A): Unit
|
|
759
|
+
|
|
760
|
+
|
|
726
761
|
namespace internal {
|
|
727
762
|
namespace boxing {
|
|
728
763
|
// For boxing polymorphic values
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
module heap
|
|
2
|
+
import resizable_array
|
|
3
|
+
|
|
4
|
+
/// Resizable 2-ary min-heap, backed by a resizable array
|
|
5
|
+
/// `cmp` defines the ordering of elements
|
|
6
|
+
record Heap[T](rawContents: ResizableArray[T], cmp: (T, T) => Ordering at {})
|
|
7
|
+
|
|
8
|
+
/// Make a new Heap with the given comparison operation
|
|
9
|
+
def heap[T](cmp: (T,T) => Ordering at {}) =
|
|
10
|
+
Heap[T](resizableArray(), cmp)
|
|
11
|
+
|
|
12
|
+
/// Make a new Heap with the given comparison operation and initial capacity
|
|
13
|
+
def heap[T](cmp: (T,T) => Ordering at {}, capacity: Int) =
|
|
14
|
+
Heap[T](resizableArray(capacity), cmp)
|
|
15
|
+
|
|
16
|
+
namespace internal {
|
|
17
|
+
def left(idx: Int) = 2 * idx + 1
|
|
18
|
+
def right(idx: Int) = 2 * idx + 2
|
|
19
|
+
def parent(idx: Int) = (idx - 1) / 2
|
|
20
|
+
|
|
21
|
+
def bubbleUp[A](heap: Heap[A], idx: Int) = {
|
|
22
|
+
val arr = heap.rawContents
|
|
23
|
+
arr.boundsCheck(idx) // idx > parent(idx), parent(parent(idx)) etc
|
|
24
|
+
|
|
25
|
+
def go(idx: Int): Unit = {
|
|
26
|
+
if (idx > 0 and (heap.cmp)(arr.unsafeGet(parent(idx)), arr.unsafeGet(idx)) is Greater()) {
|
|
27
|
+
arr.unsafeSwap(parent(idx), idx)
|
|
28
|
+
go(parent(idx))
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
go(idx)
|
|
32
|
+
}
|
|
33
|
+
def sinkDown[A](heap: Heap[A], idx: Int) = {
|
|
34
|
+
val arr = heap.rawContents
|
|
35
|
+
|
|
36
|
+
def infixLt(x: A, y: A) = {
|
|
37
|
+
if ((heap.cmp)(x,y) is Less()) { true } else { false }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
def go(idx: Int): Unit = {
|
|
41
|
+
if (right(idx) < arr.size) {
|
|
42
|
+
val v = arr.unsafeGet(idx)
|
|
43
|
+
val l = arr.unsafeGet(left(idx))
|
|
44
|
+
val r = arr.unsafeGet(right(idx))
|
|
45
|
+
if (l < v && r < v) {
|
|
46
|
+
// swap with the smaller one
|
|
47
|
+
if (l < r) {
|
|
48
|
+
arr.unsafeSwap(left(idx), idx)
|
|
49
|
+
go(left(idx))
|
|
50
|
+
} else {
|
|
51
|
+
arr.unsafeSwap(right(idx), idx)
|
|
52
|
+
go(right(idx))
|
|
53
|
+
}
|
|
54
|
+
} else if (l < v) {
|
|
55
|
+
arr.unsafeSwap(left(idx), idx)
|
|
56
|
+
go(left(idx))
|
|
57
|
+
} else if (r < v) {
|
|
58
|
+
arr.unsafeSwap(right(idx), idx)
|
|
59
|
+
go(right(idx))
|
|
60
|
+
}
|
|
61
|
+
} else if (left(idx) < arr.size) {
|
|
62
|
+
if (arr.unsafeGet(left(idx)) < arr.unsafeGet(idx)) {
|
|
63
|
+
arr.unsafeSwap(left(idx), idx)
|
|
64
|
+
go(left(idx))
|
|
65
|
+
}
|
|
66
|
+
} // else: we are at the bottom
|
|
67
|
+
}
|
|
68
|
+
go(idx)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// Insert value into heap
|
|
73
|
+
///
|
|
74
|
+
/// O(log n) worst case if capacity suffices, O(1) average
|
|
75
|
+
def insert[T](heap: Heap[T], value: T): Unit = {
|
|
76
|
+
with on[OutOfBounds].panic();
|
|
77
|
+
val idx = heap.rawContents.add(value)
|
|
78
|
+
internal::bubbleUp(heap, idx)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// find and return (but not remove) the minimal element in this heap
|
|
82
|
+
///
|
|
83
|
+
/// O(1)
|
|
84
|
+
def findMin[T](heap: Heap[T]): T / Exception[OutOfBounds] = {
|
|
85
|
+
heap.rawContents.get(0)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// find and remove the minimal element in this heap
|
|
89
|
+
///
|
|
90
|
+
/// O(log n)
|
|
91
|
+
def deleteMin[T](heap: Heap[T]): T / Exception[OutOfBounds] = {
|
|
92
|
+
val res = heap.rawContents.get(0)
|
|
93
|
+
heap.rawContents.unsafeSet(0, heap.rawContents.popRight())
|
|
94
|
+
internal::sinkDown(heap, 0)
|
|
95
|
+
res
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/// Number of elements in the heap
|
|
99
|
+
///
|
|
100
|
+
/// O(1)
|
|
101
|
+
def size[T](heap: Heap[T]): Int = {
|
|
102
|
+
heap.rawContents.size
|
|
103
|
+
}
|
|
@@ -6,35 +6,13 @@ import io
|
|
|
6
6
|
import io/error
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
/// Represents the file opening modes with their respective flags
|
|
10
|
-
/// Backends rely on the order of these
|
|
11
|
-
type Mode {
|
|
12
|
-
// Most common modes
|
|
13
|
-
ReadOnly() // 'r' exception if does not exist
|
|
14
|
-
WriteOnly() // 'w' created if does not exist, truncated if exists
|
|
15
|
-
AppendOnly() // 'a' created if does not exist
|
|
16
|
-
|
|
17
|
-
// Other modes
|
|
18
|
-
ReadWrite() // 'w+' created if does not exist, truncated if exists
|
|
19
|
-
ReadAppend() // 'a+' created if does not exist
|
|
20
|
-
AppendExclusive() // 'ax' created if does not exist, fails if exists
|
|
21
|
-
ReadAppendExclusive() // 'ax+' created if does not exist, fails if exists
|
|
22
|
-
AppendSync() // 'as' created if does not exist, append in synchronous mode
|
|
23
|
-
ReadAppendSync() // 'as+' created if does not exist, append in synchronous mode
|
|
24
|
-
ReadSync() // 'rs' exception if does not exist, read in synchronous mode
|
|
25
|
-
ReadWriteSync() // 'rs+' exception if does not exist, read/write in synchronous mode
|
|
26
|
-
WriteExclusive() // 'wx' created if does not exist, truncated if exists, fails if exists
|
|
27
|
-
ReadWriteExclusive() // 'wx+' created if does not exist, truncated if exists, fails if exists
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
9
|
/// A file descriptor. Should not be inspected.
|
|
32
10
|
type File = Int
|
|
33
11
|
|
|
34
12
|
|
|
35
13
|
/// Reads a file at given path as utf8 encoded string.
|
|
36
14
|
def readFile(path: String): String / Exception[IOError] = {
|
|
37
|
-
val file =
|
|
15
|
+
val file = openForReading(path);
|
|
38
16
|
with on[IOError].finalize { close(file) }
|
|
39
17
|
|
|
40
18
|
val chunkSize = 1048576 // 1MB
|
|
@@ -59,7 +37,7 @@ def readFile(path: String): String / Exception[IOError] = {
|
|
|
59
37
|
|
|
60
38
|
/// Writes the (utf8 encoded) string `contents` into the specified file.
|
|
61
39
|
def writeFile(path: String, contents: String): Unit / Exception[IOError] = {
|
|
62
|
-
val file =
|
|
40
|
+
val file = openForWriting(path);
|
|
63
41
|
with on[IOError].finalize { close(file) }
|
|
64
42
|
|
|
65
43
|
val chunkSize = 1048576 // 1MB
|
|
@@ -75,6 +53,23 @@ def writeFile(path: String, contents: String): Unit / Exception[IOError] = {
|
|
|
75
53
|
go()
|
|
76
54
|
}
|
|
77
55
|
|
|
56
|
+
/// Appends the (utf8 encoded) string `contents` to the specified file.
|
|
57
|
+
def appendFile(path: String, contents: String): Unit / Exception[IOError] = {
|
|
58
|
+
val file = openForAppending(path);
|
|
59
|
+
with on[IOError].finalize { close(file) }
|
|
60
|
+
|
|
61
|
+
val chunkSize = 1048576 // 1MB
|
|
62
|
+
val buffer = contents.fromString
|
|
63
|
+
var offset = 0;
|
|
64
|
+
|
|
65
|
+
def go(): Unit = {
|
|
66
|
+
val n = write(file, buffer, offset, min(buffer.size - offset, chunkSize), -1)
|
|
67
|
+
offset = offset + n
|
|
68
|
+
if (offset < buffer.size) { go() }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
go()
|
|
72
|
+
}
|
|
78
73
|
|
|
79
74
|
/// An abstract interface applications can program against.
|
|
80
75
|
///
|
|
@@ -95,8 +90,14 @@ def filesystem[R] { program: => R / Files }: R / Exception[IOError] = // TODO mo
|
|
|
95
90
|
}
|
|
96
91
|
|
|
97
92
|
|
|
98
|
-
def
|
|
99
|
-
internal::checkResult(internal::
|
|
93
|
+
def openForReading(path: String): File / Exception[IOError] =
|
|
94
|
+
internal::checkResult(internal::openForReading(path))
|
|
95
|
+
|
|
96
|
+
def openForWriting(path: String): File / Exception[IOError] =
|
|
97
|
+
internal::checkResult(internal::openForWriting(path))
|
|
98
|
+
|
|
99
|
+
def openForAppending(path: String): File / Exception[IOError] =
|
|
100
|
+
internal::checkResult(internal::openForAppending(path))
|
|
100
101
|
|
|
101
102
|
def read(file: File, buffer: ByteArray, offset: Int, size: Int, position: Int): Int / Exception[IOError] =
|
|
102
103
|
internal::checkResult(internal::read(file, buffer, offset, size, position))
|
|
@@ -111,40 +112,6 @@ def close(file: File): Unit / Exception[IOError] = {
|
|
|
111
112
|
namespace internal {
|
|
112
113
|
|
|
113
114
|
extern js """
|
|
114
|
-
function modeName(mode) {
|
|
115
|
-
switch (mode.__tag) {
|
|
116
|
-
case 0: // ReadOnly()
|
|
117
|
-
return 'r';
|
|
118
|
-
case 1: // WriteOnly()
|
|
119
|
-
return 'w';
|
|
120
|
-
case 2: // AppendOnly()
|
|
121
|
-
return 'a';
|
|
122
|
-
case 3: // ReadWrite()
|
|
123
|
-
return 'w+';
|
|
124
|
-
case 4: // ReadAppend()
|
|
125
|
-
return 'a+';
|
|
126
|
-
case 5: // AppendExclusive()
|
|
127
|
-
return 'ax';
|
|
128
|
-
case 6: // ReadAppendExclusive()
|
|
129
|
-
return 'ax+';
|
|
130
|
-
case 7: // AppendSync()
|
|
131
|
-
return 'as';
|
|
132
|
-
case 8: // ReadAppendSync()
|
|
133
|
-
return 'as+';
|
|
134
|
-
case 9: // ReadSync()
|
|
135
|
-
return 'rs';
|
|
136
|
-
case 10: // ReadWriteSync()
|
|
137
|
-
return 'rs+';
|
|
138
|
-
case 11: // WriteExclusive()
|
|
139
|
-
return 'wx';
|
|
140
|
-
case 12: // ReadWriteExclusive()
|
|
141
|
-
return 'wx+';
|
|
142
|
-
default:
|
|
143
|
-
// Invalid tag value
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
115
|
/**
|
|
149
116
|
* Nodejs file operations expect buffers, but we represent buffers as typed arrays.
|
|
150
117
|
* This function converts between the two without copying.
|
|
@@ -157,8 +124,8 @@ namespace internal {
|
|
|
157
124
|
extern jsNode """
|
|
158
125
|
const fs = require("fs");
|
|
159
126
|
|
|
160
|
-
function open(path,
|
|
161
|
-
fs.open(path,
|
|
127
|
+
function open(path, flags, callback) {
|
|
128
|
+
fs.open(path, flags, (err, file) => {
|
|
162
129
|
if (err) { callback(err.errno) } else { callback(file) }
|
|
163
130
|
})
|
|
164
131
|
}
|
|
@@ -185,16 +152,32 @@ namespace internal {
|
|
|
185
152
|
"""
|
|
186
153
|
|
|
187
154
|
extern llvm """
|
|
188
|
-
declare void @
|
|
155
|
+
declare void @c_fs_open_reading(%Pos, %Stack)
|
|
156
|
+
declare void @c_fs_open_writing(%Pos, %Stack)
|
|
157
|
+
declare void @c_fs_open_appending(%Pos, %Stack)
|
|
189
158
|
declare void @c_fs_read(%Int, %Pos, %Int, %Int, %Int, %Stack)
|
|
190
159
|
declare void @c_fs_write(%Int, %Pos, %Int, %Int, %Int, %Stack)
|
|
191
160
|
declare void @c_fs_close(%Int, %Stack)
|
|
192
161
|
"""
|
|
193
162
|
|
|
194
|
-
extern async def
|
|
195
|
-
jsNode "$effekt.capture(callback => open(${path},
|
|
163
|
+
extern async def openForReading(path: String): Int =
|
|
164
|
+
jsNode "$effekt.capture(callback => open(${path}, 'r', callback))"
|
|
165
|
+
llvm """
|
|
166
|
+
call void @c_fs_open_reading(%Pos ${path}, %Stack %stack)
|
|
167
|
+
ret void
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
extern async def openForWriting(path: String): Int =
|
|
171
|
+
jsNode "$effekt.capture(callback => open(${path}, 'w', callback))"
|
|
172
|
+
llvm """
|
|
173
|
+
call void @c_fs_open_writing(%Pos ${path}, %Stack %stack)
|
|
174
|
+
ret void
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
extern async def openForAppending(path: String): Int =
|
|
178
|
+
jsNode "$effekt.capture(callback => open(${path}, 'a', callback))"
|
|
196
179
|
llvm """
|
|
197
|
-
call void @
|
|
180
|
+
call void @c_fs_open_appending(%Pos ${path}, %Stack %stack)
|
|
198
181
|
ret void
|
|
199
182
|
"""
|
|
200
183
|
|
|
@@ -57,14 +57,17 @@ def server(host: String, port: Int, handler: () => Unit / Socket at {io, async,
|
|
|
57
57
|
|
|
58
58
|
namespace examples {
|
|
59
59
|
def helloWorldApp(): Unit / Socket = {
|
|
60
|
-
val request = do receive();
|
|
60
|
+
val request = do receive().toString;
|
|
61
61
|
|
|
62
|
-
println("Received a request: " ++ request
|
|
62
|
+
println("Received a request: " ++ request)
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
do send(
|
|
64
|
+
def respond(s: String): Unit / Socket =
|
|
65
|
+
do send(s.fromString)
|
|
66
|
+
|
|
67
|
+
if (request.startsWith("GET /")) {
|
|
68
|
+
respond("HTTP/1.1 200 OK\r\n\r\nHello from Effekt!")
|
|
66
69
|
} else {
|
|
67
|
-
|
|
70
|
+
respond("HTTP/1.1 400 Bad Request\r\n\r\n")
|
|
68
71
|
}
|
|
69
72
|
do end()
|
|
70
73
|
}
|