Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion effekt/jvm/src/main/scala/effekt/Runner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ trait Runner[Executable] {
* if module A depends on module B, then B should come before A.
* - Furthermore, each module mentioned here must import the `effekt` module as its first import.
*/
def prelude: List[String] = List("effekt", "option", "list", "result", "exception", "array", "char", "string", "ref")
def prelude: List[String] = List("effekt", "option", "stream", "list", "result", "exception", "array", "char", "bytearray", "string", "ref")

/**
* Creates a OS-specific script file that will execute the command when executed,
Expand Down
2 changes: 1 addition & 1 deletion examples/benchmarks/input_output/dyck_one.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type Tree {

def readTree(): Tree / { Scan[Byte], stop } = {
readIf[Byte] { b => b.toInt == 40 }
val children = collectList[Tree] { many { readTree() } }
val children = list::collect[Tree] { many { readTree() } }
skipIf[Byte] { b => b.toInt == 41 }
Node(children)
}
Expand Down
6 changes: 3 additions & 3 deletions examples/stdlib/list/collect.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ module examples/pos/list/collect

def main() = {
val empty = Nil[Int]()
empty.collect { x => Some(x) }.foreach { x => println(x) }
empty.collectSome { x => Some(x) }.foreach { x => println(x) }

val l = [1, 2, 3, 4]
l.collect { x => if (x > 2) { Some(x * 10) } else { None() } }.foreach { x => println(x) }
l.collectSome { x => if (x > 2) { Some(x * 10) } else { None() } }.foreach { x => println(x) }

val optList = [Some(1), None(), Some(2), None(), Some(3), Some(4), None()]
optList.collect { x => x }.foreach { x => println(x) }
optList.collectSome { x => x }.foreach { x => println(x) }
}
2 changes: 1 addition & 1 deletion examples/stdlib/stream/characters.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def main() = {
println(show(c) ++ " (" ++ show(c.toInt) ++ ")")
}

val list = collectList[Char] { each("Hello") }
val list = list::collect[Char] { each("Hello") }
println(list.map { c => c.show })
}

4 changes: 2 additions & 2 deletions examples/stdlib/stream/fastexp.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ def main() = {

def testBits(n: Int) = {
println(n)
println(collectList[Bool] {n.eachLSB}.prettyBits)
println(collectList[Bool] {n.eachMSB}.prettyBits)
println(list::collect[Bool] {n.eachLSB}.prettyBits)
println(list::collect[Bool] {n.eachMSB}.prettyBits)
}

testBits(0)
Expand Down
2 changes: 1 addition & 1 deletion examples/stdlib/stream/fuse_newlines.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def skipNewlines(): Nothing / {read[Char], emit[Char], stop} = {

def main() = {
with feed("ab\n\nc\nde")
println(collectString {
println(string::collect {
with exhaustively
fuseNewlines()
})
Expand Down
4 changes: 2 additions & 2 deletions examples/stdlib/stream/map.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ def main() = {
println(show(k) ++ ": " ++ show(v) ++ " (" ++ show(v.toInt) ++ ")")
}

val newMap = collectMap[Int, Char](compareInt) { each(m) }
val newMap = map::collect[Int, Char](compareInt) { each(m) }
println(map::internal::prettyPairs(newMap.toList) { n => show(n) } { c => show(c) })

val hello: String = collectString { eachValue(m) }
val hello: String = string::collect { eachValue(m) }
println(hello)
}

2 changes: 1 addition & 1 deletion examples/stdlib/stream/taylor.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def ints(): Unit / emit[Int] = {

/// Take the `n` first elements of a `stream`, put them in a list.
def take[A](n: Int) { stream: () => Unit / emit[A] }: List[A] = {
with collectList[A]
with collect[A]
with boundary
with limit[A](n)
stream()
Expand Down
4 changes: 2 additions & 2 deletions examples/stdlib/stream/tee.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ def main() = {
}
}
def a{ b: => Unit / emit[Int] }: Unit = {
println(collectList[Int]{boundary{limit[Int](4){b}}})
println(list::collect[Int]{boundary{limit[Int](4){b}}})
println("a done")
}
def b{ b: => Unit / emit[Int] }: Unit = {
println(collectList[Int]{boundary{limit[Int](9){b}}})
println(list::collect[Int]{boundary{limit[Int](9){b}}})
println("b done")
}
println("a{ ... }")
Expand Down
53 changes: 53 additions & 0 deletions libraries/common/array.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import effekt
import exception
import list
import option
import stream

/// A mutable 0-indexed fixed-sized array.
extern type Array[T]
Expand Down Expand Up @@ -641,3 +642,55 @@ def println(l: Array[Int]): Unit = println(show(l))
def println(l: Array[Double]): Unit = println(show(l))
def println(l: Array[Bool]): Unit = println(show(l))
def println(l: Array[String]): Unit = println(show(l))


// Streaming
// ---------

/// Turns an `array` into a producer of a push stream
/// by emitting each contained value from 0 to length - 1.
def each[T](array: Array[T]): Unit / emit[T] = {
val n = array.size
def go(i: Int): Unit = {
if (i < n) {
do emit(array.unsafeGet(i))
go(i + 1)
}
}
go(0)
}

def feed[T, R](array: Array[T]) { reader: () => R / read[T] }: R = {
var i = 0
try {
reader()
} with read[T] {
resume {
if (i < array.size) {
val c = i
i = c + 1
array.unsafeGet(c)
} else {
do stop()
}
}
}
}

def collect[A] { stream: () => Unit / emit[A] }: Array[A] =
returning::collect[A, Unit]{stream}.second

namespace returning {
def collect[A, R] { stream: () => R / emit[A] }: (R, Array[A]) = {
var i = 0
var a = allocate(1)
try {
(stream(), a.resize(i))
} with emit[A] { (v) =>
if (i >= a.size) { a = a.resize(2 * a.size) }
a.unsafeSet(i, v)
i = i + 1
resume(())
}
}
}
55 changes: 55 additions & 0 deletions libraries/common/bytearray.effekt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
module bytearray

import effekt
import stream

/**
* A memory managed, mutable, fixed-length array of bytes.
*
Expand Down Expand Up @@ -200,3 +203,55 @@ def compareStringBytes(left: String, right: String): Ordering = {
val r = right.fromString
compareByteArray(l, r)
}


// Streaming
// ---------

/// Turns `bytes` into a producer of a push stream
/// by emitting each contained value from 0 to length - 1.
def each(bytes: ByteArray): Unit / emit[Byte] = {
val n = bytes.size
def go(i: Int): Unit = {
if (i < n) {
do emit(bytes.unsafeGet(i))
go(i + 1)
}
}
go(0)
}

def feed[R](bytes: ByteArray) { reader: () => R / read[Byte] }: R = {
var i = 0
try {
reader()
} with read[Byte] {
resume {
if (i < bytes.size) {
val c = i
i = c + 1
bytes.unsafeGet(c)
} else {
do stop()
}
}
}
}

def collect { stream: () => Unit / emit[Byte] }: ByteArray =
returning::collect[Unit]{stream}.second

namespace returning {
def collect[R] { stream: () => R / emit[Byte] }: (R, ByteArray) = {
var i = 0
var a = allocate(1)
try {
(stream(), a.resize(i))
} with emit[Byte] { (v) =>
if (i >= a.size) { a = a.resize(2 * a.size) }
a.unsafeSet(i, v)
i = i + 1
resume(())
}
}
}
87 changes: 87 additions & 0 deletions libraries/common/char.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import effekt
import exception
import option
import result
import stream


/// Checks if the given character is an ASCII whitespace
Expand Down Expand Up @@ -175,3 +176,89 @@ def utf16UnitCount(codepoint: Char): Int = codepoint match {
extern def charWidth(c: Char) at {}: Int =
// JavaScript strings are UTF-16 where every unicode character after 0xffff takes two units
js "(${c} > 0xffff) ? 2 : 1"


def writeLine { body: () => Unit / emit[Char] }: Unit / emit[Char] =
returning::writeLine[Unit]{body}

def readLine { body: () => Unit / read[Char] }: Unit / {read[Char], stop} =
returning::readLine[Unit]{body}

namespace returning {
def writeLine[R] { body: () => R / emit[Char] }: R / emit[Char] = {
val result = body()
do emit('\n')
return result
}

def readLine[R] { body: () => R / read[Char] }: R / {read[Char], stop} = {
var stopped = false
try {
body()
} with read[Char] {
if(stopped){
resume { do stop() }
} else {
do read[Char] match {
case '\n' => stopped = true; resume { do stop() }
case char => resume { return char }
}
}
}
}
}

def decodeChar(): Char / {read[Byte], stop} = {
val b = do read().toInt
if (b < 128) {
b.toChar
} else if (b < 224) {
val part1 = bitwiseShl(bitwiseAnd(b, 31), 6)
val part2 = bitwiseAnd(do read().toInt, 63)
bitwiseOr(part1, part2).toChar
} else if (b < 240) {
val part1 = bitwiseShl(bitwiseAnd(b, 15), 12)
val part2 = bitwiseShl(bitwiseAnd(do read().toInt, 63), 6)
val part3 = bitwiseAnd(do read().toInt, 63)
bitwiseOr(bitwiseOr(part1, part2), part3).toChar
} else {
val part1 = bitwiseShl(bitwiseAnd(b, 7), 18)
val part2 = bitwiseShl(bitwiseAnd(do read().toInt, 63), 12)
val part3 = bitwiseShl(bitwiseAnd(do read().toInt, 63), 6)
val part4 = bitwiseAnd(do read().toInt, 63)
bitwiseOr(bitwiseOr(bitwiseOr(part1, part2), part3), part4).toChar
}
}

def encodeChar(char: Char): Unit / emit[Byte] = {
val code = char.toInt
if (code < 128) {
do emit(code.toByte)
} else if (code < 2048) {
do emit(bitwiseOr(192, bitwiseShr(code, 6)).toByte)
do emit(bitwiseOr(128, bitwiseAnd(code, 63)).toByte)
} else if (code < 65536) {
do emit(bitwiseOr(224, bitwiseShr(code, 12)).toByte)
do emit(bitwiseOr(128, bitwiseAnd(bitwiseShr(code, 6), 63)).toByte)
do emit(bitwiseOr(128, bitwiseAnd(code, 63)).toByte)
} else {
do emit(bitwiseOr(240, bitwiseShr(code, 18)).toByte)
do emit(bitwiseOr(128, bitwiseAnd(bitwiseShr(code, 12), 63)).toByte)
do emit(bitwiseOr(128, bitwiseAnd(bitwiseShr(code, 6), 63)).toByte)
do emit(bitwiseOr(128, bitwiseAnd(code, 63)).toByte)
}
}

def decodeUTF8[R] { reader: () => R / read[Char] }: R / read[Byte] =
try {
reader()
} with read[Char] {
resume { decodeChar() }
}

def encodeUTF8[R] { stream: () => R / emit[Char] }: R / emit[Byte] =
try {
stream()
} with emit[Char] { char =>
resume(encodeChar(char))
}
Loading
Loading