about summary refs log tree commit diff stats
path: root/056recipe_header.cc
diff options
context:
space:
mode:
Diffstat (limited to '056recipe_header.cc')
-rw-r--r--056recipe_header.cc105
1 files changed, 74 insertions, 31 deletions
diff --git a/056recipe_header.cc b/056recipe_header.cc
index 53670931..f59c26a4 100644
--- a/056recipe_header.cc
+++ b/056recipe_header.cc
@@ -134,10 +134,10 @@ recipe add2 x:number, y:number -> z:number [
 +error: add2: replied with the wrong type at 'reply z'
 
 :(after "Transform.push_back(check_types_by_name)")
-Transform.push_back(check_header_products);  // idempotent
+Transform.push_back(check_reply_instructions_against_header);  // idempotent
 
 :(code)
-void check_header_products(const recipe_ordinal r) {
+void check_reply_instructions_against_header(const recipe_ordinal r) {
   const recipe& rr = get(Recipe, r);
   if (rr.products.empty()) return;
   trace(9991, "transform") << "--- checking reply instructions against header for " << rr.name << end();
@@ -154,6 +154,33 @@ void check_header_products(const recipe_ordinal r) {
   }
 }
 
+:(scenario recipe_headers_check_for_duplicate_names)
+% Hide_errors = true;
+recipe add2 x:number, x:number -> z:number [
+  local-scope
+  load-ingredients
+  reply z
+]
++error: add2: x can't repeat in the ingredients
+
+:(before "End recipe Fields")
+map<string, int> ingredient_index;
+
+:(after "Transform.push_back(insert_fragments)")
+Transform.push_back(check_and_update_header_reagents);  // idempotent
+
+:(code)
+void check_and_update_header_reagents(const recipe_ordinal r) {
+  recipe& rr = get(Recipe, r);
+  if (rr.products.empty()) return;
+  trace(9991, "transform") << "--- checking reply instructions against header for " << rr.name << end();
+  for (long long int i = 0; i < SIZE(rr.ingredients); ++i) {
+    if (contains_key(rr.ingredient_index, rr.ingredients.at(i).name))
+      raise_error << maybe(rr.name) << rr.ingredients.at(i).name << " can't repeat in the ingredients\n" << end();
+    put(rr.ingredient_index, rr.ingredients.at(i).name, i);
+  }
+}
+
 //: Deduce types from the header if possible.
 
 :(scenarios run)
@@ -169,7 +196,7 @@ recipe add2 x:number, y:number -> z:number [
 ]
 +mem: storing 8 in location 1
 
-:(before "Transform.push_back(check_header_products)")
+:(before "Transform.push_back(check_reply_instructions_against_header)")
 Transform.push_back(deduce_types_from_header);  // idempotent
 
 :(code)
@@ -236,18 +263,40 @@ recipe add2 x:number, y:number -> z:number [
 ]
 +mem: storing 8 in location 1
 
-:(after "Transform.push_back(insert_fragments)")
-Transform.push_back(fill_in_reply_ingredients);
+:(after "Transform.push_back(check_and_update_header_reagents)")
+Transform.push_back(fill_in_reply_ingredients);  // idempotent
 
 :(code)
 void fill_in_reply_ingredients(recipe_ordinal r) {
-  if (!get(Recipe, r).has_header) return;
-  trace(9991, "transform") << "--- fill in reply ingredients from header for recipe " << get(Recipe, r).name << end();
-  for (long long int i = 0; i < SIZE(get(Recipe, r).steps); ++i) {
-    instruction& inst = get(Recipe, r).steps.at(i);
-    if (inst.name == "reply" && inst.ingredients.empty()) {
-      for (long long int i = 0; i < SIZE(get(Recipe, r).products); ++i)
-        inst.ingredients.push_back(get(Recipe, r).products.at(i));
+  recipe& rr = get(Recipe, r);
+  if (!rr.has_header) return;
+  trace(9991, "transform") << "--- fill in reply ingredients from header for recipe " << rr.name << end();
+  for (long long int i = 0; i < SIZE(rr.steps); ++i) {
+    instruction& inst = rr.steps.at(i);
+    if (inst.name == "reply" && inst.ingredients.empty())
+      add_header_products(inst, rr);
+  }
+  // fall through reply
+  if (rr.steps.at(SIZE(rr.steps)-1).name != "reply") {
+    instruction inst;
+    inst.name = "reply";
+    add_header_products(inst, rr);
+    rr.steps.push_back(inst);
+  }
+}
+
+void add_header_products(instruction& inst, const recipe& rr) {
+  assert(inst.name == "reply");
+  // collect any products with the same names as ingredients
+  for (long long int i = 0; i < SIZE(rr.products); ++i) {
+    // if the ingredient is missing, add it from the header
+    if (SIZE(inst.ingredients) == i)
+      inst.ingredients.push_back(rr.products.at(i));
+    // if it's missing /same_as_ingredient, try to fill it in
+    if (contains_key(rr.ingredient_index, rr.products.at(i).name) && !has_property(inst.ingredients.at(i), "same_as_ingredient")) {
+      ostringstream same_as_ingredient;
+      same_as_ingredient << get(rr.ingredient_index, rr.products.at(i).name);
+      inst.ingredients.at(i).properties.push_back(pair<string, string_tree*>("same-as-ingredient", new string_tree(same_as_ingredient.str())));
     }
   }
 }
@@ -278,25 +327,6 @@ recipe add2 x:number, y:number -> z:number [
 +transform: instruction: reply z:number
 +mem: storing 8 in location 1
 
-:(after "Transform.push_back(insert_fragments)")
-Transform.push_back(deduce_fallthrough_reply);
-
-:(code)
-void deduce_fallthrough_reply(const recipe_ordinal r) {
-  recipe& rr = get(Recipe, r);
-  if (rr.products.empty()) return;
-  if (rr.steps.empty()) return;
-  if (rr.steps.at(SIZE(rr.steps)-1).name != "reply") {
-    instruction inst;
-    inst.operation = REPLY;
-    inst.name = "reply";
-    for (long long int i = 0; i < SIZE(rr.products); ++i) {
-      inst.ingredients.push_back(rr.products.at(i));
-    }
-    rr.steps.push_back(inst);
-  }
-}
-
 :(scenario reply_on_fallthrough_already_exists)
 recipe main [
   1:number/raw <- add2 3, 5
@@ -310,3 +340,16 @@ recipe add2 x:number, y:number -> z:number [
 +transform: instruction: reply z
 -transform: instruction: reply z:number
 +mem: storing 8 in location 1
+
+:(scenario recipe_headers_perform_same_ingredient_check)
+% Hide_errors = true;
+recipe main [
+  1:number <- copy 34
+  2:number <- copy 34
+  3:number <- add2 1:number, 2:number
+]
+recipe add2 x:number, y:number -> x:number [
+  local-scope
+  load-ingredients
+]
++error: main: '3:number <- add2 1:number, 2:number' should write to 1:number rather than 3:number