Skip to content

Commit 576dc29

Browse files
committed
Move stream module to prelude
1 parent 9da850d commit 576dc29

File tree

10 files changed

+402
-363
lines changed

10 files changed

+402
-363
lines changed

effekt/jvm/src/main/scala/effekt/Runner.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ trait Runner[Executable] {
4343
* if module A depends on module B, then B should come before A.
4444
* - Furthermore, each module mentioned here must import the `effekt` module as its first import.
4545
*/
46-
def prelude: List[String] = List("effekt", "option", "list", "result", "exception", "array", "char", "string", "ref")
46+
def prelude: List[String] = List("effekt", "option", "stream", "list", "result", "exception", "array", "char", "bytearray", "string", "ref")
4747

4848
/**
4949
* Creates a OS-specific script file that will execute the command when executed,

libraries/common/array.effekt

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import effekt
44
import exception
55
import list
66
import option
7+
import stream
78

89
/// A mutable 0-indexed fixed-sized array.
910
extern type Array[T]
@@ -641,3 +642,55 @@ def println(l: Array[Int]): Unit = println(show(l))
641642
def println(l: Array[Double]): Unit = println(show(l))
642643
def println(l: Array[Bool]): Unit = println(show(l))
643644
def println(l: Array[String]): Unit = println(show(l))
645+
646+
647+
// Streaming
648+
// ---------
649+
650+
/// Turns an `array` into a producer of a push stream
651+
/// by emitting each contained value from 0 to length - 1.
652+
def each[T](array: Array[T]): Unit / emit[T] = {
653+
val n = array.size
654+
def go(i: Int): Unit = {
655+
if (i < n) {
656+
do emit(array.unsafeGet(i))
657+
go(i + 1)
658+
}
659+
}
660+
go(0)
661+
}
662+
663+
def feed[T, R](array: Array[T]) { reader: () => R / read[T] }: R = {
664+
var i = 0
665+
try {
666+
reader()
667+
} with read[T] {
668+
resume {
669+
if (i < array.size) {
670+
val c = i
671+
i = c + 1
672+
array.unsafeGet(c)
673+
} else {
674+
do stop()
675+
}
676+
}
677+
}
678+
}
679+
680+
def collectArray[A] { stream: () => Unit / emit[A] }: Array[A] =
681+
returning::collectArray[A, Unit]{stream}.second
682+
683+
namespace returning {
684+
def collectArray[A, R] { stream: () => R / emit[A] }: (R, Array[A]) = {
685+
var i = 0
686+
var a = allocate(1)
687+
try {
688+
(stream(), a.resize(i))
689+
} with emit[A] { (v) =>
690+
if (i >= a.size) { a = a.resize(2 * a.size) }
691+
a.unsafeSet(i, v)
692+
i = i + 1
693+
resume(())
694+
}
695+
}
696+
}

libraries/common/bytearray.effekt

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
module bytearray
22

3+
import effekt
4+
import stream
5+
36
/**
47
* A memory managed, mutable, fixed-length array of bytes.
58
*
@@ -200,3 +203,55 @@ def compareStringBytes(left: String, right: String): Ordering = {
200203
val r = right.fromString
201204
compareByteArray(l, r)
202205
}
206+
207+
208+
// Streaming
209+
// ---------
210+
211+
/// Turns `bytes` into a producer of a push stream
212+
/// by emitting each contained value from 0 to length - 1.
213+
def each(bytes: ByteArray): Unit / emit[Byte] = {
214+
val n = bytes.size
215+
def go(i: Int): Unit = {
216+
if (i < n) {
217+
do emit(bytes.unsafeGet(i))
218+
go(i + 1)
219+
}
220+
}
221+
go(0)
222+
}
223+
224+
def feed[R](bytes: ByteArray) { reader: () => R / read[Byte] }: R = {
225+
var i = 0
226+
try {
227+
reader()
228+
} with read[Byte] {
229+
resume {
230+
if (i < bytes.size) {
231+
val c = i
232+
i = c + 1
233+
bytes.unsafeGet(c)
234+
} else {
235+
do stop()
236+
}
237+
}
238+
}
239+
}
240+
241+
def collectBytes { stream: () => Unit / emit[Byte] }: ByteArray =
242+
returning::collectBytes[Unit]{stream}.second
243+
244+
namespace returning {
245+
def collectBytes[R] { stream: () => R / emit[Byte] }: (R, ByteArray) = {
246+
var i = 0
247+
var a = allocate(1)
248+
try {
249+
(stream(), a.resize(i))
250+
} with emit[Byte] { (v) =>
251+
if (i >= a.size) { a = a.resize(2 * a.size) }
252+
a.unsafeSet(i, v)
253+
i = i + 1
254+
resume(())
255+
}
256+
}
257+
}

libraries/common/char.effekt

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import effekt
55
import exception
66
import option
77
import result
8+
import stream
89

910

1011
/// Checks if the given character is an ASCII whitespace
@@ -175,3 +176,89 @@ def utf16UnitCount(codepoint: Char): Int = codepoint match {
175176
extern def charWidth(c: Char) at {}: Int =
176177
// JavaScript strings are UTF-16 where every unicode character after 0xffff takes two units
177178
js "(${c} > 0xffff) ? 2 : 1"
179+
180+
181+
def writeLine { body: () => Unit / emit[Char] }: Unit / emit[Char] =
182+
returning::writeLine[Unit]{body}
183+
184+
def readLine { body: () => Unit / read[Char] }: Unit / {read[Char], stop} =
185+
returning::readLine[Unit]{body}
186+
187+
namespace returning {
188+
def writeLine[R] { body: () => R / emit[Char] }: R / emit[Char] = {
189+
val result = body()
190+
do emit('\n')
191+
return result
192+
}
193+
194+
def readLine[R] { body: () => R / read[Char] }: R / {read[Char], stop} = {
195+
var stopped = false
196+
try {
197+
body()
198+
} with read[Char] {
199+
if(stopped){
200+
resume { do stop() }
201+
} else {
202+
do read[Char] match {
203+
case '\n' => stopped = true; resume { do stop() }
204+
case char => resume { return char }
205+
}
206+
}
207+
}
208+
}
209+
}
210+
211+
def decodeChar(): Char / {read[Byte], stop} = {
212+
val b = do read().toInt
213+
if (b < 128) {
214+
b.toChar
215+
} else if (b < 224) {
216+
val part1 = bitwiseShl(bitwiseAnd(b, 31), 6)
217+
val part2 = bitwiseAnd(do read().toInt, 63)
218+
bitwiseOr(part1, part2).toChar
219+
} else if (b < 240) {
220+
val part1 = bitwiseShl(bitwiseAnd(b, 15), 12)
221+
val part2 = bitwiseShl(bitwiseAnd(do read().toInt, 63), 6)
222+
val part3 = bitwiseAnd(do read().toInt, 63)
223+
bitwiseOr(bitwiseOr(part1, part2), part3).toChar
224+
} else {
225+
val part1 = bitwiseShl(bitwiseAnd(b, 7), 18)
226+
val part2 = bitwiseShl(bitwiseAnd(do read().toInt, 63), 12)
227+
val part3 = bitwiseShl(bitwiseAnd(do read().toInt, 63), 6)
228+
val part4 = bitwiseAnd(do read().toInt, 63)
229+
bitwiseOr(bitwiseOr(bitwiseOr(part1, part2), part3), part4).toChar
230+
}
231+
}
232+
233+
def encodeChar(char: Char): Unit / emit[Byte] = {
234+
val code = char.toInt
235+
if (code < 128) {
236+
do emit(code.toByte)
237+
} else if (code < 2048) {
238+
do emit(bitwiseOr(192, bitwiseShr(code, 6)).toByte)
239+
do emit(bitwiseOr(128, bitwiseAnd(code, 63)).toByte)
240+
} else if (code < 65536) {
241+
do emit(bitwiseOr(224, bitwiseShr(code, 12)).toByte)
242+
do emit(bitwiseOr(128, bitwiseAnd(bitwiseShr(code, 6), 63)).toByte)
243+
do emit(bitwiseOr(128, bitwiseAnd(code, 63)).toByte)
244+
} else {
245+
do emit(bitwiseOr(240, bitwiseShr(code, 18)).toByte)
246+
do emit(bitwiseOr(128, bitwiseAnd(bitwiseShr(code, 12), 63)).toByte)
247+
do emit(bitwiseOr(128, bitwiseAnd(bitwiseShr(code, 6), 63)).toByte)
248+
do emit(bitwiseOr(128, bitwiseAnd(code, 63)).toByte)
249+
}
250+
}
251+
252+
def decodeUTF8[R] { reader: () => R / read[Char] }: R / read[Byte] =
253+
try {
254+
reader()
255+
} with read[Char] {
256+
resume { decodeChar() }
257+
}
258+
259+
def encodeUTF8[R] { stream: () => R / emit[Char] }: R / emit[Byte] =
260+
try {
261+
stream()
262+
} with emit[Char] { char =>
263+
resume(encodeChar(char))
264+
}

libraries/common/io/filesystem.effekt

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,77 @@ def appendFile(path: String, contents: String): Unit / Exception[IOError] = {
7171
go()
7272
}
7373

74+
def writeFileUTF8[R](path: String) { stream: () => R / emit[Char] }: R / Exception[IOError] =
75+
writeFile(path) { encodeUTF8 { stream() } }
76+
77+
def readFileUTF8[R](path: String) { reader: () => R / read[Char] }: R / Exception[IOError] =
78+
readFile(path) { decodeUTF8 { reader() } }
79+
80+
def writeFile[R](path: String) { stream: () => R / emit[Byte] }: R / Exception[IOError] = {
81+
82+
val file = openForWriting(path);
83+
with on[IOError].finalize { close(file) }
84+
85+
val chunkSize = 1048576 // 1MB
86+
val buffer = bytearray::allocate(chunkSize)
87+
var offset = 0
88+
89+
def push(i: Int, n: Int): Unit = {
90+
val r = write(file, buffer, i, n, -1)
91+
if (r < n) {
92+
push(i + r, n - r)
93+
}
94+
}
95+
96+
try {
97+
val r = stream()
98+
push(0, offset)
99+
return r
100+
} with emit[Byte] { (byte) =>
101+
if (offset >= buffer.size) {
102+
push(0, buffer.size)
103+
offset = 0
104+
}
105+
buffer.unsafeSet(offset, byte)
106+
offset = offset + 1
107+
resume(())
108+
}
109+
}
110+
111+
def readFile[R](path: String) { reader: () => R / read[Byte] }: R / Exception[IOError] = {
112+
113+
val file = openForReading(path);
114+
with on[IOError].finalize { close(file) }
115+
116+
val chunkSize = 1048576 // 1MB
117+
val buffer = bytearray::allocate(chunkSize)
118+
var offset = 0
119+
var length = 0
120+
121+
def pull(): Unit / stop = {
122+
read(file, buffer, 0, chunkSize, -1) match {
123+
case 0 =>
124+
do stop()
125+
case n =>
126+
length = n
127+
}
128+
}
129+
130+
try {
131+
reader()
132+
} with read[Byte] {
133+
resume {
134+
if (offset >= length) {
135+
pull()
136+
offset = 0
137+
}
138+
val byte = buffer.unsafeGet(offset)
139+
offset = offset + 1
140+
return byte
141+
}
142+
}
143+
}
144+
74145
/// An abstract interface applications can program against.
75146
///
76147
/// Can be interpreted with the `filesystem` handler, or virtualized etc.

libraries/common/list.effekt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module list
33
import effekt
44
import option
55
import exception
6+
import stream
67

78
/// Immutable linked list for finite sequences of elements.
89
type List[A] {
@@ -836,3 +837,46 @@ def println(l: List[Int]): Unit = println(show(l))
836837
def println(l: List[Double]): Unit = println(show(l))
837838
def println(l: List[Bool]): Unit = println(show(l))
838839
def println(l: List[String]): Unit = println(show(l))
840+
841+
842+
// Streaming
843+
// ---------
844+
845+
/// Turns a `list` into a producer of a push stream
846+
/// by emitting each contained value left-to-right.
847+
def each[A](list: List[A]): Unit / emit[A] =
848+
list match {
849+
case Nil() => ()
850+
case Cons(head, tail) =>
851+
do emit(head)
852+
each(tail)
853+
}
854+
855+
def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
856+
var l = list
857+
try {
858+
reader()
859+
} with read[T] {
860+
resume {
861+
l match {
862+
case Nil() => do stop()
863+
case Cons(value, rest) =>
864+
l = rest
865+
return value
866+
}
867+
}
868+
}
869+
}
870+
871+
def collectList[A] { stream: () => Unit / emit[A] }: List[A] =
872+
returning::collectList[A, Unit]{stream}.second
873+
874+
namespace returning {
875+
def collectList[A, R] { stream: () => R / emit[A] }: (R, List[A]) =
876+
try {
877+
(stream(), Nil())
878+
} with emit[A] { (v) =>
879+
val (r, vs) = resume(());
880+
(r, Cons(v, vs))
881+
}
882+
}

0 commit comments

Comments
 (0)