1
2
3
4
5 :(scenario transform_names)
6 def main [
7 x:num <- copy 0
8 ]
9 +name: assign x 1
10 +mem: storing 0 in location 1
11
12 :(scenarios transform)
13 :(scenario transform_names_fails_on_use_before_define)
14 % Hide_errors = true;
15 def main [
16 x:num <- copy y:num
17 ]
18 +error: main: tried to read ingredient 'y' in 'x:num <- copy y:num' but it hasn't been written to yet
19
20
21 :(after "Transform.push_back(compute_container_sizes)")
22 Transform.push_back(transform_names);
23
24 :(before "End Globals")
25 map<recipe_ordinal, map<string, int> > Name;
26
27
28
29 :(before "End Globals")
30 map<recipe_ordinal, map<string, int> > Name_snapshot;
31 :(before "End save_snapshots")
32 Name_snapshot = Name;
33 :(before "End restore_snapshots")
34 Name = Name_snapshot;
35
36 :(code)
37 void transform_names(const recipe_ordinal r) {
38 recipe& caller = get(Recipe, r);
39 trace(9991, "transform") << "--- transform names for recipe " << caller.name << end();
40 bool names_used = false;
41 bool numeric_locations_used = false;
42 map<string, int>& names = Name[r];
43
44 int& curr_idx = names[""];
45 ++curr_idx;
46 for (int i = 0; i < SIZE(caller.steps); ++i) {
47 instruction& inst = caller.steps.at(i);
48
49
50 for (int in = 0; in < SIZE(inst.ingredients); ++in) {
51 reagent& ingredient = inst.ingredients.at(in);
52 if (is_disqualified(ingredient, inst, caller.name)) continue;
53 if (is_numeric_location(ingredient)) numeric_locations_used = true;
54 if (is_named_location(ingredient)) names_used = true;
55 if (is_integer(ingredient.name)) continue;
56 if (!already_transformed(ingredient, names)) {
57 raise << maybe(caller.name) << "tried to read ingredient '" << ingredient.name << "' in '" << to_original_string(inst) << "' but it hasn't been written to yet\n" << end();
58
59 return;
60 }
61 int v = lookup_name(ingredient, r);
62 if (v >= 0) {
63 ingredient.set_value(v);
64
65 }
66 else {
67 raise << maybe(caller.name) << "can't find a place to store '" << ingredient.name << "'\n" << end();
68 return;
69 }
70 }
71 for (int out = 0; out < SIZE(inst.products); ++out) {
72 reagent& product = inst.products.at(out);
73 if (is_disqualified(product, inst, caller.name)) continue;
74 if (is_numeric_location(product)) numeric_locations_used = true;
75 if (is_named_location(product)) names_used = true;
76 if (is_integer(product.name)) continue;
77 if (names.find(product.name) == names.end()) {
78 trace(9993, "name") << "assign " << product.name << " " << curr_idx << end();
79 names[product.name] = curr_idx;
80 curr_idx += size_of(product);
81 }
82 int v = lookup_name(product, r);
83 if (v >= 0) {
84 product.set_value(v);
85
86 }
87 else {
88 raise << maybe(caller.name) << "can't find a place to store '" << product.name << "'\n" << end();
89 return;
90 }
91 }
92 }
93 if (names_used && numeric_locations_used)
94 raise << maybe(caller.name) << "mixing variable names and numeric addresses\n" << end();
95 }
96
97 bool is_disqualified( reagent& x, const instruction& inst, const string& recipe_name) {
98 if (!x.type) {
99 raise << maybe(recipe_name) << "missing type for '" << x.original_string << "' in '" << to_original_string(inst) << "'\n" << end();
100
101 return true;
102 }
103 if (is_raw(x)) return true;
104 if (is_literal(x)) return true;
105
106 if (x.initialized) return true;
107 return false;
108 }
109
110 bool already_transformed(const reagent& r, const map<string, int>& names) {
111 return contains_key(names, r.name);
112 }
113
114 int lookup_name(const reagent& r, const recipe_ordinal default_recipe) {
115 return Name[default_recipe][r.name];
116 }
117
118 type_ordinal skip_addresses(type_tree* type) {
119 while (type && is_compound_type_starting_with(type, "address"))
120 type = type->right;
121 if (!type) return -1;
122 if (type->atom) return type->value;
123 const type_tree* base_type = type;
124
125 if (base_type->atom)
126 return base_type->value;
127 assert(base_type->left->atom);
128 return base_type->left->value;
129 }
130
131 bool is_compound_type_starting_with(const type_tree* type, const string& expected_name) {
132 if (!type) return false;
133 if (type->atom) return false;
134 if (!type->left->atom) return false;
135 return type->left->value == get(Type_ordinal, expected_name);
136 }
137
138 int find_element_name(const type_ordinal t, const string& name, const string& recipe_name) {
139 const type_info& container = get(Type, t);
140 for (int i = 0; i < SIZE(container.elements); ++i)
141 if (container.elements.at(i).name == name) return i;
142 raise << maybe(recipe_name) << "unknown element '" << name << "' in container '" << get(Type, t).name << "'\n" << end();
143 return -1;
144 }
145
146 bool is_numeric_location(const reagent& x) {
147 if (is_literal(x)) return false;
148 if (is_raw(x)) return false;
149 if (x.name == "0") return false;
150 return is_integer(x.name);
151 }
152
153 bool is_named_location(const reagent& x) {
154 if (is_literal(x)) return false;
155 if (is_raw(x)) return false;
156 if (is_special_name(x.name)) return false;
157 return !is_integer(x.name);
158 }
159
160
161 bool is_special_name(const string& s) {
162 if (s == "_") return true;
163 if (s == "0") return true;
164
165 return false;
166 }
167
168 :(scenario transform_names_supports_containers)
169 def main [
170 x:point <- merge 34, 35
171 y:num <- copy 3
172 ]
173 +name: assign x 1
174
175 +name: assign y 3
176
177 :(scenario transform_names_supports_static_arrays)
178 def main [
179 x:@:num:3 <- create-array
180 y:num <- copy 3
181 ]
182 +name: assign x 1
183
184 +name: assign y 5
185
186 :(scenario transform_names_passes_dummy)
187
188 def main [
189 _, x:num <- copy 0, 1
190 ]
191 +name: assign x 1
192 -name: assign _ 1
193
194
195 :(scenarios run)
196 :(scenario transform_names_passes_raw)
197 % Hide_errors = true;
198 def main [
199 x:num/raw <- copy 0
200 ]
201 -name: assign x 1
202 +error: can't write to location 0 in 'x:num/raw <- copy 0'
203
204 :(scenarios transform)
205 :(scenario transform_names_fails_when_mixing_names_and_numeric_locations)
206 % Hide_errors = true;
207 def main [
208 x:num <- copy 1:num
209 ]
210 +error: main: mixing variable names and numeric addresses
211
212 :(scenario transform_names_fails_when_mixing_names_and_numeric_locations_2)
213 % Hide_errors = true;
214 def main [
215 x:num <- copy 1
216 1:num <- copy x:num
217 ]
218 +error: main: mixing variable names and numeric addresses
219
220 :(scenario transform_names_does_not_fail_when_mixing_names_and_raw_locations)
221 def main [
222 x:num <- copy 1:num/raw
223 ]
224 -error: main: mixing variable names and numeric addresses
225 $error: 0
226
227 :(scenario transform_names_does_not_fail_when_mixing_names_and_literals)
228 def main [
229 x:num <- copy 1
230 ]
231 -error: main: mixing variable names and numeric addresses
232 $error: 0
233
234
235
236
237 :(before "End update GET offset_value in Check")
238 else {
239 if (!offset.initialized) {
240 raise << maybe(get(Recipe, r).name) << "uninitialized offset '" << offset.name << "' in '" << to_original_string(inst) << "'\n" << end();
241 break;
242 }
243 offset_value = offset.value;
244 }
245
246 :(scenario transform_names_transforms_container_elements)
247 def main [
248 p:&:point <- copy 0
249 a:num <- get *p:&:point, y:offset
250 b:num <- get *p:&:point, x:offset
251 ]
252 +name: element y of type point is at offset 1
253 +name: element x of type point is at offset 0
254
255 :(before "End transform_names(inst) Special-cases")
256
257 if (inst.name == "get" || inst.name == "get-location" || inst.name == "put") {
258
259
260
261 if (SIZE(inst.ingredients) < 2)
262 break;
263 if (!is_literal(inst.ingredients.at(1)))
264 break;
265 if (inst.ingredients.at(1).name.find_first_not_of("0123456789") != string::npos) {
266
267 type_ordinal base_type = skip_addresses(inst.ingredients.at(0).type);
268 if (contains_key(Type, base_type)) {
269 inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name, get(Recipe, r).name));
270 trace(9993, "name") << "element " << inst.ingredients.at(1).name << " of type " << get(Type, base_type).name << " is at offset " << no_scientific(inst.ingredients.at(1).value) << end();
271 }
272 }
273 }
274
275 :(scenario missing_type_in_get)
276 % Hide_errors = true;
277 def main [
278 get a, x:offset
279 ]
280 +error: main: missing type for 'a' in 'get a, x:offset'
281
282
283 :(scenarios transform)
284 :(scenario transform_names_handles_containers)
285 def main [
286 a:point <- copy 0/unsafe
287 b:num <- copy 0/unsafe
288 ]
289 +name: assign a 1
290 +name: assign b 3
291
292
293
294 :(scenarios run)
295 :(scenario transform_names_handles_exclusive_containers)
296 def main [
297 12:num <- copy 1
298 13:num <- copy 35
299 14:num <- copy 36
300 20:point, 22:bool <- maybe-convert 12:number-or-point/unsafe, p:variant
301 ]
302 +name: variant p of type number-or-point has tag 1
303 +mem: storing 1 in location 22
304 +mem: storing 35 in location 20
305 +mem: storing 36 in location 21
306
307 :(before "End transform_names(inst) Special-cases")
308
309 if (inst.name == "maybe-convert") {
310 if (SIZE(inst.ingredients) != 2) {
311 raise << maybe(get(Recipe, r).name) << "exactly 2 ingredients expected in '" << to_original_string(inst) << "'\n" << end();
312 break;
313 }
314 assert(is_literal(inst.ingredients.at(1)));
315 if (inst.ingredients.at(1).name.find_first_not_of("0123456789") != string::npos) {
316
317 type_ordinal base_type = skip_addresses(inst.ingredients.at(0).type);
318 if (contains_key(Type, base_type)) {
319 inst.ingredients.at(1).set_value(find_element_name(base_type, inst.ingredients.at(1).name, get(Recipe, r).name));
320 trace(9993, "name") << "variant " << inst.ingredients.at(1).name << " of type " << get(Type, base_type).name << " has tag " << no_scientific(inst.ingredients.at(1).value) << end();
321 }
322 }
323 }
324
325 :(scenario missing_type_in_maybe_convert)
326 % Hide_errors = true;
327 def main [
328 maybe-convert a, x:variant
329 ]
330 +error: main: missing type for 'a' in 'maybe-convert a, x:variant'