about summary refs log tree commit diff stats
path: root/cpp/010vm
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/010vm')
-rw-r--r--cpp/010vm145
1 files changed, 145 insertions, 0 deletions
diff --git a/cpp/010vm b/cpp/010vm
new file mode 100644
index 00000000..3b8a7c96
--- /dev/null
+++ b/cpp/010vm
@@ -0,0 +1,145 @@
+// A program is a book of 'recipes' (functions)
+
+:(after "Types")
+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 {
+  vector<instruction> 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<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 {
+  string name;
+  vector<type_number> types;
+  vector<pair<string, property> > properties;
+  reagent(string s);
+  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")
+// 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;
+  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<type_number> target;  // only if is_address
+  vector<vector<type_number> > 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 <name>:<type>:<type>:.../<property>/<property>/...
+  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")