// A program is a book of 'recipes' (functions) :(after "Types") 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 step; }; :(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; int integer = Type_number["integer"] = Next_type_number++; Type[integer].size = 1; } :(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. struct type_info { int 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) {} }; :(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"] = 0; Next_recipe_number = 1; Recipe_number["copy"] = Next_recipe_number++; } :(before "End Types") const int idle = 0; // always the first entry in the recipe book :(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, ':'); types.push_back(Type_number[slurp_until(in, '/')]); // todo: multiple types } string reagent::to_string() { ostringstream out; out << "{name: \"" << name << "\", type: " << types[0] << "}"; // 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(); } :(before "End Setup")