2015-10-31 17:54:48 +00:00
|
|
|
//:: Like container definitions, recipes too can contain type parameters.
|
|
|
|
|
2015-11-11 04:24:03 +00:00
|
|
|
:(scenario shape_shifting_recipe)
|
2015-10-31 17:54:48 +00:00
|
|
|
recipe main [
|
|
|
|
10:point <- merge 14, 15
|
|
|
|
11:point <- foo 10:point
|
|
|
|
]
|
|
|
|
# non-matching variant
|
|
|
|
recipe foo a:number -> result:number [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
result <- copy 34
|
|
|
|
]
|
2015-11-11 04:24:03 +00:00
|
|
|
# matching shape-shifting variant
|
2015-10-31 17:54:48 +00:00
|
|
|
recipe foo a:_t -> result:_t [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
result <- copy a
|
|
|
|
]
|
|
|
|
+mem: storing 14 in location 11
|
|
|
|
+mem: storing 15 in location 12
|
|
|
|
|
2015-11-15 06:59:09 +00:00
|
|
|
//: Before anything else, disable transforms for shape-shifting recipes and
|
|
|
|
//: make sure we never try to actually run a shape-shifting recipe. We should
|
|
|
|
//: be rewriting such instructions to *specializations* with the type
|
|
|
|
//: ingredients filled in.
|
2015-11-05 07:44:46 +00:00
|
|
|
|
2015-11-08 06:56:06 +00:00
|
|
|
:(before "End Transform Checks")
|
|
|
|
if (any_type_ingredient_in_header(/*recipe_ordinal*/p->first)) continue;
|
2015-11-05 07:44:46 +00:00
|
|
|
|
2015-11-15 06:59:09 +00:00
|
|
|
:(after "Running One Instruction")
|
|
|
|
if (Current_routine->calls.front().running_step_index == 0
|
|
|
|
&& any_type_ingredient_in_header(Current_routine->calls.front().running_recipe)) {
|
2015-11-15 20:35:59 +00:00
|
|
|
//? DUMP("");
|
2015-11-15 06:59:09 +00:00
|
|
|
raise_error << "ran into unspecialized shape-shifting recipe " << current_recipe_name() << '\n' << end();
|
|
|
|
}
|
|
|
|
|
2015-11-15 08:37:29 +00:00
|
|
|
//: Make sure we don't match up literals with type ingredients without
|
|
|
|
//: specialization.
|
|
|
|
:(before "End valid_type_for_literal Special-cases")
|
2015-11-15 09:02:53 +00:00
|
|
|
if (contains_type_ingredient_name(lhs)) return false;
|
2015-11-15 08:37:29 +00:00
|
|
|
|
2015-11-07 21:33:26 +00:00
|
|
|
//: We'll be creating recipes without loading them from anywhere by
|
2015-11-20 07:44:11 +00:00
|
|
|
//: *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")
|
|
|
|
//? cerr << "setup: clearing recently-added shape-shifting recipes\n";
|
|
|
|
recently_added_shape_shifting_recipes.clear();
|
|
|
|
|
|
|
|
//: make sure we don't clear any of these recipes when we start running tests
|
2015-11-07 21:33:26 +00:00
|
|
|
:(before "End Loading .mu Files")
|
|
|
|
recently_added_recipes.clear();
|
|
|
|
recently_added_types.clear();
|
2015-11-20 07:44:11 +00:00
|
|
|
//? cerr << "clearing recently-added shape-shifting recipes\n";
|
|
|
|
recently_added_shape_shifting_recipes.clear();
|
2015-11-07 21:33:26 +00:00
|
|
|
|
2015-10-31 17:54:48 +00:00
|
|
|
:(before "End Instruction Dispatch(inst, best_score)")
|
|
|
|
if (best_score == -1) {
|
|
|
|
trace(9992, "transform") << "no variant found; searching for variant with suitable type ingredients" << end();
|
2015-11-11 04:24:03 +00:00
|
|
|
recipe_ordinal exemplar = pick_matching_shape_shifting_variant(variants, inst, best_score);
|
2015-10-31 17:54:48 +00:00
|
|
|
if (exemplar) {
|
2015-11-06 19:06:58 +00:00
|
|
|
trace(9992, "transform") << "found variant to specialize: " << exemplar << ' ' << get(Recipe, exemplar).name << end();
|
2015-11-11 03:56:41 +00:00
|
|
|
variants.push_back(new_variant(exemplar, inst, caller_recipe));
|
2015-11-15 20:35:59 +00:00
|
|
|
//? cerr << "-- replacing " << inst.name << " with " << get(Recipe, variants.back()).name << '\n' << debug_string(get(Recipe, variants.back()));
|
2015-11-06 19:06:58 +00:00
|
|
|
inst.name = get(Recipe, variants.back()).name;
|
2015-10-31 17:54:48 +00:00
|
|
|
trace(9992, "transform") << "new specialization: " << inst.name << end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
:(code)
|
2015-11-11 04:24:03 +00:00
|
|
|
recipe_ordinal pick_matching_shape_shifting_variant(vector<recipe_ordinal>& variants, const instruction& inst, long long int& best_score) {
|
2015-11-15 20:35:59 +00:00
|
|
|
//? cerr << "---- " << inst.name << ": " << non_ghost_size(variants) << '\n';
|
2015-10-31 17:54:48 +00:00
|
|
|
recipe_ordinal result = 0;
|
|
|
|
for (long long int i = 0; i < SIZE(variants); ++i) {
|
2015-11-07 20:41:08 +00:00
|
|
|
if (variants.at(i) == -1) continue; // ghost from a previous test
|
2015-11-15 20:35:59 +00:00
|
|
|
//? cerr << "-- variant " << i << "\n" << debug_string(get(Recipe, variants.at(i)));
|
2015-11-11 04:24:03 +00:00
|
|
|
trace(9992, "transform") << "checking shape-shifting variant " << i << end();
|
|
|
|
long long int current_score = shape_shifting_variant_score(inst, variants.at(i));
|
2015-10-31 17:54:48 +00:00
|
|
|
trace(9992, "transform") << "final score: " << current_score << end();
|
2015-11-15 20:35:59 +00:00
|
|
|
//? cerr << get(Recipe, variants.at(i)).name << ": " << current_score << '\n';
|
2015-10-31 17:54:48 +00:00
|
|
|
if (current_score > best_score) {
|
|
|
|
trace(9992, "transform") << "matches" << end();
|
|
|
|
result = variants.at(i);
|
|
|
|
best_score = current_score;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-11-11 04:24:03 +00:00
|
|
|
long long int shape_shifting_variant_score(const instruction& inst, recipe_ordinal variant) {
|
2015-11-15 20:35:59 +00:00
|
|
|
//? cerr << "======== " << inst.to_string() << '\n';
|
2015-10-31 17:54:48 +00:00
|
|
|
if (!any_type_ingredient_in_header(variant)) {
|
2015-11-02 03:42:49 +00:00
|
|
|
trace(9993, "transform") << "no type ingredients" << end();
|
2015-10-31 17:54:48 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2015-11-06 19:06:58 +00:00
|
|
|
const vector<reagent>& header_ingredients = get(Recipe, variant).ingredients;
|
2015-10-31 17:54:48 +00:00
|
|
|
if (SIZE(inst.ingredients) < SIZE(header_ingredients)) {
|
|
|
|
trace(9993, "transform") << "too few ingredients" << end();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
for (long long int i = 0; i < SIZE(header_ingredients); ++i) {
|
2015-11-15 20:35:59 +00:00
|
|
|
if (!deeply_equal_concrete_types(header_ingredients.at(i), inst.ingredients.at(i))) {
|
2015-10-31 17:54:48 +00:00
|
|
|
trace(9993, "transform") << "mismatch: ingredient " << i << end();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2015-11-06 19:06:58 +00:00
|
|
|
if (SIZE(inst.products) > SIZE(get(Recipe, variant).products)) {
|
2015-10-31 17:54:48 +00:00
|
|
|
trace(9993, "transform") << "too few products" << end();
|
|
|
|
return -1;
|
|
|
|
}
|
2015-11-06 19:06:58 +00:00
|
|
|
const vector<reagent>& header_products = get(Recipe, variant).products;
|
2015-10-31 17:54:48 +00:00
|
|
|
for (long long int i = 0; i < SIZE(inst.products); ++i) {
|
2015-11-15 20:35:59 +00:00
|
|
|
if (!deeply_equal_concrete_types(header_products.at(i), inst.products.at(i))) {
|
2015-10-31 17:54:48 +00:00
|
|
|
trace(9993, "transform") << "mismatch: product " << i << end();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// the greater the number of unused ingredients, the lower the score
|
2015-11-06 19:06:58 +00:00
|
|
|
return 100 - (SIZE(get(Recipe, variant).products)-SIZE(inst.products))
|
|
|
|
- (SIZE(inst.ingredients)-SIZE(get(Recipe, variant).ingredients)); // ok to go negative
|
2015-10-31 17:54:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool any_type_ingredient_in_header(recipe_ordinal variant) {
|
2015-11-06 19:06:58 +00:00
|
|
|
for (long long int i = 0; i < SIZE(get(Recipe, variant).ingredients); ++i) {
|
|
|
|
if (contains_type_ingredient_name(get(Recipe, variant).ingredients.at(i)))
|
2015-10-31 17:54:48 +00:00
|
|
|
return true;
|
|
|
|
}
|
2015-11-10 05:31:21 +00:00
|
|
|
for (long long int i = 0; i < SIZE(get(Recipe, variant).products); ++i) {
|
|
|
|
if (contains_type_ingredient_name(get(Recipe, variant).products.at(i)))
|
|
|
|
return true;
|
|
|
|
}
|
2015-10-31 17:54:48 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-11-15 20:35:59 +00:00
|
|
|
bool deeply_equal_concrete_types(reagent lhs, reagent rhs) {
|
|
|
|
canonize_type(lhs);
|
|
|
|
canonize_type(rhs);
|
|
|
|
return deeply_equal_concrete_types(lhs.properties.at(0).second, rhs.properties.at(0).second, rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool deeply_equal_concrete_types(const string_tree* lhs, const string_tree* rhs, const reagent& rhs_reagent) {
|
|
|
|
if (!lhs) return !rhs;
|
|
|
|
if (!rhs) return !lhs;
|
|
|
|
if (is_type_ingredient_name(lhs->value)) return true; // type ingredient matches anything
|
|
|
|
if (Literal_type_names.find(lhs->value) != Literal_type_names.end())
|
|
|
|
return Literal_type_names.find(rhs->value) != Literal_type_names.end();
|
|
|
|
if (rhs->value == "literal" && lhs->value == "address")
|
|
|
|
return rhs_reagent.name == "0";
|
|
|
|
//? cerr << lhs->value << " vs " << rhs->value << '\n';
|
|
|
|
return lhs->value == rhs->value
|
|
|
|
&& deeply_equal_concrete_types(lhs->left, rhs->left, rhs_reagent)
|
|
|
|
&& deeply_equal_concrete_types(lhs->right, rhs->right, rhs_reagent);
|
2015-10-31 17:54:48 +00:00
|
|
|
}
|
|
|
|
|
2015-11-02 03:42:49 +00:00
|
|
|
bool contains_type_ingredient_name(const reagent& x) {
|
|
|
|
return contains_type_ingredient_name(x.properties.at(0).second);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool contains_type_ingredient_name(const string_tree* type) {
|
|
|
|
if (!type) return false;
|
|
|
|
if (is_type_ingredient_name(type->value)) return true;
|
|
|
|
return contains_type_ingredient_name(type->left) || contains_type_ingredient_name(type->right);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_type_ingredient_name(const string& type) {
|
|
|
|
return !type.empty() && type.at(0) == '_';
|
|
|
|
}
|
|
|
|
|
2015-11-11 03:56:41 +00:00
|
|
|
recipe_ordinal new_variant(recipe_ordinal exemplar, const instruction& inst, const recipe& caller_recipe) {
|
2015-10-31 17:54:48 +00:00
|
|
|
string new_name = next_unused_recipe_name(inst.name);
|
2015-11-02 03:42:49 +00:00
|
|
|
trace(9993, "transform") << "switching " << inst.name << " to " << new_name << end();
|
2015-11-06 21:22:16 +00:00
|
|
|
assert(!contains_key(Recipe_ordinal, new_name));
|
2015-11-08 06:56:06 +00:00
|
|
|
recipe_ordinal new_recipe_ordinal = put(Recipe_ordinal, new_name, Next_recipe_ordinal++);
|
2015-10-31 17:54:48 +00:00
|
|
|
// make a copy
|
2015-11-06 21:22:16 +00:00
|
|
|
assert(contains_key(Recipe, exemplar));
|
2015-11-08 06:56:06 +00:00
|
|
|
assert(!contains_key(Recipe, new_recipe_ordinal));
|
|
|
|
recently_added_recipes.push_back(new_recipe_ordinal);
|
2015-11-20 07:44:11 +00:00
|
|
|
recently_added_shape_shifting_recipes.push_back(new_recipe_ordinal);
|
2015-11-08 06:56:06 +00:00
|
|
|
put(Recipe, new_recipe_ordinal, get(Recipe, exemplar));
|
|
|
|
recipe& new_recipe = get(Recipe, new_recipe_ordinal);
|
|
|
|
// Since the exemplar never ran any transforms, we have to redo some of the
|
|
|
|
// work of the check_types_by_name transform while supporting type-ingredients.
|
|
|
|
compute_type_names(new_recipe);
|
|
|
|
// that gives enough information to replace type-ingredients with concrete types
|
2015-11-10 07:02:23 +00:00
|
|
|
{
|
|
|
|
map<string, const string_tree*> mappings;
|
2015-11-11 03:56:41 +00:00
|
|
|
bool error = false;
|
|
|
|
compute_type_ingredient_mappings(get(Recipe, exemplar), inst, mappings, caller_recipe, &error);
|
|
|
|
if (!error) replace_type_ingredients(new_recipe, mappings);
|
2015-11-10 07:02:23 +00:00
|
|
|
for (map<string, const string_tree*>::iterator p = mappings.begin(); p != mappings.end(); ++p)
|
|
|
|
delete p->second;
|
2015-11-11 03:56:41 +00:00
|
|
|
if (error) return exemplar;
|
2015-11-10 07:02:23 +00:00
|
|
|
}
|
2015-11-15 09:16:51 +00:00
|
|
|
ensure_all_concrete_types(new_recipe, get(Recipe, exemplar));
|
2015-11-17 18:47:11 +00:00
|
|
|
// update the name after specialization is complete (so earlier error messages look better)
|
|
|
|
new_recipe.name = new_name;
|
|
|
|
// perform all transforms on the new specialization
|
2015-11-08 06:56:06 +00:00
|
|
|
for (long long int t = 0; t < SIZE(Transform); ++t) {
|
|
|
|
(*Transform.at(t))(new_recipe_ordinal);
|
|
|
|
}
|
|
|
|
new_recipe.transformed_until = SIZE(Transform)-1;
|
|
|
|
return new_recipe_ordinal;
|
|
|
|
}
|
|
|
|
|
|
|
|
void compute_type_names(recipe& variant) {
|
2015-11-08 21:58:40 +00:00
|
|
|
trace(9993, "transform") << "compute type names: " << variant.name << end();
|
2015-11-08 06:56:06 +00:00
|
|
|
map<string, string_tree*> type_names;
|
2015-11-09 07:36:37 +00:00
|
|
|
for (long long int i = 0; i < SIZE(variant.ingredients); ++i)
|
2015-11-17 18:47:11 +00:00
|
|
|
save_or_deduce_type_name(variant.ingredients.at(i), type_names, variant);
|
2015-11-09 07:36:37 +00:00
|
|
|
for (long long int i = 0; i < SIZE(variant.products); ++i)
|
2015-11-17 18:47:11 +00:00
|
|
|
save_or_deduce_type_name(variant.products.at(i), type_names, variant);
|
2015-11-08 06:56:06 +00:00
|
|
|
for (long long int i = 0; i < SIZE(variant.steps); ++i) {
|
|
|
|
instruction& inst = variant.steps.at(i);
|
2015-11-08 21:58:40 +00:00
|
|
|
trace(9993, "transform") << " instruction: " << inst.to_string() << end();
|
2015-11-09 07:36:37 +00:00
|
|
|
for (long long int in = 0; in < SIZE(inst.ingredients); ++in)
|
2015-11-17 18:47:11 +00:00
|
|
|
save_or_deduce_type_name(inst.ingredients.at(in), type_names, variant);
|
2015-11-09 07:36:37 +00:00
|
|
|
for (long long int out = 0; out < SIZE(inst.products); ++out)
|
2015-11-17 18:47:11 +00:00
|
|
|
save_or_deduce_type_name(inst.products.at(out), type_names, variant);
|
2015-11-08 06:56:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-17 18:47:11 +00:00
|
|
|
void save_or_deduce_type_name(reagent& x, map<string, string_tree*>& type_name, const recipe& variant) {
|
2015-11-08 22:46:48 +00:00
|
|
|
trace(9994, "transform") << " checking " << x.to_string() << ": " << debug_string(x.properties.at(0).second) << end();
|
2015-11-08 06:56:06 +00:00
|
|
|
if (!x.properties.at(0).second && contains_key(type_name, x.name)) {
|
|
|
|
x.properties.at(0).second = new string_tree(*get(type_name, x.name));
|
2015-11-08 22:46:48 +00:00
|
|
|
trace(9994, "transform") << " deducing type to " << debug_string(x.properties.at(0).second) << end();
|
2015-11-08 06:56:06 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!x.properties.at(0).second) {
|
2015-11-17 18:47:11 +00:00
|
|
|
raise_error << maybe(variant.name) << "unknown type for " << x.original_string << " (check the name for typos)\n" << end();
|
2015-11-08 06:56:06 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (contains_key(type_name, x.name)) return;
|
|
|
|
if (x.properties.at(0).second->value == "offset" || x.properties.at(0).second->value == "variant") return; // special-case for container-access instructions
|
|
|
|
put(type_name, x.name, x.properties.at(0).second);
|
2015-11-08 22:46:48 +00:00
|
|
|
trace(9993, "transform") << "type of " << x.name << " is " << debug_string(x.properties.at(0).second) << end();
|
2015-11-02 03:42:49 +00:00
|
|
|
}
|
|
|
|
|
2015-11-11 03:56:41 +00:00
|
|
|
void compute_type_ingredient_mappings(const recipe& exemplar, const instruction& inst, map<string, const string_tree*>& mappings, const recipe& caller_recipe, bool* error) {
|
2015-11-15 08:37:29 +00:00
|
|
|
long long int limit = min(SIZE(inst.ingredients), SIZE(exemplar.ingredients));
|
|
|
|
for (long long int i = 0; i < limit; ++i) {
|
2015-11-11 04:14:50 +00:00
|
|
|
const reagent& exemplar_reagent = exemplar.ingredients.at(i);
|
2015-11-07 21:33:26 +00:00
|
|
|
reagent ingredient = inst.ingredients.at(i);
|
2015-11-08 06:56:06 +00:00
|
|
|
assert(ingredient.properties.at(0).second);
|
2015-11-07 21:33:26 +00:00
|
|
|
canonize_type(ingredient);
|
2015-11-15 09:02:53 +00:00
|
|
|
if (is_mu_address(exemplar_reagent) && ingredient.name == "0") continue; // assume it matches
|
2015-11-11 04:14:50 +00:00
|
|
|
accumulate_type_ingredients(exemplar_reagent, ingredient, mappings, exemplar, inst, caller_recipe, error);
|
2015-11-02 03:42:49 +00:00
|
|
|
}
|
2015-11-15 08:37:29 +00:00
|
|
|
limit = min(SIZE(inst.products), SIZE(exemplar.products));
|
|
|
|
for (long long int i = 0; i < limit; ++i) {
|
2015-11-11 04:14:50 +00:00
|
|
|
const reagent& exemplar_reagent = exemplar.products.at(i);
|
2015-11-07 21:33:26 +00:00
|
|
|
reagent product = inst.products.at(i);
|
2015-11-08 06:56:06 +00:00
|
|
|
assert(product.properties.at(0).second);
|
2015-11-07 21:33:26 +00:00
|
|
|
canonize_type(product);
|
2015-11-11 04:14:50 +00:00
|
|
|
accumulate_type_ingredients(exemplar_reagent, product, mappings, exemplar, inst, caller_recipe, error);
|
2015-11-02 03:42:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-15 08:37:29 +00:00
|
|
|
inline long long int min(long long int a, long long int b) {
|
|
|
|
return (a < b) ? a : b;
|
|
|
|
}
|
|
|
|
|
2015-11-11 04:14:50 +00:00
|
|
|
void accumulate_type_ingredients(const reagent& exemplar_reagent, reagent& refinement, map<string, const string_tree*>& mappings, const recipe& exemplar, const instruction& call_instruction, const recipe& caller_recipe, bool* error) {
|
2015-11-05 08:36:02 +00:00
|
|
|
assert(refinement.properties.at(0).second);
|
2015-11-11 04:14:50 +00:00
|
|
|
accumulate_type_ingredients(exemplar_reagent.properties.at(0).second, refinement.properties.at(0).second, mappings, exemplar, exemplar_reagent, call_instruction, caller_recipe, error);
|
2015-11-02 03:42:49 +00:00
|
|
|
}
|
|
|
|
|
2015-11-11 04:14:50 +00:00
|
|
|
void accumulate_type_ingredients(const string_tree* exemplar_type, const string_tree* refinement_type, map<string, const string_tree*>& mappings, const recipe& exemplar, const reagent& exemplar_reagent, const instruction& call_instruction, const recipe& caller_recipe, bool* error) {
|
|
|
|
if (!exemplar_type) return;
|
|
|
|
if (!refinement_type) {
|
2015-11-16 01:34:13 +00:00
|
|
|
// todo: make this smarter; only warn if exemplar_type contains some *new* type ingredient
|
2015-11-11 04:14:50 +00:00
|
|
|
raise_error << maybe(exemplar.name) << "missing type ingredient in " << exemplar_reagent.original_string << '\n' << end();
|
2015-11-05 08:57:23 +00:00
|
|
|
return;
|
2015-11-05 08:36:02 +00:00
|
|
|
}
|
2015-11-11 04:14:50 +00:00
|
|
|
if (!exemplar_type->value.empty() && exemplar_type->value.at(0) == '_') {
|
|
|
|
assert(!refinement_type->value.empty());
|
|
|
|
if (exemplar_type->right) {
|
2015-11-08 08:10:56 +00:00
|
|
|
raise_error << "type_ingredients in non-last position not currently supported\n" << end();
|
|
|
|
return;
|
|
|
|
}
|
2015-11-11 04:14:50 +00:00
|
|
|
if (!contains_key(mappings, exemplar_type->value)) {
|
|
|
|
trace(9993, "transform") << "adding mapping from " << exemplar_type->value << " to " << debug_string(refinement_type) << end();
|
2015-11-15 08:37:29 +00:00
|
|
|
if (refinement_type->value == "literal")
|
|
|
|
put(mappings, exemplar_type->value, new string_tree("number"));
|
|
|
|
else
|
|
|
|
put(mappings, exemplar_type->value, new string_tree(*refinement_type));
|
2015-11-02 03:42:49 +00:00
|
|
|
}
|
|
|
|
else {
|
2015-11-15 08:37:29 +00:00
|
|
|
if (!deeply_equal_types(get(mappings, exemplar_type->value), refinement_type)) {
|
2015-11-11 03:56:41 +00:00
|
|
|
raise_error << maybe(caller_recipe.name) << "no call found for '" << call_instruction.to_string() << "'\n" << end();
|
2015-11-15 09:44:58 +00:00
|
|
|
//? cerr << exemplar_type->value << ": " << debug_string(get(mappings, exemplar_type->value)) << " vs " << debug_string(refinement_type) << '\n';
|
2015-11-11 03:56:41 +00:00
|
|
|
*error = true;
|
|
|
|
return;
|
|
|
|
}
|
2015-11-02 03:42:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2015-11-11 04:14:50 +00:00
|
|
|
accumulate_type_ingredients(exemplar_type->left, refinement_type->left, mappings, exemplar, exemplar_reagent, call_instruction, caller_recipe, error);
|
2015-11-02 03:42:49 +00:00
|
|
|
}
|
2015-11-11 04:14:50 +00:00
|
|
|
accumulate_type_ingredients(exemplar_type->right, refinement_type->right, mappings, exemplar, exemplar_reagent, call_instruction, caller_recipe, error);
|
2015-11-02 03:42:49 +00:00
|
|
|
}
|
|
|
|
|
2015-11-08 08:10:56 +00:00
|
|
|
void replace_type_ingredients(recipe& new_recipe, const map<string, const string_tree*>& mappings) {
|
2015-10-31 17:54:48 +00:00
|
|
|
// update its header
|
2015-11-02 03:42:49 +00:00
|
|
|
if (mappings.empty()) return;
|
|
|
|
trace(9993, "transform") << "replacing in recipe header ingredients" << end();
|
2015-11-09 07:36:37 +00:00
|
|
|
for (long long int i = 0; i < SIZE(new_recipe.ingredients); ++i)
|
2015-11-17 18:47:11 +00:00
|
|
|
replace_type_ingredients(new_recipe.ingredients.at(i), mappings, new_recipe);
|
2015-11-02 03:42:49 +00:00
|
|
|
trace(9993, "transform") << "replacing in recipe header products" << end();
|
2015-11-09 07:36:37 +00:00
|
|
|
for (long long int i = 0; i < SIZE(new_recipe.products); ++i)
|
2015-11-17 18:47:11 +00:00
|
|
|
replace_type_ingredients(new_recipe.products.at(i), mappings, new_recipe);
|
2015-10-31 17:54:48 +00:00
|
|
|
// update its body
|
|
|
|
for (long long int i = 0; i < SIZE(new_recipe.steps); ++i) {
|
|
|
|
instruction& inst = new_recipe.steps.at(i);
|
2015-11-02 03:42:49 +00:00
|
|
|
trace(9993, "transform") << "replacing in instruction '" << inst.to_string() << "'" << end();
|
2015-11-09 07:36:37 +00:00
|
|
|
for (long long int j = 0; j < SIZE(inst.ingredients); ++j)
|
2015-11-17 18:47:11 +00:00
|
|
|
replace_type_ingredients(inst.ingredients.at(j), mappings, new_recipe);
|
2015-11-09 07:36:37 +00:00
|
|
|
for (long long int j = 0; j < SIZE(inst.products); ++j)
|
2015-11-17 18:47:11 +00:00
|
|
|
replace_type_ingredients(inst.products.at(j), mappings, new_recipe);
|
2015-11-08 06:56:06 +00:00
|
|
|
// special-case for new: replace type ingredient in first ingredient *value*
|
|
|
|
if (inst.name == "new" && inst.ingredients.at(0).name.at(0) != '[') {
|
|
|
|
string_tree* type_name = parse_string_tree(inst.ingredients.at(0).name);
|
|
|
|
replace_type_ingredients(type_name, mappings);
|
2015-11-09 06:42:21 +00:00
|
|
|
inst.ingredients.at(0).name = type_name->to_string();
|
2015-11-08 06:56:06 +00:00
|
|
|
delete type_name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-17 18:47:11 +00:00
|
|
|
void replace_type_ingredients(reagent& x, const map<string, const string_tree*>& mappings, const recipe& caller) {
|
2015-11-02 03:42:49 +00:00
|
|
|
trace(9993, "transform") << "replacing in ingredient " << x.original_string << end();
|
|
|
|
// replace properties
|
2015-11-17 18:47:11 +00:00
|
|
|
if (!x.properties.at(0).second) {
|
|
|
|
raise_error << "specializing " << caller.name << ": missing type for " << x.original_string << '\n' << end();
|
|
|
|
return;
|
|
|
|
}
|
2015-11-02 03:42:49 +00:00
|
|
|
replace_type_ingredients(x.properties.at(0).second, mappings);
|
|
|
|
// refresh types from properties
|
|
|
|
delete x.type;
|
|
|
|
x.type = new_type_tree(x.properties.at(0).second);
|
|
|
|
if (x.type)
|
2015-11-08 22:46:48 +00:00
|
|
|
trace(9993, "transform") << " after: " << debug_string(x.type) << end();
|
2015-11-02 03:42:49 +00:00
|
|
|
}
|
|
|
|
|
2015-11-08 08:10:56 +00:00
|
|
|
void replace_type_ingredients(string_tree* type, const map<string, const string_tree*>& mappings) {
|
2015-11-02 03:42:49 +00:00
|
|
|
if (!type) return;
|
2015-11-06 21:22:16 +00:00
|
|
|
if (is_type_ingredient_name(type->value) && contains_key(mappings, type->value)) {
|
2015-11-08 08:10:56 +00:00
|
|
|
const string_tree* replacement = get(mappings, type->value);
|
2015-11-08 22:46:48 +00:00
|
|
|
trace(9993, "transform") << type->value << " => " << debug_string(replacement) << end();
|
2015-11-08 08:10:56 +00:00
|
|
|
type->value = replacement->value;
|
|
|
|
if (replacement->left) type->left = new string_tree(*replacement->left);
|
|
|
|
if (replacement->right) type->right = new string_tree(*replacement->right);
|
2015-11-02 03:42:49 +00:00
|
|
|
}
|
|
|
|
replace_type_ingredients(type->left, mappings);
|
|
|
|
replace_type_ingredients(type->right, mappings);
|
2015-10-31 17:54:48 +00:00
|
|
|
}
|
2015-11-01 00:18:59 +00:00
|
|
|
|
2015-11-15 09:16:51 +00:00
|
|
|
void ensure_all_concrete_types(/*const*/ recipe& new_recipe, const recipe& exemplar) {
|
2015-11-09 07:36:37 +00:00
|
|
|
for (long long int i = 0; i < SIZE(new_recipe.ingredients); ++i)
|
2015-11-15 09:16:51 +00:00
|
|
|
ensure_all_concrete_types(new_recipe.ingredients.at(i), exemplar);
|
2015-11-09 07:36:37 +00:00
|
|
|
for (long long int i = 0; i < SIZE(new_recipe.products); ++i)
|
2015-11-15 09:16:51 +00:00
|
|
|
ensure_all_concrete_types(new_recipe.products.at(i), exemplar);
|
2015-11-08 06:56:06 +00:00
|
|
|
for (long long int i = 0; i < SIZE(new_recipe.steps); ++i) {
|
2015-11-15 09:16:51 +00:00
|
|
|
instruction& inst = new_recipe.steps.at(i);
|
2015-11-09 07:36:37 +00:00
|
|
|
for (long long int j = 0; j < SIZE(inst.ingredients); ++j)
|
2015-11-15 09:16:51 +00:00
|
|
|
ensure_all_concrete_types(inst.ingredients.at(j), exemplar);
|
2015-11-09 07:36:37 +00:00
|
|
|
for (long long int j = 0; j < SIZE(inst.products); ++j)
|
2015-11-15 09:16:51 +00:00
|
|
|
ensure_all_concrete_types(inst.products.at(j), exemplar);
|
2015-11-08 06:56:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-15 09:16:51 +00:00
|
|
|
void ensure_all_concrete_types(/*const*/ reagent& x, const recipe& exemplar) {
|
|
|
|
if (!x.type) {
|
|
|
|
raise_error << maybe(exemplar.name) << "failed to map a type to " << x.original_string << '\n' << end();
|
|
|
|
x.type = new type_tree(0); // just to prevent crashes later
|
2015-11-08 06:56:06 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-11-15 09:16:51 +00:00
|
|
|
if (x.type->value == -1) {
|
|
|
|
raise_error << maybe(exemplar.name) << "failed to map a type to the unknown " << x.original_string << '\n' << end();
|
2015-11-08 06:56:06 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-15 20:35:59 +00:00
|
|
|
long long int non_ghost_size(vector<recipe_ordinal>& variants) {
|
|
|
|
long long int result = 0;
|
|
|
|
for (long long int i = 0; i < SIZE(variants); ++i)
|
|
|
|
if (variants.at(i) != -1) ++result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-11-11 04:24:03 +00:00
|
|
|
:(scenario shape_shifting_recipe_2)
|
2015-11-01 00:18:59 +00:00
|
|
|
recipe main [
|
|
|
|
10:point <- merge 14, 15
|
|
|
|
11:point <- foo 10:point
|
|
|
|
]
|
2015-11-11 04:24:03 +00:00
|
|
|
# non-matching shape-shifting variant
|
2015-11-01 00:18:59 +00:00
|
|
|
recipe foo a:_t, b:_t -> result:number [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
result <- copy 34
|
|
|
|
]
|
2015-11-11 04:24:03 +00:00
|
|
|
# matching shape-shifting variant
|
2015-11-01 00:18:59 +00:00
|
|
|
recipe foo a:_t -> result:_t [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
result <- copy a
|
|
|
|
]
|
|
|
|
+mem: storing 14 in location 11
|
|
|
|
+mem: storing 15 in location 12
|
2015-11-02 03:42:49 +00:00
|
|
|
|
2015-11-11 04:24:03 +00:00
|
|
|
:(scenario shape_shifting_recipe_nonroot)
|
2015-11-02 03:42:49 +00:00
|
|
|
recipe main [
|
|
|
|
10:foo:point <- merge 14, 15, 16
|
|
|
|
20:point/raw <- bar 10:foo:point
|
|
|
|
]
|
2015-11-11 04:24:03 +00:00
|
|
|
# shape-shifting recipe with type ingredient following some other type
|
2015-11-02 03:42:49 +00:00
|
|
|
recipe bar a:foo:_t -> result:_t [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
result <- get a, x:offset
|
|
|
|
]
|
|
|
|
container foo:_t [
|
|
|
|
x:_t
|
|
|
|
y:number
|
|
|
|
]
|
|
|
|
+mem: storing 14 in location 20
|
|
|
|
+mem: storing 15 in location 21
|
2015-11-09 23:19:51 +00:00
|
|
|
|
2015-11-11 04:24:03 +00:00
|
|
|
:(scenario shape_shifting_recipe_type_deduction_ignores_offsets)
|
2015-11-09 23:19:51 +00:00
|
|
|
recipe main [
|
|
|
|
10:foo:point <- merge 14, 15, 16
|
|
|
|
20:point/raw <- bar 10:foo:point
|
|
|
|
]
|
|
|
|
recipe bar a:foo:_t -> result:_t [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
x:number <- copy 1
|
|
|
|
result <- get a, x:offset # shouldn't collide with other variable
|
|
|
|
]
|
|
|
|
container foo:_t [
|
|
|
|
x:_t
|
|
|
|
y:number
|
|
|
|
]
|
|
|
|
+mem: storing 14 in location 20
|
|
|
|
+mem: storing 15 in location 21
|
2015-11-10 05:31:21 +00:00
|
|
|
|
2015-11-11 04:24:03 +00:00
|
|
|
:(scenario shape_shifting_recipe_handles_shape_shifting_new_ingredient)
|
2015-11-10 05:31:21 +00:00
|
|
|
recipe main [
|
|
|
|
1:address:foo:point <- bar 3
|
|
|
|
11:foo:point <- copy *1:address:foo:point
|
|
|
|
]
|
|
|
|
container foo:_t [
|
|
|
|
x:_t
|
|
|
|
y:number
|
|
|
|
]
|
|
|
|
recipe bar x:number -> result:address:foo:_t [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
# new refers to _t in its ingredient *value*
|
|
|
|
result <- new {(foo _t) : type}
|
|
|
|
]
|
|
|
|
+mem: storing 0 in location 11
|
|
|
|
+mem: storing 0 in location 12
|
|
|
|
+mem: storing 0 in location 13
|
|
|
|
|
2015-11-11 04:24:03 +00:00
|
|
|
:(scenario shape_shifting_recipe_handles_shape_shifting_new_ingredient_2)
|
2015-11-10 07:02:23 +00:00
|
|
|
recipe main [
|
|
|
|
1:address:foo:point <- bar 3
|
|
|
|
11:foo:point <- copy *1:address:foo:point
|
|
|
|
]
|
|
|
|
recipe bar x:number -> result:address:foo:_t [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
# new refers to _t in its ingredient *value*
|
|
|
|
result <- new {(foo _t) : type}
|
|
|
|
]
|
|
|
|
# container defined after use
|
|
|
|
container foo:_t [
|
|
|
|
x:_t
|
|
|
|
y:number
|
|
|
|
]
|
|
|
|
+mem: storing 0 in location 11
|
|
|
|
+mem: storing 0 in location 12
|
|
|
|
+mem: storing 0 in location 13
|
2015-11-10 07:26:15 +00:00
|
|
|
|
2015-11-11 04:24:03 +00:00
|
|
|
:(scenario shape_shifting_recipe_supports_compound_types)
|
2015-11-10 07:26:15 +00:00
|
|
|
recipe main [
|
|
|
|
1:address:point <- new point:type
|
|
|
|
2:address:number <- get-address *1:address:point, y:offset
|
|
|
|
*2:address:number <- copy 34
|
|
|
|
3:address:point <- bar 1:address:point # specialize _t to address:point
|
|
|
|
4:point <- copy *3:address:point
|
|
|
|
]
|
|
|
|
recipe bar a:_t -> result:_t [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
result <- copy a
|
|
|
|
]
|
|
|
|
+mem: storing 34 in location 5
|
2015-11-11 03:56:41 +00:00
|
|
|
|
2015-11-11 04:24:03 +00:00
|
|
|
:(scenario shape_shifting_recipe_error)
|
2015-11-11 03:56:41 +00:00
|
|
|
% Hide_errors = true;
|
|
|
|
recipe main [
|
|
|
|
a:number <- copy 3
|
|
|
|
b:address:number <- foo a
|
|
|
|
]
|
|
|
|
recipe foo a:_t -> b:_t [
|
|
|
|
load-ingredients
|
|
|
|
b <- copy a
|
|
|
|
]
|
|
|
|
+error: main: no call found for 'b:address:number <- foo a'
|
2015-11-15 06:59:09 +00:00
|
|
|
|
|
|
|
:(scenario specialize_inside_recipe_without_header)
|
|
|
|
recipe main [
|
|
|
|
foo 3
|
|
|
|
]
|
|
|
|
recipe foo [
|
|
|
|
local-scope
|
|
|
|
x:number <- next-ingredient # ensure no header
|
|
|
|
1:number/raw <- bar x # call a shape-shifting recipe
|
|
|
|
]
|
|
|
|
recipe bar x:_elem -> y:_elem [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
y <- add x, 1
|
|
|
|
]
|
|
|
|
+mem: storing 4 in location 1
|
2015-11-15 08:37:29 +00:00
|
|
|
|
|
|
|
:(scenario specialize_with_literal)
|
|
|
|
recipe main [
|
|
|
|
local-scope
|
|
|
|
# permit literal to map to number
|
|
|
|
1:number/raw <- foo 3
|
|
|
|
]
|
|
|
|
recipe foo x:_elem -> y:_elem [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
y <- add x, 1
|
|
|
|
]
|
|
|
|
+mem: storing 4 in location 1
|
|
|
|
|
|
|
|
:(scenario specialize_with_literal_2)
|
|
|
|
recipe main [
|
|
|
|
local-scope
|
|
|
|
# permit literal to map to character
|
|
|
|
1:character/raw <- foo 3
|
|
|
|
]
|
|
|
|
recipe foo x:_elem -> y:_elem [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
y <- add x, 1
|
|
|
|
]
|
|
|
|
+mem: storing 4 in location 1
|
2015-11-15 09:02:53 +00:00
|
|
|
|
|
|
|
:(scenario specialize_with_literal_3)
|
|
|
|
% Hide_errors = true;
|
|
|
|
recipe main [
|
|
|
|
local-scope
|
|
|
|
# permit '0' to map to address to shape-shifting type-ingredient
|
|
|
|
1:address:character/raw <- foo 0
|
|
|
|
]
|
|
|
|
recipe foo x:address:_elem -> y:address:_elem [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
y <- copy x
|
|
|
|
]
|
|
|
|
+mem: storing 0 in location 1
|
|
|
|
$error: 0
|
2015-11-15 09:16:51 +00:00
|
|
|
|
|
|
|
:(scenario specialize_with_literal_4)
|
|
|
|
% Hide_errors = true;
|
|
|
|
recipe main [
|
|
|
|
local-scope
|
|
|
|
# ambiguous call: what's the type of its ingredient?!
|
|
|
|
foo 0
|
|
|
|
]
|
|
|
|
recipe foo x:address:_elem -> y:address:_elem [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
y <- copy x
|
|
|
|
]
|
|
|
|
+error: foo: failed to map a type to x
|
|
|
|
+error: foo: failed to map a type to y
|
2015-11-15 09:44:58 +00:00
|
|
|
|
|
|
|
:(scenario specialize_with_literal_5)
|
|
|
|
recipe main [
|
|
|
|
foo 3, 4 # recipe mapping two variables to literals
|
|
|
|
]
|
|
|
|
recipe foo x:_elem, y:_elem [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
1:number/raw <- add x, y
|
|
|
|
]
|
|
|
|
+mem: storing 7 in location 1
|
2015-11-15 20:35:59 +00:00
|
|
|
|
|
|
|
:(scenario multiple_shape_shifting_variants)
|
|
|
|
# try to call two different shape-shifting recipes with the same name
|
|
|
|
recipe main [
|
|
|
|
e1:d1:number <- merge 3
|
|
|
|
e2:d2:number <- merge 4, 5
|
|
|
|
1:number/raw <- foo e1
|
|
|
|
2:number/raw <- foo e2
|
|
|
|
]
|
|
|
|
# the two shape-shifting definitions
|
|
|
|
recipe foo a:d1:_elem -> b:number [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
reply 34
|
|
|
|
]
|
|
|
|
recipe foo a:d2:_elem -> b:number [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
reply 35
|
|
|
|
]
|
|
|
|
# the shape-shifting containers they use
|
|
|
|
container d1:_elem [
|
|
|
|
x:_elem
|
|
|
|
]
|
|
|
|
container d2:_elem [
|
|
|
|
x:number
|
|
|
|
y:_elem
|
|
|
|
]
|
|
|
|
+mem: storing 34 in location 1
|
|
|
|
+mem: storing 35 in location 2
|
|
|
|
|
|
|
|
:(scenario multiple_shape_shifting_variants_2)
|
|
|
|
# static dispatch between shape-shifting variants, _including pointer lookups_
|
|
|
|
recipe main [
|
|
|
|
e1:d1:number <- merge 3
|
|
|
|
e2:address:d2:number <- new {(d2 number): type}
|
|
|
|
1:number/raw <- foo e1
|
|
|
|
2:number/raw <- foo *e2 # different from previous scenario
|
|
|
|
]
|
|
|
|
recipe foo a:d1:_elem -> b:number [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
reply 34
|
|
|
|
]
|
|
|
|
recipe foo a:d2:_elem -> b:number [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
reply 35
|
|
|
|
]
|
|
|
|
container d1:_elem [
|
|
|
|
x:_elem
|
|
|
|
]
|
|
|
|
container d2:_elem [
|
|
|
|
x:number
|
|
|
|
y:_elem
|
|
|
|
]
|
|
|
|
+mem: storing 34 in location 1
|
|
|
|
+mem: storing 35 in location 2
|
2015-11-17 18:47:11 +00:00
|
|
|
|
|
|
|
:(scenario missing_type_in_shape_shifting_recipe)
|
|
|
|
% Hide_errors = true;
|
|
|
|
recipe main [
|
|
|
|
a:d1:number <- merge 3
|
|
|
|
foo a
|
|
|
|
]
|
|
|
|
recipe foo a:d1:_elem -> b:number [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
copy e # no such variable
|
|
|
|
reply 34
|
|
|
|
]
|
|
|
|
container d1:_elem [
|
|
|
|
x:_elem
|
|
|
|
]
|
|
|
|
+error: foo: unknown type for e (check the name for typos)
|
|
|
|
+error: specializing foo: missing type for e
|
|
|
|
# and it doesn't crash
|
2015-11-17 19:25:00 +00:00
|
|
|
|
|
|
|
:(scenario missing_type_in_shape_shifting_recipe_2)
|
|
|
|
% Hide_errors = true;
|
|
|
|
recipe main [
|
|
|
|
a:d1:number <- merge 3
|
|
|
|
foo a
|
|
|
|
]
|
|
|
|
recipe foo a:d1:_elem -> b:number [
|
|
|
|
local-scope
|
|
|
|
load-ingredients
|
|
|
|
get e, x:offset # unknown variable in a 'get', which does some extra checking
|
|
|
|
reply 34
|
|
|
|
]
|
|
|
|
container d1:_elem [
|
|
|
|
x:_elem
|
|
|
|
]
|
|
|
|
+error: foo: unknown type for e (check the name for typos)
|
|
|
|
+error: specializing foo: missing type for e
|
|
|
|
# and it doesn't crash
|