1 //: Jump primitives
  2 
  3 :(scenario jump_can_skip_instructions)
  4 def main [
  5   jump 1:offset
  6   1:num <- copy 1
  7 ]
  8 +run: jump {1: "offset"}
  9 -run: {1: "number"} <- copy {1: "literal"}
 10 -mem: storing 1 in location 1
 11 
 12 :(before "End Primitive Recipe Declarations")
 13 JUMP,
 14 :(before "End Primitive Recipe Numbers")
 15 put(Recipe_ordinal, "jump", JUMP);
 16 :(before "End Primitive Recipe Checks")
 17 case JUMP: {
 18   if (SIZE(inst.ingredients) != 1) {
 19     raise << maybe(get(Recipe, r).name) << "'jump' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end();
 20     break;
 21   }
 22   if (!is_literal(inst.ingredients.at(0))) {
 23     raise << maybe(get(Recipe, r).name) << "first ingredient of 'jump' should be a label or offset, but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
 24     break;
 25   }
 26   break;
 27 }
 28 :(before "End Primitive Recipe Implementations")
 29 case JUMP: {
 30   assert(current_instruction().ingredients.at(0).initialized);
 31   current_step_index() += ingredients.at(0).at(0)+1;
 32   trace(9998, "run") << "jumping to instruction " << current_step_index() << end();
 33   continue;  // skip rest of this instruction
 34 }
 35 
 36 //: special type to designate jump targets
 37 :(before "End Mu Types Initialization")
 38 put(Type_ordinal, "offset", 0);
 39 
 40 :(scenario jump_backward)
 41 def main [
 42   jump 1:offset  # 0 -+
 43   jump 3:offset  #    |   +-+ 1
 44                  #   \/  /\ |
 45   jump -2:offset #  2 +-->+ |
 46 ]                #         \/ 3
 47 +run: jump {1: "offset"}
 48 +run: jump {-2: "offset"}
 49 +run: jump {3: "offset"}
 50 
 51 :(before "End Primitive Recipe Declarations")
 52 JUMP_IF,
 53 :(before "End Primitive Recipe Numbers")
 54 put(Recipe_ordinal, "jump-if", JUMP_IF);
 55 :(before "End Primitive Recipe Checks")
 56 case JUMP_IF: {
 57   if (SIZE(inst.ingredients) != 2) {
 58     raise << maybe(get(Recipe, r).name) << "'jump-if' requires exactly two ingredients, but got '" << inst.original_string << "'\n" << end();
 59     break;
 60   }
 61   if (!is_mu_scalar(inst.ingredients.at(0))) {
 62     raise << maybe(get(Recipe, r).name) << "'jump-if' requires a boolean for its first ingredient, but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
 63     break;
 64   }
 65   if (!is_literal(inst.ingredients.at(1))) {
 66     raise << maybe(get(Recipe, r).name) << "'jump-if' requires a label or offset for its second ingredient, but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
 67     break;
 68   }
 69   // End JUMP_IF Checks
 70   break;
 71 }
 72 :(before "End Primitive Recipe Implementations")
 73 case JUMP_IF: {
 74   assert(current_instruction().ingredients.at(1).initialized);
 75   if (!ingredients.at(0).at(0)) {
 76     trace(9998, "run") << "jump-if fell through" << end();
 77     break;
 78   }
 79   current_step_index() += ingredients.at(1).at(0)+1;
 80   trace(9998, "run") << "jumping to instruction " << current_step_index() << end();
 81   continue;  // skip rest of this instruction
 82 }
 83 
 84 :(scenario jump_if)
 85 def main [
 86   jump-if 999, 1:offset
 87   123:num <- copy 1
 88 ]
 89 +run: jump-if {999: "literal"}, {1: "offset"}
 90 +run: jumping to instruction 2
 91 -run: {1: "number"} <- copy {1: "literal"}
 92 -mem: storing 1 in location 123
 93 
 94 :(scenario jump_if_fallthrough)
 95 def main [
 96   jump-if 0, 1:offset
 97   123:num <- copy 1
 98 ]
 99 +run: jump-if {0: "literal"}, {1: "offset"}
100 +run: jump-if fell through
101 +run: {123: "number"} <- copy {1: "literal"}
102 +mem: storing 1 in location 123
103 
104 :(before "End Primitive Recipe Declarations")
105 JUMP_UNLESS,
106 :(before "End Primitive Recipe Numbers")
107 put(Recipe_ordinal, "jump-unless", JUMP_UNLESS);
108 :(before "End Primitive Recipe Checks")
109 case JUMP_UNLESS: {
110   if (SIZE(inst.ingredients) != 2) {
111     raise << maybe(get(Recipe, r).name) << "'jump-unless' requires exactly two ingredients, but got '" << inst.original_string << "'\n" << end();
112     break;
113   }
114   if (!is_mu_scalar(inst.ingredients.at(0))) {
115     raise << maybe(get(Recipe, r).name) << "'jump-unless' requires a boolean for its first ingredient, but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
116     break;
117   }
118   if (!is_literal(inst.ingredients.at(1))) {
119     raise << maybe(get(Recipe, r).name) << "'jump-unless' requires a label or offset for its second ingredient, but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
120     break;
121   }
122   // End JUMP_UNLESS Checks
123   break;
124 }
125 :(before "End Primitive Recipe Implementations")
126 case JUMP_UNLESS: {
127   assert(current_instruction().ingredients.at(1).initialized);
128   if (ingredients.at(0).at(0)) {
129     trace(9998, "run") << "jump-unless fell through" << end();
130     break;
131   }
132   current_step_index() += ingredients.at(1).at(0)+1;
133   trace(9998, "run") << "jumping to instruction " << current_step_index() << end();
134   continue;  // skip rest of this instruction
135 }
136 
137 :(scenario jump_unless)
138 def main [
139   jump-unless 0, 1:offset
140   123:num <- copy 1
141 ]
142 +run: jump-unless {0: "literal"}, {1: "offset"}
143 +run: jumping to instruction 2
144 -run: {123: "number"} <- copy {1: "literal"}
145 -mem: storing 1 in location 123
146 
147 :(scenario jump_unless_fallthrough)
148 def main [
149   jump-unless 999, 1:offset
150   123:num <- copy 1
151 ]
152 +run: jump-unless {999: "literal"}, {1: "offset"}
153 +run: jump-unless fell through
154 +run: {123: "number"} <- copy {1: "literal"}
155 +mem: storing 1 in location 123