Skip to content

Commit a41dde5

Browse files
phischub-studios
andauthored
Basic streaming (#527)
Effects for push and pulls streams and handlers for them. --------- Co-authored-by: Jonathan Brachthäuser <jonathan@b-studios.de>
1 parent 762e939 commit a41dde5

15 files changed

Lines changed: 641 additions & 53 deletions

File tree

effekt/jvm/src/test/scala/effekt/ChezSchemeTests.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ abstract class ChezSchemeTests extends EffektTests {
2121

2222
examplesDir / "llvm",
2323

24-
// bidirectional handlers
25-
examplesDir / "pos" / "maps.effekt",
26-
2724
// bidirectional effects are not yet supported in our Chez backend
25+
examplesDir / "pos" / "maps.effekt",
2826
examplesDir / "pos" / "bidirectional",
2927
examplesDir / "pos" / "object",
3028
examplesDir / "pos" / "type_omission_op.effekt",
29+
examplesDir / "benchmarks" / "input_output" / "word_count_ascii.effekt",
30+
examplesDir / "benchmarks" / "input_output" / "word_count_utf8.effekt",
3131

3232
// unsafe continuations are not yet supported in our Chez backend
3333
examplesDir / "pos" / "unsafe_cont.effekt",

effekt/jvm/src/test/scala/effekt/LLVMTests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class LLVMTests extends EffektTests {
3636
examplesDir / "benchmarks" / "are_we_fast_yet" / "towers.effekt",
3737
examplesDir / "benchmarks" / "are_we_fast_yet" / "permute.effekt",
3838
examplesDir / "benchmarks" / "are_we_fast_yet" / "queens.effekt",
39+
examplesDir / "benchmarks" / "input_output" / "word_count_ascii.effekt",
40+
examplesDir / "benchmarks" / "input_output" / "word_count_utf8.effekt",
3941
)
4042

4143
/**

effekt/shared/src/main/scala/effekt/core/PolymorphismBoxing.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ object PolymorphismBoxing extends Phase[CoreTransformed, CoreTransformed] {
489489
case core.Type.TDouble => Pure.Literal(13.37, core.Type.TDouble)
490490
// Do strings need to be boxed? Really?
491491
case core.Type.TString => Pure.Literal("<?nothing>", core.Type.TString)
492+
case core.Type.TByte => Pure.Literal(1337, core.Type.TByte)
492493
case t if box.isDefinedAt(t) => sys error s"No default value defined for ${t}"
493494
case _ => sys error s"Trying to unbox Nothing to ${t}"
494495
}

effekt/shared/src/main/scala/effekt/core/Transformer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ object Transformer extends Phase[Typechecked, CoreTransformed] {
640640
val resumeFun: core.BlockLit = core.BlockLit(Nil, List(resumeArgCapture), Nil, List(resumeArgParam),
641641
core.Stmt.Resume(resumeVar, core.Stmt.App(resumeArgVar, Nil, Nil, bvars)))
642642

643-
core.Operation(op.definition, tps, Nil, vps, bparams,
643+
core.Operation(op.definition, tps, cps, vps, bparams,
644644
core.Shift(prompt, core.BlockLit(Nil, List(resumeCapture), Nil, resumeParam :: Nil,
645645
core.Scope(List(core.Definition.Def(resumeSymbol, resumeFun)),
646646
transform(body)))))
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
6170
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import examples/benchmarks/runner
2+
3+
import io/error
4+
import io/filesystem
5+
import stream
6+
7+
record Output(chars: Int, words: Int, lines: Int)
8+
9+
def formatWith(output: Output, filename: String): String =
10+
output.lines.show ++ " " ++ output.words.show ++ " " ++ output.chars.show ++ " " ++ filename
11+
12+
def isSpace(b: Byte) = b.toInt match {
13+
case 32 => true // ' '
14+
case 9 => true // '\t'
15+
case 10 => true // '\n'
16+
case 11 => true // '\v'
17+
case 12 => true // '\f'
18+
case 13 => true // '\r'
19+
case _ => false
20+
}
21+
22+
def isNewline(b: Byte) = b.toInt == 10 // \n
23+
24+
def countWords(): Output / read[Byte] = {
25+
26+
var chars = 0
27+
var words = 0
28+
var lines = 0
29+
var wasSpace = true
30+
31+
exhaustively { do read[Byte]() } { c =>
32+
33+
chars = chars + 1
34+
35+
val currentIsSpace = isSpace(c)
36+
37+
if (wasSpace && not(currentIsSpace)) {
38+
words = words + 1
39+
}
40+
wasSpace = currentIsSpace
41+
42+
if (isNewline(c)) {
43+
lines = lines + 1
44+
}
45+
}
46+
47+
Output(chars, words, lines)
48+
}
49+
50+
def run(n: Int) = {
51+
with on[IOError].panic;
52+
53+
val filename = "/tmp/word_count_ascii.txt"
54+
55+
val _ = {
56+
with writeFile(filename)
57+
with repeat(n)
58+
for[Int] { range(32, 127) } { c =>
59+
repeat(10) {
60+
do emit(c.toByte)
61+
}
62+
do emit(10.toByte)
63+
}
64+
}
65+
66+
val output = {
67+
with readFile(filename)
68+
countWords()
69+
}
70+
71+
return output.chars + output.words + output.lines
72+
}
73+
74+
def main() = benchmark(5){run}
75+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
650
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import examples/benchmarks/runner
2+
3+
import io/error
4+
import io/filesystem
5+
import char
6+
import stream
7+
8+
record Output(chars: Int, words: Int, lines: Int)
9+
10+
def formatWith(output: Output, filename: String): String =
11+
output.lines.show ++ " " ++ output.words.show ++ " " ++ output.chars.show ++ " " ++ filename
12+
13+
def countWords(): Output / read[Char] = {
14+
15+
var chars = 0
16+
var words = 0
17+
var lines = 0
18+
var wasSpace = true
19+
20+
exhaustively[Char] { do read() } { c =>
21+
22+
chars = chars + 1
23+
24+
val currentIsSpace = isWhitespace(c)
25+
26+
if (wasSpace && not(currentIsSpace)) {
27+
words = words + 1
28+
}
29+
wasSpace = currentIsSpace
30+
31+
if (c == '\n') {
32+
lines = lines + 1
33+
}
34+
}
35+
36+
Output(chars, words, lines)
37+
}
38+
39+
def run(n: Int) = {
40+
with on[IOError].panic;
41+
42+
val filename = "/tmp/word_count_utf8.txt"
43+
44+
val _ = {
45+
with writeFile(filename)
46+
with encodeUTF8
47+
with repeat(n)
48+
for[Int] { range(128512, 128522) } { c =>
49+
repeat(10) {
50+
do emit(c.toChar)
51+
}
52+
do emit(10.toChar)
53+
}
54+
}
55+
56+
val output = {
57+
with readFile(filename)
58+
with decodeUTF8
59+
countWords()
60+
}
61+
62+
return output.chars + output.words + output.lines
63+
}
64+
65+
def main() = benchmark(5){run}
66+

examples/stdlib/bytearray/bytearray.effekt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ def main() = {
1111

1212
b.unsafeSet(1, 102.toByte)
1313

14-
println(b.toUTF8) // hfllo
14+
println(b.toString) // hfllo
1515
}

libraries/common/array.effekt

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -77,30 +77,7 @@ extern global def unsafeSet[T](arr: Array[T], index: Int, value: T): Unit =
7777
ret %Pos %z
7878
"""
7979

80-
/// Creates a copy of `arr`
81-
def copy[T](arr: Array[T]): Array[T] = {
82-
with on[OutOfBounds].default { <> }; // should not happen
83-
val len = arr.size;
84-
val newArray = allocate[T](len);
85-
copy[T](arr, 0, newArray, 0, len);
86-
newArray
87-
}
8880

89-
/// Copies `length`-many elements from `from` to `to`
90-
/// starting at `start` (in `from`) and `offset` (in `to`)
91-
def copy[T](from: Array[T], start: Int, to: Array[T], offset: Int, length: Int): Unit / Exception[OutOfBounds] = {
92-
val startValid = start >= 0 && start + length <= from.size
93-
val offsetValid = offset >= 0 && offset + length <= to.size
94-
95-
def go(i: Int, j: Int, length: Int): Unit =
96-
if (length > 0) {
97-
to.unsafeSet(j, from.unsafeGet(i))
98-
go(i + 1, j + 1, length - 1)
99-
}
100-
101-
if (startValid && offsetValid) go(start, offset, length)
102-
else do raise(OutOfBounds(), "Array index out of bounds, when copying")
103-
}
10481

10582
// Derived operations:
10683

@@ -126,6 +103,44 @@ def build[T](size: Int) { index: Int => T }: Array[T] = {
126103
arr
127104
}
128105

106+
def resize[T](source: Array[T], size: Int): Array[T] = {
107+
val target = allocate(size)
108+
val n = min(source.size, target.size)
109+
def go(i: Int): Array[T] =
110+
if (i < n) {
111+
target.unsafeSet(i, source.unsafeGet(i))
112+
go(i + 1)
113+
} else {
114+
target
115+
}
116+
go(0)
117+
}
118+
119+
/**
120+
* Creates a copy of `arr`
121+
*/
122+
def copy[T](array: Array[T]): Array[T] =
123+
array.resize(array.size)
124+
125+
/**
126+
* Copies `length`-many elements from `from` to `to`
127+
* starting at `start` (in `from`) and `offset` (in `to`)
128+
*/
129+
def copy[T](from: Array[T], start: Int, to: Array[T], offset: Int, length: Int): Unit / Exception[OutOfBounds] = {
130+
val startValid = start >= 0 && start + length <= from.size
131+
val offsetValid = offset >= 0 && offset + length <= to.size
132+
133+
def go(i: Int, j: Int, length: Int): Unit =
134+
if (length > 0) {
135+
to.unsafeSet(j, from.unsafeGet(i))
136+
go(i + 1, j + 1, length - 1)
137+
}
138+
139+
if (startValid && offsetValid) go(start, offset, length)
140+
else do raise(OutOfBounds(), "Array index out of bounds, when copying")
141+
}
142+
143+
129144
// Utility functions:
130145

131146
def toList[T](arr: Array[T]): List[T] = {
@@ -140,26 +155,22 @@ def toList[T](arr: Array[T]): List[T] = {
140155

141156
def foreach[T](arr: Array[T]){ action: T => Unit }: Unit =
142157
each(0, arr.size) { i =>
143-
val x: T = arr.unsafeGet(i)
144-
action(x)
158+
action(arr.unsafeGet(i))
145159
}
146160

147161
def foreach[T](arr: Array[T]){ action: (T) {Control} => Unit }: Unit =
148162
each(0, arr.size) { (i) {label} =>
149-
val x: T = arr.unsafeGet(i)
150-
action(x) {label}
163+
action(arr.unsafeGet(i)) {label}
151164
}
152165

153166
def foreachIndex[T](arr: Array[T]){ action: (Int, T) => Unit }: Unit =
154167
each(0, arr.size) { i =>
155-
val x: T = arr.unsafeGet(i)
156-
action(i, x)
168+
action(i, arr.unsafeGet(i))
157169
}
158170

159171
def foreachIndex[T](arr: Array[T]){ action: (Int, T) {Control} => Unit }: Unit =
160172
each(0, arr.size) { (i) {label} =>
161-
val x: T = arr.unsafeGet(i)
162-
action(i, x) {label}
173+
action(i, arr.unsafeGet(i)) {label}
163174
}
164175

165176
def sum(list: Array[Int]): Int = {

0 commit comments

Comments
 (0)