fix other mandelbrot variants
[mu.git] / archive / 1.vm / 019type_abbreviations.cc
blob35cc5d90f5dd48c1698ba611fefd3c1cb024b423
1 //: For convenience, allow Mu types to be abbreviated.
3 void test_type_abbreviations() {
4 transform(
5 "type foo = number\n"
6 "def main [\n"
7 " a:foo <- copy 34\n"
8 "]\n"
9 );
10 CHECK_TRACE_CONTENTS(
11 "transform: product type after expanding abbreviations: \"number\"\n"
15 :(before "End Globals")
16 map<string, type_tree*> Type_abbreviations, Type_abbreviations_snapshot;
18 //:: Defining type abbreviations.
20 :(before "End Command Handlers")
21 else if (command == "type") {
22 load_type_abbreviations(in);
25 :(code)
26 void load_type_abbreviations(istream& in) {
27 string new_type_name = next_word(in);
28 assert(has_data(in) || !new_type_name.empty());
29 if (!has_data(in) || new_type_name.empty()) {
30 raise << "incomplete 'type' statement; must be of the form 'type <new type name> = <type expression>'\n" << end();
31 return;
33 string arrow = next_word(in);
34 assert(has_data(in) || !arrow.empty());
35 if (arrow.empty()) {
36 raise << "incomplete 'type' statement 'type " << new_type_name << "'\n" << end();
37 return;
39 if (arrow != "=") {
40 raise << "'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type " << new_type_name << ' ' << arrow << "'\n" << end();
41 return;
43 if (!has_data(in)) {
44 raise << "incomplete 'type' statement 'type " << new_type_name << " ='\n" << end();
45 return;
47 string old = next_word(in);
48 if (old.empty()) {
49 raise << "incomplete 'type' statement 'type " << new_type_name << " ='\n" << end();
50 raise << "'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type " << new_type_name << ' ' << arrow << "'\n" << end();
51 return;
53 if (contains_key(Type_abbreviations, new_type_name)) {
54 raise << "'type' conflict: '" << new_type_name << "' defined as both '" << names_to_string_without_quotes(get(Type_abbreviations, new_type_name)) << "' and '" << old << "'\n" << end();
55 return;
57 trace(100, "type") << "alias " << new_type_name << " = " << old << end();
58 type_tree* old_type = new_type_tree(old);
59 put(Type_abbreviations, new_type_name, old_type);
62 type_tree* new_type_tree(const string& x) {
63 string_tree* type_names = starts_with(x, "(") ? parse_string_tree(x) : parse_string_list(x);
64 type_tree* result = new_type_tree(type_names);
65 delete type_names;
66 expand_type_abbreviations(result);
67 return result;
70 string_tree* parse_string_list(const string& s) {
71 istringstream in(s);
72 in >> std::noskipws;
73 return parse_property_list(in);
76 void test_type_error1() {
77 Hide_errors = true;
78 transform(
79 "type foo\n"
81 CHECK_TRACE_CONTENTS(
82 "error: incomplete 'type' statement 'type foo'\n"
86 void test_type_error2() {
87 Hide_errors = true;
88 transform(
89 "type foo =\n"
91 CHECK_TRACE_CONTENTS(
92 "error: incomplete 'type' statement 'type foo ='\n"
96 void test_type_error3() {
97 Hide_errors = true;
98 transform(
99 "type foo bar baz\n"
101 CHECK_TRACE_CONTENTS(
102 "error: 'type' statements must be of the form 'type <new type name> = <type expression>' but got 'type foo bar'\n"
106 void test_type_conflict_error() {
107 Hide_errors = true;
108 transform(
109 "type foo = bar\n"
110 "type foo = baz\n"
112 CHECK_TRACE_CONTENTS(
113 "error: 'type' conflict: 'foo' defined as both 'bar' and 'baz'\n"
117 void test_type_abbreviation_for_compound() {
118 transform(
119 "type foo = address:number\n"
120 "def main [\n"
121 " 1:foo <- copy null\n"
122 "]\n"
124 CHECK_TRACE_CONTENTS(
125 "transform: product type after expanding abbreviations: (\"address\" \"number\")\n"
129 //: cleaning up type abbreviations between tests and before exiting
131 :(before "End save_snapshots")
132 Type_abbreviations_snapshot = Type_abbreviations;
133 :(before "End restore_snapshots")
134 restore_type_abbreviations();
135 :(before "End One-time Setup")
136 atexit(clear_type_abbreviations);
137 :(code)
138 void restore_type_abbreviations() {
139 for (map<string, type_tree*>::iterator p = Type_abbreviations.begin(); p != Type_abbreviations.end(); ++p) {
140 if (!contains_key(Type_abbreviations_snapshot, p->first))
141 delete p->second;
143 Type_abbreviations.clear();
144 Type_abbreviations = Type_abbreviations_snapshot;
146 void clear_type_abbreviations() {
147 for (map<string, type_tree*>::iterator p = Type_abbreviations.begin(); p != Type_abbreviations.end(); ++p)
148 delete p->second;
149 Type_abbreviations.clear();
152 //:: A few default abbreviations.
154 :(before "End Mu Types Initialization")
155 put(Type_abbreviations, "&", new_type_tree("address"));
156 put(Type_abbreviations, "@", new_type_tree("array"));
157 put(Type_abbreviations, "num", new_type_tree("number"));
158 put(Type_abbreviations, "bool", new_type_tree("boolean"));
159 put(Type_abbreviations, "char", new_type_tree("character"));
161 :(code)
162 void test_use_type_abbreviations_when_declaring_type_abbreviations() {
163 transform(
164 "type foo = &:num\n"
165 "def main [\n"
166 " 1:foo <- copy null\n"
167 "]\n"
169 CHECK_TRACE_CONTENTS(
170 "transform: product type after expanding abbreviations: (\"address\" \"number\")\n"
174 //:: Expand type aliases before running.
175 //: We'll do this in a transform so that we don't need to define abbreviations
176 //: before we use them.
178 void test_abbreviations_for_address_and_array() {
179 transform(
180 "def main [\n"
181 " f 1:&:num\n" // abbreviation for 'address:number'
182 " f 2:@:num\n" // abbreviation for 'array:number'
183 " f 3:&:@:num\n" // combining '&' and '@'
184 " f 4:&:&:@:&:@:num\n" // ..any number of times
185 " f {5: (array (& num) 3)}\n" // support for dilated reagents and more complex parse trees
186 "]\n"
187 "def f [\n"
188 "]\n"
190 CHECK_TRACE_CONTENTS(
191 "transform: --- expand type abbreviations in recipe 'main'\n"
192 "transform: ingredient type after expanding abbreviations: (\"address\" \"number\")\n"
193 "transform: ingredient type after expanding abbreviations: (\"array\" \"number\")\n"
194 "transform: ingredient type after expanding abbreviations: (\"address\" \"array\" \"number\")\n"
195 "transform: ingredient type after expanding abbreviations: (\"address\" \"address\" \"array\" \"address\" \"array\" \"number\")\n"
196 "transform: ingredient type after expanding abbreviations: (\"array\" (\"address\" \"number\") \"3\")\n"
200 :(before "Transform.push_back(update_instruction_operations)")
201 Transform.push_back(expand_type_abbreviations); // idempotent
202 // Begin Type Modifying Transforms
203 // End Type Modifying Transforms
205 :(code)
206 void expand_type_abbreviations(const recipe_ordinal r) {
207 expand_type_abbreviations(get(Recipe, r));
210 void expand_type_abbreviations(const recipe& caller) {
211 trace(101, "transform") << "--- expand type abbreviations in recipe '" << caller.name << "'" << end();
212 for (int i = 0; i < SIZE(caller.steps); ++i) {
213 const instruction& inst = caller.steps.at(i);
214 trace(102, "transform") << "instruction '" << to_original_string(inst) << end();
215 for (long int i = 0; i < SIZE(inst.ingredients); ++i) {
216 expand_type_abbreviations(inst.ingredients.at(i).type);
217 trace(102, "transform") << "ingredient type after expanding abbreviations: " << names_to_string(inst.ingredients.at(i).type) << end();
219 for (long int i = 0; i < SIZE(inst.products); ++i) {
220 expand_type_abbreviations(inst.products.at(i).type);
221 trace(102, "transform") << "product type after expanding abbreviations: " << names_to_string(inst.products.at(i).type) << end();
224 // End Expand Type Abbreviations(caller)
227 void expand_type_abbreviations(type_tree* type) {
228 if (!type) return;
229 if (!type->atom) {
230 expand_type_abbreviations(type->left);
231 expand_type_abbreviations(type->right);
232 return;
234 if (contains_key(Type_abbreviations, type->name))
235 *type = type_tree(*get(Type_abbreviations, type->name));