scenario channel [
run [
1:address:shared:channel <- new-channel 3/capacity
1:address:shared:channel <- write 1:address:shared:channel, 34
2:character, 1:address:shared:channel <- read 1:address:shared:channel
]
memory-should-contain [
2 <- 34
]
]
container channel [
first-full:number
first-free:number
data:address:shared:array:character
]
def new-channel capacity:number -> result:address:shared:channel [
local-scope
load-ingredients
result <- new channel:type
full:address:number <- get-address *result, first-full:offset
*full <- copy 0
free:address:number <- get-address *result, first-free:offset
*free <- copy 0
capacity <- add capacity, 1
dest:address:address:shared:array:character <- get-address *result, data:offset
*dest <- new character:type, capacity
]
def write chan:address:shared:channel, val:character -> chan:address:shared:channel [
local-scope
load-ingredients
{
full:boolean <- channel-full? chan
break-unless full
full-address:address:number <- get-address *chan, first-full:offset
wait-for-location *full-address
}
circular-buffer:address:shared:array:character <- get *chan, data:offset
free:address:number <- get-address *chan, first-free:offset
dest:address:character <- index-address *circular-buffer, *free
*dest <- copy val
*free <- add *free, 1
{
len:number <- length *circular-buffer
at-end?:boolean <- greater-or-equal *free, len
break-unless at-end?
*free <- copy 0
}
]
def read chan:address:shared:channel -> result:character, chan:address:shared:channel [
local-scope
load-ingredients
{
empty?:boolean <- channel-empty? chan
break-unless empty?
free-address:address:number <- get-address *chan, first-free:offset
wait-for-location *free-address
}
full:address:number <- get-address *chan, first-full:offset
circular-buffer:address:shared:array:character <- get *chan, data:offset
result <- index *circular-buffer, *full
*full <- add *full, 1
{
len:number <- length *circular-buffer
at-end?:boolean <- greater-or-equal *full, len
break-unless at-end?
*full <- copy 0
}
]
def clear-channel chan:address:shared:channel -> chan:address:shared:channel [
local-scope
load-ingredients
{
empty?:boolean <- channel-empty? chan
break-if empty?
_, chan <- read chan
}
]
scenario channel-initialization [
run [
1:address:shared:channel <- new-channel 3/capacity
2:number <- get *1:address:shared:channel, first-full:offset
3:number <- get *1:address:shared:channel, first-free:offset
]
memory-should-contain [
2 <- 0
3 <- 0
]
]
scenario channel-write-increments-free [
run [
1:address:shared:channel <- new-channel 3/capacity
1:address:shared:channel <- write 1:address:shared:channel, 34
2:number <- get *1:address:shared:channel, first-full:offset
3:number <- get *1:address:shared:channel, first-free:offset
]
memory-should-contain [
2 <- 0
3 <- 1
]
]
scenario channel-read-increments-full [
run [
1:address:shared:channel <- new-channel 3/capacity
1:address:shared:channel <- write 1:address:shared:channel, 34
_, 1:address:shared:channel <- read 1:address:shared:channel
2:number <- get *1:address:shared:channel, first-full:offset
3:number <- get *1:address:shared:channel, first-free:offset
]
memory-should-contain [
2 <- 1
3 <- 1
]
]
scenario channel-wrap [
run [
1:address:shared:channel <- new-channel 1/capacity
1:address:shared:channel <- write 1:address:shared:channel, 34
_, 1:address:shared:channel <- read 1:address:shared:channel
2:number <- get *1:address:shared:channel, first-free:offset
3:number <- get *1:address:shared:channel, first-free:offset
1:address:shared:channel <- write 1:address:shared:channel, 34
4:number <- get *1:address:shared:channel, first-free:offset
_, 1:address:shared:channel <- read 1:address:shared:channel
5:number <- get *1:address:shared:channel, first-full:offset
]
memory-should-contain [
2 <- 1
3 <- 1
4 <- 0
5 <- 0
]
]
def channel-empty? chan:address:shared:channel -> 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:shared:channel -> result:boolean [
local-scope
load-ingredients
tmp:number <- get *chan, first-free:offset
tmp <- add tmp, 1
{
len:number <- channel-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 channel-capacity chan:address:shared:channel -> result:number [
local-scope
load-ingredients
q:address:shared:array:character <- get *chan, data:offset
result <- length *q
]
scenario channel-new-empty-not-full [
run [
1:address:shared:channel <- new-channel 3/capacity
2:boolean <- channel-empty? 1:address:shared:channel
3:boolean <- channel-full? 1:address:shared:channel
]
memory-should-contain [
2 <- 1
3 <- 0
]
]
scenario channel-write-not-empty [
run [
1:address:shared:channel <- new-channel 3/capacity
1:address:shared:channel <- write 1:address:shared:channel, 34
2:boolean <- channel-empty? 1:address:shared:channel
3:boolean <- channel-full? 1:address:shared:channel
]
memory-should-contain [
2 <- 0
3 <- 0
]
]
scenario channel-write-full [
run [
1:address:shared:channel <- new-channel 1/capacity
1:address:shared:channel <- write 1:address:shared:channel, 34
2:boolean <- channel-empty? 1:address:shared:channel
3:boolean <- channel-full? 1:address:shared:channel
]
memory-should-contain [
2 <- 0
3 <- 1
]
]
scenario channel-read-not-full [
run [
1:address:shared:channel <- new-channel 1/capacity
1:address:shared:channel <- write 1:address:shared:channel, 34
_, 1:address:shared:channel <- read 1:address:shared:channel
2:boolean <- channel-empty? 1:address:shared:channel
3:boolean <- channel-full? 1:address:shared:channel
]
memory-should-contain [
2 <- 1
3 <- 0
]
]
def buffer-lines in:address:shared:channel, out:address:shared:channel -> out:address:shared:channel, in:address:shared:channel [
local-scope
load-ingredients
{
line:address:shared:buffer <- new-buffer 30
{
+next-character
c:character, in <- read in
{
backspace?:boolean <- equal c, 8
break-unless backspace?
{
buffer-length:address:number <- get-address *line, length:offset
buffer-empty?:boolean <- equal *buffer-length, 0
break-if buffer-empty?
*buffer-length <- subtract *buffer-length, 1
}
loop +next-character:label
}
line <- append line, c
line-done?:boolean <- equal c, 10/newline
break-if line-done?
eof?:boolean <- equal c, 0/eof
break-if eof?
loop
}
i:number <- copy 0
line-contents:address:shared: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
out <- write out, c
i <- add i, 1
loop
}
loop
}
]
scenario buffer-lines-blocks-until-newline [
run [
1:address:shared:channel/stdin <- new-channel 10/capacity
2:address:shared:channel/buffered-stdin <- new-channel 10/capacity
3:boolean <- channel-empty? 2:address:shared:channel/buffered-stdin
assert 3:boolean, [
F buffer-lines-blocks-until-newline: channel should be empty after init]
4:number/buffer-routine <- start-running buffer-lines, 1:address:shared:channel/stdin, 2:address:shared:channel/buffered-stdin
wait-for-routine 4:number/buffer-routine
5:boolean <- channel-empty? 2:address:shared:channel/buffered-stdin
assert 5:boolean, [
F buffer-lines-blocks-until-newline: channel should be empty after buffer-lines bring-up]
1:address:shared:channel <- write 1:address:shared:channel, 97/a
restart 4:number/buffer-routine
wait-for-routine 4:number/buffer-routine
6:boolean <- channel-empty? 2:address:shared:channel/buffered-stdin
assert 6:boolean, [
F buffer-lines-blocks-until-newline: channel should be empty after writing 'a']
1:address:shared:channel <- write 1:address:shared:channel, 98/b
restart 4:number/buffer-routine
wait-for-routine 4:number/buffer-routine
7:boolean <- channel-empty? 2:address:shared:channel/buffered-stdin
assert 7:boolean, [
F buffer-lines-blocks-until-newline: channel should be empty after writing 'b']
1:address:shared:channel <- write 1:address:shared:channel, 10/newline
restart 4:number/buffer-routine
wait-for-routine 4:number/buffer-routine
8:boolean <- channel-empty? 2:address:shared:channel/buffered-stdin
9:boolean/completed? <- not 8:boolean
assert 9:boolean/completed?, [
F buffer-lines-blocks-until-newline: channel should contain data after writing newline]
trace 1, [test], [reached end]
]
trace-should-contain [
test: reached end
]
]