fix other mandelbrot variants
[mu.git] / archive / 1.vm / 010vm.cc
blob42b867fcf8d40a85407077775f766384ed901818
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;
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;
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();
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();
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; }
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); }
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) {}
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) {}
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();
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.
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
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();
161 Type_ordinal.clear();
163 :(before "End One-time Setup")
164 setup_types();
165 atexit(teardown_types);
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.)
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
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
194 enum primitive_recipes {
195 IDLE = 0,
196 COPY,
197 // End Primitive Recipe Declarations
198 MAX_PRIMITIVE_RECIPES,
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
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
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();
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
250 void restore_snapshots() {
251 Recipe = Recipe_snapshot;
252 Recipe_ordinal = Recipe_ordinal_snapshot;
253 restore_non_recipe_snapshots();
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
262 //:: Helpers
264 :(code)
265 recipe::recipe() {
266 ordinal = -1;
267 // End recipe Constructor
270 instruction::instruction() :is_label(false), operation(IDLE) {
271 // End instruction Constructor
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
283 bool instruction::is_empty() { return !is_label && name.empty(); }
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
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));
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);
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;
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;
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);
364 return new type_tree(new_type_tree(properties->left),
365 new_type_tree(properties->right));
368 //: avoid memory leaks for the type tree
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)));
378 type = copy(other.type);
379 // End reagent Copy Constructor
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);
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;
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);
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;
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));
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));
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);
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));
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));
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));
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));
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));
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);
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;
500 reagent::~reagent() {
501 clear();
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;
511 delete type;
512 type = NULL;
514 type_tree::~type_tree() {
515 delete left;
516 delete right;
518 string_tree::~string_tree() {
519 delete left;
520 delete right;
523 void append(type_tree*& base, type_tree* extra) {
524 if (!base) {
525 base = extra;
526 return;
528 type_tree* curr = base;
529 while (curr->right) curr = curr->right;
530 curr->right = extra;
533 void append(string_tree*& base, string_tree* extra) {
534 if (!base) {
535 base = extra;
536 return;
538 string_tree* curr = base;
539 while (curr->right) curr = curr->right;
540 curr->right = extra;
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;
551 out << c;
553 return out.str();
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;
560 return false;
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;
568 return NULL;
571 string_tree* copy(const string_tree* x) {
572 if (x == NULL) return NULL;
573 return new string_tree(*x);
576 type_tree* copy(const type_tree* x) {
577 if (x == NULL) return NULL;
578 return new type_tree(*x);
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;
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';
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.
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();
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();
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';
638 return out.str();
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;
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;
656 return out.str();
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));
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));
672 return out.str();
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);
684 out << "}";
685 return out.str();
688 // special name for ignoring some products
689 bool is_dummy(const reagent& x) {
690 return x.name == "_";
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();
699 string to_string(const string_tree* property) {
700 if (!property) return "()";
701 ostringstream out;
702 dump(property, out);
703 return out.str();
706 void dump(const string_tree* x, ostream& out) {
707 if (!x) return;
708 if (x->atom) {
709 out << '"' << x->value << '"';
710 return;
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;
719 // check for dotted list; should never happen
720 if (curr) {
721 out << ". ";
722 dump(curr, out);
724 out << ')';
727 string to_string(const type_tree* type) {
728 if (type == NULL) return "()";
729 ostringstream out;
730 dump(type, out);
731 return out.str();
734 void dump(const type_tree* x, ostream& out) {
735 if (!x) return;
736 if (x->atom) {
737 dump(x->value, out);
738 return;
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;
747 // check for dotted list; should never happen
748 if (curr) {
749 out << ". ";
750 dump(curr, out);
752 out << ')';
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;
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();
769 void dump_names(const type_tree* x, ostream& out) {
770 if (!x) return;
771 if (x->atom) {
772 out << '"' << x->name << '"';
773 return;
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;
782 // check for dotted list; should never happen
783 if (curr) {
784 out << ". ";
785 dump_names(curr, out);
787 out << ')';
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();
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;
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;
810 // check for dotted list; should never happen
811 if (curr) {
812 out << ". ";
813 dump_names_without_quotes(curr, out);
815 out << ')';
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
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;
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
843 //: helper to print numbers without excessive precision
845 :(before "End Types")
846 struct no_scientific {
847 double x;
848 explicit no_scientific(double y) :x(y) {}
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;
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;
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;
874 if (in.at(length-1) == '.') --length;
875 if (length == 0) return "0";
876 return in.substr(0, length);
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");
895 :(before "End Includes")
896 #include <map>
897 using std::map;
898 #include <utility>
899 using std::pair;
900 #include <math.h>