about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-04-14 19:06:57 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-04-14 19:08:38 -0700
commit82ac0b7ecbc145ed8c8ecd8309166f654af1ee75 (patch)
treefb24d253bec1b3a24bf9d3e6d746111dbf4b856f
parent0edf822f996c5def4588cc207d1977cffc3e9a0c (diff)
downloadmu-82ac0b7ecbc145ed8c8ecd8309166f654af1ee75.tar.gz
1063 - variable names for surrounding spaces now work
This was a pain to debug.
-rw-r--r--cpp/.traces/closure120
-rw-r--r--cpp/.traces/convert_names_warns1
-rw-r--r--cpp/.traces/new2
-rw-r--r--cpp/.traces/new_array2
-rw-r--r--cpp/.traces/string-equal-common-lengths-but-distinct2
-rw-r--r--cpp/.traces/string-equal-distinct-lengths2
-rw-r--r--cpp/.traces/string-equal-identical2
-rw-r--r--cpp/.traces/string-equal-reflexive2
-rw-r--r--cpp/.traces/string-equal-with-empty2
-rw-r--r--cpp/.traces/surrounding_space3
-rw-r--r--cpp/010vm4
-rw-r--r--cpp/013run1
-rw-r--r--cpp/023call_reply1
-rw-r--r--cpp/025name18
-rw-r--r--cpp/026new1
-rw-r--r--cpp/027space9
-rw-r--r--cpp/028space_surround20
-rw-r--r--cpp/036closure_name142
-rw-r--r--cpp/090debug2
-rw-r--r--mu.arc12
20 files changed, 326 insertions, 22 deletions
diff --git a/cpp/.traces/closure b/cpp/.traces/closure
new file mode 100644
index 00000000..152df47e
--- /dev/null
+++ b/cpp/.traces/closure
@@ -0,0 +1,120 @@
+parse/0: instruction: 30
+parse/0:   ingredient: {name: "location", value: 0, type: 0, properties: ["location": "type"]}
+parse/0:   ingredient: {name: "30", value: 0, type: 0, properties: ["30": "literal"]}
+parse/0:   product: {name: "default-space", value: 0, type: 2-0, properties: ["default-space": "address":"space"]}
+parse/0: instruction: 1001
+parse/0:   product: {name: "1", value: 0, type: 2-0, properties: ["1": "address":"space", "names": "init-counter"]}
+parse/0: instruction: 1002
+parse/0:   ingredient: {name: "1", value: 0, type: 2-0, properties: ["1": "address":"space", "names": "init-counter"]}
+parse/0:   product: {name: "2", value: 0, type: 1, properties: ["2": "integer", "raw": ]}
+parse/0: instruction: 1002
+parse/0:   ingredient: {name: "1", value: 0, type: 2-0, properties: ["1": "address":"space", "names": "init-counter"]}
+parse/0:   product: {name: "3", value: 0, type: 1, properties: ["3": "integer", "raw": ]}
+parse/0: instruction: 30
+parse/0:   ingredient: {name: "location", value: 0, type: 0, properties: ["location": "type"]}
+parse/0:   ingredient: {name: "30", value: 0, type: 0, properties: ["30": "literal"]}
+parse/0:   product: {name: "default-space", value: 0, type: 2-0, properties: ["default-space": "address":"space"]}
+parse/0: instruction: 1
+parse/0:   ingredient: {name: "23", value: 0, type: 0, properties: ["23": "literal"]}
+parse/0:   product: {name: "x", value: 0, type: 1, properties: ["x": "integer"]}
+parse/0: instruction: 1
+parse/0:   ingredient: {name: "3", value: 0, type: 0, properties: ["3": "literal"]}
+parse/0:   product: {name: "y", value: 0, type: 1, properties: ["y": "integer"]}
+parse/0: instruction: 23
+parse/0:   ingredient: {name: "default-space", value: 0, type: 2-0, properties: ["default-space": "address":"space"]}
+parse/0: instruction: 30
+parse/0:   ingredient: {name: "space", value: 0, type: 0, properties: ["space": "literal"]}
+parse/0:   ingredient: {name: "30", value: 0, type: 0, properties: ["30": "literal"]}
+parse/0:   product: {name: "default-space", value: 0, type: 2-0, properties: ["default-space": "address":"space"]}
+parse/0: instruction: 22
+parse/0:   product: {name: "0", value: 0, type: 2-0, properties: ["0": "address":"space", "names": "init-counter"]}
+parse/0: instruction: 2
+parse/0:   ingredient: {name: "y", value: 0, type: 1, properties: ["y": "integer", "space": "1"]}
+parse/0:   ingredient: {name: "1", value: 0, type: 0, properties: ["1": "literal"]}
+parse/0:   product: {name: "y", value: 0, type: 1, properties: ["y": "integer", "space": "1"]}
+parse/0: instruction: 1
+parse/0:   ingredient: {name: "234", value: 0, type: 0, properties: ["234": "literal"]}
+parse/0:   product: {name: "y", value: 0, type: 1, properties: ["y": "integer"]}
+parse/0: instruction: 23
+parse/0:   ingredient: {name: "y", value: 0, type: 1, properties: ["y": "integer", "space": "1"]}
+name/0: recipe increment-counter is surrounded by init-counter
+new/0: location -> 1
+name/0: assign x 1
+name/0: assign y 2
+new/0: space -> 0
+name/0: assign y 1
+new/0: location -> 1
+after-brace/0: recipe init-counter
+after-brace/0: new ...
+after-brace/0: copy ...
+after-brace/0: copy ...
+after-brace/0: reply ...
+after-brace/0: recipe increment-counter
+after-brace/0: new ...
+after-brace/0: next-ingredient ...
+after-brace/0: add ...
+after-brace/0: copy ...
+after-brace/0: reply ...
+after-brace/0: recipe main
+after-brace/0: new ...
+after-brace/0: init-counter ...
+after-brace/0: increment-counter ...
+after-brace/0: increment-counter ...
+run/0: instruction main/0
+mem/0: new alloc: 1000
+mem/0: array size is 30
+run/0: instruction main/1
+run/0: instruction init-counter/0
+mem/0: new alloc: 1030
+mem/0: array size is 30
+run/0: instruction init-counter/1
+run/0: ingredient 0 is 23
+mem/0: storing 23 in location 1032
+run/0: instruction init-counter/2
+run/0: ingredient 0 is 3
+mem/0: storing 3 in location 1033
+run/0: instruction init-counter/3
+run/0: result 0 is 1030
+mem/0: storing 1030 in location 1002
+run/0: instruction main/2
+mem/0: location 1002 is 1030
+run/0: instruction increment-counter/0
+mem/0: new alloc: 1060
+mem/0: array size is 30
+run/0: instruction increment-counter/1
+run/0: product 0 is 1030
+mem/0: storing 1030 in location 1061
+run/0: instruction increment-counter/2
+run/0: ingredient 0 is y
+mem/0: location 1033 is 3
+run/0: ingredient 1 is 1
+run/0: product 0 is 4
+mem/0: storing 4 in location 1033
+run/0: instruction increment-counter/3
+run/0: ingredient 0 is 234
+mem/0: storing 234 in location 1062
+run/0: instruction increment-counter/4
+mem/0: location 1033 is 4
+run/0: result 0 is 4
+mem/0: storing 4 in location 2
+run/0: instruction main/3
+mem/0: location 1002 is 1030
+run/0: instruction increment-counter/0
+mem/0: new alloc: 1090
+mem/0: array size is 30
+run/0: instruction increment-counter/1
+run/0: product 0 is 1030
+mem/0: storing 1030 in location 1091
+run/0: instruction increment-counter/2
+run/0: ingredient 0 is y
+mem/0: location 1033 is 4
+run/0: ingredient 1 is 1
+run/0: product 0 is 5
+mem/0: storing 5 in location 1033
+run/0: instruction increment-counter/3
+run/0: ingredient 0 is 234
+mem/0: storing 234 in location 1092
+run/0: instruction increment-counter/4
+mem/0: location 1033 is 5
+run/0: result 0 is 5
+mem/0: storing 5 in location 3
diff --git a/cpp/.traces/convert_names_warns b/cpp/.traces/convert_names_warns
index 837f83df..11614845 100644
--- a/cpp/.traces/convert_names_warns
+++ b/cpp/.traces/convert_names_warns
@@ -2,6 +2,7 @@ parse/0: instruction: 1
 parse/0:   ingredient: {name: "y", value: 0, type: 1, properties: ["y": "integer"]}
 parse/0:   product: {name: "x", value: 0, type: 1, properties: ["x": "integer"]}
 warn/0: use before set: y in main
