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