https://github.com/akkartik/mu/blob/master/010vm.cc
  1 //: A Mu program is a book of 'recipes' (functions)
  2 :(before "End Globals")
  3 //: Each recipe is stored at a specific page number, or ordinal.
  4 map<recipe_ordinal, recipe> Recipe;
  5 //: You can also refer to each recipe by its name.
  6 map<string, recipe_ordinal> Recipe_ordinal;
  7 recipe_ordinal Next_recipe_ordinal = 1;
  8 
  9 //: Ordinals are like numbers, except you can't do arithmetic on them. Ordinal
 10 //: 1 is not less than 2, it's just different. Phone numbers are ordinals;
 11 //: adding two phone numbers is meaningless. Here each recipe does something
 12 //: incommensurable with any other recipe.
 13 :(after "Types")
 14 typedef int recipe_ordinal;
 15 
 16 :(before "End Types")
 17 // Recipes are lists of instructions. To perform or 'run' a recipe, the
 18 // computer runs its instructions.
 19 struct recipe {
 20   recipe_ordinal ordinal;
 21   string name;
 22   vector<instruction> steps;
 23   // End recipe Fields
 24   recipe();
 25 };
 26 
 27 :(before "struct recipe")
 28 // Each instruction is either of the form:
 29 //   product1, product2, product3, ... <- operation ingredient1, ingredient2, ingredient3, ...
 30 // or just a single 'label' starting with a non-alphanumeric character
 31 //   +label
 32 // Labels don't do anything, they're just named locations in a recipe.
 33 struct instruction {
 34   bool is_label;
 35   string label;  // only if is_label
 36   string name;  // only if !is_label
 37   string original_string;  // for error messages
 38   recipe_ordinal operation;  // get(Recipe_ordinal, name)
 39   vector<reagent> ingredients;  // only if !is_label
 40   vector<reagent> products;  // only if !is_label
 41   // End instruction Fields
 42   instruction();
 43   void clear();
 44   bool is_empty();
 45 };
 46 
 47 :(before "struct instruction")
 48 // Ingredients and products are a single species -- a reagent. Reagents refer
 49 // either to numbers or to locations in memory along with 'type' tags telling
 50 // us how to interpret them. They also can contain arbitrary other lists of
 51 // properties besides types, but we're getting ahead of ourselves.
 52 struct reagent {
 53   string original_string;
 54   string name;
 55   type_tree* type;
 56   vector<pair<string, string_tree*> > properties;  // can't be a map because the string_tree sometimes needs to be NULL, which can be confusing
 57   double value;
 58   bool initialized;
 59   // End reagent Fields
 60   reagent(const string& s);
 61   reagent() :type(NULL), value(0), initialized(false) {}
 62   reagent(type_tree* t) :type(t), value(0), initialized(false) {}
 63   ~reagent();
 64   void clear();
 65   reagent(const reagent& original);
 66   reagent& operator=(const reagent& original);
 67   void set_value(double v) { value = v;  initialized = true; }
 68 };
 69 
 70 :(before "struct reagent")
 71 // Types can range from a simple type ordinal, to arbitrarily complex trees of
 72 // type parameters, like (map (address array character) (list number))
 73 struct type_tree {
 74   bool atom;
 75   string name;  // only if atom
 76   type_ordinal value;  // only if atom
 77   type_tree* left;  // only if !atom
 78   type_tree* right;  // only if !atom
 79   ~type_tree();
 80   type_tree(const type_tree& original);
 81   // atomic type ordinal
 82   explicit type_tree(string name);
 83   type_tree(string name, type_ordinal v) :atom(true), name(name), value(v), left(NULL), right(NULL) {}
 84   // tree of type ordinals
 85   type_tree(type_tree* l, type_tree* r) :atom(false), value(0), left(l), right(r) {}
 86   type_tree& operator=(const type_tree& original);
 87   bool operator==(const type_tree& other) const;
 88   bool operator!=(const type_tree& other) const { return !operator==(other); }
 89   bool operator<(const type_tree& other) const;
 90   bool operator>(const type_tree& other) const { return other.operator<(*this); }
 91 };
 92 
 93 struct string_tree {
 94   bool atom;
 95   string value;  // only if atom
 96   string_tree* left;  // only if !atom
 97   string_tree* right;  // only if !atom
 98   ~string_tree();
 99   string_tree(const string_tree& original);
100   // atomic string
101   explicit string_tree(string v) :atom(true), value(v), left(NULL), right(NULL) {}
102   // tree of strings
103   string_tree(string_tree* l, string_tree* r) :atom(false), left(l), right(r) {}
104 };
105 
106 // End type_tree Definition
107 :(code)
108 type_tree::type_tree(string name) :atom(true), name(name), value(get(Type_ordinal, name)), left(NULL), right(NULL) {}
109 
110 :(before "End Globals")
111 // Locations refer to a common 'memory'. Each location can store a number.
112 map<int, double> Memory;
113 :(before "End Reset")
114 Memory.clear();
115 
116 :(after "Types")
117 // Mu types encode how the numbers stored in different parts of memory are
118 // interpreted. A location tagged as a 'character' type will interpret the
119 // value 97 as the letter 'a', while a different location of type 'number'
120 // would not.
121 //
122 // Unlike most computers today, Mu stores types in a single big table, shared
123 // by all the Mu programs on the computer. This is useful in providing a
124 // seamless experience to help understand arbitrary Mu programs.
125 typedef int type_ordinal;
126 :(before "End Globals")
127 map<string, type_ordinal> Type_ordinal;
128 map<type_ordinal, type_info> Type;
129 type_ordinal Next_type_ordinal = 1;
130 type_ordinal Number_type_ordinal = 0;
131 type_ordinal Boolean_type_ordinal = 0;
132 type_ordinal Character_type_ordinal = 0;
133 type_ordinal Address_type_ordinal = 0;
134 type_ordinal Array_type_ordinal = 0;
135 :(code)
136 void setup_types() {
137   Type.clear();  Type_ordinal.clear();
138   put(Type_ordinal, "literal", 0);
139   Next_type_ordinal = 1;
140   // Mu Types Initialization
141   Number_type_ordinal = put(Type_ordinal, "number", Next_type_ordinal++);
142   get_or_insert(Type, Number_type_ordinal).name = "number";
143   put(Type_ordinal, "location", Number_type_ordinal);  // synonym of number for addresses we'll never look up
144   Address_type_ordinal = put(Type_ordinal, "address", Next_type_ordinal++);
145   get_or_insert(Type, Address_type_ordinal).name = "address";
146   Boolean_type_ordinal = put(Type_ordinal, "boolean", Next_type_ordinal++);
147   get_or_insert(Type, Boolean_type_ordinal).name = "boolean";
148   Character_type_ordinal = put(Type_ordinal, "character", Next_type_ordinal++);
149   get_or_insert(Type, Character_type_ordinal).name = "character";
150   // Array types are a special modifier to any other type. For example,
151   // array:number or array:address:boolean.
152   Array_type_ordinal = put(Type_ordinal, "array", Next_type_ordinal++);
153   get_or_insert(Type, Array_type_ordinal).name = "array";
154   // End Mu Types Initialization
155 }
156 void teardown_types() {
157   for (map<type_ordinal, type_info>::iterator p = Type.begin();  p != Type.end();  ++p) {
158     for (int i = 0;  i < SIZE(p->second.elements);  ++i)
159       p->second.elements.clear();
160   }
161   Type_ordinal.clear();
162 }
163 :(before "End One-time Setup")
164 setup_types();
165 atexit(teardown_types);
166 
167 :(before "End Types")
168 // You can construct arbitrary new types. New types are either 'containers'
169 // with multiple 'elements' of other types, or 'exclusive containers' containing
170 // one of multiple 'variants'. (These are similar to C structs and unions,
171 // respectively, though exclusive containers implicitly include a tag element
172 // recording which variant they should be interpreted as.)
173 //
174 // For example, storing bank balance and name for an account might require a
175 // container, but if bank accounts may be either for individuals or groups,
176 // with different properties for each, that may require an exclusive container
177 // whose variants are individual-account and joint-account containers.
178 enum kind_of_type {
179   PRIMITIVE,
180   CONTAINER,
181   EXCLUSIVE_CONTAINER
182 };
183 
184 struct type_info {
185   string name;
186   kind_of_type kind;
187   vector<reagent> elements;
188   // End type_info Fields
189   type_info() :kind(PRIMITIVE) {
190     // End type_info Constructor
191   }
192 };
193 
194 enum primitive_recipes {
195   IDLE = 0,
196   COPY,
197   // End Primitive Recipe Declarations
198   MAX_PRIMITIVE_RECIPES,
199 };
200 :(code)
201 //: It's all very well to construct recipes out of other recipes, but we need
202 //: to know how to do *something* out of the box. For the following
203 //: recipes there are only codes, no entries in the book, because Mu just knows
204 //: what to do for them.
205 void setup_recipes() {
206   Recipe.clear();  Recipe_ordinal.clear();
207   put(Recipe_ordinal, "idle", IDLE);
208   // Primitive Recipe Numbers
209   put(Recipe_ordinal, "copy", COPY);
210   // End Primitive Recipe Numbers
211 }
212 //: We could just reset the recipe table after every test, but that gets slow
213 //: all too quickly. Instead, initialize the common stuff just once at
214 //: startup. Later layers will carefully undo each test's additions after
215 //: itself.
216 :(before "End One-time Setup")
217 setup_recipes();
218 assert(MAX_PRIMITIVE_RECIPES < 200);  // level 0 is primitives; until 199
219 Next_recipe_ordinal = 200;
220 put(Recipe_ordinal, "main", Next_recipe_ordinal++);
221 // Load Mu Prelude
222 // End Mu Prelude
223 :(before "End Commandline Parsing")
224 assert(Next_recipe_ordinal < 1000);  // recipes being tested didn't overflow into test space
225 :(before "End Reset")
226 Next_recipe_ordinal = 1000;  // consistent new numbers for each test
227 
228 //: One final detail: tests can modify our global tables of recipes and types,
229 //: so we need some way to clean up after each test is done so it doesn't
230 //: influence later ones.
231 :(before "End Globals")
232 map<string, recipe_ordinal> Recipe_ordinal_snapshot;
233 map<recipe_ordinal, recipe> Recipe_snapshot;
234 map<string, type_ordinal> Type_ordinal_snapshot;
235 map<type_ordinal, type_info> Type_snapshot;
236 :(before "End One-time Setup")
237 save_snapshots();
238 :(before "End Reset")
239 restore_snapshots();
240 
241 :(code)
242 void save_snapshots() {
243   Recipe_ordinal_snapshot = Recipe_ordinal;
244   Recipe_snapshot = Recipe;
245   Type_ordinal_snapshot = Type_ordinal;
246   Type_snapshot = Type;
247   // End save_snapshots
248 }
249 
250 void restore_snapshots() {
251   Recipe = Recipe_snapshot;
252   Recipe_ordinal = Recipe_ordinal_snapshot;
253   restore_non_recipe_snapshots();
254 }
255 // when running sandboxes in the edit/ app we'll want to restore everything except recipes defined in the app
256 void restore_non_recipe_snapshots() {
257   Type_ordinal = Type_ordinal_snapshot;
258   Type = Type_snapshot;
259   // End restore_snapshots
260 }
261 
262 //:: Helpers
263 
264 :(code)
265 recipe::recipe() {
266   ordinal = -1;
267   // End recipe Constructor
268 }
269 
270 instruction::instruction() :is_label(false), operation(IDLE) {
271   // End instruction Constructor
272 }
273 void instruction::clear() {
274   is_label=false;
275   label.clear();
276   name.clear();
277   operation=IDLE;
278   ingredients.clear();
279   products.clear();
280   original_string.clear();
281   // End instruction Clear
282 }
283 bool instruction::is_empty() { return !is_label && name.empty(); }
284 
285 // Reagents have the form <name>:<type>:<type>:.../<property>/<property>/...
286 reagent::reagent(const string& s) :original_string(s), type(NULL), value(0), initialized(false) {
287   // Parsing reagent(string s)
288   istringstream in(s);
289   in >> std::noskipws;
290   // name and type
291   istringstream first_row(slurp_until(in, '/'));
292   first_row >> std::noskipws;
293   name = slurp_until(first_row, ':');
294   string_tree* type_names = parse_property_list(first_row);
295   // End Parsing Reagent Type Property(type_names)
296   type = new_type_tree(type_names);
297   delete type_names;
298   // special cases
299   if (is_integer(name) && type == NULL)
300     type = new type_tree("literal");
301   if (name == "_" && type == NULL)
302     type = new type_tree("literal");
303   // other properties
304   slurp_properties(in, properties);
305   // End Parsing reagent
306 }
307 
308 void slurp_properties(istream& in, vector<pair<string, string_tree*> >& out) {
309   while (has_data(in)) {
310     istringstream row(slurp_until(in, '/'));
311     row >> std::noskipws;
312     string key = slurp_until(row, ':');
313     string_tree* value = parse_property_list(row);
314     out.push_back(pair<string, string_tree*>(key, value));
315   }
316 }
317 
318 string_tree* parse_property_list(istream& in) {
319   skip_whitespace_but_not_newline(in);
320   if (!has_data(in)) return NULL;
321   string_tree* first = new string_tree(slurp_until(in, ':'));
322   if (!has_data(in)) return first;
323   string_tree* rest = parse_property_list(in);
324   if (!has_data(in) && rest->atom)
325     return new string_tree(first, new string_tree(rest, NULL));
326   return new string_tree(first, rest);
327 }
328 :(before "End Unit Tests")
329 void test_parse_property_list_atom() {
330   istringstream in("a");
331   string_tree* x = parse_property_list(in);
332   CHECK(x->atom);
333   delete x;
334 }
335 void test_parse_property_list_list() {
336   istringstream in("a:b");
337   string_tree* x = parse_property_list(in);
338   CHECK(!x->atom);
339   CHECK(x->left->atom);
340   CHECK_EQ(x->left->value, "a");
341   CHECK(!x->right->atom);
342   CHECK(x->right->left->atom);
343   CHECK_EQ(x->right->left->value, "b");
344   CHECK(x->right->right == NULL);
345   delete x;
346 }
347 
348 :(code)
349 type_tree* new_type_tree(const string_tree* properties) {
350   if (!properties) return NULL;
351   if (properties->atom) {
352     const string& type_name = properties->value;
353     int value = 0;
354     if (contains_key(Type_ordinal, type_name))
355       value = get(Type_ordinal, type_name);
356     else if (is_integer(type_name))  // sometimes types will contain literal integers, like for the size of an array
357       value = 0;
358     else if (properties->value == "->")  // used in recipe types
359       value = 0;
360     else
361       value = -1;  // should never happen; will trigger errors later
362     return new type_tree(type_name, value);
363   }
364   return new type_tree(new_type_tree(properties->left),
365                        new_type_tree(properties->right));
366 }
367 
368 //: avoid memory leaks for the type tree
369 
370 reagent::reagent(const reagent& other) {
371   original_string = other.original_string;
372   name = other.name;
373   value = other.value;
374   initialized = other.initialized;
375   for (int i = 0;  i < SIZE(other.properties);  ++i) {
376     properties.push_back(pair<string, string_tree*>(other.properties.at(i).first, copy(other.properties.at(i).second)));
377   }
378   type = copy(other.type);
379   // End reagent Copy Constructor
380 }
381 
382 type_tree::type_tree(const type_tree& original) {
383   atom = original.atom;
384   name = original.name;
385   value = original.value;
386   left = copy(original.left);
387   right = copy(original.right);
388 }
389 
390 type_tree& type_tree::operator=(const type_tree& original) {
391   atom = original.atom;
392   name = original.name;
393   value = original.value;
394   if (left) delete left;
395   left = copy(original.left);
396   if (right) delete right;
397   right = copy(original.right);
398   return *this;
399 }
400 
401 bool type_tree::operator==(const type_tree& other) const {
402   if (atom != other.atom) return false;
403   if (atom)
404     return name == other.name && value == other.value;
405   return (left == other.left || *left == *other.left)
406       && (right == other.right || *right == *other.right);
407 }
408 
409 // only constraint we care about: if a < b then !(b < a)
410 bool type_tree::operator<(const type_tree& other) const {
411   if (atom != other.atom) return atom > other.atom;  // atoms before non-atoms
412   if (atom) return value < other.value;
413   // first location in one that's missing in the other makes that side 'smaller'
414   if (left && !other.left) return false;
415   if (!left && other.left) return true;
416   if (right && !other.right) return false;
417   if (!right && other.right) return true;
418   // now if either pointer is unequal neither side can be null
419   // if one side is equal that's easy
420   if (left == other.left || *left == *other.left) return right && *right < *other.right;
421   if (right == other.right || *right == *other.right) return left && *left < *other.left;
422   // if the two sides criss-cross, pick the side with the smaller lhs
423   if ((left == other.right || *left == *other.right)
424       && (right == other.left || *right == *other.left))
425     return *left < *other.left;
426   // now the hard case: both sides are not equal
427   // make sure we stay consistent between (a < b) and (b < a)
428   // just return the side with the smallest of the 4 branches
429   if (*left < *other.left && *left < *other.right) return true;
430   if (*right < *other.left && *right < *other.right) return true;
431   return false;
432 }
433 :(before "End Unit Tests")
434 // These unit tests don't always use valid types.
435 void test_compare_atom_types() {
436   reagent a("a:address"), b("b:boolean");
437   CHECK(*a.type < *b.type);
438   CHECK(!(*b.type < *a.type));
439 }
440 void test_compare_equal_atom_types() {
441   reagent a("a:address"), b("b:address");
442   CHECK(!(*a.type < *b.type));
443   CHECK(!(*b.type < *a.type));
444 }
445 void test_compare_atom_with_non_atom() {
446   reagent a("a:address:number"), b("b:boolean");
447   CHECK(!(*a.type < *b.type));
448   CHECK(*b.type < *a.type);
449 }
450 void test_compare_lists_with_identical_structure() {
451   reagent a("a:address:address"), b("b:address:boolean");
452   CHECK(*a.type < *b.type);
453   CHECK(!(*b.type < *a.type));
454 }
455 void test_compare_identical_lists() {
456   reagent a("a:address:boolean"), b("b:address:boolean");
457   CHECK(!(*a.type < *b.type));
458   CHECK(!(*b.type < *a.type));
459 }
460 void test_compare_list_with_extra_element() {
461   reagent a("a:address:address"), b("b:address:address:number");
462   CHECK(*a.type < *b.type);
463   CHECK(!(*b.type < *a.type));
464 }
465 void test_compare_list_with_smaller_left_but_larger_right() {
466   reagent a("a:number:character"), b("b:boolean:array");
467   CHECK(*a.type < *b.type);
468   CHECK(!(*b.type < *a.type));
469 }
470 void test_compare_list_with_smaller_left_but_larger_right_identical_types() {
471   reagent a("a:address:boolean"), b("b:boolean:address");
472   CHECK(*a.type < *b.type);
473   CHECK(!(*b.type < *a.type));
474 }
475 
476 :(code)
477 string_tree::string_tree(const string_tree& original) {
478   atom = original.atom;
479   value = original.value;
480   left = copy(original.left);
481   right = copy(original.right);
482 }
483 
484 reagent& reagent::operator=(const reagent& other) {
485   original_string = other.original_string;
486   for (int i = 0;  i < SIZE(properties);  ++i)
487     if (properties.at(i).second) delete properties.at(i).second;
488   properties.clear();
489   for (int i = 0;  i < SIZE(other.properties);  ++i)
490     properties.push_back(pair<string, string_tree*>(other.properties.at(i).first, copy(other.properties.at(i).second)));
491   name = other.name;
492   value = other.value;
493   initialized = other.initialized;
494   if (type) delete type;
495   type = copy(other.type);
496   // End reagent Copy Operator
497   return *this;
498 }
499 
500 reagent::~reagent() {
501   clear();
502 }
503 
504 void reagent::clear() {
505   for (int i = 0;  i < SIZE(properties);  ++i) {
506     if (properties.at(i).second) {
507       delete properties.at(i).second;
508       properties.at(i).second = NULL;
509     }
510   }
511   delete type;
512   type = NULL;
513 }
514 type_tree::~type_tree() {
515   delete left;
516   delete right;
517 }
518 string_tree::~string_tree() {
519   delete left;
520   delete right;
521 }
522 
523 void append(type_tree*& base, type_tree* extra) {
524   if (!base) {
525     base = extra;
526     return;
527   }
528   type_tree* curr = base;
529   while (curr->right) curr = curr->right;
530   curr->right = extra;
531 }
532 
533 void append(string_tree*& base, string_tree* extra) {
534   if (!base) {
535     base = extra;
536     return;
537   }
538   string_tree* curr = base;
539   while (curr->right) curr = curr->right;
540   curr->right = extra;
541 }
542 
543 string slurp_until(istream& in, char delim) {
544   ostringstream out;
545   char c;
546   while (in >> c) {
547     if (c == delim) {
548       // drop the delim
549       break;
550     }
551     out << c;
552   }
553   return out.str();
554 }
555 
556 bool has_property(const reagent& x, const string& name) {
557   for (int i = 0;  i < SIZE(x.properties);  ++i) {
558     if (x.properties.at(i).first == name) return true;
559   }
560   return false;
561 }
562 
563 string_tree* property(const reagent& r, const string& name) {
564   for (int p = 0;  p != SIZE(r.properties);  ++p) {
565     if (r.properties.at(p).first == name)
566       return r.properties.at(p).second;
567   }
568   return NULL;
569 }
570 
571 string_tree* copy(const string_tree* x) {
572   if (x == NULL) return NULL;
573   return new string_tree(*x);
574 }
575 
576 type_tree* copy(const type_tree* x) {
577   if (x == NULL) return NULL;
578   return new type_tree(*x);
579 }
580 
581 :(before "End Globals")
582 extern const string Ignore(",");  // commas are ignored in Mu except within [] strings
583 :(code)
584 void skip_whitespace_but_not_newline(istream& in) {
585   while (true) {
586     if (!has_data(in)) break;
587     else if (in.peek() == '\n') break;
588     else if (isspace(in.peek())) in.get();
589     else if (Ignore.find(in.peek()) != string::npos) in.get();
590     else break;
591   }
592 }
593 
594 void dump_memory() {
595   for (map<int, double>::iterator p = Memory.begin();  p != Memory.end();  ++p) {
596     cerr << p->first << ": " << no_scientific(p->second) << '\n';
597   }
598 }
599 
600 //:: Helpers for converting various values to string
601 //: Use to_string() in trace(), and try to keep it stable from run to run.
602 //: Use debug_string() while debugging, and throw everything into it.
603 //: Use inspect() only for emitting a canonical format that can be parsed back
604 //: into the value.
605 
606 string to_string(const recipe& r) {
607   ostringstream out;
608   out << "recipe " << r.name << " [\n";
609   for (int i = 0;  i < SIZE(r.steps);  ++i)
610     out << "  " << to_string(r.steps.at(i)) << '\n';
611   out << "]\n";
612   return out.str();
613 }
614 
615 string to_original_string(const recipe& r) {
616   ostringstream out;
617   out << "recipe " << r.name << " [\n";
618   for (int i = 0;  i < SIZE(r.steps);  ++i)
619     out << "  " << to_original_string(r.steps.at(i)) << '\n';
620   out << "]\n";
621   return out.str();
622 }
623 
624 string debug_string(const recipe& x) {
625   ostringstream out;
626   out << "- recipe " << x.name << '\n';
627   // Begin debug_string(recipe x)
628   for (int index = 0;  index < SIZE(x.steps);  ++index) {
629     const instruction& inst = x.steps.at(index);
630     out << "inst: " << to_string(inst) << '\n';
631     out << "  ingredients\n";
632     for (int i = 0;  i < SIZE(inst.ingredients);  ++i)
633       out << "    " << debug_string(inst.ingredients.at(i)) << '\n';
634     out << "  products\n";
635     for (int i = 0;  i < SIZE(inst.products);  ++i)
636       out << "    " << debug_string(inst.products.at(i)) << '\n';
637   }
638   return out.str();
639 }
640 
641 string to_original_string(const instruction& inst) {
642   if (inst.is_label) return inst.label;
643   if (!inst.original_string.empty()) return inst.original_string;
644   ostringstream out;
645   for (int i = 0;  i < SIZE(inst.products);  ++i) {
646     if (i > 0) out << ", ";
647     out << inst.products.at(i).original_string;
648   }
649   if (!inst.products.empty()) out << " <- ";
650   out << inst.name;
651   if (!inst.ingredients.empty()) out << ' ';
652   for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
653     if (i > 0) out << ", ";
654     out << inst.ingredients.at(i).original_string;
655   }
656   return out.str();
657 }
658 
659 string to_string(const instruction& inst) {
660   if (inst.is_label) return inst.label;
661   ostringstream out;
662   for (int i = 0;  i < SIZE(inst.products);  ++i) {
663     if (i > 0) out << ", ";
664     out << to_string(inst.products.at(i));
665   }
666   if (!inst.products.empty()) out << " <- ";
667   out << inst.name << ' ';
668   for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
669     if (i > 0) out << ", ";
670     out << to_string(inst.ingredients.at(i));
671   }
672   return out.str();
673 }
674 
675 string to_string(const reagent& r) {
676   if (is_dummy(r)) return "_";
677   ostringstream out;
678   out << "{";
679   out << r.name << ": " << names_to_string(r.type);
680   if (!r.properties.empty()) {
681     for (int i = 0;  i < SIZE(r.properties);  ++i)
682       out << ", \"" << r.properties.at(i).first << "\": " << to_string(r.properties.at(i).second);
683   }
684   out << "}";
685   return out.str();
686 }
687 
688 // special name for ignoring some products
689 bool is_dummy(const reagent& x) {
690   return x.name == "_";
691 }
692 
693 string debug_string(const reagent& x) {
694   ostringstream out;
695   out << x.name << ": " << x.value << ' ' << to_string(x.type) << " -- " << to_string(x);
696   return out.str();
697 }
698 
699 string to_string(const string_tree* property) {
700   if (!property) return "()";
701   ostringstream out;
702   dump(property, out);
703   return out.str();
704 }
705 
706 void dump(const string_tree* x, ostream& out) {
707   if (!x) return;
708   if (x->atom) {
709     out << '"' << x->value << '"';
710     return;
711   }
712   out << '(';
713   const string_tree* curr = x;
714   while (curr && !curr->atom) {
715     dump(curr->left, out);
716     if (curr->right) out << ' ';
717     curr = curr->right;
718   }
719   // check for dotted list; should never happen
720   if (curr) {
721     out << ". ";
722     dump(curr, out);
723   }
724   out << ')';
725 }
726 
727 string to_string(const type_tree* type) {
728   if (type == NULL) return "()";
729   ostringstream out;
730   dump(type, out);
731   return out.str();
732 }
733 
734 void dump(const type_tree* x, ostream& out) {
735   if (!x) return;
736   if (x->atom) {
737     dump(x->value, out);
738     return;
739   }
740   out << '(';
741   const type_tree* curr = x;
742   while (curr && !curr->atom) {
743     dump(curr->left, out);
744     if (curr->right) out << ' ';
745     curr = curr->right;
746   }
747   // check for dotted list; should never happen
748   if (curr) {
749     out << ". ";
750     dump(curr, out);
751   }
752   out << ')';
753 }
754 
755 void dump(type_ordinal type, ostream& out) {
756   if (contains_key(Type, type))
757     out << get(Type, type).name;
758   else
759     out << "?" << type;
760 }
761 
762 string names_to_string(const type_tree* type) {
763   if (type == NULL) return "()";  // should never happen
764   ostringstream out;
765   dump_names(type, out);
766   return out.str();
767 }
768 
769 void dump_names(const type_tree* x, ostream& out) {
770   if (!x) return;
771   if (x->atom) {
772     out << '"' << x->name << '"';
773     return;
774   }
775   out << '(';
776   const type_tree* curr = x;
777   while (curr && !curr->atom) {
778     dump_names(curr->left, out);
779     if (curr->right) out << ' ';
780     curr = curr->right;
781   }
782   // check for dotted list; should never happen
783   if (curr) {
784     out << ". ";
785     dump_names(curr, out);
786   }
787   out << ')';
788 }
789 
790 string names_to_string_without_quotes(const type_tree* type) {
791   if (type == NULL) return "()";
792   ostringstream out;
793   dump_names_without_quotes(type, out);
794   return out.str();
795 }
796 
797 void dump_names_without_quotes(const type_tree* x, ostream& out) {
798   if (!x) return;
799   if (x->atom) {
800     out << x->name;
801     return;
802   }
803   out << '(';
804   const type_tree* curr = x;
805   while (curr && !curr->atom) {
806     dump_names_without_quotes(curr->left, out);
807     if (curr->right) out << ' ';
808     curr = curr->right;
809   }
810   // check for dotted list; should never happen
811   if (curr) {
812     out << ". ";
813     dump_names_without_quotes(curr, out);
814   }
815   out << ')';
816 }
817 
818 bool is_integer(const string& s) {
819   return s.find_first_not_of("0123456789-") == string::npos  // no other characters
820       && s.find_first_of("0123456789") != string::npos  // at least one digit
821       && s.find('-', 1) == string::npos;  // '-' only at first position
822 }
823 
824 int to_integer(string n) {
825   char* end = NULL;
826   // safe because string.c_str() is guaranteed to be null-terminated
827   int result = strtoll(n.c_str(), &end, /*any base*/0);
828   if (*end != '\0') cerr << "tried to convert " << n << " to number\n";
829   assert(*end == '\0');
830   return result;
831 }
832 
833 void test_is_integer() {
834   CHECK(is_integer("1234"));
835   CHECK(is_integer("-1"));
836   CHECK(!is_integer("234.0"));
837   CHECK(is_integer("-567"));
838   CHECK(!is_integer("89-0"));
839   CHECK(!is_integer("-"));
840   CHECK(!is_integer("1e3"));  // not supported
841 }
842 
843 //: helper to print numbers without excessive precision
844 
845 :(before "End Types")
846 struct no_scientific {
847   double x;
848   explicit no_scientific(double y) :x(y) {}
849 };
850 
851 :(code)
852 ostream& operator<<(ostream& os, no_scientific x) {
853   if (!isfinite(x.x)) {
854     // Infinity or NaN
855     os << x.x;
856     return os;
857   }
858   ostringstream tmp;
859   // more accurate, but too slow
860 //?   tmp.precision(308);  // for 64-bit numbers
861   tmp << std::fixed << x.x;
862   os << trim_floating_point(tmp.str());
863   return os;
864 }
865 
866 string trim_floating_point(const string& in) {
867   if (in.empty()) return "";
868   if (in.find('.') == string::npos) return in;
869   int length = SIZE(in);
870   while (length > 1) {
871     if (in.at(length-1) != '0') break;
872     --length;
873   }
874   if (in.at(length-1) == '.') --length;
875   if (length == 0) return "0";
876   return in.substr(0, length);
877 }
878 
879 void test_trim_floating_point() {
880   CHECK_EQ(trim_floating_point(""), "");
881   CHECK_EQ(trim_floating_point(".0"), "0");
882   CHECK_EQ(trim_floating_point("1.5000"), "1.5");
883   CHECK_EQ(trim_floating_point("1.000001"), "1.000001");
884   CHECK_EQ(trim_floating_point("23.000000"), "23");
885   CHECK_EQ(trim_floating_point("23.0"), "23");
886   CHECK_EQ(trim_floating_point("23."), "23");
887   CHECK_EQ(trim_floating_point("23"), "23");
888   CHECK_EQ(trim_floating_point("230"), "230");
889   CHECK_EQ(trim_floating_point("3.000000"), "3");
890   CHECK_EQ(trim_floating_point("3.0"), "3");
891   CHECK_EQ(trim_floating_point("3."), "3");
892   CHECK_EQ(trim_floating_point("3"), "3");
893 }
894 
895 :(before "End Includes")
896 #include <map>
897 using std::map;
898 #include <utility>
899 using std::pair;
900 #include <math.h>