+warn/0: name not found: y
 name/0: assign x 1
 after-brace/0: recipe main
 after-brace/0: copy ...
diff --git a/cpp/.traces/new b/cpp/.traces/new
index e9be5862..4c3d8001 100644
--- a/cpp/.traces/new
+++ b/cpp/.traces/new
@@ -15,8 +15,10 @@ after-brace/0: new ...
 after-brace/0: new ...
 after-brace/0: equal ...
 run/0: instruction main/0
+mem/0: new alloc: 1000
 mem/0: storing 1000 in location 1
 run/0: instruction main/1
+mem/0: new alloc: 1001
 mem/0: storing 1001 in location 2
 run/0: instruction main/2
 run/0: ingredient 0 is 1
diff --git a/cpp/.traces/new_array b/cpp/.traces/new_array
index 8a44bcf8..d2e846bb 100644
--- a/cpp/.traces/new_array
+++ b/cpp/.traces/new_array
@@ -16,9 +16,11 @@ after-brace/0: new ...
 after-brace/0: new ...
 after-brace/0: subtract ...
 run/0: instruction main/0
+mem/0: new alloc: 1000
 mem/0: storing 1000 in location 1
 mem/0: array size is 5
 run/0: instruction main/1
+mem/0: new alloc: 1005
 mem/0: storing 1005 in location 2
 run/0: instruction main/2
 run/0: ingredient 0 is 2
