about summary refs log blame commit diff stats
path: root/cpp/010vm
blob: 2f2268bacd88aae67095a55028250f0e498c91af (plain) (tree)
1
2
3
4
5
6
7
8
9
                
                                               






                                                   
                                                                            

                
                            

  




                                                                                              
                                                    










                                                    



                                                                             















                                                                          

                     














                                                                             




                                     
              
                                                            
                         

                                                            

                                                            
                  


                     




                                                                                




                                                                            
                  
              



                                                    

                                                              
                                                                               

  


                                                                 






                                                                              
                               
                         
                              
                               
                       
                                  
 


                     



          

                                                                                                                     
 
                                                                              
                              
                                

                                





                                                 





                                                                     


                               




                                               










                                                                         







                                             
                       





                   
:(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 {
  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;
  // 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<type_number> target;  // only if is_address
  vector<vector<type_number> > elements;  // only if is_record
  vector<type_number> element;  // only if 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 <name>:<type>:<type>:.../<property>/<property>/...
  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
    // properties
    while (!in.eof()) {
      istringstream prop(slurp_until(in, '/'));
      string name = slurp_until(prop, ':');
      properties.push_back(pair<string, property>(name, property()));
    }
  }
  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();
}