1
2
3
4
5 :(after "Update GET base_type in Check")
6 base_type = get_base_type(base_type);
7 :(after "Update GET base_type in Run")
8 base_type = get_base_type(base_type);
9 :(after "Update PUT base_type in Check")
10 base_type = get_base_type(base_type);
11 :(after "Update PUT base_type in Run")
12 base_type = get_base_type(base_type);
13 :(after "Update MAYBE_CONVERT base_type in Check")
14 base_type = get_base_type(base_type);
15 :(after "Update base_type in size_of(type)")
16 base_type = get_base_type(base_type);
17 :(after "Update base_type in element_type")
18 base_type = get_base_type(base_type);
19
20
21
22
23
24
25 :(after "Update base_type in skip_addresses")
26 base_type = get_base_type(base_type);
27 :(replace{} "const type_tree* get_base_type(const type_tree* t)")
28 const type_tree* get_base_type(const type_tree* t) {
29 const type_tree* result = t->atom ? t : t->left;
30 if (!result->atom)
31 raise << "invalid type " << to_string(t) << '\n' << end();
32 return result;
33 }
34
35 :(scenario ill_formed_container)
36 % Hide_errors = true;
37 def main [
38 {1: ((foo) num)} <- copy 0
39 ]
40
41
42 :(scenario size_of_shape_shifting_container)
43 container foo:_t [
44 x:_t
45 y:num
46 ]
47 def main [
48 1:foo:num <- merge 12, 13
49 3:foo:point <- merge 14, 15, 16
50 ]
51 +mem: storing 12 in location 1
52 +mem: storing 13 in location 2
53 +mem: storing 14 in location 3
54 +mem: storing 15 in location 4
55 +mem: storing 16 in location 5
56
57 :(scenario size_of_shape_shifting_container_2)
58
59 container foo:_a:_b [
60 x:_a
61 y:_b
62 ]
63 def main [
64 1:foo:num:bool <- merge 34, 1/true
65 ]
66 $error: 0
67
68 :(scenario size_of_shape_shifting_container_3)
69 container foo:_a:_b [
70 x:_a
71 y:_b
72 ]
73 def main [
74 1:text <- new [abc]
75
76 {2: (foo number (address array character))} <- merge 34/x, 1:text/y
77 ]
78 $error: 0
79
80 :(scenario size_of_shape_shifting_container_4)
81 container foo:_a:_b [
82 x:_a
83 y:_b
84 ]
85 container bar:_a:_b [
86
87 {data: (foo _a (address _b))}
88 ]
89 def main [
90 1:text <- new [abc]
91 2:bar:num:@:char <- merge 34/x, 1:text/y
92 ]
93 $error: 0
94
95 :(scenario shape_shifting_container_extend)
96 container foo:_a [
97 x:_a
98 ]
99 container foo:_a [
100 y:_a
101 ]
102 $error: 0
103
104 :(scenario shape_shifting_container_extend_error)
105 % Hide_errors = true;
106 container foo:_a [
107 x:_a
108 ]
109 container foo:_b [
110 y:_b
111 ]
112 +error: headers of container 'foo' must use identical type ingredients
113
114 :(scenario type_ingredient_must_start_with_underscore)
115 % Hide_errors = true;
116 container foo:t [
117 x:num
118 ]
119 +error: foo: type ingredient 't' must begin with an underscore
120
121 :(before "End Globals")
122
123
124
125
126
127 extern const int START_TYPE_INGREDIENTS = 2000;
128 :(before "End Commandline Parsing")
129 assert(Next_type_ordinal < START_TYPE_INGREDIENTS);
130
131 :(before "End type_info Fields")
132 map<string, type_ordinal> type_ingredient_names;
133
134
135
136 :(before "Check Container Field Types(info)")
137 if (!info.type_ingredient_names.empty()) continue;
138
139 :(before "End container Name Refinements")
140 if (name.find(':') != string::npos) {
141 trace("parse") << "container has type ingredients; parsing" << end();
142 if (!read_type_ingredients(name, command)) {
143
144 slurp_balanced_bracket(in);
145 return;
146 }
147 }
148
149 :(code)
150 bool read_type_ingredients(string& name, const string& command) {
151 string save_name = name;
152 istringstream in(save_name);
153 name = slurp_until(in, ':');
154 map<string, type_ordinal> type_ingredient_names;
155 if (!slurp_type_ingredients(in, type_ingredient_names, name)) {
156 return false;
157 }
158 if (contains_key(Type_ordinal, name)
159 && contains_key(Type, get(Type_ordinal, name))) {
160 const type_info& previous_info = get(Type, get(Type_ordinal, name));
161
162 if (!type_ingredients_match(type_ingredient_names, previous_info.type_ingredient_names)) {
163 raise << "headers of " << command << " '" << name << "' must use identical type ingredients\n" << end();
164 return false;
165 }
166 return true;
167 }
168
169 if (!contains_key(Type_ordinal, name) || get(Type_ordinal, name) == 0)
170 put(Type_ordinal, name, Next_type_ordinal++);
171 type_info& info = get_or_insert(Type, get(Type_ordinal, name));
172 info.type_ingredient_names.swap(type_ingredient_names);
173 return true;
174 }
175
176 bool slurp_type_ingredients(istream& in, map<string, type_ordinal>& out, const string& container_name) {
177 int next_type_ordinal = START_TYPE_INGREDIENTS;
178 while (has_data(in)) {
179 string curr = slurp_until(in, ':');
180 if (curr.empty()) {
181 raise << container_name << ": empty type ingredients not permitted\n" << end();
182 return false;
183 }
184 if (!starts_with(curr, "_")) {
185 raise << container_name << ": type ingredient '" << curr << "' must begin with an underscore\n" << end();
186 return false;
187 }
188 if (out.find(curr) != out.end()) {
189 raise << container_name << ": can't repeat type ingredient name'" << curr << "' in a single container definition\n" << end();
190 return false;
191 }
192 put(out, curr, next_type_ordinal++);
193 }
194 return true;
195 }
196
197 bool type_ingredients_match(const map<string, type_ordinal>& a, const map<string, type_ordinal>& b) {
198 if (SIZE(a) != SIZE(b)) return false;
199 for (map<string, type_ordinal>::const_iterator p = a.begin(); p != a.end(); ++p) {
200 if (!contains_key(b, p->first)) return false;
201 if (p->second != get(b, p->first)) return false;
202 }
203 return true;
204 }
205
206 :(before "End insert_container Special-cases")
207
208 else if (is_type_ingredient_name(type->name)) {
209 type->value = get(info.type_ingredient_names, type->name);
210 }
211 :(code)
212 bool is_type_ingredient_name(const string& type) {
213 return starts_with(type, "_");
214 }
215
216 :(before "End Container Type Checks")
217 if (type->value >= START_TYPE_INGREDIENTS
218 && (type->value - START_TYPE_INGREDIENTS) < SIZE(get(Type, type->value).type_ingredient_names))
219 return;
220
221 :(scenario size_of_shape_shifting_exclusive_container)
222 exclusive-container foo:_t [
223 x:_t
224 y:num
225 ]
226 def main [
227 1:foo:num <- merge 0/x, 34
228 3:foo:point <- merge 0/x, 15, 16
229 6:foo:point <- merge 1/y, 23
230 ]
231 +run: {1: ("foo" "number")} <- merge {0: "literal", "x": ()}, {34: "literal"}
232 +mem: storing 0 in location 1
233 +mem: storing 34 in location 2
234 +run: {3: ("foo" "point")} <- merge {0: "literal", "x": ()}, {15: "literal"}, {16: "literal"}
235 +mem: storing 0 in location 3
236 +mem: storing 15 in location 4
237 +mem: storing 16 in location 5
238 +run: {6: ("foo" "point")} <- merge {1: "literal", "y": ()}, {23: "literal"}
239 +mem: storing 1 in location 6
240 +mem: storing 23 in location 7
241 +run: return
242
243 % CHECK_EQ(trace_count_prefix("mem", "storing"), 7);
244
245 :(before "End variant_type Special-cases")
246 if (contains_type_ingredient(element))
247 replace_type_ingredients(element.type, type->right, info, " while computing variant type of exclusive-container");
248
249 :(scenario get_on_shape_shifting_container)
250 container foo:_t [
251 x:_t
252 y:num
253 ]
254 def main [
255 1:foo:point <- merge 14, 15, 16
256 2:num <- get 1:foo:point, y:offset
257 ]
258 +mem: storing 16 in location 2
259
260 :(scenario get_on_shape_shifting_container_2)
261 container foo:_t [
262 x:_t
263 y:num
264 ]
265 def main [
266 1:foo:point <- merge 14, 15, 16
267 2:point <- get 1:foo:point, x:offset
268 ]
269 +mem: storing 14 in location 2
270 +mem: storing 15 in location 3
271
272 :(scenario get_on_shape_shifting_container_3)
273 container foo:_t [
274 x:_t
275 y:num
276 ]
277 def main [
278 1:foo:&:point <- merge 34/unsafe, 48
279 3:&:point <- get 1:foo:&:point, x:offset
280 ]
281 +mem: storing 34 in location 3
282
283 :(scenario get_on_shape_shifting_container_inside_container)
284 container foo:_t [
285 x:_t
286 y:num
287 ]
288 container bar [
289 x:foo:point
290 y:num
291 ]
292 def main [
293 1:bar <- merge 14, 15, 16, 17
294 2:num <- get 1:bar, 1:offset
295 ]
296 +mem: storing 17 in location 2
297
298 :(scenario get_on_complex_shape_shifting_container)
299 container foo:_a:_b [
300 x:_a
301 y:_b
302 ]
303 def main [
304 1:text <- new [abc]
305 {2: (foo number (address array character))} <- merge 34/x, 1:text/y
306 3:text <- get {2: (foo number (address array character))}, y:offset
307 4:bool <- equal 1:text, 3:text
308 ]
309 +mem: storing 1 in location 4
310
311 :(before "End element_type Special-cases")
312 replace_type_ingredients(element, type, info, " while computing element type of container");
313 :(before "Compute Container Size(element, full_type)")
314 replace_type_ingredients(element, full_type, container_info, location_for_error_messages);
315 :(before "Compute Exclusive Container Size(element, full_type)")
316 replace_type_ingredients(element, full_type, exclusive_container_info, location_for_error_messages);
317
318
319
320
321 :(after "Compute size_of Container")
322 assert(!contains_type_ingredient(type));
323 :(after "Compute size_of Exclusive Container")
324 assert(!contains_type_ingredient(type));
325
326 :(code)
327 bool contains_type_ingredient(const reagent& x) {
328 return contains_type_ingredient(x.type);
329 }
330
331 bool contains_type_ingredient(const type_tree* type) {
332 if (!type) return false;
333 if (type->atom) return type->value >= START_TYPE_INGREDIENTS;
334 return contains_type_ingredient(type->left) || contains_type_ingredient(type->right);
335 }
336
337 void replace_type_ingredients(reagent& element, const type_tree* caller_type, const type_info& info, const string& location_for_error_messages) {
338 if (contains_type_ingredient(element)) {
339 if (!caller_type->right)
340 raise << "illegal type " << names_to_string(caller_type) << " seems to be missing a type ingredient or three" << location_for_error_messages << '\n' << end();
341 replace_type_ingredients(element.type, caller_type->right, info, location_for_error_messages);
342 }
343 }
344
345
346 void replace_type_ingredients(type_tree* element_type, const type_tree* callsite_type, const type_info& container_info, const string& location_for_error_messages) {
347 if (!callsite_type) return;
348 if (!element_type) return;
349 if (!element_type->atom) {
350 if (element_type->right == NULL && is_type_ingredient(element_type->left)) {
351 int type_ingredient_index = to_type_ingredient_index(element_type->left);
352 if (corresponding(callsite_type, type_ingredient_index, is_final_type_ingredient(type_ingredient_index, container_info))->right) {
353
354 replace_type_ingredient_at(type_ingredient_index, element_type, callsite_type, container_info, location_for_error_messages);
355 return;
356 }
357 }
358 replace_type_ingredients(element_type->left, callsite_type, container_info, location_for_error_messages);
359 replace_type_ingredients(element_type->right, callsite_type, container_info, location_for_error_messages);
360 return;
361 }
362 if (is_type_ingredient(element_type))
363 replace_type_ingredient_at(to_type_ingredient_index(element_type), element_type, callsite_type, container_info, location_for_error_messages);
364 }
365
366 const type_tree* corresponding(const type_tree* type, int index, bool final) {
367 for (const type_tree* curr = type; curr; curr = curr->right, --index) {
368 assert_for_now(!curr->atom);
369 if (index == 0)
370 return final ? curr : curr->left;
371 }
372 assert_for_now(false);
373 }
374
375 bool is_type_ingredient(const type_tree* type) {
376 return type->atom && type->value >= START_TYPE_INGREDIENTS;
377 }
378
379 int to_type_ingredient_index(const type_tree* type) {
380 assert(type->atom);
381 return type->value-START_TYPE_INGREDIENTS;
382 }
383
384 void replace_type_ingredient_at(const int type_ingredient_index, type_tree* element_type, const type_tree* callsite_type, const type_info& container_info, const string& location_for_error_messages) {
385 if (!has_nth_type(callsite_type, type_ingredient_index)) {
386 raise << "illegal type " << names_to_string(callsite_type) << " seems to be missing a type ingredient or three" << location_for_error_messages << '\n' << end();
387 return;
388 }
389 *element_type = *nth_type_ingredient(callsite_type, type_ingredient_index, container_info);
390 }
391
392 const type_tree* nth_type_ingredient(const type_tree* callsite_type, int type_ingredient_index, const type_info& container_info) {
393 bool final = is_final_type_ingredient(type_ingredient_index, container_info);
394 const type_tree* curr = callsite_type;
395 for (int i = 0; i < type_ingredient_index; ++i) {
396 assert(curr);
397 assert(!curr->atom);
398
399 curr = curr->right;
400 }
401 assert(curr);
402 if (curr->atom) return curr;
403 if (!final) return curr->left;
404 if (!curr->right) return curr->left;
405 return curr;
406 }
407
408 bool is_final_type_ingredient(int type_ingredient_index, const type_info& container_info) {
409 for (map<string, type_ordinal>::const_iterator p = container_info.type_ingredient_names.begin();
410 p != container_info.type_ingredient_names.end();
411 ++p) {
412 if (p->second > START_TYPE_INGREDIENTS+type_ingredient_index) return false;
413 }
414 return true;
415 }
416
417 :(before "End Unit Tests")
418 void test_replace_type_ingredients_entire() {
419 run("container foo:_elem [\n"
420 " x:_elem\n"
421 " y:num\n"
422 "]\n");
423 reagent callsite("x:foo:point");
424 reagent element = element_type(callsite.type, 0);
425 CHECK_EQ(to_string(element), "{x: \"point\"}");
426 }
427
428 void test_replace_type_ingredients_tail() {
429 run("container foo:_elem [\n"
430 " x:_elem\n"
431 "]\n"
432 "container bar:_elem [\n"
433 " x:foo:_elem\n"
434 "]\n");
435 reagent callsite("x:bar:point");
436 reagent element = element_type(callsite.type, 0);
437 CHECK_EQ(to_string(element), "{x: (\"foo\" \"point\")}");
438 }
439
440 void test_replace_type_ingredients_head_tail_multiple() {
441 run("container foo:_elem [\n"
442 " x:_elem\n"
443 "]\n"
444 "container bar:_elem [\n"
445 " x:foo:_elem\n"
446 "]\n");
447 reagent callsite("x:bar:address:array:character");
448 reagent element = element_type(callsite.type, 0);
449 CHECK_EQ(to_string(element), "{x: (\"foo\" \"address\" \"array\" \"character\")}");
450 }
451
452 void test_replace_type_ingredients_head_middle() {
453 run("container foo:_elem [\n"
454 " x:_elem\n"
455 "]\n"
456 "container bar:_elem [\n"
457 " x:foo:_elem:num\n"
458 "]\n");
459 reagent callsite("x:bar:address");
460 reagent element = element_type(callsite.type, 0);
461 CHECK_EQ(to_string(element), "{x: (\"foo\" \"address\" \"number\")}");
462 }
463
464 void test_replace_last_type_ingredient_with_multiple() {
465 run("container foo:_a:_b [\n"
466 " x:_a\n"
467 " y:_b\n"
468 "]\n");
469 reagent callsite("{f: (foo number (address array character))}");
470 reagent element1 = element_type(callsite.type, 0);
471 CHECK_EQ(to_string(element1), "{x: \"number\"}");
472 reagent element2 = element_type(callsite.type, 1);
473 CHECK_EQ(to_string(element2), "{y: (\"address\" \"array\" \"character\")}");
474 }
475
476 void test_replace_last_type_ingredient_inside_compound() {
477 run("container foo:_a:_b [\n"
478 " {x: (bar _a (address _b))}\n"
479 "]\n");
480 reagent callsite("f:foo:number:array:character");
481 reagent element = element_type(callsite.type, 0);
482 CHECK_EQ(names_to_string_without_quotes(element.type), "(bar number (address array character))");
483 }
484
485 void test_replace_middle_type_ingredient_with_multiple() {
486 run("container foo:_a:_b:_c [\n"
487 " x:_a\n"
488 " y:_b\n"
489 " z:_c\n"
490 "]\n");
491 reagent callsite("{f: (foo number (address array character) boolean)}");
492 reagent element1 = element_type(callsite.type, 0);
493 CHECK_EQ(to_string(element1), "{x: \"number\"}");
494 reagent element2 = element_type(callsite.type, 1);
495 CHECK_EQ(to_string(element2), "{y: (\"address\" \"array\" \"character\")}");
496 reagent element3 = element_type(callsite.type, 2);
497 CHECK_EQ(to_string(element3), "{z: \"boolean\"}");
498 }
499
500 void test_replace_middle_type_ingredient_with_multiple2() {
501 run("container foo:_key:_value [\n"
502 " key:_key\n"
503 " value:_value\n"
504 "]\n");
505 reagent callsite("{f: (foo (address array character) number)}");
506 reagent element = element_type(callsite.type, 0);
507 CHECK_EQ(to_string(element), "{key: (\"address\" \"array\" \"character\")}");
508 }
509
510 void test_replace_middle_type_ingredient_with_multiple3() {
511 run("container foo_table:_key:_value [\n"
512 " data:&:@:foo_table_row:_key:_value\n"
513 "]\n"
514 "\n"
515 "container foo_table_row:_key:_value [\n"
516 " key:_key\n"
517 " value:_value\n"
518 "]\n");
519 reagent callsite("{f: (foo_table (address array character) number)}");
520 reagent element = element_type(callsite.type, 0);
521 CHECK_EQ(to_string(element), "{data: (\"address\" \"array\" \"foo_table_row\" (\"address\" \"array\" \"character\") \"number\")}");
522 }
523
524 :(code)
525 bool has_nth_type(const type_tree* base, int n) {
526 assert(n >= 0);
527 if (!base) return false;
528 if (n == 0) return true;
529 return has_nth_type(base->right, n-1);
530 }
531
532 :(scenario get_on_shape_shifting_container_error)
533 % Hide_errors = true;
534 container foo:_t [
535 x:_t
536 y:num
537 ]
538 def main [
539 10:foo:point <- merge 14, 15, 16
540 1:num <- get 10:foo, 1:offset
541 ]
542 +error: illegal type "foo" seems to be missing a type ingredient or three in '1:num <- get 10:foo, 1:offset'
543
544
545
546
547
548
549
550 :(before "End compute_container_sizes Non-atom Special-cases")
551 const type_tree* root = get_base_type(type);
552 if (contains_key(Type, root->value)) {
553 type_info& info = get(Type, root->value);
554 if (info.kind == CONTAINER) {
555 compute_container_sizes(info, type, pending_metadata, location_for_error_messages);
556 return;
557 }
558 if (info.kind == EXCLUSIVE_CONTAINER) {
559 compute_exclusive_container_sizes(info, type, pending_metadata, location_for_error_messages);
560 return;
561 }
562 }
563
564 :(before "End Unit Tests")
565 void test_container_sizes_shape_shifting_container() {
566 run("container foo:_t [\n"
567 " x:num\n"
568 " y:_t\n"
569 "]\n");
570 reagent r("x:foo:point");
571 compute_container_sizes(r, "");
572 CHECK_EQ(r.metadata.size, 3);
573 }
574
575 void test_container_sizes_shape_shifting_exclusive_container() {
576 run("exclusive-container foo:_t [\n"
577 " x:num\n"
578 " y:_t\n"
579 "]\n");
580 reagent r("x:foo:point");
581 compute_container_sizes(r, "");
582 CHECK_EQ(r.metadata.size, 3);
583 reagent r2("x:foo:num");
584 compute_container_sizes(r2, "");
585 CHECK_EQ(r2.metadata.size, 2);
586 }
587
588 void test_container_sizes_compound_type_ingredient() {
589 run("container foo:_t [\n"
590 " x:num\n"
591 " y:_t\n"
592 "]\n");
593 reagent r("x:foo:&:point");
594 compute_container_sizes(r, "");
595 CHECK_EQ(r.metadata.size, 2);
596
597 reagent point("x:point");
598 CHECK(contains_key(Container_metadata, point.type));
599 CHECK_EQ(get(Container_metadata, point.type).size, 2);
600 }
601
602 void test_container_sizes_recursive_shape_shifting_container() {
603 run("container foo:_t [\n"
604 " x:num\n"
605 " y:&:foo:_t\n"
606 "]\n");
607 reagent r2("x:foo:num");
608 compute_container_sizes(r2, "");
609 CHECK_EQ(r2.metadata.size, 2);
610 }
611
612 :(scenario typos_in_container_definitions)
613 % Hide_errors = true;
614 container foo:_t [
615 x:adress:_t
616 ]
617 def main [
618 local-scope
619 x:address:foo:num <- new {(foo num): type}
620 ]
621
622
623 :(scenario typos_in_recipes)
624 % Hide_errors = true;
625 def foo [
626 local-scope
627 x:adress:array:number <- copy 0
628 ]
629
630
631
632
633 :(scenario merge_check_shape_shifting_container_containing_exclusive_container)
634 container foo:_elem [
635 x:num
636 y:_elem
637 ]
638 exclusive-container bar [
639 x:num
640 y:num
641 ]
642 def main [
643 1:foo:bar <- merge 23, 1/y, 34
644 ]
645 +mem: storing 23 in location 1
646 +mem: storing 1 in location 2
647 +mem: storing 34 in location 3
648 $error: 0
649
650 :(scenario merge_check_shape_shifting_container_containing_exclusive_container_2)
651 % Hide_errors = true;
652 container foo:_elem [
653 x:num
654 y:_elem
655 ]
656 exclusive-container bar [
657 x:num
658 y:num
659 ]
660 def main [
661 1:foo:bar <- merge 23, 1/y, 34, 35
662 ]
663 +error: main: too many ingredients in '1:foo:bar <- merge 23, 1/y, 34, 35'
664
665 :(scenario merge_check_shape_shifting_exclusive_container_containing_container)
666 exclusive-container foo:_elem [
667 x:num
668 y:_elem
669 ]
670 container bar [
671 x:num
672 y:num
673 ]
674 def main [
675 1:foo:bar <- merge 1/y, 23, 34
676 ]
677 +mem: storing 1 in location 1
678 +mem: storing 23 in location 2
679 +mem: storing 34 in location 3
680 $error: 0
681
682 :(scenario merge_check_shape_shifting_exclusive_container_containing_container_2)
683 exclusive-container foo:_elem [
684 x:num
685 y:_elem
686 ]
687 container bar [
688 x:num
689 y:num
690 ]
691 def main [
692 1:foo:bar <- merge 0/x, 23
693 ]
694 $error: 0
695
696 :(scenario merge_check_shape_shifting_exclusive_container_containing_container_3)
697 % Hide_errors = true;
698 exclusive-container foo:_elem [
699 x:num
700 y:_elem
701 ]
702 container bar [
703 x:num
704 y:num
705 ]
706 def main [
707 1:foo:bar <- merge 1/y, 23
708 ]
709 +error: main: too few ingredients in '1:foo:bar <- merge 1/y, 23'