diff --git a/cpp/.traces/string-equal-common-lengths-but-distinct b/cpp/.traces/string-equal-common-lengths-but-distinct
index 58112874..e5e99282 100644
--- a/cpp/.traces/string-equal-common-lengths-but-distinct
+++ b/cpp/.traces/string-equal-common-lengths-but-distinct
@@ -23,6 +23,7 @@ after-brace/0: new ...
 after-brace/0: new ...
 after-brace/0: string-equal ...
 run/0: instruction test-string-equal-common-lengths-but-distinct/0
+mem/0: new alloc: 1000
 mem/0: array size is 30
 run/0: instruction test-string-equal-common-lengths-but-distinct/1
 mem/0: storing 1030 in location 1002
@@ -32,6 +33,7 @@ run/0: instruction test-string-equal-common-lengths-but-distinct/3
 mem/0: location 1002 is 1030
 mem/0: location 1003 is 1034
 run/0: instruction string-equal/0
+mem/0: new alloc: 1038
 mem/0: array size is 30
 run/0: instruction string-equal/1
 run/0: product 0 is 1030
diff --git a/cpp/.traces/string-equal-distinct-lengths b/cpp/.traces/string-equal-distinct-lengths
index 6008eaf4..4af8113c 100644
--- a/cpp/.traces/string-equal-distinct-lengths
+++ b/cpp/.traces/string-equal-distinct-lengths
@@ -23,6 +23,7 @@ after-brace/0: new ...
 after-brace/0: new ...
 after-brace/0: string-equal ...
 run/0: instruction test-string-equal-distinct-lengths/0
+mem/0: new alloc: 1000
 mem/0: array size is 30
 run/0: instruction test-string-equal-distinct-lengths/1
 mem/0: storing 1030 in location 1002
@@ -32,6 +33,7 @@ run/0: instruction test-string-equal-distinct-lengths/3
 mem/0: location 1002 is 1030
 mem/0: location 1003 is 1034
 run/0: instruction string-equal/0
+mem/0: new alloc: 1039
 mem/0: array size is 30
 run/0: instruction string-equal/1
 run/0: product 0 is 1030
diff --git a/cpp/.traces/string-equal-identical b/cpp/.traces/string-equal-identical
index ad2db713..7d25443e 100644
--- a/cpp/.traces/string-equal-identical
+++ b/cpp/.traces/string-equal-identical
@@ -23,6 +23,7 @@ after-brace/0: new ...
 after-brace/0: new ...
 after-brace/0: string-equal ...
 run/0: instruction test-string-equal-identical/0
+mem/0: new alloc: 1000
 mem/0: array size is 30
 run/0: instruction test-string-equal-identical/1
 mem/0: storing 1030 in location 1002
