about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--058shape_shifting_recipe.cc15
-rw-r--r--072channel.mu217
-rw-r--r--084console.mu2
-rw-r--r--chessboard.mu186
4 files changed, 231 insertions, 189 deletions
diff --git a/058shape_shifting_recipe.cc b/058shape_shifting_recipe.cc
index 784b1fd0..2046c622 100644
--- a/058shape_shifting_recipe.cc
+++ b/058shape_shifting_recipe.cc
@@ -313,6 +313,7 @@ void compute_type_ingredient_mappings(const recipe& exemplar, const instruction&
   for (int i = 0; i < limit; ++i) {
     const reagent& exemplar_reagent = exemplar.products.at(i);
     reagent product = inst.products.at(i);
+    if (is_dummy(product)) continue;
     canonize_type(product);
     accumulate_type_ingredients(exemplar_reagent, product, mappings, exemplar, inst, caller_recipe, error);
   }
@@ -331,7 +332,8 @@ void accumulate_type_ingredients(const type_tree* exemplar_type, const type_tree
   if (!exemplar_type) return;
   if (!refinement_type) {
     // todo: make this smarter; only flag an error if exemplar_type contains some *new* type ingredient
-    raise << maybe(exemplar.name) << "missing type ingredient in " << exemplar_reagent.original_string << '\n' << end();
+    raise << maybe(exemplar.name) << "missing type ingredient for " << exemplar_reagent.original_string << '\n' << end();
+    raise << "  (called from '" << to_string(call_instruction) << "')\n" << end();
     return;
   }
   if (is_type_ingredient_name(exemplar_type->name)) {
@@ -649,6 +651,17 @@ container foo:_t [
 +mem: storing 0 in location 12
 +mem: storing 0 in location 13
 
+:(scenario shape_shifting_recipe_called_with_dummy)
+def main [
+  _ <- bar 34
+]
+def bar x:_t -> result:address:shared:_t [
+  local-scope
+  load-ingredients
+  result <- copy 0
+]
+$error: 0
+
 :(code)
 // this one needs a little more fine-grained control
 void test_shape_shifting_new_ingredient_does_not_pollute_global_namespace() {
diff --git a/072channel.mu b/072channel.mu
index 28d5c7fe..f1a4480d 100644
--- a/072channel.mu
+++ b/072channel.mu
@@ -10,12 +10,12 @@
 
 scenario channel [
   run [
-    1:address:shared:channel:number <- new-channel 3/capacity
-    1:address:shared:channel:number <- write 1:address:shared:channel:number, 34
-    2:number, 1:address:shared:channel:number <- read 1:address:shared:channel:number
+    1:address:shared:source:number, 2:address:shared:sink:number <- new-channel 3/capacity
+    2:address:shared:sink:number <- write 2:address:shared:sink:number, 34
+    3:number, 1:address:shared:source:number <- read 1:address:shared:source:number
   ]
   memory-should-contain [
-    2 <- 34
+    3 <- 34
   ]
 ]
 
@@ -31,10 +31,21 @@ container channel:_elem [
   data:address:shared:array:_elem
 ]
 
-def new-channel capacity:number -> result:address:shared:channel:_elem [
+# Since channels have two ends, and since it's an error to use either end from
+# multiple routines, let's distinguish the ends.
+
+container source:_elem [
+  chan:address:shared:channel:_elem
+]
+
+container sink:_elem [
+  chan:address:shared:channel:_elem
+]
+
+def new-channel capacity:number -> in:address:shared:source:_elem, out:address:shared:sink:_elem [
   local-scope
   load-ingredients
-  result <- new {(channel _elem): type}
+  result:address:shared:channel:_elem <- new {(channel _elem): type}
   # result.first-full = 0
   full:address:number <- get-address *result, first-full:offset
   *full <- copy 0
@@ -45,11 +56,18 @@ def new-channel capacity:number -> result:address:shared:channel:_elem [
   capacity <- add capacity, 1  # unused slot for 'full?' below
   dest:address:address:shared:array:_elem <- get-address *result, data:offset
   *dest <- new _elem:type, capacity
+  in <- new {(source _elem): type}
+  chan:address:address:shared:channel:_elem <- get-address *in, chan:offset
+  *chan <- copy result
+  out <- new {(sink _elem): type}
+  chan:address:address:shared:channel:_elem <- get-address *out, chan:offset
+  *chan <- copy result
 ]
 
-def write chan:address:shared:channel:_elem, val:_elem -> chan:address:shared:channel:_elem [
+def write out:address:shared:sink:_elem, val:_elem -> out:address:shared:sink:_elem [
   local-scope
   load-ingredients
+  chan:address:shared:channel:_elem <- get *out, chan:offset
   {
     # block if chan is full
     full:boolean <- channel-full? chan
@@ -73,9 +91,10 @@ def write chan:address:shared:channel:_elem, val:_elem -> chan:address:shared:ch
   }
 ]
 
-def read chan:address:shared:channel:_elem -> result:_elem, chan:address:shared:channel:_elem [
+def read in:address:shared:source:_elem -> result:_elem, in:address:shared:source:_elem [
   local-scope
   load-ingredients
+  chan:address:shared:channel:_elem <- get *in, chan:offset
   {
     # block if chan is empty
     empty?:boolean <- channel-empty? chan
@@ -98,77 +117,82 @@ def read chan:address:shared:channel:_elem -> result:_elem, chan:address:shared:
   }
 ]
 
-def clear-channel chan:address:shared:channel:_elem -> chan:address:shared:channel:_elem [
+def clear in:address:shared:source:_elem -> in:address:shared:source:_elem [
   local-scope
   load-ingredients
+  chan:address:shared:channel:_elem <- get *in, chan:offset
   {
     empty?:boolean <- channel-empty? chan
     break-if empty?
-    _, chan <- read chan
+    _, in <- read in
   }
 ]
 
 scenario channel-initialization [
   run [
-    1:address:shared:channel:number <- new-channel 3/capacity
-    2:number <- get *1:address:shared:channel:number, first-full:offset
-    3:number <- get *1:address:shared:channel:number, first-free:offset
+    1:address:shared:source:number <- new-channel 3/capacity
+    2:address:shared:channel:number <- get *1:address:shared:source:number, chan:offset
+    3:number <- get *2:address:shared:channel:number, first-full:offset
+    4:number <- get *2:address:shared:channel:number, first-free:offset
   ]
   memory-should-contain [
-    2 <- 0  # first-full
-    3 <- 0  # first-free
+    3 <- 0  # first-full
+    4 <- 0  # first-free
   ]
 ]
 
 scenario channel-write-increments-free [
   run [
-    1:address:shared:channel:number <- new-channel 3/capacity
-    1:address:shared:channel:number <- write 1:address:shared:channel:number, 34
-    2:number <- get *1:address:shared:channel:number, first-full:offset
-    3:number <- get *1:address:shared:channel:number, first-free:offset
+    _, 1:address:shared:sink:number <- new-channel 3/capacity
+    1:address:shared:sink:number <- write 1:address:shared:sink:number, 34
+    2:address:shared:channel:number <- get *1:address:shared:sink:number, chan:offset
+    3:number <- get *2:address:shared:channel:character, first-full:offset
+    4:number <- get *2:address:shared:channel:character, first-free:offset
   ]
   memory-should-contain [
-    2 <- 0  # first-full
-    3 <- 1  # first-free
+    3 <- 0  # first-full
+    4 <- 1  # first-free
   ]
 ]
 
 scenario channel-read-increments-full [
   run [
-    1:address:shared:channel:number <- new-channel 3/capacity
-    1:address:shared:channel:number <- write 1:address:shared:channel:number, 34
-    _, 1:address:shared:channel:number <- read 1:address:shared:channel:number
-    2:number <- get *1:address:shared:channel:number, first-full:offset
-    3:number <- get *1:address:shared:channel:number, first-free:offset
+    1:address:shared:source:number, 2:address:shared:sink:number <- new-channel 3/capacity
+    2:address:shared:sink:number <- write 2:address:shared:sink:number, 34
+    _, 1:address:shared:source:number <- read 1:address:shared:source:number
+    3:address:shared:channel:number <- get *1:address:shared:source:number, chan:offset
+    4:number <- get *3:address:shared:channel:number, first-full:offset
+    5:number <- get *3:address:shared:channel:number, first-free:offset
   ]
   memory-should-contain [
-    2 <- 1  # first-full
-    3 <- 1  # first-free
+    4 <- 1  # first-full
+    5 <- 1  # first-free
   ]
 ]
 
 scenario channel-wrap [
   run [
     # channel with just 1 slot
-    1:address:shared:channel:number <- new-channel 1/capacity
+    1:address:shared:source:number, 2:address:shared:sink:number <- new-channel 1/capacity
+    3:address:shared:channel:number <- get *1:address:shared:source:number, chan:offset
     # write and read a value
-    1:address:shared:channel:number <- write 1:address:shared:channel:number, 34
-    _, 1:address:shared:channel:number <- read 1:address:shared:channel:number
+    2:address:shared:sink:number <- write 2:address:shared:sink:number, 34
+    _, 1:address:shared:source:number <- read 1:address:shared:source:number
     # first-free will now be 1
-    2:number <- get *1:address:shared:channel:number, first-free:offset
-    3:number <- get *1:address:shared:channel:number, first-free:offset
+    4:number <- get *3:address:shared:channel:number, first-free:offset
+    5:number <- get *3:address:shared:channel:number, first-free:offset
     # write second value, verify that first-free wraps
-    1:address:shared:channel:number <- write 1:address:shared:channel:number, 34
-    4:number <- get *1:address:shared:channel:number, first-free:offset
+    2:address:shared:sink:number <- write 2:address:shared:sink:number, 34
+    6:number <- get *3:address:shared:channel:number, first-free:offset
     # read second value, verify that first-full wraps
-    _, 1:address:shared:channel:number <- read 1:address:shared:channel:number
-    5:number <- get *1:address:shared:channel:number, first-full:offset
+    _, 1:address:shared:source:number <- read 1:address:shared:source:number
+    7:number <- get *3:address:shared:channel:number, first-full:offset
   ]
   memory-should-contain [
-    2 <- 1  # first-free after first write
-    3 <- 1  # first-full after first read
-    4 <- 0  # first-free after second write, wrapped
-    5 <- 0  # first-full after second read, wrapped
+    4 <- 1  # first-free after first write
+    5 <- 1  # first-full after first read
+    6 <- 0  # first-free after second write, wrapped
+    7 <- 0  # first-full after second read, wrapped
   ]
 ]
 
@@ -194,7 +218,7 @@ def channel-full? chan:address:shared:channel:_elem -> result:boolean [
   tmp <- add tmp, 1
   {
     # if tmp == chan.capacity, tmp = 0
-    len:number <- channel-capacity chan
+    len:number <- capacity chan
     at-end?:boolean <- greater-or-equal tmp, len
     break-unless at-end?
     tmp <- copy 0
@@ -204,7 +228,7 @@ def channel-full? chan:address:shared:channel:_elem -> result:boolean [
   result <- equal full, tmp
 ]
 
-def channel-capacity chan:address:shared:channel:_elem -> result:number [
+def capacity chan:address:shared:channel:_elem -> result:number [
   local-scope
   load-ingredients
   q:address:shared:array:_elem <- get *chan, data:offset
@@ -213,58 +237,62 @@ def channel-capacity chan:address:shared:channel:_elem -> result:number [
 
 scenario channel-new-empty-not-full [
   run [
-    1:address:shared:channel:number <- new-channel 3/capacity
-    2:boolean <- channel-empty? 1:address:shared:channel:number
-    3:boolean <- channel-full? 1:address:shared:channel:number
+    1:address:shared:source:number, 2:address:shared:sink:number <- new-channel 3/capacity
+    3:address:shared:channel:number <- get *1:address:shared:source:number, chan:offset
+    4:boolean <- channel-empty? 3:address:shared:channel:number
+    5:boolean <- channel-full? 3:address:shared:channel:number
   ]
   memory-should-contain [
-    2 <- 1  # empty?
-    3 <- 0  # full?
+    4 <- 1  # empty?
+    5 <- 0  # full?
   ]
 ]
 
 scenario channel-write-not-empty [
   run [
-    1:address:shared:channel:number <- new-channel 3/capacity
-    1:address:shared:channel:number <- write 1:address:shared:channel:number, 34
-    2:boolean <- channel-empty? 1:address:shared:channel:number
-    3:boolean <- channel-full? 1:address:shared:channel:number
+    1:address:shared:source:number, 2:address:shared:sink:number <- new-channel 3/capacity
+    3:address:shared:channel:number <- get *1:address:shared:source:number, chan:offset
+    2:address:shared:sink:number <- write 2:address:shared:sink:number, 34
+    4:boolean <- channel-empty? 3:address:shared:channel:number
+    5:boolean <- channel-full? 3:address:shared:channel:number
   ]
   memory-should-contain [
-    2 <- 0  # empty?
-    3 <- 0  # full?
+    4 <- 0  # empty?
+    5 <- 0  # full?
   ]
 ]
 
 scenario channel-write-full [
   run [
-    1:address:shared:channel:number <- new-channel 1/capacity
-    1:address:shared:channel:number <- write 1:address:shared:channel:number, 34
-    2:boolean <- channel-empty? 1:address:shared:channel:number
-    3:boolean <- channel-full? 1:address:shared:channel:number
+    1:address:shared:source:number, 2:address:shared:sink:number <- new-channel 1/capacity
+    3:address:shared:channel:number <- get *1:address:shared:source:number, chan:offset
+    2:address:shared:sink:number <- write 2:address:shared:sink:number, 34
+    4:boolean <- channel-empty? 3:address:shared:channel:number
+    5:boolean <- channel-full? 3:address:shared:channel:number
   ]
   memory-should-contain [
-    2 <- 0  # empty?
-    3 <- 1  # full?
+    4 <- 0  # empty?
+    5 <- 1  # full?
   ]
 ]
 
 scenario channel-read-not-full [
   run [
-    1:address:shared:channel:number <- new-channel 1/capacity
-    1:address:shared:channel:number <- write 1:address:shared:channel:number, 34
-    _, 1:address:shared:channel:number <- read 1:address:shared:channel:number
-    2:boolean <- channel-empty? 1:address:shared:channel:number
-    3:boolean <- channel-full? 1:address:shared:channel:number
+    1:address:shared:source:number, 2:address:shared:sink:number <- new-channel 1/capacity
+    3:address:shared:channel:number <- get *1:address:shared:source:number, chan:offset
+    2:address:shared:sink:number <- write 2:address:shared:sink:number, 34
+    _, 1:address:shared:source:number <- read 1:address:shared:source:number
+    4:boolean <- channel-empty? 3:address:shared:channel:number
+    5:boolean <- channel-full? 3:address:shared:channel:number
   ]
   memory-should-contain [
-    2 <- 1  # empty?
-    3 <- 0  # full?
+    4 <- 1  # empty?
+    5 <- 0  # full?
   ]
 ]
 
 # helper for channels of characters in particular
-def buffer-lines in:address:shared:channel:character, out:address:shared:channel:character -> out:address:shared:channel:character, in:address:shared:channel:character [
+def buffer-lines in:address:shared:source:character, buffered-out:address:shared:sink:character -> buffered-out:address:shared:sink:character, in:address:shared:source:character [
   local-scope
   load-ingredients
   # repeat forever
@@ -298,7 +326,7 @@ def buffer-lines in:address:shared:channel:character, out:address:shared:channel
       break-if eof?
       loop
     }
-    # copy line into 'out'
+    # copy line into 'buffered-out'
     i:number <- copy 0
     line-contents:address:shared:array:character <- get *line, data:offset
     max:number <- get *line, length:offset
@@ -306,7 +334,7 @@ def buffer-lines in:address:shared:channel:character, out:address:shared:channel
       done?:boolean <- greater-or-equal i, max
       break-if done?
       c:character <- index *line-contents, i
-      out <- write out, c
+      buffered-out <- write buffered-out, c
       i <- add i, 1
       loop
     }
@@ -316,38 +344,39 @@ def buffer-lines in:address:shared:channel:character, out:address:shared:channel
 
 scenario buffer-lines-blocks-until-newline [
   run [
-    1:address:shared:channel:character/stdin <- new-channel 10/capacity
-    2:address:shared:channel:character/buffered-stdin <- new-channel 10/capacity
-    3:boolean <- channel-empty? 2:address:shared:channel:character/buffered-stdin
-    assert 3:boolean, [ 
+    1:address:shared:source:number, 2:address:shared:sink:number <- new-channel 10/capacity
+    _, 3:address:shared:sink:number/buffered-stdin <- new-channel 10/capacity
+    4:address:shared:channel:number/buffered-stdin <- get *3:address:shared:source:number, chan:offset
+    5:boolean <- channel-empty? 4:address:shared:channel:character/buffered-stdin
+    assert 5:boolean, [ 
 F buffer-lines-blocks-until-newline: channel should be empty after init]
     # buffer stdin into buffered-stdin, try to read from buffered-stdin
-    4:number/buffer-routine <- start-running buffer-lines, 1:address:shared:channel:character/stdin, 2:address:shared:channel:character/buffered-stdin
-    wait-for-routine 4:number/buffer-routine
-    5:boolean <- channel-empty? 2:address:shared:channel:character/buffered-stdin
-    assert 5:boolean, [ 
+    6:number/buffer-routine <- start-running buffer-lines, 1:address:shared:source:character/stdin, 3:address:shared:sink:character/buffered-stdin
+    wait-for-routine 6:number/buffer-routine
+    7:boolean <- channel-empty? 4:address:shared:channel:character/buffered-stdin
+    assert 7:boolean, [ 
 F buffer-lines-blocks-until-newline: channel should be empty after buffer-lines bring-up]
     # write 'a'
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 97/a
-    restart 4:number/buffer-routine
-    wait-for-routine 4:number/buffer-routine
-    6:boolean <- channel-empty? 2:address:shared:channel:character/buffered-stdin
-    assert 6:boolean, [ 
+    2:address:shared:sink:character <- write 2:address:shared:sink:character, 97/a
+    restart 6:number/buffer-routine
+    wait-for-routine 6:number/buffer-routine
+    8:boolean <- channel-empty? 4:address:shared:channel:character/buffered-stdin
+    assert 8:boolean, [ 
 F buffer-lines-blocks-until-newline: channel should be empty after writing 'a']
     # write 'b'
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 98/b
-    restart 4:number/buffer-routine
-    wait-for-routine 4:number/buffer-routine
-    7:boolean <- channel-empty? 2:address:shared:channel:character/buffered-stdin
-    assert 7:boolean, [ 
+    2:address:shared:sink:character <- write 2:address:shared:sink:character, 98/b
+    restart 6:number/buffer-routine
+    wait-for-routine 6:number/buffer-routine
+    9:boolean <- channel-empty? 4:address:shared:channel:character/buffered-stdin
+    assert 9:boolean, [ 
 F buffer-lines-blocks-until-newline: channel should be empty after writing 'b']
     # write newline
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 10/newline
-    restart 4:number/buffer-routine
-    wait-for-routine 4:number/buffer-routine
-    8:boolean <- channel-empty? 2:address:shared:channel:character/buffered-stdin
-    9:boolean/completed? <- not 8:boolean
-    assert 9:boolean/completed?, [ 
+    2:address:shared:sink:character <- write 2:address:shared:sink:character, 10/newline
+    restart 6:number/buffer-routine
+    wait-for-routine 6:number/buffer-routine
+    10:boolean <- channel-empty? 4:address:shared:channel:character/buffered-stdin
+    11:boolean/completed? <- not 10:boolean
+    assert 11:boolean/completed?, [ 
 F buffer-lines-blocks-until-newline: channel should contain data after writing newline]
     trace 1, [test], [reached end]
   ]
diff --git a/084console.mu b/084console.mu
index 805e28d4..cfa0521a 100644
--- a/084console.mu
+++ b/084console.mu
@@ -72,7 +72,7 @@ def read-key console:address:shared:console -> result:character, console:address
   return *c, console/same-as-ingredient:0, 1/found, 0/quit
 ]
 
-def send-keys-to-channel console:address:shared:console, chan:address:shared:channel:character, screen:address:shared:screen -> console:address:shared:console, chan:address:shared:channel:character, screen:address:shared:screen [
+def send-keys-to-channel console:address:shared:console, chan:address:shared:sink:character, screen:address:shared:screen -> console:address:shared:console, chan:address:shared:sink:character, screen:address:shared:screen [
   local-scope
   load-ingredients
   {
diff --git a/chessboard.mu b/chessboard.mu
index 67374413..23d9bf28 100644
--- a/chessboard.mu
+++ b/chessboard.mu
@@ -70,11 +70,11 @@ def chessboard screen:address:shared:screen, console:address:shared:console -> s
   load-ingredients
   board:address:shared:array:address:shared:array:character <- initial-position
   # hook up stdin
-  stdin:address:shared:channel:character <- new-channel 10/capacity
-  start-running send-keys-to-channel, console, stdin, screen
+  stdin-in:address:shared:source:character, stdin-out:address:shared:sink:character <- new-channel 10/capacity
+  start-running send-keys-to-channel, console, stdin-out, screen
   # buffer lines in stdin
-  buffered-stdin:address:shared:channel:character <- new-channel 10/capacity
-  start-running buffer-lines, stdin, buffered-stdin
+  buffered-stdin-in:address:shared:source:character, buffered-stdin-out:address:shared:sink:character <- new-channel 10/capacity
+  start-running buffer-lines, stdin-in, buffered-stdin-out
   {
     print screen, [Stupid text-mode chessboard. White pieces in uppercase; black pieces in lowercase. No checking for legal moves.
 ]
@@ -89,9 +89,9 @@ def chessboard screen:address:shared:screen, console:address:shared:console -> s
     {
       cursor-to-next-line screen
       screen <- print screen, [move: ]
-      m:address:shared:move, quit:boolean, error:boolean <- read-move buffered-stdin, screen
+      m:address:shared:move, quit:boolean, error:boolean <- read-move buffered-stdin-in, screen
       break-if quit, +quit:label
-      buffered-stdin <- clear-channel buffered-stdin  # cleanup after error. todo: test this?
+      buffered-stdin-in <- clear buffered-stdin-in  # cleanup after error. todo: test this?
       loop-if error
     }
     board <- make-move board, m
@@ -233,7 +233,7 @@ container move [
 ]
 
 # prints only error messages to screen
-def read-move stdin:address:shared:channel:character, screen:address:shared:screen -> result:address:shared:move, quit?:boolean, error?:boolean, stdin:address:shared:channel:character, screen:address:shared:screen [
+def read-move stdin:address:shared:source:character, screen:address:shared:screen -> result:address:shared:move, quit?:boolean, error?:boolean, stdin:address:shared:source:character, screen:address:shared:screen [
   local-scope
   load-ingredients
   from-file:number, quit?:boolean, error?:boolean <- read-file stdin, screen
@@ -263,7 +263,7 @@ def read-move stdin:address:shared:channel:character, screen:address:shared:scre
 ]
 
 # valid values for file: 0-7
-def read-file stdin:address:shared:channel:character, screen:address:shared:screen -> file:number, quit:boolean, error:boolean, stdin:address:shared:channel:character, screen:address:shared:screen [
+def read-file stdin:address:shared:source:character, screen:address:shared:screen -> file:number, quit:boolean, error:boolean, stdin:address:shared:source:character, screen:address:shared:screen [
   local-scope
   load-ingredients
   c:character, stdin <- read stdin
@@ -309,7 +309,7 @@ def read-file stdin:address:shared:channel:character, screen:address:shared:scre
 ]
 
 # valid values: 0-7, -1 (quit), -2 (error)
-def read-rank stdin:address:shared:channel:character, screen:address:shared:screen -> rank:number, quit?:boolean, error?:boolean, stdin:address:shared:channel:character, screen:address:shared:screen [
+def read-rank stdin:address:shared:source:character, screen:address:shared:screen -> rank:number, quit?:boolean, error?:boolean, stdin:address:shared:source:character, screen:address:shared:screen [
   local-scope
   load-ingredients
   c:character, stdin <- read stdin
@@ -350,7 +350,7 @@ def read-rank stdin:address:shared:channel:character, screen:address:shared:scre
 
 # read a character from the given channel and check that it's what we expect
 # return true on error
-def expect-from-channel stdin:address:shared:channel:character, expected:character, screen:address:shared:screen -> result:boolean, stdin:address:shared:channel:character, screen:address:shared:screen [
+def expect-from-channel stdin:address:shared:source:character, expected:character, screen:address:shared:screen -> result:boolean, stdin:address:shared:source:character, screen:address:shared:screen [
   local-scope
   load-ingredients
   c:character, stdin <- read stdin
@@ -365,67 +365,67 @@ def expect-from-channel stdin:address:shared:channel:character, expected:charact
 scenario read-move-blocking [
   assume-screen 20/width, 2/height
   run [
-    1:address:shared:channel:character <- new-channel 2
-    2:number/routine <- start-running read-move, 1:address:shared:channel:character, screen:address:shared:screen
+    1:address:shared:source:character, 2:address:shared:sink:character <- new-channel 2/capacity
+    3:number/routine <- start-running read-move, 1:address:shared:source:character, screen:address:shared:screen
     # 'read-move' is waiting for input
-    wait-for-routine 2:number
-    3:number <- routine-state 2:number/id
-    4:boolean/waiting? <- equal 3:number/routine-state, 3/waiting
-    assert 4:boolean/waiting?, [ 
+    wait-for-routine 3:number
+    4:number <- routine-state 3:number/id
+    5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting
+    assert 5:boolean/waiting?, [ 
 F read-move-blocking: routine failed to pause after coming up (before any keys were pressed)]
     # press 'a'
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 97/a
-    restart 2:number/routine
+    2:address:shared:sink:character <- write 2:address:shared:sink:character, 97/a
+    restart 3:number/routine
     # 'read-move' still waiting for input
-    wait-for-routine 2:number
-    3:number <- routine-state 2:number/id
-    4:boolean/waiting? <- equal 3:number/routine-state, 3/waiting
-    assert 4:boolean/waiting?, [ 
+    wait-for-routine 3:number
+    4:number <- routine-state 3:number/id
+    5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting
+    assert 5:boolean/waiting?, [ 
 F read-move-blocking: routine failed to pause after rank 'a']
     # press '2'
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 50/'2'
-    restart 2:number/routine
+    2:address:shared:sink:character <- write 2:address:shared:sink:character, 50/'2'
+    restart 3:number/routine
     # 'read-move' still waiting for input
-    wait-for-routine 2:number
-    3:number <- routine-state 2:number/id
-    4:boolean/waiting? <- equal 3:number/routine-state, 3/waiting
-    assert 4:boolean/waiting?, [ 
+    wait-for-routine 3:number
+    4:number <- routine-state 3:number/id
+    5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting
+    assert 5:boolean/waiting?, [ 
 F read-move-blocking: routine failed to pause after file 'a2']
     # press '-'
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 45/'-'
-    restart 2:number/routine
+    2:address:shared:sink:character <- write 2:address:shared:sink:character, 45/'-'
+    restart 3:number/routine
     # 'read-move' still waiting for input
-    wait-for-routine 2:number
-    3:number <- routine-state 2:number
-    4:boolean/waiting? <- equal 3:number/routine-state, 3/waiting
-    assert 4:boolean/waiting?/routine-state, [ 
+    wait-for-routine 3:number
+    4:number <- routine-state 3:number
+    5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting
+    assert 5:boolean/waiting?/routine-state, [ 
 F read-move-blocking: routine failed to pause after hyphen 'a2-']
     # press 'a'
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 97/a
-    restart 2:number/routine
+    2:address:shared:sink:character <- write 2:address:shared:sink:character, 97/a
+    restart 3:number/routine
     # 'read-move' still waiting for input
-    wait-for-routine 2:number
-    3:number <- routine-state 2:number
-    4:boolean/waiting? <- equal 3:number/routine-state, 3/waiting
-    assert 4:boolean/waiting?/routine-state, [ 
+    wait-for-routine 3:number
+    4:number <- routine-state 3:number
+    5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting
+    assert 5:boolean/waiting?/routine-state, [ 
 F read-move-blocking: routine failed to pause after rank 'a2-a']
     # press '4'
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 52/'4'
-    restart 2:number/routine
+    2:address:shared:sink:character <- write 2:address:shared:sink:character, 52/'4'
+    restart 3:number/routine
     # 'read-move' still waiting for input
-    wait-for-routine 2:number
-    3:number <- routine-state 2:number
-    4:boolean/waiting? <- equal 3:number/routine-state, 3/waiting
-    assert 4:boolean/waiting?, [ 
+    wait-for-routine 3:number
+    4:number <- routine-state 3:number
+    5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting
+    assert 5:boolean/waiting?, [ 
 F read-move-blocking: routine failed to pause after file 'a2-a4']
     # press 'newline'
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 10  # newline
-    restart 2:number/routine
+    2:address:shared:sink:character <- write 2:address:shared:sink:character, 10  # newline
+    restart 3:number/routine
     # 'read-move' now completes
-    wait-for-routine 2:number
-    3:number <- routine-state 2:number
-    4:boolean/completed? <- equal 3:number/routine-state, 1/completed
-    assert 4:boolean/completed?, [ 
+    wait-for-routine 3:number
+    4:number <- routine-state 3:number
+    5:boolean/completed? <- equal 4:number/routine-state, 1/completed
+    assert 5:boolean/completed?, [ 
 F read-move-blocking: routine failed to terminate on newline]
     trace 1, [test], [reached end]
   ]
@@ -437,22 +437,22 @@ F read-move-blocking: routine failed to terminate on newline]
 scenario read-move-quit [
   assume-screen 20/width, 2/height
   run [
-    1:address:shared:channel:character <- new-channel 2
-    2:number/routine <- start-running read-move, 1:address:shared:channel:character, screen:address:shared:screen
+    1:address:shared:source:character, 2:address:shared:sink:character <- new-channel 2/capacity
+    3:number/routine <- start-running read-move, 1:address:shared:channel:character, screen:address:shared:screen
     # 'read-move' is waiting for input
-    wait-for-routine 2:number
-    3:number <- routine-state 2:number/id
-    4:boolean/waiting? <- equal 3:number/routine-state, 3/waiting
-    assert 4:boolean/waiting?, [ 
+    wait-for-routine 3:number
+    4:number <- routine-state 3:number/id
+    5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting
+    assert 5:boolean/waiting?, [ 
 F read-move-quit: routine failed to pause after coming up (before any keys were pressed)]
     # press 'q'
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 113/q
-    restart 2:number/routine
+    2:address:shared:sink:character <- write 2:address:shared:sink:character, 113/q
+    restart 3:number/routine
     # 'read-move' completes
-    wait-for-routine 2:number
-    3:number <- routine-state 2:number/id
-    4:boolean/completed? <- equal 3:number/routine-state, 1/completed
-    assert 4:boolean/completed?, [ 
+    wait-for-routine 3:number
+    4:number <- routine-state 3:number/id
+    5:boolean/completed? <- equal 4:number/routine-state, 1/completed
+    assert 5:boolean/completed?, [ 
 F read-move-quit: routine failed to terminate on 'q']
     trace 1, [test], [reached end]
   ]
@@ -464,17 +464,17 @@ F read-move-quit: routine failed to terminate on 'q']
 scenario read-move-illegal-file [
   assume-screen 20/width, 2/height
   run [
-    1:address:shared:channel:character <- new-channel 2
-    2:number/routine <- start-running read-move, 1:address:shared:channel:character, screen:address:shared:screen
+    1:address:shared:source:character, 2:address:shared:sink:character <- new-channel 2/capacity
+    3:number/routine <- start-running read-move, 1:address:shared:source:character, screen:address:shared:screen
     # 'read-move' is waiting for input
-    wait-for-routine 2:number
-    3:number <- routine-state 2:number/id
-    4:boolean/waiting? <- equal 3:number/routine-state, 3/waiting
-    assert 4:boolean/waiting?, [ 
+    wait-for-routine 3:number
+    4:number <- routine-state 3:number/id
+    5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting
+    assert 5:boolean/waiting?, [ 
 F read-move-file: routine failed to pause after coming up (before any keys were pressed)]
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 50/'2'
-    restart 2:number/routine
-    wait-for-routine 2:number
+    1:address:shared:sink:character <- write 1:address:shared:sink:character, 50/'2'
+    restart 3:number/routine
+    wait-for-routine 3:number
   ]
   screen-should-contain [
     .file too low: 2     .
@@ -485,18 +485,18 @@ F read-move-file: routine failed to pause after coming up (before any keys were
 scenario read-move-illegal-rank [
   assume-screen 20/width, 2/height
   run [
-    1:address:shared:channel:character <- new-channel 2
-    2:number/routine <- start-running read-move, 1:address:shared:channel:character, screen:address:shared:screen
+    1:address:shared:source:character, 2:address:shared:sink:character <- new-channel 2/capacity
+    3:number/routine <- start-running read-move, 1:address:shared:source:character, screen:address:shared:screen
     # 'read-move' is waiting for input
-    wait-for-routine 2:number
-    3:number <- routine-state 2:number/id
-    4:boolean/waiting? <- equal 3:number/routine-state, 3/waiting
-    assert 4:boolean/waiting?, [ 
+    wait-for-routine 3:number
+    4:number <- routine-state 3:number/id
+    5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting
+    assert 5:boolean/waiting?, [ 
 F read-move-file: routine failed to pause after coming up (before any keys were pressed)]
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 97/a
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 97/a
-    restart 2:number/routine
-    wait-for-routine 2:number
+    1:address:shared:sink:character <- write 1:address:shared:sink:character, 97/a
+    1:address:shared:sink:character <- write 1:address:shared:sink:character, 97/a
+    restart 3:number/routine
+    wait-for-routine 3:number
   ]
   screen-should-contain [
     .rank too high: a    .
@@ -507,18 +507,18 @@ F read-move-file: routine failed to pause after coming up (before any keys were
 scenario read-move-empty [
   assume-screen 20/width, 2/height
   run [
-    1:address:shared:channel:character <- new-channel 2
-    2:number/routine <- start-running read-move, 1:address:shared:channel:character, screen:address:shared:screen
+    1:address:shared:source:character, 2:address:shared:sink:character <- new-channel 2/capacity
+    3:number/routine <- start-running read-move, 1:address:shared:source:character, screen:address:shared:screen
     # 'read-move' is waiting for input
-    wait-for-routine 2:number
-    3:number <- routine-state 2:number/id
-    4:boolean/waiting? <- equal 3:number/routine-state, 3/waiting
-    assert 4:boolean/waiting?, [ 
+    wait-for-routine 3:number
+    4:number <- routine-state 3:number/id
+    5:boolean/waiting? <- equal 4:number/routine-state, 3/waiting
+    assert 5:boolean/waiting?, [ 
 F read-move-file: routine failed to pause after coming up (before any keys were pressed)]
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 10/newline
-    1:address:shared:channel:character <- write 1:address:shared:channel:character, 97/a
-    restart 2:number/routine
-    wait-for-routine 2:number
+    1:address:shared:sink:character <- write 1:address:shared:sink:character, 10/newline
+    1:address:shared:sink:character <- write 1:address:shared:sink:character, 97/a
+    restart 3:number/routine
+    wait-for-routine 3:number
   ]
   screen-should-contain [
     .that's not enough   .