about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2015-05-05 23:50:50 -0700
committerKartik K. Agaram <vc@akkartik.com>2015-05-05 23:50:50 -0700
commit20d1c9057a559ce8db83bbc2787ca91348bcb16f (patch)
tree56381f7cc68f93014d72019301f623b7993c63ef
parent8923d6f658d09de800164b8fc6514475f49307b4 (diff)
downloadmu-20d1c9057a559ce8db83bbc2787ca91348bcb16f.tar.gz
1278 - support before/after tangle directives
No way to only insert code at a label in a specific recipe. Let's see
how that goes.
-rw-r--r--011load.cc49
-rw-r--r--075scenario_keyboard.cc2
-rw-r--r--077tangle.cc198
-rw-r--r--tangle.mu36
4 files changed, 254 insertions, 31 deletions
diff --git a/011load.cc b/011load.cc
index 3f0f8e1e..ae793b59 100644
--- a/011load.cc
+++ b/011load.cc
@@ -24,7 +24,21 @@ vector<recipe_number> load(istream& in) {
     string command = next_word(in);
     // Command Handlers
     if (command == "recipe") {
-      result.push_back(add_recipe(in));
+      string recipe_name = next_word(in);
+      if (recipe_name.empty())
+        raise << "empty recipe name\n";
+      if (Recipe_number.find(recipe_name) == Recipe_number.end()) {
+        Recipe_number[recipe_name] = Next_recipe_number++;
+      }
+      if (Recipe.find(Recipe_number[recipe_name]) != Recipe.end()) {
+        raise << "redefining recipe " << Recipe[Recipe_number[recipe_name]].name << "\n";
+        Recipe.erase(Recipe_number[recipe_name]);
+      }
+      Recipe[Recipe_number[recipe_name]] = slurp_recipe(in);
+      Recipe[Recipe_number[recipe_name]].name = recipe_name;
+      // track added recipes because we may need to undo them in tests; see below
+      recently_added_recipes.push_back(Recipe_number[recipe_name]);
+      result.push_back(Recipe_number[recipe_name]);
     }
     // End Command Handlers
     else {
@@ -34,43 +48,18 @@ vector<recipe_number> load(istream& in) {
   return result;
 }
 
-recipe_number add_recipe(istream& in) {
-  skip_whitespace_and_comments(in);
-  string recipe_name = next_word(in);
-//?   cout << "recipe name: ^" << recipe_name << "$\n"; //? 3
-  if (recipe_name.empty())
-    raise << "empty recipe name\n";
-//?     raise << "empty recipe name in " << in.str() << '\n';
-  if (Recipe_number.find(recipe_name) == Recipe_number.end()) {
-    Recipe_number[recipe_name] = Next_recipe_number++;
-//?     cout << "AAA: " << recipe_name << " is now " << Recipe_number[recipe_name] << '\n'; //? 1
-  }
-  recipe_number r = Recipe_number[recipe_name];
-  if (Recipe.find(r) != Recipe.end()) {
-    raise << "redefining recipe " << Recipe[r].name << "\n";
-    Recipe.erase(r);
-  }
-//?   cout << recipe_name << ": adding recipe " << r << '\n'; //? 3
-
+recipe slurp_recipe(istream& in) {
+  recipe result;
   skip_whitespace(in);
   if (in.get() != '[')
     raise << "recipe body must begin with '['\n";
-
-//?   show_rest_of_stream(in); //? 1
   skip_whitespace_and_comments(in);
-//?   show_rest_of_stream(in); //? 1
-
   instruction curr;
   while (next_instruction(in, &curr)) {
     // End Rewrite Instruction(curr)
-//?     if (!curr.products.empty()) cout << curr.products[0].to_string() << '\n'; //? 1
-    Recipe[r].steps.push_back(curr);
+    result.steps.push_back(curr);
   }
-  Recipe[r].name = recipe_name;
-//?   cout << "recipe " << recipe_name << " has " << Recipe[r].steps.size() << " steps.\n"; //? 1
-  // track added recipes because we may need to undo them in tests; see below
-  recently_added_recipes.push_back(r);
-  return r;
+  return result;
 }
 
 bool next_instruction(istream& in, instruction* curr) {
diff --git a/075scenario_keyboard.cc b/075scenario_keyboard.cc
index d14e4de7..92e5a7a8 100644
--- a/075scenario_keyboard.cc
+++ b/075scenario_keyboard.cc
@@ -42,7 +42,7 @@ if (curr.name == "assume-keyboard") {
   assert(curr.products.empty());
   curr.products.push_back(reagent("keyboard:address"));
   curr.products[0].set_value(KEYBOARD);
-  Recipe[r].steps.push_back(curr);  // hacky that "Rewrite Instruction" is converting to multiple instructions
+  result.steps.push_back(curr);  // hacky that "Rewrite Instruction" is converting to multiple instructions
   // leave second instruction in curr
   curr.clear();
   curr.operation = Recipe_number["init-fake-keyboard"];
diff --git a/077tangle.cc b/077tangle.cc
new file mode 100644
index 00000000..9ec03f68
--- /dev/null
+++ b/077tangle.cc
@@ -0,0 +1,198 @@
+//: Allow code for recipes to be pulled in from multiple places.
+//:
+//: TODO: switch recipe.steps to a more efficient data structure.
+
+:(scenario tangle_before)
+recipe main [
+  1:integer <- copy 0:literal
+  +label1
+  3:integer <- copy 0:literal
+]
+
+before +label1 [
+  2:integer <- copy 0:literal
+]
++mem: storing 0 in location 1
++mem: storing 0 in location 2
++mem: storing 0 in location 3
+# nothing else
+$mem: 3
+
+//: while loading recipes, load before/after fragments
+
+:(before "End Globals")
+map<string /*label*/, recipe> Before_fragments, After_fragments;
+:(before "End Setup")
+Before_fragments.clear();
+After_fragments.clear();
+
+:(before "End Command Handlers")
+else if (command == "before") {
+  string label = next_word(in);
+  recipe tmp = slurp_recipe(in);
+  Before_fragments[label].steps.insert(Before_fragments[label].steps.end(), tmp.steps.begin(), tmp.steps.end());
+}
+else if (command == "after") {
+  string label = next_word(in);
+  recipe tmp = slurp_recipe(in);
+  After_fragments[label].steps.insert(After_fragments[label].steps.begin(), tmp.steps.begin(), tmp.steps.end());
+}
+
+//: after all recipes are loaded, insert fragments at appropriate labels
+
+:(after "int main")
+  Transform.push_back(insert_fragments);
+
+:(code)
+void insert_fragments(const recipe_number r) {
+  // Copy into a new vector because insertions invalidate iterators.
+  // But this way we can't insert into labels created inside before/after.
+  vector<instruction> result;
+  for (index_t i = 0; i < Recipe[r].steps.size(); ++i) {
+    const instruction inst = Recipe[r].steps[i];
+    if (!inst.is_label) {
+      result.push_back(inst);
+      continue;
+    }
+    if (Before_fragments.find(inst.label) != Before_fragments.end()) {
+      result.insert(result.end(), Before_fragments[inst.label].steps.begin(), Before_fragments[inst.label].steps.end());
+    }
+    result.push_back(inst);
+    if (After_fragments.find(inst.label) != After_fragments.end()) {
+      result.insert(result.end(), After_fragments[inst.label].steps.begin(), After_fragments[inst.label].steps.end());
+    }
+  }
+//?   for (index_t i = 0; i < result.size(); ++i) { //? 1
+//?     cout << result[i].to_string() << '\n'; //? 1
+//?   } //? 1
+  Recipe[r].steps.swap(result);
+}
+
+:(scenario tangle_before_and_after)
+recipe main [
+  1:integer <- copy 0:literal
+  +label1
+  4:integer <- copy 0:literal
+]
+before +label1 [
+  2:integer <- copy 0:literal
+]
+after +label1 [
+  3:integer <- copy 0:literal
+]
++mem: storing 0 in location 1
++mem: storing 0 in location 2
+# label1
++mem: storing 0 in location 3
++mem: storing 0 in location 4
+# nothing else
+$mem: 4
+
+:(scenario tangle_keeps_labels_separate)
+recipe main [
+  1:integer <- copy 0:literal
+  +label1
+  +label2
+  6:integer <- copy 0:literal
+]
+before +label1 [
+  2:integer <- copy 0:literal
+]
+after +label1 [
+  3:integer <- copy 0:literal
+]
+before +label2 [
+  4:integer <- copy 0:literal
+]
+after +label2 [
+  5:integer <- copy 0:literal
+]
++mem: storing 0 in location 1
++mem: storing 0 in location 2
+# label1
++mem: storing 0 in location 3
+# 'after' fragments for earlier label always go before 'before' fragments for later label
++mem: storing 0 in location 4
+# label2
++mem: storing 0 in location 5
++mem: storing 0 in location 6
+# nothing else
+$mem: 6
+
+:(scenario tangle_stacks_multiple_fragments)
+recipe main [
+  1:integer <- copy 0:literal
+  +label1
+  6:integer <- copy 0:literal
+]
+before +label1 [
+  2:integer <- copy 0:literal
+]
+after +label1 [
+  3:integer <- copy 0:literal
+]
+before +label1 [
+  4:integer <- copy 0:literal
+]
+after +label1 [
+  5:integer <- copy 0:literal
+]
++mem: storing 0 in location 1
+# 'before' fragments stack in order
++mem: storing 0 in location 2
++mem: storing 0 in location 4
+# label1
+# 'after' fragments stack in reverse order
++mem: storing 0 in location 5
++mem: storing 0 in location 3
++mem: storing 0 in location 6
+# nothing else
+$mem: 6
+
+:(scenario tangle_supports_fragments_with_multiple_instructions)
+recipe main [
+  1:integer <- copy 0:literal
+  +label1
+  6:integer <- copy 0:literal
+]
+before +label1 [
+  2:integer <- copy 0:literal
+  3:integer <- copy 0:literal
+]
+after +label1 [
+  4:integer <- copy 0:literal
+  5:integer <- copy 0:literal
+]
++mem: storing 0 in location 1
++mem: storing 0 in location 2
++mem: storing 0 in location 3
+# label1
++mem: storing 0 in location 4
++mem: storing 0 in location 5
++mem: storing 0 in location 6
+# nothing else
+$mem: 6
+
+:(scenario tangle_tangles_into_all_labels_with_same_name)
+recipe main [
+  1:integer <- copy 0:literal
+  +label1
+  +label1
+  4:integer <- copy 0:literal
+]
+before +label1 [
+  2:integer <- copy 0:literal
+]
+after +label1 [
+  3:integer <- copy 0:literal
+]
++mem: storing 0 in location 1
++mem: storing 0 in location 2
+# label1
++mem: storing 0 in location 3
++mem: storing 0 in location 2
+# label1
++mem: storing 0 in location 3
++mem: storing 0 in location 4
+# nothing else
+$mem: 6
diff --git a/tangle.mu b/tangle.mu
new file mode 100644
index 00000000..db890bc8
--- /dev/null
+++ b/tangle.mu
@@ -0,0 +1,36 @@
+# To demonstrate tangle directives, we'll construct a factorial function with
+# separate base and recursive cases. Compare factorial.mu.
+# This isn't a very realistic example, just a simple demonstration of
+# possibilities.
+
+recipe factorial [
+  default-space:address:array:location <- new location:type, 30:literal
+  n:integer <- next-ingredient
+  {
+    +base-case
+  }
+  +recursive-case
+]
+
+after +base-case [
+  # if n=0 return 1
+  zero?:boolean <- equal n:integer, 0:literal
+  break-unless zero?:boolean
+  reply 1:literal
+]
+
+after +recursive-case [
+  # return n * factorial(n - 1)
+  x:integer <- subtract n:integer, 1:literal
+  subresult:integer <- factorial x:integer
+  result:integer <- multiply subresult:integer, n:integer
+  reply result:integer
+]
+
+recipe main [
+  1:integer <- factorial 5:literal
+  $print [result: ]
+  $print 1:integer
+  $print [
+]
+]