@@ -32,6 +33,7 @@ run/0: instruction test-string-equal-identical/3
 mem/0: location 1002 is 1030
 mem/0: location 1003 is 1034
 run/0: instruction string-equal/0
+mem/0: new alloc: 1038
 mem/0: array size is 30
 run/0: instruction string-equal/1
 run/0: product 0 is 1030
diff --git a/cpp/.traces/string-equal-reflexive b/cpp/.traces/string-equal-reflexive
index be1548bb..ab94aa39 100644
--- a/cpp/.traces/string-equal-reflexive
+++ b/cpp/.traces/string-equal-reflexive
@@ -17,6 +17,7 @@ after-brace/0: new ...
 after-brace/0: new ...
 after-brace/0: string-equal ...
 run/0: instruction test-string-equal-reflexive/0
+mem/0: new alloc: 1000
 mem/0: array size is 30
 run/0: instruction test-string-equal-reflexive/1
 mem/0: storing 1030 in location 1002
@@ -24,6 +25,7 @@ run/0: instruction test-string-equal-reflexive/2
 mem/0: location 1002 is 1030
 mem/0: location 1002 is 1030
 run/0: instruction string-equal/0
+mem/0: new alloc: 1034
 mem/0: array size is 30
 run/0: instruction string-equal/1
 run/0: product 0 is 1030
diff --git a/cpp/.traces/string-equal-with-empty b/cpp/.traces/string-equal-with-empty
index 13f64aac..22a9d4a9 100644
--- a/cpp/.traces/string-equal-with-empty
+++ b/cpp/.traces/string-equal-with-empty
@@ -23,6 +23,7 @@ after-brace/0: new ...
 after-brace/0: new ...
 after-brace/0: string-equal ...
 run/0: instruction test-string-equal-with-empty/0
+mem/0: new alloc: 1000
 mem/0: array size is 30
 run/0: instruction test-string-equal-with-empty/1
 mem/0: storing 1030 in location 1002
@@ -32,6 +33,7 @@ run/0: instruction test-string-equal-with-empty/3
 mem/0: location 1002 is 1030
 mem/0: location 1003 is 1031
 run/0: instruction string-equal/0
+mem/0: new alloc: 1036
 mem/0: array size is 30
 run/0: instruction string-equal/1
 run/0: product 0 is 1030
