:(after "Types")
// A program is a book of 'recipes' (functions)
typedef int recipe_number;
:(before "End Globals")
unordered_map<string, recipe_number> Recipe_number;
unordered_map<recipe_number, recipe> 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<instruction> 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<reagent> ingredients; // only if !is_label
vector<reagent> 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<pair<string, vector<string> > > properties;
string name;
int value;
vector<type_number> types;
reagent(string s);
reagent(type_number t);
string to_string();
};
:(before "struct reagent")
struct property {
vector<string> values;
};
:(before "End Globals")
// Locations refer to a common 'memory'. Each location can store a number.
unordered_map<int, int> 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<string, type_number> Type_number;
unordered_map<type_number, type_info> Type;
int Next_type_number = 1;
:(code)
void setup_types() {
Type.clear(); Type_number.clear();
Type_number["literal"] = 0;
Type_number["offset"] = 0;
Next_type_number = 1;
// Mu Types Initialization.
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 Initialization.
}
:(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<vector<type_number> > elements; // only if is_record
vector<type_number> 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();
//: Helpers
:(code)
// indent members to avoid generating prototypes for them
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 <name>:<type>:<type>:.../<property>/<property>/...
reagent::reagent(string s) {
istringstream in(s);
// properties
while (!in.eof()) {
istringstream row(slurp_until(in, '/'));
string name = slurp_until(row, ':');
vector<string> values;
while (!row.eof())
values.push_back(slurp_until(row, ':'));
properties.push_back(pair<string, vector<string> >(name, values));
}
// structures for the first row of properties
name = properties[0].first;
value = to_int(name);
for (size_t i = 0; i < properties[0].second.size(); ++i) {
types.push_back(Type_number[properties[0].second[i]]);
}
}
reagent::reagent(type_number t) {
types.push_back(t);
}
string reagent::to_string() {
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() {
for (unordered_map<int, int>::iterator p = Memory.begin(); p != Memory.end(); ++p) {
cout << p->first << ": " << p->second << '\n';
}
}