scenario channel [
run [
local-scope
source:address:source:number, sink:address:sink:number <- new-channel 3/capacity
sink <- write sink, 34
10:number/raw, 11:boolean/raw, source <- read source
]
memory-should-contain [
10 <- 34
11 <- 0
]
]
container channel:_elem [
first-full:number
first-free:number
data:address:array:_elem
]
container source:_elem [
chan:address:channel:_elem
]
container sink:_elem [
chan:address:channel:_elem
]
def new-channel capacity:number -> in:address:source:_elem, out:address:sink:_elem [
local-scope
load-ingredients
result:address:channel:_elem <- new {(channel _elem): type}
*result <- put *result, first-full:offset, 0
*result <- put *result, first-free:offset, 0
capacity <- add capacity, 1
data:address:array:_elem <- new _elem:type, capacity
*result <- put *result, data:offset, data
in <- new {(source _elem): type}
*in <- put *in, chan:offset, result
out <- new {(sink _elem): type}
*out <- put *out, chan:offset, result
]
def write out:address:sink:_elem, val:_elem -> out:address:sink:_elem [
local-scope
load-ingredients
chan:address:channel:_elem <- get *out, chan:offset
<channel-write-initial>
{
full:boolean <- channel-full? chan
break-unless full
full-address:location <- get-location *chan, first-full:offset
wait-for-location full-address
}
circular-buffer:address:array:_elem <- get *chan, data:offset
free:number <- get *chan, first-free:offset
val-copy:_elem <- deep-copy val
*circular-buffer <- put-index *circular-buffer, free, val-copy
free <- add free, 1
{
len:number <- length *circular-buffer
at-end?:boolean <- greater-or-equal free, len
break-unless at-end?
free <- copy 0
}
*chan <- put *chan, first-free:offset, free
]
def read in:address:source:_elem -> result:_elem, eof?:boolean, in:address:source:_elem [
local-scope
load-ingredients
eof? <- copy 0/false
chan:address:channel:_elem <- get *in, chan:offset
{
empty?:boolean <- channel-empty? chan
break-unless empty?
<channel-read-empty>
free-address:location <- get-location *chan, first-free:offset
wait-for-location free-address
}
full:number <- get *chan, first-full:offset
circular-buffer:address:array:_elem <- get *chan, data:offset
result <- index *circular-buffer, full
empty:address:_elem <- new _elem:type
*circular-buffer <- put-index *circular-buffer, full, *empty
full <- add full, 1
{
len:number <- length *circular-buffer
at-end?:boolean <- greater-or-equal full, len
break-unless at-end?
full <- copy 0
}
*chan <- put *chan, first-full:offset, full
]
def clear in:address:source:_elem -> in:address:source:_elem [
local-scope
load-ingredients
chan:address:channel:_elem <- get *in, chan:offset
{
empty?:boolean <- channel-empty? chan
break-if empty?
_, _, in <- read in
}
]
scenario channel-initialization [
run [
local-scope
source:address:source:number <- new-channel 3/capacity
chan:address:channel:number <- get *source, chan:offset
10:number/raw <- get *chan, first-full:offset
11:number/raw <- get *chan, first-free:offset
]
memory-should-contain [
10 <- 0
11 <- 0
]
]
scenario channel-write-increments-free [
run [
local-scope
_, sink:address:sink:number <- new-channel 3/capacity
sink <- write sink, 34
chan:address:channel:number <- get *sink, chan:offset
10:number/raw <- get *chan, first-full:offset
11:number/raw <- get *chan, first-free:offset
]
memory-should-contain [
10 <- 0
11 <- 1
]
]
scenario channel-read-increments-full [
run [
local-scope
source:address:source:number, sink:address:sink:number <- new-channel 3/capacity
sink <- write sink, 34
_, _, source <- read source
chan:address:channel:number <- get *source, chan:offset
10:number/raw <- get *chan, first-full:offset
11:number/raw <- get *chan, first-free:offset
]
memory-should-contain [
10 <- 1
11 <- 1
]
]
scenario channel-wrap [
run [
local-scope
source:address:source:number, sink:address:sink:number <- new-channel 1/capacity
chan:address:channel:number <- get *source, chan:offset
sink <- write sink, 34
_, _, source <- read source
10:number/raw <- get *chan, first-free:offset
11:number/raw <- get *chan, first-free:offset
sink <- write sink, 34
20:number/raw <- get *chan, first-free:offset
_, _, source <- read source
30:number/raw <- get *chan, first-full:offset
]
memory-should-contain [
10 <- 1
11 <- 1
20 <- 0
30 <- 0
]
]
scenario channel-new-empty-not-full [
run [
local-scope
source:address:source:number <- new-channel 3/capacity
chan:address:channel:number <- get *source, chan:offset
10:boolean/raw <- channel-empty? chan
11:boolean/raw <- channel-full? chan
]
memory-should-contain [
10 <- 1
11 <- 0
]
]
scenario channel-write-not-empty [
run [
source:address:source:number, sink:address:sink:number <- new-channel 3/capacity
chan:address:channel:number <- get *source, chan:offset
sink <- write sink, 34
10:boolean/raw <- channel-empty? chan
11:boolean/raw <- channel-full? chan
]
memory-should-contain [
10 <- 0
11 <- 0
]
]
scenario channel-write-full [
run [
local-scope
source:address:source:number, sink:address:sink:number <- new-channel 1/capacity
chan:address:channel:number <- get *source, chan:offset
sink <- write sink, 34
10:boolean/raw <- channel-empty? chan
11:boolean/raw <- channel-full? chan
]
memory-should-contain [
10 <- 0
11 <- 1
]
]
scenario channel-read-not-full [
run [
local-scope
source:address:source:number, sink:address:sink:number <- new-channel 1/capacity
chan:address:channel:number <- get *source, chan:offset
sink <- write sink, 34
_, _, source <- read source
10:boolean/raw <- channel-empty? chan
11:boolean/raw <- channel-full? chan
]
memory-should-contain [
10 <- 1
11 <- 0
]
]
container channel:_elem [
closed?:boolean
]
def close x:address:source:_elem -> x:address:source:_elem [
local-scope
load-ingredients
chan:address:channel:_elem <- get *x, chan:offset
*chan <- put *chan, closed?:offset, 1/true
]
def close x:address:sink:_elem -> x:address:sink:_elem [
local-scope
load-ingredients
chan:address:channel:_elem <- get *x, chan:offset
*chan <- put *chan, closed?:offset, 1/true
]
after <channel-write-initial> [
closed?:boolean <- get *chan, closed?:offset
return-if closed?
]
after <channel-read-empty> [
closed?:boolean <- get *chan, closed?:offset
{
break-unless closed?
empty-result:address:_elem <- new _elem:type
return *empty-result, 1/true
}
]
def channel-empty? chan:address:channel:_elem -> result:boolean [
local-scope
load-ingredients
full:number <- get *chan, first-full:offset
free:number <- get *chan, first-free:offset
result <- equal full, free
]
def channel-full? chan:address:channel:_elem -> result:boolean [
local-scope
load-ingredients
tmp:number <- get *chan, first-free:offset
tmp <- add tmp, 1
{
len:number <- capacity chan
at-end?:boolean <- greater-or-equal tmp, len
break-unless at-end?
tmp <- copy 0
}
full:number <- get *chan, first-full:offset
result <- equal full, tmp
]
def capacity chan:address:channel:_elem -> result:number [
local-scope
load-ingredients
q:address:array:_elem <- get *chan, data:offset
result <- length *q
]
def buffer-lines in:address:source:character, buffered-out:address:sink:character -> buffered-out:address:sink:character, in:address:source:character [
local-scope
load-ingredients
eof?:boolean <- copy 0/false
{
line:address:buffer <- new-buffer 30
{
+next-character
c:character, eof?:boolean, in <- read in
break-if eof?
{
backspace?:boolean <- equal c, 8
break-unless backspace?
{
buffer-length:number <- get *line, length:offset
buffer-empty?:boolean <- equal buffer-length, 0
break-if buffer-empty?
buffer-length <- subtract buffer-length, 1
*line <- put *line, length:offset, buffer-length
}
loop +next-character:label
}
line <- append line, c
line-done?:boolean <- equal c, 10/newline
break-if line-done?
loop
}
i:number <- copy 0
line-contents:address:array:character <- get *line, data:offset
max:number <- get *line, length:offset
{
done?:boolean <- greater-or-equal i, max
break-if done?
c:character <- index *line-contents, i
buffered-out <- write buffered-out, c
i <- add i, 1
loop
}
{
break-unless eof?
buffered-out <- close buffered-out
return
}
loop
}
]
scenario buffer-lines-blocks-until-newline [
run [
local-scope
source:address:source:character, sink:address:sink:character <- new-channel 10/capacity
_, buffered-stdin:address:sink:character/buffered-stdin <- new-channel 10/capacity
buffered-chan:address:channel:character <- get *buffered-stdin, chan:offset
empty?:boolean <- channel-empty? buffered-chan
assert empty?, [
F buffer-lines-blocks-until-newline: channel should be empty after init]
buffer-routine:number <- start-running buffer-lines, source, buffered-stdin
wait-for-routine buffer-routine
empty? <- channel-empty? buffered-chan
assert empty?:boolean, [
F buffer-lines-blocks-until-newline: channel should be empty after buffer-lines bring-up]
sink <- write sink, 97/a
restart buffer-routine
wait-for-routine buffer-routine
empty? <- channel-empty? buffered-chan
assert empty?:boolean, [
F buffer-lines-blocks-until-newline: channel should be empty after writing 'a']
sink <- write sink, 98/b
restart buffer-routine
wait-for-routine buffer-routine
empty? <- channel-empty? buffered-chan
assert empty?:boolean, [
F buffer-lines-blocks-until-newline: channel should be empty after writing 'b']
sink <- write sink, 10/newline
restart buffer-routine
wait-for-routine buffer-routine
empty? <- channel-empty? buffered-chan
data-emitted?:boolean <- not empty?
assert data-emitted?, [
F buffer-lines-blocks-until-newline: channel should contain data after writing newline]
trace 1, [test], [reached end]
]
trace-should-contain [
test: reached end
]
]