about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--010vm.cc30
-rw-r--r--011load.cc20
-rw-r--r--012transform.cc7
-rw-r--r--020run.cc19
-rw-r--r--030container.cc33
-rw-r--r--037new.cc1
-rw-r--r--042name.cc13
-rw-r--r--056static_dispatch.cc14
-rw-r--r--058shape_shifting_recipe.cc19
-rw-r--r--091run_interactive.cc123
10 files changed, 92 insertions, 187 deletions
diff --git a/010vm.cc b/010vm.cc
index 56148f2a..1120280c 100644
--- a/010vm.cc
+++ b/010vm.cc
@@ -208,6 +208,36 @@ assert(Next_recipe_ordinal < 1000);  // recipes being tested didn't overflow int
 :(before "End Setup")
 Next_recipe_ordinal = 1000;  // consistent new numbers for each test
 
+//: One final detail: tests can modify our global tables of recipes and types,
+//: so we need some way to clean up after each test is done so it doesn't
+//: influence later ones.
+:(before "End Globals")
+map<string, recipe_ordinal> Recipe_ordinal_snapshot;
+map<recipe_ordinal, recipe> Recipe_snapshot;
+map<string, type_ordinal> Type_ordinal_snapshot;
+map<type_ordinal, type_info> Type_snapshot;
+:(before "End One-time Setup")
+save_snapshots();
+:(before "End Setup")
+restore_snapshots();
+
+:(code)
+void save_snapshots() {
+  Recipe_ordinal_snapshot = Recipe_ordinal;
+  Recipe_snapshot = Recipe;
+  Type_ordinal_snapshot = Type_ordinal;
+  Type_snapshot = Type;
+  // End save_snapshots
+}
+
+void restore_snapshots() {
+  Recipe = Recipe_snapshot;
+  Recipe_ordinal = Recipe_ordinal_snapshot;
+  Type_ordinal = Type_ordinal_snapshot;
+  Type = Type_snapshot;
+  // End restore_snapshots
+}
+
 
 
 //:: Helpers
diff --git a/011load.cc b/011load.cc
index ee8df684..d71a67b5 100644
--- a/011load.cc
+++ b/011load.cc
@@ -60,8 +60,6 @@ int slurp_recipe(istream& in) {
   slurp_body(in, result);
   // End Recipe Body(result)
   put(Recipe, get(Recipe_ordinal, result.name), result);
-  // track added recipes because we may need to undo them in tests; see below
-  Recently_added_recipes.push_back(get(Recipe_ordinal, result.name));
   return get(Recipe_ordinal, result.name);
 }
 
@@ -228,24 +226,6 @@ void show_rest_of_stream(istream& in) {
   exit(0);
 }
 
