1 //: Boolean primitives
  2 
  3 :(before "End Primitive Recipe Declarations")
  4 AND,
  5 :(before "End Primitive Recipe Numbers")
  6 put(Recipe_ordinal, "and", AND);
  7 :(before "End Primitive Recipe Checks")
  8 case AND: {
  9   for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
 10     if (!is_mu_scalar(inst.ingredients.at(i))) {
 11       raise << maybe(get(Recipe, r).name) << "'and' requires boolean ingredients, but got '" << inst.ingredients.at(i).original_string << "'\n" << end();
 12       goto finish_checking_instruction;
 13     }
 14   }
 15   if (SIZE(inst.products) > 1) {
 16     raise << maybe(get(Recipe, r).name) << "'and' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
 17     break;
 18   }
 19   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_boolean(inst.products.at(0))) {
 20     raise << maybe(get(Recipe, r).name) << "'and' should yield a boolean, but got '" << inst.products.at(0).original_string << "'\n" << end();
 21     break;
 22   }
 23   break;
 24 }
 25 :(before "End Primitive Recipe Implementations")
 26 case AND: {
 27   bool result = true;
 28   for (int i = 0;  i < SIZE(ingredients);  ++i)
 29     result = result && scalar_ingredient(ingredients, i);
 30   products.resize(1);
 31   products.at(0).push_back(result);
 32   break;
 33 }
 34 :(code)
 35 double scalar_ingredient(const vector<vector<double> >& ingredients, int i) {
 36   if (is_mu_address(current_instruction().ingredients.at(i)))
 37     return ingredients.at(i).at(/*skip alloc id*/1);
 38   return ingredients.at(i).at(0);
 39 }
 40 
 41 :(scenario and)
 42 def main [
 43   1:bool <- copy true
 44   2:bool <- copy false
 45   3:bool <- and 1:bool, 2:bool
 46 ]
 47 +mem: storing 0 in location 3
 48 
 49 :(scenario and_2)
 50 def main [
 51   1:bool <- and true, true
 52 ]
 53 +mem: storing 1 in location 1
 54 
 55 :(scenario and_multiple)
 56 def main [
 57   1:bool <- and true, true, false
 58 ]
 59 +mem: storing 0 in location 1
 60 
 61 :(scenario and_multiple_2)
 62 def main [
 63   1:bool <- and true, true, true
 64 ]
 65 +mem: storing 1 in location 1
 66 
 67 :(before "End Primitive Recipe Declarations")
 68 OR,
 69 :(before "End Primitive Recipe Numbers")
 70 put(Recipe_ordinal, "or", OR);
 71 :(before "End Primitive Recipe Checks")
 72 case OR: {
 73   for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
 74     if (!is_mu_scalar(inst.ingredients.at(i))) {
 75       raise << maybe(get(Recipe, r).name) << "'and' requires boolean ingredients, but got '" << inst.ingredients.at(i).original_string << "'\n" << end();
 76       goto finish_checking_instruction;
 77     }
 78   }
 79   if (SIZE(inst.products) > 1) {
 80     raise << maybe(get(Recipe, r).name) << "'or' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
 81     break;
 82   }
 83   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_boolean(inst.products.at(0))) {
 84     raise << maybe(get(Recipe, r).name) << "'or' should yield a boolean, but got '" << inst.products.at(0).original_string << "'\n" << end();
 85     break;
 86   }
 87   break;
 88 }
 89 :(before "End Primitive Recipe Implementations")
 90 case OR: {
 91   bool result = false;
 92   for (int i = 0;  i < SIZE(ingredients);  ++i)
 93     result = result || scalar_ingredient(ingredients, i);
 94   products.resize(1);
 95   products.at(0).push_back(result);
 96   break;
 97 }
 98 
 99 :(scenario or)
100 def main [
101   1:bool <- copy true
102   2:bool <- copy false
103   3:bool <- or 1:bool, 2:bool
104 ]
105 +mem: storing 1 in location 3
106 
107 :(scenario or_2)
108 def main [
109   1:bool <- or false, false
110 ]
111 +mem: storing 0 in location 1
112 
113 :(scenario or_multiple)
114 def main [
115   1:bool <- or false, false, false
116 ]
117 +mem: storing 0 in location 1
118 
119 :(scenario or_multiple_2)
120 def main [
121   1:bool <- or false, false, true
122 ]
123 +mem: storing 1 in location 1
124 
125 :(before "End Primitive Recipe Declarations")
126 NOT,
127 :(before "End Primitive Recipe Numbers")
128 put(Recipe_ordinal, "not", NOT);
129 :(before "End Primitive Recipe Checks")
130 case NOT: {
131   if (SIZE(inst.products) != SIZE(inst.ingredients)) {
132     raise << "ingredients and products should match in '" << to_original_string(inst) << "'\n" << end();
133     break;
134   }
135   for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
136     if (!is_mu_scalar(inst.ingredients.at(i)) && !is_mu_address(inst.ingredients.at(i))) {
137       raise << maybe(get(Recipe, r).name) << "'not' requires ingredients that can be interpreted as boolean, but got '" << inst.ingredients.at(i).original_string << "'\n" << end();
138       goto finish_checking_instruction;
139     }
140   }
141   for (int i = 0;  i < SIZE(inst.products);  ++i) {
142     if (is_dummy(inst.products.at(i))) continue;
143     if (!is_mu_boolean(inst.products.at(i))) {
144       raise << maybe(get(Recipe, r).name) << "'not' should yield a boolean, but got '" << inst.products.at(i).original_string << "'\n" << end();
145       goto finish_checking_instruction;
146     }
147   }
148   break;
149 }
150 :(before "End Primitive Recipe Implementations")
151 case NOT: {
152   products.resize(SIZE(ingredients));
153   for (int i = 0;  i < SIZE(ingredients);  ++i) {
154     products.at(i).push_back(!scalar_ingredient(ingredients, i));
155   }
156   break;
157 }
158 
159 :(scenario not)
160 def main [
161   1:bool <- copy true
162   2:bool <- not 1:bool
163 ]
164 +mem: storing 0 in location 2
165 
166 :(scenario not_multiple)
167 def main [
168   1:bool, 2:bool, 3:bool <- not true, false, true
169 ]
170 +mem: storing 0 in location 1
171 +mem: storing 1 in location 2
172 +mem: storing 0 in location 3