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.
14 typedef int recipe_ordinal
;
17 // Recipes are lists of instructions. To perform or 'run' a recipe, the
18 // computer runs its instructions.
20 recipe_ordinal ordinal
;
22 vector
<instruction
> steps
;
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
32 // Labels don't do anything, they're just named locations in a recipe.
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
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.
53 string original_string
;
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
60 reagent(const string
& s
);
61 reagent() :type(NULL
), value(0), initialized(false) {}
62 reagent(type_tree
* t
) :type(t
), value(0), initialized(false) {}
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))
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
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); }
95 string value
; // only if atom
96 string_tree
* left
; // only if !atom
97 string_tree
* right
; // only if !atom
99 string_tree(const string_tree
& original
);
101 explicit string_tree(string v
) :atom(true), value(v
), left(NULL
), right(NULL
) {}
103 string_tree(string_tree
* l
, string_tree
* r
) :atom(false), left(l
), right(r
) {}
106 // End type_tree Definition
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")
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'
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;
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")
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.
187 vector
<reagent
> elements
;
188 // End type_info Fields
189 type_info() :kind(PRIMITIVE
) {
190 // End type_info Constructor
194 enum primitive_recipes
{
197 // End Primitive Recipe Declarations
198 MAX_PRIMITIVE_RECIPES
,
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
216 :(before
"End One-time Setup")
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
++);
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")
238 :(before
"End Reset")
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
267 // End recipe Constructor
270 instruction::instruction() :is_label(false), operation(IDLE
) {
271 // End instruction Constructor
273 void instruction::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)
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
);
299 if (is_integer(name
) && type
== NULL
)
300 type
= new type_tree("literal");
301 if (name
== "_" && type
== NULL
)
302 type
= new type_tree("literal");
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
);
335 void test_parse_property_list_list() {
336 istringstream
in("a:b");
337 string_tree
* x
= parse_property_list(in
);
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
);
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
;
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
358 else if (properties
->value
== "->") // used in recipe types
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
;
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
);
401 bool type_tree::operator==(const type_tree
& other
) const {
402 if (atom
!= other
.atom
) return false;
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;
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
));
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
;
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
)));
493 initialized
= other
.initialized
;
494 if (type
) delete type
;
495 type
= copy(other
.type
);
496 // End reagent Copy Operator
500 reagent::~reagent() {
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
;
514 type_tree::~type_tree() {
518 string_tree::~string_tree() {
523 void append(type_tree
*& base
, type_tree
* extra
) {
528 type_tree
* curr
= base
;
529 while (curr
->right
) curr
= curr
->right
;
533 void append(string_tree
*& base
, string_tree
* extra
) {
538 string_tree
* curr
= base
;
539 while (curr
->right
) curr
= curr
->right
;
543 string
slurp_until(istream
& in
, char delim
) {
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;
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
;
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
584 void skip_whitespace_but_not_newline(istream
& in
) {
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();
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
606 string
to_string(const recipe
& r
) {
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';
615 string
to_original_string(const recipe
& r
) {
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';
624 string
debug_string(const recipe
& x
) {
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';
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
;
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
<< " <- ";
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
;
659 string
to_string(const instruction
& inst
) {
660 if (inst
.is_label
) return inst
.label
;
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
));
675 string
to_string(const reagent
& r
) {
676 if (is_dummy(r
)) return "_";
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
);
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
) {
695 out
<< x
.name
<< ": " << x
.value
<< ' ' << to_string(x
.type
) << " -- " << to_string(x
);
699 string
to_string(const string_tree
* property
) {
700 if (!property
) return "()";
706 void dump(const string_tree
* x
, ostream
& out
) {
709 out
<< '"' << x
->value
<< '"';
713 const string_tree
* curr
= x
;
714 while (curr
&& !curr
->atom
) {
715 dump(curr
->left
, out
);
716 if (curr
->right
) out
<< ' ';
719 // check for dotted list; should never happen
727 string
to_string(const type_tree
* type
) {
728 if (type
== NULL
) return "()";
734 void dump(const type_tree
* x
, ostream
& out
) {
741 const type_tree
* curr
= x
;
742 while (curr
&& !curr
->atom
) {
743 dump(curr
->left
, out
);
744 if (curr
->right
) out
<< ' ';
747 // check for dotted list; should never happen
755 void dump(type_ordinal type
, ostream
& out
) {
756 if (contains_key(Type
, type
))
757 out
<< get(Type
, type
).name
;
762 string
names_to_string(const type_tree
* type
) {
763 if (type
== NULL
) return "()"; // should never happen
765 dump_names(type
, out
);
769 void dump_names(const type_tree
* x
, ostream
& out
) {
772 out
<< '"' << x
->name
<< '"';
776 const type_tree
* curr
= x
;
777 while (curr
&& !curr
->atom
) {
778 dump_names(curr
->left
, out
);
779 if (curr
->right
) out
<< ' ';
782 // check for dotted list; should never happen
785 dump_names(curr
, out
);
790 string
names_to_string_without_quotes(const type_tree
* type
) {
791 if (type
== NULL
) return "()";
793 dump_names_without_quotes(type
, out
);
797 void dump_names_without_quotes(const type_tree
* x
, ostream
& 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
<< ' ';
810 // check for dotted list; should never happen
813 dump_names_without_quotes(curr
, 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
) {
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');
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
{
848 explicit no_scientific(double y
) :x(y
) {}
852 ostream
& operator<<(ostream
& os
, no_scientific x
) {
853 if (!isfinite(x
.x
)) {
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());
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
);
871 if (in
.at(length
-1) != '0') break;
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")