about summary refs log tree commit diff stats
path: root/html/061channel.mu.html
diff options
context:
space:
mode:
Diffstat (limited to 'html/061channel.mu.html')
-rw-r--r--html/061channel.mu.html430
1 files changed, 279 insertions, 151 deletions
diff --git a/html/061channel.mu.html b/html/061channel.mu.html
index 286aaf22..51ec7f2b 100644
--- a/html/061channel.mu.html
+++ b/html/061channel.mu.html
@@ -2,7 +2,7 @@
 <html>
 <head>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8">
-<title>Mu - 061channel.mu</title>
+<title>~/Desktop/s/mu/061channel.mu</title>
 <meta name="Generator" content="Vim/7.4">
 <meta name="plugin-version" content="vim7.4_v1">
 <meta name="syntax" content="none">
@@ -13,8 +13,10 @@
 pre { white-space: pre-wrap; font-family: monospace; color: #d0d0d0; background-color: #000000; }
 body { font-family: monospace; color: #d0d0d0; background-color: #000000; }
 * { font-size: 1em; }
-.Comment { color: #8080ff; }
+.CommentedCode { color: #6c6c6c; }
 .Delimiter { color: #c000c0; }
+.Comment { color: #8080ff; }
+.Constant { color: #008080; }
 .Special { color: #ff6060; }
 .Identifier { color: #008080; }
 .SalientComment { color: #00ffff; }
@@ -39,262 +41,388 @@ body { font-family: monospace; color: #d0d0d0; background-color: #000000; }
 <span class="Comment"># from an empty one will put the current routine in 'waiting' state until the</span>
 <span class="Comment"># operation can be completed.</span>
 
-scenario channel <span class="Delimiter">[</span>
-  run <span class="Delimiter">[</span>
-    1:address:channel<span class="Special"> &lt;- </span>init-channel 3:literal/capacity
-    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, 34:literal
-    2:integer, 1:address:channel<span class="Special"> &lt;- </span>read 1:address:channel
-  <span class="Delimiter">]</span>
-  memory-should-contain <span class="Delimiter">[</span>
+scenario channel [
+  run [
+    1:address:channel<span class="Special"> &lt;- </span>init-channel <span class="Constant">3:literal/capacity</span>
+    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, <span class="Constant">34:literal</span>
+    2:number, 1:address:channel<span class="Special"> &lt;- </span>read 1:address:channel
+  ]
+  memory-should-contain [
     2<span class="Special"> &lt;- </span>34
-  <span class="Delimiter">]</span>
-<span class="Delimiter">]</span>
+  ]
+]
 
-container channel <span class="Delimiter">[</span>
+container channel [
   <span class="Comment"># To avoid locking, writer and reader will never write to the same location.</span>
   <span class="Comment"># So channels will include fields in pairs, one for the writer and one for the</span>
   <span class="Comment"># reader.</span>
-  first-full:integer  <span class="Comment"># for write</span>
-  first-free:integer  <span class="Comment"># for read</span>
+  first-full:number  <span class="Comment"># for write</span>
+  first-free:number  <span class="Comment"># for read</span>
   <span class="Comment"># A circular buffer contains values from index first-full up to (but not</span>
   <span class="Comment"># including) index first-empty. The reader always modifies it at first-full,</span>
   <span class="Comment"># while the writer always modifies it at first-empty.</span>
   data:address:array:location
-<span class="Delimiter">]</span>
+]
 
-<span class="Comment"># result:address:channel &lt;- init-channel capacity:integer</span>
-recipe init-channel <span class="Delimiter">[</span>
-  default-space:address:array:location<span class="Special"> &lt;- </span>new location:type, 30:literal
+<span class="Comment"># result:address:channel &lt;- init-channel capacity:number</span>
+recipe init-channel [
+  default-space:address:array:location<span class="Special"> &lt;- </span>new location:type, <span class="Constant">30:literal</span>
   <span class="Comment"># result = new channel</span>
   result:address:channel<span class="Special"> &lt;- </span>new channel:type
   <span class="Comment"># result.first-full = 0</span>
-  full:address:integer<span class="Special"> &lt;- </span>get-address result:address:channel/deref, first-full:offset
-  full:address:integer/deref<span class="Special"> &lt;- </span>copy 0:literal
+  full:address:number<span class="Special"> &lt;- </span>get-address result:address:channel/deref, first-full:offset
+  full:address:number/deref<span class="Special"> &lt;- </span>copy <span class="Constant">0:literal</span>
   <span class="Comment"># result.first-free = 0</span>
-  free:address:integer<span class="Special"> &lt;- </span>get-address result:address:channel/deref, first-free:offset
-  free:address:integer/deref<span class="Special"> &lt;- </span>copy 0:literal
+  free:address:number<span class="Special"> &lt;- </span>get-address result:address:channel/deref, first-free:offset
+  free:address:number/deref<span class="Special"> &lt;- </span>copy <span class="Constant">0:literal</span>
   <span class="Comment"># result.data = new location[ingredient+1]</span>
-  capacity:integer<span class="Special"> &lt;- </span>next-ingredient
-  capacity:integer<span class="Special"> &lt;- </span>add capacity:integer, 1:literal  <span class="Comment"># unused slot for 'full?' below</span>
+  capacity:number<span class="Special"> &lt;- </span>next-ingredient
+  capacity:number<span class="Special"> &lt;- </span>add capacity:number, <span class="Constant">1:literal</span>  <span class="Comment"># unused slot for 'full?' below</span>
   dest:address:address:array:location<span class="Special"> &lt;- </span>get-address result:address:channel/deref, data:offset
-  dest:address:address:array:location/deref<span class="Special"> &lt;- </span>new location:type, capacity:integer
+  dest:address:address:array:location/deref<span class="Special"> &lt;- </span>new location:type, capacity:number
   <span class="Identifier">reply</span> result:address:channel
-<span class="Delimiter">]</span>
+]
 
 <span class="Comment"># chan:address:channel &lt;- write chan:address:channel, val:location</span>
-recipe write <span class="Delimiter">[</span>
-  default-space:address:array:location<span class="Special"> &lt;- </span>new location:type, 30:literal
+recipe write [
+  default-space:address:array:location<span class="Special"> &lt;- </span>new location:type, <span class="Constant">30:literal</span>
   chan:address:channel<span class="Special"> &lt;- </span>next-ingredient
   val:location<span class="Special"> &lt;- </span>next-ingredient
   <span class="Delimiter">{</span>
     <span class="Comment"># block if chan is full</span>
     full:boolean<span class="Special"> &lt;- </span>channel-full? chan:address:channel
     <span class="Identifier">break-unless</span> full:boolean
-    full-address:address:integer<span class="Special"> &lt;- </span>get-address chan:address:channel/deref, first-full:offset
-    wait-for-location full-address:address:integer/deref
+    full-address:address:number<span class="Special"> &lt;- </span>get-address chan:address:channel/deref, first-full:offset
+    wait-for-location full-address:address:number/deref
   <span class="Delimiter">}</span>
   <span class="Comment"># store val</span>
   circular-buffer:address:array:location<span class="Special"> &lt;- </span>get chan:address:channel/deref, data:offset
-  free:address:integer<span class="Special"> &lt;- </span>get-address chan:address:channel/deref, first-free:offset
-  dest:address:location<span class="Special"> &lt;- </span>index-address circular-buffer:address:array:location/deref, free:address:integer/deref
+  free:address:number<span class="Special"> &lt;- </span>get-address chan:address:channel/deref, first-free:offset
+  dest:address:location<span class="Special"> &lt;- </span>index-address circular-buffer:address:array:location/deref, free:address:number/deref
   dest:address:location/deref<span class="Special"> &lt;- </span>copy val:location
   <span class="Comment"># increment free</span>
-  free:address:integer/deref<span class="Special"> &lt;- </span>add free:address:integer/deref, 1:literal
+  free:address:number/deref<span class="Special"> &lt;- </span>add free:address:number/deref, <span class="Constant">1:literal</span>
   <span class="Delimiter">{</span>
     <span class="Comment"># wrap free around to 0 if necessary</span>
-    len:integer<span class="Special"> &lt;- </span>length circular-buffer:address:array:location/deref
-    at-end?:boolean<span class="Special"> &lt;- </span>greater-or-equal free:address:integer/deref, len:integer
+    len:number<span class="Special"> &lt;- </span>length circular-buffer:address:array:location/deref
+    at-end?:boolean<span class="Special"> &lt;- </span>greater-or-equal free:address:number/deref, len:number
     <span class="Identifier">break-unless</span> at-end?:boolean
-    free:address:integer/deref<span class="Special"> &lt;- </span>copy 0:literal
+    free:address:number/deref<span class="Special"> &lt;- </span>copy <span class="Constant">0:literal</span>
   <span class="Delimiter">}</span>
   <span class="Identifier">reply</span> chan:address:channel/same-as-ingredient:0
-<span class="Delimiter">]</span>
+]
 
 <span class="Comment"># result:location, chan:address:channel &lt;- read chan:address:channel</span>
-recipe read <span class="Delimiter">[</span>
-  default-space:address:array:location<span class="Special"> &lt;- </span>new location:type, 30:literal
+recipe read [
+  default-space:address:array:location<span class="Special"> &lt;- </span>new location:type, <span class="Constant">30:literal</span>
   chan:address:channel<span class="Special"> &lt;- </span>next-ingredient
   <span class="Delimiter">{</span>
     <span class="Comment"># block if chan is empty</span>
     empty:boolean<span class="Special"> &lt;- </span>channel-empty? chan:address:channel
     <span class="Identifier">break-unless</span> empty:boolean
-    free-address:address:integer<span class="Special"> &lt;- </span>get-address chan:address:channel/deref, first-free:offset
-    wait-for-location free-address:address:integer/deref
+    free-address:address:number<span class="Special"> &lt;- </span>get-address chan:address:channel/deref, first-free:offset
+    wait-for-location free-address:address:number/deref
   <span class="Delimiter">}</span>
   <span class="Comment"># read result</span>
-  full:address:integer<span class="Special"> &lt;- </span>get-address chan:address:channel/deref, first-full:offset
+  full:address:number<span class="Special"> &lt;- </span>get-address chan:address:channel/deref, first-full:offset
   circular-buffer:address:array:location<span class="Special"> &lt;- </span>get chan:address:channel/deref, data:offset
-  result:location<span class="Special"> &lt;- </span>index circular-buffer:address:array:location/deref, full:address:integer/deref
+  result:location<span class="Special"> &lt;- </span>index circular-buffer:address:array:location/deref, full:address:number/deref
   <span class="Comment"># increment full</span>
-  full:address:integer/deref<span class="Special"> &lt;- </span>add full:address:integer/deref, 1:literal
+  full:address:number/deref<span class="Special"> &lt;- </span>add full:address:number/deref, <span class="Constant">1:literal</span>
   <span class="Delimiter">{</span>
     <span class="Comment"># wrap full around to 0 if necessary</span>
-    len:integer<span class="Special"> &lt;- </span>length circular-buffer:address:array:location/deref
-    at-end?:boolean<span class="Special"> &lt;- </span>greater-or-equal full:address:integer/deref, len:integer
+    len:number<span class="Special"> &lt;- </span>length circular-buffer:address:array:location/deref
+    at-end?:boolean<span class="Special"> &lt;- </span>greater-or-equal full:address:number/deref, len:number
     <span class="Identifier">break-unless</span> at-end?:boolean
-    full:address:integer/deref<span class="Special"> &lt;- </span>copy 0:literal
+    full:address:number/deref<span class="Special"> &lt;- </span>copy <span class="Constant">0:literal</span>
   <span class="Delimiter">}</span>
   <span class="Identifier">reply</span> result:location, chan:address:channel/same-as-ingredient:0
-<span class="Delimiter">]</span>
+]
 
-scenario channel-initialization <span class="Delimiter">[</span>
-  run <span class="Delimiter">[</span>
-    1:address:channel<span class="Special"> &lt;- </span>init-channel 3:literal/capacity
-    2:integer<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-full:offset
-    3:integer<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-free:offset
-  <span class="Delimiter">]</span>
-  memory-should-contain <span class="Delimiter">[</span>
+recipe clear-channel [
+  default-space:address:address:array:location<span class="Special"> &lt;- </span>new location:type, <span class="Constant">30:literal</span>
+  chan:address:channel<span class="Special"> &lt;- </span>next-ingredient
+  <span class="Delimiter">{</span>
+    empty?:boolean<span class="Special"> &lt;- </span>channel-empty? chan:address:channel
+    <span class="Identifier">break-if</span> empty?:boolean
+    _, chan:address:channel<span class="Special"> &lt;- </span>read chan:address:channel
+  <span class="Delimiter">}</span>
+  <span class="Identifier">reply</span> chan:address:channel/same-as-ingredient:0
+]
+
+scenario channel-initialization [
+  run [
+    1:address:channel<span class="Special"> &lt;- </span>init-channel <span class="Constant">3:literal/capacity</span>
+    2:number<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-full:offset
+    3:number<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-free:offset
+  ]
+  memory-should-contain [
     2<span class="Special"> &lt;- </span>0  <span class="Comment"># first-full</span>
     3<span class="Special"> &lt;- </span>0  <span class="Comment"># first-free</span>
-  <span class="Delimiter">]</span>
-<span class="Delimiter">]</span>
+  ]
+]
 
-scenario channel-write-increments-free <span class="Delimiter">[</span>
-  run <span class="Delimiter">[</span>
-    1:address:channel<span class="Special"> &lt;- </span>init-channel 3:literal/capacity
-    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, 34:literal
-    2:integer<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-full:offset
-    3:integer<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-free:offset
-  <span class="Delimiter">]</span>
-  memory-should-contain <span class="Delimiter">[</span>
+scenario channel-write-increments-free [
+  run [
+    1:address:channel<span class="Special"> &lt;- </span>init-channel <span class="Constant">3:literal/capacity</span>
+    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, <span class="Constant">34:literal</span>
+    2:number<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-full:offset
+    3:number<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-free:offset
+  ]
+  memory-should-contain [
     2<span class="Special"> &lt;- </span>0  <span class="Comment"># first-full</span>
     3<span class="Special"> &lt;- </span>1  <span class="Comment"># first-free</span>
-  <span class="Delimiter">]</span>
-<span class="Delimiter">]</span>
+  ]
+]
 
-scenario channel-read-increments-full <span class="Delimiter">[</span>
-  run <span class="Delimiter">[</span>
-    1:address:channel<span class="Special"> &lt;- </span>init-channel 3:literal/capacity
-    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, 34:literal
+scenario channel-read-increments-full [
+  run [
+    1:address:channel<span class="Special"> &lt;- </span>init-channel <span class="Constant">3:literal/capacity</span>
+    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, <span class="Constant">34:literal</span>
     _, 1:address:channel<span class="Special"> &lt;- </span>read 1:address:channel
-    2:integer<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-full:offset
-    3:integer<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-free:offset
-  <span class="Delimiter">]</span>
-  memory-should-contain <span class="Delimiter">[</span>
+    2:number<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-full:offset
+    3:number<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-free:offset
+  ]
+  memory-should-contain [
     2<span class="Special"> &lt;- </span>1  <span class="Comment"># first-full</span>
     3<span class="Special"> &lt;- </span>1  <span class="Comment"># first-free</span>
-  <span class="Delimiter">]</span>
-<span class="Delimiter">]</span>
+  ]
+]
 
-scenario channel-wrap <span class="Delimiter">[</span>
-  run <span class="Delimiter">[</span>
+scenario channel-wrap [
+  run [
     <span class="Comment"># channel with just 1 slot</span>
-    1:address:channel<span class="Special"> &lt;- </span>init-channel 1:literal/capacity
+    1:address:channel<span class="Special"> &lt;- </span>init-channel <span class="Constant">1:literal/capacity</span>
     <span class="Comment"># write and read a value</span>
-    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, 34:literal
+    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, <span class="Constant">34:literal</span>
     _, 1:address:channel<span class="Special"> &lt;- </span>read 1:address:channel
     <span class="Comment"># first-free will now be 1</span>
-    2:integer<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-free:offset
-    3:integer<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-free:offset
+    2:number<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-free:offset
+    3:number<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-free:offset
     <span class="Comment"># write second value, verify that first-free wraps</span>
-    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, 34:literal
-    4:integer<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-free:offset
+    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, <span class="Constant">34:literal</span>
+    4:number<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-free:offset
     <span class="Comment"># read second value, verify that first-full wraps</span>
     _, 1:address:channel<span class="Special"> &lt;- </span>read 1:address:channel
-    5:integer<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-full:offset
-  <span class="Delimiter">]</span>
-  memory-should-contain <span class="Delimiter">[</span>
+    5:number<span class="Special"> &lt;- </span>get 1:address:channel/deref, first-full:offset
+  ]
+  memory-should-contain [
     2<span class="Special"> &lt;- </span>1  <span class="Comment"># first-free after first write</span>
     3<span class="Special"> &lt;- </span>1  <span class="Comment"># first-full after first read</span>
     4<span class="Special"> &lt;- </span>0  <span class="Comment"># first-free after second write, wrapped</span>
     5<span class="Special"> &lt;- </span>0  <span class="Comment"># first-full after second read, wrapped</span>
-  <span class="Delimiter">]</span>
-<span class="Delimiter">]</span>
+  ]
+]
 
 <span class="SalientComment">## helpers</span>
 
 <span class="Comment"># An empty channel has first-empty and first-full both at the same value.</span>
-recipe channel-empty? <span class="Delimiter">[</span>
-  default-space:address:array:location<span class="Special"> &lt;- </span>new location:type, 30:literal
+recipe channel-empty? [
+  default-space:address:array:location<span class="Special"> &lt;- </span>new location:type, <span class="Constant">30:literal</span>
   chan:address:channel<span class="Special"> &lt;- </span>next-ingredient
   <span class="Comment"># return chan.first-full == chan.first-free</span>
-  full:integer<span class="Special"> &lt;- </span>get chan:address:channel/deref, first-full:offset
-  free:integer<span class="Special"> &lt;- </span>get chan:address:channel/deref, first-free:offset
-  result:boolean<span class="Special"> &lt;- </span>equal full:integer, free:integer
+  full:number<span class="Special"> &lt;- </span>get chan:address:channel/deref, first-full:offset
+  free:number<span class="Special"> &lt;- </span>get chan:address:channel/deref, first-free:offset
+  result:boolean<span class="Special"> &lt;- </span>equal full:number, free:number
   <span class="Identifier">reply</span> result:boolean
-<span class="Delimiter">]</span>
+]
 
 <span class="Comment"># A full channel has first-empty just before first-full, wasting one slot.</span>
 <span class="Comment"># (Other alternatives: <a href="https://en.wikipedia.org/wiki/Circular_buffer#Full_.2F_Empty_Buffer_Distinction)">https://en.wikipedia.org/wiki/Circular_buffer#Full_.2F_Empty_Buffer_Distinction)</a></span>
-recipe channel-full? <span class="Delimiter">[</span>
-  default-space:address:array:location<span class="Special"> &lt;- </span>new location:type, 30:literal
+recipe channel-full? [
+  default-space:address:array:location<span class="Special"> &lt;- </span>new location:type, <span class="Constant">30:literal</span>
   chan:address:channel<span class="Special"> &lt;- </span>next-ingredient
   <span class="Comment"># tmp = chan.first-free + 1</span>
-  tmp:integer<span class="Special"> &lt;- </span>get chan:address:channel/deref, first-free:offset
-  tmp:integer<span class="Special"> &lt;- </span>add tmp:integer, 1:literal
+  tmp:number<span class="Special"> &lt;- </span>get chan:address:channel/deref, first-free:offset
+  tmp:number<span class="Special"> &lt;- </span>add tmp:number, <span class="Constant">1:literal</span>
   <span class="Delimiter">{</span>
     <span class="Comment"># if tmp == chan.capacity, tmp = 0</span>
-    len:integer<span class="Special"> &lt;- </span>channel-capacity chan:address:channel
-    at-end?:boolean<span class="Special"> &lt;- </span>greater-or-equal tmp:integer, len:integer
+    len:number<span class="Special"> &lt;- </span>channel-capacity chan:address:channel
+    at-end?:boolean<span class="Special"> &lt;- </span>greater-or-equal tmp:number, len:number
     <span class="Identifier">break-unless</span> at-end?:boolean
-    tmp:integer<span class="Special"> &lt;- </span>copy 0:literal
+    tmp:number<span class="Special"> &lt;- </span>copy <span class="Constant">0:literal</span>
   <span class="Delimiter">}</span>
   <span class="Comment"># return chan.first-full == tmp</span>
-  full:integer<span class="Special"> &lt;- </span>get chan:address:channel/deref, first-full:offset
-  result:boolean<span class="Special"> &lt;- </span>equal full:integer, tmp:integer
+  full:number<span class="Special"> &lt;- </span>get chan:address:channel/deref, first-full:offset
+  result:boolean<span class="Special"> &lt;- </span>equal full:number, tmp:number
   <span class="Identifier">reply</span> result:boolean
-<span class="Delimiter">]</span>
+]
 
-<span class="Comment"># result:integer &lt;- channel-capacity chan:address:channel</span>
-recipe channel-capacity <span class="Delimiter">[</span>
-  default-space:address:array:location<span class="Special"> &lt;- </span>new location:type, 30:literal
+<span class="Comment"># result:number &lt;- channel-capacity chan:address:channel</span>
+recipe channel-capacity [
+  default-space:address:array:location<span class="Special"> &lt;- </span>new location:type, <span class="Constant">30:literal</span>
   chan:address:channel<span class="Special"> &lt;- </span>next-ingredient
   q:address:array:location<span class="Special"> &lt;- </span>get chan:address:channel/deref, data:offset
-  result:integer<span class="Special"> &lt;- </span>length q:address:array:location/deref
-  <span class="Identifier">reply</span> result:integer
-<span class="Delimiter">]</span>
+  result:number<span class="Special"> &lt;- </span>length q:address:array:location/deref
+  <span class="Identifier">reply</span> result:number
+]
 
-scenario channel-new-empty-not-full <span class="Delimiter">[</span>
-  run <span class="Delimiter">[</span>
-    1:address:channel<span class="Special"> &lt;- </span>init-channel 3:literal/capacity
-    2:integer<span class="Special"> &lt;- </span>channel-empty? 1:address:channel
-    3:integer<span class="Special"> &lt;- </span>channel-full? 1:address:channel
-  <span class="Delimiter">]</span>
-  memory-should-contain <span class="Delimiter">[</span>
+scenario channel-new-empty-not-full [
+  run [
+    1:address:channel<span class="Special"> &lt;- </span>init-channel <span class="Constant">3:literal/capacity</span>
+    2:boolean<span class="Special"> &lt;- </span>channel-empty? 1:address:channel
+    3:boolean<span class="Special"> &lt;- </span>channel-full? 1:address:channel
+  ]
+  memory-should-contain [
     2<span class="Special"> &lt;- </span>1  <span class="Comment"># empty?</span>
     3<span class="Special"> &lt;- </span>0  <span class="Comment"># full?</span>
-  <span class="Delimiter">]</span>
-<span class="Delimiter">]</span>
+  ]
+]
 
-scenario channel-write-not-empty <span class="Delimiter">[</span>
-  run <span class="Delimiter">[</span>
-    1:address:channel<span class="Special"> &lt;- </span>init-channel 3:literal/capacity
-    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, 34:literal
-    2:integer<span class="Special"> &lt;- </span>channel-empty? 1:address:channel
-    3:integer<span class="Special"> &lt;- </span>channel-full? 1:address:channel
-  <span class="Delimiter">]</span>
-  memory-should-contain <span class="Delimiter">[</span>
+scenario channel-write-not-empty [
+  run [
+    1:address:channel<span class="Special"> &lt;- </span>init-channel <span class="Constant">3:literal/capacity</span>
+    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, <span class="Constant">34:literal</span>
+    2:boolean<span class="Special"> &lt;- </span>channel-empty? 1:address:channel
+    3:boolean<span class="Special"> &lt;- </span>channel-full? 1:address:channel
+  ]
+  memory-should-contain [
     2<span class="Special"> &lt;- </span>0  <span class="Comment"># empty?</span>
     3<span class="Special"> &lt;- </span>0  <span class="Comment"># full?</span>
-  <span class="Delimiter">]</span>
-<span class="Delimiter">]</span>
+  ]
+]
 
-scenario channel-write-full <span class="Delimiter">[</span>
-  run <span class="Delimiter">[</span>
-    1:address:channel<span class="Special"> &lt;- </span>init-channel 1:literal/capacity
-    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, 34:literal
-    2:integer<span class="Special"> &lt;- </span>channel-empty? 1:address:channel
-    3:integer<span class="Special"> &lt;- </span>channel-full? 1:address:channel
-  <span class="Delimiter">]</span>
-  memory-should-contain <span class="Delimiter">[</span>
+scenario channel-write-full [
+  run [
+    1:address:channel<span class="Special"> &lt;- </span>init-channel <span class="Constant">1:literal/capacity</span>
+    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, <span class="Constant">34:literal</span>
+    2:boolean<span class="Special"> &lt;- </span>channel-empty? 1:address:channel
+    3:boolean<span class="Special"> &lt;- </span>channel-full? 1:address:channel
+  ]
+  memory-should-contain [
     2<span class="Special"> &lt;- </span>0  <span class="Comment"># empty?</span>
     3<span class="Special"> &lt;- </span>1  <span class="Comment"># full?</span>
-  <span class="Delimiter">]</span>
-<span class="Delimiter">]</span>
+  ]
+]
 
-scenario channel-read-not-full <span class="Delimiter">[</span>
-  run <span class="Delimiter">[</span>
-    1:address:channel<span class="Special"> &lt;- </span>init-channel 1:literal/capacity
-    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, 34:literal
+scenario channel-read-not-full [
+  run [
+    1:address:channel<span class="Special"> &lt;- </span>init-channel <span class="Constant">1:literal/capacity</span>
+    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, <span class="Constant">34:literal</span>
     _, 1:address:channel<span class="Special"> &lt;- </span>read 1:address:channel
-    2:integer<span class="Special"> &lt;- </span>channel-empty? 1:address:channel
-    3:integer<span class="Special"> &lt;- </span>channel-full? 1:address:channel
-  <span class="Delimiter">]</span>
-  memory-should-contain <span class="Delimiter">[</span>
+    2:boolean<span class="Special"> &lt;- </span>channel-empty? 1:address:channel
+    3:boolean<span class="Special"> &lt;- </span>channel-full? 1:address:channel
+  ]
+  memory-should-contain [
     2<span class="Special"> &lt;- </span>1  <span class="Comment"># empty?</span>
     3<span class="Special"> &lt;- </span>0  <span class="Comment"># full?</span>
-  <span class="Delimiter">]</span>
-<span class="Delimiter">]</span>
+  ]
+]
+
+<span class="Comment"># helper for channels of characters in particular</span>
+<span class="Comment"># out:address:channel &lt;- buffer-lines in:address:channel, out:address:channel</span>
+recipe buffer-lines [
+  default-space:address:address:array:location<span class="Special"> &lt;- </span>new location:type, <span class="Constant">30:literal</span>
+<span class="CommentedCode">#?   $print [buffer-lines: aaa</span>
+<span class="CommentedCode">#? ]</span>
+  in:address:channel<span class="Special"> &lt;- </span>next-ingredient
+  out:address:channel<span class="Special"> &lt;- </span>next-ingredient
+  <span class="Comment"># repeat forever</span>
+  <span class="Delimiter">{</span>
+    line:address:buffer<span class="Special"> &lt;- </span>init-buffer, <span class="Constant">30:literal</span>
+    <span class="Comment"># read characters from 'in' until newline, copy into line</span>
+    <span class="Delimiter">{</span>
+     <span class="Identifier"> +next-character</span>
+      c:character, in:address:channel<span class="Special"> &lt;- </span>read in:address:channel
+      <span class="Comment"># drop a character on backspace</span>
+      <span class="Delimiter">{</span>
+        <span class="Comment"># special-case: if it's a backspace</span>
+        backspace?:boolean<span class="Special"> &lt;- </span>equal c:character, <span class="Constant">8:literal</span>
+        <span class="Identifier">break-unless</span> backspace?:boolean
+        <span class="Comment"># drop previous character</span>
+<span class="CommentedCode">#?         return-to-console #? 2</span>
+<span class="CommentedCode">#?         $print [backspace! #? 1</span>
+<span class="CommentedCode">#? ] #? 1</span>
+        <span class="Delimiter">{</span>
+          buffer-length:address:number<span class="Special"> &lt;- </span>get-address line:address:buffer/deref, length:offset
+          buffer-empty?:boolean<span class="Special"> &lt;- </span>equal buffer-length:address:number/deref, <span class="Constant">0:literal</span>
+          <span class="Identifier">break-if</span> buffer-empty?:boolean
+<span class="CommentedCode">#?           $print [before: ], buffer-length:address:number/deref, [ </span>
+<span class="CommentedCode">#? ] #? 1</span>
+          buffer-length:address:number/deref<span class="Special"> &lt;- </span>subtract buffer-length:address:number/deref, <span class="Constant">1:literal</span>
+<span class="CommentedCode">#?           $print [after: ], buffer-length:address:number/deref, [ </span>
+<span class="CommentedCode">#? ] #? 1</span>
+        <span class="Delimiter">}</span>
+<span class="CommentedCode">#?         $exit #? 2</span>
+        <span class="Comment"># and don't append this one</span>
+        <span class="Identifier">loop</span><span class="Identifier"> +next-character</span>:label
+      <span class="Delimiter">}</span>
+      <span class="Comment"># append anything else</span>
+<span class="CommentedCode">#?       $print [buffer-lines: appending ], c:character, [ </span>
+<span class="CommentedCode">#? ]</span>
+      line:address:buffer<span class="Special"> &lt;- </span>buffer-append line:address:buffer, c:character
+      line-done?:boolean<span class="Special"> &lt;- </span>equal c:character, <span class="Constant">10:literal/newline</span>
+      <span class="Identifier">break-if</span> line-done?:boolean
+      <span class="Comment"># stop buffering on eof (currently only generated by fake keyboard)</span>
+      empty-fake-keyboard?:boolean<span class="Special"> &lt;- </span>equal c:character, <span class="Constant">0:literal/eof</span>
+      <span class="Identifier">break-if</span> empty-fake-keyboard?:boolean
+      <span class="Identifier">loop</span>
+    <span class="Delimiter">}</span>
+<span class="CommentedCode">#?     return-to-console #? 1</span>
+    <span class="Comment"># copy line into 'out'</span>
+<span class="CommentedCode">#?     $print [buffer-lines: emitting</span>
+<span class="CommentedCode">#? ]</span>
+    i:number<span class="Special"> &lt;- </span>copy <span class="Constant">0:literal</span>
+    line-contents:address:array:character<span class="Special"> &lt;- </span>get line:address:buffer/deref, data:offset
+    max:number<span class="Special"> &lt;- </span>get line:address:buffer/deref, length:offset
+    <span class="Delimiter">{</span>
+      done?:boolean<span class="Special"> &lt;- </span>greater-or-equal i:number, max:number
+      <span class="Identifier">break-if</span> done?:boolean
+      c:character<span class="Special"> &lt;- </span>index line-contents:address:array:character/deref, i:number
+      out:address:channel<span class="Special"> &lt;- </span>write out:address:channel, c:character
+<span class="CommentedCode">#?       $print [writing ], i:number, [: ], c:character, [ </span>
+<span class="CommentedCode">#? ] #? 1</span>
+      i:number<span class="Special"> &lt;- </span>add i:number, <span class="Constant">1:literal</span>
+      <span class="Identifier">loop</span>
+    <span class="Delimiter">}</span>
+<span class="CommentedCode">#?     $dump-trace #? 1</span>
+<span class="CommentedCode">#?     $exit #? 1</span>
+    <span class="Identifier">loop</span>
+  <span class="Delimiter">}</span>
+  <span class="Identifier">reply</span> out:address:channel/same-as-ingredient:1
+]
+
+scenario buffer-lines-blocks-until-newline [
+  run [
+    1:address:channel/stdin<span class="Special"> &lt;- </span>init-channel <span class="Constant">10:literal/capacity</span>
+    2:address:channel/buffered-stdin<span class="Special"> &lt;- </span>init-channel <span class="Constant">10:literal/capacity</span>
+    3:boolean<span class="Special"> &lt;- </span>channel-empty? 2:address:channel/buffered-stdin
+    assert 3:boolean, [
+F buffer-lines-blocks-until-newline: channel should be empty after init]
+    <span class="Comment"># buffer stdin into buffered-stdin, try to read from buffered-stdin</span>
+    4:number/buffer-routine<span class="Special"> &lt;- </span>start-running buffer-lines:recipe, 1:address:channel/stdin, 2:address:channel/buffered-stdin
+    wait-for-routine 4:number/buffer-routine
+    5:boolean<span class="Special"> &lt;- </span>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]
+    <span class="Comment"># write 'a'</span>
+    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, <span class="Constant">97:literal/a</span>
+    restart 4:number/buffer-routine
+    wait-for-routine 4:number/buffer-routine
+    6:boolean<span class="Special"> &lt;- </span>channel-empty? 2:address:channel/buffered-stdin
+    assert 6:boolean, [
+F buffer-lines-blocks-until-newline: channel should be empty after writing<span class="Identifier"> 'a</span>']
+    <span class="Comment"># write 'b'</span>
+    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, <span class="Constant">98:literal/b</span>
+    restart 4:number/buffer-routine
+    wait-for-routine 4:number/buffer-routine
+    7:boolean<span class="Special"> &lt;- </span>channel-empty? 2:address:channel/buffered-stdin
+    assert 7:boolean, [
+F buffer-lines-blocks-until-newline: channel should be empty after writing<span class="Identifier"> 'b</span>']
+    <span class="Comment"># write newline</span>
+    1:address:channel<span class="Special"> &lt;- </span>write 1:address:channel, <span class="Constant">10:literal/newline</span>
+    restart 4:number/buffer-routine
+    wait-for-routine 4:number/buffer-routine
+    8:boolean<span class="Special"> &lt;- </span>channel-empty? 2:address:channel/buffered-stdin
+    9:boolean/completed?<span class="Special"> &lt;- </span>not 8:boolean
+    assert 9:boolean/completed?, [
+F buffer-lines-blocks-until-newline: channel should contain data after writing newline]
+    trace <span class="Constant">[test]</span>, <span class="Constant">[reached end]</span>
+  ]
+  trace-should-contain [
+    test: reached end
+  ]
+]
 </pre>
 </body>
 </html>