:(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 { 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); 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, 'array's of a single type repeated over and over, // or 'addresses' pointing at a location elsewhere in memory. // // 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. // You'll see examples using addresses later. struct type_info { size_t size; bool is_address; bool is_record; bool is_array; vector target; // only if is_address vector > elements; // only if is_record or is_array type_info() :size(0), is_address(false), 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(); Recipe_number["idle"] = IDLE; Next_recipe_number = 1; // Primitive Recipe Numbers. Recipe_number["copy"] = 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) { //? cout << s << '\n'; //? 1 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]); } //? cout << types.size() << '\n'; //? 1 // todo: properties } 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 << "-"; } out << "}"; // todo: properties return out.str(); } string slurp_until(istream& in, char delim) { ostringstream out; char c; while (in >> c) { if (c == delim) { break; } out << c; } return out.str(); }