From 598f1b5362eb799e40836ceeb5452c9ba937fd6c Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Tue, 7 Feb 2017 00:22:08 -0800 Subject: 3746 --- html/010vm.cc.html | 1605 +++++++++++++++--------------- html/011load.cc.html | 521 +++++----- html/012transform.cc.html | 2 +- html/014literal_string.cc.html | 4 +- html/015literal_noninteger.cc.html | 2 +- html/016dilated_reagent.cc.html | 10 +- html/017parse_tree.cc.html | 4 +- html/018type_abbreviations.cc.html | 6 +- html/020run.cc.html | 2 +- html/021check_instruction.cc.html | 2 +- html/028call_return.cc.html | 2 +- html/030container.cc.html | 38 +- html/031merge.cc.html | 4 +- html/032array.cc.html | 2 +- html/033exclusive_container.cc.html | 18 +- html/034address.cc.html | 2 +- html/035lookup.cc.html | 8 +- html/036refcount.cc.html | 1598 ++++++++++++++--------------- html/037abandon.cc.html | 4 +- html/040brace.cc.html | 779 +++++++-------- html/041jump_target.cc.html | 10 +- html/042name.cc.html | 16 +- html/043space.cc.html | 365 +++---- html/050scenario.cc.html | 28 +- html/052tangle.cc.html | 4 +- html/053recipe_header.cc.html | 6 +- html/054static_dispatch.cc.html | 2 +- html/055shape_shifting_container.cc.html | 10 +- html/056shape_shifting_recipe.cc.html | 2 +- html/057immutable.cc.html | 2 +- html/069hash.cc.html | 10 +- html/071recipe.cc.html | 4 +- html/072scheduler.cc.html | 2 +- html/073wait.cc.html | 4 +- html/074deep_copy.cc.html | 6 +- html/082scenario_screen.cc.html | 10 +- html/089scenario_filesystem.cc.html | 10 +- html/999spaces.cc.html | 2 +- 38 files changed, 2570 insertions(+), 2536 deletions(-) (limited to 'html') diff --git a/html/010vm.cc.html b/html/010vm.cc.html index a02ee098..b2811293 100644 --- a/html/010vm.cc.html +++ b/html/010vm.cc.html @@ -95,810 +95,827 @@ if ('onhashchange' in window) { 33 bool is_label; 34 string label; // only if is_label 35 string name; // only if !is_label - 36 string old_name; // before our automatic rewrite rules - 37 string original_string; // for error messages - 38 recipe_ordinal operation; // get(Recipe_ordinal, name) - 39 vector<reagent> ingredients; // only if !is_label - 40 vector<reagent> products; // only if !is_label - 41 // End instruction Fields - 42 instruction(); - 43 void clear(); - 44 bool is_empty(); - 45 }; - 46 - 47 :(before "struct instruction") - 48 // Ingredients and products are a single species -- a reagent. Reagents refer - 49 // either to numbers or to locations in memory along with 'type' tags telling - 50 // us how to interpret them. They also can contain arbitrary other lists of - 51 // properties besides types, but we're getting ahead of ourselves. - 52 struct reagent { - 53 string original_string; - 54 string name; - 55 type_tree* type; - 56 vector<pair<string, string_tree*> > properties; // can't be a map because the string_tree sometimes needs to be NULL, which can be confusing - 57 double value; - 58 bool initialized; - 59 // End reagent Fields - 60 reagent(const string& s); - 61 reagent() :type(NULL), value(0), initialized(false) {} - 62 ~reagent(); - 63 void clear(); - 64 reagent(const reagent& original); - 65 reagent& operator=(const reagent& original); - 66 void set_value(double v) { value = v; initialized = true; } - 67 }; - 68 - 69 :(before "struct reagent") - 70 // Types can range from a simple type ordinal, to arbitrarily complex trees of - 71 // type parameters, like (map (address array character) (list number)) - 72 struct type_tree { - 73 bool atom; - 74 string name; // only if atom - 75 type_ordinal value; // only if atom - 76 type_tree* left; // only if !atom - 77 type_tree* right; // only if !atom - 78 ~type_tree(); - 79 type_tree(const type_tree& original); - 80 // atomic type ordinal - 81 explicit type_tree(string name); - 82 type_tree(string name, type_ordinal v) :atom(true), name(name), value(v), left(NULL), right(NULL) {} - 83 // tree of type ordinals - 84 type_tree(type_tree* l, type_tree* r) :atom(false), value(0), left(l), right(r) {} - 85 type_tree& operator=(const type_tree& original); - 86 bool operator==(const type_tree& other) const; - 87 bool operator!=(const type_tree& other) const { return !operator==(other); } - 88 bool operator<(const type_tree& other) const; - 89 bool operator>(const type_tree& other) const { return other.operator<(*this); } - 90 }; - 91 - 92 struct string_tree { - 93 bool atom; - 94 string value; // only if atom - 95 string_tree* left; // only if !atom - 96 string_tree* right; // only if !atom - 97 ~string_tree(); - 98 string_tree(const string_tree& original); - 99 // atomic string -100 explicit string_tree(string v) :atom(true), value(v), left(NULL), right(NULL) {} -101 // tree of strings -102 string_tree(string_tree* l, string_tree* r) :atom(false), left(l), right(r) {} -103 }; -104 -105 // End type_tree Definition -106 :(code) -107 type_tree::type_tree(string name) :atom(true), name(name), value(get(Type_ordinal, name)), left(NULL), right(NULL) {} -108 -109 :(before "End Globals") -110 // Locations refer to a common 'memory'. Each location can store a number. -111 map<int, double> Memory; -112 :(before "End Setup") -113 Memory.clear(); -114 -115 :(after "Types") -116 // Mu types encode how the numbers stored in different parts of memory are -117 // interpreted. A location tagged as a 'character' type will interpret the -118 // value 97 as the letter 'a', while a different location of type 'number' -119 // would not. -120 // -121 // Unlike most computers today, Mu stores types in a single big table, shared -122 // by all the Mu programs on the computer. This is useful in providing a -123 // seamless experience to help understand arbitrary Mu programs. -124 typedef int type_ordinal; -125 :(before "End Globals") -126 map<string, type_ordinal> Type_ordinal; -127 map<type_ordinal, type_info> Type; -128 type_ordinal Next_type_ordinal = 1; -129 :(code) -130 void setup_types() { -131 Type.clear(); Type_ordinal.clear(); -132 put(Type_ordinal, "literal", 0); -133 Next_type_ordinal = 1; -134 // Mu Types Initialization -135 type_ordinal number = put(Type_ordinal, "number", Next_type_ordinal++); -136 get_or_insert(Type, number).name = "number"; -137 put(Type_ordinal, "location", number); // synonym of number to indicate we only care about its size -138 type_ordinal address = put(Type_ordinal, "address", Next_type_ordinal++); -139 get_or_insert(Type, address).name = "address"; -140 type_ordinal boolean = put(Type_ordinal, "boolean", Next_type_ordinal++); -141 get_or_insert(Type, boolean).name = "boolean"; -142 type_ordinal character = put(Type_ordinal, "character", Next_type_ordinal++); -143 get_or_insert(Type, character).name = "character"; -144 // Array types are a special modifier to any other type. For example, -145 // array:number or array:address:boolean. -146 type_ordinal array = put(Type_ordinal, "array", Next_type_ordinal++); -147 get_or_insert(Type, array).name = "array"; -148 // End Mu Types Initialization -149 } -150 void teardown_types() { -151 for (map<type_ordinal, type_info>::iterator p = Type.begin(); p != Type.end(); ++p) { -152 for (int i = 0; i < SIZE(p->second.elements); ++i) -153 p->second.elements.clear(); -154 } -155 Type_ordinal.clear(); -156 } -157 :(before "End One-time Setup") -158 setup_types(); -159 atexit(teardown_types); -160 -161 :(before "End Types") -162 // You can construct arbitrary new types. New types are either 'containers' -163 // with multiple 'elements' of other types, or 'exclusive containers' containing -164 // one of multiple 'variants'. (These are similar to C structs and unions, -165 // respectively, though exclusive containers implicitly include a tag element -166 // recording which variant they should be interpreted as.) -167 // -168 // For example, storing bank balance and name for an account might require a -169 // container, but if bank accounts may be either for individuals or groups, -170 // with different properties for each, that may require an exclusive container -171 // whose variants are individual-account and joint-account containers. -172 enum kind_of_type { -173 PRIMITIVE, -174 CONTAINER, -175 EXCLUSIVE_CONTAINER -176 }; -177 -178 struct type_info { -179 string name; -180 kind_of_type kind; -181 vector<reagent> elements; -182 // End type_info Fields -183 type_info() :kind(PRIMITIVE) { -184 // End type_info Constructor -185 } -186 }; -187 -188 enum primitive_recipes { -189 IDLE = 0, -190 COPY, -191 // End Primitive Recipe Declarations -192 MAX_PRIMITIVE_RECIPES, -193 }; -194 :(code) -195 //: It's all very well to construct recipes out of other recipes, but we need -196 //: to know how to do *something* out of the box. For the following -197 //: recipes there are only codes, no entries in the book, because Mu just knows -198 //: what to do for them. -199 void setup_recipes() { -200 Recipe.clear(); Recipe_ordinal.clear(); -201 put(Recipe_ordinal, "idle", IDLE); -202 // Primitive Recipe Numbers -203 put(Recipe_ordinal, "copy", COPY); -204 // End Primitive Recipe Numbers -205 } -206 //: We could just reset the recipe table after every test, but that gets slow -207 //: all too quickly. Instead, initialize the common stuff just once at -208 //: startup. Later layers will carefully undo each test's additions after -209 //: itself. -210 :(before "End One-time Setup") -211 setup_recipes(); -212 assert(MAX_PRIMITIVE_RECIPES < 200); // level 0 is primitives; until 199 -213 Next_recipe_ordinal = 200; -214 put(Recipe_ordinal, "main", Next_recipe_ordinal++); -215 // End Load Recipes -216 :(before "End Commandline Parsing") -217 assert(Next_recipe_ordinal < 1000); // recipes being tested didn't overflow into test space -218 :(before "End Setup") -219 Next_recipe_ordinal = 1000; // consistent new numbers for each test -220 -221 //: One final detail: tests can modify our global tables of recipes and types, -222 //: so we need some way to clean up after each test is done so it doesn't -223 //: influence later ones. -224 :(before "End Globals") -225 map<string, recipe_ordinal> Recipe_ordinal_snapshot; -226 map<recipe_ordinal, recipe> Recipe_snapshot; -227 map<string, type_ordinal> Type_ordinal_snapshot; -228 map<type_ordinal, type_info> Type_snapshot; -229 :(before "End One-time Setup") -230 save_snapshots(); -231 :(before "End Setup") -232 restore_snapshots(); -233 -234 :(code) -235 void save_snapshots() { -236 Recipe_ordinal_snapshot = Recipe_ordinal; -237 Recipe_snapshot = Recipe; -238 Type_ordinal_snapshot = Type_ordinal; -239 Type_snapshot = Type; -240 // End save_snapshots -241 } -242 -243 void restore_snapshots() { -244 Recipe = Recipe_snapshot; -245 Recipe_ordinal = Recipe_ordinal_snapshot; -246 restore_non_recipe_snapshots(); -247 } -248 // when running sandboxes in the edit/ app we'll want to restore everything except recipes defined in the app -249 void restore_non_recipe_snapshots() { -250 Type_ordinal = Type_ordinal_snapshot; -251 Type = Type_snapshot; -252 // End restore_snapshots -253 } -254 -255 //:: Helpers -256 -257 :(code) -258 recipe::recipe() { -259 // End recipe Constructor -260 } -261 -262 instruction::instruction() :is_label(false), operation(IDLE) { -263 // End instruction Constructor -264 } -265 void instruction::clear() { is_label=false; label.clear(); name.clear(); old_name.clear(); operation=IDLE; ingredients.clear(); products.clear(); original_string.clear(); } -266 bool instruction::is_empty() { return !is_label && name.empty(); } -267 -268 // Reagents have the form <name>:<type>:<type>:.../<property>/<property>/... -269 reagent::reagent(const string& s) :original_string(s), type(NULL), value(0), initialized(false) { -270 // Parsing reagent(string s) -271 istringstream in(s); -272 in >> std::noskipws; -273 // name and type -274 istringstream first_row(slurp_until(in, '/')); -275 first_row >> std::noskipws; -276 name = slurp_until(first_row, ':'); -277 string_tree* type_names = parse_property_list(first_row); -278 // End Parsing Reagent Type Property(type_names) -279 type = new_type_tree(type_names); -280 delete type_names; -281 // special cases -282 if (is_integer(name) && type == NULL) -283 type = new type_tree("literal"); -284 if (name == "_" && type == NULL) -285 type = new type_tree("literal"); -286 // other properties -287 slurp_properties(in, properties); -288 // End Parsing reagent -289 } -290 -291 void slurp_properties(istream& in, vector<pair<string, string_tree*> >& out) { -292 while (has_data(in)) { -293 istringstream row(slurp_until(in, '/')); -294 row >> std::noskipws; -295 string key = slurp_until(row, ':'); -296 string_tree* value = parse_property_list(row); -297 out.push_back(pair<string, string_tree*>(key, value)); -298 } -299 } -300 -301 string_tree* parse_property_list(istream& in) { -302 skip_whitespace_but_not_newline(in); -303 if (!has_data(in)) return NULL; -304 string_tree* first = new string_tree(slurp_until(in, ':')); -305 if (!has_data(in)) return first; -306 string_tree* rest = parse_property_list(in); -307 if (!has_data(in) && rest->atom) -308 return new string_tree(first, new string_tree(rest, NULL)); -309 return new string_tree(first, rest); -310 } -311 :(before "End Unit Tests") -312 void test_parse_property_list_atom() { -313 istringstream in("a"); -314 string_tree* x = parse_property_list(in); -315 CHECK(x->atom); -316 delete x; -317 } -318 void test_parse_property_list_list() { -319 istringstream in("a:b"); -320 string_tree* x = parse_property_list(in); -321 CHECK(!x->atom); -322 CHECK(x->left->atom); -323 CHECK_EQ(x->left->value, "a"); -324 CHECK(!x->right->atom); -325 CHECK(x->right->left->atom); -326 CHECK_EQ(x->right->left->value, "b"); -327 CHECK(x->right->right == NULL); -328 delete x; -329 } -330 -331 :(code) -332 type_tree* new_type_tree(const string_tree* properties) { -333 if (!properties) return NULL; -334 if (properties->atom) { -335 const string& type_name = properties->value; -336 int value = 0; -337 if (contains_key(Type_ordinal, type_name)) -338 value = get(Type_ordinal, type_name); -339 else if (is_integer(type_name)) // sometimes types will contain non-type tags, like numbers for the size of an array -340 value = 0; -341 else if (properties->value == "->") // used in recipe types -342 value = 0; -343 else -344 value = -1; // should never happen; will trigger errors later -345 return new type_tree(type_name, value); -346 } -347 return new type_tree(new_type_tree(properties->left), -348 new_type_tree(properties->right)); -349 } -350 -351 //: avoid memory leaks for the type tree -352 -353 reagent::reagent(const reagent& other) { -354 original_string = other.original_string; -355 name = other.name; -356 value = other.value; -357 initialized = other.initialized; -358 for (int i = 0; i < SIZE(other.properties); ++i) { -359 properties.push_back(pair<string, string_tree*>(other.properties.at(i).first, -360 other.properties.at(i).second ? new string_tree(*other.properties.at(i).second) : NULL)); -361 } -362 type = other.type ? new type_tree(*other.type) : NULL; -363 // End reagent Copy Constructor -364 } -365 -366 type_tree::type_tree(const type_tree& original) { -367 atom = original.atom; -368 name = original.name; -369 value = original.value; -370 left = original.left ? new type_tree(*original.left) : NULL; -371 right = original.right ? new type_tree(*original.right) : NULL; -372 } -373 -374 type_tree& type_tree::operator=(const type_tree& original) { -375 atom = original.atom; -376 name = original.name; -377 value = original.value; -378 if (left) delete left; -379 left = original.left ? new type_tree(*original.left) : NULL; -380 if (right) delete right; -381 right = original.right ? new type_tree(*original.right) : NULL; -382 return *this; -383 } -384 -385 bool type_tree::operator==(const type_tree& other) const { -386 if (atom != other.atom) return false; -387 if (atom) -388 return name == other.name && value == other.value; -389 return (left == other.left || *left == *other.left) -390 && (right == other.right || *right == *other.right); -391 } -392 -393 // only constraint we care about: if a < b then !(b < a) -394 bool type_tree::operator<(const type_tree& other) const { -395 if (atom != other.atom) return atom > other.atom; // atoms before non-atoms -396 if (atom) return name < other.name; // sort atoms in lexical order -397 // first location in one that's missing in the other makes that side 'smaller' -398 if (left && !other.left) return false; -399 if (!left && other.left) return true; -400 if (right && !other.right) return false; -401 if (!right && other.right) return true; -402 // now if either pointer is unequal neither side can be null -403 // if one side is equal that's easy -404 if (left == other.left || *left == *other.left) return right && *right < *other.right; -405 if (right == other.right || *right == *other.right) return left && *left < *other.left; -406 // if the two sides criss-cross, pick the side with the smaller lhs -407 if ((left == other.right || *left == *other.right) -408 && (right == other.left || *right == *other.left)) -409 return *left < *other.left; -410 // now the hard case: both sides are not equal -411 // make sure we stay consistent between (a < b) and (b < a) -412 // just return the side with the smallest of the 4 branches -413 if (*left < *other.left && *left < *other.right) return true; -414 if (*right < *other.left && *right < *other.right) return true; -415 return false; -416 } -417 :(before "End Unit Tests") -418 // These unit tests don't always use valid types. -419 void test_compare_atom_types() { -420 reagent a("a:address"), b("b:boolean"); -421 CHECK(*a.type < *b.type); -422 CHECK(!(*b.type < *a.type)); + 36 string original_string; // for error messages + 37 recipe_ordinal operation; // get(Recipe_ordinal, name) + 38 vector<reagent> ingredients; // only if !is_label + 39 vector<reagent> products; // only if !is_label + 40 // End instruction Fields + 41 instruction(); + 42 void clear(); + 43 bool is_empty(); + 44 }; + 45 + 46 :(before "struct instruction") + 47 // Ingredients and products are a single species -- a reagent. Reagents refer + 48 // either to numbers or to locations in memory along with 'type' tags telling + 49 // us how to interpret them. They also can contain arbitrary other lists of + 50 // properties besides types, but we're getting ahead of ourselves. + 51 struct reagent { + 52 string original_string; + 53 string name; + 54 type_tree* type; + 55 vector<pair<string, string_tree*> > properties; // can't be a map because the string_tree sometimes needs to be NULL, which can be confusing + 56 double value; + 57 bool initialized; + 58 // End reagent Fields + 59 reagent(const string& s); + 60 reagent() :type(NULL), value(0), initialized(false) {} + 61 ~reagent(); + 62 void clear(); + 63 reagent(const reagent& original); + 64 reagent& operator=(const reagent& original); + 65 void set_value(double v) { value = v; initialized = true; } + 66 }; + 67 + 68 :(before "struct reagent") + 69 // Types can range from a simple type ordinal, to arbitrarily complex trees of + 70 // type parameters, like (map (address array character) (list number)) + 71 struct type_tree { + 72 bool atom; + 73 string name; // only if atom + 74 type_ordinal value; // only if atom + 75 type_tree* left; // only if !atom + 76 type_tree* right; // only if !atom + 77 ~type_tree(); + 78 type_tree(const type_tree& original); + 79 // atomic type ordinal + 80 explicit type_tree(string name); + 81 type_tree(string name, type_ordinal v) :atom(true), name(name), value(v), left(NULL), right(NULL) {} + 82 // tree of type ordinals + 83 type_tree(type_tree* l, type_tree* r) :atom(false), value(0), left(l), right(r) {} + 84 type_tree& operator=(const type_tree& original); + 85 bool operator==(const type_tree& other) const; + 86 bool operator!=(const type_tree& other) const { return !operator==(other); } + 87 bool operator<(const type_tree& other) const; + 88 bool operator>(const type_tree& other) const { return other.operator<(*this); } + 89 }; + 90 + 91 struct string_tree { + 92 bool atom; + 93 string value; // only if atom + 94 string_tree* left; // only if !atom + 95 string_tree* right; // only if !atom + 96 ~string_tree(); + 97 string_tree(const string_tree& original); + 98 // atomic string + 99 explicit string_tree(string v) :atom(true), value(v), left(NULL), right(NULL) {} +100 // tree of strings +101 string_tree(string_tree* l, string_tree* r) :atom(false), left(l), right(r) {} +102 }; +103 +104 // End type_tree Definition +105 :(code) +106 type_tree::type_tree(string name) :atom(true), name(name), value(get(Type_ordinal, name)), left(NULL), right(NULL) {} +107 +108 :(before "End Globals") +109 // Locations refer to a common 'memory'. Each location can store a number. +110 map<int, double> Memory; +111 :(before "End Setup") +112 Memory.clear(); +113 +114 :(after "Types") +115 // Mu types encode how the numbers stored in different parts of memory are +116 // interpreted. A location tagged as a 'character' type will interpret the +117 // value 97 as the letter 'a', while a different location of type 'number' +118 // would not. +119 // +120 // Unlike most computers today, Mu stores types in a single big table, shared +121 // by all the Mu programs on the computer. This is useful in providing a +122 // seamless experience to help understand arbitrary Mu programs. +123 typedef int type_ordinal; +124 :(before "End Globals") +125 map<string, type_ordinal> Type_ordinal; +126 map<type_ordinal, type_info> Type; +127 type_ordinal Next_type_ordinal = 1; +128 :(code) +129 void setup_types() { +130 Type.clear(); Type_ordinal.clear(); +131 put(Type_ordinal, "literal", 0); +132 Next_type_ordinal = 1; +133 // Mu Types Initialization +134 type_ordinal number = put(Type_ordinal, "number", Next_type_ordinal++); +135 get_or_insert(Type, number).name = "number"; +136 put(Type_ordinal, "location", number); // synonym of number to indicate we only care about its size +137 type_ordinal address = put(Type_ordinal, "address", Next_type_ordinal++); +138 get_or_insert(Type, address).name = "address"; +139 type_ordinal boolean = put(Type_ordinal, "boolean", Next_type_ordinal++); +140 get_or_insert(Type, boolean).name = "boolean"; +141 type_ordinal character = put(Type_ordinal, "character", Next_type_ordinal++); +142 get_or_insert(Type, character).name = "character"; +143 // Array types are a special modifier to any other type. For example, +144 // array:number or array:address:boolean. +145 type_ordinal array = put(Type_ordinal, "array", Next_type_ordinal++); +146 get_or_insert(Type, array).name = "array"; +147 // End Mu Types Initialization +148 } +149 void teardown_types() { +150 for (map<type_ordinal, type_info>::iterator p = Type.begin(); p != Type.end(); ++p) { +151 for (int i = 0; i < SIZE(p->second.elements); ++i) +152 p->second.elements.clear(); +153 } +154 Type_ordinal.clear(); +155 } +156 :(before "End One-time Setup") +157 setup_types(); +158 atexit(teardown_types); +159 +160 :(before "End Types") +161 // You can construct arbitrary new types. New types are either 'containers' +162 // with multiple 'elements' of other types, or 'exclusive containers' containing +163 // one of multiple 'variants'. (These are similar to C structs and unions, +164 // respectively, though exclusive containers implicitly include a tag element +165 // recording which variant they should be interpreted as.) +166 // +167 // For example, storing bank balance and name for an account might require a +168 // container, but if bank accounts may be either for individuals or groups, +169 // with different properties for each, that may require an exclusive container +170 // whose variants are individual-account and joint-account containers. +171 enum kind_of_type { +172 PRIMITIVE, +173 CONTAINER, +174 EXCLUSIVE_CONTAINER +175 }; +176 +177 struct type_info { +178 string name; +179 kind_of_type kind; +180 vector<reagent> elements; +181 // End type_info Fields +182 type_info() :kind(PRIMITIVE) { +183 // End type_info Constructor +184 } +185 }; +186 +187 enum primitive_recipes { +188 IDLE = 0, +189 COPY, +190 // End Primitive Recipe Declarations +191 MAX_PRIMITIVE_RECIPES, +192 }; +193 :(code) +194 //: It's all very well to construct recipes out of other recipes, but we need +195 //: to know how to do *something* out of the box. For the following +196 //: recipes there are only codes, no entries in the book, because Mu just knows +197 //: what to do for them. +198 void setup_recipes() { +199 Recipe.clear(); Recipe_ordinal.clear(); +200 put(Recipe_ordinal, "idle", IDLE); +201 // Primitive Recipe Numbers +202 put(Recipe_ordinal, "copy", COPY); +203 // End Primitive Recipe Numbers +204 } +205 //: We could just reset the recipe table after every test, but that gets slow +206 //: all too quickly. Instead, initialize the common stuff just once at +207 //: startup. Later layers will carefully undo each test's additions after +208 //: itself. +209 :(before "End One-time Setup") +210 setup_recipes(); +211 assert(MAX_PRIMITIVE_RECIPES < 200); // level 0 is primitives; until 199 +212 Next_recipe_ordinal = 200; +213 put(Recipe_ordinal, "main", Next_recipe_ordinal++); +214 // End Load Recipes +215 :(before "End Commandline Parsing") +216 assert(Next_recipe_ordinal < 1000); // recipes being tested didn't overflow into test space +217 :(before "End Setup") +218 Next_recipe_ordinal = 1000; // consistent new numbers for each test +219 +220 //: One final detail: tests can modify our global tables of recipes and types, +221 //: so we need some way to clean up after each test is done so it doesn't +222 //: influence later ones. +223 :(before "End Globals") +224 map<string, recipe_ordinal> Recipe_ordinal_snapshot; +225 map<recipe_ordinal, recipe> Recipe_snapshot; +226 map<string, type_ordinal> Type_ordinal_snapshot; +227 map<type_ordinal, type_info> Type_snapshot; +228 :(before "End One-time Setup") +229 save_snapshots(); +230 :(before "End Setup") +231 restore_snapshots(); +232 +233 :(code) +234 void save_snapshots() { +235 Recipe_ordinal_snapshot = Recipe_ordinal; +236 Recipe_snapshot = Recipe; +237 Type_ordinal_snapshot = Type_ordinal; +238 Type_snapshot = Type; +239 // End save_snapshots +240 } +241 +242 void restore_snapshots() { +243 Recipe = Recipe_snapshot; +244 Recipe_ordinal = Recipe_ordinal_snapshot; +245 restore_non_recipe_snapshots(); +246 } +247 // when running sandboxes in the edit/ app we'll want to restore everything except recipes defined in the app +248 void restore_non_recipe_snapshots() { +249 Type_ordinal = Type_ordinal_snapshot; +250 Type = Type_snapshot; +251 // End restore_snapshots +252 } +253 +254 //:: Helpers +255 +256 :(code) +257 recipe::recipe() { +258 // End recipe Constructor +259 } +260 +261 instruction::instruction() :is_label(false), operation(IDLE) { +262 // End instruction Constructor +263 } +264 void instruction::clear() { +265 is_label=false; +266 label.clear(); +267 name.clear(); +268 operation=IDLE; +269 ingredients.clear(); +270 products.clear(); +271 original_string.clear(); +272 // End instruction Clear +273 } +274 bool instruction::is_empty() { return !is_label && name.empty(); } +275 +276 // Reagents have the form <name>:<type>:<type>:.../<property>/<property>/... +277 reagent::reagent(const string& s) :original_string(s), type(NULL), value(0), initialized(false) { +278 // Parsing reagent(string s) +279 istringstream in(s); +280 in >> std::noskipws; +281 // name and type +282 istringstream first_row(slurp_until(in, '/')); +283 first_row >> std::noskipws; +284 name = slurp_until(first_row, ':'); +285 string_tree* type_names = parse_property_list(first_row); +286 // End Parsing Reagent Type Property(type_names) +287 type = new_type_tree(type_names); +288 delete type_names; +289 // special cases +290 if (is_integer(name) && type == NULL) +291 type = new type_tree("literal"); +292 if (name == "_" && type == NULL) +293 type = new type_tree("literal"); +294 // other properties +295 slurp_properties(in, properties); +296 // End Parsing reagent +297 } +298 +299 void slurp_properties(istream& in, vector<pair<string, string_tree*> >& out) { +300 while (has_data(in)) { +301 istringstream row(slurp_until(in, '/')); +302 row >> std::noskipws; +303 string key = slurp_until(row, ':'); +304 string_tree* value = parse_property_list(row); +305 out.push_back(pair<string, string_tree*>(key, value)); +306 } +307 } +308 +309 string_tree* parse_property_list(istream& in) { +310 skip_whitespace_but_not_newline(in); +311 if (!has_data(in)) return NULL; +312 string_tree* first = new string_tree(slurp_until(in, ':')); +313 if (!has_data(in)) return first; +314 string_tree* rest = parse_property_list(in); +315 if (!has_data(in) && rest->atom) +316 return new string_tree(first, new string_tree(rest, NULL)); +317 return new string_tree(first, rest); +318 } +319 :(before "End Unit Tests") +320 void test_parse_property_list_atom() { +321 istringstream in("a"); +322 string_tree* x = parse_property_list(in); +323 CHECK(x->atom); +324 delete x; +325 } +326 void test_parse_property_list_list() { +327 istringstream in("a:b"); +328 string_tree* x = parse_property_list(in); +329 CHECK(!x->atom); +330 CHECK(x->left->atom); +331 CHECK_EQ(x->left->value, "a"); +332 CHECK(!x->right->atom); +333 CHECK(x->right->left->atom); +334 CHECK_EQ(x->right->left->value, "b"); +335 CHECK(x->right->right == NULL); +336 delete x; +337 } +338 +339 :(code) +340 type_tree* new_type_tree(const string_tree* properties) { +341 if (!properties) return NULL; +342 if (properties->atom) { +343 const string& type_name = properties->value; +344 int value = 0; +345 if (contains_key(Type_ordinal, type_name)) +346 value = get(Type_ordinal, type_name); +347 else if (is_integer(type_name)) // sometimes types will contain non-type tags, like numbers for the size of an array +348 value = 0; +349 else if (properties->value == "->") // used in recipe types +350 value = 0; +351 else +352 value = -1; // should never happen; will trigger errors later +353 return new type_tree(type_name, value); +354 } +355 return new type_tree(new_type_tree(properties->left), +356 new_type_tree(properties->right)); +357 } +358 +359 //: avoid memory leaks for the type tree +360 +361 reagent::reagent(const reagent& other) { +362 original_string = other.original_string; +363 name = other.name; +364 value = other.value; +365 initialized = other.initialized; +366 for (int i = 0; i < SIZE(other.properties); ++i) { +367 properties.push_back(pair<string, string_tree*>(other.properties.at(i).first, copy(other.properties.at(i).second))); +368 } +369 type = copy(other.type); +370 // End reagent Copy Constructor +371 } +372 +373 type_tree::type_tree(const type_tree& original) { +374 atom = original.atom; +375 name = original.name; +376 value = original.value; +377 left = copy(original.left); +378 right = copy(original.right); +379 } +380 +381 type_tree& type_tree::operator=(const type_tree& original) { +382 atom = original.atom; +383 name = original.name; +384 value = original.value; +385 if (left) delete left; +386 left = copy(original.left); +387 if (right) delete right; +388 right = copy(original.right); +389 return *this; +390 } +391 +392 bool type_tree::operator==(const type_tree& other) const { +393 if (atom != other.atom) return false; +394 if (atom) +395 return name == other.name && value == other.value; +396 return (left == other.left || *left == *other.left) +397 && (right == other.right || *right == *other.right); +398 } +399 +400 // only constraint we care about: if a < b then !(b < a) +401 bool type_tree::operator<(const type_tree& other) const { +402 if (atom != other.atom) return atom > other.atom; // atoms before non-atoms +403 if (atom) return name < other.name; // sort atoms in lexical order +404 // first location in one that's missing in the other makes that side 'smaller' +405 if (left && !other.left) return false; +406 if (!left && other.left) return true; +407 if (right && !other.right) return false; +408 if (!right && other.right) return true; +409 // now if either pointer is unequal neither side can be null +410 // if one side is equal that's easy +411 if (left == other.left || *left == *other.left) return right && *right < *other.right; +412 if (right == other.right || *right == *other.right) return left && *left < *other.left; +413 // if the two sides criss-cross, pick the side with the smaller lhs +414 if ((left == other.right || *left == *other.right) +415 && (right == other.left || *right == *other.left)) +416 return *left < *other.left; +417 // now the hard case: both sides are not equal +418 // make sure we stay consistent between (a < b) and (b < a) +419 // just return the side with the smallest of the 4 branches +420 if (*left < *other.left && *left < *other.right) return true; +421 if (*right < *other.left && *right < *other.right) return true; +422 return false; 423 } -424 void test_compare_equal_atom_types() { -425 reagent a("a:address"), b("b:address"); -426 CHECK(!(*a.type < *b.type)); -427 CHECK(!(*b.type < *a.type)); -428 } -429 void test_compare_atom_with_non_atom() { -430 reagent a("a:address:number"), b("b:boolean"); -431 CHECK(!(*a.type < *b.type)); -432 CHECK(*b.type < *a.type); -433 } -434 void test_compare_lists_with_identical_structure() { -435 reagent a("a:address:address"), b("b:address:boolean"); -436 CHECK(*a.type < *b.type); -437 CHECK(!(*b.type < *a.type)); -438 } -439 void test_compare_identical_lists() { -440 reagent a("a:address:boolean"), b("b:address:boolean"); -441 CHECK(!(*a.type < *b.type)); -442 CHECK(!(*b.type < *a.type)); -443 } -444 void test_compare_list_with_extra_element() { -445 reagent a("a:address:address"), b("b:address:address:number"); -446 CHECK(*a.type < *b.type); -447 CHECK(!(*b.type < *a.type)); -448 } -449 void test_compare_list_with_smaller_left_but_larger_right() { -450 reagent a("a:address:number"), b("b:character:array"); -451 CHECK(*a.type < *b.type); -452 CHECK(!(*b.type < *a.type)); -453 } -454 void test_compare_list_with_smaller_left_but_larger_right_identical_types() { -455 reagent a("a:address:boolean"), b("b:boolean:address"); -456 CHECK(*a.type < *b.type); -457 CHECK(!(*b.type < *a.type)); -458 } -459 -460 :(code) -461 string_tree::string_tree(const string_tree& original) { -462 atom = original.atom; -463 value = original.value; -464 left = original.left ? new string_tree(*original.left) : NULL; -465 right = original.right ? new string_tree(*original.right) : NULL; -466 } -467 -468 reagent& reagent::operator=(const reagent& other) { -469 original_string = other.original_string; -470 for (int i = 0; i < SIZE(properties); ++i) -471 if (properties.at(i).second) delete properties.at(i).second; -472 properties.clear(); -473 for (int i = 0; i < SIZE(other.properties); ++i) -474 properties.push_back(pair<string, string_tree*>(other.properties.at(i).first, other.properties.at(i).second ? new string_tree(*other.properties.at(i).second) : NULL)); -475 name = other.name; -476 value = other.value; -477 initialized = other.initialized; -478 if (type) delete type; -479 type = other.type ? new type_tree(*other.type) : NULL; -480 // End reagent Copy Operator -481 return *this; -482 } -483 -484 reagent::~reagent() { -485 clear(); -486 } -487 -488 void reagent::clear() { -489 for (int i = 0; i < SIZE(properties); ++i) { -490 if (properties.at(i).second) { -491 delete properties.at(i).second; -492 properties.at(i).second = NULL; -493 } -494 } -495 delete type; -496 type = NULL; -497 } -498 type_tree::~type_tree() { -499 delete left; -500 delete right; -501 } -502 string_tree::~string_tree() { -503 delete left; -504 delete right; -505 } -506 -507 void append(type_tree*& base, type_tree* extra) { -508 if (!base) { -509 base = extra; -510 return; -511 } -512 type_tree* curr = base; -513 while (curr->right) curr = curr->right; -514 curr->right = extra; -515 } -516 -517 void append(string_tree*& base, string_tree* extra) { -518 if (!base) { -519 base = extra; -520 return; -521 } -522 string_tree* curr = base; -523 while (curr->right) curr = curr->right; -524 curr->right = extra; -525 } -526 -527 string slurp_until(istream& in, char delim) { -528 ostringstream out; -529 char c; -530 while (in >> c) { -531 if (c == delim) { -532 // drop the delim -533 break; -534 } -535 out << c; -536 } -537 return out.str(); -538 } -539 -540 bool has_property(const reagent& x, const string& name) { -541 for (int i = 0; i < SIZE(x.properties); ++i) { -542 if (x.properties.at(i).first == name) return true; +424 :(before "End Unit Tests") +425 // These unit tests don't always use valid types. +426 void test_compare_atom_types() { +427 reagent a("a:address"), b("b:boolean"); +428 CHECK(*a.type < *b.type); +429 CHECK(!(*b.type < *a.type)); +430 } +431 void test_compare_equal_atom_types() { +432 reagent a("a:address"), b("b:address"); +433 CHECK(!(*a.type < *b.type)); +434 CHECK(!(*b.type < *a.type)); +435 } +436 void test_compare_atom_with_non_atom() { +437 reagent a("a:address:number"), b("b:boolean"); +438 CHECK(!(*a.type < *b.type)); +439 CHECK(*b.type < *a.type); +440 } +441 void test_compare_lists_with_identical_structure() { +442 reagent a("a:address:address"), b("b:address:boolean"); +443 CHECK(*a.type < *b.type); +444 CHECK(!(*b.type < *a.type)); +445 } +446 void test_compare_identical_lists() { +447 reagent a("a:address:boolean"), b("b:address:boolean"); +448 CHECK(!(*a.type < *b.type)); +449 CHECK(!(*b.type < *a.type)); +450 } +451 void test_compare_list_with_extra_element() { +452 reagent a("a:address:address"), b("b:address:address:number"); +453 CHECK(*a.type < *b.type); +454 CHECK(!(*b.type < *a.type)); +455 } +456 void test_compare_list_with_smaller_left_but_larger_right() { +457 reagent a("a:address:number"), b("b:character:array"); +458 CHECK(*a.type < *b.type); +459 CHECK(!(*b.type < *a.type)); +460 } +461 void test_compare_list_with_smaller_left_but_larger_right_identical_types() { +462 reagent a("a:address:boolean"), b("b:boolean:address"); +463 CHECK(*a.type < *b.type); +464 CHECK(!(*b.type < *a.type)); +465 } +466 +467 :(code) +468 string_tree::string_tree(const string_tree& original) { +469 atom = original.atom; +470 value = original.value; +471 left = copy(original.left); +472 right = copy(original.right); +473 } +474 +475 reagent& reagent::operator=(const reagent& other) { +476 original_string = other.original_string; +477 for (int i = 0; i < SIZE(properties); ++i) +478 if (properties.at(i).second) delete properties.at(i).second; +479 properties.clear(); +480 for (int i = 0; i < SIZE(other.properties); ++i) +481 properties.push_back(pair<string, string_tree*>(other.properties.at(i).first, copy(other.properties.at(i).second))); +482 name = other.name; +483 value = other.value; +484 initialized = other.initialized; +485 if (type) delete type; +486 type = copy(other.type); +487 // End reagent Copy Operator +488 return *this; +489 } +490 +491 reagent::~reagent() { +492 clear(); +493 } +494 +495 void reagent::clear() { +496 for (int i = 0; i < SIZE(properties); ++i) { +497 if (properties.at(i).second) { +498 delete properties.at(i).second; +499 properties.at(i).second = NULL; +500 } +501 } +502 delete type; +503 type = NULL; +504 } +505 type_tree::~type_tree() { +506 delete left; +507 delete right; +508 } +509 string_tree::~string_tree() { +510 delete left; +511 delete right; +512 } +513 +514 void append(type_tree*& base, type_tree* extra) { +515 if (!base) { +516 base = extra; +517 return; +518 } +519 type_tree* curr = base; +520 while (curr->right) curr = curr->right; +521 curr->right = extra; +522 } +523 +524 void append(string_tree*& base, string_tree* extra) { +525 if (!base) { +526 base = extra; +527 return; +528 } +529 string_tree* curr = base; +530 while (curr->right) curr = curr->right; +531 curr->right = extra; +532 } +533 +534 string slurp_until(istream& in, char delim) { +535 ostringstream out; +536 char c; +537 while (in >> c) { +538 if (c == delim) { +539 // drop the delim +540 break; +541 } +542 out << c; 543 } -544 return false; +544 return out.str(); 545 } 546 -547 string_tree* property(const reagent& r, const string& name) { -548 for (int p = 0; p != SIZE(r.properties); ++p) { -549 if (r.properties.at(p).first == name) -550 return r.properties.at(p).second; -551 } -552 return NULL; -553 } -554 -555 :(before "End Globals") -556 extern const string Ignore(","); // commas are ignored in Mu except within [] strings -557 :(code) -558 void skip_whitespace_but_not_newline(istream& in) { -559 while (true) { -560 if (!has_data(in)) break; -561 else if (in.peek() == '\n') break; -562 else if (isspace(in.peek())) in.get(); -563 else if (Ignore.find(in.peek()) != string::npos) in.get(); -564 else break; -565 } -566 } -567 -568 void dump_memory() { -569 for (map<int, double>::iterator p = Memory.begin(); p != Memory.end(); ++p) { -570 cout << p->first << ": " << no_scientific(p->second) << '\n'; -571 } -572 } -573 -574 //:: Helpers for converting various values to string -575 //: Use to_string() in trace(), and try to keep it stable from run to run. -576 //: Use debug_string() while debugging, and throw everything into it. -577 //: Use inspect() only for emitting a canonical format that can be parsed back -578 //: into the value. -579 -580 string to_string(const recipe& r) { -581 ostringstream out; -582 out << "recipe " << r.name << " [\n"; -583 for (int i = 0; i < SIZE(r.steps); ++i) -584 out << " " << to_string(r.steps.at(i)) << '\n'; -585 out << "]\n"; -586 return out.str(); -587 } -588 -589 string debug_string(const recipe& x) { -590 ostringstream out; -591 out << "- recipe " << x.name << '\n'; -592 // Begin debug_string(recipe x) -593 for (int index = 0; index < SIZE(x.steps); ++index) { -594 const instruction& inst = x.steps.at(index); -595 out << "inst: " << to_string(inst) << '\n'; -596 out << " ingredients\n"; -597 for (int i = 0; i < SIZE(inst.ingredients); ++i) -598 out << " " << debug_string(inst.ingredients.at(i)) << '\n'; -599 out << " products\n"; -600 for (int i = 0; i < SIZE(inst.products); ++i) -601 out << " " << debug_string(inst.products.at(i)) << '\n'; -602 } +547 bool has_property(const reagent& x, const string& name) { +548 for (int i = 0; i < SIZE(x.properties); ++i) { +549 if (x.properties.at(i).first == name) return true; +550 } +551 return false; +552 } +553 +554 string_tree* property(const reagent& r, const string& name) { +555 for (int p = 0; p != SIZE(r.properties); ++p) { +556 if (r.properties.at(p).first == name) +557 return r.properties.at(p).second; +558 } +559 return NULL; +560 } +561 +562 string_tree* copy(const string_tree* x) { +563 if (x == NULL) return NULL; +564 return new string_tree(*x); +565 } +566 +567 type_tree* copy(const type_tree* x) { +568 if (x == NULL) return NULL; +569 return new type_tree(*x); +570 } +571 +572 :(before "End Globals") +573 extern const string Ignore(","); // commas are ignored in Mu except within [] strings +574 :(code) +575 void skip_whitespace_but_not_newline(istream& in) { +576 while (true) { +577 if (!has_data(in)) break; +578 else if (in.peek() == '\n') break; +579 else if (isspace(in.peek())) in.get(); +580 else if (Ignore.find(in.peek()) != string::npos) in.get(); +581 else break; +582 } +583 } +584 +585 void dump_memory() { +586 for (map<int, double>::iterator p = Memory.begin(); p != Memory.end(); ++p) { +587 cout << p->first << ": " << no_scientific(p->second) << '\n'; +588 } +589 } +590 +591 //:: Helpers for converting various values to string +592 //: Use to_string() in trace(), and try to keep it stable from run to run. +593 //: Use debug_string() while debugging, and throw everything into it. +594 //: Use inspect() only for emitting a canonical format that can be parsed back +595 //: into the value. +596 +597 string to_string(const recipe& r) { +598 ostringstream out; +599 out << "recipe " << r.name << " [\n"; +600 for (int i = 0; i < SIZE(r.steps); ++i) +601 out << " " << to_string(r.steps.at(i)) << '\n'; +602 out << "]\n"; 603 return out.str(); 604 } 605 -606 string to_original_string(const instruction& inst) { -607 if (inst.is_label) return inst.label; -608 ostringstream out; -609 for (int i = 0; i < SIZE(inst.products); ++i) { -610 if (i > 0) out << ", "; -611 out << inst.products.at(i).original_string; -612 } -613 if (!inst.products.empty()) out << " <- "; -614 out << inst.name << ' '; -615 for (int i = 0; i < SIZE(inst.ingredients); ++i) { -616 if (i > 0) out << ", "; -617 out << inst.ingredients.at(i).original_string; -618 } -619 return out.str(); -620 } -621 -622 string to_string(const instruction& inst) { -623 if (inst.is_label) return inst.label; -624 ostringstream out; -625 for (int i = 0; i < SIZE(inst.products); ++i) { -626 if (i > 0) out << ", "; -627 out << to_string(inst.products.at(i)); -628 } -629 if (!inst.products.empty()) out << " <- "; -630 out << inst.name << ' '; -631 for (int i = 0; i < SIZE(inst.ingredients); ++i) { -632 if (i > 0) out << ", "; -633 out << to_string(inst.ingredients.at(i)); -634 } -635 return out.str(); -636 } -637 -638 string to_string(const reagent& r) { -639 if (is_dummy(r)) return "_"; -640 ostringstream out; -641 out << "{"; -642 out << r.name << ": " << names_to_string(r.type); -643 if (!r.properties.empty()) { -644 for (int i = 0; i < SIZE(r.properties); ++i) -645 out << ", \"" << r.properties.at(i).first << "\": " << to_string(r.properties.at(i).second); -646 } -647 out << "}"; -648 return out.str(); -649 } -650 -651 // special name for ignoring some products -652 bool is_dummy(const reagent& x) { -653 return x.name == "_"; -654 } -655 -656 string debug_string(const reagent& x) { +606 string debug_string(const recipe& x) { +607 ostringstream out; +608 out << "- recipe " << x.name << '\n'; +609 // Begin debug_string(recipe x) +610 for (int index = 0; index < SIZE(x.steps); ++index) { +611 const instruction& inst = x.steps.at(index); +612 out << "inst: " << to_string(inst) << '\n'; +613 out << " ingredients\n"; +614 for (int i = 0; i < SIZE(inst.ingredients); ++i) +615 out << " " << debug_string(inst.ingredients.at(i)) << '\n'; +616 out << " products\n"; +617 for (int i = 0; i < SIZE(inst.products); ++i) +618 out << " " << debug_string(inst.products.at(i)) << '\n'; +619 } +620 return out.str(); +621 } +622 +623 string to_original_string(const instruction& inst) { +624 if (inst.is_label) return inst.label; +625 ostringstream out; +626 for (int i = 0; i < SIZE(inst.products); ++i) { +627 if (i > 0) out << ", "; +628 out << inst.products.at(i).original_string; +629 } +630 if (!inst.products.empty()) out << " <- "; +631 out << inst.name << ' '; +632 for (int i = 0; i < SIZE(inst.ingredients); ++i) { +633 if (i > 0) out << ", "; +634 out << inst.ingredients.at(i).original_string; +635 } +636 return out.str(); +637 } +638 +639 string to_string(const instruction& inst) { +640 if (inst.is_label) return inst.label; +641 ostringstream out; +642 for (int i = 0; i < SIZE(inst.products); ++i) { +643 if (i > 0) out << ", "; +644 out << to_string(inst.products.at(i)); +645 } +646 if (!inst.products.empty()) out << " <- "; +647 out << inst.name << ' '; +648 for (int i = 0; i < SIZE(inst.ingredients); ++i) { +649 if (i > 0) out << ", "; +650 out << to_string(inst.ingredients.at(i)); +651 } +652 return out.str(); +653 } +654 +655 string to_string(const reagent& r) { +656 if (is_dummy(r)) return "_"; 657 ostringstream out; -658 out << x.name << ": " << x.value << ' ' << to_string(x.type) << " -- " << to_string(x); -659 return out.str(); -660 } -661 -662 string to_string(const string_tree* property) { -663 if (!property) return "()"; -664 ostringstream out; -665 dump(property, out); -666 return out.str(); -667 } -668 -669 void dump(const string_tree* x, ostream& out) { -670 if (!x) return; -671 if (x->atom) { -672 out << '"' << x->value << '"'; -673 return; -674 } -675 out << '('; -676 const string_tree* curr = x; -677 while (curr && !curr->atom) { -678 dump(curr->left, out); -679 if (curr->right) out << ' '; -680 curr = curr->right; -681 } -682 // check for dotted list; should never happen -683 if (curr) { -684 out << ". "; -685 dump(curr, out); -686 } -687 out << ')'; -688 } -689 -690 string to_string(const type_tree* type) { -691 // abbreviate a single-node tree to just its contents -692 if (!type) return "NULLNULLNULL"; // should never happen -693 ostringstream out; -694 dump(type, out); -695 return out.str(); -696 } -697 -698 void dump(const type_tree* x, ostream& out) { -699 if (!x) return; -700 if (x->atom) { -701 dump(x->value, out); -702 return; +658 out << "{"; +659 out << r.name << ": " << names_to_string(r.type); +660 if (!r.properties.empty()) { +661 for (int i = 0; i < SIZE(r.properties); ++i) +662 out << ", \"" << r.properties.at(i).first << "\": " << to_string(r.properties.at(i).second); +663 } +664 out << "}"; +665 return out.str(); +666 } +667 +668 // special name for ignoring some products +669 bool is_dummy(const reagent& x) { +670 return x.name == "_"; +671 } +672 +673 string debug_string(const reagent& x) { +674 ostringstream out; +675 out << x.name << ": " << x.value << ' ' << to_string(x.type) << " -- " << to_string(x); +676 return out.str(); +677 } +678 +679 string to_string(const string_tree* property) { +680 if (!property) return "()"; +681 ostringstream out; +682 dump(property, out); +683 return out.str(); +684 } +685 +686 void dump(const string_tree* x, ostream& out) { +687 if (!x) return; +688 if (x->atom) { +689 out << '"' << x->value << '"'; +690 return; +691 } +692 out << '('; +693 const string_tree* curr = x; +694 while (curr && !curr->atom) { +695 dump(curr->left, out); +696 if (curr->right) out << ' '; +697 curr = curr->right; +698 } +699 // check for dotted list; should never happen +700 if (curr) { +701 out << ". "; +702 dump(curr, out); 703 } -704 out << '('; -705 const type_tree* curr = x; -706 while (curr && !curr->atom) { -707 dump(curr->left, out); -708 if (curr->right) out << ' '; -709 curr = curr->right; -710 } -711 // check for dotted list; should never happen -712 if (curr) { -713 out << ". "; -714 dump(curr, out); -715 } -716 out << ')'; -717 } -718 -719 void dump(type_ordinal type, ostream& out) { -720 if (contains_key(Type, type)) -721 out << get(Type, type).name; -722 else -723 out << "?" << type; -724 } -725 -726 string names_to_string(const type_tree* type) { -727 // abbreviate a single-node tree to just its contents -728 if (!type) return "()"; // should never happen -729 ostringstream out; -730 dump_names(type, out); -731 return out.str(); -732 } -733 -734 void dump_names(const type_tree* x, ostream& out) { -735 if (!x) return; -736 if (x->atom) { -737 out << '"' << x->name << '"'; -738 return; -739 } -740 out << '('; -741 const type_tree* curr = x; -742 while (curr && !curr->atom) { -743 dump_names(curr->left, out); -744 if (curr->right) out << ' '; -745 curr = curr->right; -746 } -747 // check for dotted list; should never happen -748 if (curr) { -749 out << ". "; -750 dump_names(curr, out); -751 } -752 out << ')'; -753 } -754 -755 string names_to_string_without_quotes(const type_tree* type) { -756 // abbreviate a single-node tree to just its contents -757 if (!type) return "NULLNULLNULL"; // should never happen -758 ostringstream out; -759 dump_names_without_quotes(type, out); -760 return out.str(); -761 } -762 -763 void dump_names_without_quotes(const type_tree* x, ostream& out) { -764 if (!x) return; -765 if (x->atom) { -766 out << x->name; -767 return; +704 out << ')'; +705 } +706 +707 string to_string(const type_tree* type) { +708 // abbreviate a single-node tree to just its contents +709 if (!type) return "NULLNULLNULL"; // should never happen +710 ostringstream out; +711 dump(type, out); +712 return out.str(); +713 } +714 +715 void dump(const type_tree* x, ostream& out) { +716 if (!x) return; +717 if (x->atom) { +718 dump(x->value, out); +719 return; +720 } +721 out << '('; +722 const type_tree* curr = x; +723 while (curr && !curr->atom) { +724 dump(curr->left, out); +725 if (curr->right) out << ' '; +726 curr = curr->right; +727 } +728 // check for dotted list; should never happen +729 if (curr) { +730 out << ". "; +731 dump(curr, out); +732 } +733 out << ')'; +734 } +735 +736 void dump(type_ordinal type, ostream& out) { +737 if (contains_key(Type, type)) +738 out << get(Type, type).name; +739 else +740 out << "?" << type; +741 } +742 +743 string names_to_string(const type_tree* type) { +744 // abbreviate a single-node tree to just its contents +745 if (!type) return "()"; // should never happen +746 ostringstream out; +747 dump_names(type, out); +748 return out.str(); +749 } +750 +751 void dump_names(const type_tree* x, ostream& out) { +752 if (!x) return; +753 if (x->atom) { +754 out << '"' << x->name << '"'; +755 return; +756 } +757 out << '('; +758 const type_tree* curr = x; +759 while (curr && !curr->atom) { +760 dump_names(curr->left, out); +761 if (curr->right) out << ' '; +762 curr = curr->right; +763 } +764 // check for dotted list; should never happen +765 if (curr) { +766 out << ". "; +767 dump_names(curr, out); 768 } -769 out << '('; -770 const type_tree* curr = x; -771 while (curr && !curr->atom) { -772 dump_names_without_quotes(curr->left, out); -773 if (curr->right) out << ' '; -774 curr = curr->right; -775 } -776 // check for dotted list; should never happen -777 if (curr) { -778 out << ". "; -779 dump_names_without_quotes(curr, out); -780 } -781 out << ')'; -782 } -783 -784 //: helper to print numbers without excessive precision -785 -786 :(before "End Types") -787 struct no_scientific { -788 double x; -789 explicit no_scientific(double y) :x(y) {} -790 }; -791 -792 :(code) -793 ostream& operator<<(ostream& os, no_scientific x) { -794 if (!isfinite(x.x)) { -795 // Infinity or NaN -796 os << x.x; -797 return os; -798 } -799 ostringstream tmp; -800 // more accurate, but too slow -801 //? tmp.precision(308); // for 64-bit numbers -802 tmp << std::fixed << x.x; -803 os << trim_floating_point(tmp.str()); -804 return os; -805 } -806 -807 string trim_floating_point(const string& in) { -808 if (in.empty()) return ""; -809 if (in.find('.') == string::npos) return in; -810 int length = SIZE(in); -811 while (length > 1) { -812 if (in.at(length-1) != '0') break; -813 --length; -814 } -815 if (in.at(length-1) == '.') --length; -816 if (length == 0) return "0"; -817 return in.substr(0, length); -818 } -819 -820 void test_trim_floating_point() { -821 CHECK_EQ(trim_floating_point(""), ""); -822 CHECK_EQ(trim_floating_point(".0"), "0"); -823 CHECK_EQ(trim_floating_point("1.5000"), "1.5"); -824 CHECK_EQ(trim_floating_point("1.000001"), "1.000001"); -825 CHECK_EQ(trim_floating_point("23.000000"), "23"); -826 CHECK_EQ(trim_floating_point("23.0"), "23"); -827 CHECK_EQ(trim_floating_point("23."), "23"); -828 CHECK_EQ(trim_floating_point("23"), "23"); -829 CHECK_EQ(trim_floating_point("230"), "230"); -830 CHECK_EQ(trim_floating_point("3.000000"), "3"); -831 CHECK_EQ(trim_floating_point("3.0"), "3"); -832 CHECK_EQ(trim_floating_point("3."), "3"); -833 CHECK_EQ(trim_floating_point("3"), "3"); -834 } -835 -836 :(before "End Includes") -837 #include <utility> -838 using std::pair; -839 #include <math.h> +769 out << ')'; +770 } +771 +772 string names_to_string_without_quotes(const type_tree* type) { +773 // abbreviate a single-node tree to just its contents +774 if (!type) return "NULLNULLNULL"; // should never happen +775 ostringstream out; +776 dump_names_without_quotes(type, out); +777 return out.str(); +778 } +779 +780 void dump_names_without_quotes(const type_tree* x, ostream& out) { +781 if (!x) return; +782 if (x->atom) { +783 out << x->name; +784 return; +785 } +786 out << '('; +787 const type_tree* curr = x; +788 while (curr && !curr->atom) { +789 dump_names_without_quotes(curr->left, out); +790 if (curr->right) out << ' '; +791 curr = curr->right; +792 } +793 // check for dotted list; should never happen +794 if (curr) { +795 out << ". "; +796 dump_names_without_quotes(curr, out); +797 } +798 out << ')'; +799 } +800 +801 //: helper to print numbers without excessive precision +802 +803 :(before "End Types") +804 struct no_scientific { +805 double x; +806 explicit no_scientific(double y) :x(y) {} +807 }; +808 +809 :(code) +810 ostream& operator<<(ostream& os, no_scientific x) { +811 if (!isfinite(x.x)) { +812 // Infinity or NaN +813 os << x.x; +814 return os; +815 } +816 ostringstream tmp; +817 // more accurate, but too slow +818 //? tmp.precision(308); // for 64-bit numbers +819 tmp << std::fixed << x.x; +820 os << trim_floating_point(tmp.str()); +821 return os; +822 } +823 +824 string trim_floating_point(const string& in) { +825 if (in.empty()) return ""; +826 if (in.find('.') == string::npos) return in; +827 int length = SIZE(in); +828 while (length > 1) { +829 if (in.at(length-1) != '0') break; +830 --length; +831 } +832 if (in.at(length-1) == '.') --length; +833 if (length == 0) return "0"; +834 return in.substr(0, length); +835 } +836 +837 void test_trim_floating_point() { +838 CHECK_EQ(trim_floating_point(""), ""); +839 CHECK_EQ(trim_floating_point(".0"), "0"); +840 CHECK_EQ(trim_floating_point("1.5000"), "1.5"); +841 CHECK_EQ(trim_floating_point("1.000001"), "1.000001"); +842 CHECK_EQ(trim_floating_point("23.000000"), "23"); +843 CHECK_EQ(trim_floating_point("23.0"), "23"); +844 CHECK_EQ(trim_floating_point("23."), "23"); +845 CHECK_EQ(trim_floating_point("23"), "23"); +846 CHECK_EQ(trim_floating_point("230"), "230"); +847 CHECK_EQ(trim_floating_point("3.000000"), "3"); +848 CHECK_EQ(trim_floating_point("3.0"), "3"); +849 CHECK_EQ(trim_floating_point("3."), "3"); +850 CHECK_EQ(trim_floating_point("3"), "3"); +851 } +852 +853 :(before "End Includes") +854 #include <utility> +855 using std::pair; +856 #include <math.h> diff --git a/html/011load.cc.html b/html/011load.cc.html index 886a30c7..53ccec7a 100644 --- a/html/011load.cc.html +++ b/html/011load.cc.html @@ -85,9 +85,9 @@ if ('onhashchange' in window) { 23 in >> std::noskipws; 24 vector<recipe_ordinal> result; 25 while (has_data(in)) { - 26 skip_whitespace_and_comments(in); + 26 skip_whitespace_and_comments(in); 27 if (!has_data(in)) break; - 28 string command = next_word(in); + 28 string command = next_word(in); 29 if (command.empty()) { 30 assert(!has_data(in)); 31 break; @@ -114,7 +114,7 @@ if ('onhashchange' in window) { 52 // return the recipe ordinal slurped, or -1 if it failed 53 int slurp_recipe(istream& in) { 54 recipe result; - 55 result.name = next_word(in); + 55 result.name = next_word(in); 56 if (result.name.empty()) { 57 assert(!has_data(in)); 58 raise << "file ended with 'recipe'\n" << end(); @@ -130,7 +130,7 @@ if ('onhashchange' in window) { 68 put(Recipe_ordinal, result.name, Next_recipe_ordinal++); 69 if (Recipe.find(get(Recipe_ordinal, result.name)) != Recipe.end()) { 70 trace(9991, "parse") << "already exists" << end(); - 71 if (should_check_for_redefine(result.name)) + 71 if (should_check_for_redefine(result.name)) 72 raise << "redefining recipe " << result.name << "\n" << end(); 73 Recipe.erase(get(Recipe_ordinal, result.name)); 74 } @@ -145,7 +145,7 @@ if ('onhashchange' in window) { 83 skip_whitespace_but_not_newline(in); 84 if (in.get() != '[') 85 raise << result.name << ": recipe body must begin with '['\n" << end(); - 86 skip_whitespace_and_comments(in); // permit trailing comment after '[' + 86 skip_whitespace_and_comments(in); // permit trailing comment after '[' 87 instruction curr; 88 while (next_instruction(in, &curr)) { 89 curr.original_string = to_original_string(curr); @@ -157,7 +157,7 @@ if ('onhashchange' in window) { 95 96 bool next_instruction(istream& in, instruction* curr) { 97 curr->clear(); - 98 skip_whitespace_and_comments(in); + 98 skip_whitespace_and_comments(in); 99 if (!has_data(in)) { 100 raise << "incomplete recipe at end of file (0)\n" << end(); 101 return false; @@ -170,7 +170,7 @@ if ('onhashchange' in window) { 108 raise << "incomplete recipe at end of file (1)\n" << end(); 109 return false; 110 } -111 string word = next_word(in); +111 string word = next_word(in); 112 if (word.empty()) { 113 assert(!has_data(in)); 114 raise << "incomplete recipe at end of file (2)\n" << end(); @@ -179,11 +179,11 @@ if ('onhashchange' in window) { 117 words.push_back(word); 118 skip_whitespace_but_not_newline(in); 119 } -120 skip_whitespace_and_comments(in); +120 skip_whitespace_and_comments(in); 121 if (SIZE(words) == 1 && words.at(0) == "]") 122 return false; // end of recipe 123 -124 if (SIZE(words) == 1 && is_label_word(words.at(0))) { +124 if (SIZE(words) == 1 && is_label_word(words.at(0))) { 125 curr->is_label = true; 126 curr->label = words.at(0); 127 trace(9993, "parse") << "label: " << curr->label << end(); @@ -205,7 +205,7 @@ if ('onhashchange' in window) { 143 raise << "instruction prematurely ended with '<-'\n" << end(); 144 return false; 145 } -146 curr->old_name = curr->name = *p; ++p; +146 curr->name = *p; ++p; 147 // curr->operation will be set at transform time 148 149 for (; p != words.end(); ++p) @@ -221,256 +221,257 @@ if ('onhashchange' in window) { 159 raise << "9: unbalanced '[' for recipe\n" << end(); 160 return false; 161 } -162 return true; -163 } -164 -165 // can return empty string -- only if `in` has no more data -166 string next_word(istream& in) { -167 skip_whitespace_but_not_newline(in); -168 // End next_word Special-cases -169 ostringstream out; -170 slurp_word(in, out); -171 skip_whitespace_and_comments_but_not_newline(in); -172 string result = out.str(); -173 if (result != "[" && ends_with(result, '[')) -174 raise << "insert a space before '[' in '" << result << "'\n" << end(); -175 return result; -176 } -177 -178 bool is_label_word(const string& word) { -179 if (word.empty()) return false; // error raised elsewhere -180 return !isalnum(word.at(0)) && string("$_*@&,=-[]()").find(word.at(0)) == string::npos; -181 } -182 -183 bool ends_with(const string& s, const char c) { -184 if (s.empty()) return false; -185 return *s.rbegin() == c; -186 } -187 -188 :(before "End Globals") -189 // word boundaries -190 extern const string Terminators("(){}"); -191 :(code) -192 void slurp_word(istream& in, ostream& out) { -193 char c; -194 if (has_data(in) && Terminators.find(in.peek()) != string::npos) { -195 in >> c; -196 out << c; -197 return; -198 } -199 while (in >> c) { -200 if (isspace(c) || Terminators.find(c) != string::npos || Ignore.find(c) != string::npos) { -201 in.putback(c); -202 break; -203 } -204 out << c; -205 } -206 } -207 -208 void skip_whitespace_and_comments(istream& in) { -209 while (true) { -210 if (!has_data(in)) break; -211 if (isspace(in.peek())) in.get(); -212 else if (Ignore.find(in.peek()) != string::npos) in.get(); -213 else if (in.peek() == '#') skip_comment(in); -214 else break; -215 } -216 } -217 -218 // confusing; move to the next line only to skip a comment, but never otherwise -219 void skip_whitespace_and_comments_but_not_newline(istream& in) { -220 while (true) { -221 if (!has_data(in)) break; -222 if (in.peek() == '\n') break; -223 if (isspace(in.peek())) in.get(); -224 else if (Ignore.find(in.peek()) != string::npos) in.get(); -225 else if (in.peek() == '#') skip_comment(in); -226 else break; -227 } -228 } -229 -230 void skip_comment(istream& in) { -231 if (has_data(in) && in.peek() == '#') { -232 in.get(); -233 while (has_data(in) && in.peek() != '\n') in.get(); -234 } -235 } -236 -237 :(scenario recipe_instead_of_def) -238 recipe main [ -239 1:number <- copy 23 -240 ] -241 +parse: instruction: copy -242 +parse: ingredient: {23: "literal"} -243 +parse: product: {1: "number"} -244 -245 :(scenario parse_comment_outside_recipe) -246 # this comment will be dropped by the tangler, so we need a dummy recipe to stop that -247 def f1 [ -248 ] -249 # this comment will go through to 'load' -250 def main [ -251 1:number <- copy 23 -252 ] -253 +parse: instruction: copy -254 +parse: ingredient: {23: "literal"} -255 +parse: product: {1: "number"} -256 -257 :(scenario parse_comment_amongst_instruction) -258 def main [ -259 # comment -260 1:number <- copy 23 -261 ] -262 +parse: instruction: copy -263 +parse: ingredient: {23: "literal"} -264 +parse: product: {1: "number"} -265 -266 :(scenario parse_comment_amongst_instruction_2) -267 def main [ -268 # comment -269 1:number <- copy 23 -270 # comment -271 ] -272 +parse: instruction: copy -273 +parse: ingredient: {23: "literal"} -274 +parse: product: {1: "number"} -275 -276 :(scenario parse_comment_amongst_instruction_3) -277 def main [ -278 1:number <- copy 23 -279 # comment -280 2:number <- copy 23 -281 ] -282 +parse: instruction: copy -283 +parse: ingredient: {23: "literal"} -284 +parse: product: {1: "number"} -285 +parse: instruction: copy -286 +parse: ingredient: {23: "literal"} -287 +parse: product: {2: "number"} -288 -289 :(scenario parse_comment_after_instruction) -290 def main [ -291 1:number <- copy 23 # comment -292 ] -293 +parse: instruction: copy -294 +parse: ingredient: {23: "literal"} -295 +parse: product: {1: "number"} -296 -297 :(scenario parse_label) -298 def main [ -299 +foo -300 ] -301 +parse: label: +foo -302 -303 :(scenario parse_dollar_as_recipe_name) -304 def main [ -305 $foo -306 ] -307 +parse: instruction: $foo -308 -309 :(scenario parse_multiple_properties) -310 def main [ -311 1:number <- copy 23/foo:bar:baz -312 ] -313 +parse: instruction: copy -314 +parse: ingredient: {23: "literal", "foo": ("bar" "baz")} -315 +parse: product: {1: "number"} -316 -317 :(scenario parse_multiple_products) -318 def main [ -319 1:number, 2:number <- copy 23 -320 ] -321 +parse: instruction: copy -322 +parse: ingredient: {23: "literal"} -323 +parse: product: {1: "number"} -324 +parse: product: {2: "number"} -325 -326 :(scenario parse_multiple_ingredients) -327 def main [ -328 1:number, 2:number <- copy 23, 4:number -329 ] -330 +parse: instruction: copy -331 +parse: ingredient: {23: "literal"} -332 +parse: ingredient: {4: "number"} -333 +parse: product: {1: "number"} -334 +parse: product: {2: "number"} -335 -336 :(scenario parse_multiple_types) -337 def main [ -338 1:number, 2:address:number <- copy 23, 4:number -339 ] -340 +parse: instruction: copy -341 +parse: ingredient: {23: "literal"} -342 +parse: ingredient: {4: "number"} -343 +parse: product: {1: "number"} -344 +parse: product: {2: ("address" "number")} -345 -346 :(scenario parse_properties) -347 def main [ -348 1:address:number/lookup <- copy 23 -349 ] -350 +parse: product: {1: ("address" "number"), "lookup": ()} -351 -352 //: this test we can't represent with a scenario -353 :(code) -354 void test_parse_comment_terminated_by_eof() { -355 load("recipe main [\n" -356 " a:number <- copy 34\n" -357 "]\n" -358 "# abc"); // no newline after comment -359 cerr << "."; // termination = success -360 } -361 -362 :(scenario warn_on_missing_space_before_bracket) -363 % Hide_errors = true; -364 def main[ -365 1:number <- copy 23 -366 ] -367 +error: insert a space before '[' in 'main[' -368 -369 //: Warn if a recipe gets redefined, because large codebases can accidentally -370 //: step on their own toes. But there'll be many occasions later where -371 //: we'll want to disable the errors. -372 :(before "End Globals") -373 bool Disable_redefine_checks = false; -374 :(before "End Setup") -375 Disable_redefine_checks = false; -376 :(code) -377 bool should_check_for_redefine(const string& recipe_name) { -378 if (Disable_redefine_checks) return false; -379 return true; -380 } -381 -382 :(scenario forbid_redefining_recipes) -383 % Hide_errors = true; -384 def main [ -385 1:number <- copy 23 -386 ] -387 def main [ -388 1:number <- copy 24 -389 ] -390 +error: redefining recipe main -391 -392 :(scenario permit_forcibly_redefining_recipes) -393 def main [ -394 1:number <- copy 23 -395 ] -396 def! main [ -397 1:number <- copy 24 -398 ] -399 -error: redefining recipe main -400 $error: 0 -401 -402 :(code) -403 // for debugging -404 void show_rest_of_stream(istream& in) { -405 cerr << '^'; -406 char c; -407 while (in >> c) -408 cerr << c; -409 cerr << "$\n"; -410 exit(0); -411 } +162 // End next_instruction(curr) +163 return true; +164 } +165 +166 // can return empty string -- only if `in` has no more data +167 string next_word(istream& in) { +168 skip_whitespace_but_not_newline(in); +169 // End next_word Special-cases +170 ostringstream out; +171 slurp_word(in, out); +172 skip_whitespace_and_comments_but_not_newline(in); +173 string result = out.str(); +174 if (result != "[" && ends_with(result, '[')) +175 raise << "insert a space before '[' in '" << result << "'\n" << end(); +176 return result; +177 } +178 +179 bool is_label_word(const string& word) { +180 if (word.empty()) return false; // error raised elsewhere +181 return !isalnum(word.at(0)) && string("$_*@&,=-[]()").find(word.at(0)) == string::npos; +182 } +183 +184 bool ends_with(const string& s, const char c) { +185 if (s.empty()) return false; +186 return *s.rbegin() == c; +187 } +188 +189 :(before "End Globals") +190 // word boundaries +191 extern const string Terminators("(){}"); +192 :(code) +193 void slurp_word(istream& in, ostream& out) { +194 char c; +195 if (has_data(in) && Terminators.find(in.peek()) != string::npos) { +196 in >> c; +197 out << c; +198 return; +199 } +200 while (in >> c) { +201 if (isspace(c) || Terminators.find(c) != string::npos || Ignore.find(c) != string::npos) { +202 in.putback(c); +203 break; +204 } +205 out << c; +206 } +207 } +208 +209 void skip_whitespace_and_comments(istream& in) { +210 while (true) { +211 if (!has_data(in)) break; +212 if (isspace(in.peek())) in.get(); +213 else if (Ignore.find(in.peek()) != string::npos) in.get(); +214 else if (in.peek() == '#') skip_comment(in); +215 else break; +216 } +217 } +218 +219 // confusing; move to the next line only to skip a comment, but never otherwise +220 void skip_whitespace_and_comments_but_not_newline(istream& in) { +221 while (true) { +222 if (!has_data(in)) break; +223 if (in.peek() == '\n') break; +224 if (isspace(in.peek())) in.get(); +225 else if (Ignore.find(in.peek()) != string::npos) in.get(); +226 else if (in.peek() == '#') skip_comment(in); +227 else break; +228 } +229 } +230 +231 void skip_comment(istream& in) { +232 if (has_data(in) && in.peek() == '#') { +233 in.get(); +234 while (has_data(in) && in.peek() != '\n') in.get(); +235 } +236 } +237 +238 :(scenario recipe_instead_of_def) +239 recipe main [ +240 1:number <- copy 23 +241 ] +242 +parse: instruction: copy +243 +parse: ingredient: {23: "literal"} +244 +parse: product: {1: "number"} +245 +246 :(scenario parse_comment_outside_recipe) +247 # this comment will be dropped by the tangler, so we need a dummy recipe to stop that +248 def f1 [ +249 ] +250 # this comment will go through to 'load' +251 def main [ +252 1:number <- copy 23 +253 ] +254 +parse: instruction: copy +255 +parse: ingredient: {23: "literal"} +256 +parse: product: {1: "number"} +257 +258 :(scenario parse_comment_amongst_instruction) +259 def main [ +260 # comment +261 1:number <- copy 23 +262 ] +263 +parse: instruction: copy +264 +parse: ingredient: {23: "literal"} +265 +parse: product: {1: "number"} +266 +267 :(scenario parse_comment_amongst_instruction_2) +268 def main [ +269 # comment +270 1:number <- copy 23 +271 # comment +272 ] +273 +parse: instruction: copy +274 +parse: ingredient: {23: "literal"} +275 +parse: product: {1: "number"} +276 +277 :(scenario parse_comment_amongst_instruction_3) +278 def main [ +279 1:number <- copy 23 +280 # comment +281 2:number <- copy 23 +282 ] +283 +parse: instruction: copy +284 +parse: ingredient: {23: "literal"} +285 +parse: product: {1: "number"} +286 +parse: instruction: copy +287 +parse: ingredient: {23: "literal"} +288 +parse: product: {2: "number"} +289 +290 :(scenario parse_comment_after_instruction) +291 def main [ +292 1:number <- copy 23 # comment +293 ] +294 +parse: instruction: copy +295 +parse: ingredient: {23: "literal"} +296 +parse: product: {1: "number"} +297 +298 :(scenario parse_label) +299 def main [ +300 +foo +301 ] +302 +parse: label: +foo +303 +304 :(scenario parse_dollar_as_recipe_name) +305 def main [ +306 $foo +307 ] +308 +parse: instruction: $foo +309 +310 :(scenario parse_multiple_properties) +311 def main [ +312 1:number <- copy 23/foo:bar:baz +313 ] +314 +parse: instruction: copy +315 +parse: ingredient: {23: "literal", "foo": ("bar" "baz")} +316 +parse: product: {1: "number"} +317 +318 :(scenario parse_multiple_products) +319 def main [ +320 1:number, 2:number <- copy 23 +321 ] +322 +parse: instruction: copy +323 +parse: ingredient: {23: "literal"} +324 +parse: product: {1: "number"} +325 +parse: product: {2: "number"} +326 +327 :(scenario parse_multiple_ingredients) +328 def main [ +329 1:number, 2:number <- copy 23, 4:number +330 ] +331 +parse: instruction: copy +332 +parse: ingredient: {23: "literal"} +333 +parse: ingredient: {4: "number"} +334 +parse: product: {1: "number"} +335 +parse: product: {2: "number"} +336 +337 :(scenario parse_multiple_types) +338 def main [ +339 1:number, 2:address:number <- copy 23, 4:number +340 ] +341 +parse: instruction: copy +342 +parse: ingredient: {23: "literal"} +343 +parse: ingredient: {4: "number"} +344 +parse: product: {1: "number"} +345 +parse: product: {2: ("address" "number")} +346 +347 :(scenario parse_properties) +348 def main [ +349 1:address:number/lookup <- copy 23 +350 ] +351 +parse: product: {1: ("address" "number"), "lookup": ()} +352 +353 //: this test we can't represent with a scenario +354 :(code) +355 void test_parse_comment_terminated_by_eof() { +356 load("recipe main [\n" +357 " a:number <- copy 34\n" +358 "]\n" +359 "# abc"); // no newline after comment +360 cerr << "."; // termination = success +361 } +362 +363 :(scenario warn_on_missing_space_before_bracket) +364 % Hide_errors = true; +365 def main[ +366 1:number <- copy 23 +367 ] +368 +error: insert a space before '[' in 'main[' +369 +370 //: Warn if a recipe gets redefined, because large codebases can accidentally +371 //: step on their own toes. But there'll be many occasions later where +372 //: we'll want to disable the errors. +373 :(before "End Globals") +374 bool Disable_redefine_checks = false; +375 :(before "End Setup") +376 Disable_redefine_checks = false; +377 :(code) +378 bool should_check_for_redefine(const string& recipe_name) { +379 if (Disable_redefine_checks) return false; +380 return true; +381 } +382 +383 :(scenario forbid_redefining_recipes) +384 % Hide_errors = true; +385 def main [ +386 1:number <- copy 23 +387 ] +388 def main [ +389 1:number <- copy 24 +390 ] +391 +error: redefining recipe main +392 +393 :(scenario permit_forcibly_redefining_recipes) +394 def main [ +395 1:number <- copy 23 +396 ] +397 def! main [ +398 1:number <- copy 24 +399 ] +400 -error: redefining recipe main +401 $error: 0 +402 +403 :(code) +404 // for debugging +405 void show_rest_of_stream(istream& in) { +406 cerr << '^'; +407 char c; +408 while (in >> c) +409 cerr << c; +410 cerr << "$\n"; +411 exit(0); +412 } diff --git a/html/012transform.cc.html b/html/012transform.cc.html index ae1c212f..c7aa2a27 100644 --- a/html/012transform.cc.html +++ b/html/012transform.cc.html @@ -148,7 +148,7 @@ if ('onhashchange' in window) { 91 if (r.initialized) return; 92 // End Reagent-parsing Exceptions 93 if (!is_integer(r.name)) return; - 94 r.set_value(to_integer(r.name)); + 94 r.set_value(to_integer(r.name)); 95 } 96 97 // helper for tests -- temporarily suppress run diff --git a/html/014literal_string.cc.html b/html/014literal_string.cc.html index fbc0aa4d..070e3951 100644 --- a/html/014literal_string.cc.html +++ b/html/014literal_string.cc.html @@ -83,10 +83,10 @@ if ('onhashchange' in window) { 22 :(before "End Mu Types Initialization") 23 put(Type_ordinal, "literal-string", 0); 24 - 25 :(before "End next_word Special-cases") + 25 :(before "End next_word Special-cases") 26 if (in.peek() == '[') { 27 string result = slurp_quoted(in); - 28 skip_whitespace_and_comments_but_not_newline(in); + 28 skip_whitespace_and_comments_but_not_newline(in); 29 return result; 30 } 31 diff --git a/html/015literal_noninteger.cc.html b/html/015literal_noninteger.cc.html index 16e2bbf5..f09dba94 100644 --- a/html/015literal_noninteger.cc.html +++ b/html/015literal_noninteger.cc.html @@ -72,7 +72,7 @@ if ('onhashchange' in window) { 11 if (is_noninteger(s)) { 12 name = s; 13 type = new type_tree("literal-fractional-number", 0); -14 set_value(to_double(s)); +14 set_value(to_double(s)); 15 return; 16 } 17 diff --git a/html/016dilated_reagent.cc.html b/html/016dilated_reagent.cc.html index 1f550782..38f950e7 100644 --- a/html/016dilated_reagent.cc.html +++ b/html/016dilated_reagent.cc.html @@ -94,7 +94,7 @@ if ('onhashchange' in window) { 31 32 //: First augment next_word to group balanced brackets together. 33 - 34 :(before "End next_word Special-cases") + 34 :(before "End next_word Special-cases") 35 if (in.peek() == '(') 36 return slurp_balanced_bracket(in); 37 // treat curlies mostly like parens, but don't mess up labels @@ -150,7 +150,7 @@ if ('onhashchange' in window) { 87 result << c; 88 if (open_brackets.empty()) break; 89 } - 90 skip_whitespace_and_comments_but_not_newline(in); + 90 skip_whitespace_and_comments_but_not_newline(in); 91 return result.str(); 92 } 93 @@ -170,7 +170,7 @@ if ('onhashchange' in window) { 107 return; 108 } 109 { -110 string s = next_word(in); +110 string s = next_word(in); 111 if (s.empty()) { 112 assert(!has_data(in)); 113 raise << "incomplete dilated reagent at end of file (0)\n" << end(); @@ -185,7 +185,7 @@ if ('onhashchange' in window) { 122 string key = slurp_key(in); 123 if (key.empty()) continue; 124 if (key == "}") continue; -125 string s = next_word(in); +125 string s = next_word(in); 126 if (s.empty()) { 127 assert(!has_data(in)); 128 raise << "incomplete dilated reagent at end of file (1)\n" << end(); @@ -200,7 +200,7 @@ if ('onhashchange' in window) { 137 138 :(code) 139 string slurp_key(istream& in) { -140 string result = next_word(in); +140 string result = next_word(in); 141 if (result.empty()) { 142 assert(!has_data(in)); 143 raise << "incomplete dilated reagent at end of file (2)\n" << end(); diff --git a/html/017parse_tree.cc.html b/html/017parse_tree.cc.html index 3224e48a..27457430 100644 --- a/html/017parse_tree.cc.html +++ b/html/017parse_tree.cc.html @@ -107,7 +107,7 @@ if ('onhashchange' in window) { 45 return NULL; 46 } 47 if (in.peek() != '(') { - 48 string s = next_word(in); + 48 string s = next_word(in); 49 if (s.empty()) { 50 assert(!has_data(in)); 51 raise << "incomplete string tree at end of file (0)\n" << end(); @@ -128,7 +128,7 @@ if ('onhashchange' in window) { 66 (*curr)->left = parse_string_tree(in); 67 } 68 else { - 69 string s = next_word(in); + 69 string s = next_word(in); 70 if (s.empty()) { 71 assert(!has_data(in)); 72 raise << "incomplete string tree at end of file (1)\n" << end(); diff --git a/html/018type_abbreviations.cc.html b/html/018type_abbreviations.cc.html index 495f9aed..51375fe5 100644 --- a/html/018type_abbreviations.cc.html +++ b/html/018type_abbreviations.cc.html @@ -83,13 +83,13 @@ if ('onhashchange' in window) { 20 21 :(code) 22 void load_type_abbreviations(istream& in) { - 23 string new_type_name = next_word(in); + 23 string new_type_name = next_word(in); 24 assert(has_data(in) || !new_type_name.empty()); 25 if (!has_data(in) || new_type_name.empty()) { 26 raise << "incomplete 'type' statement; must be of the form 'type <new type name> = <type expression>'\n" << end(); 27 return; 28 } - 29 string arrow = next_word(in); + 29 string arrow = next_word(in); 30 assert(has_data(in) || !arrow.empty()); 31 if (arrow.empty()) { 32 raise << "incomplete 'type' statement 'type " << new_type_name << "'\n" << end(); @@ -103,7 +103,7 @@ if ('onhashchange' in window) { 40 raise << "incomplete 'type' statement 'type " << new_type_name << " ='\n" << end(); 41 return; 42 } - 43 string old = next_word(in); + 43 string old = next_word(in); 44 if (old.empty()) { 45 raise << "incomplete 'type' statement 'type " << new_type_name << " ='\n" << end(); 46 raise << "'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type " << new_type_name << ' ' << arrow << "'\n" << end(); diff --git a/html/020run.cc.html b/html/020run.cc.html index 3e78d263..d8b62d00 100644 --- a/html/020run.cc.html +++ b/html/020run.cc.html @@ -146,7 +146,7 @@ if ('onhashchange' in window) { 81 vector<vector<double> > products; 82 switch (current_instruction().operation) { 83 // Primitive Recipe Implementations - 84 case COPY: { + 84 case COPY: { 85 copy(ingredients.begin(), ingredients.end(), inserter(products, products.begin())); 86 break; 87 } diff --git a/html/021check_instruction.cc.html b/html/021check_instruction.cc.html index 34214dfc..9e8d0d1f 100644 --- a/html/021check_instruction.cc.html +++ b/html/021check_instruction.cc.html @@ -81,7 +81,7 @@ if ('onhashchange' in window) { 20 if (inst.is_label) continue; 21 switch (inst.operation) { 22 // Primitive Recipe Checks - 23 case COPY: { + 23 case COPY: { 24 if (SIZE(inst.products) != SIZE(inst.ingredients)) { 25 raise << maybe(get(Recipe, r).name) << "ingredients and products should match in '" << inst.original_string << "'\n" << end(); 26 break; diff --git a/html/028call_return.cc.html b/html/028call_return.cc.html index c6847dd7..a65a25ba 100644 --- a/html/028call_return.cc.html +++ b/html/028call_return.cc.html @@ -128,7 +128,7 @@ if ('onhashchange' in window) { 67 const instruction& caller_instruction = caller.steps.at(i); 68 if (caller_instruction.is_label) continue; 69 if (caller_instruction.products.empty()) continue; - 70 if (caller_instruction.operation < MAX_PRIMITIVE_RECIPES) continue; + 70 if (caller_instruction.operation < MAX_PRIMITIVE_RECIPES) continue; 71 const recipe& callee = get(Recipe, caller_instruction.operation); 72 for (int i = 0; i < SIZE(callee.steps); ++i) { 73 const instruction& return_inst = callee.steps.at(i); diff --git a/html/030container.cc.html b/html/030container.cc.html index b7fea808..1f89f15b 100644 --- a/html/030container.cc.html +++ b/html/030container.cc.html @@ -67,9 +67,9 @@ if ('onhashchange' in window) { 2 3 :(before "End Mu Types Initialization") 4 //: We'll use this container as a running example in scenarios below. - 5 type_ordinal point = put(Type_ordinal, "point", Next_type_ordinal++); + 5 type_ordinal point = put(Type_ordinal, "point", Next_type_ordinal++); 6 get_or_insert(Type, point); // initialize - 7 get(Type, point).kind = CONTAINER; + 7 get(Type, point).kind = CONTAINER; 8 get(Type, point).name = "point"; 9 get(Type, point).elements.push_back(reagent("x:number")); 10 get(Type, point).elements.push_back(reagent("y:number")); @@ -100,9 +100,9 @@ if ('onhashchange' in window) { 35 :(before "End Mu Types Initialization") 36 // A more complex example container, containing another container as one of 37 // its elements. - 38 type_ordinal point_number = put(Type_ordinal, "point-number", Next_type_ordinal++); + 38 type_ordinal point_number = put(Type_ordinal, "point-number", Next_type_ordinal++); 39 get_or_insert(Type, point_number); // initialize - 40 get(Type, point_number).kind = CONTAINER; + 40 get(Type, point_number).kind = CONTAINER; 41 get(Type, point_number).name = "point-number"; 42 get(Type, point_number).elements.push_back(reagent("xy:point")); 43 get(Type, point_number).elements.push_back(reagent("z:number")); @@ -220,7 +220,7 @@ if ('onhashchange' in window) { 155 return 0; 156 } 157 type_info t = get(Type, base_type->value); -158 if (t.kind == CONTAINER) { +158 if (t.kind == CONTAINER) { 159 // Compute size_of Container 160 if (!contains_key(Container_metadata, type)) { 161 raise << "unknown size for container type '" << to_string(type) << "'\n" << end(); @@ -279,13 +279,13 @@ if ('onhashchange' in window) { 214 assert(type->atom); 215 if (!contains_key(Type, type->value)) return; // error raised elsewhere 216 type_info& info = get(Type, type->value); -217 if (info.kind == CONTAINER) +217 if (info.kind == CONTAINER) 218 compute_container_sizes(info, type, pending_metadata, location_for_error_messages); 219 // End compute_container_sizes Atom Special-cases 220 } 221 222 void compute_container_sizes(const type_info& container_info, const type_tree* full_type, set<type_tree>& pending_metadata, const string& location_for_error_messages) { -223 assert(container_info.kind == CONTAINER); +223 assert(container_info.kind == CONTAINER); 224 // size of a container is the sum of the sizes of its element 225 // (So it can only contain arrays if they're static and include their 226 // length in the type.) @@ -437,7 +437,7 @@ if ('onhashchange' in window) { 372 } 373 const type_tree* base_type = base.type; 374 // Update GET base_type in Check -375 if (!base_type->atom || base_type->value == 0 || !contains_key(Type, base_type->value) || get(Type, base_type->value).kind != CONTAINER) { +375 if (!base_type->atom || base_type->value == 0 || !contains_key(Type, base_type->value) || get(Type, base_type->value).kind != CONTAINER) { 376 raise << maybe(get(Recipe, r).name) << "first ingredient of 'get' should be a container, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); 377 break; 378 } @@ -484,7 +484,7 @@ if ('onhashchange' in window) { 419 trace(9998, "run") << "address to copy is " << src << end(); 420 //: use base.type rather than base_type because later layers will introduce compound types 421 reagent/*copy*/ element = element_type(base.type, offset); -422 element.set_value(src); +422 element.set_value(src); 423 trace(9998, "run") << "its type is " << names_to_string(element.type) << end(); 424 // Read element 425 products.push_back(read_memory(element)); @@ -499,7 +499,7 @@ if ('onhashchange' in window) { 434 assert(contains_key(Type, base_type->value)); 435 assert(!get(Type, base_type->value).name.empty()); 436 const type_info& info = get(Type, base_type->value); -437 assert(info.kind == CONTAINER); +437 assert(info.kind == CONTAINER); 438 if (offset_value >= SIZE(info.elements)) return reagent(); // error handled elsewhere 439 reagent/*copy*/ element = info.elements.at(offset_value); 440 // End element_type Special-cases @@ -585,7 +585,7 @@ if ('onhashchange' in window) { 520 } 521 const type_tree* base_type = base.type; 522 // Update PUT base_type in Check -523 if (!base_type->atom || base_type->value == 0 || !contains_key(Type, base_type->value) || get(Type, base_type->value).kind != CONTAINER) { +523 if (!base_type->atom || base_type->value == 0 || !contains_key(Type, base_type->value) || get(Type, base_type->value).kind != CONTAINER) { 524 raise << maybe(get(Recipe, r).name) << "first ingredient of 'put' should be a container, but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); 525 break; 526 } @@ -711,7 +711,7 @@ if ('onhashchange' in window) { 646 647 :(before "End Command Handlers") 648 else if (command == "container") { -649 insert_container(command, CONTAINER, in); +649 insert_container(command, CONTAINER, in); 650 } 651 652 //: Even though we allow containers to be extended, we don't allow this after @@ -723,9 +723,9 @@ if ('onhashchange' in window) { 658 Num_calls_to_transform_all_at_first_definition = -1; 659 660 :(code) -661 void insert_container(const string& command, kind_of_type kind, istream& in) { +661 void insert_container(const string& command, kind_of_type kind, istream& in) { 662 skip_whitespace_but_not_newline(in); -663 string name = next_word(in); +663 string name = next_word(in); 664 if (name.empty()) { 665 assert(!has_data(in)); 666 raise << "incomplete container definition at end of file (0)\n" << end(); @@ -752,8 +752,8 @@ if ('onhashchange' in window) { 687 info.name = name; 688 info.kind = kind; 689 while (has_data(in)) { -690 skip_whitespace_and_comments(in); -691 string element = next_word(in); +690 skip_whitespace_and_comments(in); +691 string element = next_word(in); 692 if (element.empty()) { 693 assert(!has_data(in)); 694 raise << "incomplete container definition at end of file (1)\n" << end(); @@ -764,8 +764,8 @@ if ('onhashchange' in window) { 699 raise << command << " '" << name << "' contains multiple elements on a single line. Containers and exclusive containers must only contain elements, one to a line, no code.\n" << end(); 700 // skip rest of container declaration 701 while (has_data(in)) { -702 skip_whitespace_and_comments(in); -703 if (next_word(in) == "]") break; +702 skip_whitespace_and_comments(in); +703 if (next_word(in) == "]") break; 704 } 705 break; 706 } @@ -796,7 +796,7 @@ if ('onhashchange' in window) { 731 } 732 733 void skip_bracket(istream& in, string message) { -734 skip_whitespace_and_comments(in); +734 skip_whitespace_and_comments(in); 735 if (in.get() != '[') 736 raise << message << '\n' << end(); 737 } diff --git a/html/031merge.cc.html b/html/031merge.cc.html index 158f4f93..056a7c7a 100644 --- a/html/031merge.cc.html +++ b/html/031merge.cc.html @@ -194,7 +194,7 @@ if ('onhashchange' in window) { 131 continue; 132 } 133 const type_info& info = get(Type, product_base_type->value); -134 if (info.kind != CONTAINER && info.kind != EXCLUSIVE_CONTAINER) { +134 if (info.kind != CONTAINER && info.kind != EXCLUSIVE_CONTAINER) { 135 raise << maybe(caller.name) << "'merge' should yield a container in '" << inst.original_string << "'\n" << end(); 136 continue; 137 } @@ -219,7 +219,7 @@ if ('onhashchange' in window) { 156 assert(top_root_type->atom); 157 type_info& container_info = get(Type, top_root_type->value); 158 switch (container_info.kind) { -159 case CONTAINER: { +159 case CONTAINER: { 160 // degenerate case: merge with the same type always succeeds 161 if (state.data.top().container_element_index == 0 && types_coercible(container, inst.ingredients.at(ingredient_index))) 162 return; diff --git a/html/032array.cc.html b/html/032array.cc.html index c3019efb..048f3dde 100644 --- a/html/032array.cc.html +++ b/html/032array.cc.html @@ -417,7 +417,7 @@ if ('onhashchange' in window) { 354 } 355 reagent/*local*/ element; 356 element.type = copy_array_element(base.type); -357 element.set_value(base_address + /*skip length*/1 + index_val.at(0)*size_of(element.type)); +357 element.set_value(base_address + /*skip length*/1 + index_val.at(0)*size_of(element.type)); 358 trace(9998, "run") << "address to copy is " << element.value << end(); 359 trace(9998, "run") << "its type is " << to_string(element.type) << end(); 360 // Read element diff --git a/html/033exclusive_container.cc.html b/html/033exclusive_container.cc.html index 9148aa53..f830b617 100644 --- a/html/033exclusive_container.cc.html +++ b/html/033exclusive_container.cc.html @@ -70,9 +70,9 @@ if ('onhashchange' in window) { 7 :(before "End Mu Types Initialization") 8 //: We'll use this container as a running example, with two number elements. 9 { - 10 type_ordinal tmp = put(Type_ordinal, "number-or-point", Next_type_ordinal++); + 10 type_ordinal tmp = put(Type_ordinal, "number-or-point", Next_type_ordinal++); 11 get_or_insert(Type, tmp); // initialize - 12 get(Type, tmp).kind = EXCLUSIVE_CONTAINER; + 12 get(Type, tmp).kind = EXCLUSIVE_CONTAINER; 13 get(Type, tmp).name = "number-or-point"; 14 get(Type, tmp).elements.push_back(reagent("i:number")); 15 get(Type, tmp).elements.push_back(reagent("p:point")); @@ -94,12 +94,12 @@ if ('onhashchange' in window) { 31 +mem: storing 35 in location 6 32 33 :(before "End size_of(type) Special-cases") - 34 if (t.kind == EXCLUSIVE_CONTAINER) { + 34 if (t.kind == EXCLUSIVE_CONTAINER) { 35 // Compute size_of Exclusive Container 36 return get(Container_metadata, type).size; 37 } 38 :(before "End compute_container_sizes Atom Special-cases") - 39 if (info.kind == EXCLUSIVE_CONTAINER) { + 39 if (info.kind == EXCLUSIVE_CONTAINER) { 40 compute_exclusive_container_sizes(info, type, pending_metadata, location_for_error_messages); 41 } 42 @@ -174,7 +174,7 @@ if ('onhashchange' in window) { 111 } 112 const type_tree* base_type = base.type; 113 // Update MAYBE_CONVERT base_type in Check -114 if (!base_type->atom || base_type->value == 0 || !contains_key(Type, base_type->value) || get(Type, base_type->value).kind != EXCLUSIVE_CONTAINER) { +114 if (!base_type->atom || base_type->value == 0 || !contains_key(Type, base_type->value) || get(Type, base_type->value).kind != EXCLUSIVE_CONTAINER) { 115 raise << maybe(caller.name) << "first ingredient of 'maybe-convert' should be an exclusive-container, but got '" << base.original_string << "'\n" << end(); 116 break; 117 } @@ -254,7 +254,7 @@ if ('onhashchange' in window) { 191 assert(contains_key(Type, root_type->value)); 192 assert(!get(Type, root_type->value).name.empty()); 193 const type_info& info = get(Type, root_type->value); -194 assert(info.kind == EXCLUSIVE_CONTAINER); +194 assert(info.kind == EXCLUSIVE_CONTAINER); 195 reagent/*copy*/ element = info.elements.at(tag); 196 // End variant_type Special-cases 197 return element; @@ -292,7 +292,7 @@ if ('onhashchange' in window) { 229 230 :(before "End Command Handlers") 231 else if (command == "exclusive-container") { -232 insert_container(command, EXCLUSIVE_CONTAINER, in); +232 insert_container(command, EXCLUSIVE_CONTAINER, in); 233 } 234 235 //: arrays are disallowed inside exclusive containers unless their length is @@ -378,7 +378,7 @@ if ('onhashchange' in window) { 315 $error: 0 316 317 :(before "End check_merge_call Special-cases") -318 case EXCLUSIVE_CONTAINER: { +318 case EXCLUSIVE_CONTAINER: { 319 assert(state.data.top().container_element_index == 0); 320 trace(9999, "transform") << "checking exclusive container " << to_string(container) << " vs ingredient " << ingredient_index << end(); 321 // easy case: exact match @@ -507,7 +507,7 @@ if ('onhashchange' in window) { 444 // Update size_mismatch Check for MERGE(x) 445 const type_tree* root_type = x.type->atom ? x.type : x.type->left; 446 assert(root_type->atom); -447 if (get(Type, root_type->value).kind == EXCLUSIVE_CONTAINER) +447 if (get(Type, root_type->value).kind == EXCLUSIVE_CONTAINER) 448 return size_of(x) < SIZE(data); 449 } 450 diff --git a/html/034address.cc.html b/html/034address.cc.html index 7ebf5662..b014ca8b 100644 --- a/html/034address.cc.html +++ b/html/034address.cc.html @@ -341,7 +341,7 @@ if ('onhashchange' in window) { 279 if (inst.name == "new") { 280 inst.operation = ALLOCATE; 281 type_tree* type = new_type_tree(inst.ingredients.at(0).name); -282 inst.ingredients.at(0).set_value(size_of(type)); +282 inst.ingredients.at(0).set_value(size_of(type)); 283 trace(9992, "new") << "size of '" << inst.ingredients.at(0).name << "' is " << inst.ingredients.at(0).value << end(); 284 delete type; 285 } diff --git a/html/035lookup.cc.html b/html/035lookup.cc.html index ce6d0ed6..c52b00d4 100644 --- a/html/035lookup.cc.html +++ b/html/035lookup.cc.html @@ -163,11 +163,11 @@ if ('onhashchange' in window) { 100 void lookup_memory_core(reagent& x, bool check_for_null) { 101 if (x.value == 0) return; 102 trace(9999, "mem") << "location " << x.value << " is " << no_scientific(get_or_insert(Memory, x.value)) << end(); -103 x.set_value(get_or_insert(Memory, x.value)); +103 x.set_value(get_or_insert(Memory, x.value)); 104 drop_from_type(x, "address"); 105 if (x.value) { 106 trace(9999, "mem") << "skipping refcount at " << x.value << end(); -107 x.set_value(x.value+1); // skip refcount +107 x.set_value(x.value+1); // skip refcount 108 } 109 else if (check_for_null) { 110 if (Current_routine) @@ -180,7 +180,7 @@ if ('onhashchange' in window) { 117 118 void test_lookup_address_skips_refcount() { 119 reagent x("*x:address:num"); -120 x.set_value(34); // unsafe +120 x.set_value(34); // unsafe 121 put(Memory, 34, 1000); 122 lookup_memory(x); 123 CHECK_TRACE_CONTENTS("mem: skipping refcount at 1000"); @@ -190,7 +190,7 @@ if ('onhashchange' in window) { 127 void test_lookup_zero_address_does_not_skip_refcount() { 128 Hide_errors = true; 129 reagent x("*x:address:num"); -130 x.set_value(34); // unsafe +130 x.set_value(34); // unsafe 131 put(Memory, 34, 0); 132 lookup_memory(x); 133 CHECK_TRACE_DOESNT_CONTAIN("mem: skipping refcount at 0"); diff --git a/html/036refcount.cc.html b/html/036refcount.cc.html index 714ef77b..f06ca44f 100644 --- a/html/036refcount.cc.html +++ b/html/036refcount.cc.html @@ -202,7 +202,7 @@ if ('onhashchange' in window) { 139 :(after "Write Memory in PUT in Run") 140 reagent/*copy*/ element = element_type(base.type, offset); 141 assert(!has_property(element, "lookup")); - 142 element.set_value(address); + 142 element.set_value(address); 143 update_any_refcounts(element, ingredients.at(2)); 144 145 :(scenario refcounts_put_index) @@ -221,7 +221,7 @@ if ('onhashchange' in window) { 158 159 :(after "Write Memory in PUT_INDEX in Run") 160 reagent/*local*/ element; - 161 element.set_value(address); + 161 element.set_value(address); 162 element.type = copy_array_element(base.type); 163 update_any_refcounts(element, value); 164 @@ -274,807 +274,813 @@ if ('onhashchange' in window) { 211 +run: {3: "foo"} <- copy {2: ("address" "foo"), "lookup": ()} 212 +mem: incrementing refcount of 1000: 2 -> 3 213 - 214 :(after "End type_tree Definition") + 214 :(before "End type_tree Definition") 215 struct address_element_info { 216 int offset; // where inside a container type (after flattening nested containers!) the address lies 217 const type_tree* payload_type; // all the information we need to compute sizes of items inside an address inside a container. Doesn't need to be a full-scale reagent, since an address inside a container can never be an array, and arrays are the only type that need to know their location to compute their size. - 218 address_element_info(int o, const type_tree* p) { - 219 offset = o; - 220 payload_type = p; - 221 } - 222 address_element_info(const address_element_info& other) { - 223 offset = other.offset; - 224 payload_type = other.payload_type ? new type_tree(*other.payload_type) : NULL; - 225 } - 226 ~address_element_info() { - 227 if (payload_type) { - 228 delete payload_type; - 229 payload_type = NULL; - 230 } - 231 } - 232 address_element_info& operator=(const address_element_info& other) { - 233 offset = other.offset; - 234 if (payload_type) delete payload_type; - 235 payload_type = other.payload_type ? new type_tree(*other.payload_type) : NULL; - 236 return *this; - 237 } - 238 }; - 239 - 240 // For exclusive containers we might sometimes have an address at some offset - 241 // if some other offset has a specific tag. This struct encapsulates such - 242 // guards. - 243 struct tag_condition_info { - 244 int offset; - 245 int tag; - 246 tag_condition_info(int o, int t) :offset(o), tag(t) {} - 247 }; - 248 - 249 :(before "End container_metadata Fields") - 250 // a list of facts of the form: - 251 // - 252 // IF offset o1 has tag t2 AND offset o2 has tag t2 AND .., THEN - 253 // for all address_element_infos: - 254 // you need to update refcounts for the address at offset pointing to a payload of type payload_type (just in case we need to abandon something in the process) - 255 map<set<tag_condition_info>, set<address_element_info> > address; - 256 :(code) - 257 bool operator<(const set<tag_condition_info>& a, const set<tag_condition_info>& b) { - 258 if (a.size() != b.size()) return a.size() < b.size(); - 259 for (set<tag_condition_info>::const_iterator pa = a.begin(), pb = b.begin(); pa != a.end(); ++pa, ++pb) { - 260 if (pa->offset != pb->offset) return pa->offset < pb->offset; - 261 if (pa->tag != pb->tag) return pa->tag < pb->tag; - 262 } - 263 return false; // equal - 264 } - 265 bool operator<(const tag_condition_info& a, const tag_condition_info& b) { - 266 if (a.offset != b.offset) return a.offset < b.offset; - 267 if (a.tag != b.tag) return a.tag < b.tag; - 268 return false; // equal - 269 } - 270 bool operator<(const set<address_element_info>& a, const set<address_element_info>& b) { - 271 if (a.size() != b.size()) return a.size() < b.size(); - 272 for (set<address_element_info>::const_iterator pa = a.begin(), pb = b.begin(); pa != a.end(); ++pa, ++pb) { - 273 if (pa->offset != pb->offset) return pa->offset < pb->offset; - 274 } - 275 return false; // equal - 276 } - 277 bool operator<(const address_element_info& a, const address_element_info& b) { - 278 if (a.offset != b.offset) return a.offset < b.offset; - 279 return false; // equal - 280 } - 281 - 282 //: populate metadata.address in a separate transform, because it requires - 283 //: already knowing the sizes of all types - 284 - 285 :(after "Transform.push_back(compute_container_sizes)") - 286 Transform.push_back(compute_container_address_offsets); // idempotent - 287 :(code) - 288 void compute_container_address_offsets(const recipe_ordinal r) { - 289 recipe& caller = get(Recipe, r); - 290 trace(9992, "transform") << "--- compute address offsets for " << caller.name << end(); - 291 for (int i = 0; i < SIZE(caller.steps); ++i) { - 292 instruction& inst = caller.steps.at(i); - 293 trace(9993, "transform") << "- compute address offsets for " << to_string(inst) << end(); - 294 for (int i = 0; i < SIZE(inst.ingredients); ++i) - 295 compute_container_address_offsets(inst.ingredients.at(i), " in '"+to_original_string(inst)+"'"); - 296 for (int i = 0; i < SIZE(inst.products); ++i) - 297 compute_container_address_offsets(inst.products.at(i), " in '"+to_original_string(inst)+"'"); - 298 } - 299 } - 300 - 301 void compute_container_address_offsets(reagent& r, const string& location_for_error_messages) { - 302 if (is_literal(r) || is_dummy(r)) return; - 303 compute_container_address_offsets(r.type, location_for_error_messages); - 304 if (contains_key(Container_metadata, r.type)) - 305 r.metadata = get(Container_metadata, r.type); - 306 } - 307 - 308 // the recursive structure of this function needs to exactly match - 309 // compute_container_sizes - 310 void compute_container_address_offsets(const type_tree* type, const string& location_for_error_messages) { - 311 if (!type) return; - 312 if (!type->atom) { - 313 if (!type->left->atom) { - 314 raise << "invalid type " << to_string(type) << location_for_error_messages << '\n' << end(); - 315 return; - 316 } - 317 if (type->left->name == "address") - 318 compute_container_address_offsets(payload_type(type), location_for_error_messages); - 319 else if (type->left->name == "array") - 320 compute_container_address_offsets(array_element(type), location_for_error_messages); - 321 // End compute_container_address_offsets Non-atom Special-cases - 322 } - 323 const type_tree* base_type = type; - 324 // Update base_type in compute_container_address_offsets - 325 if (!contains_key(Type, base_type->value)) return; // error raised elsewhere - 326 type_info& info = get(Type, base_type->value); - 327 if (info.kind == CONTAINER) { - 328 compute_container_address_offsets(info, type, location_for_error_messages); - 329 } - 330 if (info.kind == EXCLUSIVE_CONTAINER) { - 331 compute_exclusive_container_address_offsets(info, type, location_for_error_messages); - 332 } - 333 } - 334 - 335 void compute_container_address_offsets(const type_info& container_info, const type_tree* full_type, const string& location_for_error_messages) { - 336 container_metadata& metadata = get(Container_metadata, full_type); - 337 if (!metadata.address.empty()) return; - 338 trace(9994, "transform") << "compute address offsets for container " << container_info.name << end(); - 339 append_addresses(0, full_type, metadata.address, set<tag_condition_info>(), location_for_error_messages); - 340 } - 341 - 342 void compute_exclusive_container_address_offsets(const type_info& exclusive_container_info, const type_tree* full_type, const string& location_for_error_messages) { - 343 container_metadata& metadata = get(Container_metadata, full_type); - 344 trace(9994, "transform") << "compute address offsets for exclusive container " << exclusive_container_info.name << end(); - 345 for (int tag = 0; tag < SIZE(exclusive_container_info.elements); ++tag) { - 346 set<tag_condition_info> key; - 347 key.insert(tag_condition_info(/*tag is at offset*/0, tag)); - 348 append_addresses(/*skip tag offset*/1, variant_type(full_type, tag).type, metadata.address, key, location_for_error_messages); - 349 } - 350 } - 351 - 352 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 string& location_for_error_messages) { - 353 if (is_mu_address(type)) { - 354 get_or_insert(out, key).insert(address_element_info(base_offset, new type_tree(*payload_type(type)))); - 355 return; - 356 } - 357 const type_tree* base_type = type; - 358 // Update base_type in append_container_address_offsets - 359 const type_info& info = get(Type, base_type->value); - 360 if (info.kind == CONTAINER) { - 361 for (int curr_index = 0, curr_offset = base_offset; curr_index < SIZE(info.elements); ++curr_index) { - 362 trace(9993, "transform") << "checking container " << base_type->name << ", element " << curr_index << end(); - 363 reagent/*copy*/ element = element_type(type, curr_index); // not base_type - 364 // Compute Container Address Offset(element) - 365 if (is_mu_address(element)) { - 366 trace(9993, "transform") << "address at offset " << curr_offset << end(); - 367 get_or_insert(out, key).insert(address_element_info(curr_offset, new type_tree(*payload_type(element.type)))); - 368 ++curr_offset; - 369 } - 370 else if (is_mu_array(element)) { - 371 curr_offset += /*array length*/1; - 372 const type_tree* array_element_type = array_element(element.type); - 373 int array_element_size = size_of(array_element_type); - 374 for (int i = 0; i < static_array_length(element.type); ++i) { - 375 append_addresses(curr_offset, array_element_type, out, key, location_for_error_messages); - 376 curr_offset += array_element_size; - 377 } - 378 } - 379 else if (is_mu_container(element)) { - 380 append_addresses(curr_offset, element.type, out, key, location_for_error_messages); - 381 curr_offset += size_of(element); - 382 } - 383 else if (is_mu_exclusive_container(element)) { - 384 const type_tree* element_base_type = element.type; - 385 // Update element_base_type For Exclusive Container in append_addresses - 386 const type_info& element_info = get(Type, element_base_type->value); - 387 for (int tag = 0; tag < SIZE(element_info.elements); ++tag) { - 388 set<tag_condition_info> new_key = key; - 389 new_key.insert(tag_condition_info(curr_offset, tag)); - 390 if (!contains_key(out, new_key)) - 391 append_addresses(curr_offset+/*skip tag*/1, variant_type(element.type, tag).type, out, new_key, location_for_error_messages); - 392 } - 393 curr_offset += size_of(element); - 394 } - 395 else { - 396 // non-address primitive - 397 ++curr_offset; - 398 } - 399 } - 400 } - 401 else if (info.kind == EXCLUSIVE_CONTAINER) { - 402 for (int tag = 0; tag < SIZE(info.elements); ++tag) { - 403 set<tag_condition_info> new_key = key; - 404 new_key.insert(tag_condition_info(base_offset, tag)); - 405 if (!contains_key(out, new_key)) - 406 append_addresses(base_offset+/*skip tag*/1, variant_type(type, tag).type, out, new_key, location_for_error_messages); - 407 } - 408 } - 409 } - 410 - 411 //: for the following unit tests we'll do the work of the transform by hand - 412 - 413 :(before "End Unit Tests") - 414 void test_container_address_offsets_empty() { - 415 int old_size = SIZE(Container_metadata); - 416 // define a container with no addresses - 417 reagent r("x:point"); - 418 compute_container_sizes(r, ""); // need to first pre-populate the metadata - 419 // scan - 420 compute_container_address_offsets(r, ""); - 421 // global metadata contains just the entry for foo - 422 // no entries for non-container types or other junk - 423 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 424 // the reagent we scanned knows it has no addresses - 425 CHECK(r.metadata.address.empty()); - 426 // the global table contains an identical entry - 427 CHECK(contains_key(Container_metadata, r.type)); - 428 CHECK(get(Container_metadata, r.type).address.empty()); - 429 // compute_container_address_offsets creates no new entries - 430 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 431 } - 432 - 433 void test_container_address_offsets() { - 434 int old_size = SIZE(Container_metadata); - 435 // define a container with an address at offset 0 that we have the size for - 436 run("container foo [\n" - 437 " x:address:num\n" - 438 "]\n"); - 439 reagent r("x:foo"); - 440 compute_container_sizes(r, ""); // need to first pre-populate the metadata - 441 // scan - 442 compute_container_address_offsets(r, ""); - 443 // global metadata contains just the entry for foo - 444 // no entries for non-container types or other junk - 445 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 446 // the reagent we scanned knows it has an address at offset 0 - 447 CHECK_EQ(SIZE(r.metadata.address), 1); - 448 CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); - 449 const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>()); // unconditional for containers - 450 CHECK_EQ(SIZE(address_offsets), 1); - 451 CHECK_EQ(address_offsets.begin()->offset, 0); - 452 CHECK(address_offsets.begin()->payload_type->atom); - 453 CHECK_EQ(address_offsets.begin()->payload_type->name, "number"); - 454 // the global table contains an identical entry - 455 CHECK(contains_key(Container_metadata, r.type)); - 456 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>()); - 457 CHECK_EQ(SIZE(address_offsets2), 1); - 458 CHECK_EQ(address_offsets2.begin()->offset, 0); - 459 CHECK(address_offsets2.begin()->payload_type->atom); - 460 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); - 461 // compute_container_address_offsets creates no new entries - 462 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 463 } - 464 - 465 void test_container_address_offsets_2() { - 466 int old_size = SIZE(Container_metadata); - 467 // define a container with an address at offset 1 that we have the size for - 468 run("container foo [\n" - 469 " x:num\n" - 470 " y:address:num\n" - 471 "]\n"); - 472 reagent r("x:foo"); - 473 compute_container_sizes(r, ""); // need to first pre-populate the metadata - 474 // global metadata contains just the entry for foo - 475 // no entries for non-container types or other junk - 476 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 477 // scan - 478 compute_container_address_offsets(r, ""); - 479 // compute_container_address_offsets creates no new entries - 480 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 481 // the reagent we scanned knows it has an address at offset 1 - 482 CHECK_EQ(SIZE(r.metadata.address), 1); - 483 CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); - 484 const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>()); - 485 CHECK_EQ(SIZE(address_offsets), 1); - 486 CHECK_EQ(address_offsets.begin()->offset, 1); // - 487 CHECK(address_offsets.begin()->payload_type->atom); - 488 CHECK_EQ(address_offsets.begin()->payload_type->name, "number"); - 489 // the global table contains an identical entry - 490 CHECK(contains_key(Container_metadata, r.type)); - 491 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>()); - 492 CHECK_EQ(SIZE(address_offsets2), 1); - 493 CHECK_EQ(address_offsets2.begin()->offset, 1); // - 494 CHECK(address_offsets2.begin()->payload_type->atom); - 495 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); - 496 } - 497 - 498 void test_container_address_offsets_nested() { - 499 int old_size = SIZE(Container_metadata); - 500 // define a container with a nested container containing an address - 501 run("container foo [\n" - 502 " x:address:num\n" - 503 " y:num\n" - 504 "]\n" - 505 "container bar [\n" - 506 " p:point\n" - 507 " f:foo\n" // nested container containing address - 508 "]\n"); - 509 reagent r("x:bar"); - 510 compute_container_sizes(r, ""); // need to first pre-populate the metadata - 511 // global metadata contains entries for bar and included types: point and foo - 512 // no entries for non-container types or other junk - 513 CHECK_EQ(SIZE(Container_metadata)-old_size, 3); - 514 // scan - 515 compute_container_address_offsets(r, ""); - 516 // the reagent we scanned knows it has an address at offset 2 - 517 CHECK_EQ(SIZE(r.metadata.address), 1); - 518 CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); - 519 const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>()); - 520 CHECK_EQ(SIZE(address_offsets), 1); - 521 CHECK_EQ(address_offsets.begin()->offset, 2); // - 522 CHECK(address_offsets.begin()->payload_type->atom); - 523 CHECK_EQ(address_offsets.begin()->payload_type->name, "number"); - 524 // the global table also knows its address offset - 525 CHECK(contains_key(Container_metadata, r.type)); - 526 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>()); - 527 CHECK_EQ(SIZE(address_offsets2), 1); - 528 CHECK_EQ(address_offsets2.begin()->offset, 2); // - 529 CHECK(address_offsets2.begin()->payload_type->atom); - 530 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); - 531 // compute_container_address_offsets creates no new entries - 532 CHECK_EQ(SIZE(Container_metadata)-old_size, 3); - 533 } - 534 - 535 void test_container_address_offsets_from_address() { - 536 int old_size = SIZE(Container_metadata); - 537 // define a container with an address at offset 0 - 538 run("container foo [\n" - 539 " x:address:num\n" - 540 "]\n"); - 541 reagent r("x:address:foo"); - 542 compute_container_sizes(r, ""); // need to first pre-populate the metadata - 543 // global metadata contains just the entry for foo - 544 // no entries for non-container types or other junk - 545 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 546 // scan an address to the container - 547 compute_container_address_offsets(r, ""); - 548 // compute_container_address_offsets creates no new entries - 549 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 550 // scanning precomputed metadata for the container - 551 reagent container("x:foo"); - 552 CHECK(contains_key(Container_metadata, container.type)); - 553 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); - 554 CHECK_EQ(SIZE(address_offsets2), 1); - 555 CHECK_EQ(address_offsets2.begin()->offset, 0); - 556 CHECK(address_offsets2.begin()->payload_type->atom); - 557 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); - 558 } - 559 - 560 void test_container_address_offsets_from_array() { - 561 int old_size = SIZE(Container_metadata); - 562 // define a container with an address at offset 0 - 563 run("container foo [\n" - 564 " x:address:num\n" - 565 "]\n"); - 566 reagent r("x:array:foo"); - 567 compute_container_sizes(r, ""); // need to first pre-populate the metadata - 568 // global metadata contains just the entry for foo - 569 // no entries for non-container types or other junk - 570 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 571 // scan an array of the container - 572 compute_container_address_offsets(r, ""); - 573 // compute_container_address_offsets creates no new entries - 574 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 575 // scanning precomputed metadata for the container - 576 reagent container("x:foo"); - 577 CHECK(contains_key(Container_metadata, container.type)); - 578 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); - 579 CHECK_EQ(SIZE(address_offsets2), 1); - 580 CHECK_EQ(address_offsets2.begin()->offset, 0); - 581 CHECK(address_offsets2.begin()->payload_type->atom); - 582 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); - 583 } - 584 - 585 void test_container_address_offsets_from_address_to_array() { - 586 int old_size = SIZE(Container_metadata); - 587 // define a container with an address at offset 0 - 588 run("container foo [\n" - 589 " x:address:num\n" - 590 "]\n"); - 591 reagent r("x:address:array:foo"); - 592 compute_container_sizes(r, ""); // need to first pre-populate the metadata - 593 // global metadata contains just the entry for foo - 594 // no entries for non-container types or other junk - 595 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 596 // scan an address to an array of the container - 597 compute_container_address_offsets(r, ""); - 598 // compute_container_address_offsets creates no new entries - 599 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 600 // scanning precomputed metadata for the container - 601 reagent container("x:foo"); - 602 CHECK(contains_key(Container_metadata, container.type)); - 603 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); - 604 CHECK_EQ(SIZE(address_offsets2), 1); - 605 CHECK_EQ(address_offsets2.begin()->offset, 0); - 606 CHECK(address_offsets2.begin()->payload_type->atom); - 607 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); - 608 } - 609 - 610 void test_container_address_offsets_from_static_array() { - 611 int old_size = SIZE(Container_metadata); - 612 // define a container with an address at offset 0 - 613 run("container foo [\n" - 614 " x:address:num\n" - 615 "]\n"); - 616 reagent r("x:array:foo:10"); - 617 compute_container_sizes(r, ""); // need to first pre-populate the metadata - 618 // global metadata contains just the entry for foo - 619 // no entries for non-container types or other junk - 620 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 621 // scan a static array of the container - 622 compute_container_address_offsets(r, ""); - 623 // compute_container_address_offsets creates no new entries - 624 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 625 // scanning precomputed metadata for the container - 626 reagent container("x:foo"); - 627 CHECK(contains_key(Container_metadata, container.type)); - 628 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); - 629 CHECK_EQ(SIZE(address_offsets2), 1); - 630 CHECK_EQ(address_offsets2.begin()->offset, 0); - 631 CHECK(address_offsets2.begin()->payload_type->atom); - 632 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); - 633 } - 634 - 635 void test_container_address_offsets_from_address_to_static_array() { - 636 int old_size = SIZE(Container_metadata); - 637 // define a container with an address at offset 0 - 638 run("container foo [\n" - 639 " x:address:num\n" - 640 "]\n"); - 641 reagent r("x:address:array:foo:10"); - 642 compute_container_sizes(r, ""); // need to first pre-populate the metadata - 643 // global metadata contains just the entry for foo - 644 // no entries for non-container types or other junk - 645 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 646 // scan an address to a static array of the container - 647 compute_container_address_offsets(r, ""); - 648 // compute_container_address_offsets creates no new entries - 649 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 650 // scanning precomputed metadata for the container - 651 reagent container("x:foo"); - 652 CHECK(contains_key(Container_metadata, container.type)); - 653 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); - 654 CHECK_EQ(SIZE(address_offsets2), 1); - 655 CHECK_EQ(address_offsets2.begin()->offset, 0); - 656 CHECK(address_offsets2.begin()->payload_type->atom); - 657 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); - 658 } - 659 - 660 void test_container_address_offsets_from_repeated_address_and_array_types() { - 661 int old_size = SIZE(Container_metadata); - 662 // define a container with an address at offset 0 - 663 run("container foo [\n" - 664 " x:address:num\n" - 665 "]\n"); - 666 // scan a deep nest of 'address' and 'array' types modifying a container - 667 reagent r("x:address:array:address:address:array:foo:10"); - 668 compute_container_sizes(r, ""); // need to first pre-populate the metadata - 669 // global metadata contains just the entry for foo - 670 // no entries for non-container types or other junk - 671 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 672 compute_container_address_offsets(r, ""); - 673 // compute_container_address_offsets creates no new entries - 674 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); - 675 // scanning precomputed metadata for the container - 676 reagent container("x:foo"); - 677 CHECK(contains_key(Container_metadata, container.type)); - 678 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); - 679 CHECK_EQ(SIZE(address_offsets2), 1); - 680 CHECK_EQ(address_offsets2.begin()->offset, 0); - 681 CHECK(address_offsets2.begin()->payload_type->atom); - 682 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); - 683 } - 684 - 685 //: use metadata.address to update refcounts within containers, arrays and - 686 //: exclusive containers - 687 - 688 :(before "End Increment Refcounts(canonized_x)") - 689 if (is_mu_container(canonized_x) || is_mu_exclusive_container(canonized_x)) { - 690 const container_metadata& metadata = get(Container_metadata, canonized_x.type); - 691 for (map<set<tag_condition_info>, set<address_element_info> >::const_iterator p = metadata.address.begin(); p != metadata.address.end(); ++p) { - 692 if (!all_match(data, p->first)) continue; - 693 for (set<address_element_info>::const_iterator info = p->second.begin(); info != p->second.end(); ++info) - 694 increment_refcount(data.at(info->offset)); - 695 } - 696 } - 697 - 698 :(before "End Decrement Refcounts(canonized_x)") - 699 if (is_mu_container(canonized_x) || is_mu_exclusive_container(canonized_x)) { - 700 trace(9999, "mem") << "need to read old value of '" << to_string(canonized_x) << "' to figure out what refcounts to decrement" << end(); - 701 // read from canonized_x but without canonizing again - 702 // todo: inline without running canonize all over again - 703 reagent/*copy*/ tmp = canonized_x; - 704 tmp.properties.push_back(pair<string, string_tree*>("raw", NULL)); - 705 vector<double> data = read_memory(tmp); - 706 trace(9999, "mem") << "done reading old value of '" << to_string(canonized_x) << "'" << end(); - 707 const container_metadata& metadata = get(Container_metadata, canonized_x.type); - 708 for (map<set<tag_condition_info>, set<address_element_info> >::const_iterator p = metadata.address.begin(); p != metadata.address.end(); ++p) { - 709 if (!all_match(data, p->first)) continue; - 710 for (set<address_element_info>::const_iterator info = p->second.begin(); info != p->second.end(); ++info) { - 711 int element_address = get_or_insert(Memory, canonized_x.value + info->offset); - 712 reagent/*local*/ element; - 713 element.set_value(element_address+/*skip refcount*/1); - 714 element.type = new type_tree(*info->payload_type); - 715 decrement_refcount(element_address, info->payload_type, size_of(element)+/*refcount*/1); - 716 } - 717 } - 718 } - 719 - 720 :(code) - 721 bool all_match(const vector<double>& data, const set<tag_condition_info>& conditions) { - 722 for (set<tag_condition_info>::const_iterator p = conditions.begin(); p != conditions.end(); ++p) { - 723 if (data.at(p->offset) != p->tag) - 724 return false; - 725 } - 726 return true; - 727 } - 728 - 729 :(scenario refcounts_put_container) - 730 container foo [ - 731 a:bar # contains an address - 732 ] - 733 container bar [ - 734 x:address:num - 735 ] - 736 def main [ - 737 1:address:num <- new number:type - 738 2:bar <- merge 1:address:num - 739 3:address:foo <- new foo:type - 740 *3:address:foo <- put *3:address:foo, a:offset, 2:bar + 218 address_element_info(int o, const type_tree* p); + 219 address_element_info(const address_element_info& other); + 220 ~address_element_info(); + 221 address_element_info& operator=(const address_element_info& other); + 222 }; + 223 :(code) + 224 address_element_info::address_element_info(int o, const type_tree* p) { + 225 offset = o; + 226 payload_type = p; + 227 } + 228 address_element_info::address_element_info(const address_element_info& other) { + 229 offset = other.offset; + 230 payload_type = copy(other.payload_type); + 231 } + 232 address_element_info::~address_element_info() { + 233 if (payload_type) { + 234 delete payload_type; + 235 payload_type = NULL; + 236 } + 237 } + 238 address_element_info& address_element_info::operator=(const address_element_info& other) { + 239 offset = other.offset; + 240 if (payload_type) delete payload_type; + 241 payload_type = copy(other.payload_type); + 242 return *this; + 243 } + 244 + 245 :(before "End type_tree Definition") + 246 // For exclusive containers we might sometimes have an address at some offset + 247 // if some other offset has a specific tag. This struct encapsulates such + 248 // guards. + 249 struct tag_condition_info { + 250 int offset; + 251 int tag; + 252 tag_condition_info(int o, int t) :offset(o), tag(t) {} + 253 }; + 254 + 255 :(before "End container_metadata Fields") + 256 // a list of facts of the form: + 257 // + 258 // IF offset o1 has tag t2 AND offset o2 has tag t2 AND .., THEN + 259 // for all address_element_infos: + 260 // you need to update refcounts for the address at offset pointing to a payload of type payload_type (just in case we need to abandon something in the process) + 261 map<set<tag_condition_info>, set<address_element_info> > address; + 262 :(code) + 263 bool operator<(const set<tag_condition_info>& a, const set<tag_condition_info>& b) { + 264 if (a.size() != b.size()) return a.size() < b.size(); + 265 for (set<tag_condition_info>::const_iterator pa = a.begin(), pb = b.begin(); pa != a.end(); ++pa, ++pb) { + 266 if (pa->offset != pb->offset) return pa->offset < pb->offset; + 267 if (pa->tag != pb->tag) return pa->tag < pb->tag; + 268 } + 269 return false; // equal + 270 } + 271 bool operator<(const tag_condition_info& a, const tag_condition_info& b) { + 272 if (a.offset != b.offset) return a.offset < b.offset; + 273 if (a.tag != b.tag) return a.tag < b.tag; + 274 return false; // equal + 275 } + 276 bool operator<(const set<address_element_info>& a, const set<address_element_info>& b) { + 277 if (a.size() != b.size()) return a.size() < b.size(); + 278 for (set<address_element_info>::const_iterator pa = a.begin(), pb = b.begin(); pa != a.end(); ++pa, ++pb) { + 279 if (pa->offset != pb->offset) return pa->offset < pb->offset; + 280 } + 281 return false; // equal + 282 } + 283 bool operator<(const address_element_info& a, const address_element_info& b) { + 284 if (a.offset != b.offset) return a.offset < b.offset; + 285 return false; // equal + 286 } + 287 + 288 //: populate metadata.address in a separate transform, because it requires + 289 //: already knowing the sizes of all types + 290 + 291 :(after "Transform.push_back(compute_container_sizes)") + 292 Transform.push_back(compute_container_address_offsets); // idempotent + 293 :(code) + 294 void compute_container_address_offsets(const recipe_ordinal r) { + 295 recipe& caller = get(Recipe, r); + 296 trace(9992, "transform") << "--- compute address offsets for " << caller.name << end(); + 297 for (int i = 0; i < SIZE(caller.steps); ++i) { + 298 instruction& inst = caller.steps.at(i); + 299 trace(9993, "transform") << "- compute address offsets for " << to_string(inst) << end(); + 300 for (int i = 0; i < SIZE(inst.ingredients); ++i) + 301 compute_container_address_offsets(inst.ingredients.at(i), " in '"+to_original_string(inst)+"'"); + 302 for (int i = 0; i < SIZE(inst.products); ++i) + 303 compute_container_address_offsets(inst.products.at(i), " in '"+to_original_string(inst)+"'"); + 304 } + 305 } + 306 + 307 void compute_container_address_offsets(reagent& r, const string& location_for_error_messages) { + 308 if (is_literal(r) || is_dummy(r)) return; + 309 compute_container_address_offsets(r.type, location_for_error_messages); + 310 if (contains_key(Container_metadata, r.type)) + 311 r.metadata = get(Container_metadata, r.type); + 312 } + 313 + 314 // the recursive structure of this function needs to exactly match + 315 // compute_container_sizes + 316 void compute_container_address_offsets(const type_tree* type, const string& location_for_error_messages) { + 317 if (!type) return; + 318 if (!type->atom) { + 319 if (!type->left->atom) { + 320 raise << "invalid type " << to_string(type) << location_for_error_messages << '\n' << end(); + 321 return; + 322 } + 323 if (type->left->name == "address") + 324 compute_container_address_offsets(payload_type(type), location_for_error_messages); + 325 else if (type->left->name == "array") + 326 compute_container_address_offsets(array_element(type), location_for_error_messages); + 327 // End compute_container_address_offsets Non-atom Special-cases + 328 } + 329 const type_tree* base_type = type; + 330 // Update base_type in compute_container_address_offsets + 331 if (!contains_key(Type, base_type->value)) return; // error raised elsewhere + 332 type_info& info = get(Type, base_type->value); + 333 if (info.kind == CONTAINER) { + 334 compute_container_address_offsets(info, type, location_for_error_messages); + 335 } + 336 if (info.kind == EXCLUSIVE_CONTAINER) { + 337 compute_exclusive_container_address_offsets(info, type, location_for_error_messages); + 338 } + 339 } + 340 + 341 void compute_container_address_offsets(const type_info& container_info, const type_tree* full_type, const string& location_for_error_messages) { + 342 container_metadata& metadata = get(Container_metadata, full_type); + 343 if (!metadata.address.empty()) return; + 344 trace(9994, "transform") << "compute address offsets for container " << container_info.name << end(); + 345 append_addresses(0, full_type, metadata.address, set<tag_condition_info>(), location_for_error_messages); + 346 } + 347 + 348 void compute_exclusive_container_address_offsets(const type_info& exclusive_container_info, const type_tree* full_type, const string& location_for_error_messages) { + 349 container_metadata& metadata = get(Container_metadata, full_type); + 350 trace(9994, "transform") << "compute address offsets for exclusive container " << exclusive_container_info.name << end(); + 351 for (int tag = 0; tag < SIZE(exclusive_container_info.elements); ++tag) { + 352 set<tag_condition_info> key; + 353 key.insert(tag_condition_info(/*tag is at offset*/0, tag)); + 354 append_addresses(/*skip tag offset*/1, variant_type(full_type, tag).type, metadata.address, key, location_for_error_messages); + 355 } + 356 } + 357 + 358 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 string& location_for_error_messages) { + 359 if (is_mu_address(type)) { + 360 get_or_insert(out, key).insert(address_element_info(base_offset, new type_tree(*payload_type(type)))); + 361 return; + 362 } + 363 const type_tree* base_type = type; + 364 // Update base_type in append_container_address_offsets + 365 const type_info& info = get(Type, base_type->value); + 366 if (info.kind == CONTAINER) { + 367 for (int curr_index = 0, curr_offset = base_offset; curr_index < SIZE(info.elements); ++curr_index) { + 368 trace(9993, "transform") << "checking container " << base_type->name << ", element " << curr_index << end(); + 369 reagent/*copy*/ element = element_type(type, curr_index); // not base_type + 370 // Compute Container Address Offset(element) + 371 if (is_mu_address(element)) { + 372 trace(9993, "transform") << "address at offset " << curr_offset << end(); + 373 get_or_insert(out, key).insert(address_element_info(curr_offset, new type_tree(*payload_type(element.type)))); + 374 ++curr_offset; + 375 } + 376 else if (is_mu_array(element)) { + 377 curr_offset += /*array length*/1; + 378 const type_tree* array_element_type = array_element(element.type); + 379 int array_element_size = size_of(array_element_type); + 380 for (int i = 0; i < static_array_length(element.type); ++i) { + 381 append_addresses(curr_offset, array_element_type, out, key, location_for_error_messages); + 382 curr_offset += array_element_size; + 383 } + 384 } + 385 else if (is_mu_container(element)) { + 386 append_addresses(curr_offset, element.type, out, key, location_for_error_messages); + 387 curr_offset += size_of(element); + 388 } + 389 else if (is_mu_exclusive_container(element)) { + 390 const type_tree* element_base_type = element.type; + 391 // Update element_base_type For Exclusive Container in append_addresses + 392 const type_info& element_info = get(Type, element_base_type->value); + 393 for (int tag = 0; tag < SIZE(element_info.elements); ++tag) { + 394 set<tag_condition_info> new_key = key; + 395 new_key.insert(tag_condition_info(curr_offset, tag)); + 396 if (!contains_key(out, new_key)) + 397 append_addresses(curr_offset+/*skip tag*/1, variant_type(element.type, tag).type, out, new_key, location_for_error_messages); + 398 } + 399 curr_offset += size_of(element); + 400 } + 401 else { + 402 // non-address primitive + 403 ++curr_offset; + 404 } + 405 } + 406 } + 407 else if (info.kind == EXCLUSIVE_CONTAINER) { + 408 for (int tag = 0; tag < SIZE(info.elements); ++tag) { + 409 set<tag_condition_info> new_key = key; + 410 new_key.insert(tag_condition_info(base_offset, tag)); + 411 if (!contains_key(out, new_key)) + 412 append_addresses(base_offset+/*skip tag*/1, variant_type(type, tag).type, out, new_key, location_for_error_messages); + 413 } + 414 } + 415 } + 416 + 417 //: for the following unit tests we'll do the work of the transform by hand + 418 + 419 :(before "End Unit Tests") + 420 void test_container_address_offsets_empty() { + 421 int old_size = SIZE(Container_metadata); + 422 // define a container with no addresses + 423 reagent r("x:point"); + 424 compute_container_sizes(r, ""); // need to first pre-populate the metadata + 425 // scan + 426 compute_container_address_offsets(r, ""); + 427 // global metadata contains just the entry for foo + 428 // no entries for non-container types or other junk + 429 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 430 // the reagent we scanned knows it has no addresses + 431 CHECK(r.metadata.address.empty()); + 432 // the global table contains an identical entry + 433 CHECK(contains_key(Container_metadata, r.type)); + 434 CHECK(get(Container_metadata, r.type).address.empty()); + 435 // compute_container_address_offsets creates no new entries + 436 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 437 } + 438 + 439 void test_container_address_offsets() { + 440 int old_size = SIZE(Container_metadata); + 441 // define a container with an address at offset 0 that we have the size for + 442 run("container foo [\n" + 443 " x:address:num\n" + 444 "]\n"); + 445 reagent r("x:foo"); + 446 compute_container_sizes(r, ""); // need to first pre-populate the metadata + 447 // scan + 448 compute_container_address_offsets(r, ""); + 449 // global metadata contains just the entry for foo + 450 // no entries for non-container types or other junk + 451 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 452 // the reagent we scanned knows it has an address at offset 0 + 453 CHECK_EQ(SIZE(r.metadata.address), 1); + 454 CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); + 455 const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>()); // unconditional for containers + 456 CHECK_EQ(SIZE(address_offsets), 1); + 457 CHECK_EQ(address_offsets.begin()->offset, 0); + 458 CHECK(address_offsets.begin()->payload_type->atom); + 459 CHECK_EQ(address_offsets.begin()->payload_type->name, "number"); + 460 // the global table contains an identical entry + 461 CHECK(contains_key(Container_metadata, r.type)); + 462 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>()); + 463 CHECK_EQ(SIZE(address_offsets2), 1); + 464 CHECK_EQ(address_offsets2.begin()->offset, 0); + 465 CHECK(address_offsets2.begin()->payload_type->atom); + 466 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); + 467 // compute_container_address_offsets creates no new entries + 468 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 469 } + 470 + 471 void test_container_address_offsets_2() { + 472 int old_size = SIZE(Container_metadata); + 473 // define a container with an address at offset 1 that we have the size for + 474 run("container foo [\n" + 475 " x:num\n" + 476 " y:address:num\n" + 477 "]\n"); + 478 reagent r("x:foo"); + 479 compute_container_sizes(r, ""); // need to first pre-populate the metadata + 480 // global metadata contains just the entry for foo + 481 // no entries for non-container types or other junk + 482 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 483 // scan + 484 compute_container_address_offsets(r, ""); + 485 // compute_container_address_offsets creates no new entries + 486 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 487 // the reagent we scanned knows it has an address at offset 1 + 488 CHECK_EQ(SIZE(r.metadata.address), 1); + 489 CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); + 490 const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>()); + 491 CHECK_EQ(SIZE(address_offsets), 1); + 492 CHECK_EQ(address_offsets.begin()->offset, 1); // + 493 CHECK(address_offsets.begin()->payload_type->atom); + 494 CHECK_EQ(address_offsets.begin()->payload_type->name, "number"); + 495 // the global table contains an identical entry + 496 CHECK(contains_key(Container_metadata, r.type)); + 497 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>()); + 498 CHECK_EQ(SIZE(address_offsets2), 1); + 499 CHECK_EQ(address_offsets2.begin()->offset, 1); // + 500 CHECK(address_offsets2.begin()->payload_type->atom); + 501 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); + 502 } + 503 + 504 void test_container_address_offsets_nested() { + 505 int old_size = SIZE(Container_metadata); + 506 // define a container with a nested container containing an address + 507 run("container foo [\n" + 508 " x:address:num\n" + 509 " y:num\n" + 510 "]\n" + 511 "container bar [\n" + 512 " p:point\n" + 513 " f:foo\n" // nested container containing address + 514 "]\n"); + 515 reagent r("x:bar"); + 516 compute_container_sizes(r, ""); // need to first pre-populate the metadata + 517 // global metadata contains entries for bar and included types: point and foo + 518 // no entries for non-container types or other junk + 519 CHECK_EQ(SIZE(Container_metadata)-old_size, 3); + 520 // scan + 521 compute_container_address_offsets(r, ""); + 522 // the reagent we scanned knows it has an address at offset 2 + 523 CHECK_EQ(SIZE(r.metadata.address), 1); + 524 CHECK(contains_key(r.metadata.address, set<tag_condition_info>())); + 525 const set<address_element_info>& address_offsets = get(r.metadata.address, set<tag_condition_info>()); + 526 CHECK_EQ(SIZE(address_offsets), 1); + 527 CHECK_EQ(address_offsets.begin()->offset, 2); // + 528 CHECK(address_offsets.begin()->payload_type->atom); + 529 CHECK_EQ(address_offsets.begin()->payload_type->name, "number"); + 530 // the global table also knows its address offset + 531 CHECK(contains_key(Container_metadata, r.type)); + 532 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, r.type).address, set<tag_condition_info>()); + 533 CHECK_EQ(SIZE(address_offsets2), 1); + 534 CHECK_EQ(address_offsets2.begin()->offset, 2); // + 535 CHECK(address_offsets2.begin()->payload_type->atom); + 536 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); + 537 // compute_container_address_offsets creates no new entries + 538 CHECK_EQ(SIZE(Container_metadata)-old_size, 3); + 539 } + 540 + 541 void test_container_address_offsets_from_address() { + 542 int old_size = SIZE(Container_metadata); + 543 // define a container with an address at offset 0 + 544 run("container foo [\n" + 545 " x:address:num\n" + 546 "]\n"); + 547 reagent r("x:address:foo"); + 548 compute_container_sizes(r, ""); // need to first pre-populate the metadata + 549 // global metadata contains just the entry for foo + 550 // no entries for non-container types or other junk + 551 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 552 // scan an address to the container + 553 compute_container_address_offsets(r, ""); + 554 // compute_container_address_offsets creates no new entries + 555 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 556 // scanning precomputed metadata for the container + 557 reagent container("x:foo"); + 558 CHECK(contains_key(Container_metadata, container.type)); + 559 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); + 560 CHECK_EQ(SIZE(address_offsets2), 1); + 561 CHECK_EQ(address_offsets2.begin()->offset, 0); + 562 CHECK(address_offsets2.begin()->payload_type->atom); + 563 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); + 564 } + 565 + 566 void test_container_address_offsets_from_array() { + 567 int old_size = SIZE(Container_metadata); + 568 // define a container with an address at offset 0 + 569 run("container foo [\n" + 570 " x:address:num\n" + 571 "]\n"); + 572 reagent r("x:array:foo"); + 573 compute_container_sizes(r, ""); // need to first pre-populate the metadata + 574 // global metadata contains just the entry for foo + 575 // no entries for non-container types or other junk + 576 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 577 // scan an array of the container + 578 compute_container_address_offsets(r, ""); + 579 // compute_container_address_offsets creates no new entries + 580 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 581 // scanning precomputed metadata for the container + 582 reagent container("x:foo"); + 583 CHECK(contains_key(Container_metadata, container.type)); + 584 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); + 585 CHECK_EQ(SIZE(address_offsets2), 1); + 586 CHECK_EQ(address_offsets2.begin()->offset, 0); + 587 CHECK(address_offsets2.begin()->payload_type->atom); + 588 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); + 589 } + 590 + 591 void test_container_address_offsets_from_address_to_array() { + 592 int old_size = SIZE(Container_metadata); + 593 // define a container with an address at offset 0 + 594 run("container foo [\n" + 595 " x:address:num\n" + 596 "]\n"); + 597 reagent r("x:address:array:foo"); + 598 compute_container_sizes(r, ""); // need to first pre-populate the metadata + 599 // global metadata contains just the entry for foo + 600 // no entries for non-container types or other junk + 601 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 602 // scan an address to an array of the container + 603 compute_container_address_offsets(r, ""); + 604 // compute_container_address_offsets creates no new entries + 605 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 606 // scanning precomputed metadata for the container + 607 reagent container("x:foo"); + 608 CHECK(contains_key(Container_metadata, container.type)); + 609 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); + 610 CHECK_EQ(SIZE(address_offsets2), 1); + 611 CHECK_EQ(address_offsets2.begin()->offset, 0); + 612 CHECK(address_offsets2.begin()->payload_type->atom); + 613 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); + 614 } + 615 + 616 void test_container_address_offsets_from_static_array() { + 617 int old_size = SIZE(Container_metadata); + 618 // define a container with an address at offset 0 + 619 run("container foo [\n" + 620 " x:address:num\n" + 621 "]\n"); + 622 reagent r("x:array:foo:10"); + 623 compute_container_sizes(r, ""); // need to first pre-populate the metadata + 624 // global metadata contains just the entry for foo + 625 // no entries for non-container types or other junk + 626 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 627 // scan a static array of the container + 628 compute_container_address_offsets(r, ""); + 629 // compute_container_address_offsets creates no new entries + 630 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 631 // scanning precomputed metadata for the container + 632 reagent container("x:foo"); + 633 CHECK(contains_key(Container_metadata, container.type)); + 634 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); + 635 CHECK_EQ(SIZE(address_offsets2), 1); + 636 CHECK_EQ(address_offsets2.begin()->offset, 0); + 637 CHECK(address_offsets2.begin()->payload_type->atom); + 638 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); + 639 } + 640 + 641 void test_container_address_offsets_from_address_to_static_array() { + 642 int old_size = SIZE(Container_metadata); + 643 // define a container with an address at offset 0 + 644 run("container foo [\n" + 645 " x:address:num\n" + 646 "]\n"); + 647 reagent r("x:address:array:foo:10"); + 648 compute_container_sizes(r, ""); // need to first pre-populate the metadata + 649 // global metadata contains just the entry for foo + 650 // no entries for non-container types or other junk + 651 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 652 // scan an address to a static array of the container + 653 compute_container_address_offsets(r, ""); + 654 // compute_container_address_offsets creates no new entries + 655 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 656 // scanning precomputed metadata for the container + 657 reagent container("x:foo"); + 658 CHECK(contains_key(Container_metadata, container.type)); + 659 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); + 660 CHECK_EQ(SIZE(address_offsets2), 1); + 661 CHECK_EQ(address_offsets2.begin()->offset, 0); + 662 CHECK(address_offsets2.begin()->payload_type->atom); + 663 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); + 664 } + 665 + 666 void test_container_address_offsets_from_repeated_address_and_array_types() { + 667 int old_size = SIZE(Container_metadata); + 668 // define a container with an address at offset 0 + 669 run("container foo [\n" + 670 " x:address:num\n" + 671 "]\n"); + 672 // scan a deep nest of 'address' and 'array' types modifying a container + 673 reagent r("x:address:array:address:address:array:foo:10"); + 674 compute_container_sizes(r, ""); // need to first pre-populate the metadata + 675 // global metadata contains just the entry for foo + 676 // no entries for non-container types or other junk + 677 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 678 compute_container_address_offsets(r, ""); + 679 // compute_container_address_offsets creates no new entries + 680 CHECK_EQ(SIZE(Container_metadata)-old_size, 1); + 681 // scanning precomputed metadata for the container + 682 reagent container("x:foo"); + 683 CHECK(contains_key(Container_metadata, container.type)); + 684 const set<address_element_info>& address_offsets2 = get(get(Container_metadata, container.type).address, set<tag_condition_info>()); + 685 CHECK_EQ(SIZE(address_offsets2), 1); + 686 CHECK_EQ(address_offsets2.begin()->offset, 0); + 687 CHECK(address_offsets2.begin()->payload_type->atom); + 688 CHECK_EQ(address_offsets2.begin()->payload_type->name, "number"); + 689 } + 690 + 691 //: use metadata.address to update refcounts within containers, arrays and + 692 //: exclusive containers + 693 + 694 :(before "End Increment Refcounts(canonized_x)") + 695 if (is_mu_container(canonized_x) || is_mu_exclusive_container(canonized_x)) { + 696 const container_metadata& metadata = get(Container_metadata, canonized_x.type); + 697 for (map<set<tag_condition_info>, set<address_element_info> >::const_iterator p = metadata.address.begin(); p != metadata.address.end(); ++p) { + 698 if (!all_match(data, p->first)) continue; + 699 for (set<address_element_info>::const_iterator info = p->second.begin(); info != p->second.end(); ++info) + 700 increment_refcount(data.at(info->offset)); + 701 } + 702 } + 703 + 704 :(before "End Decrement Refcounts(canonized_x)") + 705 if (is_mu_container(canonized_x) || is_mu_exclusive_container(canonized_x)) { + 706 trace(9999, "mem") << "need to read old value of '" << to_string(canonized_x) << "' to figure out what refcounts to decrement" << end(); + 707 // read from canonized_x but without canonizing again + 708 // todo: inline without running canonize all over again + 709 reagent/*copy*/ tmp = canonized_x; + 710 tmp.properties.push_back(pair<string, string_tree*>("raw", NULL)); + 711 vector<double> data = read_memory(tmp); + 712 trace(9999, "mem") << "done reading old value of '" << to_string(canonized_x) << "'" << end(); + 713 const container_metadata& metadata = get(Container_metadata, canonized_x.type); + 714 for (map<set<tag_condition_info>, set<address_element_info> >::const_iterator p = metadata.address.begin(); p != metadata.address.end(); ++p) { + 715 if (!all_match(data, p->first)) continue; + 716 for (set<address_element_info>::const_iterator info = p->second.begin(); info != p->second.end(); ++info) { + 717 int element_address = get_or_insert(Memory, canonized_x.value + info->offset); + 718 reagent/*local*/ element; + 719 element.set_value(element_address+/*skip refcount*/1); + 720 element.type = new type_tree(*info->payload_type); + 721 decrement_refcount(element_address, info->payload_type, size_of(element)+/*refcount*/1); + 722 } + 723 } + 724 } + 725 + 726 :(code) + 727 bool all_match(const vector<double>& data, const set<tag_condition_info>& conditions) { + 728 for (set<tag_condition_info>::const_iterator p = conditions.begin(); p != conditions.end(); ++p) { + 729 if (data.at(p->offset) != p->tag) + 730 return false; + 731 } + 732 return true; + 733 } + 734 + 735 :(scenario refcounts_put_container) + 736 container foo [ + 737 a:bar # contains an address + 738 ] + 739 container bar [ + 740 x:address:num 741 ] - 742 +run: {1: ("address" "number")} <- new {number: "type"} - 743 +mem: incrementing refcount of 1000: 0 -> 1 - 744 +run: {2: "bar"} <- merge {1: ("address" "number")} - 745 +mem: incrementing refcount of 1000: 1 -> 2 - 746 +run: {3: ("address" "foo"), "lookup": ()} <- put {3: ("address" "foo"), "lookup": ()}, {a: "offset"}, {2: "bar"} - 747 # put increments refcount inside container - 748 +mem: incrementing refcount of 1000: 2 -> 3 - 749 - 750 :(scenario refcounts_put_index_array) - 751 container bar [ - 752 x:address:num - 753 ] - 754 def main [ - 755 1:address:num <- new number:type - 756 2:bar <- merge 1:address:num - 757 3:address:array:bar <- new bar:type, 3 - 758 *3:address:array:bar <- put-index *3:address:array:bar, 0, 2:bar + 742 def main [ + 743 1:address:num <- new number:type + 744 2:bar <- merge 1:address:num + 745 3:address:foo <- new foo:type + 746 *3:address:foo <- put *3:address:foo, a:offset, 2:bar + 747 ] + 748 +run: {1: ("address" "number")} <- new {number: "type"} + 749 +mem: incrementing refcount of 1000: 0 -> 1 + 750 +run: {2: "bar"} <- merge {1: ("address" "number")} + 751 +mem: incrementing refcount of 1000: 1 -> 2 + 752 +run: {3: ("address" "foo"), "lookup": ()} <- put {3: ("address" "foo"), "lookup": ()}, {a: "offset"}, {2: "bar"} + 753 # put increments refcount inside container + 754 +mem: incrementing refcount of 1000: 2 -> 3 + 755 + 756 :(scenario refcounts_put_index_array) + 757 container bar [ + 758 x:address:num 759 ] - 760 +run: {1: ("address" "number")} <- new {number: "type"} - 761 +mem: incrementing refcount of 1000: 0 -> 1 - 762 +run: {2: "bar"} <- merge {1: ("address" "number")} - 763 +mem: incrementing refcount of 1000: 1 -> 2 - 764 +run: {3: ("address" "array" "bar"), "lookup": ()} <- put-index {3: ("address" "array" "bar"), "lookup": ()}, {0: "literal"}, {2: "bar"} - 765 # put-index increments refcount inside container - 766 +mem: incrementing refcount of 1000: 2 -> 3 - 767 - 768 :(scenario refcounts_maybe_convert_container) - 769 exclusive-container foo [ - 770 a:num - 771 b:bar # contains an address - 772 ] - 773 container bar [ - 774 x:address:num - 775 ] - 776 def main [ - 777 1:address:num <- new number:type - 778 2:bar <- merge 1:address:num - 779 3:foo <- merge 1/b, 2:bar - 780 5:bar, 6:bool <- maybe-convert 3:foo, 1:variant/b + 760 def main [ + 761 1:address:num <- new number:type + 762 2:bar <- merge 1:address:num + 763 3:address:array:bar <- new bar:type, 3 + 764 *3:address:array:bar <- put-index *3:address:array:bar, 0, 2:bar + 765 ] + 766 +run: {1: ("address" "number")} <- new {number: "type"} + 767 +mem: incrementing refcount of 1000: 0 -> 1 + 768 +run: {2: "bar"} <- merge {1: ("address" "number")} + 769 +mem: incrementing refcount of 1000: 1 -> 2 + 770 +run: {3: ("address" "array" "bar"), "lookup": ()} <- put-index {3: ("address" "array" "bar"), "lookup": ()}, {0: "literal"}, {2: "bar"} + 771 # put-index increments refcount inside container + 772 +mem: incrementing refcount of 1000: 2 -> 3 + 773 + 774 :(scenario refcounts_maybe_convert_container) + 775 exclusive-container foo [ + 776 a:num + 777 b:bar # contains an address + 778 ] + 779 container bar [ + 780 x:address:num 781 ] - 782 +run: {1: ("address" "number")} <- new {number: "type"} - 783 +mem: incrementing refcount of 1000: 0 -> 1 - 784 +run: {2: "bar"} <- merge {1: ("address" "number")} - 785 +mem: incrementing refcount of 1000: 1 -> 2 - 786 +run: {3: "foo"} <- merge {1: "literal", "b": ()}, {2: "bar"} - 787 +mem: incrementing refcount of 1000: 2 -> 3 - 788 +run: {5: "bar"}, {6: "boolean"} <- maybe-convert {3: "foo"}, {1: "variant", "b": ()} - 789 +mem: incrementing refcount of 1000: 3 -> 4 - 790 - 791 :(scenario refcounts_copy_doubly_nested) - 792 container foo [ - 793 a:bar # no addresses - 794 b:curr # contains addresses - 795 ] - 796 container bar [ - 797 x:num - 798 y:num - 799 ] - 800 container curr [ - 801 x:num - 802 y:address:num # address inside container inside container - 803 ] - 804 def main [ - 805 1:address:num <- new number:type - 806 2:address:curr <- new curr:type - 807 *2:address:curr <- put *2:address:curr, 1:offset/y, 1:address:num - 808 3:address:foo <- new foo:type - 809 *3:address:foo <- put *3:address:foo, 1:offset/b, *2:address:curr - 810 4:foo <- copy *3:address:foo - 811 ] - 812 +transform: compute address offsets for container foo - 813 +transform: checking container foo, element 1 - 814 +transform: address at offset 3 - 815 +run: {1: ("address" "number")} <- new {number: "type"} - 816 +mem: incrementing refcount of 1000: 0 -> 1 - 817 # storing an address in a container updates its refcount - 818 +run: {2: ("address" "curr"), "lookup": ()} <- put {2: ("address" "curr"), "lookup": ()}, {1: "offset", "y": ()}, {1: ("address" "number")} - 819 +mem: incrementing refcount of 1000: 1 -> 2 - 820 # storing a container in a container updates refcounts of any contained addresses - 821 +run: {3: ("address" "foo"), "lookup": ()} <- put {3: ("address" "foo"), "lookup": ()}, {1: "offset", "b": ()}, {2: ("address" "curr"), "lookup": ()} - 822 +mem: incrementing refcount of 1000: 2 -> 3 - 823 # copying a container containing a container containing an address updates refcount - 824 +run: {4: "foo"} <- copy {3: ("address" "foo"), "lookup": ()} - 825 +mem: incrementing refcount of 1000: 3 -> 4 - 826 - 827 :(scenario refcounts_copy_exclusive_container_within_container) - 828 container foo [ - 829 a:num - 830 b:bar - 831 ] - 832 exclusive-container bar [ - 833 x:num - 834 y:num - 835 z:address:num - 836 ] - 837 def main [ - 838 1:address:num <- new number:type - 839 2:bar <- merge 0/x, 34 - 840 3:foo <- merge 12, 2:bar - 841 5:bar <- merge 1/y, 35 - 842 6:foo <- merge 13, 5:bar - 843 8:bar <- merge 2/z, 1:address:num - 844 9:foo <- merge 14, 8:bar - 845 11:foo <- copy 9:foo - 846 ] - 847 +run: {1: ("address" "number")} <- new {number: "type"} - 848 +mem: incrementing refcount of 1000: 0 -> 1 - 849 # no change while merging items of other types - 850 +run: {8: "bar"} <- merge {2: "literal", "z": ()}, {1: ("address" "number")} - 851 +mem: incrementing refcount of 1000: 1 -> 2 - 852 +run: {9: "foo"} <- merge {14: "literal"}, {8: "bar"} - 853 +mem: incrementing refcount of 1000: 2 -> 3 - 854 +run: {11: "foo"} <- copy {9: "foo"} - 855 +mem: incrementing refcount of 1000: 3 -> 4 - 856 - 857 :(scenario refcounts_copy_container_within_exclusive_container) - 858 exclusive-container foo [ - 859 a:num - 860 b:bar - 861 ] - 862 container bar [ - 863 x:num - 864 y:num - 865 z:address:num - 866 ] - 867 def main [ - 868 1:address:num <- new number:type - 869 2:foo <- merge 0/a, 34 - 870 6:foo <- merge 0/a, 35 - 871 10:bar <- merge 2/x, 15/y, 1:address:num - 872 13:foo <- merge 1/b, 10:bar - 873 17:foo <- copy 13:foo - 874 ] - 875 +run: {1: ("address" "number")} <- new {number: "type"} - 876 +mem: incrementing refcount of 1000: 0 -> 1 - 877 # no change while merging items of other types - 878 +run: {10: "bar"} <- merge {2: "literal", "x": ()}, {15: "literal", "y": ()}, {1: ("address" "number")} - 879 +mem: incrementing refcount of 1000: 1 -> 2 - 880 +run: {13: "foo"} <- merge {1: "literal", "b": ()}, {10: "bar"} - 881 +mem: incrementing refcount of 1000: 2 -> 3 - 882 +run: {17: "foo"} <- copy {13: "foo"} - 883 +mem: incrementing refcount of 1000: 3 -> 4 - 884 - 885 :(scenario refcounts_copy_exclusive_container_within_exclusive_container) - 886 exclusive-container foo [ - 887 a:num - 888 b:bar - 889 ] - 890 exclusive-container bar [ - 891 x:num - 892 y:address:num - 893 ] - 894 def main [ - 895 1:address:num <- new number:type - 896 10:foo <- merge 1/b, 1/y, 1:address:num - 897 20:foo <- copy 10:foo - 898 ] - 899 +run: {1: ("address" "number")} <- new {number: "type"} - 900 +mem: incrementing refcount of 1000: 0 -> 1 - 901 # no change while merging items of other types - 902 +run: {10: "foo"} <- merge {1: "literal", "b": ()}, {1: "literal", "y": ()}, {1: ("address" "number")} - 903 +mem: incrementing refcount of 1000: 1 -> 2 - 904 +run: {20: "foo"} <- copy {10: "foo"} - 905 +mem: incrementing refcount of 1000: 2 -> 3 - 906 - 907 :(scenario refcounts_copy_array_within_container) - 908 container foo [ - 909 x:address:array:num - 910 ] - 911 def main [ - 912 1:address:array:num <- new number:type, 3 - 913 2:foo <- merge 1:address:array:num - 914 3:address:array:num <- new number:type, 5 - 915 2:foo <- merge 3:address:array:num + 782 def main [ + 783 1:address:num <- new number:type + 784 2:bar <- merge 1:address:num + 785 3:foo <- merge 1/b, 2:bar + 786 5:bar, 6:bool <- maybe-convert 3:foo, 1:variant/b + 787 ] + 788 +run: {1: ("address" "number")} <- new {number: "type"} + 789 +mem: incrementing refcount of 1000: 0 -> 1 + 790 +run: {2: "bar"} <- merge {1: ("address" "number")} + 791 +mem: incrementing refcount of 1000: 1 -> 2 + 792 +run: {3: "foo"} <- merge {1: "literal", "b": ()}, {2: "bar"} + 793 +mem: incrementing refcount of 1000: 2 -> 3 + 794 +run: {5: "bar"}, {6: "boolean"} <- maybe-convert {3: "foo"}, {1: "variant", "b": ()} + 795 +mem: incrementing refcount of 1000: 3 -> 4 + 796 + 797 :(scenario refcounts_copy_doubly_nested) + 798 container foo [ + 799 a:bar # no addresses + 800 b:curr # contains addresses + 801 ] + 802 container bar [ + 803 x:num + 804 y:num + 805 ] + 806 container curr [ + 807 x:num + 808 y:address:num # address inside container inside container + 809 ] + 810 def main [ + 811 1:address:num <- new number:type + 812 2:address:curr <- new curr:type + 813 *2:address:curr <- put *2:address:curr, 1:offset/y, 1:address:num + 814 3:address:foo <- new foo:type + 815 *3:address:foo <- put *3:address:foo, 1:offset/b, *2:address:curr + 816 4:foo <- copy *3:address:foo + 817 ] + 818 +transform: compute address offsets for container foo + 819 +transform: checking container foo, element 1 + 820 +transform: address at offset 3 + 821 +run: {1: ("address" "number")} <- new {number: "type"} + 822 +mem: incrementing refcount of 1000: 0 -> 1 + 823 # storing an address in a container updates its refcount + 824 +run: {2: ("address" "curr"), "lookup": ()} <- put {2: ("address" "curr"), "lookup": ()}, {1: "offset", "y": ()}, {1: ("address" "number")} + 825 +mem: incrementing refcount of 1000: 1 -> 2 + 826 # storing a container in a container updates refcounts of any contained addresses + 827 +run: {3: ("address" "foo"), "lookup": ()} <- put {3: ("address" "foo"), "lookup": ()}, {1: "offset", "b": ()}, {2: ("address" "curr"), "lookup": ()} + 828 +mem: incrementing refcount of 1000: 2 -> 3 + 829 # copying a container containing a container containing an address updates refcount + 830 +run: {4: "foo"} <- copy {3: ("address" "foo"), "lookup": ()} + 831 +mem: incrementing refcount of 1000: 3 -> 4 + 832 + 833 :(scenario refcounts_copy_exclusive_container_within_container) + 834 container foo [ + 835 a:num + 836 b:bar + 837 ] + 838 exclusive-container bar [ + 839 x:num + 840 y:num + 841 z:address:num + 842 ] + 843 def main [ + 844 1:address:num <- new number:type + 845 2:bar <- merge 0/x, 34 + 846 3:foo <- merge 12, 2:bar + 847 5:bar <- merge 1/y, 35 + 848 6:foo <- merge 13, 5:bar + 849 8:bar <- merge 2/z, 1:address:num + 850 9:foo <- merge 14, 8:bar + 851 11:foo <- copy 9:foo + 852 ] + 853 +run: {1: ("address" "number")} <- new {number: "type"} + 854 +mem: incrementing refcount of 1000: 0 -> 1 + 855 # no change while merging items of other types + 856 +run: {8: "bar"} <- merge {2: "literal", "z": ()}, {1: ("address" "number")} + 857 +mem: incrementing refcount of 1000: 1 -> 2 + 858 +run: {9: "foo"} <- merge {14: "literal"}, {8: "bar"} + 859 +mem: incrementing refcount of 1000: 2 -> 3 + 860 +run: {11: "foo"} <- copy {9: "foo"} + 861 +mem: incrementing refcount of 1000: 3 -> 4 + 862 + 863 :(scenario refcounts_copy_container_within_exclusive_container) + 864 exclusive-container foo [ + 865 a:num + 866 b:bar + 867 ] + 868 container bar [ + 869 x:num + 870 y:num + 871 z:address:num + 872 ] + 873 def main [ + 874 1:address:num <- new number:type + 875 2:foo <- merge 0/a, 34 + 876 6:foo <- merge 0/a, 35 + 877 10:bar <- merge 2/x, 15/y, 1:address:num + 878 13:foo <- merge 1/b, 10:bar + 879 17:foo <- copy 13:foo + 880 ] + 881 +run: {1: ("address" "number")} <- new {number: "type"} + 882 +mem: incrementing refcount of 1000: 0 -> 1 + 883 # no change while merging items of other types + 884 +run: {10: "bar"} <- merge {2: "literal", "x": ()}, {15: "literal", "y": ()}, {1: ("address" "number")} + 885 +mem: incrementing refcount of 1000: 1 -> 2 + 886 +run: {13: "foo"} <- merge {1: "literal", "b": ()}, {10: "bar"} + 887 +mem: incrementing refcount of 1000: 2 -> 3 + 888 +run: {17: "foo"} <- copy {13: "foo"} + 889 +mem: incrementing refcount of 1000: 3 -> 4 + 890 + 891 :(scenario refcounts_copy_exclusive_container_within_exclusive_container) + 892 exclusive-container foo [ + 893 a:num + 894 b:bar + 895 ] + 896 exclusive-container bar [ + 897 x:num + 898 y:address:num + 899 ] + 900 def main [ + 901 1:address:num <- new number:type + 902 10:foo <- merge 1/b, 1/y, 1:address:num + 903 20:foo <- copy 10:foo + 904 ] + 905 +run: {1: ("address" "number")} <- new {number: "type"} + 906 +mem: incrementing refcount of 1000: 0 -> 1 + 907 # no change while merging items of other types + 908 +run: {10: "foo"} <- merge {1: "literal", "b": ()}, {1: "literal", "y": ()}, {1: ("address" "number")} + 909 +mem: incrementing refcount of 1000: 1 -> 2 + 910 +run: {20: "foo"} <- copy {10: "foo"} + 911 +mem: incrementing refcount of 1000: 2 -> 3 + 912 + 913 :(scenario refcounts_copy_array_within_container) + 914 container foo [ + 915 x:address:array:num 916 ] - 917 +run: {1: ("address" "array" "number")} <- new {number: "type"}, {3: "literal"} - 918 +mem: incrementing refcount of 1000: 0 -> 1 - 919 +run: {2: "foo"} <- merge {1: ("address" "array" "number")} - 920 +mem: incrementing refcount of 1000: 1 -> 2 - 921 +run: {2: "foo"} <- merge {3: ("address" "array" "number")} - 922 +mem: decrementing refcount of 1000: 2 -> 1 - 923 - 924 :(scenario refcounts_copy_address_within_static_array_within_container) - 925 container foo [ - 926 a:array:bar:3 - 927 b:address:num - 928 ] - 929 container bar [ - 930 y:num - 931 z:address:num - 932 ] - 933 def main [ - 934 1:address:num <- new number:type - 935 2:bar <- merge 34, 1:address:num - 936 10:array:bar:3 <- create-array - 937 put-index 10:array:bar:3, 1, 2:bar - 938 20:foo <- merge 10:array:bar:3, 1:address:num - 939 1:address:num <- copy 0 - 940 2:bar <- merge 34, 1:address:num - 941 put-index 10:array:bar:3, 1, 2:bar - 942 20:foo <- merge 10:array:bar:3, 1:address:num - 943 ] - 944 +run: {1: ("address" "number")} <- new {number: "type"} - 945 +mem: incrementing refcount of 1000: 0 -> 1 - 946 +run: {2: "bar"} <- merge {34: "literal"}, {1: ("address" "number")} - 947 +mem: incrementing refcount of 1000: 1 -> 2 - 948 +run: put-index {10: ("array" "bar" "3")}, {1: "literal"}, {2: "bar"} - 949 +mem: incrementing refcount of 1000: 2 -> 3 - 950 +run: {20: "foo"} <- merge {10: ("array" "bar" "3")}, {1: ("address" "number")} - 951 +mem: incrementing refcount of 1000: 3 -> 4 - 952 +mem: incrementing refcount of 1000: 4 -> 5 - 953 +run: {1: ("address" "number")} <- copy {0: "literal"} - 954 +mem: decrementing refcount of 1000: 5 -> 4 - 955 +run: {2: "bar"} <- merge {34: "literal"}, {1: ("address" "number")} - 956 +mem: decrementing refcount of 1000: 4 -> 3 - 957 +run: put-index {10: ("array" "bar" "3")}, {1: "literal"}, {2: "bar"} - 958 +mem: decrementing refcount of 1000: 3 -> 2 - 959 +run: {20: "foo"} <- merge {10: ("array" "bar" "3")}, {1: ("address" "number")} - 960 +mem: decrementing refcount of 1000: 2 -> 1 - 961 +mem: decrementing refcount of 1000: 1 -> 0 - 962 - 963 :(scenario refcounts_handle_exclusive_containers_with_different_tags) - 964 container foo1 [ - 965 x:address:num - 966 y:num - 967 ] - 968 container foo2 [ - 969 x:num - 970 y:address:num - 971 ] - 972 exclusive-container bar [ - 973 a:foo1 - 974 b:foo2 - 975 ] - 976 def main [ - 977 1:address:num <- copy 12000/unsafe # pretend allocation - 978 *1:address:num <- copy 34 - 979 2:bar <- merge 0/foo1, 1:address:num, 97 - 980 5:address:num <- copy 13000/unsafe # pretend allocation - 981 *5:address:num <- copy 35 - 982 6:bar <- merge 1/foo2, 98, 5:address:num - 983 2:bar <- copy 6:bar - 984 ] - 985 +run: {2: "bar"} <- merge {0: "literal", "foo1": ()}, {1: ("address" "number")}, {97: "literal"} - 986 +mem: incrementing refcount of 12000: 1 -> 2 - 987 +run: {6: "bar"} <- merge {1: "literal", "foo2": ()}, {98: "literal"}, {5: ("address" "number")} - 988 +mem: incrementing refcount of 13000: 1 -> 2 - 989 +run: {2: "bar"} <- copy {6: "bar"} - 990 +mem: incrementing refcount of 13000: 2 -> 3 - 991 +mem: decrementing refcount of 12000: 2 -> 1 - 992 - 993 :(code) - 994 bool is_mu_container(const reagent& r) { - 995 return is_mu_container(r.type); - 996 } - 997 bool is_mu_container(const type_tree* type) { - 998 if (!type) return false; - 999 // End is_mu_container(type) Special-cases -1000 if (type->value == 0) return false; -1001 type_info& info = get(Type, type->value); -1002 return info.kind == CONTAINER; -1003 } -1004 -1005 bool is_mu_exclusive_container(const reagent& r) { -1006 return is_mu_exclusive_container(r.type); -1007 } -1008 bool is_mu_exclusive_container(const type_tree* type) { -1009 if (!type) return false; -1010 // End is_mu_exclusive_container(type) Special-cases -1011 if (type->value == 0) return false; -1012 type_info& info = get(Type, type->value); -1013 return info.kind == EXCLUSIVE_CONTAINER; -1014 } + 917 def main [ + 918 1:address:array:num <- new number:type, 3 + 919 2:foo <- merge 1:address:array:num + 920 3:address:array:num <- new number:type, 5 + 921 2:foo <- merge 3:address:array:num + 922 ] + 923 +run: {1: ("address" "array" "number")} <- new {number: "type"}, {3: "literal"} + 924 +mem: incrementing refcount of 1000: 0 -> 1 + 925 +run: {2: "foo"} <- merge {1: ("address" "array" "number")} + 926 +mem: incrementing refcount of 1000: 1 -> 2 + 927 +run: {2: "foo"} <- merge {3: ("address" "array" "number")} + 928 +mem: decrementing refcount of 1000: 2 -> 1 + 929 + 930 :(scenario refcounts_copy_address_within_static_array_within_container) + 931 container foo [ + 932 a:array:bar:3 + 933 b:address:num + 934 ] + 935 container bar [ + 936 y:num + 937 z:address:num + 938 ] + 939 def main [ + 940 1:address:num <- new number:type + 941 2:bar <- merge 34, 1:address:num + 942 10:array:bar:3 <- create-array + 943 put-index 10:array:bar:3, 1, 2:bar + 944 20:foo <- merge 10:array:bar:3, 1:address:num + 945 1:address:num <- copy 0 + 946 2:bar <- merge 34, 1:address:num + 947 put-index 10:array:bar:3, 1, 2:bar + 948 20:foo <- merge 10:array:bar:3, 1:address:num + 949 ] + 950 +run: {1: ("address" "number")} <- new {number: "type"} + 951 +mem: incrementing refcount of 1000: 0 -> 1 + 952 +run: {2: "bar"} <- merge {34: "literal"}, {1: ("address" "number")} + 953 +mem: incrementing refcount of 1000: 1 -> 2 + 954 +run: put-index {10: ("array" "bar" "3")}, {1: "literal"}, {2: "bar"} + 955 +mem: incrementing refcount of 1000: 2 -> 3 + 956 +run: {20: "foo"} <- merge {10: ("array" "bar" "3")}, {1: ("address" "number")} + 957 +mem: incrementing refcount of 1000: 3 -> 4 + 958 +mem: incrementing refcount of 1000: 4 -> 5 + 959 +run: {1: ("address" "number")} <- copy {0: "literal"} + 960 +mem: decrementing refcount of 1000: 5 -> 4 + 961 +run: {2: "bar"} <- merge {34: "literal"}, {1: ("address" "number")} + 962 +mem: decrementing refcount of 1000: 4 -> 3 + 963 +run: put-index {10: ("array" "bar" "3")}, {1: "literal"}, {2: "bar"} + 964 +mem: decrementing refcount of 1000: 3 -> 2 + 965 +run: {20: "foo"} <- merge {10: ("array" "bar" "3")}, {1: ("address" "number")} + 966 +mem: decrementing refcount of 1000: 2 -> 1 + 967 +mem: decrementing refcount of 1000: 1 -> 0 + 968 + 969 :(scenario refcounts_handle_exclusive_containers_with_different_tags) + 970 container foo1 [ + 971 x:address:num + 972 y:num + 973 ] + 974 container foo2 [ + 975 x:num + 976 y:address:num + 977 ] + 978 exclusive-container bar [ + 979 a:foo1 + 980 b:foo2 + 981 ] + 982 def main [ + 983 1:address:num <- copy 12000/unsafe # pretend allocation + 984 *1:address:num <- copy 34 + 985 2:bar <- merge 0/foo1, 1:address:num, 97 + 986 5:address:num <- copy 13000/unsafe # pretend allocation + 987 *5:address:num <- copy 35 + 988 6:bar <- merge 1/foo2, 98, 5:address:num + 989 2:bar <- copy 6:bar + 990 ] + 991 +run: {2: "bar"} <- merge {0: "literal", "foo1": ()}, {1: ("address" "number")}, {97: "literal"} + 992 +mem: incrementing refcount of 12000: 1 -> 2 + 993 +run: {6: "bar"} <- merge {1: "literal", "foo2": ()}, {98: "literal"}, {5: ("address" "number")} + 994 +mem: incrementing refcount of 13000: 1 -> 2 + 995 +run: {2: "bar"} <- copy {6: "bar"} + 996 +mem: incrementing refcount of 13000: 2 -> 3 + 997 +mem: decrementing refcount of 12000: 2 -> 1 + 998 + 999 :(code) +1000 bool is_mu_container(const reagent& r) { +1001 return is_mu_container(r.type); +1002 } +1003 bool is_mu_container(const type_tree* type) { +1004 if (!type) return false; +1005 // End is_mu_container(type) Special-cases +1006 if (type->value == 0) return false; +1007 type_info& info = get(Type, type->value); +1008 return info.kind == CONTAINER; +1009 } +1010 +1011 bool is_mu_exclusive_container(const reagent& r) { +1012 return is_mu_exclusive_container(r.type); +1013 } +1014 bool is_mu_exclusive_container(const type_tree* type) { +1015 if (!type) return false; +1016 // End is_mu_exclusive_container(type) Special-cases +1017 if (type->value == 0) return false; +1018 type_info& info = get(Type, type->value); +1019 return info.kind == EXCLUSIVE_CONTAINER; +1020 } diff --git a/html/037abandon.cc.html b/html/037abandon.cc.html index b1326186..0718a421 100644 --- a/html/037abandon.cc.html +++ b/html/037abandon.cc.html @@ -101,14 +101,14 @@ if ('onhashchange' in window) { 38 assert(element.type->name != "array"); 39 int element_size = size_of(element); 40 for (int i = 0; i < array_length; ++i) { - 41 element.set_value(address + /*skip refcount and length*/2 + i*element_size); + 41 element.set_value(address + /*skip refcount and length*/2 + i*element_size); 42 decrement_any_refcounts(element); 43 } 44 } 45 else if (is_mu_container(payload_type) || is_mu_exclusive_container(payload_type)) { 46 reagent tmp; 47 tmp.type = new type_tree(*payload_type); - 48 tmp.set_value(address + /*skip refcount*/1); + 48 tmp.set_value(address + /*skip refcount*/1); 49 decrement_any_refcounts(tmp); 50 } 51 // clear memory diff --git a/html/040brace.cc.html b/html/040brace.cc.html index fb92c3da..41ac0d83 100644 --- a/html/040brace.cc.html +++ b/html/040brace.cc.html @@ -128,411 +128,412 @@ if ('onhashchange' in window) { 67 continue; 68 } 69 if (inst.is_label) continue; - 70 if (inst.old_name != "loop" - 71 && inst.old_name != "loop-if" - 72 && inst.old_name != "loop-unless" - 73 && inst.old_name != "break" - 74 && inst.old_name != "break-if" - 75 && inst.old_name != "break-unless") { - 76 trace(9992, "transform") << inst.old_name << " ..." << end(); + 70 if (inst.name != "loop" + 71 && inst.name != "loop-if" + 72 && inst.name != "loop-unless" + 73 && inst.name != "break" + 74 && inst.name != "break-if" + 75 && inst.name != "break-unless") { + 76 trace(9992, "transform") << inst.name << " ..." << end(); 77 continue; 78 } 79 // check for errors - 80 if (inst.old_name.find("-if") != string::npos || inst.old_name.find("-unless") != string::npos) { + 80 if (inst.name.find("-if") != string::npos || inst.name.find("-unless") != string::npos) { 81 if (inst.ingredients.empty()) { - 82 raise << "'" << inst.old_name << "' expects 1 or 2 ingredients, but got none\n" << end(); + 82 raise << "'" << inst.name << "' expects 1 or 2 ingredients, but got none\n" << end(); 83 continue; 84 } 85 } 86 // update instruction operation - 87 if (inst.old_name.find("-if") != string::npos) { - 88 inst.name = "jump-if"; - 89 inst.operation = JUMP_IF; - 90 } - 91 else if (inst.old_name.find("-unless") != string::npos) { - 92 inst.name = "jump-unless"; - 93 inst.operation = JUMP_UNLESS; - 94 } - 95 else { - 96 inst.name = "jump"; - 97 inst.operation = JUMP; - 98 } - 99 // check for explicitly provided targets -100 if (inst.old_name.find("-if") != string::npos || inst.old_name.find("-unless") != string::npos) { -101 // conditional branches check arg 1 -102 if (SIZE(inst.ingredients) > 1 && is_literal(inst.ingredients.at(1))) { -103 trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(1).name << ":offset" << end(); -104 continue; -105 } -106 } -107 else { -108 // unconditional branches check arg 0 -109 if (!inst.ingredients.empty() && is_literal(inst.ingredients.at(0))) { -110 trace(9992, "transform") << "jump " << inst.ingredients.at(0).name << ":offset" << end(); -111 continue; -112 } -113 } -114 // if implicit, compute target -115 reagent target; -116 target.type = new type_tree("offset"); -117 target.set_value(0); -118 if (open_braces.empty()) -119 raise << "'" << inst.old_name << "' needs a '{' before\n" << end(); -120 else if (inst.old_name.find("loop") != string::npos) -121 target.set_value(open_braces.top()-index); -122 else // break instruction -123 target.set_value(matching_brace(open_braces.top(), braces, r) - index - 1); -124 inst.ingredients.push_back(target); -125 // log computed target -126 if (inst.name == "jump") -127 trace(9992, "transform") << "jump " << no_scientific(target.value) << ":offset" << end(); -128 else -129 trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(0).name << ", " << no_scientific(target.value) << ":offset" << end(); -130 } -131 } -132 -133 // returns a signed integer not just so that we can return -1 but also to -134 // enable future signed arithmetic -135 int matching_brace(int index, const list<pair<int, int> >& braces, recipe_ordinal r) { -136 int stacksize = 0; -137 for (list<pair<int, int> >::const_iterator p = braces.begin(); p != braces.end(); ++p) { -138 if (p->second < index) continue; -139 stacksize += (p->first ? 1 : -1); -140 if (stacksize == 0) return p->second; -141 } -142 raise << maybe(get(Recipe, r).name) << "unbalanced '{'\n" << end(); -143 return SIZE(get(Recipe, r).steps); // exit current routine -144 } -145 -146 :(scenario loop) -147 def main [ -148 1:num <- copy 0 -149 2:num <- copy 0 -150 { -151 3:num <- copy 0 -152 loop -153 } -154 ] -155 +transform: --- transform braces for recipe main -156 +transform: copy ... + 87 string old_name = inst.name; // save a copy + 88 if (inst.name.find("-if") != string::npos) { + 89 inst.name = "jump-if"; + 90 inst.operation = JUMP_IF; + 91 } + 92 else if (inst.name.find("-unless") != string::npos) { + 93 inst.name = "jump-unless"; + 94 inst.operation = JUMP_UNLESS; + 95 } + 96 else { + 97 inst.name = "jump"; + 98 inst.operation = JUMP; + 99 } +100 // check for explicitly provided targets +101 if (inst.name.find("-if") != string::npos || inst.name.find("-unless") != string::npos) { +102 // conditional branches check arg 1 +103 if (SIZE(inst.ingredients) > 1 && is_literal(inst.ingredients.at(1))) { +104 trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(1).name << ":offset" << end(); +105 continue; +106 } +107 } +108 else { +109 // unconditional branches check arg 0 +110 if (!inst.ingredients.empty() && is_literal(inst.ingredients.at(0))) { +111 trace(9992, "transform") << "jump " << inst.ingredients.at(0).name << ":offset" << end(); +112 continue; +113 } +114 } +115 // if implicit, compute target +116 reagent target; +117 target.type = new type_tree("offset"); +118 target.set_value(0); +119 if (open_braces.empty()) +120 raise << "'" << old_name << "' needs a '{' before\n" << end(); +121 else if (old_name.find("loop") != string::npos) +122 target.set_value(open_braces.top()-index); +123 else // break instruction +124 target.set_value(matching_brace(open_braces.top(), braces, r) - index - 1); +125 inst.ingredients.push_back(target); +126 // log computed target +127 if (inst.name == "jump") +128 trace(9992, "transform") << "jump " << no_scientific(target.value) << ":offset" << end(); +129 else +130 trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(0).name << ", " << no_scientific(target.value) << ":offset" << end(); +131 } +132 } +133 +134 // returns a signed integer not just so that we can return -1 but also to +135 // enable future signed arithmetic +136 int matching_brace(int index, const list<pair<int, int> >& braces, recipe_ordinal r) { +137 int stacksize = 0; +138 for (list<pair<int, int> >::const_iterator p = braces.begin(); p != braces.end(); ++p) { +139 if (p->second < index) continue; +140 stacksize += (p->first ? 1 : -1); +141 if (stacksize == 0) return p->second; +142 } +143 raise << maybe(get(Recipe, r).name) << "unbalanced '{'\n" << end(); +144 return SIZE(get(Recipe, r).steps); // exit current routine +145 } +146 +147 :(scenario loop) +148 def main [ +149 1:num <- copy 0 +150 2:num <- copy 0 +151 { +152 3:num <- copy 0 +153 loop +154 } +155 ] +156 +transform: --- transform braces for recipe main 157 +transform: copy ... 158 +transform: copy ... -159 +transform: jump -2:offset -160 -161 :(scenario break_empty_block) -162 def main [ -163 1:num <- copy 0 -164 { -165 break -166 } -167 ] -168 +transform: --- transform braces for recipe main -169 +transform: copy ... -170 +transform: jump 0:offset -171 -172 :(scenario break_cascading) -173 def main [ -174 1:num <- copy 0 -175 { -176 break -177 } -178 { -179 break -180 } -181 ] -182 +transform: --- transform braces for recipe main -183 +transform: copy ... -184 +transform: jump 0:offset +159 +transform: copy ... +160 +transform: jump -2:offset +161 +162 :(scenario break_empty_block) +163 def main [ +164 1:num <- copy 0 +165 { +166 break +167 } +168 ] +169 +transform: --- transform braces for recipe main +170 +transform: copy ... +171 +transform: jump 0:offset +172 +173 :(scenario break_cascading) +174 def main [ +175 1:num <- copy 0 +176 { +177 break +178 } +179 { +180 break +181 } +182 ] +183 +transform: --- transform braces for recipe main +184 +transform: copy ... 185 +transform: jump 0:offset -186 -187 :(scenario break_cascading_2) -188 def main [ -189 1:num <- copy 0 -190 2:num <- copy 0 -191 { -192 break -193 3:num <- copy 0 -194 } -195 { -196 break -197 } -198 ] -199 +transform: --- transform braces for recipe main -200 +transform: copy ... +186 +transform: jump 0:offset +187 +188 :(scenario break_cascading_2) +189 def main [ +190 1:num <- copy 0 +191 2:num <- copy 0 +192 { +193 break +194 3:num <- copy 0 +195 } +196 { +197 break +198 } +199 ] +200 +transform: --- transform braces for recipe main 201 +transform: copy ... -202 +transform: jump 1:offset -203 +transform: copy ... -204 +transform: jump 0:offset -205 -206 :(scenario break_if) -207 def main [ -208 1:num <- copy 0 -209 2:num <- copy 0 -210 { -211 break-if 2:num -212 3:num <- copy 0 -213 } -214 { -215 break -216 } -217 ] -218 +transform: --- transform braces for recipe main -219 +transform: copy ... +202 +transform: copy ... +203 +transform: jump 1:offset +204 +transform: copy ... +205 +transform: jump 0:offset +206 +207 :(scenario break_if) +208 def main [ +209 1:num <- copy 0 +210 2:num <- copy 0 +211 { +212 break-if 2:num +213 3:num <- copy 0 +214 } +215 { +216 break +217 } +218 ] +219 +transform: --- transform braces for recipe main 220 +transform: copy ... -221 +transform: jump-if 2, 1:offset -222 +transform: copy ... -223 +transform: jump 0:offset -224 -225 :(scenario break_nested) -226 def main [ -227 1:num <- copy 0 -228 { -229 2:num <- copy 0 -230 break -231 { -232 3:num <- copy 0 -233 } -234 4:num <- copy 0 -235 } -236 ] -237 +transform: jump 4:offset -238 -239 :(scenario break_nested_degenerate) -240 def main [ -241 1:num <- copy 0 -242 { -243 2:num <- copy 0 -244 break -245 { -246 } -247 4:num <- copy 0 -248 } -249 ] -250 +transform: jump 3:offset -251 -252 :(scenario break_nested_degenerate_2) -253 def main [ -254 1:num <- copy 0 -255 { -256 2:num <- copy 0 -257 break -258 { -259 } -260 } -261 ] -262 +transform: jump 2:offset -263 -264 :(scenario break_label) -265 % Hide_errors = true; -266 def main [ -267 1:num <- copy 0 -268 { -269 break +foo:offset -270 } -271 ] -272 +transform: jump +foo:offset -273 -274 :(scenario break_unless) -275 def main [ -276 1:num <- copy 0 -277 2:num <- copy 0 -278 { -279 break-unless 2:num -280 3:num <- copy 0 -281 } -282 ] -283 +transform: --- transform braces for recipe main -284 +transform: copy ... +221 +transform: copy ... +222 +transform: jump-if 2, 1:offset +223 +transform: copy ... +224 +transform: jump 0:offset +225 +226 :(scenario break_nested) +227 def main [ +228 1:num <- copy 0 +229 { +230 2:num <- copy 0 +231 break +232 { +233 3:num <- copy 0 +234 } +235 4:num <- copy 0 +236 } +237 ] +238 +transform: jump 4:offset +239 +240 :(scenario break_nested_degenerate) +241 def main [ +242 1:num <- copy 0 +243 { +244 2:num <- copy 0 +245 break +246 { +247 } +248 4:num <- copy 0 +249 } +250 ] +251 +transform: jump 3:offset +252 +253 :(scenario break_nested_degenerate_2) +254 def main [ +255 1:num <- copy 0 +256 { +257 2:num <- copy 0 +258 break +259 { +260 } +261 } +262 ] +263 +transform: jump 2:offset +264 +265 :(scenario break_label) +266 % Hide_errors = true; +267 def main [ +268 1:num <- copy 0 +269 { +270 break +foo:offset +271 } +272 ] +273 +transform: jump +foo:offset +274 +275 :(scenario break_unless) +276 def main [ +277 1:num <- copy 0 +278 2:num <- copy 0 +279 { +280 break-unless 2:num +281 3:num <- copy 0 +282 } +283 ] +284 +transform: --- transform braces for recipe main 285 +transform: copy ... -286 +transform: jump-unless 2, 1:offset -287 +transform: copy ... -288 -289 :(scenario loop_unless) -290 def main [ -291 1:num <- copy 0 -292 2:num <- copy 0 -293 { -294 loop-unless 2:num -295 3:num <- copy 0 -296 } -297 ] -298 +transform: --- transform braces for recipe main -299 +transform: copy ... +286 +transform: copy ... +287 +transform: jump-unless 2, 1:offset +288 +transform: copy ... +289 +290 :(scenario loop_unless) +291 def main [ +292 1:num <- copy 0 +293 2:num <- copy 0 +294 { +295 loop-unless 2:num +296 3:num <- copy 0 +297 } +298 ] +299 +transform: --- transform braces for recipe main 300 +transform: copy ... -301 +transform: jump-unless 2, -1:offset -302 +transform: copy ... -303 -304 :(scenario loop_nested) -305 def main [ -306 1:num <- copy 0 -307 { -308 2:num <- copy 0 -309 { -310 3:num <- copy 0 -311 } -312 loop-if 4:bool -313 5:num <- copy 0 -314 } -315 ] -316 +transform: --- transform braces for recipe main -317 +transform: jump-if 4, -5:offset -318 -319 :(scenario loop_label) -320 def main [ -321 1:num <- copy 0 -322 +foo -323 2:num <- copy 0 -324 ] -325 +transform: --- transform braces for recipe main -326 +transform: copy ... +301 +transform: copy ... +302 +transform: jump-unless 2, -1:offset +303 +transform: copy ... +304 +305 :(scenario loop_nested) +306 def main [ +307 1:num <- copy 0 +308 { +309 2:num <- copy 0 +310 { +311 3:num <- copy 0 +312 } +313 loop-if 4:bool +314 5:num <- copy 0 +315 } +316 ] +317 +transform: --- transform braces for recipe main +318 +transform: jump-if 4, -5:offset +319 +320 :(scenario loop_label) +321 def main [ +322 1:num <- copy 0 +323 +foo +324 2:num <- copy 0 +325 ] +326 +transform: --- transform braces for recipe main 327 +transform: copy ... -328 -329 //: test how things actually run -330 :(scenarios run) -331 :(scenario brace_conversion_and_run) -332 def test-factorial [ -333 1:num <- copy 5 -334 2:num <- copy 1 -335 { -336 3:bool <- equal 1:num, 1 -337 break-if 3:bool -338 # $print 1:num -339 2:num <- multiply 2:num, 1:num -340 1:num <- subtract 1:num, 1 -341 loop -342 } -343 4:num <- copy 2:num # trigger a read -344 ] -345 +mem: location 2 is 120 -346 -347 :(scenario break_outside_braces_fails) -348 % Hide_errors = true; -349 def main [ -350 break -351 ] -352 +error: 'break' needs a '{' before -353 -354 :(scenario break_conditional_without_ingredient_fails) -355 % Hide_errors = true; -356 def main [ -357 { -358 break-if -359 } -360 ] -361 +error: 'break-if' expects 1 or 2 ingredients, but got none -362 -363 //: Using break we can now implement conditional returns. -364 -365 :(scenario return_if) -366 def main [ -367 1:num <- test1 -368 ] -369 def test1 [ -370 return-if 0, 34 -371 return 35 -372 ] -373 +mem: storing 35 in location 1 -374 -375 :(scenario return_if_2) -376 def main [ -377 1:num <- test1 -378 ] -379 def test1 [ -380 return-if 1, 34 -381 return 35 -382 ] -383 +mem: storing 34 in location 1 -384 -385 :(before "End Rewrite Instruction(curr, recipe result)") -386 // rewrite `return-if a, b, c, ...` to -387 // ``` -388 // { -389 // break-unless a -390 // return b, c, ... -391 // } -392 // ``` -393 if (curr.name == "return-if" || curr.name == "reply-if") { -394 if (curr.products.empty()) { -395 emit_return_block(result, "break-unless", curr); -396 curr.clear(); -397 } -398 else { -399 raise << "'" << curr.name << "' never yields any products\n" << end(); -400 } -401 } -402 // rewrite `return-unless a, b, c, ...` to -403 // ``` -404 // { -405 // break-if a -406 // return b, c, ... -407 // } -408 // ``` -409 if (curr.name == "return-unless" || curr.name == "reply-unless") { -410 if (curr.products.empty()) { -411 emit_return_block(result, "break-if", curr); -412 curr.clear(); -413 } -414 else { -415 raise << "'" << curr.name << "' never yields any products\n" << end(); -416 } -417 } -418 -419 :(code) -420 void emit_return_block(recipe& out, const string& break_command, const instruction& inst) { -421 const vector<reagent>& ingredients = inst.ingredients; -422 reagent/*copy*/ condition = ingredients.at(0); -423 vector<reagent> return_ingredients; -424 copy(++ingredients.begin(), ingredients.end(), inserter(return_ingredients, return_ingredients.end())); -425 -426 // { -427 instruction open_label; open_label.is_label=true; open_label.label = "{"; -428 out.steps.push_back(open_label); -429 -430 // <break command> <condition> -431 instruction break_inst; -432 break_inst.operation = get(Recipe_ordinal, break_command); -433 break_inst.name = break_inst.old_name = break_command; -434 break_inst.ingredients.push_back(condition); -435 out.steps.push_back(break_inst); -436 -437 // return <return ingredients> -438 instruction return_inst; -439 return_inst.operation = get(Recipe_ordinal, "return"); -440 return_inst.name = "return"; -441 return_inst.ingredients.swap(return_ingredients); -442 return_inst.original_string = inst.original_string; -443 out.steps.push_back(return_inst); -444 -445 // } -446 instruction close_label; close_label.is_label=true; close_label.label = "}"; -447 out.steps.push_back(close_label); -448 } -449 -450 //: Make sure these pseudo recipes get consistent numbers in all tests, even -451 //: though they aren't implemented. Allows greater flexibility in ordering -452 //: transforms. -453 -454 :(before "End Primitive Recipe Declarations") -455 BREAK, -456 BREAK_IF, -457 BREAK_UNLESS, -458 LOOP, -459 LOOP_IF, -460 LOOP_UNLESS, -461 :(before "End Primitive Recipe Numbers") -462 put(Recipe_ordinal, "break", BREAK); -463 put(Recipe_ordinal, "break-if", BREAK_IF); -464 put(Recipe_ordinal, "break-unless", BREAK_UNLESS); -465 put(Recipe_ordinal, "loop", LOOP); -466 put(Recipe_ordinal, "loop-if", LOOP_IF); -467 put(Recipe_ordinal, "loop-unless", LOOP_UNLESS); -468 :(before "End Primitive Recipe Checks") -469 case BREAK: break; -470 case BREAK_IF: break; -471 case BREAK_UNLESS: break; -472 case LOOP: break; -473 case LOOP_IF: break; -474 case LOOP_UNLESS: break; +328 +transform: copy ... +329 +330 //: test how things actually run +331 :(scenarios run) +332 :(scenario brace_conversion_and_run) +333 def test-factorial [ +334 1:num <- copy 5 +335 2:num <- copy 1 +336 { +337 3:bool <- equal 1:num, 1 +338 break-if 3:bool +339 # $print 1:num +340 2:num <- multiply 2:num, 1:num +341 1:num <- subtract 1:num, 1 +342 loop +343 } +344 4:num <- copy 2:num # trigger a read +345 ] +346 +mem: location 2 is 120 +347 +348 :(scenario break_outside_braces_fails) +349 % Hide_errors = true; +350 def main [ +351 break +352 ] +353 +error: 'break' needs a '{' before +354 +355 :(scenario break_conditional_without_ingredient_fails) +356 % Hide_errors = true; +357 def main [ +358 { +359 break-if +360 } +361 ] +362 +error: 'break-if' expects 1 or 2 ingredients, but got none +363 +364 //: Using break we can now implement conditional returns. +365 +366 :(scenario return_if) +367 def main [ +368 1:num <- test1 +369 ] +370 def test1 [ +371 return-if 0, 34 +372 return 35 +373 ] +374 +mem: storing 35 in location 1 +375 +376 :(scenario return_if_2) +377 def main [ +378 1:num <- test1 +379 ] +380 def test1 [ +381 return-if 1, 34 +382 return 35 +383 ] +384 +mem: storing 34 in location 1 +385 +386 :(before "End Rewrite Instruction(curr, recipe result)") +387 // rewrite `return-if a, b, c, ...` to +388 // ``` +389 // { +390 // break-unless a +391 // return b, c, ... +392 // } +393 // ``` +394 if (curr.name == "return-if" || curr.name == "reply-if") { +395 if (curr.products.empty()) { +396 emit_return_block(result, "break-unless", curr); +397 curr.clear(); +398 } +399 else { +400 raise << "'" << curr.name << "' never yields any products\n" << end(); +401 } +402 } +403 // rewrite `return-unless a, b, c, ...` to +404 // ``` +405 // { +406 // break-if a +407 // return b, c, ... +408 // } +409 // ``` +410 if (curr.name == "return-unless" || curr.name == "reply-unless") { +411 if (curr.products.empty()) { +412 emit_return_block(result, "break-if", curr); +413 curr.clear(); +414 } +415 else { +416 raise << "'" << curr.name << "' never yields any products\n" << end(); +417 } +418 } +419 +420 :(code) +421 void emit_return_block(recipe& out, const string& break_command, const instruction& inst) { +422 const vector<reagent>& ingredients = inst.ingredients; +423 reagent/*copy*/ condition = ingredients.at(0); +424 vector<reagent> return_ingredients; +425 copy(++ingredients.begin(), ingredients.end(), inserter(return_ingredients, return_ingredients.end())); +426 +427 // { +428 instruction open_label; open_label.is_label=true; open_label.label = "{"; +429 out.steps.push_back(open_label); +430 +431 // <break command> <condition> +432 instruction break_inst; +433 break_inst.operation = get(Recipe_ordinal, break_command); +434 break_inst.name = break_command; +435 break_inst.ingredients.push_back(condition); +436 out.steps.push_back(break_inst); +437 +438 // return <return ingredients> +439 instruction return_inst; +440 return_inst.operation = get(Recipe_ordinal, "return"); +441 return_inst.name = "return"; +442 return_inst.ingredients.swap(return_ingredients); +443 return_inst.original_string = inst.original_string; +444 out.steps.push_back(return_inst); +445 +446 // } +447 instruction close_label; close_label.is_label=true; close_label.label = "}"; +448 out.steps.push_back(close_label); +449 } +450 +451 //: Make sure these pseudo recipes get consistent numbers in all tests, even +452 //: though they aren't implemented. Allows greater flexibility in ordering +453 //: transforms. +454 +455 :(before "End Primitive Recipe Declarations") +456 BREAK, +457 BREAK_IF, +458 BREAK_UNLESS, +459 LOOP, +460 LOOP_IF, +461 LOOP_UNLESS, +462 :(before "End Primitive Recipe Numbers") +463 put(Recipe_ordinal, "break", BREAK); +464 put(Recipe_ordinal, "break-if", BREAK_IF); +465 put(Recipe_ordinal, "break-unless", BREAK_UNLESS); +466 put(Recipe_ordinal, "loop", LOOP); +467 put(Recipe_ordinal, "loop-if", LOOP_IF); +468 put(Recipe_ordinal, "loop-unless", LOOP_UNLESS); +469 :(before "End Primitive Recipe Checks") +470 case BREAK: break; +471 case BREAK_IF: break; +472 case BREAK_UNLESS: break; +473 case LOOP: break; +474 case LOOP_IF: break; +475 case LOOP_UNLESS: break; diff --git a/html/041jump_target.cc.html b/html/041jump_target.cc.html index aa5ce74d..af84acf4 100644 --- a/html/041jump_target.cc.html +++ b/html/041jump_target.cc.html @@ -73,7 +73,7 @@ if ('onhashchange' in window) { 11 bool is_jump_target(const string& label) { 12 if (label == "{" || label == "}") return false; 13 // End is_jump_target Special-cases - 14 return is_label_word(label); + 14 return is_label_word(label); 15 } 16 17 :(scenario jump_to_label) @@ -138,22 +138,22 @@ if ('onhashchange' in window) { 76 void replace_offset(reagent& x, /*const*/ map<string, int>& offset, const int current_offset, const recipe_ordinal r) { 77 if (!is_literal(x)) { 78 raise << maybe(get(Recipe, r).name) << "jump target must be offset or label but is '" << x.original_string << "'\n" << end(); - 79 x.set_value(0); // no jump by default + 79 x.set_value(0); // no jump by default 80 return; 81 } 82 if (x.initialized) return; 83 if (is_integer(x.name)) return; // non-labels will be handled like other number operands 84 if (!is_jump_target(x.name)) { 85 raise << maybe(get(Recipe, r).name) << "can't jump to label '" << x.name << "'\n" << end(); - 86 x.set_value(0); // no jump by default + 86 x.set_value(0); // no jump by default 87 return; 88 } 89 if (!contains_key(offset, x.name)) { 90 raise << maybe(get(Recipe, r).name) << "can't find label '" << x.name << "'\n" << end(); - 91 x.set_value(0); // no jump by default + 91 x.set_value(0); // no jump by default 92 return; 93 } - 94 x.set_value(get(offset, x.name) - current_offset); + 94 x.set_value(get(offset, x.name) - current_offset); 95 } 96 97 :(scenario break_to_label) diff --git a/html/042name.cc.html b/html/042name.cc.html index 915412f1..562616f8 100644 --- a/html/042name.cc.html +++ b/html/042name.cc.html @@ -124,7 +124,7 @@ if ('onhashchange' in window) { 61 } 62 int v = lookup_name(ingredient, r); 63 if (v >= 0) { - 64 ingredient.set_value(v); + 64 ingredient.set_value(v); 65 // Done Placing Ingredient(ingredient, inst, caller) 66 } 67 else { @@ -146,7 +146,7 @@ if ('onhashchange' in window) { 83 } 84 int v = lookup_name(product, r); 85 if (v >= 0) { - 86 product.set_value(v); + 86 product.set_value(v); 87 // Done Placing Product(product, inst, caller) 88 } 89 else { @@ -180,7 +180,7 @@ if ('onhashchange' in window) { 117 return Name[default_recipe][r.name]; 118 } 119 -120 type_ordinal skip_addresses(type_tree* type) { +120 type_ordinal skip_addresses(type_tree* type) { 121 while (type && is_compound_type_starting_with(type, "address")) 122 type = type->right; 123 if (!type) return -1; // error handled elsewhere @@ -193,7 +193,7 @@ if ('onhashchange' in window) { 130 return base_type->left->value; 131 } 132 -133 int find_element_name(const type_ordinal t, const string& name, const string& recipe_name) { +133 int find_element_name(const type_ordinal t, const string& name, const string& recipe_name) { 134 const type_info& container = get(Type, t); 135 for (int i = 0; i < SIZE(container.elements); ++i) 136 if (container.elements.at(i).name == name) return i; @@ -322,9 +322,9 @@ if ('onhashchange' in window) { 259 break; // error raised elsewhere 260 if (inst.ingredients.at(1).name.find_first_not_of("0123456789") != string::npos) { 261 // since first non-address in base type must be a container, we don't have to canonize -262 type_ordinal base_type = skip_addresses(inst.ingredients.at(0).type); +262 type_ordinal base_type = skip_addresses(inst.ingredients.at(0).type); 263 if (contains_key(Type, base_type)) { // otherwise we'll raise an error elsewhere -264 inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name, get(Recipe, r).name)); +264 inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name, get(Recipe, r).name)); 265 trace(9993, "name") << "element " << inst.ingredients.at(1).name << " of type " << get(Type, base_type).name << " is at offset " << no_scientific(inst.ingredients.at(1).value) << end(); 266 } 267 } @@ -372,9 +372,9 @@ if ('onhashchange' in window) { 309 assert(is_literal(inst.ingredients.at(1))); 310 if (inst.ingredients.at(1).name.find_first_not_of("0123456789") != string::npos) { 311 // since first non-address in base type must be an exclusive container, we don't have to canonize -312 type_ordinal base_type = skip_addresses(inst.ingredients.at(0).type); +312 type_ordinal base_type = skip_addresses(inst.ingredients.at(0).type); 313 if (contains_key(Type, base_type)) { // otherwise we'll raise an error elsewhere -314 inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name, get(Recipe, r).name)); +314 inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name, get(Recipe, r).name)); 315 trace(9993, "name") << "variant " << inst.ingredients.at(1).name << " of type " << get(Type, base_type).name << " has tag " << no_scientific(inst.ingredients.at(1).value) << end(); 316 } 317 } diff --git a/html/043space.cc.html b/html/043space.cc.html index efc582f4..60bc5518 100644 --- a/html/043space.cc.html +++ b/html/043space.cc.html @@ -127,7 +127,7 @@ if ('onhashchange' in window) { 63 if (x.name == "default-space") return; 64 if (!x.initialized) 65 raise << to_original_string(current_instruction()) << ": reagent not initialized: '" << x.original_string << "'\n" << end(); - 66 x.set_value(address(x.value, space_base(x))); + 66 x.set_value(address(x.value, space_base(x))); 67 x.properties.push_back(pair<string, string_tree*>("raw", NULL)); 68 assert(is_raw(x)); 69 } @@ -241,7 +241,7 @@ if ('onhashchange' in window) { 177 // `default-space:space <- new location:type, number-of-locals:literal` 178 // where N is Name[recipe][""] 179 if (curr.name == "new-default-space") { -180 rewrite_default_space_instruction(curr); +180 rewrite_default_space_instruction(curr); 181 } 182 :(after "Begin Preprocess read_memory(x)") 183 if (x.name == "number-of-locals") { @@ -283,7 +283,7 @@ if ('onhashchange' in window) { 219 220 :(before "End Rewrite Instruction(curr, recipe result)") 221 if (curr.name == "local-scope") { -222 rewrite_default_space_instruction(curr); +222 rewrite_default_space_instruction(curr); 223 } 224 225 //: todo: do this in a transform, rather than magically in the 'return' instruction @@ -309,7 +309,7 @@ if ('onhashchange' in window) { 245 // local variables only 246 if (has_property(product, "lookup")) continue; 247 if (has_property(product, "raw")) continue; // tests often want to check such locations after they run -248 if (escaping(product)) continue; +248 if (escaping(product)) continue; 249 // End Checks For Reclaiming Locals 250 trace(9999, "mem") << "clearing " << product.original_string << end(); 251 zeros.resize(size_of(product)); @@ -322,180 +322,189 @@ if ('onhashchange' in window) { 258 /*refcount*/1 + /*array length*/1 + /*number-of-locals*/Name[r][""]); 259 } 260 -261 :(code) -262 // is this reagent one of the values returned by the current (return) instruction? -263 // is the corresponding ingredient saved in the caller? -264 bool escaping(const reagent& r) { -265 assert(Current_routine); // run-time only -266 // nothing escapes when you fall through past end of recipe -267 if (current_step_index() >= SIZE(Current_routine->steps())) return false; -268 for (long long i = 0; i < SIZE(current_instruction().ingredients); ++i) { -269 if (r == current_instruction().ingredients.at(i)) { -270 if (caller_uses_product(i)) -271 return true; -272 } -273 } -274 return false; -275 } -276 -277 //: since we don't decrement refcounts for escaping values above, make sure we -278 //: don't increment them when the caller saves them either -279 -280 :(after "Write Products of Instruction") -281 Update_refcounts_in_write_memory = should_update_refcounts_in_write_memory(); -282 :(before "End Write Products of Instruction") -283 Update_refcounts_in_write_memory = true; -284 :(code) -285 bool should_update_refcounts_in_write_memory() { -286 const instruction& inst = current_instruction(); -287 // End should_update_refcounts_in_write_memory Special-cases For Primitives -288 if (inst.operation < MAX_PRIMITIVE_RECIPES) return true; -289 if (!contains_key(Recipe, inst.operation)) return true; -290 const recipe& callee = get(Recipe, inst.operation); -291 if (callee.steps.empty()) return true; -292 return callee.steps.at(0).old_name != "local-scope"; // callees that call local-scope are already dealt with before return -293 } -294 -295 bool caller_uses_product(int product_index) { -296 assert(Current_routine); // run-time only -297 assert(!Current_routine->calls.empty()); -298 if (Current_routine->calls.size() == 1) return false; -299 const call& caller = *++Current_routine->calls.begin(); -300 const instruction& caller_inst = to_instruction(caller); -301 if (product_index >= SIZE(caller_inst.products)) return false; -302 return !is_dummy(caller_inst.products.at(product_index)); -303 } -304 -305 void rewrite_default_space_instruction(instruction& curr) { -306 if (!curr.ingredients.empty()) -307 raise << to_original_string(curr) << " can't take any ingredients\n" << end(); -308 curr.name = "new"; -309 curr.ingredients.push_back(reagent("location:type")); -310 curr.ingredients.push_back(reagent("number-of-locals:literal")); -311 if (!curr.products.empty()) -312 raise << "new-default-space can't take any results\n" << end(); -313 curr.products.push_back(reagent("default-space:space")); -314 } -315 -316 :(scenario local_scope_frees_up_addresses_inside_containers) -317 container foo [ -318 x:num -319 y:&:num -320 ] -321 def main [ -322 local-scope -323 x:&:num <- new number:type -324 y:foo <- merge 34, x:&:num -325 # x and y are both cleared when main returns -326 ] -327 +mem: clearing x:&:num -328 +mem: decrementing refcount of 1006: 2 -> 1 -329 +mem: clearing y:foo -330 +mem: decrementing refcount of 1006: 1 -> 0 -331 +mem: automatically abandoning 1006 -332 -333 :(scenario local_scope_returns_addresses_inside_containers) -334 container foo [ -335 x:num -336 y:&:num -337 ] -338 def f [ -339 local-scope -340 x:&:num <- new number:type -341 *x:&:num <- copy 12 -342 y:foo <- merge 34, x:&:num -343 # since y is 'escaping' f, it should not be cleared -344 return y:foo -345 ] -346 def main [ -347 1:foo <- f -348 3:num <- get 1:foo, x:offset -349 4:&:num <- get 1:foo, y:offset -350 5:num <- copy *4:&:num -351 1:foo <- put 1:foo, y:offset, 0 -352 4:&:num <- copy 0 -353 ] -354 +mem: storing 34 in location 1 -355 +mem: storing 1006 in location 2 -356 +mem: storing 34 in location 3 -357 # refcount of 1:foo shouldn't include any stray ones from f -358 +run: {4: ("address" "number")} <- get {1: "foo"}, {y: "offset"} -359 +mem: incrementing refcount of 1006: 1 -> 2 -360 # 1:foo wasn't abandoned/cleared -361 +run: {5: "number"} <- copy {4: ("address" "number"), "lookup": ()} -362 +mem: storing 12 in location 5 -363 +run: {1: "foo"} <- put {1: "foo"}, {y: "offset"}, {0: "literal"} -364 +mem: decrementing refcount of 1006: 2 -> 1 -365 +run: {4: ("address" "number")} <- copy {0: "literal"} -366 +mem: decrementing refcount of 1006: 1 -> 0 -367 +mem: automatically abandoning 1006 -368 -369 :(scenario local_scope_claims_return_values_when_not_saved) -370 def f [ -371 local-scope -372 x:&:num <- new number:type -373 return x:&:num -374 ] -375 def main [ -376 f # doesn't save result -377 ] -378 # x reclaimed -379 +mem: automatically abandoning 1004 -380 # f's local scope reclaimed -381 +mem: automatically abandoning 1000 -382 -383 //:: all recipes must set default-space one way or another -384 -385 :(before "End Globals") -386 bool Hide_missing_default_space_errors = true; -387 :(before "End Checks") -388 Transform.push_back(check_default_space); // idempotent -389 :(code) -390 void check_default_space(const recipe_ordinal r) { -391 if (Hide_missing_default_space_errors) return; // skip previous core tests; this is only for Mu code -392 const recipe& caller = get(Recipe, r); -393 // End check_default_space Special-cases -394 // assume recipes with only numeric addresses know what they're doing (usually tests) -395 if (!contains_non_special_name(r)) return; -396 trace(9991, "transform") << "--- check that recipe " << caller.name << " sets default-space" << end(); -397 if (caller.steps.empty()) return; -398 if (caller.steps.at(0).products.empty() -399 || caller.steps.at(0).products.at(0).name != "default-space") { -400 raise << caller.name << " does not seem to start with default-space or local-scope\n" << end(); -401 } -402 } -403 :(after "Load Mu Prelude") -404 Hide_missing_default_space_errors = false; -405 :(after "Test Runs") -406 Hide_missing_default_space_errors = true; -407 :(after "Running Main") -408 Hide_missing_default_space_errors = false; -409 -410 :(code) -411 bool contains_non_special_name(const recipe_ordinal r) { -412 for (map<string, int>::iterator p = Name[r].begin(); p != Name[r].end(); ++p) { -413 if (p->first.empty()) continue; -414 if (p->first.find("stash_") == 0) continue; // generated by rewrite_stashes_to_text (cross-layer) -415 if (!is_special_name(p->first)) -416 return true; -417 } -418 return false; -419 } -420 -421 // reagent comparison -- only between reagents in a single recipe -422 bool operator==(const reagent& a, const reagent& b) { -423 if (a.name != b.name) return false; -424 if (property(a, "space") != property(b, "space")) return false; -425 return true; -426 } -427 -428 bool operator<(const reagent& a, const reagent& b) { -429 int aspace = 0, bspace = 0; -430 if (has_property(a, "space")) aspace = to_integer(property(a, "space")->value); -431 if (has_property(b, "space")) bspace = to_integer(property(b, "space")->value); -432 if (aspace != bspace) return aspace < bspace; -433 return a.name < b.name; -434 } +261 //: Reclaiming local variables above requires remembering what name an +262 //: instruction had before any rewrites or transforms. +263 :(before "End instruction Fields") +264 string old_name; +265 :(before "End instruction Clear") +266 old_name.clear(); +267 :(before "End next_instruction(curr)") +268 curr->old_name = curr->name; // before rewrite rules modify it +269 +270 :(code) +271 // is this reagent one of the values returned by the current (return) instruction? +272 // is the corresponding ingredient saved in the caller? +273 bool escaping(const reagent& r) { +274 assert(Current_routine); // run-time only +275 // nothing escapes when you fall through past end of recipe +276 if (current_step_index() >= SIZE(Current_routine->steps())) return false; +277 for (long long i = 0; i < SIZE(current_instruction().ingredients); ++i) { +278 if (r == current_instruction().ingredients.at(i)) { +279 if (caller_uses_product(i)) +280 return true; +281 } +282 } +283 return false; +284 } +285 +286 //: since we don't decrement refcounts for escaping values above, make sure we +287 //: don't increment them when the caller saves them either +288 +289 :(after "Write Products of Instruction") +290 Update_refcounts_in_write_memory = should_update_refcounts_in_write_memory(); +291 :(before "End Write Products of Instruction") +292 Update_refcounts_in_write_memory = true; +293 :(code) +294 bool should_update_refcounts_in_write_memory() { +295 const instruction& inst = current_instruction(); +296 // End should_update_refcounts_in_write_memory Special-cases For Primitives +297 if (inst.operation < MAX_PRIMITIVE_RECIPES) return true; +298 if (!contains_key(Recipe, inst.operation)) return true; +299 const recipe& callee = get(Recipe, inst.operation); +300 if (callee.steps.empty()) return true; +301 return callee.steps.at(0).old_name != "local-scope"; // callees that call local-scope are already dealt with before return +302 } +303 +304 bool caller_uses_product(int product_index) { +305 assert(Current_routine); // run-time only +306 assert(!Current_routine->calls.empty()); +307 if (Current_routine->calls.size() == 1) return false; +308 const call& caller = *++Current_routine->calls.begin(); +309 const instruction& caller_inst = to_instruction(caller); +310 if (product_index >= SIZE(caller_inst.products)) return false; +311 return !is_dummy(caller_inst.products.at(product_index)); +312 } +313 +314 void rewrite_default_space_instruction(instruction& curr) { +315 if (!curr.ingredients.empty()) +316 raise << to_original_string(curr) << " can't take any ingredients\n" << end(); +317 curr.name = "new"; +318 curr.ingredients.push_back(reagent("location:type")); +319 curr.ingredients.push_back(reagent("number-of-locals:literal")); +320 if (!curr.products.empty()) +321 raise << "new-default-space can't take any results\n" << end(); +322 curr.products.push_back(reagent("default-space:space")); +323 } +324 +325 :(scenario local_scope_frees_up_addresses_inside_containers) +326 container foo [ +327 x:num +328 y:&:num +329 ] +330 def main [ +331 local-scope +332 x:&:num <- new number:type +333 y:foo <- merge 34, x:&:num +334 # x and y are both cleared when main returns +335 ] +336 +mem: clearing x:&:num +337 +mem: decrementing refcount of 1006: 2 -> 1 +338 +mem: clearing y:foo +339 +mem: decrementing refcount of 1006: 1 -> 0 +340 +mem: automatically abandoning 1006 +341 +342 :(scenario local_scope_returns_addresses_inside_containers) +343 container foo [ +344 x:num +345 y:&:num +346 ] +347 def f [ +348 local-scope +349 x:&:num <- new number:type +350 *x:&:num <- copy 12 +351 y:foo <- merge 34, x:&:num +352 # since y is 'escaping' f, it should not be cleared +353 return y:foo +354 ] +355 def main [ +356 1:foo <- f +357 3:num <- get 1:foo, x:offset +358 4:&:num <- get 1:foo, y:offset +359 5:num <- copy *4:&:num +360 1:foo <- put 1:foo, y:offset, 0 +361 4:&:num <- copy 0 +362 ] +363 +mem: storing 34 in location 1 +364 +mem: storing 1006 in location 2 +365 +mem: storing 34 in location 3 +366 # refcount of 1:foo shouldn't include any stray ones from f +367 +run: {4: ("address" "number")} <- get {1: "foo"}, {y: "offset"} +368 +mem: incrementing refcount of 1006: 1 -> 2 +369 # 1:foo wasn't abandoned/cleared +370 +run: {5: "number"} <- copy {4: ("address" "number"), "lookup": ()} +371 +mem: storing 12 in location 5 +372 +run: {1: "foo"} <- put {1: "foo"}, {y: "offset"}, {0: "literal"} +373 +mem: decrementing refcount of 1006: 2 -> 1 +374 +run: {4: ("address" "number")} <- copy {0: "literal"} +375 +mem: decrementing refcount of 1006: 1 -> 0 +376 +mem: automatically abandoning 1006 +377 +378 :(scenario local_scope_claims_return_values_when_not_saved) +379 def f [ +380 local-scope +381 x:&:num <- new number:type +382 return x:&:num +383 ] +384 def main [ +385 f # doesn't save result +386 ] +387 # x reclaimed +388 +mem: automatically abandoning 1004 +389 # f's local scope reclaimed +390 +mem: automatically abandoning 1000 +391 +392 //:: all recipes must set default-space one way or another +393 +394 :(before "End Globals") +395 bool Hide_missing_default_space_errors = true; +396 :(before "End Checks") +397 Transform.push_back(check_default_space); // idempotent +398 :(code) +399 void check_default_space(const recipe_ordinal r) { +400 if (Hide_missing_default_space_errors) return; // skip previous core tests; this is only for Mu code +401 const recipe& caller = get(Recipe, r); +402 // End check_default_space Special-cases +403 // assume recipes with only numeric addresses know what they're doing (usually tests) +404 if (!contains_non_special_name(r)) return; +405 trace(9991, "transform") << "--- check that recipe " << caller.name << " sets default-space" << end(); +406 if (caller.steps.empty()) return; +407 if (caller.steps.at(0).products.empty() +408 || caller.steps.at(0).products.at(0).name != "default-space") { +409 raise << caller.name << " does not seem to start with default-space or local-scope\n" << end(); +410 } +411 } +412 :(after "Load Mu Prelude") +413 Hide_missing_default_space_errors = false; +414 :(after "Test Runs") +415 Hide_missing_default_space_errors = true; +416 :(after "Running Main") +417 Hide_missing_default_space_errors = false; +418 +419 :(code) +420 bool contains_non_special_name(const recipe_ordinal r) { +421 for (map<string, int>::iterator p = Name[r].begin(); p != Name[r].end(); ++p) { +422 if (p->first.empty()) continue; +423 if (p->first.find("stash_") == 0) continue; // generated by rewrite_stashes_to_text (cross-layer) +424 if (!is_special_name(p->first)) +425 return true; +426 } +427 return false; +428 } +429 +430 // reagent comparison -- only between reagents in a single recipe +431 bool operator==(const reagent& a, const reagent& b) { +432 if (a.name != b.name) return false; +433 if (property(a, "space") != property(b, "space")) return false; +434 return true; +435 } +436 +437 bool operator<(const reagent& a, const reagent& b) { +438 int aspace = 0, bspace = 0; +439 if (has_property(a, "space")) aspace = to_integer(property(a, "space")->value); +440 if (has_property(b, "space")) bspace = to_integer(property(b, "space")->value); +441 if (aspace != bspace) return aspace < bspace; +442 return a.name < b.name; +443 } diff --git a/html/050scenario.cc.html b/html/050scenario.cc.html index e96df0bc..94cfcdae 100644 --- a/html/050scenario.cc.html +++ b/html/050scenario.cc.html @@ -155,7 +155,7 @@ if ('onhashchange' in window) { 90 :(code) 91 scenario parse_scenario(istream& in) { 92 scenario result; - 93 result.name = next_word(in); + 93 result.name = next_word(in); 94 if (contains_key(Scenario_names, result.name)) 95 raise << "duplicate scenario name: '" << result.name << "'\n" << end(); 96 Scenario_names.insert(result.name); @@ -164,7 +164,7 @@ if ('onhashchange' in window) { 99 raise << "incomplete scenario at end of file\n" << end(); 100 return result; 101 } -102 skip_whitespace_and_comments(in); +102 skip_whitespace_and_comments(in); 103 if (in.peek() != '[') { 104 raise << "Expected '[' after scenario '" << result.name << "'\n" << end(); 105 exit(0); @@ -462,9 +462,9 @@ if ('onhashchange' in window) { 397 in >> std::noskipws; 398 set<int> locations_checked; 399 while (true) { -400 skip_whitespace_and_comments(in); +400 skip_whitespace_and_comments(in); 401 if (!has_data(in)) break; -402 string lhs = next_word(in); +402 string lhs = next_word(in); 403 if (lhs.empty()) { 404 assert(!has_data(in)); 405 raise << "incomplete 'memory-should-contain' block at end of file (0)\n" << end(); @@ -475,10 +475,10 @@ if ('onhashchange' in window) { 410 continue; 411 } 412 int address = to_integer(lhs); -413 skip_whitespace_and_comments(in); +413 skip_whitespace_and_comments(in); 414 string _assign; in >> _assign; assert(_assign == "<-"); -415 skip_whitespace_and_comments(in); -416 string rhs = next_word(in); +415 skip_whitespace_and_comments(in); +416 string rhs = next_word(in); 417 if (rhs.empty()) { 418 assert(!has_data(in)); 419 raise << "incomplete 'memory-should-contain' block at end of file (1)\n" << end(); @@ -517,17 +517,17 @@ if ('onhashchange' in window) { 452 void check_type(const string& lhs, istream& in) { 453 reagent x(lhs); 454 if (is_mu_array(x.type) && is_mu_character(array_element(x.type))) { -455 x.set_value(to_integer(x.name)); -456 skip_whitespace_and_comments(in); -457 string _assign = next_word(in); +455 x.set_value(to_integer(x.name)); +456 skip_whitespace_and_comments(in); +457 string _assign = next_word(in); 458 if (_assign.empty()) { 459 assert(!has_data(in)); 460 raise << "incomplete 'memory-should-contain' block at end of file (2)\n" << end(); 461 return; 462 } 463 assert(_assign == "<-"); -464 skip_whitespace_and_comments(in); -465 string literal = next_word(in); +464 skip_whitespace_and_comments(in); +465 string literal = next_word(in); 466 if (literal.empty()) { 467 assert(!has_data(in)); 468 raise << "incomplete 'memory-should-contain' block at end of file (3)\n" << end(); @@ -905,8 +905,8 @@ if ('onhashchange' in window) { 840 Scenario_names.clear(); 841 istringstream in(form); 842 in >> std::noskipws; -843 skip_whitespace_and_comments(in); -844 string _scenario = next_word(in); +843 skip_whitespace_and_comments(in); +844 string _scenario = next_word(in); 845 if (_scenario.empty()) { 846 assert(!has_data(in)); 847 raise << "no scenario in string passed into run_mu_scenario()\n" << end(); diff --git a/html/052tangle.cc.html b/html/052tangle.cc.html index add6b6e7..4f0b26a4 100644 --- a/html/052tangle.cc.html +++ b/html/052tangle.cc.html @@ -103,7 +103,7 @@ if ('onhashchange' in window) { 41 42 :(before "End Command Handlers") 43 else if (command == "before") { - 44 string label = next_word(in); + 44 string label = next_word(in); 45 if (label.empty()) { 46 assert(!has_data(in)); 47 raise << "incomplete 'before' block at end of file\n" << end(); @@ -118,7 +118,7 @@ if ('onhashchange' in window) { 56 // End before Command Handler 57 } 58 else if (command == "after") { - 59 string label = next_word(in); + 59 string label = next_word(in); 60 if (label.empty()) { 61 assert(!has_data(in)); 62 raise << "incomplete 'after' block at end of file\n" << end(); diff --git a/html/053recipe_header.cc.html b/html/053recipe_header.cc.html index da12865f..db023d6e 100644 --- a/html/053recipe_header.cc.html +++ b/html/053recipe_header.cc.html @@ -96,7 +96,7 @@ if ('onhashchange' in window) { 32 void load_recipe_header(istream& in, recipe& result) { 33 result.has_header = true; 34 while (has_data(in) && in.peek() != '[' && in.peek() != '\n') { - 35 string s = next_word(in); + 35 string s = next_word(in); 36 if (s.empty()) { 37 assert(!has_data(in)); 38 raise << "incomplete recipe header at end of file (0)\n" << end(); @@ -110,7 +110,7 @@ if ('onhashchange' in window) { 46 skip_whitespace_but_not_newline(in); 47 } 48 while (has_data(in) && in.peek() != '[' && in.peek() != '\n') { - 49 string s = next_word(in); + 49 string s = next_word(in); 50 if (s.empty()) { 51 assert(!has_data(in)); 52 raise << "incomplete recipe header at end of file (1)\n" << end(); @@ -355,7 +355,7 @@ if ('onhashchange' in window) { 291 trace(9991, "transform") << "--- type-check calls inside recipe " << caller.name << end(); 292 for (int i = 0; i < SIZE(caller.steps); ++i) { 293 const instruction& inst = caller.steps.at(i); -294 if (inst.operation < MAX_PRIMITIVE_RECIPES) continue; +294 if (inst.operation < MAX_PRIMITIVE_RECIPES) continue; 295 const recipe& callee = get(Recipe, inst.operation); 296 if (!callee.has_header) continue; 297 for (long int i = 0; i < min(SIZE(inst.ingredients), SIZE(callee.ingredients)); ++i) { diff --git a/html/054static_dispatch.cc.html b/html/054static_dispatch.cc.html index b31daf29..3248f049 100644 --- a/html/054static_dispatch.cc.html +++ b/html/054static_dispatch.cc.html @@ -260,7 +260,7 @@ if ('onhashchange' in window) { 197 if (!candidates.empty()) return best_variant(inst, candidates).name; 198 199 // error messages -200 if (get(Recipe_ordinal, inst.name) >= MAX_PRIMITIVE_RECIPES) { // we currently don't check types for primitive variants +200 if (get(Recipe_ordinal, inst.name) >= MAX_PRIMITIVE_RECIPES) { // we currently don't check types for primitive variants 201 raise << maybe(caller_recipe.name) << "failed to find a matching call for '" << inst.original_string << "'\n" << end(); 202 for (list<call>::iterator p = /*skip*/++Resolve_stack.begin(); p != Resolve_stack.end(); ++p) { 203 const recipe& specializer_recipe = get(Recipe, p->running_recipe); diff --git a/html/055shape_shifting_container.cc.html b/html/055shape_shifting_container.cc.html index fb41b269..d0d61c1d 100644 --- a/html/055shape_shifting_container.cc.html +++ b/html/055shape_shifting_container.cc.html @@ -620,11 +620,11 @@ if ('onhashchange' in window) { 556 :(before "End compute_container_sizes Non-atom Special-cases") 557 const type_tree* root = get_base_type(type); 558 type_info& info = get(Type, root->value); -559 if (info.kind == CONTAINER) { +559 if (info.kind == CONTAINER) { 560 compute_container_sizes(info, type, pending_metadata, location_for_error_messages); 561 return; 562 } -563 if (info.kind == EXCLUSIVE_CONTAINER) { +563 if (info.kind == EXCLUSIVE_CONTAINER) { 564 compute_exclusive_container_sizes(info, type, pending_metadata, location_for_error_messages); 565 return; 566 } @@ -680,12 +680,12 @@ if ('onhashchange' in window) { 616 :(before "End compute_container_address_offsets Non-atom Special-cases") 617 const type_tree* root = get_base_type(type); 618 type_info& info = get(Type, root->value); -619 if (info.kind == CONTAINER) { +619 if (info.kind == CONTAINER) { 620 compute_container_address_offsets(info, type, location_for_error_messages); 621 return; 622 } -623 if (info.kind == EXCLUSIVE_CONTAINER) { -624 compute_exclusive_container_address_offsets(info, type, location_for_error_messages); +623 if (info.kind == EXCLUSIVE_CONTAINER) { +624 compute_exclusive_container_address_offsets(info, type, location_for_error_messages); 625 return; 626 } 627 diff --git a/html/056shape_shifting_recipe.cc.html b/html/056shape_shifting_recipe.cc.html index 2c19f2bb..3896e1e9 100644 --- a/html/056shape_shifting_recipe.cc.html +++ b/html/056shape_shifting_recipe.cc.html @@ -147,7 +147,7 @@ if ('onhashchange' in window) { 82 //: recipes can be called 83 84 :(before "End Instruction Operation Checks") - 85 if (contains_key(Recipe, inst.operation) && inst.operation >= MAX_PRIMITIVE_RECIPES + 85 if (contains_key(Recipe, inst.operation) && inst.operation >= MAX_PRIMITIVE_RECIPES 86 && any_type_ingredient_in_header(inst.operation)) { 87 raise << maybe(caller.name) << "instruction '" << inst.name << "' has no valid specialization\n" << end(); 88 return; diff --git a/html/057immutable.cc.html b/html/057immutable.cc.html index 94bbc350..9876e93f 100644 --- a/html/057immutable.cc.html +++ b/html/057immutable.cc.html @@ -428,7 +428,7 @@ if ('onhashchange' in window) { 366 if (!contains_key(Recipe, inst.operation)) { 367 // primitive recipe 368 switch (inst.operation) { -369 case COPY: +369 case COPY: 370 for (set<int>::iterator p = current_ingredient_indices.begin(); p != current_ingredient_indices.end(); ++p) 371 current_ingredient_and_aliases.insert(inst.products.at(*p).name); 372 break; diff --git a/html/069hash.cc.html b/html/069hash.cc.html index ae67a728..1d97969e 100644 --- a/html/069hash.cc.html +++ b/html/069hash.cc.html @@ -119,10 +119,10 @@ if ('onhashchange' in window) { 56 size_t hash_mu_address(size_t h, reagent& r) { 57 if (r.value == 0) return 0; 58 trace(9999, "mem") << "location " << r.value << " is " << no_scientific(get_or_insert(Memory, r.value)) << end(); - 59 r.set_value(get_or_insert(Memory, r.value)); + 59 r.set_value(get_or_insert(Memory, r.value)); 60 if (r.value != 0) { 61 trace(9999, "mem") << "skipping refcount at " << r.value << end(); - 62 r.set_value(r.value+1); // skip refcount + 62 r.set_value(r.value+1); // skip refcount 63 } 64 drop_from_type(r, "address"); 65 return hash(h, r); @@ -144,7 +144,7 @@ if ('onhashchange' in window) { 81 elem.type = copy_array_element(r.type); 82 for (int i=0, address = r.value+1; i < size; ++i, address += size_of(elem)) { 83 reagent/*copy*/ tmp = elem; - 84 tmp.set_value(address); + 84 tmp.set_value(address); 85 h = hash(h, tmp); 86 //? cerr << i << " (" << address << "): " << h << '\n'; 87 } @@ -158,7 +158,7 @@ if ('onhashchange' in window) { 95 for (int i = 0; i < SIZE(info.elements); ++i) { 96 reagent/*copy*/ element = element_type(r.type, i); 97 if (has_property(element, "ignore-for-hash")) continue; - 98 element.set_value(address+offset); + 98 element.set_value(address+offset); 99 h = hash(h, element); 100 //? cerr << i << ": " << h << '\n'; 101 offset += size_of(info.elements.at(i).type); @@ -174,7 +174,7 @@ if ('onhashchange' in window) { 111 // todo: move this error to container definition time 112 if (has_property(variant, "ignore-for-hash")) 113 raise << get(Type, type->value).name << ": /ignore-for-hash won't work in exclusive containers\n" << end(); -114 variant.set_value(r.value + /*skip tag*/1); +114 variant.set_value(r.value + /*skip tag*/1); 115 h = hash(h, variant); 116 return h; 117 } diff --git a/html/071recipe.cc.html b/html/071recipe.cc.html index 5ecfdb46..e957e635 100644 --- a/html/071recipe.cc.html +++ b/html/071recipe.cc.html @@ -92,7 +92,7 @@ if ('onhashchange' in window) { 30 :(before "End Mu Types Initialization") 31 put(Type_ordinal, "recipe-literal", 0); 32 // 'recipe' variables can store recipe-literal - 33 type_ordinal recipe = put(Type_ordinal, "recipe", Next_type_ordinal++); + 33 type_ordinal recipe = put(Type_ordinal, "recipe", Next_type_ordinal++); 34 get_or_insert(Type, recipe).name = "recipe"; 35 36 :(after "Begin transform_names Ingredient Special-cases(ingredient, inst, caller)") @@ -117,7 +117,7 @@ if ('onhashchange' in window) { 55 } 56 void initialize_recipe_literal(reagent& x) { 57 x.type = new type_tree("recipe-literal"); - 58 x.set_value(get(Recipe_ordinal, x.name)); + 58 x.set_value(get(Recipe_ordinal, x.name)); 59 } 60 bool contains_reagent_with_type(const recipe& caller, const string& name) { 61 for (int i = 0; i < SIZE(caller.steps); ++i) { diff --git a/html/072scheduler.cc.html b/html/072scheduler.cc.html index 392eef4c..61e253bb 100644 --- a/html/072scheduler.cc.html +++ b/html/072scheduler.cc.html @@ -352,7 +352,7 @@ if ('onhashchange' in window) { 289 increment_any_refcounts(ingredient, ingredients.at(i)); 290 :(before "End Populate start-running Ingredient") 291 increment_any_refcounts(ingredient, ingredients.at(i)); -292 :(before "End should_update_refcounts_in_write_memory Special-cases For Primitives") +292 :(before "End should_update_refcounts_in_write_memory Special-cases For Primitives") 293 if (inst.operation == NEXT_INGREDIENT || inst.operation == NEXT_INGREDIENT_WITHOUT_TYPECHECKING) { 294 if (space_index(inst.products.at(0)) > 0) return true; 295 if (has_property(inst.products.at(0), "raw")) return true; diff --git a/html/073wait.cc.html b/html/073wait.cc.html index 07a7bb9f..b7348439 100644 --- a/html/073wait.cc.html +++ b/html/073wait.cc.html @@ -231,11 +231,11 @@ if ('onhashchange' in window) { 168 break; 169 } 170 const type_tree* base_root_type = base.type->atom ? base.type : base.type->left; -171 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) { +171 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) { 172 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(); 173 break; 174 } -175 type_ordinal base_type = base.type->value; +175 type_ordinal base_type = base.type->value; 176 const reagent& offset = inst.ingredients.at(1); 177 if (!is_literal(offset) || !is_mu_scalar(offset)) { 178 raise << maybe(get(Recipe, r).name) << "second ingredient of 'get-location' should have type 'offset', but got '" << inst.ingredients.at(1).original_string << "'\n" << end(); diff --git a/html/074deep_copy.cc.html b/html/074deep_copy.cc.html index 89490db8..a74b4704 100644 --- a/html/074deep_copy.cc.html +++ b/html/074deep_copy.cc.html @@ -276,7 +276,7 @@ if ('onhashchange' in window) { 214 // allocate a tiny bit of temporary space for deep_copy() 215 trace(9991, "run") << "deep-copy: allocating space for temporary" << end(); 216 reagent tmp("tmp:address:number"); -217 tmp.set_value(allocate(1)); +217 tmp.set_value(allocate(1)); 218 products.push_back(deep_copy(input, tmp)); 219 // reclaim Mu memory allocated for tmp 220 trace(9991, "run") << "deep-copy: reclaiming temporary" << end(); @@ -323,7 +323,7 @@ if ('onhashchange' in window) { 261 // a temporary location to help copy the payload. 262 trace(9991, "run") << "deep-copy: writing temporary " << tmp.value << ": " << out << end(); 263 put(Memory, tmp.value, out); -264 payload.set_value(tmp.value); // now modified for output +264 payload.set_value(tmp.value); // now modified for output 265 vector<double> old_data = read_memory(payload); 266 trace(9991, "run") << "deep-copy: really writing to " << payload.value << ' ' << to_string(payload) << " (old value " << to_string(old_data) << " new value " << to_string(data) << ")" << end(); 267 write_memory(payload, data); @@ -349,7 +349,7 @@ if ('onhashchange' in window) { 287 curr.type = new type_tree(new type_tree("address"), new type_tree(new type_tree(info->payload_type->name), NULL)); 288 else 289 curr.type = new type_tree(new type_tree("address"), new type_tree(*info->payload_type)); -290 curr.set_value(canonized_in.value + info->offset); +290 curr.set_value(canonized_in.value + info->offset); 291 curr.properties.push_back(pair<string, string_tree*>("raw", NULL)); 292 trace(9991, "run") << "deep-copy: copying address " << curr.value << end(); 293 out.at(info->offset) = deep_copy_address(curr, addresses_copied, tmp); diff --git a/html/082scenario_screen.cc.html b/html/082scenario_screen.cc.html index 50abb903..78485464 100644 --- a/html/082scenario_screen.cc.html +++ b/html/082scenario_screen.cc.html @@ -232,7 +232,7 @@ if ('onhashchange' in window) { 169 else { 170 assert(curr.products.empty()); 171 curr.products.push_back(reagent("screen:&:screen/raw")); -172 curr.products.at(0).set_value(SCREEN); +172 curr.products.at(0).set_value(SCREEN); 173 } 174 } 175 @@ -308,7 +308,7 @@ if ('onhashchange' in window) { 245 uint32_t get(); // unicode codepoint 246 uint32_t peek(); // unicode codepoint 247 bool at_end() const; -248 void skip_whitespace_and_comments(); +248 void skip_whitespace_and_comments(); 249 }; 250 251 :(code) @@ -326,7 +326,7 @@ if ('onhashchange' in window) { 263 // todo: too-long expected_contents should fail 264 int addr = screen_data_start+/*skip length*/1; 265 for (int row = 0; row < screen_height; ++row) { -266 cursor.skip_whitespace_and_comments(); +266 cursor.skip_whitespace_and_comments(); 267 if (cursor.at_end()) break; 268 if (cursor.get() != '.') { 269 raise << Current_scenario->name << ": each row of the expected screen should start with a '.'\n" << end(); @@ -390,7 +390,7 @@ if ('onhashchange' in window) { 327 return; 328 } 329 } -330 cursor.skip_whitespace_and_comments(); +330 cursor.skip_whitespace_and_comments(); 331 if (!cursor.at_end()) { 332 raise << Current_scenario->name << ": expected screen has too many rows\n" << end(); 333 Passed = false; @@ -425,7 +425,7 @@ if ('onhashchange' in window) { 362 return result; 363 } 364 -365 void raw_string_stream::skip_whitespace_and_comments() { +365 void raw_string_stream::skip_whitespace_and_comments() { 366 while (!at_end()) { 367 if (isspace(peek())) get(); 368 else if (peek() == '#') { diff --git a/html/089scenario_filesystem.cc.html b/html/089scenario_filesystem.cc.html index 42f50748..7bbca967 100644 --- a/html/089scenario_filesystem.cc.html +++ b/html/089scenario_filesystem.cc.html @@ -172,9 +172,9 @@ if ('onhashchange' in window) { 112 in >> std::noskipws; 113 while (true) { 114 if (!has_data(in)) break; -115 skip_whitespace_and_comments(in); +115 skip_whitespace_and_comments(in); 116 if (!has_data(in)) break; -117 string filename = next_word(in); +117 string filename = next_word(in); 118 if (filename.empty()) { 119 assert(!has_data(in)); 120 raise << "incomplete 'resources' block at end of file (0)\n" << end(); @@ -194,7 +194,7 @@ if ('onhashchange' in window) { 134 raise << caller << ": assume-resources: no data for filename '" << filename << "'\n" << end(); 135 break; 136 } -137 string arrow = next_word(in); +137 string arrow = next_word(in); 138 if (arrow.empty()) { 139 assert(!has_data(in)); 140 raise << "incomplete 'resources' block at end of file (1)\n" << end(); @@ -208,7 +208,7 @@ if ('onhashchange' in window) { 148 raise << caller << ": assume-resources: no data for filename '" << filename << "' after '<-'\n" << end(); 149 break; 150 } -151 string contents = next_word(in); +151 string contents = next_word(in); 152 if (contents.empty()) { 153 assert(!has_data(in)); 154 raise << "incomplete 'resources' block at end of file (2)\n" << end(); @@ -232,7 +232,7 @@ if ('onhashchange' in window) { 172 if (data.empty()) return ""; 173 istringstream in(data); 174 in >> std::noskipws; -175 skip_whitespace_and_comments(in); +175 skip_whitespace_and_comments(in); 176 ostringstream out; 177 while (true) { 178 if (!has_data(in)) break; diff --git a/html/999spaces.cc.html b/html/999spaces.cc.html index 548f3257..9d3d5b67 100644 --- a/html/999spaces.cc.html +++ b/html/999spaces.cc.html @@ -74,7 +74,7 @@ if ('onhashchange' in window) { 18 //: 19 //: 0 - unused (IDLE; do nothing) 20 //: 1-199 - primitives -21 assert(MAX_PRIMITIVE_RECIPES < 200); +21 assert(MAX_PRIMITIVE_RECIPES < 200); 22 //: 200-999 - defined in .mu files as sequences of primitives 23 assert(Next_recipe_ordinal == 1000); 24 //: 1000 onwards - reserved for tests, cleared between tests -- cgit 1.4.1-2-gfad0