1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 :(scenarios transform)
23 :(scenario brace_conversion)
24 def main [
25 {
26 break
27 1:num <- copy 0
28 }
29 ]
30 +transform: --- transform braces for recipe main
31 +transform: jump 1:offset
32 +transform: copy ...
33
34 :(before "End Instruction Modifying Transforms")
35 Transform.push_back(transform_braces);
36
37 :(code)
38 void transform_braces(const recipe_ordinal r) {
39 const int OPEN = 0, CLOSE = 1;
40
41 list<pair<int, int> > braces;
42 trace(9991, "transform") << "--- transform braces for recipe " << get(Recipe, r).name << end();
43 for (int index = 0; index < SIZE(get(Recipe, r).steps); ++index) {
44 const instruction& inst = get(Recipe, r).steps.at(index);
45 if (inst.label == "{") {
46 trace(9993, "transform") << maybe(get(Recipe, r).name) << "push (open, " << index << ")" << end();
47 braces.push_back(pair<int,int>(OPEN, index));
48 }
49 if (inst.label == "}") {
50 trace(9993, "transform") << "push (close, " << index << ")" << end();
51 braces.push_back(pair<int,int>(CLOSE, index));
52 }
53 }
54 stack<int> open_braces;
55 for (int index = 0; index < SIZE(get(Recipe, r).steps); ++index) {
56 instruction& inst = get(Recipe, r).steps.at(index);
57 if (inst.label == "{") {
58 open_braces.push(index);
59 continue;
60 }
61 if (inst.label == "}") {
62 if (open_braces.empty()) {
63 raise << "missing '{' in '" << get(Recipe, r).name << "'\n" << end();
64 return;
65 }
66 open_braces.pop();
67 continue;
68 }
69 if (inst.is_label) continue;
70 if (inst.name != "loop"
71 && inst.name != "loop-if"
72 && inst.name != "loop-unless"
73 && inst.name != "break"
74 && inst.name != "break-if"
75 && inst.name != "break-unless") {
76 trace(9992, "transform") << inst.name << " ..." << end();
77 continue;
78 }
79
80 if (inst.name.find("-if") != string::npos || inst.name.find("-unless") != string::npos) {
81 if (inst.ingredients.empty()) {
82 raise << "'" << inst.name << "' expects 1 or 2 ingredients, but got none\n" << end();
83 continue;
84 }
85 }
86
87 string old_name = inst.name;
88 if (inst.name.find("-if") != string::npos) {
89 inst.name = "jump-if";
90 inst.operation = JUMP_IF;
91 }
92 else if (inst.name.find("-unless") != string::npos) {
93 inst.name = "jump-unless";
94 inst.operation = JUMP_UNLESS;
95 }
96 else {
97 inst.name = "jump";
98 inst.operation = JUMP;
99 }
100
101 if (inst.name.find("-if") != string::npos || inst.name.find("-unless") != string::npos) {
102
103 if (SIZE(inst.ingredients) > 1 && is_literal(inst.ingredients.at(1))) {
104 trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(1).name << ":offset" << end();
105 continue;
106 }
107 }
108 else {
109
110 if (!inst.ingredients.empty() && is_literal(inst.ingredients.at(0))) {
111 trace(9992, "transform") << "jump " << inst.ingredients.at(0).name << ":offset" << end();
112 continue;
113 }
114 }
115
116 reagent target;
117 target.type = new type_tree("offset");
118 target.set_value(0);
119 if (open_braces.empty())
120 raise << "'" << old_name << "' needs a '{' before\n" << end();
121 else if (old_name.find("loop") != string::npos)
122 target.set_value(open_braces.top()-index);
123 else
124 target.set_value(matching_brace(open_braces.top(), braces, r) - index - 1);
125 inst.ingredients.push_back(target);
126
127 if (inst.name == "jump")
128 trace(9992, "transform") << "jump " << no_scientific(target.value) << ":offset" << end();
129 else
130 trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(0).name << ", " << no_scientific(target.value) << ":offset" << end();
131 }
132 }
133
134
135
136 int matching_brace(int index, const list<pair<int, int> >& braces, recipe_ordinal r) {
137 int stacksize = 0;
138 for (list<pair<int, int> >::const_iterator p = braces.begin(); p != braces.end(); ++p) {
139 if (p->second < index) continue;
140 stacksize += (p->first ? 1 : -1);
141 if (stacksize == 0) return p->second;
142 }
143 raise << maybe(get(Recipe, r).name) << "unbalanced '{'\n" << end();
144 return SIZE(get(Recipe, r).steps);
145 }
146
147 :(scenario loop)
148 def main [
149 1:num <- copy 0
150 2:num <- copy 0
151 {
152 3:num <- copy 0
153 loop
154 }
155 ]
156 +transform: --- transform braces for recipe main
157 +transform: copy ...
158 +transform: copy ...
159 +transform: copy ...
160 +transform: jump -2:offset
161
162 :(scenario break_empty_block)
163 def main [
164 1:num <- copy 0
165 {
166 break
167 }
168 ]
169 +transform: --- transform braces for recipe main
170 +transform: copy ...
171 +transform: jump 0:offset
172
173 :(scenario break_cascading)
174 def main [
175 1:num <- copy 0
176 {
177 break
178 }
179 {
180 break
181 }
182 ]
183 +transform: --- transform braces for recipe main
184 +transform: copy ...
185 +transform: jump 0:offset
186 +transform: jump 0:offset
187
188 :(scenario break_cascading_2)
189 def main [
190 1:num <- copy 0
191 2:num <- copy 0
192 {
193 break
194 3:num <- copy 0
195 }
196 {
197 break
198 }
199 ]
200 +transform: --- transform braces for recipe main
201 +transform: copy ...
202 +transform: copy ...
203 +transform: jump 1:offset
204 +transform: copy ...
205 +transform: jump 0:offset
206
207 :(scenario break_if)
208 def main [
209 1:num <- copy 0
210 2:num <- copy 0
211 {
212 break-if 2:num
213 3:num <- copy 0
214 }
215 {
216 break
217 }
218 ]
219 +transform: --- transform braces for recipe main
220 +transform: copy ...
221 +transform: copy ...
222 +transform: jump-if 2, 1:offset
223 +transform: copy ...
224 +transform: jump 0:offset
225
226 :(scenario break_nested)
227 def main [
228 1:num <- copy 0
229 {
230 2:num <- copy 0
231 break
232 {
233 3:num <- copy 0
234 }
235 4:num <- copy 0
236 }
237 ]
238 +transform: jump 4:offset
239
240 :(scenario break_nested_degenerate)
241 def main [
242 1:num <- copy 0
243 {
244 2:num <- copy 0
245 break
246 {
247 }
248 4:num <- copy 0
249 }
250 ]
251 +transform: jump 3:offset
252
253 :(scenario break_nested_degenerate_2)
254 def main [
255 1:num <- copy 0
256 {
257 2:num <- copy 0
258 break
259 {
260 }
261 }
262 ]
263 +transform: jump 2:offset
264
265 :(scenario break_label)
266 % Hide_errors = true;
267 def main [
268 1:num <- copy 0
269 {
270 break +foo:offset
271 }
272 ]
273 +transform: jump +foo:offset
274
275 :(scenario break_unless)
276 def main [
277 1:num <- copy 0
278 2:num <- copy 0
279 {
280 break-unless 2:num
281 3:num <- copy 0
282 }
283 ]
284 +transform: --- transform braces for recipe main
285 +transform: copy ...
286 +transform: copy ...
287 +transform: jump-unless 2, 1:offset
288 +transform: copy ...
289
290 :(scenario loop_unless)
291 def main [
292 1:num <- copy 0
293 2:num <- copy 0
294 {
295 loop-unless 2:num
296 3:num <- copy 0
297 }
298 ]
299 +transform: --- transform braces for recipe main
300 +transform: copy ...
301 +transform: copy ...
302 +transform: jump-unless 2, -1:offset
303 +transform: copy ...
304
305 :(scenario loop_nested)
306 def main [
307 1:num <- copy 0
308 {
309 2:num <- copy 0
310 {
311 3:num <- copy 0
312 }
313 loop-if 4:bool
314 5:num <- copy 0
315 }
316 ]
317 +transform: --- transform braces for recipe main
318 +transform: jump-if 4, -5:offset
319
320 :(scenario loop_label)
321 def main [
322 1:num <- copy 0
323 +foo
324 2:num <- copy 0
325 ]
326 +transform: --- transform braces for recipe main
327 +transform: copy ...
328 +transform: copy ...
329
330
331 :(scenarios run)
332 :(scenario brace_conversion_and_run)
333 def test-factorial [
334 1:num <- copy 5
335 2:num <- copy 1
336 {
337 3:bool <- equal 1:num, 1
338 break-if 3:bool
339
340 2:num <- multiply 2:num, 1:num
341 1:num <- subtract 1:num, 1
342 loop
343 }
344 4:num <- copy 2:num
345 ]
346 +mem: location 2 is 120
347
348 :(scenario break_outside_braces_fails)
349 % Hide_errors = true;
350 def main [
351 break
352 ]
353 +error: 'break' needs a '{' before
354
355 :(scenario break_conditional_without_ingredient_fails)
356 % Hide_errors = true;
357 def main [
358 {
359 break-if
360 }
361 ]
362 +error: 'break-if' expects 1 or 2 ingredients, but got none
363
364
365
366 :(scenario return_if)
367 def main [
368 1:num <- test1
369 ]
370 def test1 [
371 return-if 0, 34
372 return 35
373 ]
374 +mem: storing 35 in location 1
375
376 :(scenario return_if_2)
377 def main [
378 1:num <- test1
379 ]
380 def test1 [
381 return-if 1, 34
382 return 35
383 ]
384 +mem: storing 34 in location 1
385
386 :(before "End Rewrite Instruction(curr, recipe result)")
387
388
389
390
391
392
393
394 if (curr.name == "return-if" || curr.name == "reply-if") {
395 if (curr.products.empty()) {
396 emit_return_block(result, "break-unless", curr);
397 curr.clear();
398 }
399 else {
400 raise << "'" << curr.name << "' never yields any products\n" << end();
401 }
402 }
403
404
405
406
407
408
409
410 if (curr.name == "return-unless" || curr.name == "reply-unless") {
411 if (curr.products.empty()) {
412 emit_return_block(result, "break-if", curr);
413 curr.clear();
414 }
415 else {
416 raise << "'" << curr.name << "' never yields any products\n" << end();
417 }
418 }
419
420 :(code)
421 void emit_return_block(recipe& out, const string& break_command, const instruction& inst) {
422 const vector<reagent>& ingredients = inst.ingredients;
423 reagent condition = ingredients.at(0);
424 vector<reagent> return_ingredients;
425 copy(++ingredients.begin(), ingredients.end(), inserter(return_ingredients, return_ingredients.end()));
426
427
428 instruction open_label; open_label.is_label=true; open_label.label = "{";
429 out.steps.push_back(open_label);
430
431
432 instruction break_inst;
433 break_inst.operation = get(Recipe_ordinal, break_command);
434 break_inst.name = break_command;
435 break_inst.ingredients.push_back(condition);
436 out.steps.push_back(break_inst);
437
438
439 instruction return_inst;
440 return_inst.operation = get(Recipe_ordinal, "return");
441 return_inst.name = "return";
442 return_inst.ingredients.swap(return_ingredients);
443 return_inst.original_string = inst.original_string;
444 out.steps.push_back(return_inst);
445
446
447 instruction close_label; close_label.is_label=true; close_label.label = "}";
448 out.steps.push_back(close_label);
449 }
450
451
452
453
454
455 :(before "End Primitive Recipe Declarations")
456 BREAK,
457 BREAK_IF,
458 BREAK_UNLESS,
459 LOOP,
460 LOOP_IF,
461 LOOP_UNLESS,
462 :(before "End Primitive Recipe Numbers")
463 put(Recipe_ordinal, "break", BREAK);
464 put(Recipe_ordinal, "break-if", BREAK_IF);
465 put(Recipe_ordinal, "break-unless", BREAK_UNLESS);
466 put(Recipe_ordinal, "loop", LOOP);
467 put(Recipe_ordinal, "loop-if", LOOP_IF);
468 put(Recipe_ordinal, "loop-unless", LOOP_UNLESS);
469 :(before "End Primitive Recipe Checks")
470 case BREAK: break;
471 case BREAK_IF: break;
472 case BREAK_UNLESS: break;
473 case LOOP: break;
474 case LOOP_IF: break;
475 case LOOP_UNLESS: break;