:(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; }; :(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 recipe_number operation; // only if !is_label 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 { string name; vector types; vector > properties; reagent(string s); reagent(type_number t); string to_string(); }; :(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") // 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. int integer = Type_number["integer"] = Next_type_number++; Type[integer].size = 1; int address = Type_number["address"] = Next_type_number++; Type[address].size = 1; int boolean = Type_number["boolean"] = Next_type_number++; Type[boolean].size = 1; // End Mu Types. } :(before "End Setup") setup_types(); :(before "End Types") // You can construct arbitrary new types. Types are either 'records', containing // 'fields' of other types, or 'array's of a single type repeated over and over. // // For example: // storing bank balance next to a person's name might require a record, and // high scores in a game might need an array of numbers. struct type_info { size_t size; bool is_record; bool is_array; vector > elements; // only if is_record vector element; // only if is_array type_info() :size(0), is_record(false), is_array(false) {} }; :(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. } :(before "End Setup") setup_recipes(); :(code) // Helpers 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) { istringstream in(s); name = slurp_until(in, ':'); istringstream ts(slurp_until(in, '/')); string t; while (!(t = slurp_until(ts, ':')).empty()) types.push_back(Type_number[t]); // properties while (!in.eof()) { istringstream prop(slurp_until(in, '/')); string name = slurp_until(prop, ':'); properties.push_back(pair(name, property())); } } reagent::reagent(type_number t) { types.push_back(t); } string reagent::to_string() { ostringstream out; out << "{name: \"" << name << "\", type: "; for (size_t i = 0; i < types.size(); ++i) { out << types[i]; if (i < types.size()-1) out << "-"; } if (!properties.empty()) { out << ", property: "; for (size_t i = 0; i < properties.size(); ++i) { out << properties[i].first << ":"; for (size_t j = 0; j < properties[i].second.values.size(); ++j) { out << properties[i].second.values[j]; if (j < properties[i].second.values.size()-1) 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() { for (unordered_map::iterator p = Memory.begin(); p != Memory.end(); ++p) { cout << p->first << ": " << p->second << '\n'; } }