From 6e1eeeebfb453fa7c871869c19375ce60fbd7413 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sat, 27 Jul 2019 16:01:55 -0700 Subject: 5485 - promote SubX to top-level --- html/053recipe_header.cc.html | 710 ------------------------------------------ 1 file changed, 710 deletions(-) delete mode 100644 html/053recipe_header.cc.html (limited to 'html/053recipe_header.cc.html') diff --git a/html/053recipe_header.cc.html b/html/053recipe_header.cc.html deleted file mode 100644 index d63ad5f9..00000000 --- a/html/053recipe_header.cc.html +++ /dev/null @@ -1,710 +0,0 @@ - - - - -Mu - 053recipe_header.cc - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/053recipe_header.cc -
-  1 //: Advanced notation for the common/easy case where a recipe takes some fixed
-  2 //: number of ingredients and yields some fixed number of products.
-  3 
-  4 :(scenario recipe_with_header)
-  5 def main [
-  6   1:num/raw <- add2 3, 5
-  7 ]
-  8 def add2 x:num, y:num -> z:num [
-  9   local-scope
- 10   load-ingredients
- 11   z:num <- add x, y
- 12   return z
- 13 ]
- 14 +mem: storing 8 in location 1
- 15 
- 16 //: When loading recipes save any header.
- 17 
- 18 :(before "End recipe Fields")
- 19 bool has_header;
- 20 vector<reagent> ingredients;
- 21 vector<reagent> products;
- 22 :(before "End recipe Constructor")
- 23 has_header = false;
- 24 
- 25 :(before "End Recipe Refinements")
- 26 if (in.peek() != '[') {
- 27   trace("parse") << "recipe has a header; parsing" << end();
- 28   load_recipe_header(in, result);
- 29 }
- 30 
- 31 :(code)
- 32 void load_recipe_header(istream& in, recipe& result) {
- 33   result.has_header = true;
- 34   while (has_data(in) && in.peek() != '[' && in.peek() != '\n') {
- 35     string s = next_word(in);
- 36     if (s.empty()) {
- 37       assert(!has_data(in));
- 38       raise << "incomplete recipe header at end of file (0)\n" << end();
- 39       return;
- 40     }
- 41     if (s == "<-")
- 42       raise << "recipe " << result.name << " should say '->' and not '<-'\n" << end();
- 43     if (s == "->") break;
- 44     result.ingredients.push_back(reagent(s));
- 45     trace("parse") << "header ingredient: " << result.ingredients.back().original_string << end();
- 46     skip_whitespace_but_not_newline(in);
- 47   }
- 48   while (has_data(in) && in.peek() != '[' && in.peek() != '\n') {
- 49     string s = next_word(in);
- 50     if (s.empty()) {
- 51       assert(!has_data(in));
- 52       raise << "incomplete recipe header at end of file (1)\n" << end();
- 53       return;
- 54     }
- 55     result.products.push_back(reagent(s));
- 56     trace("parse") << "header product: " << result.products.back().original_string << end();
- 57     skip_whitespace_but_not_newline(in);
- 58   }
- 59   // End Load Recipe Header(result)
- 60 }
- 61 
- 62 :(scenario recipe_handles_stray_comma)
- 63 def main [
- 64   1:num/raw <- add2 3, 5
- 65 ]
- 66 def add2 x:num, y:num -> z:num, [
- 67   local-scope
- 68   load-ingredients
- 69   z:num <- add x, y
- 70   return z
- 71 ]
- 72 +mem: storing 8 in location 1
- 73 
- 74 :(scenario recipe_handles_stray_comma_2)
- 75 def main [
- 76   foo
- 77 ]
- 78 def foo, [
- 79   1:num/raw <- add 2, 2
- 80 ]
- 81 def bar [
- 82   1:num/raw <- add 2, 3
- 83 ]
- 84 +mem: storing 4 in location 1
- 85 
- 86 :(scenario recipe_handles_wrong_arrow)
- 87 % Hide_errors = true;
- 88 def foo a:num <- b:num [
- 89 ]
- 90 +error: recipe foo should say '->' and not '<-'
- 91 
- 92 :(scenario recipe_handles_missing_bracket)
- 93 % Hide_errors = true;
- 94 def main
- 95 ]
- 96 +error: main: recipe body must begin with '['
- 97 
- 98 :(scenario recipe_handles_missing_bracket_2)
- 99 % Hide_errors = true;
-100 def main
-101   local-scope
-102   {
-103   }
-104 ]
-105 # doesn't overflow line when reading header
-106 -parse: header ingredient: local-scope
-107 +error: main: recipe body must begin with '['
-108 
-109 :(scenario recipe_handles_missing_bracket_3)
-110 % Hide_errors = true;
-111 def main  # comment
-112   local-scope
-113   {
-114   }
-115 ]
-116 # doesn't overflow line when reading header
-117 -parse: header ingredient: local-scope
-118 +error: main: recipe body must begin with '['
-119 
-120 :(after "Begin debug_string(recipe x)")
-121 out << "ingredients:\n";
-122 for (int i = 0;  i < SIZE(x.ingredients);  ++i)
-123   out << "  " << debug_string(x.ingredients.at(i)) << '\n';
-124 out << "products:\n";
-125 for (int i = 0;  i < SIZE(x.products);  ++i)
-126   out << "  " << debug_string(x.products.at(i)) << '\n';
-127 
-128 //: If a recipe never mentions any ingredients or products, assume it has a header.
-129 
-130 :(scenario recipe_without_ingredients_or_products_has_header)
-131 def test [
-132   1:num <- copy 34
-133 ]
-134 +parse: recipe test has a header
-135 
-136 :(before "End Recipe Body(result)")
-137 if (!result.has_header) {
-138   result.has_header = true;
-139   for (int i = 0;  i < SIZE(result.steps);  ++i) {
-140     const instruction& inst = result.steps.at(i);
-141     if ((inst.name == "reply" && !inst.ingredients.empty())
-142         || (inst.name == "return" && !inst.ingredients.empty())
-143         || inst.name == "next-ingredient"
-144         || inst.name == "ingredient"
-145         || inst.name == "rewind-ingredients") {
-146       result.has_header = false;
-147       break;
-148     }
-149   }
-150 }
-151 if (result.has_header) {
-152   trace("parse") << "recipe " << result.name << " has a header" << end();
-153 }
-154 
-155 //: Support type abbreviations in headers.
-156 
-157 :(scenario type_abbreviations_in_recipe_headers)
-158 def main [
-159   local-scope
-160   a:text <- foo
-161   1:char/raw <- index *a, 0
-162 ]
-163 def foo -> a:text [  # 'text' is an abbreviation
-164   local-scope
-165   load-ingredients
-166   a <- new [abc]
-167 ]
-168 +mem: storing 97 in location 1
-169 
-170 :(before "End Expand Type Abbreviations(caller)")
-171 for (long int i = 0;  i < SIZE(caller.ingredients);  ++i)
-172   expand_type_abbreviations(caller.ingredients.at(i).type);
-173 for (long int i = 0;  i < SIZE(caller.products);  ++i)
-174   expand_type_abbreviations(caller.products.at(i).type);
-175 
-176 //: Rewrite 'load-ingredients' to instructions to create all reagents in the header.
-177 
-178 :(before "End Rewrite Instruction(curr, recipe result)")
-179 if (curr.name == "load-ingredients" || curr.name == "load-inputs") {
-180   curr.clear();
-181   recipe_ordinal op = get(Recipe_ordinal, "next-ingredient-without-typechecking");
-182   for (int i = 0;  i < SIZE(result.ingredients);  ++i) {
-183     curr.operation = op;
-184     curr.name = "next-ingredient-without-typechecking";
-185     curr.products.push_back(result.ingredients.at(i));
-186     result.steps.push_back(curr);
-187     curr.clear();
-188   }
-189 }
-190 if (curr.name == "next-ingredient-without-typechecking") {
-191   raise << maybe(result.name) << "never call 'next-ingredient-without-typechecking' directly\n" << end();
-192   curr.clear();
-193 }
-194 
-195 //: internal version of next-ingredient; don't call this directly
-196 :(before "End Primitive Recipe Declarations")
-197 NEXT_INGREDIENT_WITHOUT_TYPECHECKING,
-198 :(before "End Primitive Recipe Numbers")
-199 put(Recipe_ordinal, "next-ingredient-without-typechecking", NEXT_INGREDIENT_WITHOUT_TYPECHECKING);
-200 :(before "End Primitive Recipe Checks")
-201 case NEXT_INGREDIENT_WITHOUT_TYPECHECKING: {
-202   break;
-203 }
-204 :(before "End Primitive Recipe Implementations")
-205 case NEXT_INGREDIENT_WITHOUT_TYPECHECKING: {
-206   assert(!Current_routine->calls.empty());
-207   if (current_call().next_ingredient_to_process < SIZE(current_call().ingredient_atoms)) {
-208     products.push_back(
-209         current_call().ingredient_atoms.at(current_call().next_ingredient_to_process));
-210     assert(SIZE(products) == 1);  products.resize(2);  // push a new vector
-211     products.at(1).push_back(1);
-212     ++current_call().next_ingredient_to_process;
-213   }
-214   else {
-215     products.resize(2);
-216     // pad the first product with sufficient zeros to match its type
-217     products.at(0).resize(size_of(current_instruction().products.at(0)));
-218     products.at(1).push_back(0);
-219   }
-220   break;
-221 }
-222 
-223 //: more useful error messages if someone forgets 'load-ingredients'
-224 
-225 :(scenario load_ingredients_missing_error)
-226 % Hide_errors = true;
-227 def foo a:num [
-228   local-scope
-229   b:num <- add a:num, 1
-230 ]
-231 +error: foo: tried to read ingredient 'a' in 'b:num <- add a:num, 1' but it hasn't been written to yet
-232 +error:   did you forget 'load-ingredients'?
-233 
-234 :(after "use-before-set Error")
-235 if (is_present_in_ingredients(caller, ingredient.name))
-236   raise << "  did you forget 'load-ingredients'?\n" << end();
-237 
-238 :(scenario load_ingredients_missing_error_2)
-239 % Hide_errors = true;
-240 def foo a:num [
-241   local-scope
-242   b:num <- add a, 1
-243 ]
-244 +error: foo: missing type for 'a' in 'b:num <- add a, 1'
-245 +error:   did you forget 'load-ingredients'?
-246 
-247 :(after "missing-type Error 1")
-248 if (is_present_in_ingredients(get(Recipe, get(Recipe_ordinal, recipe_name)), x.name))
-249   raise << "  did you forget 'load-ingredients'?\n" << end();
-250 
-251 :(code)
-252 bool is_present_in_ingredients(const recipe& callee, const string& ingredient_name) {
-253   for (int i = 0;  i < SIZE(callee.ingredients);  ++i) {
-254     if (callee.ingredients.at(i).name == ingredient_name)
-255       return true;
-256   }
-257   return false;
-258 }
-259 
-260 //:: Check all calls against headers.
-261 
-262 :(scenario show_clear_error_on_bad_call)
-263 % Hide_errors = true;
-264 def main [
-265   1:num <- foo 34
-266 ]
-267 def foo x:point -> y:num [
-268   local-scope
-269   load-ingredients
-270   return 35
-271 ]
-272 +error: main: ingredient 0 has the wrong type at '1:num <- foo 34'
-273 
-274 :(scenario show_clear_error_on_bad_call_2)
-275 % Hide_errors = true;
-276 def main [
-277   1:point <- foo 34
-278 ]
-279 def foo x:num -> y:num [
-280   local-scope
-281   load-ingredients
-282   return x
-283 ]
-284 +error: main: product 0 has the wrong type at '1:point <- foo 34'
-285 
-286 :(after "Transform.push_back(check_instruction)")
-287 Transform.push_back(check_calls_against_header);  // idempotent
-288 :(code)
-289 void check_calls_against_header(const recipe_ordinal r) {
-290   const recipe& caller = get(Recipe, r);
-291   trace(9991, "transform") << "--- type-check calls inside recipe " << caller.name << end();
-292   for (int i = 0;  i < SIZE(caller.steps);  ++i) {
-293     const instruction& inst = caller.steps.at(i);
-294     if (is_primitive(inst.operation)) continue;
-295     const recipe& callee = get(Recipe, inst.operation);
-296     if (!callee.has_header) continue;
-297     for (long int i = 0;  i < min(SIZE(inst.ingredients), SIZE(callee.ingredients));  ++i) {
-298       // ingredients coerced from call to callee
-299       if (!types_coercible(callee.ingredients.at(i), inst.ingredients.at(i))) {
-300         raise << maybe(caller.name) << "ingredient " << i << " has the wrong type at '" << to_original_string(inst) << "'\n" << end();
-301         raise << "  ['" << to_string(callee.ingredients.at(i).type) << "' vs '" << to_string(inst.ingredients.at(i).type) << "']\n" << end();
-302       }
-303     }
-304     for (long int i = 0;  i < min(SIZE(inst.products), SIZE(callee.products));  ++i) {
-305       if (is_dummy(inst.products.at(i))) continue;
-306       // products coerced from callee to call
-307       if (!types_coercible(inst.products.at(i), callee.products.at(i))) {
-308         raise << maybe(caller.name) << "product " << i << " has the wrong type at '" << to_original_string(inst) << "'\n" << end();
-309         raise << "  ['" << to_string(inst.products.at(i).type) << "' vs '" << to_string(callee.products.at(i).type) << "']\n" << end();
-310       }
-311     }
-312   }
-313 }
-314 
-315 //:: Check types going in and out of all recipes with headers.
-316 
-317 :(scenarios transform)
-318 :(scenario recipe_headers_are_checked)
-319 % Hide_errors = true;
-320 def add2 x:num, y:num -> z:num [
-321   local-scope
-322   load-ingredients
-323   z:&:num <- copy 0/unsafe
-324   return z
-325 ]
-326 +error: add2: replied with the wrong type at 'return z'
-327 
-328 :(before "End Checks")
-329 Transform.push_back(check_return_instructions_against_header);  // idempotent
-330 
-331 :(code)
-332 void check_return_instructions_against_header(const recipe_ordinal r) {
-333   const recipe& caller_recipe = get(Recipe, r);
-334   if (!caller_recipe.has_header) return;
-335   trace(9991, "transform") << "--- checking return instructions against header for " << caller_recipe.name << end();
-336   for (int i = 0;  i < SIZE(caller_recipe.steps);  ++i) {
-337     const instruction& inst = caller_recipe.steps.at(i);
-338     if (inst.name != "reply" && inst.name != "return") continue;
-339     if (SIZE(caller_recipe.products) != SIZE(inst.ingredients)) {
-340       raise << maybe(caller_recipe.name) << "replied with the wrong number of products at '" << to_original_string(inst) << "'\n" << end();
-341       continue;
-342     }
-343     for (int i = 0;  i < SIZE(caller_recipe.products);  ++i) {
-344       if (!types_match(caller_recipe.products.at(i), inst.ingredients.at(i)))
-345         raise << maybe(caller_recipe.name) << "replied with the wrong type at '" << to_original_string(inst) << "'\n" << end();
-346     }
-347   }
-348 }
-349 
-350 :(scenario recipe_headers_are_checked_2)
-351 % Hide_errors = true;
-352 def add2 x:num, y:num [
-353   local-scope
-354   load-ingredients
-355   z:&:num <- copy 0/unsafe
-356   return z
-357 ]
-358 +error: add2: replied with the wrong number of products at 'return z'
-359 
-360 :(scenario recipe_headers_are_checked_against_pre_transformed_instructions)
-361 % Hide_errors = true;
-362 def foo -> x:num [
-363   local-scope
-364   x:num <- copy 0
-365   z:bool <- copy false
-366   return-if z, z
-367 ]
-368 +error: foo: replied with the wrong type at 'return-if z, z'
-369 
-370 :(scenario recipe_headers_check_for_duplicate_names)
-371 % Hide_errors = true;
-372 def foo x:num, x:num -> z:num [
-373   local-scope
-374   load-ingredients
-375   return z
-376 ]
-377 +error: foo: 'x' can't repeat in the ingredients
-378 
-379 :(scenario recipe_headers_check_for_duplicate_names_2)
-380 % Hide_errors = true;
-381 def foo x:num, x:num [  # no result
-382   local-scope
-383   load-ingredients
-384 ]
-385 +error: foo: 'x' can't repeat in the ingredients
-386 
-387 :(scenario recipe_headers_check_for_missing_types)
-388 % Hide_errors = true;
-389 def main [
-390   foo 0
-391 ]
-392 def foo a [  # no type for 'a'
-393 ]
-394 +error: foo: ingredient 'a' has no type
-395 
-396 :(before "End recipe Fields")
-397 map<string, int> ingredient_index;
-398 
-399 :(after "Begin Instruction Modifying Transforms")
-400 Transform.push_back(check_header_ingredients);  // idempotent
-401 
-402 :(code)
-403 void check_header_ingredients(const recipe_ordinal r) {
-404   recipe& caller_recipe = get(Recipe, r);
-405   caller_recipe.ingredient_index.clear();
-406   trace(9991, "transform") << "--- checking return instructions against header for " << caller_recipe.name << end();
-407   for (int i = 0;  i < SIZE(caller_recipe.ingredients);  ++i) {
-408     if (caller_recipe.ingredients.at(i).type == NULL)
-409       raise << maybe(caller_recipe.name) << "ingredient '" << caller_recipe.ingredients.at(i).name << "' has no type\n" << end();
-410     if (contains_key(caller_recipe.ingredient_index, caller_recipe.ingredients.at(i).name))
-411       raise << maybe(caller_recipe.name) << "'" << caller_recipe.ingredients.at(i).name << "' can't repeat in the ingredients\n" << end();
-412     put(caller_recipe.ingredient_index, caller_recipe.ingredients.at(i).name, i);
-413   }
-414 }
-415 
-416 //: Deduce types from the header if possible.
-417 
-418 :(scenarios run)
-419 :(scenario deduce_instruction_types_from_recipe_header)
-420 def main [
-421   1:num/raw <- add2 3, 5
-422 ]
-423 def add2 x:num, y:num -> z:num [
-424   local-scope
-425   load-ingredients
-426   z <- add x, y  # no type for z
-427   return z
-428 ]
-429 +mem: storing 8 in location 1
-430 
-431 :(after "Begin Type Modifying Transforms")
-432 Transform.push_back(deduce_types_from_header);  // idempotent
-433 
-434 :(code)
-435 void deduce_types_from_header(const recipe_ordinal r) {
-436   recipe& caller_recipe = get(Recipe, r);
-437   if (caller_recipe.products.empty()) return;
-438   trace(9991, "transform") << "--- deduce types from header for " << caller_recipe.name << end();
-439   map<string, const type_tree*> header_type;
-440   for (int i = 0;  i < SIZE(caller_recipe.ingredients);  ++i) {
-441     if (!caller_recipe.ingredients.at(i).type) continue;  // error handled elsewhere
-442     put(header_type, caller_recipe.ingredients.at(i).name, caller_recipe.ingredients.at(i).type);
-443     trace(9993, "transform") << "type of " << caller_recipe.ingredients.at(i).name << " is " << names_to_string(caller_recipe.ingredients.at(i).type) << end();
-444   }
-445   for (int i = 0;  i < SIZE(caller_recipe.products);  ++i) {
-446     if (!caller_recipe.products.at(i).type) continue;  // error handled elsewhere
-447     put(header_type, caller_recipe.products.at(i).name, caller_recipe.products.at(i).type);
-448     trace(9993, "transform") << "type of " << caller_recipe.products.at(i).name << " is " << names_to_string(caller_recipe.products.at(i).type) << end();
-449   }
-450   for (int i = 0;  i < SIZE(caller_recipe.steps);  ++i) {
-451     instruction& inst = caller_recipe.steps.at(i);
-452     trace(9992, "transform") << "instruction: " << to_string(inst) << end();
-453     for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
-454       if (inst.ingredients.at(i).type) continue;
-455       if (header_type.find(inst.ingredients.at(i).name) == header_type.end())
-456         continue;
-457       if (!contains_key(header_type, inst.ingredients.at(i).name)) continue;  // error handled elsewhere
-458       inst.ingredients.at(i).type = new type_tree(*get(header_type, inst.ingredients.at(i).name));
-459       trace(9993, "transform") << "type of " << inst.ingredients.at(i).name << " is " << names_to_string(inst.ingredients.at(i).type) << end();
-460     }
-461     for (int i = 0;  i < SIZE(inst.products);  ++i) {
-462       trace(9993, "transform") << "  product: " << to_string(inst.products.at(i)) << end();
-463       if (inst.products.at(i).type) continue;
-464       if (header_type.find(inst.products.at(i).name) == header_type.end())
-465         continue;
-466       if (!contains_key(header_type, inst.products.at(i).name)) continue;  // error handled elsewhere
-467       inst.products.at(i).type = new type_tree(*get(header_type, inst.products.at(i).name));
-468       trace(9993, "transform") << "type of " << inst.products.at(i).name << " is " << names_to_string(inst.products.at(i).type) << end();
-469     }
-470   }
-471 }
-472 
-473 //: One final convenience: no need to say what to return if the information is
-474 //: in the header.
-475 
-476 :(scenario return_based_on_header)
-477 def main [
-478   1:num/raw <- add2 3, 5
-479 ]
-480 def add2 x:num, y:num -> z:num [
-481   local-scope
-482   load-ingredients
-483   z <- add x, y
-484   return
-485 ]
-486 +mem: storing 8 in location 1
-487 
-488 :(after "Transform.push_back(check_header_ingredients)")
-489 Transform.push_back(fill_in_return_ingredients);  // idempotent
-490 
-491 :(code)
-492 void fill_in_return_ingredients(const recipe_ordinal r) {
-493   recipe& caller_recipe = get(Recipe, r);
-494   trace(9991, "transform") << "--- fill in return ingredients from header for recipe " << caller_recipe.name << end();
-495   if (!caller_recipe.has_header) return;
-496   for (int i = 0;  i < SIZE(caller_recipe.steps);  ++i) {
-497     instruction& inst = caller_recipe.steps.at(i);
-498     if (inst.name == "reply" || inst.name == "return")
-499       add_header_products(inst, caller_recipe);
-500   }
-501   // fall through return
-502   if (!caller_recipe.steps.empty()) {
-503     const instruction& final_instruction = caller_recipe.steps.at(SIZE(caller_recipe.steps)-1);
-504     if (final_instruction.name == "reply" || final_instruction.name == "return")
-505       return;
-506   }
-507   instruction inst;
-508   inst.name = "return";
-509   add_header_products(inst, caller_recipe);
-510   caller_recipe.steps.push_back(inst);
-511 }
-512 
-513 void add_header_products(instruction& inst, const recipe& caller_recipe) {
-514   assert(inst.name == "reply" || inst.name == "return");
-515   // collect any products with the same names as ingredients
-516   for (int i = 0;  i < SIZE(caller_recipe.products);  ++i) {
-517     // if the ingredient is missing, add it from the header
-518     if (SIZE(inst.ingredients) == i)
-519       inst.ingredients.push_back(caller_recipe.products.at(i));
-520     // if it's missing /same_as_ingredient, try to fill it in
-521     if (contains_key(caller_recipe.ingredient_index, caller_recipe.products.at(i).name) && !has_property(inst.ingredients.at(i), "same_as_ingredient")) {
-522       ostringstream same_as_ingredient;
-523       same_as_ingredient << get(caller_recipe.ingredient_index, caller_recipe.products.at(i).name);
-524       inst.ingredients.at(i).properties.push_back(pair<string, string_tree*>("same-as-ingredient", new string_tree(same_as_ingredient.str())));
-525     }
-526   }
-527 }
-528 
-529 :(scenario explicit_return_ignores_header)
-530 def main [
-531   1:num/raw, 2:num/raw <- add2 3, 5
-532 ]
-533 def add2 a:num, b:num -> y:num, z:num [
-534   local-scope
-535   load-ingredients
-536   y <- add a, b
-537   z <- subtract a, b
-538   return a, z
-539 ]
-540 +mem: storing 3 in location 1
-541 +mem: storing -2 in location 2
-542 
-543 :(scenario return_on_fallthrough_based_on_header)
-544 def main [
-545   1:num/raw <- add2 3, 5
-546 ]
-547 def add2 x:num, y:num -> z:num [
-548   local-scope
-549   load-ingredients
-550   z <- add x, y
-551 ]
-552 +transform: instruction: return {z: "number"}
-553 +mem: storing 8 in location 1
-554 
-555 :(scenario return_on_fallthrough_already_exists)
-556 def main [
-557   1:num/raw <- add2 3, 5
-558 ]
-559 def add2 x:num, y:num -> z:num [
-560   local-scope
-561   load-ingredients
-562   z <- add x, y  # no type for z
-563   return z
-564 ]
-565 +transform: instruction: return {z: ()}
-566 -transform: instruction: return z:num
-567 +mem: storing 8 in location 1
-568 
-569 :(scenario return_causes_error_in_empty_recipe)
-570 % Hide_errors = true;
-571 def foo -> x:num [
-572 ]
-573 +error: foo: tried to read ingredient 'x' in 'return x:num' but it hasn't been written to yet
-574 
-575 :(scenario return_after_conditional_return_based_on_header)
-576 def main [
-577   1:num/raw <- add2 3, 5
-578 ]
-579 def add2 x:num, y:num -> z:num [
-580   local-scope
-581   load-ingredients
-582   z <- add x, y  # no type for z
-583   return-if false, 34
-584 ]
-585 +mem: storing 8 in location 1
-586 
-587 :(scenario recipe_headers_perform_same_ingredient_check)
-588 % Hide_errors = true;
-589 def main [
-590   1:num <- copy 34
-591   2:num <- copy 34
-592   3:num <- add2 1:num, 2:num
-593 ]
-594 def add2 x:num, y:num -> x:num [
-595   local-scope
-596   load-ingredients
-597 ]
-598 +error: main: '3:num <- add2 1:num, 2:num' should write to '1:num' rather than '3:num'
-599 
-600 //: One special-case is recipe 'main'. Make sure it's only ever taking in text
-601 //: ingredients, and returning a single number.
-602 
-603 :(scenario recipe_header_ingredients_constrained_for_main)
-604 % Hide_errors = true;
-605 def main x:num [
-606 ]
-607 +error: ingredients of recipe 'main' must all be text (address:array:character)
-608 
-609 :(scenario recipe_header_products_constrained_for_main)
-610 % Hide_errors = true;
-611 def main -> x:text [
-612 ]
-613 +error: recipe 'main' must return at most a single product, a number
-614 
-615 :(scenario recipe_header_products_constrained_for_main_2)
-616 % Hide_errors = true;
-617 def main -> x:num, y:num [
-618 ]
-619 +error: recipe 'main' must return at most a single product, a number
-620 
-621 :(after "Transform.push_back(expand_type_abbreviations)")
-622 Transform.push_back(check_recipe_header_constraints);
-623 :(code)
-624 void check_recipe_header_constraints(const recipe_ordinal r) {
-625   const recipe& caller = get(Recipe, r);
-626   if (caller.name != "main") return;
-627   trace(9992, "transform") << "check recipe header constraints for recipe " << caller.name << end();
-628   if (!caller.has_header) return;
-629   reagent/*local*/ expected_ingredient("x:address:array:character");
-630   for (int i = 0; i < SIZE(caller.ingredients); ++i) {
-631     if (!types_strictly_match(expected_ingredient, caller.ingredients.at(i))) {
-632       raise << "ingredients of recipe 'main' must all be text (address:array:character)\n" << end();
-633       break;
-634     }
-635   }
-636   int nprod = SIZE(caller.products);
-637   reagent/*local*/ expected_product("x:number");
-638   if (nprod > 1
-639       || (nprod == 1 && !types_strictly_match(expected_product, caller.products.at(0)))) {
-640     raise << "recipe 'main' must return at most a single product, a number\n" << end();
-641   }
-642 }
-
- - - -- cgit 1.4.1-2-gfad0