about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-07-13 22:43:16 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-07-13 22:50:49 -0700
commit77d5b5d658830bd24724f945e0d6ddf6a06adc0e (patch)
tree94c50c0ddfa6d55dc1189d62243ceeacaf783326
parent84e4ed1ab58d5b34cf92919aedbb15736a7349d9 (diff)
downloadmu-77d5b5d658830bd24724f945e0d6ddf6a06adc0e.tar.gz
1780 - now we always reclaim local scopes
But still no difference in either memory footprint or in running time.
This will teach me -- for the umpteenth time -- to optimize before
measuring.
-rw-r--r--043new.cc2
-rw-r--r--044space.cc61
-rw-r--r--060string.mu42
-rw-r--r--061channel.mu16
-rw-r--r--062array.mu2
-rw-r--r--063list.mu6
-rw-r--r--065duplex_list.mu12
-rw-r--r--066stream.mu8
-rw-r--r--071print.mu40
-rw-r--r--074console.mu12
-rw-r--r--channel.mu6
-rw-r--r--chessboard.mu20
-rw-r--r--counters.mu4
-rw-r--r--edit.mu52
-rw-r--r--factorial.mu4
-rw-r--r--mu.vim2
-rw-r--r--tangle.mu2
17 files changed, 168 insertions, 123 deletions
diff --git a/043new.cc b/043new.cc
index d75d4bd0..feb4c0cf 100644
--- a/043new.cc
+++ b/043new.cc
@@ -211,7 +211,7 @@ case ABANDON: {
 
 :(code)
 void abandon(long long int address, long long int size) {
-//?   cerr << "abandon: " << size << '\n'; //? 1
+//?   cerr << "abandon: " << size << '\n'; //? 2
   // clear memory
   for (long long int curr = address; curr < address+size; ++curr)
     Memory[curr] = 0;
diff --git a/044space.cc b/044space.cc
index aecfd398..4469e47f 100644
--- a/044space.cc
+++ b/044space.cc
@@ -124,14 +124,7 @@ if (s == "number-of-locals") return true;
 //   `default-space:address:array:location <- new location:type, number-of-locals:literal`
 // where N is Name[recipe][""]
 if (curr.name == "new-default-space") {
-  curr.operation = Recipe_ordinal["new"];
-  if (!curr.ingredients.empty())
-    raise << "new-default-space can't take any ingredients\n";
-  curr.ingredients.push_back(reagent("location:type"));
-  curr.ingredients.push_back(reagent("number-of-locals:literal"));
-  if (!curr.products.empty())
-    raise << "new-default-space can't take any results\n";
-  curr.products.push_back(reagent("default-space:address:array:location"));
+  rewrite_default_space_instruction(curr);
 }
 :(after "vector<double> read_memory(reagent x)")
   if (x.name == "number-of-locals") {
@@ -148,6 +141,58 @@ if (curr.name == "new-default-space") {
     return;
   }
 
+//:: a little hook to automatically reclaim the default-space when returning
+//:: from a recipe
+
+:(scenario local_scope)
+recipe main [
+  1:address <- foo
+  2:address <- foo
+  3:boolean <- equal 1:address, 2:address
+]
+recipe foo [
+  local-scope
+  x:number <- copy 34:literal
+  reply default-space:address:array:location
+]
+# both calls to foo should have received the same default-space
++mem: storing 1 in location 3
+
+:(after "Falling Through End Of Recipe")
+try_reclaim_locals();
+:(after "Starting Reply")
+try_reclaim_locals();
+
+//: now 'local-scope' is identical to 'new-default-space' except that we'll
+//: reclaim the default-space when the routine exits
+:(before "End Rewrite Instruction(curr)")
+if (curr.name == "local-scope") {
+  rewrite_default_space_instruction(curr);
+}
+
+:(code)
+void try_reclaim_locals() {
+  // only reclaim routines starting with 'local-scope'
+  const recipe_ordinal r = Recipe_ordinal[current_recipe_name()];
+  const instruction& inst = Recipe[r].steps.at(0);
+  if (inst.name != "local-scope")
+    return;
+//?   cerr << inst.to_string() << '\n'; //? 1
+  abandon(Current_routine->calls.front().default_space,
+          /*array length*/1+/*number-of-locals*/Name[r][""]);
+}
+
+void rewrite_default_space_instruction(instruction& curr) {
+  curr.operation = Recipe_ordinal["new"];
+  if (!curr.ingredients.empty())
+    raise << "new-default-space can't take any ingredients\n";
+  curr.ingredients.push_back(reagent("location:type"));
+  curr.ingredients.push_back(reagent("number-of-locals:literal"));
+  if (!curr.products.empty())
+    raise << "new-default-space can't take any results\n";
+  curr.products.push_back(reagent("default-space:address:array:location"));
+}
+
 //:: helpers
 
 :(code)
diff --git a/060string.mu b/060string.mu
index b04f8864..6cfbc318 100644
--- a/060string.mu
+++ b/060string.mu
@@ -1,7 +1,7 @@
 # Some useful helpers for dealing with strings.
 
 recipe string-equal [
-  new-default-space
+  local-scope
   a:address:array:character <- next-ingredient
   a-len:number <- length a:address:array:character/deref
   b:address:array:character <- next-ingredient
@@ -104,7 +104,7 @@ container buffer [
 ]
 
 recipe new-buffer [
-  new-default-space
+  local-scope
 #?   $print default-space:address:array:location, [
 #? ]
   result:address:buffer <- new buffer:type
@@ -120,7 +120,7 @@ recipe new-buffer [
 ]
 
 recipe grow-buffer [
-  new-default-space
+  local-scope
   in:address:buffer <- next-ingredient
   # double buffer size
   x:address:address:array:character <- get-address in:address:buffer/deref, data:offset
@@ -143,7 +143,7 @@ recipe grow-buffer [
 ]
 
 recipe buffer-full? [
-  new-default-space
+  local-scope
   in:address:buffer <- next-ingredient
   len:number <- get in:address:buffer/deref, length:offset
   s:address:array:character <- get in:address:buffer/deref, data:offset
@@ -154,7 +154,7 @@ recipe buffer-full? [
 
 # in:address:buffer <- buffer-append in:address:buffer, c:character
 recipe buffer-append [
-  new-default-space
+  local-scope
   in:address:buffer <- next-ingredient
   c:character <- next-ingredient
   len:address:number <- get-address in:address:buffer/deref, length:offset
@@ -188,7 +188,7 @@ recipe buffer-append [
 
 scenario buffer-append-works [
   run [
-    new-default-space
+    local-scope
     x:address:buffer <- new-buffer 3:literal
     s1:address:array:character <- get x:address:buffer/deref, data:offset
     x:address:buffer <- buffer-append x:address:buffer, 97:literal  # 'a'
@@ -240,7 +240,7 @@ scenario buffer-append-works [
 
 scenario buffer-append-handles-backspace [
   run [
-    new-default-space
+    local-scope
     x:address:buffer <- new-buffer 3:literal
     x:address:buffer <- buffer-append x:address:buffer, 97:literal  # 'a'
     x:address:buffer <- buffer-append x:address:buffer, 98:literal  # 'b'
@@ -257,7 +257,7 @@ scenario buffer-append-handles-backspace [
 
 # result:address:array:character <- integer-to-decimal-string n:number
 recipe integer-to-decimal-string [
-  new-default-space
+  local-scope
   n:number <- next-ingredient
   # is it zero?
   {
@@ -313,7 +313,7 @@ recipe integer-to-decimal-string [
 ]
 
 recipe buffer-to-array [
-  new-default-space
+  local-scope
   in:address:buffer <- next-ingredient
   {
     # propagate null buffer
@@ -374,7 +374,7 @@ scenario integer-to-decimal-digit-negative [
 
 # result:address:array:character <- string-append a:address:array:character, b:address:array:character
 recipe string-append [
-  new-default-space
+  local-scope
   # result = new character[a.length + b.length]
   a:address:array:character <- next-ingredient
   a-len:number <- length a:address:array:character/deref
@@ -433,7 +433,7 @@ scenario string-append-1 [
 # replace underscores in first with remaining args
 # result:address:array:character <- interpolate template:address:array:character, ...
 recipe interpolate [
-  new-default-space
+  local-scope
   template:address:array:character <- next-ingredient
   # compute result-len, space to allocate for result
   tem-len:number <- length template:address:array:character/deref
@@ -558,7 +558,7 @@ scenario interpolate-at-end [
 
 # result:boolean <- space? c:character
 recipe space? [
-  new-default-space
+  local-scope
   c:character <- next-ingredient
   # most common case first
   result:boolean <- equal c:character, 32:literal/space
@@ -623,7 +623,7 @@ recipe space? [
 
 # result:address:array:character <- trim s:address:array:character
 recipe trim [
-  new-default-space
+  local-scope
   s:address:array:character <- next-ingredient
   len:number <- length s:address:array:character/deref
   # left trim: compute start
@@ -732,7 +732,7 @@ scenario trim-newline-tab [
 
 # next-index:number <- find-next text:address:array:character, pattern:character
 recipe find-next [
-  new-default-space
+  local-scope
   text:address:array:character <- next-ingredient
   pattern:character <- next-ingredient
   idx:number <- next-ingredient
@@ -832,7 +832,7 @@ scenario string-find-next-second [
 # like find-next, but searches for multiple characters
 # fairly dumb algorithm
 recipe find-substring [
-  new-default-space
+  local-scope
   text:address:array:character <- next-ingredient
   pattern:address:array:character <- next-ingredient
   idx:number <- next-ingredient
@@ -911,7 +911,7 @@ scenario find-substring-suffix-match-2 [
 # result:boolean <- match-at text:address:array:character, pattern:address:array:character, idx:number
 # checks if substring matches at index 'idx'
 recipe match-at [
-  new-default-space
+  local-scope
   text:address:array:character <- next-ingredient
   pattern:address:array:character <- next-ingredient
   idx:number <- next-ingredient
@@ -1042,7 +1042,7 @@ scenario match-at-inside-bounds-2 [
 
 # result:address:array:address:array:character <- split s:address:array:character, delim:character
 recipe split [
-  new-default-space
+  local-scope
   s:address:array:character <- next-ingredient
   delim:character <- next-ingredient
   # empty string? return empty array
@@ -1172,7 +1172,7 @@ scenario string-split-empty-piece [
 
 # x:address:array:character, y:address:array:character <- split-first text:address:array:character, delim:character
 recipe split-first [
-  new-default-space
+  local-scope
   text:address:array:character <- next-ingredient
   delim:character <- next-ingredient
   # empty string? return empty strings
@@ -1207,7 +1207,7 @@ scenario string-split-first [
 # result:address:array:character <- string-copy buf:address:array:character, start:number, end:number
 # todo: make this generic
 recipe string-copy [
-  new-default-space
+  local-scope
   buf:address:array:character <- next-ingredient
   start:number <- next-ingredient
   end:number <- next-ingredient
@@ -1267,7 +1267,7 @@ scenario string-copy-out-of-bounds-2 [
 ]
 
 recipe min [
-  new-default-space
+  local-scope
   x:number <- next-ingredient
   y:number <- next-ingredient
   {
@@ -1279,7 +1279,7 @@ recipe min [
 ]
 
 recipe max [
-  new-default-space
+  local-scope
   x:number <- next-ingredient
   y:number <- next-ingredient
   {
diff --git a/061channel.mu b/061channel.mu
index 5b3f31d3..9f86ba2d 100644
--- a/061channel.mu
+++ b/061channel.mu
@@ -33,7 +33,7 @@ container channel [
 
 # result:address:channel <- new-channel capacity:number
 recipe new-channel [
-  new-default-space
+  local-scope
   # result = new channel
   result:address:channel <- new channel:type
   # result.first-full = 0
@@ -52,7 +52,7 @@ recipe new-channel [
 
 # chan:address:channel <- write chan:address:channel, val:location
 recipe write [
-  new-default-space
+  local-scope
   chan:address:channel <- next-ingredient
   val:location <- next-ingredient
   {
@@ -81,7 +81,7 @@ recipe write [
 
 # result:location, chan:address:channel <- read chan:address:channel
 recipe read [
-  new-default-space
+  local-scope
   chan:address:channel <- next-ingredient
   {
     # block if chan is empty
@@ -107,7 +107,7 @@ recipe read [
 ]
 
 recipe clear-channel [
-  new-default-space
+  local-scope
   chan:address:channel <- next-ingredient
   {
     empty?:boolean <- channel-empty? chan:address:channel
@@ -185,7 +185,7 @@ scenario channel-wrap [
 
 # An empty channel has first-empty and first-full both at the same value.
 recipe channel-empty? [
-  new-default-space
+  local-scope
   chan:address:channel <- next-ingredient
   # return chan.first-full == chan.first-free
   full:number <- get chan:address:channel/deref, first-full:offset
@@ -197,7 +197,7 @@ recipe channel-empty? [
 # A full channel has first-empty just before first-full, wasting one slot.
 # (Other alternatives: https://en.wikipedia.org/wiki/Circular_buffer#Full_.2F_Empty_Buffer_Distinction)
 recipe channel-full? [
-  new-default-space
+  local-scope
   chan:address:channel <- next-ingredient
   # tmp = chan.first-free + 1
   tmp:number <- get chan:address:channel/deref, first-free:offset
@@ -217,7 +217,7 @@ recipe channel-full? [
 
 # result:number <- channel-capacity chan:address:channel
 recipe channel-capacity [
-  new-default-space
+  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
@@ -279,7 +279,7 @@ scenario channel-read-not-full [
 # helper for channels of characters in particular
 # out:address:channel <- buffer-lines in:address:channel, out:address:channel
 recipe buffer-lines [
-  new-default-space
+  local-scope
 #?   $print [buffer-lines: aaa
 #? ]
   in:address:channel <- next-ingredient
diff --git a/062array.mu b/062array.mu
index b7937fbc..a14f4916 100644
--- a/062array.mu
+++ b/062array.mu
@@ -13,7 +13,7 @@ scenario array-from-args [
 
 # create an array out of a list of scalar args
 recipe new-array [
-  new-default-space
+  local-scope
   capacity:number <- copy 0:literal
   {
     # while read curr-value
diff --git a/063list.mu b/063list.mu
index b9bc9c53..fd64cfe8 100644
--- a/063list.mu
+++ b/063list.mu
@@ -10,7 +10,7 @@ container list [
 
 # result:address:list <- push x:location, in:address:list
 recipe push [
-  new-default-space
+  local-scope
   x:location <- next-ingredient
   in:address:list <- next-ingredient
   result:address:list <- new list:type
@@ -23,7 +23,7 @@ recipe push [
 
 # result:location <- first in:address:list
 recipe first [
-  new-default-space
+  local-scope
   in:address:list <- next-ingredient
   result:location <- get in:address:list/deref, value:offset
   reply result:location
@@ -31,7 +31,7 @@ recipe first [
 
 # result:address:list <- rest in:address:list
 recipe rest [
-  new-default-space
+  local-scope
   in:address:list <- next-ingredient
   result:address:list <- get in:address:list/deref, next:offset
   reply result:address:list
diff --git a/065duplex_list.mu b/065duplex_list.mu
index 02e1462d..c54934ec 100644
--- a/065duplex_list.mu
+++ b/065duplex_list.mu
@@ -8,7 +8,7 @@ container duplex-list [
 
 # result:address:duplex-list <- push-duplex x:location, in:address:duplex-list
 recipe push-duplex [
-  new-default-space
+  local-scope
   x:location <- next-ingredient
   in:address:duplex-list <- next-ingredient
   result:address:duplex-list <- new duplex-list:type
@@ -24,7 +24,7 @@ recipe push-duplex [
 
 # result:location <- first-duplex in:address:duplex-list
 recipe first-duplex [
-  new-default-space
+  local-scope
   in:address:duplex-list <- next-ingredient
   reply-unless in:address:duplex-list, 0:literal
   result:location <- get in:address:duplex-list/deref, value:offset
@@ -33,7 +33,7 @@ recipe first-duplex [
 
 # result:address:duplex-list <- next-duplex in:address:duplex-list
 recipe next-duplex [
-  new-default-space
+  local-scope
   in:address:duplex-list <- next-ingredient
   reply-unless in:address:duplex-list, 0:literal
   result:address:duplex-list <- get in:address:duplex-list/deref, next:offset
@@ -42,7 +42,7 @@ recipe next-duplex [
 
 # result:address:duplex-list <- prev-duplex in:address:duplex-list
 recipe prev-duplex [
-  new-default-space
+  local-scope
   in:address:duplex-list <- next-ingredient
   reply-unless in:address:duplex-list, 0:literal
   result:address:duplex-list <- get in:address:duplex-list/deref, prev:offset
@@ -95,7 +95,7 @@ scenario duplex-list-handling [
 # l:address:duplex-list <- insert-duplex x:location, in:address:duplex-list
 # Inserts 'x' after 'in'. Returns some pointer into the list.
 recipe insert-duplex [
-  new-default-space
+  local-scope
   x:location <- next-ingredient
   in:address:duplex-list <- next-ingredient
   new-node:address:duplex-list <- new duplex-list:type
@@ -237,7 +237,7 @@ scenario inserting-after-start-of-duplex-list [
 # Returns null if and only if list is empty. Beware: in that case any pointers
 # to the head are now invalid.
 recipe remove-duplex [
-  new-default-space
+  local-scope
   in:address:duplex-list <- next-ingredient
   # if 'in' is null, return
   reply-unless in:address:duplex-list, in:address:duplex-list
diff --git a/066stream.mu b/066stream.mu
index d07d99e9..7bdf36a5 100644
--- a/066stream.mu
+++ b/066stream.mu
@@ -5,7 +5,7 @@ container stream [
 ]
 
 recipe new-stream [
-  new-default-space
+  local-scope
   result:address:stream <- new stream:type
   i:address:number <- get-address result:address:stream/deref, index:offset
   i:address:number/deref <- copy 0:literal
@@ -15,7 +15,7 @@ recipe new-stream [
 ]
 
 recipe rewind-stream [
-  new-default-space
+  local-scope
   in:address:stream <- next-ingredient
   x:address:number <- get-address in:address:stream/deref, index:offset
   x:address:number/deref <- copy 0:literal
@@ -23,7 +23,7 @@ recipe rewind-stream [
 ]
 
 recipe read-line [
-  new-default-space
+  local-scope
   in:address:stream <- next-ingredient
   idx:address:number <- get-address in:address:stream/deref, index:offset
   s:address:array:character <- get in:address:stream/deref, data:offset
@@ -34,7 +34,7 @@ recipe read-line [
 ]
 
 recipe end-of-stream? [
-  new-default-space
+  local-scope
   in:address:stream <- next-ingredient
   idx:number <- get in:address:stream/deref, index:offset
   s:address:array:character <- get in:address:stream/deref, data:offset
diff --git a/071print.mu b/071print.mu
index 9c05b09e..f0be8cfa 100644
--- a/071print.mu
+++ b/071print.mu
@@ -15,7 +15,7 @@ container screen-cell [
 ]
 
 recipe new-fake-screen [
-  new-default-space
+  local-scope
   result:address:screen <- new screen:type
   width:address:number <- get-address result:address:screen/deref, num-columns:offset
   width:address:number/deref <- next-ingredient
@@ -35,7 +35,7 @@ recipe new-fake-screen [
 ]
 
 recipe clear-screen [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
 #?   $print [clearing screen
 #? ] #? 1
@@ -70,7 +70,7 @@ recipe clear-screen [
 ]
 
 recipe print-character [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   c:character <- next-ingredient
   color:number, color-found?:boolean <- next-ingredient
@@ -314,7 +314,7 @@ scenario print-at-bottom-right [
 ]
 
 recipe clear-line [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   # if x exists, clear line in fake screen
   {
@@ -343,7 +343,7 @@ recipe clear-line [
 ]
 
 recipe cursor-position [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   # if x exists, lookup cursor in fake screen
   {
@@ -357,7 +357,7 @@ recipe cursor-position [
 ]
 
 recipe move-cursor [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   new-row:number <- next-ingredient
   new-column:number <- next-ingredient
@@ -407,7 +407,7 @@ scenario clear-line-erases-printed-characters [
 ]
 
 recipe cursor-down [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   # if x exists, move cursor in fake screen
   {
@@ -434,7 +434,7 @@ recipe cursor-down [
 ]
 
 recipe cursor-up [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   # if x exists, move cursor in fake screen
   {
@@ -455,7 +455,7 @@ recipe cursor-up [
 ]
 
 recipe cursor-right [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   # if x exists, move cursor in fake screen
   {
@@ -477,7 +477,7 @@ recipe cursor-right [
 ]
 
 recipe cursor-left [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   # if x exists, move cursor in fake screen
   {
@@ -498,7 +498,7 @@ recipe cursor-left [
 ]
 
 recipe cursor-to-start-of-line [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   row:number, _, x:address:screen <- cursor-position x:address:screen
   column:number <- copy 0:literal
@@ -507,7 +507,7 @@ recipe cursor-to-start-of-line [
 ]
 
 recipe cursor-to-next-line [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   x:address:screen <- cursor-down x:address:screen
   x:address:screen <- cursor-to-start-of-line x:address:screen
@@ -515,7 +515,7 @@ recipe cursor-to-next-line [
 ]
 
 recipe screen-width [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   # if x exists, move cursor in fake screen
   {
@@ -529,7 +529,7 @@ recipe screen-width [
 ]
 
 recipe screen-height [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   # if x exists, move cursor in fake screen
   {
@@ -543,7 +543,7 @@ recipe screen-height [
 ]
 
 recipe hide-cursor [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   # if x exists (not real display), do nothing
   {
@@ -556,7 +556,7 @@ recipe hide-cursor [
 ]
 
 recipe show-cursor [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   # if x exists (not real display), do nothing
   {
@@ -569,7 +569,7 @@ recipe show-cursor [
 ]
 
 recipe hide-screen [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   # if x exists (not real display), do nothing
   {
@@ -582,7 +582,7 @@ recipe hide-screen [
 ]
 
 recipe show-screen [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   # if x exists (not real display), do nothing
   {
@@ -595,7 +595,7 @@ recipe show-screen [
 ]
 
 recipe print-string [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   s:address:array:character <- next-ingredient
   color:number, color-found?:boolean <- next-ingredient
@@ -644,7 +644,7 @@ scenario print-string-stops-at-right-margin [
 ]
 
 recipe print-integer [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   n:number <- next-ingredient
   color:number, color-found?:boolean <- next-ingredient
diff --git a/074console.mu b/074console.mu
index 8c96e123..9c0b2bea 100644
--- a/074console.mu
+++ b/074console.mu
@@ -20,7 +20,7 @@ container console [
 ]
 
 recipe new-fake-console [
-  new-default-space
+  local-scope
   result:address:console <- new console:type
   buf:address:address:array:character <- get-address result:address:console/deref, data:offset
 #?   $start-tracing #? 1
@@ -32,7 +32,7 @@ recipe new-fake-console [
 ]
 
 recipe read-event [
-  new-default-space
+  local-scope
   x:address:console <- next-ingredient
   {
     break-unless x:address:console
@@ -59,7 +59,7 @@ recipe read-event [
 # isn't unicode, so no arrow keys, page-up/page-down, etc. But you still get
 # newlines, tabs, ctrl-d..
 recipe read-key [
-  new-default-space
+  local-scope
 #?   $print default-space:address:array:location #? 1
 #?   $exit #? 1
 #?   $start-tracing #? 1
@@ -77,7 +77,7 @@ recipe read-key [
 ]
 
 recipe send-keys-to-channel [
-  new-default-space
+  local-scope
   console:address <- next-ingredient
   chan:address:channel <- next-ingredient
   screen:address <- next-ingredient
@@ -93,7 +93,7 @@ recipe send-keys-to-channel [
 ]
 
 recipe wait-for-event [
-  new-default-space
+  local-scope
   console:address <- next-ingredient
   {
     _, console:address, found?:boolean <- read-event console:address
@@ -103,7 +103,7 @@ recipe wait-for-event [
 
 # use this helper to skip rendering if there's lots of other events queued up
 recipe has-more-events? [
-  new-default-space
+  local-scope
   console:address <- next-ingredient
   {
     break-unless console:address
diff --git a/channel.mu b/channel.mu
index 61f0e92b..94cb2521 100644
--- a/channel.mu
+++ b/channel.mu
@@ -2,7 +2,7 @@
 
 recipe producer [
   # produce numbers 1 to 5 on a channel
-  new-default-space
+  local-scope
   chan:address:channel <- next-ingredient
   # n = 0
   n:number <- copy 0:literal
@@ -20,7 +20,7 @@ recipe producer [
 
 recipe consumer [
   # consume and print integers from a channel
-  new-default-space
+  local-scope
   chan:address:channel <- next-ingredient
   {
     # read an integer from the channel
@@ -33,7 +33,7 @@ recipe consumer [
 ]
 
 recipe main [
-  new-default-space
+  local-scope
   chan:address:channel <- new-channel 3:literal
   # create two background 'routines' that communicate by a channel
   routine1:number <- start-running producer:recipe, chan:address:channel
diff --git a/chessboard.mu b/chessboard.mu
index f5b298cb..a91e6da7 100644
--- a/chessboard.mu
+++ b/chessboard.mu
@@ -82,7 +82,7 @@ scenario print-board-and-read-move [
 recipe chessboard [
 #?   $start-tracing [schedule] #? 2
 #?   $start-tracing #? 1
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   console:address <- next-ingredient
 #?   $print [screen: ], screen:address, [, console: ], console:address, [ 
@@ -138,7 +138,7 @@ recipe chessboard [
 ## a board is an array of files, a file is an array of characters (squares)
 
 recipe new-board [
-  new-default-space
+  local-scope
   initial-position:address:array:number <- next-ingredient
   # assert(length(initial-position) == 64)
   len:number <- length initial-position:address:array:number/deref
@@ -159,7 +159,7 @@ recipe new-board [
 ]
 
 recipe new-file [
-  new-default-space
+  local-scope
   position:address:array:number <- next-ingredient
   index:number <- next-ingredient
   index:number <- multiply index:number, 8:literal
@@ -178,7 +178,7 @@ recipe new-file [
 ]
 
 recipe print-board [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   board:address:array:address:array:character <- next-ingredient
   row:number <- copy 7:literal  # start printing from the top of the board
@@ -227,7 +227,7 @@ recipe print-board [
 
 # board:address:array:address:array:character <- initial-position
 recipe initial-position [
-  new-default-space
+  local-scope
   # layout in memory (in raster order):
   #   R P _ _ _ _ p r
   #   N P _ _ _ _ p n
@@ -287,7 +287,7 @@ container move [
 # result:address:move, quit?:boolean, error?:boolean <- read-move stdin:address:channel, screen:address
 # prints only error messages to screen
 recipe read-move [
-  new-default-space
+  local-scope
   stdin:address:channel <- next-ingredient
   screen:address <- next-ingredient
 #?   $print screen:address #? 1
@@ -322,7 +322,7 @@ recipe read-move [
 # file:number, quit:boolean, error:boolean <- read-file stdin:address:channel, screen:address
 # valid values for file: 0-7
 recipe read-file [
-  new-default-space
+  local-scope
   stdin:address:channel <- next-ingredient
   screen:address <- next-ingredient
   c:character, stdin:address:channel <- read stdin:address:channel
@@ -375,7 +375,7 @@ recipe read-file [
 # rank:number <- read-rank stdin:address:channel, screen:address
 # valid values: 0-7, -1 (quit), -2 (error)
 recipe read-rank [
-  new-default-space
+  local-scope
   stdin:address:channel <- next-ingredient
   screen:address <- next-ingredient
   c:character, stdin:address:channel <- read stdin:address:channel
@@ -422,7 +422,7 @@ recipe read-rank [
 # read a character from the given channel and check that it's what we expect
 # return true on error
 recipe expect-from-channel [
-  new-default-space
+  local-scope
   stdin:address:channel <- next-ingredient
   expected:character <- next-ingredient
   screen:address <- next-ingredient
@@ -624,7 +624,7 @@ F read-move-file: routine failed to pause after coming up (before any keys were
 ]
 
 recipe make-move [
-  new-default-space
+  local-scope
   b:address:array:address:array:character <- next-ingredient
   m:address:move <- next-ingredient
   from-file:number <- get m:address:move/deref, from-file:offset
diff --git a/counters.mu b/counters.mu
index 713c8fa8..2d57943d 100644
--- a/counters.mu
+++ b/counters.mu
@@ -8,7 +8,7 @@ recipe new-counter [
 ]
 
 recipe increment-counter [
-  new-default-space
+  local-scope
   0:address:array:location/names:new-counter <- next-ingredient  # setup outer space; it *must* come from 'new-counter'
   x:number <- next-ingredient
   n:number/space:1 <- add n:number/space:1, x:number
@@ -16,7 +16,7 @@ recipe increment-counter [
 ]
 
 recipe main [
-  new-default-space
+  local-scope
   # counter A
   a:address:array:location <- new-counter 34:literal
   # counter B
diff --git a/edit.mu b/edit.mu
index fef432cb..518d7ed7 100644
--- a/edit.mu
+++ b/edit.mu
@@ -1,7 +1,7 @@
 # Environment for learning programming using mu.
 
 recipe main [
-  new-default-space
+  local-scope
   open-console
   initial-recipe:address:array:character <- new [recipe new-add [
   x:number <- next-ingredient
@@ -24,7 +24,7 @@ container programming-environment-data [
 ]
 
 recipe new-programming-environment [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   initial-recipe-contents:address:array:character <- next-ingredient
   initial-sandbox-contents:address:array:character <- next-ingredient
@@ -91,7 +91,7 @@ container editor-data [
 #   top/left/right constrain the screen area available to the new editor.
 #   right is exclusive.
 recipe new-editor [
-  new-default-space
+  local-scope
   s:address:array:character <- next-ingredient
   screen:address <- next-ingredient
   # no clipping of bounds
@@ -161,7 +161,7 @@ scenario editor-initializes-without-data [
 
 # bottom:number, screen:address <- render screen:address, editor:address:editor-data
 recipe render [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   editor:address:editor-data <- next-ingredient
   reply-unless editor:address:editor-data, 1:literal/top, screen:address/same-as-ingredient:0
@@ -268,7 +268,7 @@ recipe render [
 # print a string 's' to 'editor' in 'color' starting at 'row'
 # leave cursor at start of next line
 recipe render-string [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   s:address:array:character <- next-ingredient
   left:number <- next-ingredient
@@ -334,7 +334,7 @@ recipe render-string [
 ]
 
 recipe clear-line-delimited [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   left:number <- next-ingredient
   right:number <- next-ingredient
@@ -455,7 +455,7 @@ scenario editor-initializes-empty-text [
 ## handling events from the keyboard, mouse, touch screen, ...
 
 recipe event-loop [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   console:address <- next-ingredient
   env:address:programming-environment-data <- next-ingredient
@@ -514,7 +514,7 @@ recipe event-loop [
 
 # helper for testing a single editor
 recipe editor-event-loop [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   console:address <- next-ingredient
   editor:address:editor-data <- next-ingredient
@@ -547,7 +547,7 @@ recipe editor-event-loop [
 ]
 
 recipe handle-event [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   console:address <- next-ingredient
   editor:address:editor-data <- next-ingredient
@@ -686,7 +686,7 @@ recipe handle-event [
 # process click, return if it was on current editor
 # todo: ignores menu bar (for now just displays shortcuts)
 recipe move-cursor-in-editor [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   editor:address:editor-data <- next-ingredient
   t:touch-event <- next-ingredient
@@ -709,7 +709,7 @@ recipe move-cursor-in-editor [
 ]
 
 recipe insert-at-cursor [
-  new-default-space
+  local-scope
   editor:address:editor-data <- next-ingredient
   c:character <- next-ingredient
   screen:address <- next-ingredient
@@ -757,7 +757,7 @@ recipe insert-at-cursor [
 ]
 
 recipe delete-before-cursor [
-  new-default-space
+  local-scope
   editor:address:editor-data <- next-ingredient
   before-cursor:address:address:duplex-list <- get-address editor:address:editor-data/deref, before-cursor:offset
   d:address:duplex-list <- get editor:address:editor-data/deref, data:offset
@@ -778,7 +778,7 @@ recipe delete-before-cursor [
 # takes a pointer 'curr' into the doubly-linked list and its sentinel, counts
 # the length of the previous line before the 'curr' pointer.
 recipe previous-line-length [
-  new-default-space
+  local-scope
   curr:address:duplex-list <- next-ingredient
   start:address:duplex-list <- next-ingredient
   result:number <- copy 0:literal
@@ -800,7 +800,7 @@ recipe previous-line-length [
 ]
 
 recipe render-all [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   env:address:programming-environment-data <- next-ingredient
   recipes:address:editor-data <- get env:address:programming-environment-data/deref, recipes:offset
@@ -844,7 +844,7 @@ recipe render-all [
 ]
 
 recipe render-sandboxes [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   sandbox:address:sandbox-data <- next-ingredient
   left:number <- next-ingredient
@@ -877,7 +877,7 @@ recipe render-sandboxes [
 ]
 
 recipe update-cursor [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   recipes:address:editor-data <- next-ingredient
   current-sandbox:address:editor-data <- next-ingredient
@@ -1941,7 +1941,7 @@ scenario run-and-show-results [
 ]
 
 recipe run-sandboxes [
-  new-default-space
+  local-scope
   env:address:programming-environment-data <- next-ingredient
   recipes:address:editor-data <- get env:address:programming-environment-data/deref, recipes:offset
   current-sandbox:address:editor-data <- get env:address:programming-environment-data/deref, current-sandbox:offset
@@ -2084,7 +2084,7 @@ scenario run-instruction-and-print-warnings [
 ]
 
 recipe editor-contents [
-  new-default-space
+  local-scope
   editor:address:editor-data <- next-ingredient
   buf:address:buffer <- new-buffer 80:literal
   curr:address:duplex-list <- get editor:address:editor-data/deref, data:offset
@@ -2124,7 +2124,7 @@ scenario editor-provides-edited-contents [
 ## helpers for drawing editor borders
 
 recipe draw-box [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   top:number <- next-ingredient
   left:number <- next-ingredient
@@ -2152,7 +2152,7 @@ recipe draw-box [
 ]
 
 recipe draw-horizontal [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   row:number <- next-ingredient
   x:number <- next-ingredient
@@ -2184,7 +2184,7 @@ recipe draw-horizontal [
 ]
 
 recipe draw-vertical [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   col:number <- next-ingredient
   x:number <- next-ingredient
@@ -2211,7 +2211,7 @@ recipe draw-vertical [
 ]
 
 recipe draw-top-left [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   top:number <- next-ingredient
   left:number <- next-ingredient
@@ -2226,7 +2226,7 @@ recipe draw-top-left [
 ]
 
 recipe draw-top-right [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   top:number <- next-ingredient
   right:number <- next-ingredient
@@ -2241,7 +2241,7 @@ recipe draw-top-right [
 ]
 
 recipe draw-bottom-left [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   bottom:number <- next-ingredient
   left:number <- next-ingredient
@@ -2256,7 +2256,7 @@ recipe draw-bottom-left [
 ]
 
 recipe draw-bottom-right [
-  new-default-space
+  local-scope
   screen:address <- next-ingredient
   bottom:number <- next-ingredient
   right:number <- next-ingredient
@@ -2271,7 +2271,7 @@ recipe draw-bottom-right [
 ]
 
 recipe print-string-with-gradient-background [
-  new-default-space
+  local-scope
   x:address:screen <- next-ingredient
   s:address:array:character <- next-ingredient
   color:number <- next-ingredient
diff --git a/factorial.mu b/factorial.mu
index 70fa17fe..6448bff1 100644
--- a/factorial.mu
+++ b/factorial.mu
@@ -1,14 +1,14 @@
 # example program: compute the factorial of 5
 
 recipe main [
-  new-default-space
+  local-scope
   x:number <- factorial 5:literal
   $print [result: ], x:number, [ 
 ]
 ]
 
 recipe factorial [
-  new-default-space
+  local-scope
   n:number <- next-ingredient
   {
     # if n=0 return 1
diff --git a/mu.vim b/mu.vim
index 7d43ec9a..f8440bfb 100644
--- a/mu.vim
+++ b/mu.vim
@@ -45,7 +45,7 @@ highlight link muNumber Constant
 syntax match muLabel "^\s\+[^ 0-9a-zA-Z{}\[\]][^ ]*\s*$"
 syntax match muLabel %[^ ]\+:label/\?[^ ,]*%
 highlight link muLabel Constant
-syntax keyword muKeyword default-space global-space new-default-space next-ingredient ingredient | highlight link muKeyword Constant
+syntax keyword muKeyword default-space global-space new-default-space local-scope next-ingredient ingredient | highlight link muKeyword Constant
 
 syntax match muDelimiter "[{}]" | highlight link muDelimiter Delimiter
 syntax match muAssign " <- \|\<raw\>" | highlight link muAssign SpecialChar
diff --git a/tangle.mu b/tangle.mu
index b97d46b4..8ac7abe6 100644
--- a/tangle.mu
+++ b/tangle.mu
@@ -7,7 +7,7 @@
 # possibilities.
 
 recipe factorial [
-  new-default-space
+  local-scope
   n:number <- next-ingredient
   {
     +base-case