:(after "Types") // A program is a book of 'recipes' (functions) typedef int recipe_number; :(before "End Globals") unordered_map Recipe_number; unordered_map Recipe; int Next_recipe_number = 1; :(before "End Types") // Recipes are lists of instructions. To run a recipe, the computer runs its // instructions. struct recipe { string name; vector steps; // End recipe Fields }; :(before "struct recipe") // Each instruction is either of the form: // product1, product2, product3, ... <- operation ingredient1, ingredient2, ingredient3, ... // or just a single 'label' followed by a colon // label: // Labels don't do anything, they're just waypoints. struct instruction { bool is_label; string label; // only if is_label string name; // only if !is_label recipe_number operation; // Recipe_number[name] vector ingredients; // only if !is_label vector products; // only if !is_label instruction(); void clear(); }; :(before "struct instruction") // Ingredients and products are a single species -- a reagent. Reagents refer // either to numbers or to locations in memory along with 'type' tags telling // us how to interpret them. They also can contain arbitrary other lists of // properties besides types, but we're getting ahead of ourselves. struct reagent { vector > > properties; string name; int value; bool initialized; vector types; reagent(string s); reagent(); void set_value(int v) { value = v; initialized = true; } string to_string() const; }; :(before "struct reagent") struct property { vector values; }; :(before "End Globals") // Locations refer to a common 'memory'. Each location can store a number. unordered_map Memory; :(before "End Setup") Memory.clear(); :(after "Types") // Mu types encode how the numbers stored in different parts of memory are // interpreted. A location tagged as a 'character' type will interpret the // number 97 as the letter 'a', while a different location of type 'integer' // would not. // // Unlike most computers today, mu stores types in a single big table, shared // by all the mu programs on the computer. This is useful in providing a // seamless experience to help understand arbitrary mu programs. typedef int type_number; :(before "End Globals") unordered_map Type_number; unordered_map Type; int Next_type_number = 1; :(code) void setup_types() { Type.clear(); Type_number.clear(); Type_number["literal"] = 0; Next_type_number = 1; // Mu Types Initialization int integer = Type_number["integer"] = Next_type_number++; Type_number["location"] = Type_number["integer"]; // wildcard type Type[integer].name = "integer"; int address = Type_number["address"] = Next_type_number++; Type[address].name = "address"; int boolean = Type_number["boolean"] = Next_type_number++; Type[boolean].name = "boolean"; int character = Type_number["character"] = Next_type_number++; Type[character].name = "character"; // Array types are a special modifier to any other type. For example, // array:integer or array:address:boolean. int array = Type_number["array"] = Next_type_number++; Type[array].name = "array"; // End Mu Types Initialization } :(before "End One-time Setup") setup_types(); :(before "End Types") // You can construct arbitrary new types. New types are either 'containers' // with multiple 'elements' of other types, or 'exclusive containers' containing // one of multiple 'variants'. (These are similar to C structs and unions, // respectively, though exclusive containers implicitly include a tag element // recording which variant they should be interpreted as.) // // For example, storing bank balance and name for an account might require a // container, but if bank accounts may be either for individuals or groups, // with different properties for each, that may require an exclusive container // whose variants are individual-account and joint-account containers. enum kind_of_type { primitive, container, exclusive_container }; struct type_info { string name; kind_of_type kind; size_t size; // only if type is not primitive; primitives and addresses have size 1 (except arrays are dynamic) vector > elements; vector element_names; // End type_info Fields type_info() :kind(primitive), size(0) {} }; :(before "End Globals") const int IDLE = 0; // always the first entry in the recipe book const int COPY = 1; :(code) // It's all very well to construct recipes out of other recipes, but we need // to know how to do *something* out of the box. For the following // recipes there are only codes, no entries in the book, because mu just knows // what to do for them. void setup_recipes() { Recipe.clear(); Recipe_number.clear(); Next_recipe_number = 0; Recipe_number["idle"] = IDLE; assert(Next_recipe_number == IDLE); Next_recipe_number++; // Primitive Recipe Numbers Recipe_number["copy"] = COPY; assert(Next_recipe_number == COPY); Next_recipe_number++; // End Primitive Recipe Numbers } //: We could just reset the recipe table after every test, but that gets slow //: all too quickly. Instead, initialize the common stuff just once at //: startup. Later layers will carefully undo each test's additions after //: itself. :(before "End One-time Setup") setup_recipes(); assert(Next_recipe_number < 100); // level 0 is primitives; until 99 Next_recipe_number = 100; // End Load Recipes // give tests a consistent starting point assert(Next_recipe_number < 1000); Next_recipe_number = 1000; delete Trace_stream; Trace_stream = new trace_stream; :(before "End Setup") Next_recipe_number = 1000; // consistent new numbers for each test //: Helpers :(code) instruction::instruction() :is_label(false), operation(IDLE) {} void instruction::clear() { is_label=false; label.clear(); operation=IDLE; ingredients.clear(); products.clear(); } // Reagents have the form :::...///... reagent::reagent(string s) :value(0), initialized(false) { istringstream in(s); in >> std::noskipws; // properties while (!in.eof()) { istringstream row(slurp_until(in, '/')); row >> std::noskipws; string name = slurp_until(row, ':'); vector values; while (!row.eof()) values.push_back(slurp_until(row, ':')); properties.push_back(pair >(name, values)); } // structures for the first row of properties name = properties[0].first; for (size_t i = 0; i < properties[0].second.size(); ++i) { types.push_back(Type_number[properties[0].second[i]]); } if (name == "_" && types.empty()) { types.push_back(0); properties[0].second.push_back("dummy"); } } reagent::reagent() :value(0), initialized(false) { // The first property is special, so ensure we always have it. // Other properties can be pushed back, but the first must always be // assigned to. properties.push_back(pair >("", vector())); } string reagent::to_string() const { ostringstream out; out << "{name: \"" << name << "\", value: " << value << ", type: "; for (size_t i = 0; i < types.size(); ++i) { out << types[i]; if (i < types.size()-1) out << "-"; } if (!properties.empty()) { out << ", properties: ["; for (size_t i = 0; i < properties.size(); ++i) { out << "\"" << properties[i].first << "\": "; for (size_t j = 0; j < properties[i].second.size(); ++j) { out << "\"" << properties[i].second[j] << "\""; if (j < properties[i].second.size()-1) out << ":"; } if (i < properties.size()-1) out << ", "; else out << "]"; } } out << "}"; return out.str(); } string slurp_until(istream& in, char delim) { ostringstream out; char c; while (in >> c) { if (c == delim) { // drop the delim break; } out << c; } return out.str(); } void dump_memory() { map ordered(Memory.begin(), Memory.end()); for (map::iterator p = ordered.begin(); p != ordered.end(); ++p) { cout << p->first << ": " << p->second << '\n'; } } :(before "End Includes") #include using std::map;