1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 :(scenario new)
123
124
125 def main [
126 1:address:num/raw <- new number:type
127 2:address:num/raw <- new number:type
128 3:bool/raw <- equal 1:address:num/raw, 2:address:num/raw
129 ]
130 +mem: storing 0 in location 3
131
132 :(scenario new_array)
133
134 def main [
135 1:address:array:num/raw <- new number:type, 5
136 2:address:num/raw <- new number:type
137 3:num/raw <- subtract 2:address:num/raw, 1:address:array:num/raw
138 ]
139 +run: {1: ("address" "array" "number"), "raw": ()} <- new {number: "type"}, {5: "literal"}
140 +mem: array length is 5
141
142 +mem: storing 7 in location 3
143
144 :(scenario dilated_reagent_with_new)
145 def main [
146 1:address:address:num <- new {(address number): type}
147 ]
148 +new: size of '(address number)' is 1
149
150
151 :(before "End Mu Types Initialization")
152 put(Type_ordinal, "type", 0);
153 :(code)
154 bool is_mu_type_literal(const reagent& r) {
155 return is_literal(r) && r.type && r.type->name == "type";
156 }
157
158 :(before "End Primitive Recipe Declarations")
159 NEW,
160 :(before "End Primitive Recipe Numbers")
161 put(Recipe_ordinal, "new", NEW);
162 :(before "End Primitive Recipe Checks")
163 case NEW: {
164 const recipe& caller = get(Recipe, r);
165 if (inst.ingredients.empty() || SIZE(inst.ingredients) > 2) {
166 ¦ raise << maybe(caller.name) << "'new' requires one or two ingredients, but got '" << to_original_string(inst) << "'\n" << end();
167 ¦ break;
168 }
169
170 const reagent& type = inst.ingredients.at(0);
171 if (!is_mu_type_literal(type)) {
172 ¦ raise << maybe(caller.name) << "first ingredient of 'new' should be a type, but got '" << type.original_string << "'\n" << end();
173 ¦ break;
174 }
175 if (SIZE(inst.ingredients) > 1 && !is_mu_number(inst.ingredients.at(1))) {
176 ¦ raise << maybe(caller.name) << "second ingredient of 'new' should be a number (array length), but got '" << type.original_string << "'\n" << end();
177 ¦ break;
178 }
179 if (inst.products.empty()) {
180 ¦ raise << maybe(caller.name) << "result of 'new' should never be ignored\n" << end();
181 ¦ break;
182 }
183 if (!product_of_new_is_valid(inst)) {
184 ¦ raise << maybe(caller.name) << "product of 'new' has incorrect type: '" << to_original_string(inst) << "'\n" << end();
185 ¦ break;
186 }
187 break;
188 }
189 :(code)
190 bool product_of_new_is_valid(const instruction& inst) {
191 reagent product = inst.products.at(0);
192
193 if (!product.type || product.type->atom || product.type->left->value != get(Type_ordinal, "address"))
194 ¦ return false;
195 drop_from_type(product, "address");
196 if (SIZE(inst.ingredients) > 1) {
197 ¦
198 ¦ if (!product.type || product.type->atom || product.type->left->value != get(Type_ordinal, "array"))
199 ¦ ¦ return false;
200 ¦ drop_from_type(product, "array");
201 }
202 reagent expected_product;
203 expected_product.type = new_type_tree(inst.ingredients.at(0).name);
204 return types_strictly_match(product, expected_product);
205 }
206
207 void drop_from_type(reagent& r, string expected_type) {
208 assert(!r.type->atom);
209 if (r.type->left->name != expected_type) {
210 ¦ raise << "can't drop2 " << expected_type << " from '" << to_string(r) << "'\n" << end();
211 ¦ return;
212 }
213
214 type_tree* tmp = r.type;
215 r.type = tmp->right;
216 tmp->right = NULL;
217 delete tmp;
218
219 assert(!r.type->atom);
220 if (r.type->right) return;
221 tmp = r.type;
222 r.type = tmp->left;
223 tmp->left = NULL;
224 delete tmp;
225 }
226
227 :(scenario new_returns_incorrect_type)
228 % Hide_errors = true;
229 def main [
230 1:bool <- new num:type
231 ]
232 +error: main: product of 'new' has incorrect type: '1:bool <- new num:type'
233
234 :(scenario new_discerns_singleton_list_from_atom_container)
235 % Hide_errors = true;
236 def main [
237 1:address:num/raw <- new {(num): type}
238 ]
239 +error: main: product of 'new' has incorrect type: '1:address:num/raw <- new {(num): type}'
240
241 :(scenario new_with_type_abbreviation)
242 def main [
243 1:address:num/raw <- new num:type
244 ]
245 $error: 0
246
247 :(scenario new_with_type_abbreviation_inside_compound)
248 def main [
249 {1: (address address number), raw: ()} <- new {(& num): type}
250 ]
251 $error: 0
252
253
254
255
256
257
258
259 :(before "End Primitive Recipe Checks")
260 case ALLOCATE: {
261 raise << "never call 'allocate' directly'; always use 'new'\n" << end();
262 break;
263 }
264 :(before "End Primitive Recipe Implementations")
265 case NEW: {
266 raise << "no implementation for 'new'; why wasn't it translated to 'allocate'? Please save a copy of your program and send it to Kartik.\n" << end();
267 break;
268 }
269
270 :(after "Transform.push_back(check_instruction)")
271 Transform.push_back(transform_new_to_allocate);
272
273 :(code)
274 void transform_new_to_allocate(const recipe_ordinal r) {
275 trace(9991, "transform") << "--- convert 'new' to 'allocate' for recipe " << get(Recipe, r).name << end();
276 for (int i = 0; i < SIZE(get(Recipe, r).steps); ++i) {
277 ¦ instruction& inst = get(Recipe, r).steps.at(i);
278 ¦
279 ¦ if (inst.name == "new") {
280 ¦ ¦ if (inst.ingredients.empty()) return;
281 ¦ ¦ inst.operation = ALLOCATE;
282 ¦ ¦ type_tree* type = new_type_tree(inst.ingredients.at(0).name);
283 ¦ ¦ inst.ingredients.at(0).set_value(size_of(type));
284 ¦ ¦ trace(9992, "new") << "size of '" << inst.ingredients.at(0).name << "' is " << inst.ingredients.at(0).value << end();
285 ¦ ¦ delete type;
286 ¦ }
287 }
288 }
289
290
291
292 :(before "End Globals")
293 extern const int Reserved_for_tests = 1000;
294 int Memory_allocated_until = Reserved_for_tests;
295 int Initial_memory_per_routine = 100000;
296 :(before "End Setup")
297 Memory_allocated_until = Reserved_for_tests;
298 Initial_memory_per_routine = 100000;
299 :(before "End routine Fields")
300 int alloc, alloc_max;
301 :(before "End routine Constructor")
302 alloc = Memory_allocated_until;
303 Memory_allocated_until += Initial_memory_per_routine;
304 alloc_max = Memory_allocated_until;
305 trace(9999, "new") << "routine allocated memory from " << alloc << " to " << alloc_max << end();
306
307 :(before "End Primitive Recipe Declarations")
308 ALLOCATE,
309 :(before "End Primitive Recipe Numbers")
310 put(Recipe_ordinal, "allocate", ALLOCATE);
311 :(before "End Primitive Recipe Implementations")
312 case ALLOCATE: {
313
314 int size = ingredients.at(0).at(0);
315 if (SIZE(ingredients) > 1) {
316 ¦
317 ¦ trace(9999, "mem") << "array length is " << ingredients.at(1).at(0) << end();
318 ¦ size = 1 + size*ingredients.at(1).at(0);
319 }
320 int result = allocate(size);
321 if (SIZE(current_instruction().ingredients) > 1) {
322 ¦
323 ¦ trace(9999, "mem") << "storing " << ingredients.at(1).at(0) << " in location " << result+1 << end();
324 ¦ put(Memory, result+1, ingredients.at(1).at(0));
325 }
326 products.resize(1);
327 products.at(0).push_back(result);
328 break;
329 }
330 :(code)
331 int allocate(int size) {
332
333 ++size;
334 trace(9999, "mem") << "allocating size " << size << end();
335
336
337
338
339
340 ensure_space(size);
341 const int result = Current_routine->alloc;
342 trace(9999, "mem") << "new alloc: " << result << end();
343
344 for (int address = result; address < result+size; ++address) {
345 ¦ trace(9999, "mem") << "storing 0 in location " << address << end();
346 ¦ put(Memory, address, 0);
347 }
348 Current_routine->alloc += size;
349
350 assert(Current_routine->alloc <= Current_routine->alloc_max);
351 return result;
352 }
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367 :(code)
368 void ensure_space(int size) {
369 if (size > Initial_memory_per_routine) {
370 ¦ tb_shutdown();
371 ¦ cerr << "can't allocate " << size << " locations, that's too much compared to " << Initial_memory_per_routine << ".\n";
372 ¦ exit(0);
373 }
374 if (Current_routine->alloc + size > Current_routine->alloc_max) {
375 ¦
376 ¦ Current_routine->alloc = Memory_allocated_until;
377 ¦ Memory_allocated_until += Initial_memory_per_routine;
378 ¦ Current_routine->alloc_max = Memory_allocated_until;
379 ¦ trace(9999, "new") << "routine allocated memory from " << Current_routine->alloc << " to " << Current_routine->alloc_max << end();
380 }
381 }
382
383 :(scenario new_initializes)
384 % Memory_allocated_until = 10;
385 % put(Memory, Memory_allocated_until, 1);
386 def main [
387 1:address:num <- new number:type
388 ]
389 +mem: storing 0 in location 10
390
391 :(scenario new_size)
392 def main [
393 11:address:num/raw <- new number:type
394 12:address:num/raw <- new number:type
395 13:num/raw <- subtract 12:address:num/raw, 11:address:num/raw
396 ]
397
398 +mem: storing 2 in location 13
399
400 :(scenario new_array_size)
401 def main [
402 1:address:array:num/raw <- new number:type, 5
403 2:address:num/raw <- new number:type
404 3:num/raw <- subtract 2:address:num/raw, 1:address:array:num/raw
405 ]
406
407 +mem: storing 7 in location 3
408
409 :(scenario new_empty_array)
410 def main [
411 1:address:array:num/raw <- new number:type, 0
412 2:address:num/raw <- new number:type
413 3:num/raw <- subtract 2:address:num/raw, 1:address:array:num/raw
414 ]
415 +run: {1: ("address" "array" "number"), "raw": ()} <- new {number: "type"}, {0: "literal"}
416 +mem: array length is 0
417
418 +mem: storing 2 in location 3
419
420
421 :(scenario new_overflow)
422 % Initial_memory_per_routine = 3; // barely enough room for point allocation below
423 def main [
424 1:address:num/raw <- new number:type
425 2:address:point/raw <- new point:type
426 ]
427 +new: routine allocated memory from 1000 to 1003
428 +new: routine allocated memory from 1003 to 1006
429
430 :(scenario new_without_ingredient)
431 % Hide_errors = true;
432 def main [
433 1:address:number <- new
434 ]
435 +error: main: 'new' requires one or two ingredients, but got '1:address:number <- new'