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