diff options
-rw-r--r-- | 010vm.cc | 161 | ||||
-rw-r--r-- | 017parse_tree.cc | 28 | ||||
-rw-r--r-- | 018type_abbreviations.cc | 52 | ||||
-rw-r--r-- | 020run.cc | 10 | ||||
-rw-r--r-- | 021check_instruction.cc | 76 | ||||
-rw-r--r-- | 027call_ingredient.cc | 16 | ||||
-rw-r--r-- | 028call_reply.cc | 2 | ||||
-rw-r--r-- | 030container.cc | 299 | ||||
-rw-r--r-- | 031merge.cc | 16 | ||||
-rw-r--r-- | 032array.cc | 48 | ||||
-rw-r--r-- | 033exclusive_container.cc | 35 | ||||
-rw-r--r-- | 034address.cc | 8 | ||||
-rw-r--r-- | 035lookup.cc | 8 | ||||
-rw-r--r-- | 036refcount.cc | 346 | ||||
-rw-r--r-- | 037abandon.cc | 2 | ||||
-rw-r--r-- | 039location_array.cc | 22 | ||||
-rw-r--r-- | 042name.cc | 10 | ||||
-rw-r--r-- | 043space.cc | 14 | ||||
-rw-r--r-- | 045closure_name.cc | 15 | ||||
-rw-r--r-- | 046global.cc | 17 | ||||
-rw-r--r-- | 047check_type_by_name.cc | 2 | ||||
-rw-r--r-- | 050scenario.cc | 4 | ||||
-rw-r--r-- | 054static_dispatch.cc | 5 | ||||
-rw-r--r-- | 055shape_shifting_container.cc | 203 | ||||
-rw-r--r-- | 056shape_shifting_recipe.cc | 130 | ||||
-rw-r--r-- | 057immutable.cc | 4 | ||||
-rw-r--r-- | 062rewrite_stash.cc | 7 | ||||
-rw-r--r-- | 069hash.cc | 8 | ||||
-rw-r--r-- | 071recipe.cc | 31 | ||||
-rw-r--r-- | 073wait.cc | 11 | ||||
-rw-r--r-- | 074deep_copy.cc | 2 |
31 files changed, 1104 insertions, 488 deletions
diff --git a/010vm.cc b/010vm.cc index 23d4ef0b..a3dd3bc5 100644 --- a/010vm.cc +++ b/010vm.cc @@ -70,40 +70,37 @@ struct reagent { // Types can range from a simple type ordinal, to arbitrarily complex trees of // type parameters, like (map (address array character) (list number)) struct type_tree { - string name; - type_ordinal value; - type_tree* left; - type_tree* right; + bool atom; + string name; // only if atom + type_ordinal value; // only if atom + type_tree* left; // only if !atom + type_tree* right; // only if !atom ~type_tree(); type_tree(const type_tree& old); - // simple: type ordinal + // atomic type ordinal explicit type_tree(string name); - type_tree(string name, type_ordinal v) :name(name), value(v), left(NULL), right(NULL) {} - // intermediate: list of type ordinals - type_tree(string name, type_ordinal v, type_tree* r) :name(name), value(v), left(NULL), right(r) {} - type_tree(string name, type_tree* r); - // advanced: tree containing type ordinals - type_tree(type_tree* l, type_tree* r) :value(0), left(l), right(r) {} + type_tree(string name, type_ordinal v) :atom(true), name(name), value(v), left(NULL), right(NULL) {} + // tree of type ordinals + type_tree(type_tree* l, type_tree* r) :atom(false), value(0), left(l), right(r) {} + type_tree& operator=(const type_tree& old); }; struct string_tree { - string value; - string_tree* left; - string_tree* right; + bool atom; + string value; // only if atom + string_tree* left; // only if !atom + string_tree* right; // only if !atom ~string_tree(); string_tree(const string_tree& old); - // simple: flat string - explicit string_tree(string v) :value(v), left(NULL), right(NULL) {} - // intermediate: list of strings - string_tree(string v, string_tree* r) :value(v), left(NULL), right(r) {} - // advanced: tree containing strings - string_tree(string_tree* l, string_tree* r) :left(l), right(r) {} + // atomic string + explicit string_tree(string v) :atom(true), value(v), left(NULL), right(NULL) {} + // tree of strings + string_tree(string_tree* l, string_tree* r) :atom(false), left(l), right(r) {} }; // End type_tree Definition :(code) -type_tree::type_tree(string name) :name(name), value(get(Type_ordinal, name)), left(NULL), right(NULL) {} -type_tree::type_tree(string name, type_tree* r) :name(name), value(get(Type_ordinal, name)), left(NULL), right(r) {} +type_tree::type_tree(string name) :atom(true), name(name), value(get(Type_ordinal, name)), left(NULL), right(NULL) {} :(before "End Globals") // Locations refer to a common 'memory'. Each location can store a number. @@ -296,26 +293,29 @@ void slurp_properties(istream& in, vector<pair<string, string_tree*> >& out) { string_tree* parse_property_list(istream& in) { skip_whitespace_but_not_newline(in); if (!has_data(in)) return NULL; - string_tree* result = new string_tree(slurp_until(in, ':')); - result->right = parse_property_list(in); - return result; + string_tree* left = new string_tree(slurp_until(in, ':')); + if (!has_data(in)) return left; + string_tree* right = parse_property_list(in); + return new string_tree(left, right); } type_tree* new_type_tree(const string_tree* properties) { if (!properties) return NULL; - type_tree* result = new type_tree("", 0); - if (!properties->value.empty()) { - const string& type_name = result->name = properties->value; + if (properties->atom) { + const string& type_name = properties->value; + int value = 0; if (contains_key(Type_ordinal, type_name)) - result->value = get(Type_ordinal, type_name); + value = get(Type_ordinal, type_name); else if (is_integer(type_name)) // sometimes types will contain non-type tags, like numbers for the size of an array - result->value = 0; - else if (properties->value != "->") // used in recipe types - result->value = -1; // should never happen; will trigger errors later + value = 0; + else if (properties->value == "->") // used in recipe types + value = 0; + else + value = -1; // should never happen; will trigger errors later + return new type_tree(type_name, value); } - result->left = new_type_tree(properties->left); - result->right = new_type_tree(properties->right); - return result; + return new type_tree(new_type_tree(properties->left), + new_type_tree(properties->right)); } //: avoid memory leaks for the type tree @@ -334,13 +334,24 @@ reagent::reagent(const reagent& other) { } type_tree::type_tree(const type_tree& old) { + atom = old.atom; + name = old.name; + value = old.value; + left = old.left ? new type_tree(*old.left) : NULL; + right = old.right ? new type_tree(*old.right) : NULL; +} + +type_tree& type_tree::operator=(const type_tree& old) { + atom = old.atom; name = old.name; value = old.value; left = old.left ? new type_tree(*old.left) : NULL; right = old.right ? new type_tree(*old.right) : NULL; + return *this; } string_tree::string_tree(const string_tree& old) { + atom = old.atom; value = old.value; left = old.left ? new string_tree(*old.left) : NULL; right = old.right ? new string_tree(*old.right) : NULL; @@ -543,27 +554,25 @@ string debug_string(const reagent& x) { string to_string(const string_tree* property) { if (!property) return "()"; ostringstream out; - if (!property->left && !property->right) - // abbreviate a single-node tree to just its contents - out << '"' << property->value << '"'; - else - dump(property, out); + dump(property, out); return out.str(); } void dump(const string_tree* x, ostream& out) { - if (!x->left && !x->right) { - out << x->value; + if (!x) return; + if (x->atom) { + out << '"' << x->value << '"'; return; } out << '('; - for (const string_tree* curr = x; curr; curr = curr->right) { - if (curr != x) out << ' '; - if (curr->left) - dump(curr->left, out); - else - out << '"' << curr->value << '"'; + const string_tree* curr = x; + while (curr && !curr->atom) { + dump(curr->left, out); + if (curr->right) out << ' '; + curr = curr->right; } + // final right + dump(curr, out); out << ')'; } @@ -576,18 +585,20 @@ string to_string(const type_tree* type) { } void dump(const type_tree* x, ostream& out) { - if (!x->left && !x->right) { + if (!x) return; + if (x->atom) { dump(x->value, out); return; } out << '('; - for (const type_tree* curr = x; curr; curr = curr->right) { - if (curr != x) out << ' '; - if (curr->left) - dump(curr->left, out); - else - dump(curr->value, out); + const type_tree* curr = x; + while (curr && !curr->atom) { + dump(curr->left, out); + if (curr->right) out << ' '; + curr = curr->right; } + // final right + dump(curr, out); out << ')'; } @@ -606,19 +617,21 @@ string names_to_string(const type_tree* type) { return out.str(); } -void dump_names(const type_tree* type, ostream& out) { - if (!type->left && !type->right) { - out << '"' << type->name << '"'; +void dump_names(const type_tree* x, ostream& out) { + if (!x) return; + if (x->atom) { + out << '"' << x->name << '"'; return; } out << '('; - for (const type_tree* curr = type; curr; curr = curr->right) { - if (curr != type) out << ' '; - if (curr->left) - dump_names(curr->left, out); - else - out << '"' << curr->name << '"'; + const type_tree* curr = x; + while (curr && !curr->atom) { + dump_names(curr->left, out); + if (curr->right) out << ' '; + curr = curr->right; } + // final right + dump_names(curr, out); out << ')'; } @@ -630,19 +643,21 @@ string names_to_string_without_quotes(const type_tree* type) { return out.str(); } -void dump_names_without_quotes(const type_tree* type, ostream& out) { - if (!type->left && !type->right) { - out << type->name; +void dump_names_without_quotes(const type_tree* x, ostream& out) { + if (!x) return; + if (x->atom) { + out << x->name; return; } out << '('; - for (const type_tree* curr = type; curr; curr = curr->right) { - if (curr != type) out << ' '; - if (curr->left) - dump_names_without_quotes(curr->left, out); - else - out << curr->name; + const type_tree* curr = x; + while (curr && !curr->atom) { + dump_names_without_quotes(curr->left, out); + if (curr->right) out << ' '; + curr = curr->right; } + // final right + dump_names_without_quotes(curr, out); out << ')'; } diff --git a/017parse_tree.cc b/017parse_tree.cc index ce744f0f..8efcb83e 100644 --- a/017parse_tree.cc +++ b/017parse_tree.cc @@ -2,6 +2,11 @@ // support for more complex trees of properties in dilated reagents. This will // come in handy later for expressing complex types, like "a dictionary from // (address to array of charaters) to (list of numbers)". +// +// Type trees aren't as general as s-expressions even if they look like them: +// the first element of a type tree is always an atom, and left and right +// pointers of non-atoms are never NULL. All type trees are 'dotted' in lisp +// parlance. :(scenarios load) :(scenario dilated_reagent_with_nested_brackets) @@ -17,7 +22,7 @@ type_names = parse_string_tree(type_names); :(code) string_tree* parse_string_tree(string_tree* s) { - assert(!s->left && !s->right); + assert(s->atom); if (s->value.at(0) != '(') return s; string_tree* result = parse_string_tree(s->value); delete s; @@ -44,17 +49,30 @@ string_tree* parse_string_tree(istream& in) { in.get(); // skip '(' string_tree* result = NULL; string_tree** curr = &result; - while (in.peek() != ')') { - assert(has_data(in)); - *curr = new string_tree(""); + while (true) { skip_whitespace_but_not_newline(in); + assert(has_data(in)); + if (in.peek() == ')') break; + *curr = new string_tree(NULL, NULL); if (in.peek() == '(') (*curr)->left = parse_string_tree(in); else - (*curr)->value = next_word(in); + (*curr)->left = new string_tree(next_word(in)); curr = &(*curr)->right; } in.get(); // skip ')' + assert(*curr == NULL); + // standardize the final element to always be on the right if it's an atom + // (a b c) => (a b . c) in s-expression parlance + string_tree* tmp = result; + while (tmp->right && tmp->right->right) tmp = tmp->right; + assert(!tmp->right->atom); + if (!tmp->right->left->atom) return result; + string_tree* tmp2 = tmp->right; + tmp->right = tmp2->left; + tmp2->left = NULL; + assert(tmp2->right == NULL); + delete tmp2; return result; } diff --git a/018type_abbreviations.cc b/018type_abbreviations.cc index c4871d8c..a13d97a8 100644 --- a/018type_abbreviations.cc +++ b/018type_abbreviations.cc @@ -16,8 +16,7 @@ def main [ f 4:&&@&@number # ..any number of times f 5:array:&number:3 # abbreviations take precedence over ':' f {6: (array &number 3)} # support for dilated reagents and more complex parse trees - f 7:&&@& # abbreviations without payload - f 8:@number:3 # *not* the same as array:number:3 + f 7:@number:3 # *not* the same as array:number:3 ] +parse: ingredient: {1: ("address" "number")} +parse: ingredient: {2: ("array" "number")} @@ -25,32 +24,33 @@ def main [ +parse: ingredient: {4: ("address" "address" "array" "address" "array" "number")} +parse: ingredient: {5: ("array" ("address" "number") "3")} +parse: ingredient: {6: ("array" ("address" "number") "3")} -# an error that will be raised elsewhere -+parse: ingredient: {7: ("address" "address" "array" "address")} # not what you want -+parse: ingredient: {8: (("array" "number") "3")} ++parse: ingredient: {7: (("array" "number") "3")} + +:(scenario abbreviation_error) +% Hide_errors = true; +def main [ + f 1:&&@& # abbreviations without payload +] ++error: invalid type abbreviation &&@& :(before "End Parsing Reagent Type Property(type_names)") -type_names = replace_address_and_array_symbols(type_names); +string_tree* new_type_names = replace_address_and_array_symbols(type_names); +delete type_names; +type_names = new_type_names; :(before "End Parsing Dilated Reagent Type Property(type_names)") -type_names = replace_address_and_array_symbols(type_names); +string_tree* new_type_names = replace_address_and_array_symbols(type_names); +delete type_names; +type_names = new_type_names; :(code) // simple version; lots of unnecessary allocations; always creates a new pointer -string_tree* replace_address_and_array_symbols(string_tree*& orig) { +string_tree* replace_address_and_array_symbols(string_tree* orig) { if (orig == NULL) return NULL; - string_tree* new_left = replace_address_and_array_symbols(orig->left); - string_tree* new_right = replace_address_and_array_symbols(orig->right); - if (orig->value.empty()) { - delete orig; orig = NULL; - return new string_tree(new_left, new_right); - } - assert(new_left == NULL); - new_left = replace_address_and_array_symbols(orig->value); - assert(new_left); - delete orig; orig = NULL; - append(new_left, new_right); - return new_left; + if (orig->atom) + return replace_address_and_array_symbols(orig->value); + return new string_tree(replace_address_and_array_symbols(orig->left), + replace_address_and_array_symbols(orig->right)); } // todo: unicode @@ -69,14 +69,18 @@ string_tree* replace_address_and_array_symbols(const string& type_name) { new_node = new string_tree("array"); else break; - if (!curr) - result = curr = new_node; - else - curr->right = new_node, curr = curr->right; + if (result == NULL) + result = curr = new string_tree(new_node, NULL); + else { + curr->right = new string_tree(new_node, NULL); + curr = curr->right; + } ++i; } if (i < SIZE(type_name)) curr->right = new string_tree(type_name.substr(i)); + else + raise << "invalid type abbreviation " << type_name << "\n" << end(); return result; } diff --git a/020run.cc b/020run.cc index 51944784..7c8f292d 100644 --- a/020run.cc +++ b/020run.cc @@ -326,10 +326,12 @@ bool size_mismatch(const reagent& x, const vector<double>& data) { } bool is_literal(const reagent& r) { - if (!r.type) return false; - if (r.type->value == 0) - assert(!r.type->left && !r.type->right); - return r.type->value == 0; + return is_literal(r.type); +} +bool is_literal(const type_tree* type) { + if (!type) return false; + if (!type->atom) return false; + return type->value == 0; } bool scalar(const vector<int>& x) { diff --git a/021check_instruction.cc b/021check_instruction.cc index 3554d253..de5476a0 100644 --- a/021check_instruction.cc +++ b/021check_instruction.cc @@ -102,7 +102,6 @@ bool types_coercible(const reagent& to, const reagent& from) { if (is_mu_address(from) && is_mu_number(to)) return true; if (is_mu_boolean(from) && is_mu_number(to)) return true; if (is_mu_number(from) && is_mu_boolean(to)) return true; - // End types_coercible Special-cases return false; } @@ -116,20 +115,13 @@ bool types_match(const reagent& to, const reagent& from) { // allow writing 0 to any address if (is_mu_address(to)) return from.name == "0"; if (!to.type) return false; - if (to.type->value == get(Type_ordinal, "boolean")) - return boolean_matches_literal(to, from); + if (to.type->atom && to.type->value == get(Type_ordinal, "boolean")) + return from.name == "0" || from.name == "1"; return size_of(to) == 1; // literals are always scalars } return types_strictly_match(to, from); } -bool boolean_matches_literal(const reagent& to, const reagent& from) { - if (!is_literal(from)) return false; - if (!to.type) return false; - if (to.type->value != get(Type_ordinal, "boolean")) return false; - return from.name == "0" || from.name == "1"; -} - // copy arguments because later layers will want to make changes to them // without perturbing the caller bool types_strictly_match(reagent/*copy*/ to, reagent/*copy*/ from) { @@ -147,10 +139,14 @@ bool types_strictly_match(reagent/*copy*/ to, reagent/*copy*/ from) { // two types match if the second begins like the first // (trees perform the same check recursively on each subtree) bool types_strictly_match(const type_tree* to, const type_tree* from) { - if (!to) return true; - if (!from) return to->value == 0; - if (from->value == -1) return from->name == to->name; - if (to->value != from->value) return false; + if (from == to) return true; + if (!from) return to->atom && to->value == 0; + if (to->atom && !from->atom) return from->left->atom && from->left->name == to->name; + if (from->atom) { + if (!to->atom) return false; + if (from->value == -1) return from->name == to->name; + return from->value == to->value; + } return types_strictly_match(to->left, from->left) && types_strictly_match(to->right, from->right); } @@ -172,30 +168,43 @@ bool is_unsafe(const reagent& r) { bool is_mu_array(reagent/*copy*/ r) { // End Preprocess is_mu_array(reagent r) - if (!r.type) return false; - if (is_literal(r)) return false; - return r.type->value == get(Type_ordinal, "array"); + return is_mu_array(r.type); +} + +bool is_mu_array(const type_tree* type) { + if (!type) return false; + if (is_literal(type)) return false; + if (type->atom) return false; + assert(type->left->atom); + return type->left->value == get(Type_ordinal, "array"); } bool is_mu_address(reagent/*copy*/ r) { // End Preprocess is_mu_address(reagent r) - if (!r.type) return false; - if (is_literal(r)) return false; - return r.type->value == get(Type_ordinal, "address"); + return is_mu_address(r.type); +} + +bool is_mu_address(const type_tree* type) { + if (!type) return false; + if (is_literal(type)) return false; + if (type->atom) return false; + assert(type->left->atom); + return type->left->value == get(Type_ordinal, "address"); } bool is_mu_boolean(reagent/*copy*/ r) { // End Preprocess is_mu_boolean(reagent r) if (!r.type) return false; if (is_literal(r)) return false; + if (!r.type->atom) return false; return r.type->value == get(Type_ordinal, "boolean"); } bool is_mu_number(reagent/*copy*/ r) { // End Preprocess is_mu_number(reagent r) if (!r.type) return false; + if (!r.type->atom) return false; if (is_literal(r)) { - if (!r.type) return false; return r.type->name == "literal-fractional-number" || r.type->name == "literal"; } @@ -205,15 +214,24 @@ bool is_mu_number(reagent/*copy*/ r) { bool is_mu_character(reagent/*copy*/ r) { // End Preprocess is_mu_character(reagent r) - if (!r.type) return false; - if (is_literal(r)) return false; - return r.type->value == get(Type_ordinal, "character"); + return is_mu_character(r.type); +} +bool is_mu_character(const type_tree* type) { + if (!type) return false; + if (!type->atom) return false; + if (is_literal(type)) return false; + return type->value == get(Type_ordinal, "character"); } bool is_mu_scalar(reagent/*copy*/ r) { - if (!r.type) return false; - if (is_literal(r)) - return !r.type || r.type->name != "literal-string"; - if (is_mu_array(r)) return false; - return size_of(r) == 1; + return is_mu_scalar(r.type); +} + +bool is_mu_scalar(const type_tree* type) { + if (!type) return false; + if (is_mu_address(type)) return true; + if (!type->atom) return false; + if (is_literal(type)) + return type->name != "literal-string"; + return size_of(type) == 1; } diff --git a/027call_ingredient.cc b/027call_ingredient.cc index 1bc3a563..c9917614 100644 --- a/027call_ingredient.cc +++ b/027call_ingredient.cc @@ -176,10 +176,14 @@ case INGREDIENT: { bool is_mu_string(reagent/*copy*/ x) { // End Preprocess is_mu_string(reagent x) return x.type - && x.type->value == get(Type_ordinal, "address") - && x.type->right - && x.type->right->value == get(Type_ordinal, "array") - && x.type->right->right - && x.type->right->right->value == get(Type_ordinal, "character") - && x.type->right->right->right == NULL; + && !x.type->atom + && x.type->left->atom + && x.type->left->value == get(Type_ordinal, "address") + && x.type->right + && !x.type->right->atom + && x.type->right->left->atom + && x.type->right->left->value == get(Type_ordinal, "array") + && x.type->right->right + && x.type->right->right->atom + && x.type->right->right->value == get(Type_ordinal, "character"); } diff --git a/028call_reply.cc b/028call_reply.cc index 507357c6..f5e61384 100644 --- a/028call_reply.cc +++ b/028call_reply.cc @@ -80,7 +80,7 @@ void check_types_of_reply_instructions(recipe_ordinal r) { for (int i = 0; i < SIZE(caller_instruction.products); ++i) { if (has_property(reply_inst.ingredients.at(i), "same-as-ingredient")) { string_tree* tmp = property(reply_inst.ingredients.at(i), "same-as-ingredient"); - if (!tmp || tmp->right) { + if (!tmp || !tmp->atom) { raise << maybe(caller.name) << "'same-as-ingredient' metadata should take exactly one value in '" << to_original_string(reply_inst) << "'\n" << end(); goto finish_reply_check; } diff --git a/030container.cc b/030container.cc index c24d6f7d..bb7ce08f 100644 --- a/030container.cc +++ b/030container.cc @@ -148,32 +148,41 @@ void clear_container_metadata() { if (r.metadata.size) return r.metadata.size; :(before "End size_of(type) Cases") -if (type->value == -1) return 1; // error value, but we'll raise it elsewhere -if (type->value == 0) { - assert(!type->left && !type->right); - return 1; +if (type->atom) { + if (type->value == -1) return 1; // error value, but we'll raise it elsewhere + if (type->value == 0) return 1; } -if (!contains_key(Type, type->value)) { - raise << "no such type " << type->value << '\n' << end(); +const type_tree* root = root_type(type); +if (!contains_key(Type, root->value)) { + raise << "no such type " << root->value << '\n' << end(); return 0; } -type_info t = get(Type, type->value); +type_info t = get(Type, root->value); if (t.kind == CONTAINER) { // Compute size_of Container + if (!contains_key(Container_metadata, type)) return 1; // error raised elsewhere return get(Container_metadata, type).size; } +:(code) +const type_tree* root_type(const type_tree* t) { + const type_tree* result = t->atom ? t : t->left; + assert(result->atom); + return result; +} + //: precompute Container_metadata before we need size_of //: also store a copy in each reagent in each instruction in each recipe -//: sometimes does unnecessary work for meaningless types :(after "Begin Instruction Modifying Transforms") // needs to happen before transform_names, therefore after Type Modifying Transforms below Transform.push_back(compute_container_sizes); :(code) void compute_container_sizes(recipe_ordinal r) { recipe& caller = get(Recipe, r); + trace(9992, "transform") << "--- compute container sizes for " << caller.name << end(); for (int i = 0; i < SIZE(caller.steps); ++i) { instruction& inst = caller.steps.at(i); + trace(9993, "transform") << "- compute container sizes for " << to_string(inst) << end(); for (int i = 0; i < SIZE(inst.ingredients); ++i) compute_container_sizes(inst.ingredients.at(i)); for (int i = 0; i < SIZE(inst.products); ++i) @@ -185,37 +194,64 @@ void compute_container_sizes(reagent& r) { if (is_literal(r) || is_dummy(r)) return; reagent rcopy = r; // Compute Container Size(reagent rcopy) - set<type_ordinal> pending_metadata; + set<string> pending_metadata; compute_container_sizes(rcopy.type, pending_metadata); if (contains_key(Container_metadata, rcopy.type)) r.metadata = get(Container_metadata, rcopy.type); } -void compute_container_sizes(const type_tree* type, set<type_ordinal>& pending_metadata) { +void compute_container_sizes(const type_tree* type, set<string>& pending_metadata) { if (!type) return; - if (contains_key(pending_metadata, type->value)) return; - if (type->value) pending_metadata.insert(type->value); + trace(9993, "transform") << "compute container sizes for " << to_string(type) << end(); if (contains_key(Container_metadata, type)) return; - // might be needed by later layers, but we haven't found a need for it yet -//? if (type->left) compute_container_sizes(type->left, pending_metadata); - if (type->right) compute_container_sizes(type->right, pending_metadata); + if (contains_key(pending_metadata, names_to_string_without_quotes(type))) return; + pending_metadata.insert(names_to_string_without_quotes(type)); +//? cerr << to_string(type) << '\n'; + if (!type->atom) { + assert(type->left->atom); + if (type->left->name == "address") { +//? cerr << " address\n"; + compute_container_sizes(type->right, pending_metadata); + } + else if (type->left->name == "array") { + const type_tree* element_type = type->right; + // hack: support both array:number:3 and array:address:number +//? cerr << " array\n"; +//? cerr << " " << to_string(type) << '\n'; +//? cerr << " " << to_string(element_type) << ' ' << element_type->atom << ' ' << element_type->right; +//? if (element_type->right) +//? cerr << " -- " << to_string(element_type->right) << ' ' << element_type->right->atom << ' ' << is_integer(element_type->right->name); +//? cerr << '\n'; + if (!element_type->atom && element_type->right && element_type->right->atom && is_integer(element_type->right->name)) + element_type = element_type->left; + compute_container_sizes(element_type, pending_metadata); + } + // End compute_container_sizes Non-atom Cases + return; + } + assert(type->atom); if (!contains_key(Type, type->value)) return; // error raised elsewhere type_info& info = get(Type, type->value); if (info.kind == CONTAINER) { - // size of a container is the sum of the sizes of its element - // (So it can only contain arrays if they're static and include their - // length in the type.) - container_metadata metadata; - for (int i = 0; i < SIZE(info.elements); ++i) { - reagent/*copy*/ element = info.elements.at(i); - // Compute Container Size(element) - compute_container_sizes(element.type, pending_metadata); - metadata.offset.push_back(metadata.size); // save previous size as offset - metadata.size += size_of(element.type); - } - Container_metadata.push_back(pair<type_tree*, container_metadata>(new type_tree(*type), metadata)); + compute_container_sizes(info, type, pending_metadata); + } + // End compute_container_sizes Atom Cases +} + +void compute_container_sizes(const type_info& container_info, const type_tree* full_type, set<string>& pending_metadata) { + assert(container_info.kind == CONTAINER); + // size of a container is the sum of the sizes of its element + // (So it can only contain arrays if they're static and include their + // length in the type.) + container_metadata metadata; + for (int i = 0; i < SIZE(container_info.elements); ++i) { + reagent/*copy*/ element = container_info.elements.at(i); + // Compute Container Size(element, full_type) + compute_container_sizes(element.type, pending_metadata); + metadata.offset.push_back(metadata.size); // save previous size as offset + metadata.size += size_of(element.type); } - // End compute_container_sizes Cases + Container_metadata.push_back(pair<type_tree*, container_metadata>(new type_tree(*full_type), metadata)); } container_metadata& get(vector<pair<type_tree*, container_metadata> >& all, const type_tree* key) { @@ -239,7 +275,8 @@ bool contains_key(const vector<pair<type_tree*, container_metadata> >& all, cons bool matches(const type_tree* a, const type_tree* b) { if (a == b) return true; if (!a || !b) return false; - if (a->value != b->value) return false; + if (a->atom != b->atom) return false; + if (a->atom) return a->value == b->value; return matches(a->left, b->left) && matches(a->right, b->right); } @@ -252,7 +289,122 @@ def main [ ] +app: foo: 34 35 36 +//: for the following unit tests we'll do the work of the transform by hand + +:(before "End Unit Tests") +void test_container_sizes() { + // a container we don't have the size for + reagent r("x:point"); + CHECK(!contains_key(Container_metadata, r.type)); + // scan + compute_container_sizes(r); + // the reagent we scanned knows its size + CHECK_EQ(r.metadata.size, 2); + // the global table also knows its size + CHECK(contains_key(Container_metadata, r.type)); + CHECK_EQ(get(Container_metadata, r.type).size, 2); +} + +void test_container_sizes_nested() { + // a container we don't have the size for + reagent r("x:point-number"); + CHECK(!contains_key(Container_metadata, r.type)); + // scan + compute_container_sizes(r); + // the reagent we scanned knows its size + CHECK_EQ(r.metadata.size, 3); + // the global table also knows its size + CHECK(contains_key(Container_metadata, r.type)); + CHECK_EQ(get(Container_metadata, r.type).size, 3); +} + +void test_container_sizes_recursive() { + // define a container containing an address to itself + run("container foo [\n" + " x:number\n" + " y:address:foo\n" + "]\n"); + reagent r("x:foo"); + compute_container_sizes(r); + CHECK_EQ(r.metadata.size, 2); +} + +void test_container_sizes_from_address() { + // a container we don't have the size for + reagent container("x:point"); + CHECK(!contains_key(Container_metadata, container.type)); + // scanning an address to the container precomputes the size of the container + reagent r("x:address:point"); + compute_container_sizes(r); + CHECK(contains_key(Container_metadata, container.type)); + CHECK_EQ(get(Container_metadata, container.type).size, 2); +} + +void test_container_sizes_from_array() { + // a container we don't have the size for + reagent container("x:point"); + CHECK(!contains_key(Container_metadata, container.type)); + // scanning an array of the container precomputes the size of the container + reagent r("x:array:point"); + compute_container_sizes(r); + CHECK(contains_key(Container_metadata, container.type)); + CHECK_EQ(get(Container_metadata, container.type).size, 2); +} + +void test_container_sizes_from_address_to_array() { + // a container we don't have the size for + reagent container("x:point"); + CHECK(!contains_key(Container_metadata, container.type)); + // scanning an address to an array of the container precomputes the size of the container + reagent r("x:address:array:point"); + compute_container_sizes(r); + CHECK(contains_key(Container_metadata, container.type)); + CHECK_EQ(get(Container_metadata, container.type).size, 2); +} + +void test_container_sizes_from_static_array() { + // a container we don't have the size for + reagent container("x:point"); + int old_size = SIZE(Container_metadata); + // scanning an address to an array of the container precomputes the size of the container + reagent r("x:array:point:10"); + compute_container_sizes(r); + CHECK(contains_key(Container_metadata, container.type)); + CHECK_EQ(get(Container_metadata, container.type).size, 2); + // no non-container types precomputed + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); +} + +void test_container_sizes_from_address_to_static_array() { + // a container we don't have the size for + reagent container("x:point"); + int old_size = SIZE(Container_metadata); + // scanning an address to an array of the container precomputes the size of the container + reagent r("x:address:array:point:10"); + compute_container_sizes(r); + CHECK(contains_key(Container_metadata, container.type)); + CHECK_EQ(get(Container_metadata, container.type).size, 2); + // no non-container types precomputed + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); +} + +void test_container_sizes_from_repeated_address_and_array_types() { + // a container we don't have the size for + reagent container("x:point"); + int old_size = SIZE(Container_metadata); + // scanning repeated address and array types modifying the container precomputes the size of the container + reagent r("x:address:array:address:array:point:10"); + compute_container_sizes(r); + CHECK(contains_key(Container_metadata, container.type)); + CHECK_EQ(get(Container_metadata, container.type).size, 2); + // no non-container types precomputed + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); +} + //:: To access elements of a container, use 'get' +//: 'get' takes a 'base' container and an 'offset' into it and returns the +//: appropriate element of the container value. + :(scenario get) def main [ 12:number <- copy 34 @@ -273,11 +425,15 @@ case GET: { } reagent/*copy*/ base = inst.ingredients.at(0); // new copy for every invocation // Update GET base in Check - if (!base.type || !base.type->value || !contains_key(Type, base.type->value) || get(Type, base.type->value).kind != CONTAINER) { + if (!base.type) { + raise << maybe(get(Recipe, r).name) << "first ingredient of 'get' should be a container, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); + break; + } + const type_tree* base_root_type = base.type->atom ? base.type : base.type->left; + if (!base_root_type->atom || base_root_type->value == 0 || !contains_key(Type, base_root_type->value) || get(Type, base_root_type->value).kind != CONTAINER) { raise << maybe(get(Recipe, r).name) << "first ingredient of 'get' should be a container, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); break; } - type_ordinal base_type = base.type->value; const reagent& offset = inst.ingredients.at(1); if (!is_literal(offset) || !is_mu_scalar(offset)) { raise << maybe(get(Recipe, r).name) << "second ingredient of 'get' should have type 'offset', but got '" << inst.ingredients.at(1).original_string << "'\n" << end(); @@ -288,14 +444,14 @@ case GET: { offset_value = to_integer(offset.name); else offset_value = offset.value; - if (offset_value < 0 || offset_value >= SIZE(get(Type, base_type).elements)) { - raise << maybe(get(Recipe, r).name) << "invalid offset '" << offset_value << "' for '" << get(Type, base_type).name << "'\n" << end(); + if (offset_value < 0 || offset_value >= SIZE(get(Type, base_root_type->value).elements)) { + raise << maybe(get(Recipe, r).name) << "invalid offset '" << offset_value << "' for '" << get(Type, base_root_type->value).name << "'\n" << end(); break; } if (inst.products.empty()) break; reagent/*copy*/ product = inst.products.at(0); // Update GET product in Check - const reagent/*copy*/ element = element_type(base.type, offset_value); + const reagent/*copy*/ element = element_type(base.type, offset_value); // not just base_root_type because later layers will introduce compound types if (!types_coercible(product, element)) { raise << maybe(get(Recipe, r).name) << "'get " << base.original_string << ", " << offset.original_string << "' should write to " << names_to_string_without_quotes(element.type) << " but '" << product.name << "' has type " << names_to_string_without_quotes(product.type) << '\n' << end(); break; @@ -311,13 +467,13 @@ case GET: { raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << to_original_string(current_instruction()) << "'\n" << end(); break; } - type_ordinal base_type = base.type->value; + const type_tree* base_root_type = root_type(base.type); int offset = ingredients.at(1).at(0); - if (offset < 0 || offset >= SIZE(get(Type, base_type).elements)) break; // copied from Check above + if (offset < 0 || offset >= SIZE(get(Type, base_root_type->value).elements)) break; // copied from Check above assert(base.metadata.size); int src = base_address + base.metadata.offset.at(offset); trace(9998, "run") << "address to copy is " << src << end(); - reagent/*copy*/ element = element_type(base.type, offset); + reagent/*copy*/ element = element_type(base.type, offset); // not just base_root_type because later layers will introduce compound types element.set_value(src); trace(9998, "run") << "its type is " << names_to_string(element.type) << end(); // Read element @@ -328,9 +484,10 @@ case GET: { :(code) const reagent element_type(const type_tree* type, int offset_value) { assert(offset_value >= 0); - assert(contains_key(Type, type->value)); - assert(!get(Type, type->value).name.empty()); - const type_info& info = get(Type, type->value); + const type_tree* root = root_type(type); + assert(contains_key(Type, root->value)); + assert(!get(Type, root->value).name.empty()); + const type_info& info = get(Type, root->value); assert(info.kind == CONTAINER); if (offset_value >= SIZE(info.elements)) return reagent(); // error handled elsewhere reagent/*copy*/ element = info.elements.at(offset_value); @@ -411,11 +568,15 @@ case PUT: { } reagent/*copy*/ base = inst.ingredients.at(0); // Update PUT base in Check - if (!base.type || !base.type->value || !contains_key(Type, base.type->value) || get(Type, base.type->value).kind != CONTAINER) { + if (!base.type) { + raise << maybe(get(Recipe, r).name) << "first ingredient of 'put' should be a container, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); + break; + } + const type_tree* base_root_type = base.type->atom ? base.type : base.type->left; + if (!base_root_type->atom || base_root_type->value == 0 || !contains_key(Type, base_root_type->value) || get(Type, base_root_type->value).kind != CONTAINER) { raise << maybe(get(Recipe, r).name) << "first ingredient of 'put' should be a container, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); break; } - type_ordinal base_type = base.type->value; reagent/*copy*/ offset = inst.ingredients.at(1); // Update PUT offset in Check if (!is_literal(offset) || !is_mu_scalar(offset)) { @@ -425,8 +586,8 @@ case PUT: { int offset_value = 0; if (is_integer(offset.name)) { // later layers permit non-integer offsets offset_value = to_integer(offset.name); - if (offset_value < 0 || offset_value >= SIZE(get(Type, base_type).elements)) { - raise << maybe(get(Recipe, r).name) << "invalid offset '" << offset_value << "' for '" << get(Type, base_type).name << "'\n" << end(); + if (offset_value < 0 || offset_value >= SIZE(get(Type, base_root_type->value).elements)) { + raise << maybe(get(Recipe, r).name) << "invalid offset '" << offset_value << "' for '" << get(Type, base_root_type->value).name << "'\n" << end(); break; } } @@ -434,7 +595,7 @@ case PUT: { offset_value = offset.value; } const reagent& value = inst.ingredients.at(2); - const reagent& element = element_type(base.type, offset_value); + const reagent& element = element_type(base.type, offset_value); // not just base_root_type because later layers will introduce compound types if (!types_coercible(element, value)) { raise << maybe(get(Recipe, r).name) << "'put " << base.original_string << ", " << offset.original_string << "' should write to " << names_to_string_without_quotes(element.type) << " but '" << value.name << "' has type " << names_to_string_without_quotes(value.type) << '\n' << end(); break; @@ -456,9 +617,9 @@ case PUT: { raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << to_original_string(current_instruction()) << "'\n" << end(); break; } - type_ordinal base_type = base.type->value; + const type_tree* base_root_type = root_type(base.type); int offset = ingredients.at(1).at(0); - if (offset < 0 || offset >= SIZE(get(Type, base_type).elements)) break; // copied from Check above + if (offset < 0 || offset >= SIZE(get(Type, base_root_type->value).elements)) break; // copied from Check above int address = base_address + base.metadata.offset.at(offset); trace(9998, "run") << "address to copy to is " << address << end(); // optimization: directly write the element rather than updating 'product' @@ -592,21 +753,23 @@ void insert_container(const string& command, kind_of_type kind, istream& in) { void replace_unknown_types_with_unique_ordinals(type_tree* type, const type_info& info) { if (!type) return; - if (!type->name.empty()) { - if (contains_key(Type_ordinal, type->name)) { - type->value = get(Type_ordinal, type->name); - } - else if (is_integer(type->name)) { // sometimes types will contain non-type tags, like numbers for the size of an array - type->value = 0; - } - // End insert_container Special-cases - else if (type->name != "->") { // used in recipe types - put(Type_ordinal, type->name, Next_type_ordinal++); - type->value = get(Type_ordinal, type->name); - } + if (!type->atom) { + replace_unknown_types_with_unique_ordinals(type->left, info); + replace_unknown_types_with_unique_ordinals(type->right, info); + return; + } + assert(!type->name.empty()); + if (contains_key(Type_ordinal, type->name)) { + type->value = get(Type_ordinal, type->name); + } + else if (is_integer(type->name)) { // sometimes types will contain non-type tags, like numbers for the size of an array + type->value = 0; + } + // End insert_container Special-cases + else if (type->name != "->") { // used in recipe types + put(Type_ordinal, type->name, Next_type_ordinal++); + type->value = get(Type_ordinal, type->name); } - replace_unknown_types_with_unique_ordinals(type->left, info); - replace_unknown_types_with_unique_ordinals(type->right, info); } void skip_bracket(istream& in, string message) { @@ -687,6 +850,11 @@ void check_or_set_invalid_types(const recipe_ordinal r) { void check_or_set_invalid_types(type_tree* type, const string& block, const string& name) { if (!type) return; // will throw a more precise error elsewhere // End Container Type Checks + if (!type->atom) { + check_or_set_invalid_types(type->left, block, name); + check_or_set_invalid_types(type->right, block, name); + return; + } if (type->value == 0) return; if (!contains_key(Type, type->value)) { assert(!type->name.empty()); @@ -695,8 +863,6 @@ void check_or_set_invalid_types(type_tree* type, const string& block, const stri else raise << block << "unknown type " << type->name << " in " << name << '\n' << end(); } - check_or_set_invalid_types(type->left, block, name); - check_or_set_invalid_types(type->right, block, name); } :(scenario container_unknown_field) @@ -738,10 +904,13 @@ void check_container_field_types() { void check_invalid_types(const type_tree* type, const string& block, const string& name) { if (!type) return; // will throw a more precise error elsewhere + if (!type->atom) { + check_invalid_types(type->left, block, name); + check_invalid_types(type->right, block, name); + return; + } if (type->value != 0) { // value 0 = compound types (layer parse_tree) or type ingredients (layer shape_shifting_container) if (!contains_key(Type, type->value)) raise << block << "unknown type in " << name << '\n' << end(); } - check_invalid_types(type->left, block, name); - check_invalid_types(type->right, block, name); } diff --git a/031merge.cc b/031merge.cc index 21c036c8..f8f07c1a 100644 --- a/031merge.cc +++ b/031merge.cc @@ -124,12 +124,13 @@ void check_merge_calls(const recipe_ordinal r) { } reagent/*copy*/ product = inst.products.at(0); // Update product While Type-checking Merge - type_ordinal product_type = product.type->value; - if (product_type == 0 || !contains_key(Type, product_type)) { + const type_tree* product_base_type = product.type->atom ? product.type : product.type->left; + assert(product_base_type->atom); + if (product_base_type->value == 0 || !contains_key(Type, product_base_type->value)) { raise << maybe(caller.name) << "'merge' should yield a container in '" << inst.original_string << "'\n" << end(); continue; } - const type_info& info = get(Type, product_type); + const type_info& info = get(Type, product_base_type->value); if (info.kind != CONTAINER && info.kind != EXCLUSIVE_CONTAINER) { raise << maybe(caller.name) << "'merge' should yield a container in '" << inst.original_string << "'\n" << end(); continue; @@ -151,7 +152,9 @@ void check_merge_call(const vector<reagent>& ingredients, const reagent& product } reagent& container = state.data.top().container; if (!container.type) return; // error handled elsewhere - type_info& container_info = get(Type, container.type->value); + const type_tree* top_root_type = container.type->atom ? container.type : container.type->left; + assert(top_root_type->atom); + type_info& container_info = get(Type, top_root_type->value); switch (container_info.kind) { case CONTAINER: { // degenerate case: merge with the same type always succeeds @@ -163,7 +166,7 @@ void check_merge_call(const vector<reagent>& ingredients, const reagent& product if (types_coercible(expected_ingredient, ingredients.at(ingredient_index))) { ++ingredient_index; ++state.data.top().container_element_index; - while (state.data.top().container_element_index >= SIZE(get(Type, state.data.top().container.type->value).elements)) { + while (state.data.top().container_element_index >= SIZE(get(Type, root_type(state.data.top().container.type)->value).elements)) { state.data.pop(); if (state.data.empty()) { if (ingredient_index < SIZE(ingredients)) @@ -198,7 +201,7 @@ void check_merge_call(const vector<reagent>& ingredients, const reagent& product return; } ++state.data.top().container_element_index; - } while (state.data.top().container_element_index >= SIZE(get(Type, state.data.top().container.type->value).elements)); + } while (state.data.top().container_element_index >= SIZE(get(Type, root_type(state.data.top().container.type)->value).elements)); } } } @@ -216,4 +219,3 @@ def main [ :(before "End Includes") #include <stack> using std::stack; - diff --git a/032array.cc b/032array.cc index 0cb27bd3..f8c26234 100644 --- a/032array.cc +++ b/032array.cc @@ -34,11 +34,14 @@ case CREATE_ARRAY: { break; } // 'create-array' will need to check properties rather than types - if (!product.type->right->right) { + type_tree* array_length_from_type = product.type->right->right; + if (!array_length_from_type) { raise << maybe(get(Recipe, r).name) << "create array of what size? '" << inst.original_string << "'\n" << end(); break; } - if (!is_integer(product.type->right->right->name)) { + if (!product.type->right->right->atom) + array_length_from_type = array_length_from_type->left; + if (!is_integer(array_length_from_type->name)) { raise << maybe(get(Recipe, r).name) << "'create-array' product should specify size of array after its element type, but got '" << product.type->right->right->name << "'\n" << end(); break; } @@ -49,7 +52,10 @@ case CREATE_ARRAY: { reagent/*copy*/ product = current_instruction().products.at(0); // Update CREATE_ARRAY product in Run int base_address = product.value; - int array_length = to_integer(product.type->right->right->name); + type_tree* array_length_from_type = product.type->right->right; + if (!product.type->right->right->atom) + array_length_from_type = array_length_from_type->left; + int array_length = to_integer(array_length_from_type->name); // initialize array length, so that size_of will work trace(9999, "mem") << "storing " << array_length << " in location " << base_address << end(); put(Memory, base_address, array_length); // in array elements @@ -93,7 +99,7 @@ def main [ +app: foo: 3 14 15 16 :(before "End size_of(reagent r) Cases") -if (r.type && r.type->value == get(Type_ordinal, "array")) { +if (!r.type->atom && r.type->left->atom && r.type->left->value == get(Type_ordinal, "array")) { if (!r.type->right) { raise << maybe(current_recipe_name()) << "'" << r.original_string << "' is an array of what?\n" << end(); return 1; @@ -107,7 +113,7 @@ if (r.type && r.type->value == get(Type_ordinal, "array")) { //: disable the size mismatch check for arrays since the destination array //: need not be initialized :(before "End size_mismatch(x) Cases") -if (x.type && x.type->value == get(Type_ordinal, "array")) return false; +if (x.type && !x.type->atom && x.type->left->value == get(Type_ordinal, "array")) return false; //: arrays are disallowed inside containers unless their length is fixed in //: advance @@ -147,12 +153,16 @@ def main [ :(before "End Load Container Element Definition") { const type_tree* type = info.elements.back().type; - if (type && type->name == "array") { + if (type && type->atom && type->name == "array") { + raise << "container '" << name << "' doesn't specify type of array elements for '" << info.elements.back().name << "'\n" << end(); + continue; + } + if (type && !type->atom && type->left->atom && type->left->name == "array") { if (!type->right) { raise << "container '" << name << "' doesn't specify type of array elements for '" << info.elements.back().name << "'\n" << end(); continue; } - if (!type->right->right) { // array has no length + if (type->right->atom) { // array has no length raise << "container '" << name << "' cannot determine size of element '" << info.elements.back().name << "'\n" << end(); continue; } @@ -182,6 +192,16 @@ def main [ ] +mem: storing 14 in location 5 +:(scenario index_compound_element) +def main [ + {1: (array (address number) 3)} <- create-array + 2:number <- copy 14 + 3:number <- copy 15 + 4:number <- copy 16 + 5:address:number <- index {1: (array (address number) 3)}, 0 +] ++mem: storing 14 in location 5 + :(scenario index_direct_offset) def main [ 1:array:number:3 <- create-array @@ -246,7 +266,7 @@ case INDEX: { type_tree* element_type = copy_array_element(base.type); int src = base_address + 1 + index_val.at(0)*size_of(element_type); trace(9998, "run") << "address to copy is " << src << end(); - trace(9998, "run") << "its type is " << get(Type, element_type->value).name << end(); + trace(9998, "run") << "its type is " << to_string(element_type) << end(); reagent element; element.set_value(src); element.type = element_type; @@ -257,19 +277,15 @@ case INDEX: { :(code) type_tree* copy_array_element(const type_tree* type) { - if (type->right->left) { - assert(!type->right->left->left); - return new type_tree(*type->right->left); - } assert(type->right); - // array:<type>:<size>? return just <type> - if (type->right->right && is_integer(type->right->right->name)) - return new type_tree(type->right->name, type->right->value); // snip type->right->right + // hack: don't require parens for either array:number:3 array:address:number + if (!type->right->atom && type->right->right && type->right->right->atom && is_integer(type->right->right->name)) + return new type_tree(*type->right->left); return new type_tree(*type->right); } int array_length(const reagent& x) { - if (x.type->right->right && !x.type->right->right->right // exactly 3 types + if (!x.type->atom && !x.type->right->atom && x.type->right->right->atom // exactly 3 types && is_integer(x.type->right->right->name)) { // third 'type' is a number // get size from type return to_integer(x.type->right->right->name); diff --git a/033exclusive_container.cc b/033exclusive_container.cc index ff384de5..77968966 100644 --- a/033exclusive_container.cc +++ b/033exclusive_container.cc @@ -35,22 +35,27 @@ if (t.kind == EXCLUSIVE_CONTAINER) { // Compute size_of Exclusive Container return get(Container_metadata, type).size; } -:(before "End compute_container_sizes Cases") +:(before "End compute_container_sizes Atom Cases") if (info.kind == EXCLUSIVE_CONTAINER) { + compute_exclusive_container_sizes(info, type, pending_metadata); +} + +:(code) +void compute_exclusive_container_sizes(const type_info& exclusive_container_info, const type_tree* full_type, set<string>& pending_metadata) { // size of an exclusive container is the size of its largest variant // (So, like containers, it can only contain arrays if they're static and // include their length in the type.) container_metadata metadata; - for (int i = 0; i < SIZE(info.elements); ++i) { - reagent/*copy*/ element = info.elements.at(i); - // Compute Exclusive Container Size(element) + for (int i = 0; i < SIZE(exclusive_container_info.elements); ++i) { + reagent/*copy*/ element = exclusive_container_info.elements.at(i); + // Compute Exclusive Container Size(element, full_type) compute_container_sizes(element.type, pending_metadata); int variant_size = size_of(element); if (variant_size > metadata.size) metadata.size = variant_size; } // ...+1 for its tag. ++metadata.size; - Container_metadata.push_back(pair<type_tree*, container_metadata>(new type_tree(*type), metadata)); + Container_metadata.push_back(pair<type_tree*, container_metadata>(new type_tree(*full_type), metadata)); } //:: To access variants of an exclusive container, use 'maybe-convert'. @@ -100,7 +105,12 @@ case MAYBE_CONVERT: { } reagent/*copy*/ base = inst.ingredients.at(0); // Update MAYBE_CONVERT base in Check - if (!base.type || !base.type->value || get(Type, base.type->value).kind != EXCLUSIVE_CONTAINER) { + if (!base.type) { + raise << maybe(caller.name) << "first ingredient of 'maybe-convert' should be an exclusive-container, but got '" << base.original_string << "'\n" << end(); + break; + } + const type_tree* root_type = base.type->atom ? base.type : base.type->left; + if (!root_type->atom || root_type->value == 0 || !contains_key(Type, root_type->value) || get(Type, root_type->value).kind != EXCLUSIVE_CONTAINER) { raise << maybe(caller.name) << "first ingredient of 'maybe-convert' should be an exclusive-container, but got '" << base.original_string << "'\n" << end(); break; } @@ -117,7 +127,7 @@ case MAYBE_CONVERT: { // Update MAYBE_CONVERT product in Check reagent& offset = inst.ingredients.at(1); populate_value(offset); - if (offset.value >= SIZE(get(Type, base.type->value).elements)) { + if (offset.value >= SIZE(get(Type, root_type->value).elements)) { raise << maybe(caller.name) << "invalid tag " << offset.value << " in '" << inst.original_string << '\n' << end(); break; } @@ -176,9 +186,10 @@ const reagent variant_type(const reagent& base, int tag) { const reagent variant_type(const type_tree* type, int tag) { assert(tag >= 0); - assert(contains_key(Type, type->value)); - assert(!get(Type, type->value).name.empty()); - const type_info& info = get(Type, type->value); + const type_tree* root_type = type->atom ? type : type->left; + assert(contains_key(Type, root_type->value)); + assert(!get(Type, root_type->value).name.empty()); + const type_info& info = get(Type, root_type->value); assert(info.kind == EXCLUSIVE_CONTAINER); reagent/*copy*/ element = info.elements.at(tag); // End variant_type Special-cases @@ -430,7 +441,9 @@ if (current_step_index() < SIZE(Current_routine->steps()) && current_instruction().products.at(0).type) { reagent/*copy*/ x = current_instruction().products.at(0); // Update size_mismatch Check for MERGE(x) - if (get(Type, x.type->value).kind == EXCLUSIVE_CONTAINER) + const type_tree* root_type = x.type->atom ? x.type : x.type->left; + assert(root_type->atom); + if (get(Type, root_type->value).kind == EXCLUSIVE_CONTAINER) return size_of(x) < SIZE(data); } diff --git a/034address.cc b/034address.cc index aa47cc1c..efa53c72 100644 --- a/034address.cc +++ b/034address.cc @@ -174,12 +174,13 @@ case NEW: { bool product_of_new_is_valid(const instruction& inst) { reagent/*copy*/ product = inst.products.at(0); // Update NEW product in Check - if (!product.type || product.type->value != get(Type_ordinal, "address")) + if (!product.type || product.type->atom || product.type->left->value != get(Type_ordinal, "address")) return false; drop_from_type(product, "address"); if (SIZE(inst.ingredients) > 1) { // array allocation - if (!product.type || product.type->value != get(Type_ordinal, "array")) return false; + if (!product.type || product.type->atom || product.type->left->value != get(Type_ordinal, "array")) + return false; drop_from_type(product, "array"); } reagent/*copy*/ expected_product("x:"+inst.ingredients.at(0).name); @@ -193,7 +194,8 @@ bool product_of_new_is_valid(const instruction& inst) { } void drop_from_type(reagent& r, string expected_type) { - if (r.type->name != expected_type) { + assert(!r.type->atom); + if (r.type->left->name != expected_type) { raise << "can't drop2 " << expected_type << " from '" << to_string(r) << "'\n" << end(); return; } diff --git a/035lookup.cc b/035lookup.cc index 91314aee..7dbf301b 100644 --- a/035lookup.cc +++ b/035lookup.cc @@ -76,7 +76,7 @@ void canonize(reagent& x) { } void lookup_memory(reagent& x) { - if (!x.type || x.type->value != get(Type_ordinal, "address")) { + if (!x.type || x.type->atom || x.type->left->value != get(Type_ordinal, "address")) { raise << maybe(current_recipe_name()) << "tried to /lookup '" << x.original_string << "' but it isn't an address\n" << end(); return; } @@ -149,15 +149,15 @@ canonize_type(rhs); :(before "Compute Container Size(reagent rcopy)") if (!canonize_type(rcopy)) return; -:(before "Compute Container Size(element)") +:(before "Compute Container Size(element, full_type)") assert(!has_property(element, "lookup")); -:(before "Compute Exclusive Container Size(element)") +:(before "Compute Exclusive Container Size(element, full_type)") assert(!has_property(element, "lookup")); :(code) bool canonize_type(reagent& r) { while (has_property(r, "lookup")) { - if (!r.type || r.type->value != get(Type_ordinal, "address")) { + if (!r.type || r.type->atom || !r.type->left || !r.type->left->atom || r.type->left->value != get(Type_ordinal, "address")) { raise << "can't lookup non-address: '" << to_string(r) << "': '" << to_string(r.type) << "'\n" << end(); return false; } diff --git a/036refcount.cc b/036refcount.cc index 05ddf47e..c983290f 100644 --- a/036refcount.cc +++ b/036refcount.cc @@ -279,7 +279,6 @@ bool operator<(const address_element_info& a, const address_element_info& b) { //: populate metadata.address in a separate transform, because it requires //: already knowing the sizes of all types -//: sometimes does unnecessary work for meaningless types :(after "Transform.push_back(compute_container_sizes)") Transform.push_back(compute_container_address_offsets); @@ -296,46 +295,70 @@ void compute_container_address_offsets(const recipe_ordinal r) { compute_container_address_offsets(inst.products.at(i)); } } + void compute_container_address_offsets(reagent& r) { if (is_literal(r) || is_dummy(r)) return; compute_container_address_offsets(r.type); if (contains_key(Container_metadata, r.type)) r.metadata = get(Container_metadata, r.type); } -void compute_container_address_offsets(type_tree* type) { + +// the recursive structure of this function needs to exactly match +// compute_container_sizes +void compute_container_address_offsets(const type_tree* type) { if (!type) return; - // might be needed by later layers, but we haven't found a need for it yet -//? if (type->left) compute_container_address_offsets(type->left); - if (type->right) compute_container_address_offsets(type->right); - if (!contains_key(Type, type->value)) return; // error raised elsewhere - type_info& info = get(Type, type->value); + if (!type->atom) { + assert(type->left->atom); + if (type->left->name == "address") { + compute_container_address_offsets(type->right); + } + else if (type->left->name == "array") { + const type_tree* element_type = type->right; + // hack: support both array:number:3 and array:address:number + if (!element_type->atom && element_type->right && element_type->right->atom && is_integer(element_type->right->name)) + element_type = element_type->left; + compute_container_address_offsets(element_type); + } + // End compute_container_address_offsets Non-atom Cases + } + if (!contains_key(Type, root_type(type)->value)) return; // error raised elsewhere + type_info& info = get(Type, root_type(type)->value); if (info.kind == CONTAINER) { - container_metadata& metadata = get(Container_metadata, type); - if (!metadata.address.empty()) return; - trace(9994, "transform") << "compute address offsets for container " << info.name << end(); - append_addresses(0, type, metadata.address, set<tag_condition_info>()); + compute_container_address_offsets(info, type); } if (info.kind == EXCLUSIVE_CONTAINER) { - container_metadata& metadata = get(Container_metadata, type); - trace(9994, "transform") << "compute address offsets for exclusive container " << info.name << end(); - for (int tag = 0; tag < SIZE(info.elements); ++tag) { - set<tag_condition_info> key; - key.insert(tag_condition_info(/*tag is at offset*/0, tag)); - append_addresses(/*skip tag offset*/1, variant_type(type, tag).type, metadata.address, key); - } + compute_exclusive_container_address_offsets(info, type); + } +} + +void compute_container_address_offsets(const type_info& container_info, const type_tree* full_type) { + container_metadata& metadata = get(Container_metadata, full_type); + if (!metadata.address.empty()) return; + trace(9994, "transform") << "compute address offsets for container " << container_info.name << end(); + append_addresses(0, full_type, metadata.address, set<tag_condition_info>()); +} + +void compute_exclusive_container_address_offsets(const type_info& exclusive_container_info, const type_tree* full_type) { + container_metadata& metadata = get(Container_metadata, full_type); + trace(9994, "transform") << "compute address offsets for exclusive container " << exclusive_container_info.name << end(); + for (int tag = 0; tag < SIZE(exclusive_container_info.elements); ++tag) { + set<tag_condition_info> key; + key.insert(tag_condition_info(/*tag is at offset*/0, tag)); + append_addresses(/*skip tag offset*/1, variant_type(full_type, tag).type, metadata.address, key); } } void append_addresses(int base_offset, const type_tree* type, map<set<tag_condition_info>, set<address_element_info> >& out, const set<tag_condition_info>& key) { - const type_info& info = get(Type, type->value); - if (type->name == "address") { + if (is_mu_address(type)) { get_or_insert(out, key).insert(address_element_info(base_offset, new type_tree(*type->right))); return; } + const type_tree* root = root_type(type); + const type_info& info = get(Type, root->value); if (info.kind == CONTAINER) { for (int curr_index = 0, curr_offset = base_offset; curr_index < SIZE(info.elements); ++curr_index) { - trace(9993, "transform") << "checking container " << type->name << ", element " << curr_index << end(); - reagent/*copy*/ element = element_type(type, curr_index); + trace(9993, "transform") << "checking container " << root->name << ", element " << curr_index << end(); + reagent/*copy*/ element = element_type(type, curr_index); // not root // Compute Container Address Offset(element) if (is_mu_address(element)) { trace(9993, "transform") << "address at offset " << curr_offset << end(); @@ -347,7 +370,8 @@ void append_addresses(int base_offset, const type_tree* type, map<set<tag_condit curr_offset += size_of(element); } else if (is_mu_exclusive_container(element)) { - const type_info& element_info = get(Type, element.type->value); + const type_tree* element_root_type = root_type(element.type); + const type_info& element_info = get(Type, element_root_type->value); for (int tag = 0; tag < SIZE(element_info.elements); ++tag) { set<tag_condition_info> new_key = key; new_key.insert(tag_condition_info(curr_offset, tag)); @@ -378,6 +402,280 @@ int payload_size(const type_tree* type) { return size_of(type->right) + /*refcount*/1; } +//: for the following unit tests we'll do the work of the transform by hand + +:(before "End Unit Tests") +void test_container_address_offsets_empty() { + int old_size = SIZE(Container_metadata); + // define a container with no addresses + reagent r("x:point"); + compute_container_sizes(r); // need to first pre-populate the metadata + // scan + compute_container_address_offsets(r); + // global metadata contains just the entry for foo + // no entries for non-container types or other junk + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // the reagent we scanned knows it has no addresses + CHECK(r.metadata.address.empty()); + // the global table contains an identical entry + CHECK(contains_key(Container_metadata, r.type)); + CHECK(get(Container_metadata, r.type).address.empty()); + // compute_container_address_offsets creates no new entries + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); +} + +void test_container_address_offsets() { + int old_size = SIZE(Container_metadata); + // define a container with an address at offset 0 that we have the size for + run("container foo [\n" + " x:address:number\n" + "]\n"); + reagent r("x:foo"); + compute_container_sizes(r); // need to first pre-populate the metadata + // scan + compute_container_address_offsets(r); + // global metadata contains just the entry for foo + // no entries for non-container types or other junk + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // the reagent we scanned knows it has an address at offset 0 + CHECK_EQ(SIZE(r.metadata.address), 1); + CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); + const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>()); // unconditional for containers + CHECK_EQ(SIZE(address_offsets), 1); + CHECK_EQ(address_offsets.begin()->offset, 0); + CHECK(address_offsets.begin()->payload_type->atom); + CHECK_EQ(address_offsets.begin()->payload_type->name, "number"); + // the global table contains an identical entry + CHECK(contains_key(Container_metadata, r.type)); + const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>()); + CHECK_EQ(SIZE(address_offsets2), 1); + CHECK_EQ(address_offsets2.begin()->offset, 0); + CHECK(address_offsets2.begin()->payload_type->atom); + CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); + // compute_container_address_offsets creates no new entries + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); +} + +void test_container_address_offsets_2() { + int old_size = SIZE(Container_metadata); + // define a container with an address at offset 1 that we have the size for + run("container foo [\n" + " x:number\n" + " y:address:number\n" + "]\n"); + reagent r("x:foo"); + compute_container_sizes(r); // need to first pre-populate the metadata + // global metadata contains just the entry for foo + // no entries for non-container types or other junk + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // scan + compute_container_address_offsets(r); + // compute_container_address_offsets creates no new entries + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // the reagent we scanned knows it has an address at offset 1 + CHECK_EQ(SIZE(r.metadata.address), 1); + CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); + const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>()); + CHECK_EQ(SIZE(address_offsets), 1); + CHECK_EQ(address_offsets.begin()->offset, 1); // + CHECK(address_offsets.begin()->payload_type->atom); + CHECK_EQ(address_offsets.begin()->payload_type->name, "number"); + // the global table contains an identical entry + CHECK(contains_key(Container_metadata, r.type)); + const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>()); + CHECK_EQ(SIZE(address_offsets2), 1); + CHECK_EQ(address_offsets2.begin()->offset, 1); // + CHECK(address_offsets2.begin()->payload_type->atom); + CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); +} + +void test_container_address_offsets_nested() { + int old_size = SIZE(Container_metadata); + // define a container with a nested container containing an address + run("container foo [\n" + " x:address:number\n" + " y:number\n" + "]\n" + "container bar [\n" + " p:point\n" + " f:foo\n" // nested container containing address + "]\n"); + reagent r("x:bar"); + compute_container_sizes(r); // need to first pre-populate the metadata + // global metadata contains entries for bar and included types: point and foo + // no entries for non-container types or other junk + CHECK_EQ(SIZE(Container_metadata)-old_size, 3); + // scan + compute_container_address_offsets(r); + // the reagent we scanned knows it has an address at offset 2 + CHECK_EQ(SIZE(r.metadata.address), 1); + CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); + const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>()); + CHECK_EQ(SIZE(address_offsets), 1); + CHECK_EQ(address_offsets.begin()->offset, 2); // + CHECK(address_offsets.begin()->payload_type->atom); + CHECK_EQ(address_offsets.begin()->payload_type->name, "number"); + // the global table also knows its address offset + CHECK(contains_key(Container_metadata, r.type)); + const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>()); + CHECK_EQ(SIZE(address_offsets2), 1); + CHECK_EQ(address_offsets2.begin()->offset, 2); // + CHECK(address_offsets2.begin()->payload_type->atom); + CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); + // compute_container_address_offsets creates no new entries + CHECK_EQ(SIZE(Container_metadata)-old_size, 3); +} + +void test_container_address_offsets_from_address() { + int old_size = SIZE(Container_metadata); + // define a container with an address at offset 0 + run("container foo [\n" + " x:address:number\n" + "]\n"); + reagent r("x:address:foo"); + compute_container_sizes(r); // need to first pre-populate the metadata + // global metadata contains just the entry for foo + // no entries for non-container types or other junk + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // scan an address to the container + compute_container_address_offsets(r); + // compute_container_address_offsets creates no new entries + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // scanning precomputed metadata for the container + reagent container("x:foo"); + CHECK(contains_key(Container_metadata, container.type)); + const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); + CHECK_EQ(SIZE(address_offsets2), 1); + CHECK_EQ(address_offsets2.begin()->offset, 0); + CHECK(address_offsets2.begin()->payload_type->atom); + CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); +} + +void test_container_address_offsets_from_array() { + int old_size = SIZE(Container_metadata); + // define a container with an address at offset 0 + run("container foo [\n" + " x:address:number\n" + "]\n"); + reagent r("x:array:foo"); + compute_container_sizes(r); // need to first pre-populate the metadata + // global metadata contains just the entry for foo + // no entries for non-container types or other junk + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // scan an array of the container + compute_container_address_offsets(r); + // compute_container_address_offsets creates no new entries + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // scanning precomputed metadata for the container + reagent container("x:foo"); + CHECK(contains_key(Container_metadata, container.type)); + const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); + CHECK_EQ(SIZE(address_offsets2), 1); + CHECK_EQ(address_offsets2.begin()->offset, 0); + CHECK(address_offsets2.begin()->payload_type->atom); + CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); +} + +void test_container_address_offsets_from_address_to_array() { + int old_size = SIZE(Container_metadata); + // define a container with an address at offset 0 + run("container foo [\n" + " x:address:number\n" + "]\n"); + reagent r("x:address:array:foo"); + compute_container_sizes(r); // need to first pre-populate the metadata + // global metadata contains just the entry for foo + // no entries for non-container types or other junk + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // scan an address to an array of the container + compute_container_address_offsets(r); + // compute_container_address_offsets creates no new entries + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // scanning precomputed metadata for the container + reagent container("x:foo"); + CHECK(contains_key(Container_metadata, container.type)); + const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); + CHECK_EQ(SIZE(address_offsets2), 1); + CHECK_EQ(address_offsets2.begin()->offset, 0); + CHECK(address_offsets2.begin()->payload_type->atom); + CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); +} + +void test_container_address_offsets_from_static_array() { + int old_size = SIZE(Container_metadata); + // define a container with an address at offset 0 + run("container foo [\n" + " x:address:number\n" + "]\n"); + reagent r("x:array:foo:10"); + compute_container_sizes(r); // need to first pre-populate the metadata + // global metadata contains just the entry for foo + // no entries for non-container types or other junk + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // scan a static array of the container + compute_container_address_offsets(r); + // compute_container_address_offsets creates no new entries + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // scanning precomputed metadata for the container + reagent container("x:foo"); + CHECK(contains_key(Container_metadata, container.type)); + const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); + CHECK_EQ(SIZE(address_offsets2), 1); + CHECK_EQ(address_offsets2.begin()->offset, 0); + CHECK(address_offsets2.begin()->payload_type->atom); + CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); +} + +void test_container_address_offsets_from_address_to_static_array() { + int old_size = SIZE(Container_metadata); + // define a container with an address at offset 0 + run("container foo [\n" + " x:address:number\n" + "]\n"); + reagent r("x:address:array:foo:10"); + compute_container_sizes(r); // need to first pre-populate the metadata + // global metadata contains just the entry for foo + // no entries for non-container types or other junk + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // scan an address to a static array of the container + compute_container_address_offsets(r); + // compute_container_address_offsets creates no new entries + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // scanning precomputed metadata for the container + reagent container("x:foo"); + CHECK(contains_key(Container_metadata, container.type)); + const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); + CHECK_EQ(SIZE(address_offsets2), 1); + CHECK_EQ(address_offsets2.begin()->offset, 0); + CHECK(address_offsets2.begin()->payload_type->atom); + CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); +} + +void test_container_address_offsets_from_repeated_address_and_array_types() { + int old_size = SIZE(Container_metadata); + // define a container with an address at offset 0 + run("container foo [\n" + " x:address:number\n" + "]\n"); + // scan a deep nest of 'address' and 'array' types modifying a container + reagent r("x:address:array:address:address:array:foo:10"); + compute_container_sizes(r); // need to first pre-populate the metadata + // global metadata contains just the entry for foo + // no entries for non-container types or other junk + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + compute_container_address_offsets(r); + // compute_container_address_offsets creates no new entries + CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + // scanning precomputed metadata for the container + reagent container("x:foo"); + CHECK(contains_key(Container_metadata, container.type)); + const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); + CHECK_EQ(SIZE(address_offsets2), 1); + CHECK_EQ(address_offsets2.begin()->offset, 0); + CHECK(address_offsets2.begin()->payload_type->atom); + CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); +} + //: use metadata.address to update refcounts within containers, arrays and //: exclusive containers @@ -648,6 +946,7 @@ bool is_mu_container(const reagent& r) { } bool is_mu_container(const type_tree* type) { if (!type) return false; + // End is_mu_container(type) Special-cases if (type->value == 0) return false; type_info& info = get(Type, type->value); return info.kind == CONTAINER; @@ -658,6 +957,7 @@ bool is_mu_exclusive_container(const reagent& r) { } bool is_mu_exclusive_container(const type_tree* type) { if (!type) return false; + // End is_mu_exclusive_container(type) Special-cases if (type->value == 0) return false; type_info& info = get(Type, type->value); return info.kind == EXCLUSIVE_CONTAINER; diff --git a/037abandon.cc b/037abandon.cc index d34a493b..4c8121fb 100644 --- a/037abandon.cc +++ b/037abandon.cc @@ -31,7 +31,7 @@ void abandon(int address, const type_tree* payload_type, int payload_size) { //? ++Num_free; //? cerr << "abandon: " << size << '\n'; // decrement any contained refcounts - if (payload_type->name == "array") { + if (is_mu_array(payload_type)) { reagent element; element.type = copy_array_element(payload_type); int array_length = get_or_insert(Memory, address+/*skip refcount*/1); diff --git a/039location_array.cc b/039location_array.cc index 814fb922..e43717a5 100644 --- a/039location_array.cc +++ b/039location_array.cc @@ -12,15 +12,21 @@ case TO_LOCATION_ARRAY: { break; } :(code) -bool is_address_of_array_of_numbers(reagent/*copy*/ product) { - canonize_type(product); - if (!product.type || product.type->value != get(Type_ordinal, "address")) return false; - drop_from_type(product, "address"); - if (!product.type || product.type->value != get(Type_ordinal, "array")) return false; - drop_from_type(product, "array"); - if (!product.type || product.type->value != get(Type_ordinal, "number")) return false; - return true; +bool is_address_of_array_of_numbers(reagent/*copy*/ x) { + canonize_type(x); + if (!is_compound_type_starting_with(x.type, "address")) return false; + drop_from_type(x, "address"); + if (!is_compound_type_starting_with(x.type, "array")) return false; + drop_from_type(x, "array"); + return x.type && x.type->atom && x.type->value == get(Type_ordinal, "number"); } +bool is_compound_type_starting_with(const type_tree* type, const string& expected_name) { + if (!type) return false; + if (type->atom) return false; + if (!type->left->atom) return false; + return type->left->value == get(Type_ordinal, expected_name); +} + :(before "End Primitive Recipe Implementations") case TO_LOCATION_ARRAY: { int array_size = SIZE(ingredients.at(0)); diff --git a/042name.cc b/042name.cc index 2711b94d..c3e9e0ef 100644 --- a/042name.cc +++ b/042name.cc @@ -117,12 +117,10 @@ int lookup_name(const reagent& r, const recipe_ordinal default_recipe) { } type_ordinal skip_addresses(type_tree* type) { - type_ordinal address = get(Type_ordinal, "address"); - for (; type; type = type->right) { - if (type->value != address) - return type->value; - } - return -1; + while (type && is_compound_type_starting_with(type, "address")) + type = type->right; + if (!type) return -1; // error handled elsewhere + return root_type(type)->value; } int find_element_name(const type_ordinal t, const string& name, const string& recipe_name) { diff --git a/043space.cc b/043space.cc index 15325c3c..1c26cc2f 100644 --- a/043space.cc +++ b/043space.cc @@ -83,19 +83,15 @@ int address(int offset, int base) { :(after "Begin Preprocess write_memory(x, data)") if (x.name == "default-space") { - if (!scalar(data) - || !x.type - || x.type->value != get(Type_ordinal, "address") - || !x.type->right - || x.type->right->value != get(Type_ordinal, "array") - || !x.type->right->right - || x.type->right->right->value != get(Type_ordinal, "location") - || x.type->right->right->right) { + if (!scalar(data) || !is_space(x)) raise << maybe(current_recipe_name()) << "'default-space' should be of type address:array:location, but is " << to_string(x.type) << '\n' << end(); - } current_call().default_space = data.at(0); return; } +:(code) +bool is_space(const reagent& r) { + return is_address_of_array_of_numbers(r); +} :(scenario get_default_space) def main [ diff --git a/045closure_name.cc b/045closure_name.cc index 77389b3a..b14030a9 100644 --- a/045closure_name.cc +++ b/045closure_name.cc @@ -45,14 +45,7 @@ void collect_surrounding_spaces(const recipe_ordinal r) { for (int j = 0; j < SIZE(inst.products); ++j) { if (is_literal(inst.products.at(j))) continue; if (inst.products.at(j).name != "0") continue; - type_tree* type = inst.products.at(j).type; - if (!type - || type->value != get(Type_ordinal, "address") - || !type->right - || type->right->value != get(Type_ordinal, "array") - || !type->right->right - || type->right->right->value != get(Type_ordinal, "location") - || type->right->right->right) { + if (!is_space(inst.products.at(j))) { raise << "slot 0 should always have type address:array:location, but is '" << to_string(inst.products.at(j)) << "'\n" << end(); continue; } @@ -61,7 +54,7 @@ void collect_surrounding_spaces(const recipe_ordinal r) { raise << "slot 0 requires a /names property in recipe '" << get(Recipe, r).name << "'\n" << end(); continue; } - if (s->right) raise << "slot 0 should have a single value in /names, but got '" << to_string(inst.products.at(j)) << "'\n" << end(); + if (!s->atom) raise << "slot 0 should have a single value in /names, but got '" << to_string(inst.products.at(j)) << "'\n" << end(); const string& surrounding_recipe_name = s->value; if (surrounding_recipe_name.empty()) { raise << "slot 0 doesn't initialize its /names property in recipe '" << get(Recipe, r).name << "'\n" << end(); @@ -92,7 +85,7 @@ int lookup_name(const reagent& x, const recipe_ordinal default_recipe) { return Name[default_recipe][x.name]; } string_tree* p = property(x, "space"); - if (!p || p->right) raise << "/space property should have exactly one (non-negative integer) value\n" << end(); + if (!p || !p->atom) raise << "/space property should have exactly one (non-negative integer) value\n" << end(); int n = to_integer(p->value); assert(n >= 0); recipe_ordinal surrounding_recipe = lookup_surrounding_recipe(default_recipe, n); @@ -136,7 +129,7 @@ recipe_ordinal lookup_surrounding_recipe(const recipe_ordinal r, int n) { bool already_transformed(const reagent& r, const map<string, int>& names) { if (has_property(r, "space")) { string_tree* p = property(r, "space"); - if (!p || p->right) { + if (!p || !p->atom) { raise << "/space property should have exactly one (non-negative integer) value in '" << r.original_string << "'\n" << end(); return false; } diff --git a/046global.cc b/046global.cc index 8c57236a..8f36918a 100644 --- a/046global.cc +++ b/046global.cc @@ -42,16 +42,8 @@ int global_space; global_space = 0; :(after "Begin Preprocess write_memory(x, data)") if (x.name == "global-space") { - if (!scalar(data) - || !x.type - || x.type->value != get(Type_ordinal, "address") - || !x.type->right - || x.type->right->value != get(Type_ordinal, "array") - || !x.type->right->right - || x.type->right->right->value != get(Type_ordinal, "location") - || x.type->right->right->right) { + if (!scalar(data) || !is_space(x)) raise << maybe(current_recipe_name()) << "'global-space' should be of type address:array:location, but tried to write '" << to_string(x.type) << "'\n" << end(); - } if (Current_routine->global_space) raise << "routine already has a global-space; you can't over-write your globals" << end(); Current_routine->global_space = data.at(0); @@ -85,9 +77,6 @@ $error: 0 :(code) bool is_global(const reagent& x) { - for (int i = 0; i < SIZE(x.properties); ++i) { - if (x.properties.at(i).first == "space") - return x.properties.at(i).second && x.properties.at(i).second->value == "global"; - } - return false; + string_tree* s = property(x, "space"); + return s && s->atom && s->value == "global"; } diff --git a/047check_type_by_name.cc b/047check_type_by_name.cc index 7812044b..780392ad 100644 --- a/047check_type_by_name.cc +++ b/047check_type_by_name.cc @@ -54,7 +54,7 @@ void check_type(set<reagent>& known, const reagent& x, const recipe& caller) { raise << maybe(caller.name) << "'" << x.name << "' used with multiple types\n" << end(); return; } - if (x.type->name == "array") { + if (is_mu_array(x)) { if (!x.type->right) { raise << maybe(caller.name) << "'" << x.name << ": can't be just an array. What is it an array of?\n" << end(); return; diff --git a/050scenario.cc b/050scenario.cc index 7a2ea7a8..3c033c94 100644 --- a/050scenario.cc +++ b/050scenario.cc @@ -388,9 +388,7 @@ void check_memory(const string& s) { void check_type(const string& lhs, istream& in) { reagent x(lhs); - if (x.type->name == "array" - && x.type->right && x.type->right->name == "character" - && !x.type->right->right) { + if (is_mu_array(x.type) && is_mu_character(x.type->right)) { x.set_value(to_integer(x.name)); skip_whitespace_and_comments(in); string _assign = next_word(in); diff --git a/054static_dispatch.cc b/054static_dispatch.cc index ca2bcba9..29cb259f 100644 --- a/054static_dispatch.cc +++ b/054static_dispatch.cc @@ -310,9 +310,8 @@ bool all_header_reagents_strictly_match_except_literal_against_address_or_boolea } bool types_strictly_match_except_literal_against_address_or_boolean(const reagent& to, const reagent& from) { - if (is_literal(from) - && to.type && to.type->value == get(Type_ordinal, "boolean")) - return boolean_matches_literal(to, from); + if (is_literal(from) && is_mu_boolean(to)) + return from.name == "0" || from.name == "1"; return types_strictly_match_except_literal_zero_against_address(to, from); } diff --git a/055shape_shifting_container.cc b/055shape_shifting_container.cc index a539e34f..131028a5 100644 --- a/055shape_shifting_container.cc +++ b/055shape_shifting_container.cc @@ -1,5 +1,14 @@ //:: Container definitions can contain 'type ingredients' +//: pre-requisite: extend our notion of containers to not necessarily be +//: atomic types +:(before "End is_mu_container(type) Special-cases") +if (!type->atom) + return is_mu_container(root_type(type)); +:(before "End is_mu_exclusive_container(type) Special-cases") +if (!type->atom) + return is_mu_exclusive_container(root_type(type)); + :(scenario size_of_shape_shifting_container) container foo:_t [ x:_t @@ -267,10 +276,10 @@ def main [ :(before "End element_type Special-cases") replace_type_ingredients(element, type, info); -:(before "Compute Container Size(element)") -replace_type_ingredients(element, type, info); -:(before "Compute Exclusive Container Size(element)") -replace_type_ingredients(element, type, info); +:(before "Compute Container Size(element, full_type)") +replace_type_ingredients(element, full_type, container_info); +:(before "Compute Exclusive Container Size(element, full_type)") +replace_type_ingredients(element, full_type, exclusive_container_info); :(before "Compute Container Address Offset(element)") replace_type_ingredients(element, type, info); if (contains_type_ingredient(element)) return; // error raised elsewhere @@ -296,63 +305,42 @@ bool contains_type_ingredient(const reagent& x) { bool contains_type_ingredient(const type_tree* type) { if (!type) return false; - if (type->value >= START_TYPE_INGREDIENTS) return true; - assert(!is_type_ingredient_name(type->name)); + if (type->atom) return type->value >= START_TYPE_INGREDIENTS; return contains_type_ingredient(type->left) || contains_type_ingredient(type->right); } // replace all type_ingredients in element_type with corresponding elements of callsite_type -// todo: too complicated and likely incomplete; maybe avoid replacing in place? void replace_type_ingredients(type_tree* element_type, const type_tree* callsite_type, const type_info& container_info) { if (!callsite_type) return; // error but it's already been raised above if (!element_type) return; - - // A. recurse first to avoid nested replaces (which I can't reason about yet) - replace_type_ingredients(element_type->left, callsite_type, container_info); - replace_type_ingredients(element_type->right, callsite_type, container_info); + if (!element_type->atom) { + replace_type_ingredients(element_type->left, callsite_type, container_info); + replace_type_ingredients(element_type->right, callsite_type, container_info); + return; + } if (element_type->value < START_TYPE_INGREDIENTS) return; - const int type_ingredient_index = element_type->value-START_TYPE_INGREDIENTS; if (!has_nth_type(callsite_type, type_ingredient_index)) { raise << "illegal type " << names_to_string(callsite_type) << " seems to be missing a type ingredient or three\n" << end(); return; } + *element_type = *nth_type_ingredient(callsite_type, type_ingredient_index, container_info); +} - // B. replace the current location - const type_tree* replacement = NULL; - bool zig_left = false; - { - const type_tree* curr = callsite_type; - for (int i = 0; i < type_ingredient_index; ++i) - curr = curr->right; - if (curr && curr->left) { - replacement = curr->left; - zig_left = true; - } - else { - // We want foo:_t to be used like foo:number, which expands to {foo: number} - // rather than {foo: (number)} - // We'd also like to use it with multiple types: foo:address:number. - replacement = curr; - } - } - if (element_type->right && replacement->right && zig_left) { // ZERO confidence that this condition is accurate - element_type->name = ""; - element_type->value = 0; - element_type->left = new type_tree(*replacement); - } - else { - string old_name = element_type->name; - element_type->name = replacement->name; - element_type->value = replacement->value; - assert(!element_type->left); // since value is set - element_type->left = replacement->left ? new type_tree(*replacement->left) : NULL; - if (zig_left || final_type_ingredient(type_ingredient_index, container_info)) { - type_tree* old_right = element_type->right; - element_type->right = replacement->right ? new type_tree(*replacement->right) : NULL; - append(element_type->right, old_right); - } +const type_tree* nth_type_ingredient(const type_tree* callsite_type, int type_ingredient_index, const type_info& container_info) { + bool final = final_type_ingredient(type_ingredient_index, container_info); + const type_tree* curr = callsite_type; + for (int i = 0; i < type_ingredient_index; ++i) { + assert(curr); + assert(!curr->atom); +//? cerr << "type ingredient " << i << " is " << to_string(curr->left) << '\n'; + curr = curr->right; } + assert(curr); + if (curr->atom) return curr; + if (!final) return curr->left; + if (!curr->right) return curr->left; + return curr; } bool final_type_ingredient(int type_ingredient_index, const type_info& container_info) { @@ -482,7 +470,128 @@ def main [ ] +error: illegal type "foo" seems to be missing a type ingredient or three -//: 'merge' on shape-shifting containers +//:: fix up previous layers + +//: We have two transforms in previous layers -- for computing sizes and +//: offsets containing addresses for containers and exclusive containers -- +//: that we need to teach about type ingredients. + +:(before "End compute_container_sizes Non-atom Cases") +const type_tree* root = root_type(type); +type_info& info = get(Type, root->value); +if (info.kind == CONTAINER) { + compute_container_sizes(info, type, pending_metadata); + return; +} +if (info.kind == EXCLUSIVE_CONTAINER) { + compute_exclusive_container_sizes(info, type, pending_metadata); + return; +} + +:(before "End Unit Tests") +void test_container_sizes_shape_shifting_container() { + run("container foo:_t [\n" + " x:number\n" + " y:_t\n" + "]\n"); + reagent r("x:foo:point"); + compute_container_sizes(r); + CHECK_EQ(r.metadata.size, 3); +} + +void test_container_sizes_shape_shifting_exclusive_container() { + run("exclusive-container foo:_t [\n" + " x:number\n" + " y:_t\n" + "]\n"); + reagent r("x:foo:point"); + compute_container_sizes(r); + CHECK_EQ(r.metadata.size, 3); + reagent r2("x:foo:number"); + compute_container_sizes(r2); + CHECK_EQ(r2.metadata.size, 2); +} + +void test_container_sizes_compound_type_ingredient() { + run("container foo:_t [\n" + " x:number\n" + " y:_t\n" + "]\n"); + reagent r("x:foo:address:point"); + compute_container_sizes(r); + CHECK_EQ(r.metadata.size, 2); + // scan also pre-computes metadata for type ingredient + reagent point("x:point"); + CHECK(contains_key(Container_metadata, point.type)); + CHECK_EQ(get(Container_metadata, point.type).size, 2); +} + +void test_container_sizes_recursive_shape_shifting_container() { + run("container foo:_t [\n" + " x:number\n" + " y:address:foo:_t\n" + "]\n"); + reagent r2("x:foo:number"); + compute_container_sizes(r2); + CHECK_EQ(r2.metadata.size, 2); +} + +:(before "End compute_container_address_offsets Non-atom Cases") +const type_tree* root = root_type(type); +type_info& info = get(Type, root->value); +if (info.kind == CONTAINER) { + compute_container_address_offsets(info, type); + return; +} +if (info.kind == EXCLUSIVE_CONTAINER) { + compute_exclusive_container_address_offsets(info, type); + return; +} + +:(before "End Unit Tests") +void test_container_address_offsets_in_shape_shifting_container() { + run("container foo:_t [\n" + " x:number\n" + " y:_t\n" + "]\n"); + reagent r("x:foo:address:number"); + compute_container_sizes(r); + compute_container_address_offsets(r); + CHECK_EQ(SIZE(r.metadata.address), 1); + CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); + set<address_element_info>& offset_info = get(r.metadata.address, set<tag_condition_info>()); + CHECK_EQ(SIZE(offset_info), 1); + CHECK_EQ(offset_info.begin()->offset, 1); // + CHECK(offset_info.begin()->payload_type->atom); + CHECK_EQ(offset_info.begin()->payload_type->name, "number"); +} + +void test_container_address_offsets_in_nested_shape_shifting_container() { + run("container foo:_t [\n" + " x:number\n" + " y:_t\n" + "]\n" + "container bar:_t [\n" + " x:_t\n" + " y:foo:_t\n" + "]\n"); + reagent r("x:bar:address:number"); + CLEAR_TRACE; + compute_container_sizes(r); + compute_container_address_offsets(r); + CHECK_EQ(SIZE(r.metadata.address), 1); + CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); + set<address_element_info>& offset_info = get(r.metadata.address, set<tag_condition_info>()); + CHECK_EQ(SIZE(offset_info), 2); + CHECK_EQ(offset_info.begin()->offset, 0); // + CHECK(offset_info.begin()->payload_type->atom); + CHECK_EQ(offset_info.begin()->payload_type->name, "number"); + CHECK_EQ((++offset_info.begin())->offset, 2); // + CHECK((++offset_info.begin())->payload_type->atom); + CHECK_EQ((++offset_info.begin())->payload_type->name, "number"); +} + +//:: 'merge' on shape-shifting containers :(scenario merge_check_shape_shifting_container_containing_exclusive_container) container foo:_elem [ diff --git a/056shape_shifting_recipe.cc b/056shape_shifting_recipe.cc index 3441829b..428c3194 100644 --- a/056shape_shifting_recipe.cc +++ b/056shape_shifting_recipe.cc @@ -97,8 +97,8 @@ vector<recipe_ordinal> strictly_matching_shape_shifting_variants(const instructi for (int i = 0; i < SIZE(variants); ++i) { if (variants.at(i) == -1) continue; if (!any_type_ingredient_in_header(variants.at(i))) continue; - if (all_concrete_header_reagents_strictly_match(inst, get(Recipe, variants.at(i)))) - result.push_back(variants.at(i)); + if (!all_concrete_header_reagents_strictly_match(inst, get(Recipe, variants.at(i)))) continue; + result.push_back(variants.at(i)); } return result; } @@ -192,31 +192,28 @@ int number_of_concrete_type_names(const reagent& r) { int number_of_concrete_type_names(const type_tree* type) { if (!type) return 0; - int result = 0; - if (!type->name.empty() && !is_type_ingredient_name(type->name)) - ++result; - result += number_of_concrete_type_names(type->left); - result += number_of_concrete_type_names(type->right); - return result; + if (type->atom) + return is_type_ingredient_name(type->name) ? 0 : 1; + return number_of_concrete_type_names(type->left) + + number_of_concrete_type_names(type->right); } bool concrete_type_names_strictly_match(const type_tree* to, const type_tree* from, const reagent& rhs_reagent) { if (!to) return !from; if (!from) return !to; - if (is_type_ingredient_name(to->name)) return true; // type ingredient matches anything - if (to->name == "literal" && from->name == "literal") - return true; - if (to->name == "literal" - && Literal_type_names.find(from->name) != Literal_type_names.end()) - return true; - if (from->name == "literal" - && Literal_type_names.find(to->name) != Literal_type_names.end()) - return true; - if (from->name == "literal" && to->name == "address") - return rhs_reagent.name == "0"; - return to->name == from->name - && concrete_type_names_strictly_match(to->left, from->left, rhs_reagent) - && concrete_type_names_strictly_match(to->right, from->right, rhs_reagent); + if (to->atom && is_type_ingredient_name(to->name)) return true; // type ingredient matches anything + if (from->atom && is_mu_address(to)) + return from->name == "literal" && rhs_reagent.name == "0"; + if (!from->atom && !to->atom) + return concrete_type_names_strictly_match(to->left, from->left, rhs_reagent) + && concrete_type_names_strictly_match(to->right, from->right, rhs_reagent); + if (from->atom != to->atom) return false; + // both from and to are atoms + if (from->name == "literal") + return Literal_type_names.find(to->name) != Literal_type_names.end(); + if (to->name == "literal") + return Literal_type_names.find(from->name) != Literal_type_names.end(); + return to->name == from->name; } bool contains_type_ingredient_name(const reagent& x) { @@ -337,14 +334,12 @@ void accumulate_type_ingredients(const type_tree* exemplar_type, const type_tree } if (is_type_ingredient_name(exemplar_type->name)) { const type_tree* curr_refinement_type = NULL; // temporary heap allocation; must always be deleted before it goes out of scope - if (refinement_type->left) - curr_refinement_type = new type_tree(*refinement_type->left); - else if (exemplar_type->right) - // splice out refinement_type->right, it'll be used later by the exemplar_type->right - curr_refinement_type = new type_tree(refinement_type->name, refinement_type->value, NULL); - else + if (exemplar_type->atom) curr_refinement_type = new type_tree(*refinement_type); - assert(!curr_refinement_type->left); + else { + assert(!refinement_type->atom); + curr_refinement_type = new type_tree(*refinement_type->left); + } if (!contains_key(mappings, exemplar_type->name)) { trace(9993, "transform") << "adding mapping from " << exemplar_type->name << " to " << to_string(curr_refinement_type) << end(); put(mappings, exemplar_type->name, new type_tree(*curr_refinement_type)); @@ -409,49 +404,27 @@ void replace_type_ingredients(reagent& x, const map<string, const type_tree*>& m // todo: too complicated and likely incomplete; maybe avoid replacing in place? void replace_type_ingredients(type_tree* type, const map<string, const type_tree*>& mappings) { if (!type) return; - if (contains_key(Type_ordinal, type->name)) // todo: ugly side effect - type->value = get(Type_ordinal, type->name); - if (!is_type_ingredient_name(type->name) || !contains_key(mappings, type->name)) { + if (!type->atom) { replace_type_ingredients(type->left, mappings); replace_type_ingredients(type->right, mappings); return; } - + if (contains_key(Type_ordinal, type->name)) // todo: ugly side effect + type->value = get(Type_ordinal, type->name); + if (!contains_key(mappings, type->name)) + return; const type_tree* replacement = get(mappings, type->name); trace(9993, "transform") << type->name << " => " << names_to_string(replacement) << end(); - if (!contains_key(Type_ordinal, replacement->name)) { - // error in program; should be reported elsewhere - return; - } - - // type is a single type ingredient - assert(!type->left); - if (!type->right) assert(!replacement->left); - - if (!replacement->right) { - if (!replacement->left) { - type->name = (replacement->name == "literal") ? "number" : replacement->name; - type->value = get(Type_ordinal, type->name); - } - else { - type->name = ""; - type->value = 0; - type->left = new type_tree(*replacement); + if (replacement->atom) { + if (!contains_key(Type_ordinal, replacement->name)) { + // error in program; should be reported elsewhere + return; } - replace_type_ingredients(type->right, mappings); - } - // replace non-last type? - else if (type->right) { - type->name = ""; - type->value = 0; - type->left = new type_tree(*replacement); - replace_type_ingredients(type->right, mappings); + type->name = (replacement->name == "literal") ? "number" : replacement->name; + type->value = get(Type_ordinal, type->name); } - // replace last type? else { - type->name = replacement->name; - type->value = get(Type_ordinal, type->name); - type->right = new type_tree(*replacement->right); + *type = *replacement; } } @@ -473,34 +446,9 @@ void accumulate_type_ingredients(const type_tree* type, set<string>& out) { } type_tree* parse_type_tree(const string& s) { - istringstream in(s); - in >> std::noskipws; - return parse_type_tree(in); -} - -type_tree* parse_type_tree(istream& in) { - skip_whitespace_but_not_newline(in); - if (!has_data(in)) return NULL; - if (in.peek() == ')') { - in.get(); - return NULL; - } - if (in.peek() != '(') - return new type_tree(next_word(in), 0); - in.get(); // skip '(' - type_tree* result = NULL; - type_tree** curr = &result; - while (in.peek() != ')') { - assert(has_data(in)); - *curr = new type_tree("", 0); - skip_whitespace_but_not_newline(in); - if (in.peek() == '(') - (*curr)->left = parse_type_tree(in); - else - (*curr)->name = next_word(in); - curr = &(*curr)->right; - } - in.get(); // skip ')' + string_tree* s2 = parse_string_tree(s); + type_tree* result = new_type_tree(s2); + delete s2; return result; } diff --git a/057immutable.cc b/057immutable.cc index 0d82c010..739f6a56 100644 --- a/057immutable.cc +++ b/057immutable.cc @@ -555,9 +555,9 @@ $error: 0 :(before "End Immutable Ingredients Special-cases") if (has_property(current_ingredient, "contained-in")) { const string_tree* tmp = property(current_ingredient, "contained-in"); - if (tmp->left || tmp->right + if (!tmp->atom || !is_present_in_ingredients(caller, tmp->value) || !is_present_in_products(caller, tmp->value)) - raise << maybe(caller.name) << "/contained-in can only point to another ingredient+product, but got '" << to_string(property(current_ingredient, "contained-in")) << "'\n" << end(); + raise << maybe(caller.name) << "/contained-in can only point to another ingredient or product, but got '" << to_string(property(current_ingredient, "contained-in")) << "'\n" << end(); continue; } diff --git a/062rewrite_stash.cc b/062rewrite_stash.cc index 68738341..7ef0f38c 100644 --- a/062rewrite_stash.cc +++ b/062rewrite_stash.cc @@ -117,14 +117,15 @@ void rewrite_stash_to_text(reagent& r, vector<instruction>& out, const string& t } bool is_lookup_of_address_of_array(reagent/*copy*/ x) { - if (x.type->name != "address") return false; + if (x.type->atom) return false; + if (x.type->left->name != "address") return false; if (!canonize_type(x)) return false; - return x.type->name == "array"; + return is_mu_array(x); } bool is_static_array(const reagent& x) { // no canonize_type() - return x.type->name == "array"; + return !x.type->atom && x.type->left->atom && x.type->left->name == "array"; } //: Make sure that the new system is strictly better than just the 'stash' diff --git a/069hash.cc b/069hash.cc index 87cf666c..9133fa6d 100644 --- a/069hash.cc +++ b/069hash.cc @@ -89,8 +89,7 @@ size_t hash_mu_array(size_t h, const reagent& r) { } size_t hash_mu_container(size_t h, const reagent& r) { - assert(r.type->value); - type_info& info = get(Type, r.type->value); + type_info& info = get(Type, root_type(r.type)->value); int address = r.value; int offset = 0; for (int i = 0; i < SIZE(info.elements); ++i) { @@ -105,12 +104,13 @@ size_t hash_mu_container(size_t h, const reagent& r) { } size_t hash_mu_exclusive_container(size_t h, const reagent& r) { - assert(r.type->value); + const type_tree* type = root_type(r.type); + assert(type->value); int tag = get(Memory, r.value); reagent/*copy*/ variant = variant_type(r, tag); // todo: move this error to container definition time if (has_property(variant, "ignore-for-hash")) - raise << get(Type, r.type->value).name << ": /ignore-for-hash won't work in exclusive containers\n" << end(); + raise << get(Type, type->value).name << ": /ignore-for-hash won't work in exclusive containers\n" << end(); variant.set_value(r.value + /*skip tag*/1); h = hash(h, variant); return h; diff --git a/071recipe.cc b/071recipe.cc index 5a4377cb..30df827a 100644 --- a/071recipe.cc +++ b/071recipe.cc @@ -70,8 +70,8 @@ bool contains_reagent_with_type(const recipe& caller, const string& name) { bool is_matching_non_recipe_literal(const reagent& x, const string& name) { if (x.name != name) return false; if (!x.type) return false; - if (x.type->value == get(Type_ordinal, "recipe-literal")) return false; - return true; + if (!x.type->atom) return false; + return x.type->value != get(Type_ordinal, "recipe-literal"); } //: It's confusing to use variable names that are also recipe names. Always @@ -177,22 +177,32 @@ void check_indirect_calls_against_header(const recipe_ordinal r) { } recipe from_reagent(const reagent& r) { - assert(r.type->name == "recipe"); + assert(!r.type->atom && r.type->left->atom && r.type->left->name == "recipe"); recipe result_header; // will contain only ingredients and products, nothing else result_header.has_header = true; const type_tree* curr = r.type->right; - for (; curr; curr=curr->right) { - if (curr->name == "->") { + for (/*nada*/; curr && !curr->atom; curr = curr->right) { + if (curr->left->atom && curr->left->name == "->") { curr = curr->right; // skip delimiter break; } - result_header.ingredients.push_back(next_recipe_reagent(curr)); + result_header.ingredients.push_back(next_recipe_reagent(curr->left)); + if (curr->right && curr->right->atom) { + result_header.ingredients.push_back(next_recipe_reagent(curr->right)); + return result_header; // no products + } } - for (; curr; curr=curr->right) + for (; curr && !curr->atom; curr=curr->right) + result_header.products.push_back(next_recipe_reagent(curr->left)); + if (curr) { + assert(curr->atom); result_header.products.push_back(next_recipe_reagent(curr)); + } return result_header; } +// todo: unit test: 'recipe number' vs 'recipe -> number' + reagent next_recipe_reagent(const type_tree* curr) { if (!curr->left) return reagent("recipe:"+curr->name); reagent result; @@ -203,9 +213,10 @@ reagent next_recipe_reagent(const type_tree* curr) { bool is_mu_recipe(const reagent& r) { if (!r.type) return false; - if (r.type->name == "recipe") return true; - if (r.type->name == "recipe-literal") return true; - // End is_mu_recipe Cases + if (r.type->atom) + return r.type->name == "recipe-literal"; + if (!r.type->left->atom) return false; + if (r.type->left->name == "recipe") return true; return false; } diff --git a/073wait.cc b/073wait.cc index 46086589..a642c86a 100644 --- a/073wait.cc +++ b/073wait.cc @@ -123,7 +123,12 @@ case GET_LOCATION: { } reagent/*copy*/ base = inst.ingredients.at(0); if (!canonize_type(base)) break; - if (!base.type || !base.type->value || !contains_key(Type, base.type->value) || get(Type, base.type->value).kind != CONTAINER) { + if (!base.type) { + raise << maybe(get(Recipe, r).name) << "first ingredient of 'get-location' should be a container, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); + break; + } + const type_tree* base_root_type = base.type->atom ? base.type : base.type->left; + if (!base_root_type->atom || base_root_type->value == 0 || !contains_key(Type, base_root_type->value) || get(Type, base_root_type->value).kind != CONTAINER) { raise << maybe(get(Recipe, r).name) << "first ingredient of 'get-location' should be a container, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); break; } @@ -160,9 +165,9 @@ case GET_LOCATION: { raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << to_original_string(current_instruction()) << "'\n" << end(); break; } - type_ordinal base_type = base.type->value; + const type_tree* base_root_type = root_type(base.type); int offset = ingredients.at(1).at(0); - if (offset < 0 || offset >= SIZE(get(Type, base_type).elements)) break; // copied from Check above + if (offset < 0 || offset >= SIZE(get(Type, base_root_type->value).elements)) break; // copied from Check above int result = base_address; for (int i = 0; i < offset; ++i) result += size_of(element_type(base.type, i)); diff --git a/074deep_copy.cc b/074deep_copy.cc index 4452549f..a3d7c48b 100644 --- a/074deep_copy.cc +++ b/074deep_copy.cc @@ -283,7 +283,7 @@ void deep_copy(const reagent& canonized_in, map<int, int>& addresses_copied, con // construct a fake reagent that reads directly from the appropriate // field of the container reagent curr; - curr.type = new type_tree("address", new type_tree(*info->payload_type)); + curr.type = new type_tree(new type_tree("address"), new type_tree(*info->payload_type)); curr.set_value(canonized_in.value + info->offset); curr.properties.push_back(pair<string, string_tree*>("raw", NULL)); trace(9991, "run") << "deep-copy: copying address " << curr.value << end(); |