diff --git a/cpp/.traces/surrounding_space b/cpp/.traces/surrounding_space
index 0f1371d5..f4bc55fe 100644
--- a/cpp/.traces/surrounding_space
+++ b/cpp/.traces/surrounding_space
@@ -9,13 +9,14 @@ parse/0:   ingredient: {name: "10", value: 0, type: 0, properties: ["10": "liter
 parse/0:   product: {name: "default-space", value: 0, type: 2-0, properties: ["default-space": "address":"space"]}
 parse/0: instruction: 1
 parse/0:   ingredient: {name: "20", value: 0, type: 0, properties: ["20": "literal"]}
-parse/0:   product: {name: "0", value: 0, type: 2-0, properties: ["0": "address":"space"]}
+parse/0:   product: {name: "0", value: 0, type: 2-0, properties: ["0": "address":"space", "names": "dummy"]}
 parse/0: instruction: 1
 parse/0:   ingredient: {name: "32", value: 0, type: 0, properties: ["32": "literal"]}
 parse/0:   product: {name: "1", value: 0, type: 1, properties: ["1": "integer"]}
 parse/0: instruction: 1
 parse/0:   ingredient: {name: "33", value: 0, type: 0, properties: ["33": "literal"]}
 parse/0:   product: {name: "1", value: 0, type: 1, properties: ["1": "integer", "space": "1"]}
+name/0: recipe main is surrounded by dummy
 after-brace/0: recipe main
 after-brace/0: copy ...
 after-brace/0: copy ...
diff --git a/cpp/010vm b/cpp/010vm
index 54fc8906..2c230c10 100644
--- a/cpp/010vm
+++ b/cpp/010vm
@@ -46,7 +46,7 @@ struct reagent {
   reagent(string s);
   reagent();
   void set_value(int v) { value = v; initialized = true; }
-  string to_string();
+  string to_string() const;
 };
 
 :(before "struct reagent")
@@ -192,7 +192,7 @@ Next_recipe_number = 1000;  // consistent new numbers for each test
     // assigned to.
     properties.push_back(pair<string, vector<string> >("", vector<string>()));
   }
-  string reagent::to_string() {
+  string reagent::to_string() const {
     ostringstream out;
     out << "{name: \"" << name << "\", value: " << value << ", type: ";
     for (size_t i = 0; i < types.size(); ++i) {
diff --git a/cpp/013run b/cpp/013run
index 1606a82d..91e242a7 100644
--- a/cpp/013run
+++ b/cpp/013run
@@ -44,6 +44,7 @@ void run(routine rr) {
 //?     cout << "AAA " << Trace_stream << " ^" << Trace_stream->dump_layer << "$\n"; //? 1
     trace("run") << "instruction " << recipe_name(rr) << '/' << pc;
 //?     cout << "operation " << instructions[pc].operation << '\n'; //? 3
+//?     if (!instructions[pc].products.empty()) trace("foo") << "AAA product 0 is " << instructions[pc].products[0].to_string(); //? 1
     switch (instructions[pc].operation) {
       // Primitive Recipe Implementations
       case COPY: {
diff --git a/cpp/023call_reply b/cpp/023call_reply
index 476875ab..4c9fe34c 100644
--- a/cpp/023call_reply
+++ b/cpp/023call_reply
@@ -27,6 +27,7 @@ case REPLY: {
     callee_results.push_back(read_memory(instructions[pc].ingredients[i]));
   }
   rr.calls.pop();
+  assert(!rr.calls.empty());
   size_t& caller_pc = rr.calls.top().pc;
   instruction& caller_instruction = Recipe[rr.calls.top().running_recipe].steps[caller_pc];
   assert(caller_instruction.products.size() <= callee_results.size());
diff --git a/cpp/025name b/cpp/025name
index 81ab0cc9..45fdece2 100644
--- a/cpp/025name
+++ b/cpp/025name
@@ -40,6 +40,7 @@ void transform_names(const recipe_number r) {
     // Per-recipe Transforms
     // map names to addresses
     for (size_t in = 0; in < inst.ingredients.size(); ++in) {
+//?       cout << "ingredients\n"; //? 1
       if (is_raw(inst.ingredients[in])) continue;
 //?       cout << "ingredient " << inst.ingredients[in].name << '\n'; //? 1
       if (inst.ingredients[in].name == "default-space")
@@ -47,13 +48,15 @@ void transform_names(const recipe_number r) {
       if (inst.ingredients[in].types[0]  // not a literal
           && !inst.ingredients[in].initialized
           && inst.ingredients[in].name.find_first_not_of("0123456789-.") != string::npos) {
-        if (names.find(inst.ingredients[in].name) == names.end()) {
+        if (!already_transformed(inst.ingredients[in], names)) {
           raise << "use before set: " << inst.ingredients[in].name << " in " << Recipe[r].name << '\n';
         }
-        inst.ingredients[in].set_value(names[inst.ingredients[in].name]);
+        inst.ingredients[in].set_value(lookup_name(inst.ingredients[in], r));
+//?         cout << "lookup ingredient " << Recipe[r].name << "/" << i << ": " << inst.ingredients[in].to_string() << '\n'; //? 1
       }
     }
     for (size_t out = 0; out < inst.products.size(); ++out) {
+//?       cout << "products\n"; //? 1
       if (is_raw(inst.products[out])) continue;
 //?       cout << "product " << out << '/' << inst.products.size() << " " << inst.products[out].name << '\n'; //? 4
 //?       cout << inst.products[out].types[0] << '\n'; //? 1
@@ -67,12 +70,21 @@ void transform_names(const recipe_number r) {
           names[inst.products[out].name] = curr_idx;
           curr_idx += size_of(inst.products[out]);
         }
-        inst.products[out].set_value(names[inst.products[out].name]);
+        inst.products[out].set_value(lookup_name(inst.products[out], r));
+//?         cout << "lookup product " << Recipe[r].name << "/" << i << ": " << inst.products[out].to_string() << '\n'; //? 1
       }
     }
   }
 }
 
+bool already_transformed(const reagent& r, const unordered_map<string, int>& names) {
+  return names.find(r.name) != names.end();
+}
+
+size_t lookup_name(const reagent& r, const recipe_number default_recipe) {
+  return Name[default_recipe][r.name];
+}
+
 type_number skip_addresses(const vector<type_number>& types) {
   for (size_t i = 0; i < types.size(); ++i) {
     if (types[i] != Type_number["address"]) return types[i];
diff --git a/cpp/026new b/cpp/026new
index 02196c49..e75d4148 100644
--- a/cpp/026new
+++ b/cpp/026new
@@ -45,6 +45,7 @@ Next_recipe_number++;
 :(before "End Primitive Recipe Implementations")
 case NEW: {
   vector<int> result;
+  trace("mem") << "new alloc: " << Current_routine->alloc;
   result.push_back(Current_routine->alloc);
   write_memory(instructions[pc].products[0], result);
   vector<int> types;
diff --git a/cpp/027space b/cpp/027space
index 2a845015..1bb91b8e 100644
--- a/cpp/027space
+++ b/cpp/027space
@@ -35,13 +35,15 @@ call(recipe_number r) :running_recipe(r), pc(0), next_ingredient_to_process(0),
 reagent r = absolutize(x);
 :(code)
 reagent absolutize(reagent x) {
-//?   cout << "absolutize " << x.to_string() << '\n'; //? 2
+//?   if (Recipe_number.find("increment-counter") != Recipe_number.end()) //? 1
+//?     cout << "AAA " << "increment-counter/2: " << Recipe[Recipe_number["increment-counter"]].steps[2].products[0].to_string() << '\n'; //? 1
+//?   cout << "absolutize " << x.to_string() << '\n'; //? 3
 //?   cout << is_raw(x) << '\n'; //? 1
   if (is_raw(x) || is_dummy(x)) return x;
 //?   cout << "not raw: " << x.to_string() << '\n'; //? 1
   assert(x.initialized);
   reagent r = x;
-  r.set_value(address(r.value, space(r)));
+  r.set_value(address(r.value, space_base(r)));
 //?   cout << "after absolutize: " << r.value << '\n'; //? 1
   r.properties.push_back(pair<string, vector<string> >("raw", vector<string>()));
   assert(is_raw(r));
@@ -51,12 +53,13 @@ reagent absolutize(reagent x) {
 result.properties.push_back(pair<string, vector<string> >("raw", vector<string>()));
 
 :(code)
-int space(const reagent& x) {
+int space_base(const reagent& x) {
   return Current_routine->calls.top().default_space;
 }
 
 int address(int offset, int base) {
   if (base == 0) return offset;  // raw
+//?   cout << base << '\n'; //? 1
   if (offset >= Memory[base]) {
     // todo: test
     raise << "location " << offset << " is out of bounds " << Memory[base] << '\n';
diff --git a/cpp/028space_surround b/cpp/028space_surround
index dd4fb44f..76cd5807 100644
--- a/cpp/028space_surround
+++ b/cpp/028space_surround
@@ -10,7 +10,7 @@ recipe main [
   10:integer <- copy 5:literal  # pretend array
   20:integer <- copy 5:literal  # pretend array
   default-space:address:space <- copy 10:literal
-  0:address:space <- copy 20:literal
+  0:address:space/names:dummy <- copy 20:literal  # later layers will explain the /names: property
   1:integer <- copy 32:literal
   1:integer/space:1 <- copy 33:literal
 ]
@@ -25,14 +25,20 @@ recipe main [
 //: lifetime, surrounding allows managing shorter lifetimes inside a longer
 //: one.
 
-:(replace{} "int space(const reagent& x)")
-int space(const reagent& x) {
-  return space(x, space_index(x), Current_routine->calls.top().default_space);
+:(replace{} "int space_base(const reagent& x)")
+int space_base(const reagent& x) {
+  return space_base(x, space_index(x), Current_routine->calls.top().default_space);
 }
 
-int space(const reagent& x, int space_index, int base) {
-  if (space_index == 0) return base;
-  return space(x, space_index-1, Memory[base+1]);
+int space_base(const reagent& x, int space_index, int base) {
+//?   trace("foo") << "base of space " << space_index << '\n'; //? 1
+  if (space_index == 0) {
+//?     trace("foo") << "base of space " << space_index << " is " << base << '\n'; //? 1
+    return base;
+  }
+//?   trace("foo") << "base of space " << space_index << " is " << Memory[base+1] << '\n'; //? 1
+  int result = space_base(x, space_index-1, Memory[base+1]);
+  return result;
 }
 
 int space_index(const reagent& x) {
diff --git a/cpp/036closure_name b/cpp/036closure_name
new file mode 100644
index 00000000..76a1bdd7
--- /dev/null
+++ b/cpp/036closure_name
@@ -0,0 +1,142 @@
+//: Writing to a literal (not computed) address of 0 in a recipe chains two
+//: spaces together. When a variable has a property of /space:1, it looks up
+//: the variable in the chained/surrounding space. /space:2 looks up the
+//: surrounding space of the surrounding space, etc.
+:(scenario "closure")
+recipe main [
+  default-space:address:space <- new location:type, 30:literal
+  1:address:space/names:init-counter <- init-counter
+#?   $print [AAAAAAAAAAAAAAAA]
+#?   $print 1:address:space
+  2:integer/raw <- increment-counter 1:address:space/names:init-counter
+  3:integer/raw <- increment-counter 1:address:space/names:init-counter
+]
+
+recipe init-counter [
+  default-space:address:space <- new location:type, 30:literal
+  x:integer <- copy 23:literal
+  y:integer <- copy 3:literal  # variable that will be incremented
+  reply default-space:address:space
+]
+
+recipe increment-counter [
+  default-space:address:space <- new space:literal, 30:literal
+  0:address:space/names:init-counter <- next-ingredient  # outer space must be created by 'init-counter' above
+  y:integer/space:1 <- add y:integer/space:1, 1:literal  # increment
+  y:integer <- copy 234:literal  # dummy
+  reply y:integer/space:1
+]
+
++name: recipe increment-counter is surrounded by init-counter
++mem: storing 5 in location 3
+
+//: To make this work, compute the recipe that provides names for the
+//: surrounding space of each recipe. This must happen before transform_names.
+:(before "End Globals")
+unordered_map<recipe_number, recipe_number> Surrounding_space;
+
+:(after "int main")
+  Transform.push_back(collect_surrounding_spaces);
+
+:(code)
+void collect_surrounding_spaces(const recipe_number r) {
+  for (size_t i = 0; i < Recipe[r].steps.size(); ++i) {
+    const instruction& inst = Recipe[r].steps[i];
+    if (inst.is_label) continue;
+    for (size_t j = 0; j < inst.products.size(); ++j) {
+      if (isa_literal(inst.products[j])) continue;
+      if (inst.products[j].name != "0") continue;
+      if (inst.products[j].types.size() != 2
+          || inst.products[j].types[0] != Type_number["address"]
+          || inst.products[j].types[1] != Type_number["space"]) {
+        raise << "slot 0 should always have type address:space, but is " << inst.products[j].to_string() << '\n';
+        continue;
+      }
+      vector<string> s = property(inst.products[j], "names");
+      if (s.empty())
+        raise << "slot 0 requires a /names property in recipe " << Recipe[r].name << die();
+      if (s.size() > 1) raise << "slot 0 should have a single value in /names, got " << inst.products[j].to_string() << '\n';
+      string surrounding_recipe_name = s[0];
+      if (Surrounding_space.find(r) != Surrounding_space.end()
+          && Surrounding_space[r] != Recipe_number[surrounding_recipe_name]) {
+        raise << "recipe " << Recipe[r].name << " can have only one 'surrounding' recipe but has " << Recipe[Surrounding_space[r]].name << " and " << surrounding_recipe_name << '\n';
+        continue;
+      }
+      trace("name") << "recipe " << Recipe[r].name << " is surrounded by " << surrounding_recipe_name;
+      Surrounding_space[r] = Recipe_number[surrounding_recipe_name];
+    }
+  }
+}
+
+vector<string> property(const reagent& r, const string& name) {
+  for (size_t p = 0; p != r.properties.size(); ++p) {
+    if (r.properties[p].first == name)
+      return r.properties[p].second;
+  }
+  return vector<string>();
+}
+
+//: Once surrounding spaces are available, transform_names uses them to handle
+//: /space properties.
+
+:(replace{} "size_t lookup_name(const reagent& r, const recipe_number default_recipe)")
+size_t lookup_name(const reagent& x, const recipe_number default_recipe) {
+//?   cout << "AAA " << default_recipe << " " << Recipe[default_recipe].name << '\n'; //? 2
+//?   cout << "AAA " << x.to_string() << '\n'; //? 1
+  if (!has_property(x, "space")) {
+    if (Name[default_recipe].empty()) raise << "name not found: " << x.name << '\n' << die();
+    return Name[default_recipe][x.name];
+  }
+  vector<string> p = property(x, "space");
+  if (p.size() != 1) raise << "/space property should have exactly one (non-negative integer) value\n";
+  int n = to_int(p[0]);
+  assert(n >= 0);
+  recipe_number surrounding_recipe = lookup_surrounding_recipe(default_recipe, n);
+  set<recipe_number> done;
+  vector<recipe_number> path;
+  return lookup_name(x, surrounding_recipe, done, path);
+}
+
+// If the recipe we need to lookup this name in doesn't have names done yet,
+// recursively call transform_names on it.
+size_t lookup_name(const reagent& x, const recipe_number r, set<recipe_number>& done, vector<recipe_number>& path) {
+  if (!Name[r].empty()) return Name[r][x.name];
+  if (done.find(r) != done.end()) {
+    raise << "can't compute address of " << x.to_string() << " because ";
+    for (size_t i = 1; i < path.size(); ++i) {
+      raise << path[i-1] << " requires computing names of " << path[i] << '\n';
+    }
+    raise << path[path.size()-1] << " requires computing names of " << r << "..ad infinitum\n" << die();
+    return 0;
+  }
+  done.insert(r);
+  path.push_back(r);
+  transform_names(r);  // Not passing 'done' through. Might this somehow cause an infinite loop?
+  assert(!Name[r].empty());
+  return Name[r][x.name];
+}
+
+recipe_number lookup_surrounding_recipe(const recipe_number r, size_t n) {
+  if (n == 0) return r;
+  if (Surrounding_space.find(r) == Surrounding_space.end()) {
+    raise << "don't know surrounding recipe of " << Recipe[r].name << '\n';
+    return 0;
+  }
+  assert(Surrounding_space[r]);
+  return lookup_surrounding_recipe(Surrounding_space[r], n-1);
+}
+
+//: weaken use-before-set warnings just a tad
+:(replace{} "bool already_transformed(const reagent& r, const unordered_map<string, int>& names)")
+bool already_transformed(const reagent& r, const unordered_map<string, int>& names) {
+  if (has_property(r, "space")) {
+    vector<string> p = property(r, "space");
+    assert(p.size() == 1);
+    if (p[0] != "0") return true;
+  }
+  return names.find(r.name) != names.end();
+}
+
+:(before "End Includes")
+#include<set>
+using std::set;
diff --git a/cpp/090debug b/cpp/090debug
index 08373a49..b6acae9d 100644
--- a/cpp/090debug
+++ b/cpp/090debug
@@ -7,11 +7,13 @@ assert(Next_recipe_number < _PRINT);
 :(before "End Primitive Recipe Implementations")
 case _PRINT: {
   if (isa_literal(instructions[pc].ingredients[0])) {
+    trace("run") << "$print: " << instructions[pc].ingredients[0].name;
     cout << instructions[pc].ingredients[0].name << '\n';
     break;
   }
   vector<int> result(read_memory(instructions[pc].ingredients[0]));
   for (size_t i = 0; i < result.size(); ++i) {
+    trace("run") << "$print: " << result[i];
     cout << result[i] << " ";
   }
   cout << '\n';
diff --git a/mu.arc b/mu.arc
index 283f056f..292126b3 100644
--- a/mu.arc
+++ b/mu.arc
@@ -1134,12 +1134,12 @@
           (die "routine has no globals: @operand"))
       :else
         (iflet base rep.routine*!call-stack.0!default-space
-          (lookup-space (rem [caris _ 'space] operand)
-                        base
-                        space.operand)
+          (space-base (rem [caris _ 'space] operand)
+                      base
+                      space.operand)
           operand)))
 
-(def lookup-space (operand base space)
+(def space-base (operand base space)
 ;?   (prn operand " " base) ;? 1
   (if (is 0 space)
     ; base case
@@ -1149,8 +1149,8 @@
         (raw))
       (die "no room for var @operand in routine of size @memory*.base"))
     ; recursive case
-    (lookup-space operand (memory* (+ base 1))  ; location 0 points to next space
-                  (- space 1))))
+    (space-base operand (memory* (+ base 1))  ; location 0 points to next space
+                (- space 1))))
 
 (def space (operand)
   (or (alref metadata.operand 'space)