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