From e5c11a5137d538b7713dd8708ca767c208824c06 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Mon, 26 Dec 2016 01:17:01 -0800 Subject: 3709 - line numbers in html Each line number also gets an anchor name, but I'm not hyperlinking them for now because I don't want to encourage bookmarking these links just yet. They aren't permalinks because every revision may change what's at any given line number. --- html/040brace.cc.html | 982 ++++++++++++++++++++++++++------------------------ 1 file changed, 503 insertions(+), 479 deletions(-) (limited to 'html/040brace.cc.html') diff --git a/html/040brace.cc.html b/html/040brace.cc.html index f1bda55e..f704e329 100644 --- a/html/040brace.cc.html +++ b/html/040brace.cc.html @@ -6,7 +6,7 @@ - + - +
-//: Structured programming
-//:
-//: Our jump recipes are quite inconvenient to use, so Mu provides a
-//: lightweight tool called 'transform_braces' to work in a slightly more
-//: convenient format with nested braces:
-//:
-//:   {
-//:     some instructions
-//:     {
-//:       more instructions
-//:     }
-//:   }
-//:
-//: Braces are just labels, they require no special parsing. The pseudo
-//: instructions 'loop' and 'break' jump to just after the enclosing '{' and
-//: '}' respectively.
-//:
-//: Conditional and unconditional 'loop' and 'break' should give us 80% of the
-//: benefits of the control-flow primitives we're used to in other languages,
-//: like 'if', 'while', 'for', etc.
-
-:(scenarios transform)
-:(scenario brace_conversion)
-def main [
-  {
-    break
-    1:num <- copy 0
-  }
-]
-+transform: --- transform braces for recipe main
-+transform: jump 1:offset
-+transform: copy ...
-
-:(before "End Instruction Modifying Transforms")
-Transform.push_back(transform_braces);  // idempotent
-
-:(code)
-void transform_braces(const recipe_ordinal r) {
-  const int OPEN = 0, CLOSE = 1;
-  // use signed integer for step index because we'll be doing arithmetic on it
-  list<pair<int/*OPEN/CLOSE*/, /*step*/int> > braces;
-  trace(9991, "transform") << "--- transform braces for recipe " << get(Recipe, r).name << end();
-  for (int index = 0;  index < SIZE(get(Recipe, r).steps);  ++index) {
-    const instruction& inst = get(Recipe, r).steps.at(index);
-    if (inst.label == "{") {
-      trace(9993, "transform") << maybe(get(Recipe, r).name) << "push (open, " << index << ")" << end();
-      braces.push_back(pair<int,int>(OPEN, index));
-    }
-    if (inst.label == "}") {
-      trace(9993, "transform") << "push (close, " << index << ")" << end();
-      braces.push_back(pair<int,int>(CLOSE, index));
-    }
-  }
-  stack</*step*/int> open_braces;
-  for (int index = 0;  index < SIZE(get(Recipe, r).steps);  ++index) {
-    instruction& inst = get(Recipe, r).steps.at(index);
-    if (inst.label == "{") {
-      open_braces.push(index);
-      continue;
-    }
-    if (inst.label == "}") {
-      if (open_braces.empty()) {
-        raise << "missing '{' in '" << get(Recipe, r).name << "'\n" << end();
-        return;
-      }
-      open_braces.pop();
-      continue;
-    }
-    if (inst.is_label) continue;
-    if (inst.old_name != "loop"
-         && inst.old_name != "loop-if"
-         && inst.old_name != "loop-unless"
-         && inst.old_name != "break"
-         && inst.old_name != "break-if"
-         && inst.old_name != "break-unless") {
-      trace(9992, "transform") << inst.old_name << " ..." << end();
-      continue;
-    }
-    // check for errors
-    if (inst.old_name.find("-if") != string::npos || inst.old_name.find("-unless") != string::npos) {
-      if (inst.ingredients.empty()) {
-        raise << "'" << inst.old_name << "' expects 1 or 2 ingredients, but got none\n" << end();
-        continue;
-      }
-    }
-    // update instruction operation
-    if (inst.old_name.find("-if") != string::npos) {
-      inst.name = "jump-if";
-      inst.operation = JUMP_IF;
-    }
-    else if (inst.old_name.find("-unless") != string::npos) {
-      inst.name = "jump-unless";
-      inst.operation = JUMP_UNLESS;
-    }
-    else {
-      inst.name = "jump";
-      inst.operation = JUMP;
-    }
-    // check for explicitly provided targets
-    if (inst.old_name.find("-if") != string::npos || inst.old_name.find("-unless") != string::npos) {
-      // conditional branches check arg 1
-      if (SIZE(inst.ingredients) > 1 && is_literal(inst.ingredients.at(1))) {
-        trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(1).name << ":offset" << end();
-        continue;
-      }
-    }
-    else {
-      // unconditional branches check arg 0
-      if (!inst.ingredients.empty() && is_literal(inst.ingredients.at(0))) {
-        trace(9992, "transform") << "jump " << inst.ingredients.at(0).name << ":offset" << end();
-        continue;
-      }
-    }
-    // if implicit, compute target
-    reagent target;
-    target.type = new type_tree("offset");
-    target.set_value(0);
-    if (open_braces.empty())
-      raise << "'" << inst.old_name << "' needs a '{' before\n" << end();
-    else if (inst.old_name.find("loop") != string::npos)
-      target.set_value(open_braces.top()-index);
-    else  // break instruction
-      target.set_value(matching_brace(open_braces.top(), braces, r) - index - 1);
-    inst.ingredients.push_back(target);
-    // log computed target
-    if (inst.name == "jump")
-      trace(9992, "transform") << "jump " << no_scientific(target.value) << ":offset" << end();
-    else
-      trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(0).name << ", " << no_scientific(target.value) << ":offset" << end();
-  }
-}
-
-// returns a signed integer not just so that we can return -1 but also to
-// enable future signed arithmetic
-int matching_brace(int index, const list<pair<int, int> >& braces, recipe_ordinal r) {
-  int stacksize = 0;
-  for (list<pair<int, int> >::const_iterator p = braces.begin();  p != braces.end();  ++p) {
-    if (p->second < index) continue;
-    stacksize += (p->first ? 1 : -1);
-    if (stacksize == 0) return p->second;
-  }
-  raise << maybe(get(Recipe, r).name) << "unbalanced '{'\n" << end();
-  return SIZE(get(Recipe, r).steps);  // exit current routine
-}
-
-:(scenario loop)
-def main [
-  1:num <- copy 0
-  2:num <- copy 0
-  {
-    3:num <- copy 0
-    loop
-  }
-]
-+transform: --- transform braces for recipe main
-+transform: copy ...
-+transform: copy ...
-+transform: copy ...
-+transform: jump -2:offset
-
-:(scenario break_empty_block)
-def main [
-  1:num <- copy 0
-  {
-    break
-  }
-]
-+transform: --- transform braces for recipe main
-+transform: copy ...
-+transform: jump 0:offset
-
-:(scenario break_cascading)
-def main [
-  1:num <- copy 0
-  {
-    break
-  }
-  {
-    break
-  }
-]
-+transform: --- transform braces for recipe main
-+transform: copy ...
-+transform: jump 0:offset
-+transform: jump 0:offset
-
-:(scenario break_cascading_2)
-def main [
-  1:num <- copy 0
-  2:num <- copy 0
-  {
-    break
-    3:num <- copy 0
-  }
-  {
-    break
-  }
-]
-+transform: --- transform braces for recipe main
-+transform: copy ...
-+transform: copy ...
-+transform: jump 1:offset
-+transform: copy ...
-+transform: jump 0:offset
-
-:(scenario break_if)
-def main [
-  1:num <- copy 0
-  2:num <- copy 0
-  {
-    break-if 2:num
-    3:num <- copy 0
-  }
-  {
-    break
-  }
-]
-+transform: --- transform braces for recipe main
-+transform: copy ...
-+transform: copy ...
-+transform: jump-if 2, 1:offset
-+transform: copy ...
-+transform: jump 0:offset
-
-:(scenario break_nested)
-def main [
-  1:num <- copy 0
-  {
-    2:num <- copy 0
-    break
-    {
-      3:num <- copy 0
-    }
-    4:num <- copy 0
-  }
-]
-+transform: jump 4:offset
-
-:(scenario break_nested_degenerate)
-def main [
-  1:num <- copy 0
-  {
-    2:num <- copy 0
-    break
-    {
-    }
-    4:num <- copy 0
-  }
-]
-+transform: jump 3:offset
-
-:(scenario break_nested_degenerate_2)
-def main [
-  1:num <- copy 0
-  {
-    2:num <- copy 0
-    break
-    {
-    }
-  }
-]
-+transform: jump 2:offset
-
-:(scenario break_label)
-% Hide_errors = true;
-def main [
-  1:num <- copy 0
-  {
-    break +foo:offset
-  }
-]
-+transform: jump +foo:offset
-
-:(scenario break_unless)
-def main [
-  1:num <- copy 0
-  2:num <- copy 0
-  {
-    break-unless 2:num
-    3:num <- copy 0
-  }
-]
-+transform: --- transform braces for recipe main
-+transform: copy ...
-+transform: copy ...
-+transform: jump-unless 2, 1:offset
-+transform: copy ...
-
-:(scenario loop_unless)
-def main [
-  1:num <- copy 0
-  2:num <- copy 0
-  {
-    loop-unless 2:num
-    3:num <- copy 0
-  }
-]
-+transform: --- transform braces for recipe main
-+transform: copy ...
-+transform: copy ...
-+transform: jump-unless 2, -1:offset
-+transform: copy ...
-
-:(scenario loop_nested)
-def main [
-  1:num <- copy 0
-  {
-    2:num <- copy 0
-    {
-      3:num <- copy 0
-    }
-    loop-if 4:bool
-    5:num <- copy 0
-  }
-]
-+transform: --- transform braces for recipe main
-+transform: jump-if 4, -5:offset
-
-:(scenario loop_label)
-def main [
-  1:num <- copy 0
-  +foo
-  2:num <- copy 0
-]
-+transform: --- transform braces for recipe main
-+transform: copy ...
-+transform: copy ...
-
-//: test how things actually run
-:(scenarios run)
-:(scenario brace_conversion_and_run)
-def test-factorial [
-  1:num <- copy 5
-  2:num <- copy 1
-  {
-    3:bool <- equal 1:num, 1
-    break-if 3:bool
-#    $print 1:num
-    2:num <- multiply 2:num, 1:num
-    1:num <- subtract 1:num, 1
-    loop
-  }
-  4:num <- copy 2:num  # trigger a read
-]
-+mem: location 2 is 120
-
-:(scenario break_outside_braces_fails)
-% Hide_errors = true;
-def main [
-  break
-]
-+error: 'break' needs a '{' before
-
-:(scenario break_conditional_without_ingredient_fails)
-% Hide_errors = true;
-def main [
-  {
-    break-if
-  }
-]
-+error: 'break-if' expects 1 or 2 ingredients, but got none
-
-//: Using break we can now implement conditional returns.
-
-:(scenario return_if)
-def main [
-  1:num <- test1
-]
-def test1 [
-  return-if 0, 34
-  return 35
-]
-+mem: storing 35 in location 1
-
-:(scenario return_if_2)
-def main [
-  1:num <- test1
-]
-def test1 [
-  return-if 1, 34
-  return 35
-]
-+mem: storing 34 in location 1
-
-:(before "End Rewrite Instruction(curr, recipe result)")
-// rewrite `return-if a, b, c, ...` to
-//   ```
-//   {
-//     break-unless a
-//     return b, c, ...
-//   }
-//   ```
-if (curr.name == "return-if" || curr.name == "reply-if") {
-  if (curr.products.empty()) {
-    emit_return_block(result, "break-unless", curr);
-    curr.clear();
-  }
-  else {
-    raise << "'" << curr.name << "' never yields any products\n" << end();
-  }
-}
-// rewrite `return-unless a, b, c, ...` to
-//   ```
-//   {
-//     break-if a
-//     return b, c, ...
-//   }
-//   ```
-if (curr.name == "return-unless" || curr.name == "reply-unless") {
-  if (curr.products.empty()) {
-    emit_return_block(result, "break-if", curr);
-    curr.clear();
-  }
-  else {
-    raise << "'" << curr.name << "' never yields any products\n" << end();
-  }
-}
-
-:(code)
-void emit_return_block(recipe& out, const string& break_command, const instruction& inst) {
-  const vector<reagent>& ingredients = inst.ingredients;
-  reagent/*copy*/ condition = ingredients.at(0);
-  vector<reagent> return_ingredients;
-  copy(++ingredients.begin(), ingredients.end(), inserter(return_ingredients, return_ingredients.end()));
-
-  // {
-  instruction open_label;  open_label.is_label=true;  open_label.label = "{";
-  out.steps.push_back(open_label);
-
-  // <break command> <condition>
-  instruction break_inst;
-  break_inst.operation = get(Recipe_ordinal, break_command);
-  break_inst.name = break_inst.old_name = break_command;
-  break_inst.ingredients.push_back(condition);
-  out.steps.push_back(break_inst);
-
-  // return <return ingredients>
-  instruction return_inst;
-  return_inst.operation = get(Recipe_ordinal, "return");
-  return_inst.name = "return";
-  return_inst.ingredients.swap(return_ingredients);
-  return_inst.original_string = inst.original_string;
-  out.steps.push_back(return_inst);
-
-  // }
-  instruction close_label;  close_label.is_label=true;  close_label.label = "}";
-  out.steps.push_back(close_label);
-}
-
-//: Make sure these pseudo recipes get consistent numbers in all tests, even
-//: though they aren't implemented. Allows greater flexibility in ordering
-//: transforms.
-
-:(before "End Primitive Recipe Declarations")
-BREAK,
-BREAK_IF,
-BREAK_UNLESS,
-LOOP,
-LOOP_IF,
-LOOP_UNLESS,
-:(before "End Primitive Recipe Numbers")
-put(Recipe_ordinal, "break", BREAK);
-put(Recipe_ordinal, "break-if", BREAK_IF);
-put(Recipe_ordinal, "break-unless", BREAK_UNLESS);
-put(Recipe_ordinal, "loop", LOOP);
-put(Recipe_ordinal, "loop-if", LOOP_IF);
-put(Recipe_ordinal, "loop-unless", LOOP_UNLESS);
-:(before "End Primitive Recipe Checks")
-case BREAK: break;
-case BREAK_IF: break;
-case BREAK_UNLESS: break;
-case LOOP: break;
-case LOOP_IF: break;
-case LOOP_UNLESS: break;
+  1 //: Structured programming
+  2 //:
+  3 //: Our jump recipes are quite inconvenient to use, so Mu provides a
+  4 //: lightweight tool called 'transform_braces' to work in a slightly more
+  5 //: convenient format with nested braces:
+  6 //:
+  7 //:   {
+  8 //:     some instructions
+  9 //:     {
+ 10 //:       more instructions
+ 11 //:     }
+ 12 //:   }
+ 13 //:
+ 14 //: Braces are just labels, they require no special parsing. The pseudo
+ 15 //: instructions 'loop' and 'break' jump to just after the enclosing '{' and
+ 16 //: '}' respectively.
+ 17 //:
+ 18 //: Conditional and unconditional 'loop' and 'break' should give us 80% of the
+ 19 //: benefits of the control-flow primitives we're used to in other languages,
+ 20 //: like 'if', 'while', 'for', etc.
+ 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);  // idempotent
+ 36 
+ 37 :(code)
+ 38 void transform_braces(const recipe_ordinal r) {
+ 39   const int OPEN = 0, CLOSE = 1;
+ 40   // use signed integer for step index because we'll be doing arithmetic on it
+ 41   list<pair<int/*OPEN/CLOSE*/, /*step*/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</*step*/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.old_name != "loop"
+ 71          && inst.old_name != "loop-if"
+ 72          && inst.old_name != "loop-unless"
+ 73          && inst.old_name != "break"
+ 74          && inst.old_name != "break-if"
+ 75          && inst.old_name != "break-unless") {
+ 76       trace(9992, "transform") << inst.old_name << " ..." << end();
+ 77       continue;
+ 78     }
+ 79     // check for errors
+ 80     if (inst.old_name.find("-if") != string::npos || inst.old_name.find("-unless") != string::npos) {
+ 81       if (inst.ingredients.empty()) {
+ 82         raise << "'" << inst.old_name << "' expects 1 or 2 ingredients, but got none\n" << end();
+ 83         continue;
+ 84       }
+ 85     }
+ 86     // update instruction operation
+ 87     if (inst.old_name.find("-if") != string::npos) {
+ 88       inst.name = "jump-if";
+ 89       inst.operation = JUMP_IF;
+ 90     }
+ 91     else if (inst.old_name.find("-unless") != string::npos) {
+ 92       inst.name = "jump-unless";
+ 93       inst.operation = JUMP_UNLESS;
+ 94     }
+ 95     else {
+ 96       inst.name = "jump";
+ 97       inst.operation = JUMP;
+ 98     }
+ 99     // check for explicitly provided targets
+100     if (inst.old_name.find("-if") != string::npos || inst.old_name.find("-unless") != string::npos) {
+101       // conditional branches check arg 1
+102       if (SIZE(inst.ingredients) > 1 && is_literal(inst.ingredients.at(1))) {
+103         trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(1).name << ":offset" << end();
+104         continue;
+105       }
+106     }
+107     else {
+108       // unconditional branches check arg 0
+109       if (!inst.ingredients.empty() && is_literal(inst.ingredients.at(0))) {
+110         trace(9992, "transform") << "jump " << inst.ingredients.at(0).name << ":offset" << end();
+111         continue;
+112       }
+113     }
+114     // if implicit, compute target
+115     reagent target;
+116     target.type = new type_tree("offset");
+117     target.set_value(0);
+118     if (open_braces.empty())
+119       raise << "'" << inst.old_name << "' needs a '{' before\n" << end();
+120     else if (inst.old_name.find("loop") != string::npos)
+121       target.set_value(open_braces.top()-index);
+122     else  // break instruction
+123       target.set_value(matching_brace(open_braces.top(), braces, r) - index - 1);
+124     inst.ingredients.push_back(target);
+125     // log computed target
+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 // returns a signed integer not just so that we can return -1 but also to
+134 // enable future signed arithmetic
+135 int matching_brace(int index, const list<pair<int, int> >& braces, recipe_ordinal r) {
+136   int stacksize = 0;
+137   for (list<pair<int, 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);  // exit current routine
+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 //: test how things actually run
+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 #    $print 1:num
+339     2:num <- multiply 2:num, 1:num
+340     1:num <- subtract 1:num, 1
+341     loop
+342   }
+343   4:num <- copy 2:num  # trigger a read
+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: '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: 'break-if' expects 1 or 2 ingredients, but got none
+362 
+363 //: Using break we can now implement conditional returns.
+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 // rewrite `return-if a, b, c, ...` to
+387 //   ```
+388 //   {
+389 //     break-unless a
+390 //     return b, c, ...
+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 // rewrite `return-unless a, b, c, ...` to
+403 //   ```
+404 //   {
+405 //     break-if a
+406 //     return b, c, ...
+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/*copy*/ 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   // <break command> <condition>
+431   instruction break_inst;
+432   break_inst.operation = get(Recipe_ordinal, break_command);
+433   break_inst.name = break_inst.old_name = break_command;
+434   break_inst.ingredients.push_back(condition);
+435   out.steps.push_back(break_inst);
+436 
+437   // return <return ingredients>
+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 //: Make sure these pseudo recipes get consistent numbers in all tests, even
+451 //: though they aren't implemented. Allows greater flexibility in ordering
+452 //: transforms.
+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;
 
-- cgit 1.4.1-2-gfad0