1 //: So far we've been calling a fixed recipe in each instruction, but we'd
  2 //: also like to make the recipe a variable, pass recipes to "higher-order"
  3 //: recipes, return recipes from recipes and so on.
  4 
  5 :(scenario call_literal_recipe)
  6 def main [
  7   1:num <- call f, 34
  8 ]
  9 def f x:num -> y:num [
 10   local-scope
 11   load-ingredients
 12   y <- copy x
 13 ]
 14 +mem: storing 34 in location 1
 15 
 16 :(before "End Mu Types Initialization")
 17 put(Type_ordinal, "recipe-literal", 0);
 18 // 'recipe' variables can store recipe-literal
 19 type_ordinal recipe = put(Type_ordinal, "recipe", Next_type_ordinal++);
 20 get_or_insert(Type, recipe).name = "recipe";
 21 
 22 :(after "Deduce Missing Type(x, caller)")
 23 if (!x.type)
 24   try_initialize_recipe_literal(x, caller);
 25 :(before "Type Check in Type-ingredient-aware check_or_set_types_by_name")
 26 if (!x.type)
 27   try_initialize_recipe_literal(x, variant);
 28 :(code)
 29 void try_initialize_recipe_literal(reagent& x, const recipe& caller) {
 30   if (x.type) return;
 31   if (!contains_key(Recipe_ordinal, x.name)) return;
 32   if (contains_reagent_with_non_recipe_literal_type(caller, x.name)) return;
 33   x.type = new type_tree("recipe-literal");
 34   x.set_value(get(Recipe_ordinal, x.name));
 35 }
 36 bool contains_reagent_with_non_recipe_literal_type(const recipe& caller, const string& name) {
 37   for (int i = 0;  i < SIZE(caller.steps);  ++i) {
 38     const instruction& inst = caller.steps.at(i);
 39     for (int i = 0;  i < SIZE(inst.ingredients);  ++i)
 40       if (is_matching_non_recipe_literal(inst.ingredients.at(i), name)) return true;
 41     for (int i = 0pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
c{0: 0 (((1 string-address)) <- ((new)) abcd) -- nil
c{0: 1 (((2 string-address)) <- ((new)) bc) -- nil
c{0: 2 (((3 integer)) <- ((find-substring)) ((1 string-address)) ((2 string-address)) ((1 literal))) -- nil
c{1: 0 ✓ (((1 string-address)) <- ((new)) abcd)
c{1: 1 ✓ (((2 string-address)) <- ((new)) bc)
c{1: 2 ✓ (((3 integer)) <- ((find-substring)) ((1 string-address)) ((2 string-address)) ((1 literal)))
cn0: convert-names in main
cn0: (((1 string-address)) <- ((new)) abcd) nil nil
cn0: checking arg abcd
cn0: checking oarg ((1 string-address))
maybe-add: ((1 string-address))
cn0: (((2 string-address)) <- ((new)) bc) nil nil
cn0: checking arg bc
cn0: checking oarg ((2 string-address))
maybe-add: ((2 string-address))
cn0: (((3 integer)) <- ((find-substring)) ((1 string-address)) ((2 string-address)) ((1 literal))) nil nil
cn0: checking arg ((1 string-address))
maybe-add: ((1 string-address))
cn0: checking arg ((2 string-address))
maybe-add: ((2 string-address))
cn0: checking arg ((1 literal))
cn0: checking oarg ((3 integer))
maybe-add: ((3 integer))
cn1: (((1 string-address)) <- ((new)) abcd)
cn1: (((2 string-address)) <- ((new)) bc)
cn1: (((3 integer)) <- ((find-substring)) ((1 string-address)) ((2 string-address)) ((1 literal)))
schedule: main
run: main 0: (((1 string-address)) <- ((new)) abcd)
run: main 0: 1000 => ((1 string-address))
mem: ((1 string-address)): 1 <= 1000
run: main 1: (((2 string-address)) <- ((new)) bc)
run: main 1: 1005 => ((2 string-address))
mem: ((2 string-address)): 2 <= 1005
run: main 2: (((3 integer)) <- ((find-substring)) ((1 string-address)) ((2 string-address)) ((1 literal)))
mem: ((1 string-address)) => 1000
mem: ((2 string-address)) => 1005
run: find-substring/main 0: (((default-space space-address)) <- ((new)) ((space literal)) ((30 literal)))
run: find-substring/main 0: 1008 => ((default-space space-address))
run: find-substring/main 1: (((1 string-address)) <- ((next-input)))
arg: nil 0 (1000 1005 1)
run: find-substring/main 1: 1000 => ((1 string-address))
mem: ((1 string-address)): 1010 <= 1000
run: find-substring/main 2: (((2 string-address)) <- ((next-input)))
arg: nil 1 (1000 1005 1)
run: find-substring/main 2: 1005 => ((2 string-address))
mem: ((2 string-address)): 1011 <= 1005
run: find-substring/main 3: (((3 integer)) <- ((next-input)))
arg: nil 2 (1000 1005 1)
run: find-substring/main 3: 1 => ((3 integer))
mem: ((3 integer)): 1012 <= 1
run: find-substring/main 4: (((4 character)) <- ((index)) ((2 string-address) (deref)) ((0 literal)))
array-len: ((1005 string) (raw))
mem: ((1005 integer) (raw)) => 2
mem: ((1006 byte) (raw)) => b
run: find-substring/main 4: #\b => ((4 character))
mem: ((4 character)): 1013 <= #\b
run: find-substring/main 5: (((5 integer)) <- ((length)) ((1 string-address) (deref)))
array-len: ((1 string-address) (deref))
mem: ((1000 integer) (raw)) => 4
run: find-substring/main 5: 4 => ((5 integer))
mem: ((5 integer)): 1014 <= 4
run: find-substring/main 6: (((6 boolean)) <- ((greater-or-equal)) ((3 integer)) ((5 integer)))
mem: ((3 integer)) => 1
mem: ((5 integer)) => 4
run: find-substring/main 6: nil => ((6 boolean))
mem: ((6 boolean)): 1015 <= nil
run: find-substring/main 7: (((jump-if)) ((6 boolean)) ((5 offset)))
mem: ((6 boolean)) => nil
run: find-substring/main 8: (((7 boolean)) <- ((match-at)) ((1 string-address)) ((2 string-address)) ((3 integer)))
mem: ((1 string-address)) => 1000
mem: ((2 string-address)) => 1005
mem: ((3 integer)) => 1
run: match-at/find-substring/main 0: (((default-space space-address)) <- ((new)) ((space literal)) ((30 literal)))
run: match-at/find-substring/main 0: 1039 => ((default-space space-address))
run: match-at/find-substring/main 1: (((1 string-address)) <- ((next-input)))
arg: nil 0 (1000 1005 1)
run: match-at/find-substring/main 1: 1000 => ((1 string-address))
mem: ((1 string-address)): 1041 <= 1000
run: match-at/find-substring/main 2: (((2 string-address)) <- ((next-input)))
arg: nil 1 (1000 1005 1)
run: match-at/find-substring/main 2: 1005 => ((2 string-address))
mem: ((2 string-address)): 1042 <= 1005
run: match-at/find-substring/main 3: (((3 integer)) <- ((next-input)))
arg: nil 2 (1000 1005 1)
run: match-at/find-substring/main 3: 1 => ((3 integer))
mem: ((3 integer)): 1043 <= 1
run: match-at/find-substring/main 4: (((4 integer)) <- ((length)) ((2 string-address) (deref)))
array-len: ((2 string-address) (deref))
mem: ((1005 integer) (raw)) => 2
run: match-at/find-substring/main 4: 2 => ((4 integer))
mem: ((4 integer)): 1044 <= 2
run: match-at/find-substring/main 5: (((5 integer)) <- ((length)) ((1 string-address) (deref)))
array-len: ((1 string-address) (deref))
mem: ((1000 integer) (raw)) => 4
run: match-at/find-substring/main 5: 4 => ((5 integer))
mem: ((5 integer)): 1045 <= 4
run: match-at/find-substring/main 6: (((5 integer)) <- ((subtract)) ((5 integer)) ((4 integer)))
mem: ((5 integer)) => 4
mem: ((4 integer)) => 2
run: match-at/find-substring/main 6: 2 => ((5 integer))
mem: ((5 integer)): 1045 <= 2
run: match-at/find-substring/main 7: (((6 boolean)) <- ((lesser-or-equal)) ((3 integer)) ((5 integer)))
mem: ((3 integer)) => 1
mem: ((5 integer)) => 2
run: match-at/find-substring/main 7: t => ((6 boolean))
mem: ((6 boolean)): 1046 <= t
run: match-at/find-substring/main 8: (((jump-if)) ((6 boolean)) ((1 offset)))
mem: ((6 boolean)) => t
run: match-at/find-substring/main 10: (((7 integer)) <- ((copy)) ((0 literal)))
run: match-at/find-substring/main 10: 0 => ((7 integer))
mem: ((7 integer)): 1047 <= 0
run: match-at/find-substring/main 11: (((8 boolean)) <- ((greater-or-equal)) ((7 integer)) ((4 integer)))
mem: ((7 integer)) => 0
mem: ((4 integer)) => 2
run: match-at/find-substring/main 11: nil => ((8 boolean))
mem: ((8 boolean)): 1048 <= nil
run: match-at/find-substring/main 12: (((jump-if)) ((8 boolean)) ((8 offset)))
mem: ((8 boolean)) => nil
run: match-at/find-substring/main 13: (((9 character)) <- ((index)) ((1 string-address) (deref)) ((3 integer)))
mem: ((3 integer)) => 1
array-len: ((1000 string) (raw))
mem: ((1000 integer) (raw)) => 4
mem: ((1002 byte) (raw)) => b
run: match-at/find-substring/main 13: #\b => ((9 character))
mem: ((9 character)): 1049 <= #\b
run: match-at/find-substring/main 14: (((10 character)) <- ((index)) ((2 string-address) (deref)) ((7 integer)))
mem: ((7 integer)) => 0
array-len: ((1005 string) (raw))
mem: ((1005 integer) (raw)) => 2
mem: ((1006 byte) (raw)) => b
run: match-at/find-substring/main 14: #\b => ((10 character))
mem: ((10 character)): 1050 <= #\b
run: match-at/find-substring/main 15: (((11 boolean)) <- ((equal)) ((9 character)) ((10 character)))
mem: ((9 character)) => b
mem: ((10 character)) => b
run: match-at/find-substring/main 15: t => ((11 boolean))
mem: ((11 boolean)): 1051 <= t
run: match-at/find-substring/main 16: (((jump-if)) ((11 boolean)) ((1 offset)))
mem: ((11 boolean)) => t
run: match-at/find-substring/main 18: (((3 integer)) <- ((add)) ((3 integer)) ((1 literal)))
mem: ((3 integer)) => 1
run: match-at/find-substring/main 18: 2 => ((3 integer))
mem: ((3 integer)): 1043 <= 2
run: match-at/find-substring/main 19: (((7 integer)) <- ((add)) ((7 integer)) ((1 literal)))
mem: ((7 integer)) => 0
run: match-at/find-substring/main 19: 1 => ((7 integer))
mem: ((7 integer)): 1047 <= 1
run: match-at/find-substring/main 20: (((jump)) ((-10 offset)))
run: match-at/find-substring/main 11: (((8 boolean)) <- ((greater-or-equal)) ((7 integer)) ((4 integer)))
mem: ((7 integer)) => 1
mem: ((4 integer)) => 2
run: match-at/find-substring/main 11: nil => ((8 boolean))
mem: ((8 boolean)): 1048 <= nil
run: match-at/find-substring/main 12: (((jump-if)) ((8 boolean)) ((8 offset)))
mem: ((8 boolean)) => nil
run: match-at/find-substring/main 13: (((9 character)) <- ((index)) ((1 string-address) (deref)) ((3 integer)))
mem: ((3 integer)) => 2
array-len: ((1000 string) (raw))
mem: ((1000 integer) (raw)) => 4
mem: ((1003 byte) (raw)) => c
run: match-at/find-substring/main 13: #\c => ((9 character))
mem: ((9 character)): 1049 <= #\c
run: match-at/find-substring/main 14: (((10 character)) <- ((index)) ((2 string-address) (deref)) ((7 integer)))
mem: ((7 integer)) => 1
array-len: ((1005 string) (raw))
mem: ((1005 integer) (raw)) => 2
mem: ((1007 byte) (raw)) => c
run: match-at/find-substring/main 14: #\c => ((10 character))
mem: ((10 character)): 1050 <= #\c
run: match-at/find-substring/main 15: (((11 boolean)) <- ((equal)) ((9 character)) ((10 character)))
mem: ((9 character)) => c
mem: ((10 character)) => c
run: match-at/find-substring/main 15: t => ((11 boolean))
mem: ((11 boolean)): 1051 <= t
run: match-at/find-substring/main 16: (((jump-if)) ((11 boolean)) ((1 offset)))
mem: ((11 boolean)) => t
run: match-at/find-substring/main 18: (((3 integer)) <- ((add)) ((3 integer)) ((1 literal)))
mem: ((3 integer)) => 2
run: match-at/find-substring/main 18: 3 => ((3 integer))
mem: ((3 integer)): 1043 <= 3
run: match-at/find-substring/main 19: (((7 integer)) <- ((add)) ((7 integer)) ((1 literal)))
mem: ((7 integer)) => 1
run: match-at/find-substring/main 19: 2 => ((7 integer))
mem: ((7 integer)): 1047 <= 2
run: match-at/find-substring/main 20: (((jump)) ((-10 offset)))
run: match-at/find-substring/main 11: (((8 boolean)) <- ((greater-or-equal)) ((7 integer)) ((4 integer)))
mem: ((7 integer)) => 2
mem: ((4 integer)) => 2
run: match-at/find-substring/main 11: t => ((8 boolean))
mem: ((8 boolean)): 1048 <= t
run: match-at/find-substring/main 12: (((jump-if)) ((8 boolean)) ((8 offset)))
mem: ((8 boolean)) => t
run: match-at/find-substring/main 21: (((reply)) ((t literal)))
run: find-substring/main 8: t => ((7 boolean))
mem: ((7 boolean)): 1016 <= t
run: find-substring/main 9: (((jump-if)) ((7 boolean)) ((3 offset)))
mem: ((7 boolean)) => t
run: find-substring/main 13: (((reply)) ((3 integer)))
mem: ((3 integer)) => 1
run: main 2: 1 => ((3 integer))
mem: ((3 integer)): 3 <= 1
schedule: done with routine nil
neNr">136 y <- copy x 137 ] 138 +mem: storing 34 in location 1 139 140 :(scenario call_shape_shifting_recipe_inside_shape_shifting_recipe) 141 def main [ 142 1:num <- f 34 143 ] 144 def f x:_elem -> y:_elem [ 145 local-scope 146 load-ingredients 147 y <- call g x 148 ] 149 def g x:_elem -> y:_elem [ 150 local-scope 151 load-ingredients 152 y <- copy x 153 ] 154 +mem: storing 34 in location 1 155 156 :(scenario call_shape_shifting_recipe_repeatedly_inside_shape_shifting_recipe) 157 def main [ 158 1:num <- f 34 159 ] 160 def f x:_elem -> y:_elem [ 161 local-scope 162 load-ingredients 163 y <- call g x 164 y <- call g x 165 ] 166 def g x:_elem -> y:_elem [ 167 local-scope 168 load-ingredients 169 y <- copy x 170 ] 171 +mem: storing 34 in location 1 172 173 //:: check types for 'call' instructions 174 175 :(scenario call_check_literal_recipe) 176 % Hide_errors = true; 177 def main [ 178 1:num <- call f, 34 179 ] 180 def f x:point -> y:point [ 181 local-scope 182 load-ingredients 183 y <- copy x 184 ] 185 +error: main: ingredient 0 has the wrong type at '1:num <- call f, 34' 186 +error: main: product 0 has the wrong type at '1:num <- call f, 34' 187 188 :(scenario call_check_variable_recipe) 189 % Hide_errors = true; 190 def main [ 191 {1: (recipe point -> point)} <- copy f 192 2:num <- call {1: (recipe point -> point)}, 34 193 ] 194 def f x:point -> y:point [ 195 local-scope 196 load-ingredients 197 y <- copy x 198 ] 199 +error: main: ingredient 0 has the wrong type at '2:num <- call {1: (recipe point -> point)}, 34' 200 +error: main: product 0 has the wrong type at '2:num <- call {1: (recipe point -> point)}, 34' 201 202 :(before "End resolve_ambiguous_call(r, index, inst, caller_recipe) Special-cases") 203 if (inst.name == "call" && !inst.ingredients.empty() && is_recipe_literal(inst.ingredients.at(0))) { 204 resolve_indirect_ambiguous_call(r, index, inst, caller_recipe); 205 return; 206 } 207 :(code) 208 bool is_recipe_literal(const reagent& x) { 209 return x.type && x.type->atom && x.type->name == "recipe-literal"; 210 } 211 void resolve_indirect_ambiguous_call(const recipe_ordinal r, int index, instruction& inst, const recipe& caller_recipe) { 212 instruction inst2; 213 inst2.name = inst.ingredients.at(0).name; 214 for (int i = /*skip recipe*/1; i < SIZE(inst.ingredients); ++i) 215 inst2.ingredients.push_back(inst.ingredients.at(i)); 216 for (int i = 0; i < SIZE(inst.products); ++i) 217 inst2.products.push_back(inst.products.at(i)); 218 resolve_ambiguous_call(r, index, inst2, caller_recipe); 219 inst.ingredients.at(0).name = inst2.name; 220 inst.ingredients.at(0).set_value(get(Recipe_ordinal, inst2.name)); 221 } 222 223 :(after "Transform.push_back(check_instruction)") 224 Transform.push_back(check_indirect_calls_against_header); // idempotent 225 :(code) 226 void check_indirect_calls_against_header(const recipe_ordinal r) { 227 trace(9991, "transform") << "--- type-check 'call' instructions inside recipe " << get(Recipe, r).name << end(); 228 const recipe& caller = get(Recipe, r); 229 for (int i = 0; i < SIZE(caller.steps); ++i) { 230 const instruction& inst = caller.steps.at(i); 231 if (!is_indirect_call(inst.operation)) continue; 232 if (inst.ingredients.empty()) continue; // error raised above 233 const reagent& callee = inst.ingredients.at(0); 234 if (!is_mu_recipe(callee)) continue; // error raised above 235 const recipe callee_header = is_literal(callee) ? get(Recipe, callee.value) : from_reagent(inst.ingredients.at(0)); 236 if (!callee_header.has_header) continue; 237 if (is_indirect_call_with_ingredients(inst.operation)) { 238 for (long int i = /*skip callee*/1; i < min(SIZE(inst.ingredients), SIZE(callee_header.ingredients)+/*skip callee*/1); ++i) { 239 if (!types_coercible(callee_header.ingredients.at(i-/*skip callee*/1), inst.ingredients.at(i))) 240 raise << maybe(caller.name) << "ingredient " << i-/*skip callee*/1 << " has the wrong type at '" << to_original_string(inst) << "'\n" << end(); 241 } 242 } 243 if (is_indirect_call_with_products(inst.operation)) { 244 for (long int i = 0; i < min(SIZE(inst.products), SIZE(callee_header.products)); ++i) { 245 if (is_dummy(inst.products.at(i))) continue; 246 if (!types_coercible(callee_header.products.at(i), inst.products.at(i))) 247 raise << maybe(caller.name) << "product " << i << " has the wrong type at '" << to_original_string(inst) << "'\n" << end(); 248 } 249 } 250 } 251 } 252 253 bool is_indirect_call(const recipe_ordinal r) { 254 return is_indirect_call_with_ingredients(r) || is_indirect_call_with_products(r); 255 } 256 257 bool is_indirect_call_with_ingredients(const recipe_ordinal r) { 258 if (r == CALL) return true; 259 // End is_indirect_call_with_ingredients Special-cases 260 return false; 261 } 262 bool is_indirect_call_with_products(const recipe_ordinal r) { 263 if (r == CALL) return true; 264 // End is_indirect_call_with_products Special-cases 265 return false; 266 } 267 268 recipe from_reagent(const reagent& r) { 269 assert(r.type); 270 recipe result_header; // will contain only ingredients and products, nothing else 271 result_header.has_header = true; 272 // Begin Reagent->Recipe(r, recipe_header) 273 if (r.type->atom) { 274 assert(r.type->name == "recipe"); 275 return result_header; 276 } 277 const type_tree* root_type = r.type->atom ? r.type : r.type->left; 278 assert(root_type->atom); 279 assert(root_type->name == "recipe"); 280 const type_tree* curr = r.type->right; 281 for (/*nada*/; curr && !curr->atom; curr = curr->right) { 282 if (curr->left->atom && curr->left->name == "->") { 283 curr = curr->right; // skip delimiter 284 goto read_products; 285 } 286 result_header.ingredients.push_back(next_recipe_reagent(curr->left)); 287 } 288 if (curr) { 289 assert(curr->atom); 290 result_header.ingredients.push_back(next_recipe_reagent(curr)); 291 return result_header; // no products 292 } 293 read_products: 294 for (/*nada*/; curr && !curr->atom; curr = curr->right) 295 result_header.products.push_back(next_recipe_reagent(curr->left)); 296 if (curr) { 297 assert(curr->atom); 298 result_header.products.push_back(next_recipe_reagent(curr)); 299 } 300 return result_header; 301 } 302 303 :(before "End Unit Tests") 304 void test_from_reagent_atomic() { 305 reagent a("{f: recipe}"); 306 recipe r_header = from_reagent(a); 307 CHECK(r_header.ingredients.empty()); 308 CHECK(r_header.products.empty()); 309 } 310 void test_from_reagent_non_atomic() { 311 reagent a("{f: (recipe number -> number)}"); 312 recipe r_header = from_reagent(a); 313 CHECK_EQ(SIZE(r_header.ingredients), 1); 314 CHECK_EQ(SIZE(r_header.products), 1); 315 } 316 void test_from_reagent_reads_ingredient_at_end() { 317 reagent a("{f: (recipe number number)}"); 318 recipe r_header = from_reagent(a); 319 CHECK_EQ(SIZE(r_header.ingredients), 2); 320 CHECK(r_header.products.empty()); 321 } 322 void test_from_reagent_reads_sole_ingredient_at_end() { 323 reagent a("{f: (recipe number)}"); 324 recipe r_header = from_reagent(a); 325 CHECK_EQ(SIZE(r_header.ingredients), 1); 326 CHECK(r_header.products.empty()); 327 } 328 329 :(code) 330 reagent next_recipe_reagent(const type_tree* curr) { 331 if (!curr->left) return reagent("recipe:"+curr->name); 332 return reagent(new type_tree(*curr)); 333 } 334 335 bool is_mu_recipe(const reagent& r) { 336 if (!r.type) return false; 337 if (r.type->atom) { 338 // End is_mu_recipe Atom Cases(r) 339 return r.type->name == "recipe-literal"; 340 } 341 return r.type->left->atom && r.type->left->name == "recipe"; 342 } 343 344 :(scenario copy_typecheck_recipe_variable) 345 % Hide_errors = true; 346 def main [ 347 3:num <- copy 34 # abc def 348 {1: (recipe number -> number)} <- copy f # store literal in a matching variable 349 {2: (recipe boolean -> boolean)} <- copy {1: (recipe number -> number)} # mismatch between recipe variables 350 ] 351 def f x:num -> y:num [ 352 local-scope 353 load-ingredients 354 y <- copy x 355 ] 356 +error: main: can't copy '{1: (recipe number -> number)}' to '{2: (recipe boolean -> boolean)}'; types don't match 357 358 :(scenario copy_typecheck_recipe_variable_2) 359 % Hide_errors = true; 360 def main [ 361 {1: (recipe number -> number)} <- copy f # mismatch with a recipe literal 362 ] 363 def f x:bool -> y:bool [ 364 local-scope 365 load-ingredients 366 y <- copy x 367 ] 368 +error: main: can't copy 'f' to '{1: (recipe number -> number)}'; types don't match 369 370 :(before "End Matching Types For Literal(to)") 371 if (is_mu_recipe(to)) { 372 if (!contains_key(Recipe, from.value)) { 373 raise << "trying to store recipe " << from.name << " into " << to_string(to) << " but there's no such recipe\n" << end(); 374 return false; 375 } 376 const recipe& rrhs = get(Recipe, from.value); 377 const recipe& rlhs = from_reagent(to); 378 for (long int i = 0; i < min(SIZE(rlhs.ingredients), SIZE(rrhs.ingredients)); ++i) { 379 if (!types_match(rlhs.ingredients.at(i), rrhs.ingredients.at(i))) 380 return false; 381 } 382 for (long int i = 0; i < min(SIZE(rlhs.products), SIZE(rrhs.products)); ++i) { 383 if (!types_match(rlhs.products.at(i), rrhs.products.at(i))) 384 return false; 385 } 386 return true; 387 } 388 389 :(scenario call_variable_compound_ingredient) 390 def main [ 391 {1: (recipe (address number) -> number)} <- copy f 392 2:&:num <- copy null 393 3:num <- call {1: (recipe (address number) -> number)}, 2:&:num 394 ] 395 def f x:&:num -> y:num [ 396 local-scope 397 load-ingredients 398 y <- deaddress x 399 ] 400 $error: 0 401 402 //: make sure we don't accidentally break on a recipe literal 403 :(scenario jump_forbidden_on_recipe_literals) 404 % Hide_errors = true; 405 def foo [ 406 local-scope 407 ] 408 def main [ 409 local-scope 410 { 411 break-if foo 412 } 413 ] 414 # error should be as if foo is not a recipe 415 +error: main: missing type for 'foo' in 'break-if foo' 416 417 :(before "End JUMP_IF Checks") 418 check_for_recipe_literals(inst, get(Recipe, r)); 419 :(before "End JUMP_UNLESS Checks") 420 check_for_recipe_literals(inst, get(Recipe, r)); 421 :(code) 422 void check_for_recipe_literals(const instruction& inst, const recipe& caller) { 423 for (int i = 0; i < SIZE(inst.ingredients); ++i) { 424 if (is_mu_recipe(inst.ingredients.at(i))) { 425 raise << maybe(caller.name) << "missing type for '" << inst.ingredients.at(i).original_string << "' in '" << to_original_string(inst) << "'\n" << end(); 426 if (is_present_in_ingredients(caller, inst.ingredients.at(i).name)) 427 raise << " did you forget 'load-ingredients'?\n" << end(); 428 } 429 } 430 } 431 432 :(scenario load_ingredients_missing_error_3) 433 % Hide_errors = true; 434 def foo {f: (recipe num -> num)} [ 435 local-scope 436 b:num <- call f, 1 437 ] 438 +error: foo: missing type for 'f' in 'b:num <- call f, 1' 439 +error: did you forget 'load-ingredients'? 440 441 :(before "End Mu Types Initialization") 442 put(Type_abbreviations, "function", new_type_tree("recipe")); 443 put(Type_abbreviations, "fn", new_type_tree("recipe")); 444 445 //: copying functions to variables 446 447 :(scenario copy_recipe_to_variable) 448 def main [ 449 {1: (fn number -> number)} <- copy f 450 2:num <- call {1: (function number -> number)}, 34 451 ] 452 def f x:num -> y:num [ 453 local-scope 454 load-ingredients 455 y <- copy x 456 ] 457 +mem: storing 34 in location 2 458 459 :(scenario copy_overloaded_recipe_to_variable) 460 def main [ 461 local-scope 462 {x: (fn num -> num)} <- copy f 463 1:num/raw <- call x, 34 464 ] 465 # variant f 466 def f x:bool -> y:bool [ 467 local-scope 468 load-ingredients 469 y <- copy x 470 ] 471 # variant f_2 472 def f x:num -> y:num [ 473 local-scope 474 load-ingredients 475 y <- copy x 476 ] 477 # x contains f_2 478 +mem: storing 34 in location 1 479 480 :(before "End resolve_ambiguous_call(r, index, inst, caller_recipe) Special-cases") 481 if (inst.name == "copy") { 482 for (int i = 0; i < SIZE(inst.ingredients); ++i) { 483 if (!is_recipe_literal(inst.ingredients.at(i))) continue; 484 if (non_ghost_size(get_or_insert(Recipe_variants, inst.ingredients.at(i).name)) < 1) continue; 485 // potentially overloaded recipe 486 string new_name = resolve_ambiguous_call(inst.ingredients.at(i).name, inst.products.at(i), r, index, caller_recipe); 487 if (new_name == "") continue; 488 inst.ingredients.at(i).name = new_name; 489 inst.ingredients.at(i).value = get(Recipe_ordinal, new_name); 490 } 491 return; 492 } 493 :(code) 494 string resolve_ambiguous_call(const string& recipe_name, const reagent& call_types, const recipe_ordinal r, int index, const recipe& caller_recipe) { 495 instruction inst; 496 inst.name = recipe_name; 497 if (!is_mu_recipe(call_types)) return ""; // error raised elsewhere 498 if (is_recipe_literal(call_types)) return ""; // error raised elsewhere 499 construct_fake_call(call_types, inst); 500 resolve_ambiguous_call(r, index, inst, caller_recipe); 501 return inst.name; 502 } 503 void construct_fake_call(const reagent& recipe_var, instruction& out) { 504 assert(recipe_var.type->left->name == "recipe"); 505 type_tree* stem = NULL; 506 for (stem = recipe_var.type->right; stem && stem->left->name != "->"; stem = stem->right) 507 out.ingredients.push_back(copy(stem->left)); 508 if (stem == NULL) return; 509 for (/*skip '->'*/stem = stem->right; stem; stem = stem->right) 510 out.products.push_back(copy(stem->left)); 511 } 512 513 :(scenario copy_shape_shifting_recipe_to_variable) 514 def main [ 515 local-scope 516 {x: (fn num -> num)} <- copy f 517 1:num/raw <- call x, 34 518 ] 519 def f x:_elem -> y:_elem [ 520 local-scope 521 load-inputs 522 y <- copy x 523 ] 524 +mem: storing 34 in location 1 525 526 //: passing function literals to (higher-order) functions 527 528 :(scenario pass_overloaded_recipe_literal_to_ingredient) 529 # like copy_overloaded_recipe_to_variable except we bind 'x' in the course of 530 # a 'call' rather than 'copy' 531 def main [ 532 1:num <- g f 533 ] 534 def g {x: (fn num -> num)} -> result:num [ 535 local-scope 536 load-ingredients 537 result <- call x, 34 538 ] 539 # variant f 540 def f x:bool -> y:bool [ 541 local-scope 542 load-ingredients 543 y <- copy x 544 ] 545 # variant f_2 546 def f x:num -> y:num [ 547 local-scope 548 load-ingredients 549 y <- copy x 550 ] 551 # x contains f_2 552 +mem: storing 34 in location 1 553 554 :(after "End resolve_ambiguous_call(r, index, inst, caller_recipe) Special-cases") 555 for (int i = 0; i < SIZE(inst.ingredients); ++i) { 556 if (!is_mu_recipe(inst.ingredients.at(i))) continue; 557 if (non_ghost_size(get_or_insert(Recipe_variants, inst.ingredients.at(i).name)) < 1) continue; 558 if (get(Recipe_ordinal, inst.name) < MAX_PRIMITIVE_RECIPES) continue; 559 if (non_ghost_size(get_or_insert(Recipe_variants, inst.name)) > 1) { 560 raise << maybe(caller_recipe.name) << "sorry, we're not yet smart enough to simultaneously guess which overloads you want for '" << inst.name << "' and '" << inst.ingredients.at(i).name << "'\n" << end(); 561 return; 562 } 563 const recipe& callee = get(Recipe, get(Recipe_ordinal, inst.name)); 564 if (!callee.has_header) { 565 raise << maybe(caller_recipe.name) << "sorry, we're not yet smart enough to guess which variant of '" << inst.ingredients.at(i).name << "' you want, when the caller '" << inst.name << "' doesn't have a header\n" << end(); 566 return; 567 } 568 string new_name = resolve_ambiguous_call(inst.ingredients.at(i).name, callee.ingredients.at(i), r, index, caller_recipe); 569 if (new_name != "") { 570 inst.ingredients.at(i).name = new_name; 571 inst.ingredients.at(i).value = get(Recipe_ordinal, new_name); 572 } 573 } 574 575 :(scenario return_overloaded_recipe_literal_to_caller) 576 def main [ 577 local-scope 578 {x: (fn num -> num)} <- g 579 1:num/raw <- call x, 34 580 ] 581 def g -> {x: (fn num -> num)} [ 582 local-scope 583 return f 584 ] 585 # variant f 586 def f x:bool -> y:bool [ 587 local-scope 588 load-ingredients 589 y <- copy x 590 ] 591 # variant f_2 592 def f x:num -> y:num [ 593 local-scope 594 load-ingredients 595 y <- copy x 596 ] 597 # x contains f_2 598 +mem: storing 34 in location 1 599 600 :(before "End resolve_ambiguous_call(r, index, inst, caller_recipe) Special-cases") 601 if (inst.name == "return" || inst.name == "reply") { 602 for (int i = 0; i < SIZE(inst.ingredients); ++i) { 603 if (!is_recipe_literal(inst.ingredients.at(i))) continue; 604 if (non_ghost_size(get_or_insert(Recipe_variants, inst.ingredients.at(i).name)) < 1) continue; 605 // potentially overloaded recipe 606 if (!caller_recipe.has_header) { 607 raise << maybe(caller_recipe.name) << "sorry, we're not yet smart enough to guess which variant of '" << inst.ingredients.at(i).name << "' you want, without a recipe header\n" << end(); 608 return; 609 } 610 string new_name = resolve_ambiguous_call(inst.ingredients.at(i).name, caller_recipe.products.at(i), r, index, caller_recipe); 611 if (new_name == "") continue; 612 inst.ingredients.at(i).name = new_name; 613 inst.ingredients.at(i).value = get(Recipe_ordinal, new_name); 614 } 615 return; 616 }