Skip to content
Open
Show file tree
Hide file tree
Changes from all 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: 2 additions & 0 deletions effekt/js/src/main/scala/effekt/EffektConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ trait EffektConfig {
def prelude(): List[String] = List(
"effekt",
"option",
"stream",
"list",
"result",
"exception",
"array",
"char",
"bytearray",
"string",
"ref"
)
Expand Down
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 effekt/jvm/src/test/scala/effekt/LSPTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1380,7 +1380,7 @@ class LSPTests extends FunSuite {
val expectedIRContents =
raw"""ModuleDecl(
| test,
| List(effekt, option, list, result, exception, array, char, string, ref),
| List(effekt, option, stream, list, result, exception, array, char, bytearray, string, ref),
| Nil,
| Nil,
| List(
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
2 changes: 1 addition & 1 deletion examples/stdlib/array/map.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ def main() = {
arr.map { x => x * 2 }
println(arr)

val arr1 = allocate(0)
val arr1 = array::allocate(0)
arr1.map { x => x * 2 }
println(arr1)
}
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
3 changes: 1 addition & 2 deletions examples/stdlib/stream/fibonacci.effekt
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import stream

def main() = {
val max = 10

val fibs = collectList[Int] {
val fibs = list::collect[Int] {
var a = 0
var b = 1

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