-//: Have tests clean up any recipes they added.
-:(before "End Globals")
-vector<recipe_ordinal> Recently_added_recipes;
-int Reserved_for_tests = 1000;
-:(before "End Setup")
-clear_recently_added_recipes();
-:(code)
-void clear_recently_added_recipes() {
-  for (int i = 0; i < SIZE(Recently_added_recipes); ++i) {
-    if (Recently_added_recipes.at(i) >= Reserved_for_tests  // don't renumber existing recipes, like 'interactive'
-        && contains_key(Recipe, Recently_added_recipes.at(i)))  // in case previous test had duplicate definitions
-      Recipe_ordinal.erase(get(Recipe, Recently_added_recipes.at(i)).name);
-    Recipe.erase(Recently_added_recipes.at(i));
-  }
-  // Clear Other State For Recently_added_recipes
-  Recently_added_recipes.clear();
-}
-
 :(scenario parse_comment_outside_recipe)
 # this comment will be dropped by the tangler, so we need a dummy recipe to stop that
 def f1 [
diff --git a/012transform.cc b/012transform.cc
index 0130cd16..a416c20a 100644
--- a/012transform.cc
+++ b/012transform.cc
@@ -56,13 +56,6 @@ void transform_all() {
   // End Transform All
 }
 
-// Later we'll have transforms create recipes out of other recipes. This
-// helper will help ensure we don't lose the new recipes.
-void transform_permanently() {
-  transform_all();
-  Recently_added_recipes.clear();
-}
-
 void parse_int_reagents() {
   trace(9991, "transform") << "--- parsing any uninitialized reagents as integers" << end();
   for (map<recipe_ordinal, recipe>::iterator p = Recipe.begin(); p != Recipe.end(); ++p) {
diff --git a/020run.cc b/020run.cc
index 41154f06..f3891cf6 100644
--- a/020run.cc
+++ b/020run.cc
@@ -136,8 +136,7 @@ inline const vector<instruction>& routine::steps() const {
 // Load .mu Core
 //? Trace_file = "interactive";
 //? START_TRACING_UNTIL_END_OF_SCOPE;
-load_permanently("core.mu");
-transform_permanently();
+load_file_or_directory("core.mu");
 //? DUMP("");
 //? exit(0);
 
@@ -152,14 +151,15 @@ if (argc > 1) {
   // ignore argv past '--'; that's commandline args for 'main'
   while (argc > 0) {
     if (string(*argv) == "--") break;
-    load_permanently(*argv);
+    load_file_or_directory(*argv);
     argv++;
     argc--;
   }
-  transform_permanently();
   if (Run_tests) Recipe.erase(get(Recipe_ordinal, "main"));
   // End Loading .mu Files
 }
+transform_all();
+save_snapshots();
 
 //: Step 3: if we aren't running tests, locate a recipe called 'main' and
 //: start running it.
@@ -209,9 +209,9 @@ void cleanup_main() {
 atexit(cleanup_main);
 
 :(code)
-void load_permanently(string filename) {
+void load_file_or_directory(string filename) {
   if (is_directory(filename)) {
-    load_all_permanently(filename);
+    load_all(filename);
     return;
   }
   ifstream fin(filename.c_str());
@@ -222,9 +222,6 @@ void load_permanently(string filename) {
   trace(9990, "load") << "=== " << filename << end();
   load(fin);
   fin.close();
-  // freeze everything so it doesn't get cleared by tests
-  Recently_added_recipes.clear();
-  // End load_permanently.
 }
 
 bool is_directory(string path) {
@@ -233,13 +230,13 @@ bool is_directory(string path) {
   return info.st_mode & S_IFDIR;
 }
 
-void load_all_permanently(string dir) {
+void load_all(string dir) {
   dirent** files;
   int num_files = scandir(dir.c_str(), &files, NULL, alphasort);
   for (int i = 0; i < num_files; ++i) {
     string curr_file = files[i]->d_name;
     if (isdigit(curr_file.at(0)))
-      load_permanently(dir+'/'+curr_file);
+      load_file_or_directory(dir+'/'+curr_file);
     free(files[i]);
     files[i] = NULL;
   }
diff --git a/030container.cc b/030container.cc
index 4cc0d58e..a29f7171 100644
--- a/030container.cc
+++ b/030container.cc
@@ -416,7 +416,6 @@ void insert_container(const string& command, kind_of_type kind, istream& in) {
   trace(9999, "parse") << "type number: " << get(Type_ordinal, name) << end();
   skip_bracket(in, "'container' must begin with '['");
   type_info& info = get_or_insert(Type, get(Type_ordinal, name));
-  Recently_added_types.push_back(get(Type_ordinal, name));
   info.name = name;
   info.kind = kind;
   while (has_data(in)) {
@@ -475,40 +474,12 @@ def main [
 +mem: storing 34 in location 3
 +mem: storing 35 in location 4
 
-//: ensure types created in one scenario don't leak outside it.
-:(before "End Globals")
-vector<type_ordinal> Recently_added_types;
-:(before "End load_permanently")  //: for non-tests
-Recently_added_types.clear();
+//: ensure scenarios are consistent by always starting them at the same type
+//: number.
 :(before "End Setup")  //: for tests
-for (int i = 0; i < SIZE(Recently_added_types); ++i) {
-  if (!contains_key(Type, Recently_added_types.at(i))) continue;
-  Type_ordinal.erase(get(Type, Recently_added_types.at(i)).name);
-  // todo: why do I explicitly need to provide this?
-  for (int j = 0; j < SIZE(Type.at(Recently_added_types.at(i)).elements); ++j)
-    Type.at(Recently_added_types.at(i)).elements.at(j).clear();
-  Type.erase(Recently_added_types.at(i));
-}
-Recently_added_types.clear();
-// delete recent type references
-// can't rely on Recently_added_types to cleanup Type_ordinal, because of deliberately misbehaving tests with references to undefined types
-map<string, type_ordinal>::iterator p = Type_ordinal.begin();
-while(p != Type_ordinal.end()) {
-  // save current item
-  string name = p->first;
-  type_ordinal t = p->second;
-  // increment iterator
-  ++p;
-  // now delete current item if necessary
-  if (t >= 1000) Type_ordinal.erase(name);
-}
-//: lastly, ensure scenarios are consistent by always starting them at the
-//: same type number.
 Next_type_ordinal = 1000;
 :(before "End Test Run Initialization")
 assert(Next_type_ordinal < 1000);
-:(before "End Setup")
-Next_type_ordinal = 1000;
 
 //:: Allow container definitions anywhere in the codebase, but complain if you
 //:: can't find a definition at the end.
diff --git a/037new.cc b/037new.cc
index d7daa397..feebd58e 100644
--- a/037new.cc
+++ b/037new.cc
@@ -55,6 +55,7 @@ def main [
 +mem: storing 0 in location 3
 
 :(before "End Globals")
+const int Reserved_for_tests = 1000;
 int Memory_allocated_until = Reserved_for_tests;
 int Initial_memory_per_routine = 100000;
 :(before "End Setup")
diff --git a/042name.cc b/042name.cc
index 46e3a816..872b5678 100644
--- a/042name.cc
+++ b/042name.cc
@@ -23,10 +23,15 @@ Transform.push_back(transform_names);  // idempotent
 
 :(before "End Globals")
 map<recipe_ordinal, map<string, int> > Name;
-:(after "Clear Other State For Recently_added_recipes")
-for (int i = 0; i < SIZE(Recently_added_recipes); ++i) {
-  Name.erase(Recently_added_recipes.at(i));
-}
+
+//: the Name map is a global, so save it before tests and reset it for every
+//: test, just to be safe.
+:(before "End Globals")
+map<recipe_ordinal, map<string, int> > Name_snapshot;
+:(before "End save_snapshots")
+Name_snapshot = Name;
+:(before "End restore_snapshots")
+Name = Name_snapshot;
 
 :(code)
 void transform_names(const recipe_ordinal r) {
diff --git a/056static_dispatch.cc b/056static_dispatch.cc
index da97ea80..263dc2bf 100644
--- a/056static_dispatch.cc
+++ b/056static_dispatch.cc
@@ -21,13 +21,13 @@ def test a:number, b:number -> z:number [
 map<string, vector<recipe_ordinal> > Recipe_variants;
 :(before "End One-time Setup")
 put(Recipe_variants, "main", vector<recipe_ordinal>());  // since we manually added main to Recipe_ordinal
-:(before "Clear Other State For Recently_added_recipes")
-for (map<string, vector<recipe_ordinal> >::iterator p = Recipe_variants.begin(); p != Recipe_variants.end(); ++p) {
-  for (int i = 0; i < SIZE(p->second); ++i) {
-    if (find(Recently_added_recipes.begin(), Recently_added_recipes.end(), p->second.at(i)) != Recently_added_recipes.end())
-      p->second.at(i) = -1;  // just leave a ghost
-  }
-}
+
+:(before "End Globals")
+map<string, vector<recipe_ordinal> > Recipe_variants_snapshot;
+:(before "End save_snapshots")
+Recipe_variants_snapshot = Recipe_variants;
+:(before "End restore_snapshots")
+Recipe_variants = Recipe_variants_snapshot;
 
 :(before "End Load Recipe Header(result)")
 // there can only ever be one variant for main
diff --git a/058shape_shifting_recipe.cc b/058shape_shifting_recipe.cc
index 2046c622..f80edb3b 100644
--- a/058shape_shifting_recipe.cc
+++ b/058shape_shifting_recipe.cc
@@ -41,23 +41,6 @@ if (Current_routine->calls.front().running_step_index == 0
 :(before "End Matching Types For Literal(to)")
 if (contains_type_ingredient_name(to)) return false;
 
-//: We'll be creating recipes without loading them from anywhere by
-//: *specializing* existing recipes.
-//:
-//: Keep track of these new recipes in a separate variable in addition to
-//: Recently_added_recipes, so that edit/ can clear them before reloading to
-//: regenerate errors.
-:(before "End Globals")
-vector<recipe_ordinal> Recently_added_shape_shifting_recipes;
-:(before "End Setup")
-Recently_added_shape_shifting_recipes.clear();
-
-//: make sure we don't clear any of these recipes when we start running tests
-:(before "End Loading .mu Files")
-Recently_added_recipes.clear();
-Recently_added_types.clear();
-Recently_added_shape_shifting_recipes.clear();
-
 //: save original name of specialized recipes
 :(before "End recipe Fields")
 string original_name;
@@ -243,8 +226,6 @@ recipe_ordinal new_variant(recipe_ordinal exemplar, const instruction& inst, con
   // make a copy
   assert(contains_key(Recipe, exemplar));
   assert(!contains_key(Recipe, new_recipe_ordinal));
-  Recently_added_recipes.push_back(new_recipe_ordinal);
-  Recently_added_shape_shifting_recipes.push_back(new_recipe_ordinal);
   put(Recipe, new_recipe_ordinal, get(Recipe, exemplar));
   recipe& new_recipe = get(Recipe, new_recipe_ordinal);
   new_recipe.name = new_name;
diff --git a/091run_interactive.cc b/091run_interactive.cc
index 1e6bf9c2..b84d8ca9 100644
--- a/091run_interactive.cc
+++ b/091run_interactive.cc
@@ -62,8 +62,6 @@ bool Track_most_recent_products = false;
 :(before "End Tracing")
 trace_stream* Save_trace_stream = NULL;
 string Save_trace_file;
-vector<recipe_ordinal> Save_recently_added_recipes;
-vector<recipe_ordinal> Save_recently_added_shape_shifting_recipes;
 :(before "End Setup")
 Track_most_recent_products = false;
 :(code)
@@ -79,9 +77,9 @@ bool run_interactive(int address) {
       Memory.erase(i);
   }
   string command = trim(strip_comments(read_mu_string(address)));
-  if (command.empty()) return false;
   Name[get(Recipe_ordinal, "interactive")].clear();
   run_code_begin(/*snapshot_recently_added_recipes*/true);
+  if (command.empty()) return false;
   // don't kill the current routine on parse errors
   routine* save_current_routine = Current_routine;
   Current_routine = NULL;
@@ -108,16 +106,22 @@ bool run_interactive(int address) {
   return true;
 }
 
+//: Carefully update all state to exactly how it was -- including snapshots.
+
+:(before "End Globals")
+map<string, recipe_ordinal> Recipe_ordinal_snapshot_stash;
+map<recipe_ordinal, recipe> Recipe_snapshot_stash;
+map<string, type_ordinal> Type_ordinal_snapshot_stash;
+map<type_ordinal, type_info> Type_snapshot_stash;
+map<recipe_ordinal, map<string, int> > Name_snapshot_stash;
+map<string, vector<recipe_ordinal> > Recipe_variants_snapshot_stash;
+:(code)
 void run_code_begin(bool snapshot_recently_added_recipes) {
   // stuff to undo later, in run_code_end()
   Hide_errors = true;
   Disable_redefine_checks = true;
-  if (snapshot_recently_added_recipes) {
-    Save_recently_added_recipes = Recently_added_recipes;
-    Recently_added_recipes.clear();
-    Save_recently_added_shape_shifting_recipes = Recently_added_shape_shifting_recipes;
-    Recently_added_shape_shifting_recipes.clear();
-  }
+  if (snapshot_recently_added_recipes)
+    stash_snapshots();
   Save_trace_stream = Trace_stream;
   Save_trace_file = Trace_file;
   Trace_file = "";
@@ -134,13 +138,28 @@ void run_code_end() {
   Trace_file = Save_trace_file;
   Save_trace_file.clear();
   Recipe.erase(get(Recipe_ordinal, "interactive"));  // keep past sandboxes from inserting errors
-  if (!Save_recently_added_recipes.empty()) {
-    clear_recently_added_recipes();
-    Recently_added_recipes = Save_recently_added_recipes;
-    Save_recently_added_recipes.clear();
-    Recently_added_shape_shifting_recipes = Save_recently_added_shape_shifting_recipes;
-    Save_recently_added_shape_shifting_recipes.clear();
-  }
+  if (!Recipe_snapshot_stash.empty())
+    unstash_snapshots();
+}
+
+// keep sync'd with save_snapshots and restore_snapshots
+void stash_snapshots() {
+  Recipe_ordinal_snapshot_stash = Recipe_ordinal_snapshot;
+  Recipe_snapshot_stash = Recipe_snapshot;
+  Type_ordinal_snapshot_stash = Type_ordinal_snapshot;
+  Type_snapshot_stash = Type_snapshot;
+  Name_snapshot_stash = Name_snapshot;
+  Recipe_variants_snapshot_stash = Recipe_variants_snapshot;
+  save_snapshots();
+}
+void unstash_snapshots() {
+  restore_snapshots();
+  Recipe_ordinal_snapshot = Recipe_ordinal_snapshot_stash;  Recipe_ordinal_snapshot_stash.clear();
+  Recipe_snapshot = Recipe_snapshot_stash;  Recipe_snapshot_stash.clear();
+  Type_ordinal_snapshot = Type_ordinal_snapshot_stash;  Type_ordinal_snapshot_stash.clear();
+  Type_snapshot = Type_snapshot_stash;  Type_snapshot_stash.clear();
+  Name_snapshot = Name_snapshot_stash;  Name_snapshot_stash.clear();
+  Recipe_variants_snapshot = Recipe_variants_snapshot_stash;  Recipe_variants_snapshot_stash.clear();
 }
 
 :(before "End Load Recipes")
@@ -161,8 +180,6 @@ load(string(
   "$cleanup-run-interactive\n" +
   "return output, errors, screen, stashes, completed?\n" +
 "]\n");
-transform_all();
-Recently_added_recipes.clear();
 
 //: adjust errors in the sandbox
 :(after "string maybe(string s)")
@@ -315,31 +332,6 @@ b:number <- copy 0
 # no errors
 +mem: storing 0 in location 3
 
-:(code)
-void test_run_interactive_cleans_up_any_created_specializations() {
-  // define a generic recipe
-  assert(!contains_key(Recipe_ordinal, "foo"));
-  load("recipe foo x:_elem -> n:number [\n"
-       "  return 34\n"
-       "]\n");
-  assert(SIZE(Recently_added_recipes) == 1);  // foo
-  assert(variant_count("foo") == 1);
-  // run-interactive a call that specializes this recipe
-  run("recipe main [\n"
-       "  1:number/raw <- copy 0\n"
-       "  2:address:shared:array:character <- new [foo 1:number/raw]\n"
-       "  run-interactive 2:address:shared:array:character\n"
-       "]\n");
-  assert(SIZE(Recently_added_recipes) == 2);  // foo, main
-  // check that number of variants doesn't change
-  CHECK_EQ(variant_count("foo"), 1);
-}
-
-int variant_count(string recipe_name) {
-  if (!contains_key(Recipe_variants, recipe_name)) return 0;
-  return non_ghost_size(get(Recipe_variants, recipe_name));
-}
-
 :(before "End Globals")
 string Most_recent_products;
 :(before "End Setup")
@@ -456,25 +448,6 @@ case RELOAD: {
 }
 :(before "End Primitive Recipe Implementations")
 case RELOAD: {
-  // clear any containers in advance
-  for (int i = 0; i < SIZE(Recently_added_types); ++i) {
-    if (!contains_key(Type, Recently_added_types.at(i))) continue;
-    Type_ordinal.erase(get(Type, Recently_added_types.at(i)).name);
-    Type.erase(Recently_added_types.at(i));
-  }
-  for (map<string, vector<recipe_ordinal> >::iterator p = Recipe_variants.begin(); p != Recipe_variants.end(); ++p) {
-    vector<recipe_ordinal>& variants = p->second;
-    for (int i = 0; i < SIZE(p->second); ++i) {
-      if (variants.at(i) == -1) continue;
-      if (find(Recently_added_shape_shifting_recipes.begin(), Recently_added_shape_shifting_recipes.end(), variants.at(i)) != Recently_added_shape_shifting_recipes.end())
-        variants.at(i) = -1;  // ghost
-    }
-  }
-  for (int i = 0; i < SIZE(Recently_added_shape_shifting_recipes); ++i) {
-    Recipe_ordinal.erase(get(Recipe, Recently_added_shape_shifting_recipes.at(i)).name);
-    Recipe.erase(Recently_added_shape_shifting_recipes.at(i));
-  }
-  Recently_added_shape_shifting_recipes.clear();
   string code = read_mu_string(ingredients.at(0).at(0));
   run_code_begin(/*snapshot_recently_added_recipes*/false);
   routine* save_current_routine = Current_routine;
@@ -503,29 +476,3 @@ def main [
   1:number/raw <- copy 34
 ]
 +mem: storing 34 in location 1
-
-:(code)
-void test_reload_cleans_up_any_created_specializations() {
-  // define a generic recipe and a call to it
-  assert(!contains_key(Recipe_ordinal, "foo"));
-  assert(variant_count("foo") == 0);
-  // a call that specializes this recipe
-  run("recipe main [\n"
-      "  local-scope\n"
-      "  x:address:shared:array:character <- new [recipe foo x:_elem -> n:number [\n"
-      "local-scope\n"
-      "load-ingredients\n"
-      "return 34\n"
-      "]\n"
-      "recipe main2 [\n"
-      "local-scope\n"
-      "load-ingredients\n"
-      "x:number <- copy 34\n"
-      "foo x:number\n"
-      "]]\n"
-      "  reload x\n"
-      "]\n");
-  // check that number of variants includes specialization
-  assert(SIZE(Recently_added_recipes) == 4);  // foo, main, main2, foo specialization
-  CHECK_EQ(variant_count("foo"), 2);
-}