fix other mandelbrot variants
[mu.git] / archive / 1.vm / 022arithmetic.cc
blob05e79e3bff6b5efebb0ee0737015f27abf12b06a
1 //: Arithmetic primitives
3 :(before "End Primitive Recipe Declarations")
4 ADD,
5 :(before "End Primitive Recipe Numbers")
6 put(Recipe_ordinal, "add", ADD);
7 :(before "End Primitive Recipe Checks")
8 case ADD: {
9 // primary goal of these checks is to forbid address arithmetic
10 for (int i = 0; i < SIZE(inst.ingredients); ++i) {
11 if (!is_mu_number(inst.ingredients.at(i))) {
12 raise << maybe(get(Recipe, r).name) << "'add' requires number ingredients, but got '" << inst.ingredients.at(i).original_string << "'\n" << end();
13 goto finish_checking_instruction;
16 if (SIZE(inst.products) > 1) {
17 raise << maybe(get(Recipe, r).name) << "'add' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
18 break;
20 if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
21 raise << maybe(get(Recipe, r).name) << "'add' should yield a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
22 break;
24 break;
26 :(before "End Primitive Recipe Implementations")
27 case ADD: {
28 double result = 0;
29 for (int i = 0; i < SIZE(ingredients); ++i) {
30 result += ingredients.at(i).at(0);
32 products.resize(1);
33 products.at(0).push_back(result);
34 break;
37 :(code)
38 void test_add_literal() {
39 run(
40 "def main [\n"
41 " 1:num <- add 23, 34\n"
42 "]\n"
44 CHECK_TRACE_CONTENTS(
45 "mem: storing 57 in location 1\n"
49 void test_add() {
50 run(
51 "def main [\n"
52 " 1:num <- copy 23\n"
53 " 2:num <- copy 34\n"
54 " 3:num <- add 1:num, 2:num\n"
55 "]\n"
57 CHECK_TRACE_CONTENTS(
58 "mem: storing 57 in location 3\n"
62 void test_add_multiple() {
63 run(
64 "def main [\n"
65 " 1:num <- add 3, 4, 5\n"
66 "]\n"
68 CHECK_TRACE_CONTENTS(
69 "mem: storing 12 in location 1\n"
73 void test_add_checks_type() {
74 Hide_errors = true;
75 run(
76 "def main [\n"
77 " 1:num <- add 2:bool, 1\n"
78 "]\n"
80 CHECK_TRACE_CONTENTS(
81 "error: main: 'add' requires number ingredients, but got '2:bool'\n"
85 void test_add_checks_return_type() {
86 Hide_errors = true;
87 run(
88 "def main [\n"
89 " 1:&:num <- add 2, 2\n"
90 "]\n"
92 CHECK_TRACE_CONTENTS(
93 "error: main: 'add' should yield a number, but got '1:&:num'\n"
97 :(before "End Primitive Recipe Declarations")
98 SUBTRACT,
99 :(before "End Primitive Recipe Numbers")
100 put(Recipe_ordinal, "subtract", SUBTRACT);
101 :(before "End Primitive Recipe Checks")
102 case SUBTRACT: {
103 if (inst.ingredients.empty()) {
104 raise << maybe(get(Recipe, r).name) << "'subtract' has no ingredients\n" << end();
105 break;
107 for (int i = 0; i < SIZE(inst.ingredients); ++i) {
108 if (!is_mu_number(inst.ingredients.at(i))) {
109 raise << maybe(get(Recipe, r).name) << "'subtract' requires number ingredients, but got '" << inst.ingredients.at(i).original_string << "'\n" << end();
110 goto finish_checking_instruction;
113 if (SIZE(inst.products) > 1) {
114 raise << maybe(get(Recipe, r).name) << "'subtract' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
115 break;
117 if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
118 raise << maybe(get(Recipe, r).name) << "'subtract' should yield a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
119 break;
121 break;
123 :(before "End Primitive Recipe Implementations")
124 case SUBTRACT: {
125 double result = ingredients.at(0).at(0);
126 for (int i = 1; i < SIZE(ingredients); ++i)
127 result -= ingredients.at(i).at(0);
128 products.resize(1);
129 products.at(0).push_back(result);
130 break;
133 :(code)
134 void test_subtract_literal() {
135 run(
136 "def main [\n"
137 " 1:num <- subtract 5, 2\n"
138 "]\n"
140 CHECK_TRACE_CONTENTS(
141 "mem: storing 3 in location 1\n"
145 void test_subtract() {
146 run(
147 "def main [\n"
148 " 1:num <- copy 23\n"
149 " 2:num <- copy 34\n"
150 " 3:num <- subtract 1:num, 2:num\n"
151 "]\n"
153 CHECK_TRACE_CONTENTS(
154 "mem: storing -11 in location 3\n"
158 void test_subtract_multiple() {
159 run(
160 "def main [\n"
161 " 1:num <- subtract 6, 3, 2\n"
162 "]\n"
164 CHECK_TRACE_CONTENTS(
165 "mem: storing 1 in location 1\n"
169 :(before "End Primitive Recipe Declarations")
170 MULTIPLY,
171 :(before "End Primitive Recipe Numbers")
172 put(Recipe_ordinal, "multiply", MULTIPLY);
173 :(before "End Primitive Recipe Checks")
174 case MULTIPLY: {
175 for (int i = 0; i < SIZE(inst.ingredients); ++i) {
176 if (!is_mu_number(inst.ingredients.at(i))) {
177 raise << maybe(get(Recipe, r).name) << "'multiply' requires number ingredients, but got '" << inst.ingredients.at(i).original_string << "'\n" << end();
178 goto finish_checking_instruction;
181 if (SIZE(inst.products) > 1) {
182 raise << maybe(get(Recipe, r).name) << "'multiply' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
183 break;
185 if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
186 raise << maybe(get(Recipe, r).name) << "'multiply' should yield a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
187 break;
189 break;
191 :(before "End Primitive Recipe Implementations")
192 case MULTIPLY: {
193 double result = 1;
194 for (int i = 0; i < SIZE(ingredients); ++i) {
195 result *= ingredients.at(i).at(0);
197 products.resize(1);
198 products.at(0).push_back(result);
199 break;
202 :(code)
203 void test_multiply_literal() {
204 run(
205 "def main [\n"
206 " 1:num <- multiply 2, 3\n"
207 "]\n"
209 CHECK_TRACE_CONTENTS(
210 "mem: storing 6 in location 1\n"
214 void test_multiply() {
215 run(
216 "def main [\n"
217 " 1:num <- copy 4\n"
218 " 2:num <- copy 6\n"
219 " 3:num <- multiply 1:num, 2:num\n"
220 "]\n"
222 CHECK_TRACE_CONTENTS(
223 "mem: storing 24 in location 3\n"
227 void test_multiply_multiple() {
228 run(
229 "def main [\n"
230 " 1:num <- multiply 2, 3, 4\n"
231 "]\n"
233 CHECK_TRACE_CONTENTS(
234 "mem: storing 24 in location 1\n"
238 :(before "End Primitive Recipe Declarations")
239 DIVIDE,
240 :(before "End Primitive Recipe Numbers")
241 put(Recipe_ordinal, "divide", DIVIDE);
242 :(before "End Primitive Recipe Checks")
243 case DIVIDE: {
244 if (inst.ingredients.empty()) {
245 raise << maybe(get(Recipe, r).name) << "'divide' has no ingredients\n" << end();
246 break;
248 for (int i = 0; i < SIZE(inst.ingredients); ++i) {
249 if (!is_mu_number(inst.ingredients.at(i))) {
250 raise << maybe(get(Recipe, r).name) << "'divide' requires number ingredients, but got '" << inst.ingredients.at(i).original_string << "'\n" << end();
251 goto finish_checking_instruction;
254 if (SIZE(inst.products) > 1) {
255 raise << maybe(get(Recipe, r).name) << "'divide' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
256 break;
258 if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
259 raise << maybe(get(Recipe, r).name) << "'divide' should yield a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
260 break;
262 break;
264 :(before "End Primitive Recipe Implementations")
265 case DIVIDE: {
266 double result = ingredients.at(0).at(0);
267 for (int i = 1; i < SIZE(ingredients); ++i)
268 result /= ingredients.at(i).at(0);
269 products.resize(1);
270 products.at(0).push_back(result);
271 break;
274 :(code)
275 void test_divide_literal() {
276 run(
277 "def main [\n"
278 " 1:num <- divide 8, 2\n"
279 "]\n"
281 CHECK_TRACE_CONTENTS(
282 "mem: storing 4 in location 1\n"
286 void test_divide() {
287 run(
288 "def main [\n"
289 " 1:num <- copy 27\n"
290 " 2:num <- copy 3\n"
291 " 3:num <- divide 1:num, 2:num\n"
292 "]\n"
294 CHECK_TRACE_CONTENTS(
295 "mem: storing 9 in location 3\n"
299 void test_divide_multiple() {
300 run(
301 "def main [\n"
302 " 1:num <- divide 12, 3, 2\n"
303 "]\n"
305 CHECK_TRACE_CONTENTS(
306 "mem: storing 2 in location 1\n"
310 //: Integer division
312 :(before "End Primitive Recipe Declarations")
313 DIVIDE_WITH_REMAINDER,
314 :(before "End Primitive Recipe Numbers")
315 put(Recipe_ordinal, "divide-with-remainder", DIVIDE_WITH_REMAINDER);
316 :(before "End Primitive Recipe Checks")
317 case DIVIDE_WITH_REMAINDER: {
318 if (SIZE(inst.ingredients) != 2) {
319 raise << maybe(get(Recipe, r).name) << "'divide-with-remainder' requires exactly two ingredients, but got '" << to_original_string(inst) << "'\n" << end();
320 break;
322 if (!is_mu_number(inst.ingredients.at(0)) || !is_mu_number(inst.ingredients.at(1))) {
323 raise << maybe(get(Recipe, r).name) << "'divide-with-remainder' requires number ingredients, but got '" << to_original_string(inst) << "'\n" << end();
324 break;
326 if (SIZE(inst.products) > 2) {
327 raise << maybe(get(Recipe, r).name) << "'divide-with-remainder' yields two products in '" << to_original_string(inst) << "'\n" << end();
328 break;
330 for (int i = 0; i < SIZE(inst.products); ++i) {
331 if (!is_dummy(inst.products.at(i)) && !is_mu_number(inst.products.at(i))) {
332 raise << maybe(get(Recipe, r).name) << "'divide-with-remainder' should yield a number, but got '" << inst.products.at(i).original_string << "'\n" << end();
333 goto finish_checking_instruction;
336 break;
338 :(before "End Primitive Recipe Implementations")
339 case DIVIDE_WITH_REMAINDER: {
340 products.resize(2);
341 // fractions will be dropped; very large numbers will overflow
342 long long int a = static_cast<long long int>(ingredients.at(0).at(0));
343 long long int b = static_cast<long long int>(ingredients.at(1).at(0));
344 if (b == 0) {
345 raise << maybe(current_recipe_name()) << "divide by zero in '" << to_original_string(current_instruction()) << "'\n" << end();
346 products.resize(2);
347 products.at(0).push_back(0);
348 products.at(1).push_back(0);
349 break;
351 long long int quotient = a / b;
352 long long int remainder = a % b;
353 products.at(0).push_back(static_cast<double>(quotient));
354 products.at(1).push_back(static_cast<double>(remainder));
355 break;
358 :(code)
359 void test_divide_with_remainder_literal() {
360 run(
361 "def main [\n"
362 " 1:num, 2:num <- divide-with-remainder 9, 2\n"
363 "]\n"
365 CHECK_TRACE_CONTENTS(
366 "mem: storing 4 in location 1\n"
367 "mem: storing 1 in location 2\n"
371 void test_divide_with_remainder() {
372 run(
373 "def main [\n"
374 " 1:num <- copy 27\n"
375 " 2:num <- copy 11\n"
376 " 3:num, 4:num <- divide-with-remainder 1:num, 2:num\n"
377 "]\n"
379 CHECK_TRACE_CONTENTS(
380 "mem: storing 2 in location 3\n"
381 "mem: storing 5 in location 4\n"
385 void test_divide_with_decimal_point() {
386 run(
387 "def main [\n"
388 " 1:num <- divide 5, 2\n"
389 "]\n"
391 CHECK_TRACE_CONTENTS(
392 "mem: storing 2.5 in location 1\n"
396 void test_divide_by_zero() {
397 run(
398 "def main [\n"
399 " 1:num <- divide 4, 0\n"
400 "]\n"
402 CHECK_TRACE_CONTENTS(
403 "mem: storing inf in location 1\n"
407 void test_divide_by_zero_2() {
408 Hide_errors = true;
409 run(
410 "def main [\n"
411 " 1:num <- divide-with-remainder 4, 0\n"
412 "]\n"
414 // integer division can't return floating-point infinity
415 CHECK_TRACE_CONTENTS(
416 "error: main: divide by zero in '1:num <- divide-with-remainder 4, 0'\n"
420 //: Bitwise shifts
422 :(before "End Primitive Recipe Declarations")
423 SHIFT_LEFT,
424 :(before "End Primitive Recipe Numbers")
425 put(Recipe_ordinal, "shift-left", SHIFT_LEFT);
426 :(before "End Primitive Recipe Checks")
427 case SHIFT_LEFT: {
428 if (SIZE(inst.ingredients) != 2) {
429 raise << maybe(get(Recipe, r).name) << "'shift-left' requires exactly two ingredients, but got '" << to_original_string(inst) << "'\n" << end();
430 break;
432 if (!is_mu_number(inst.ingredients.at(0)) || !is_mu_number(inst.ingredients.at(1))) {
433 raise << maybe(get(Recipe, r).name) << "'shift-left' requires number ingredients, but got '" << to_original_string(inst) << "'\n" << end();
434 break;
436 if (SIZE(inst.products) > 1) {
437 raise << maybe(get(Recipe, r).name) << "'shift-left' yields one product in '" << to_original_string(inst) << "'\n" << end();
438 break;
440 if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
441 raise << maybe(get(Recipe, r).name) << "'shift-left' should yield a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
442 goto finish_checking_instruction;
444 break;
446 :(before "End Primitive Recipe Implementations")
447 case SHIFT_LEFT: {
448 // ingredients must be integers
449 int a = static_cast<int>(ingredients.at(0).at(0));
450 int b = static_cast<int>(ingredients.at(1).at(0));
451 products.resize(1);
452 if (b < 0) {
453 raise << maybe(current_recipe_name()) << "second ingredient can't be negative in '" << to_original_string(current_instruction()) << "'\n" << end();
454 products.at(0).push_back(0);
455 break;
457 products.at(0).push_back(a<<b);
458 break;
461 :(code)
462 void test_shift_left_by_zero() {
463 run(
464 "def main [\n"
465 " 1:num <- shift-left 1, 0\n"
466 "]\n"
468 CHECK_TRACE_CONTENTS(
469 "mem: storing 1 in location 1\n"
473 void test_shift_left_1() {
474 run(
475 "def main [\n"
476 " 1:num <- shift-left 1, 4\n"
477 "]\n"
479 CHECK_TRACE_CONTENTS(
480 "mem: storing 16 in location 1\n"
484 void test_shift_left_2() {
485 run(
486 "def main [\n"
487 " 1:num <- shift-left 3, 2\n"
488 "]\n"
490 CHECK_TRACE_CONTENTS(
491 "mem: storing 12 in location 1\n"
495 void test_shift_left_by_negative() {
496 Hide_errors = true;
497 run(
498 "def main [\n"
499 " 1:num <- shift-left 3, -1\n"
500 "]\n"
502 CHECK_TRACE_CONTENTS(
503 "error: main: second ingredient can't be negative in '1:num <- shift-left 3, -1'\n"
507 void test_shift_left_ignores_fractional_part() {
508 run(
509 "def main [\n"
510 " 1:num <- divide 3, 2\n"
511 " 2:num <- shift-left 1:num, 1\n"
512 "]\n"
514 CHECK_TRACE_CONTENTS(
515 "mem: storing 2 in location 2\n"
519 :(before "End Primitive Recipe Declarations")
520 SHIFT_RIGHT,
521 :(before "End Primitive Recipe Numbers")
522 put(Recipe_ordinal, "shift-right", SHIFT_RIGHT);
523 :(before "End Primitive Recipe Checks")
524 case SHIFT_RIGHT: {
525 if (SIZE(inst.ingredients) != 2) {
526 raise << maybe(get(Recipe, r).name) << "'shift-right' requires exactly two ingredients, but got '" << to_original_string(inst) << "'\n" << end();
527 break;
529 if (!is_mu_number(inst.ingredients.at(0)) || !is_mu_number(inst.ingredients.at(1))) {
530 raise << maybe(get(Recipe, r).name) << "'shift-right' requires number ingredients, but got '" << to_original_string(inst) << "'\n" << end();
531 break;
533 if (SIZE(inst.products) > 1) {
534 raise << maybe(get(Recipe, r).name) << "'shift-right' yields one product in '" << to_original_string(inst) << "'\n" << end();
535 break;
537 if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
538 raise << maybe(get(Recipe, r).name) << "'shift-right' should yield a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
539 goto finish_checking_instruction;
541 break;
543 :(before "End Primitive Recipe Implementations")
544 case SHIFT_RIGHT: {
545 // ingredients must be integers
546 int a = static_cast<int>(ingredients.at(0).at(0));
547 int b = static_cast<int>(ingredients.at(1).at(0));
548 products.resize(1);
549 if (b < 0) {
550 raise << maybe(current_recipe_name()) << "second ingredient can't be negative in '" << to_original_string(current_instruction()) << "'\n" << end();
551 products.at(0).push_back(0);
552 break;
554 products.at(0).push_back(a>>b);
555 break;
558 :(code)
559 void test_shift_right_by_zero() {
560 run(
561 "def main [\n"
562 " 1:num <- shift-right 1, 0\n"
563 "]\n"
565 CHECK_TRACE_CONTENTS(
566 "mem: storing 1 in location 1\n"
570 void test_shift_right_1() {
571 run(
572 "def main [\n"
573 " 1:num <- shift-right 1024, 1\n"
574 "]\n"
576 CHECK_TRACE_CONTENTS(
577 "mem: storing 512 in location 1\n"
581 void test_shift_right_2() {
582 run(
583 "def main [\n"
584 " 1:num <- shift-right 3, 1\n"
585 "]\n"
587 CHECK_TRACE_CONTENTS(
588 "mem: storing 1 in location 1\n"
592 void test_shift_right_by_negative() {
593 Hide_errors = true;
594 run(
595 "def main [\n"
596 " 1:num <- shift-right 4, -1\n"
597 "]\n"
599 CHECK_TRACE_CONTENTS(
600 "error: main: second ingredient can't be negative in '1:num <- shift-right 4, -1'\n"
604 void test_shift_right_ignores_fractional_part() {
605 run(
606 "def main [\n"
607 " 1:num <- divide 3, 2\n"
608 " 2:num <- shift-right 1:num, 1\n"
609 "]\n"
611 CHECK_TRACE_CONTENTS(
612 "mem: storing 0 in location 2\n"
616 :(before "End Primitive Recipe Declarations")
617 AND_BITS,
618 :(before "End Primitive Recipe Numbers")
619 put(Recipe_ordinal, "and-bits", AND_BITS);
620 :(before "End Primitive Recipe Checks")
621 case AND_BITS: {
622 if (SIZE(inst.ingredients) != 2) {
623 raise << maybe(get(Recipe, r).name) << "'and-bits' requires exactly two ingredients, but got '" << to_original_string(inst) << "'\n" << end();
624 break;
626 if (!is_mu_number(inst.ingredients.at(0)) || !is_mu_number(inst.ingredients.at(1))) {
627 raise << maybe(get(Recipe, r).name) << "'and-bits' requires number ingredients, but got '" << to_original_string(inst) << "'\n" << end();
628 break;
630 if (SIZE(inst.products) > 1) {
631 raise << maybe(get(Recipe, r).name) << "'and-bits' yields one product in '" << to_original_string(inst) << "'\n" << end();
632 break;
634 if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
635 raise << maybe(get(Recipe, r).name) << "'and-bits' should yield a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
636 goto finish_checking_instruction;
638 break;
640 :(before "End Primitive Recipe Implementations")
641 case AND_BITS: {
642 // ingredients must be integers
643 int a = static_cast<int>(ingredients.at(0).at(0));
644 int b = static_cast<int>(ingredients.at(1).at(0));
645 products.resize(1);
646 products.at(0).push_back(a&b);
647 break;
650 :(code)
651 void test_and_bits_1() {
652 run(
653 "def main [\n"
654 " 1:num <- and-bits 8, 3\n"
655 "]\n"
657 CHECK_TRACE_CONTENTS(
658 "mem: storing 0 in location 1\n"
662 void test_and_bits_2() {
663 run(
664 "def main [\n"
665 " 1:num <- and-bits 3, 2\n"
666 "]\n"
668 CHECK_TRACE_CONTENTS(
669 "mem: storing 2 in location 1\n"
673 void test_and_bits_3() {
674 run(
675 "def main [\n"
676 " 1:num <- and-bits 14, 3\n"
677 "]\n"
679 CHECK_TRACE_CONTENTS(
680 "mem: storing 2 in location 1\n"
684 void test_and_bits_negative() {
685 run(
686 "def main [\n"
687 " 1:num <- and-bits -3, 4\n"
688 "]\n"
690 CHECK_TRACE_CONTENTS(
691 "mem: storing 4 in location 1\n"
695 :(before "End Primitive Recipe Declarations")
696 OR_BITS,
697 :(before "End Primitive Recipe Numbers")
698 put(Recipe_ordinal, "or-bits", OR_BITS);
699 :(before "End Primitive Recipe Checks")
700 case OR_BITS: {
701 if (SIZE(inst.ingredients) != 2) {
702 raise << maybe(get(Recipe, r).name) << "'or-bits' requires exactly two ingredients, but got '" << to_original_string(inst) << "'\n" << end();
703 break;
705 if (!is_mu_number(inst.ingredients.at(0)) || !is_mu_number(inst.ingredients.at(1))) {
706 raise << maybe(get(Recipe, r).name) << "'or-bits' requires number ingredients, but got '" << to_original_string(inst) << "'\n" << end();
707 break;
709 if (SIZE(inst.products) > 1) {
710 raise << maybe(get(Recipe, r).name) << "'or-bits' yields one product in '" << to_original_string(inst) << "'\n" << end();
711 break;
713 if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
714 raise << maybe(get(Recipe, r).name) << "'or-bits' should yield a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
715 goto finish_checking_instruction;
717 break;
719 :(before "End Primitive Recipe Implementations")
720 case OR_BITS: {
721 // ingredients must be integers
722 int a = static_cast<int>(ingredients.at(0).at(0));
723 int b = static_cast<int>(ingredients.at(1).at(0));
724 products.resize(1);
725 products.at(0).push_back(a|b);
726 break;
729 :(code)
730 void test_or_bits_1() {
731 run(
732 "def main [\n"
733 " 1:num <- or-bits 3, 8\n"
734 "]\n"
736 CHECK_TRACE_CONTENTS(
737 "mem: storing 11 in location 1\n"
741 void test_or_bits_2() {
742 run(
743 "def main [\n"
744 " 1:num <- or-bits 3, 10\n"
745 "]\n"
747 CHECK_TRACE_CONTENTS(
748 "mem: storing 11 in location 1\n"
752 void test_or_bits_3() {
753 run(
754 "def main [\n"
755 " 1:num <- or-bits 4, 6\n"
756 "]\n"
758 CHECK_TRACE_CONTENTS(
759 "mem: storing 6 in location 1\n"
763 :(before "End Primitive Recipe Declarations")
764 XOR_BITS,
765 :(before "End Primitive Recipe Numbers")
766 put(Recipe_ordinal, "xor-bits", XOR_BITS);
767 :(before "End Primitive Recipe Checks")
768 case XOR_BITS: {
769 if (SIZE(inst.ingredients) != 2) {
770 raise << maybe(get(Recipe, r).name) << "'xor-bits' requires exactly two ingredients, but got '" << to_original_string(inst) << "'\n" << end();
771 break;
773 if (!is_mu_number(inst.ingredients.at(0)) || !is_mu_number(inst.ingredients.at(1))) {
774 raise << maybe(get(Recipe, r).name) << "'xor-bits' requires number ingredients, but got '" << to_original_string(inst) << "'\n" << end();
775 break;
777 if (SIZE(inst.products) > 1) {
778 raise << maybe(get(Recipe, r).name) << "'xor-bits' yields one product in '" << to_original_string(inst) << "'\n" << end();
779 break;
781 if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
782 raise << maybe(get(Recipe, r).name) << "'xor-bits' should yield a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
783 goto finish_checking_instruction;
785 break;
787 :(before "End Primitive Recipe Implementations")
788 case XOR_BITS: {
789 // ingredients must be integers
790 int a = static_cast<int>(ingredients.at(0).at(0));
791 int b = static_cast<int>(ingredients.at(1).at(0));
792 products.resize(1);
793 products.at(0).push_back(a^b);
794 break;
797 :(code)
798 void test_xor_bits_1() {
799 run(
800 "def main [\n"
801 " 1:num <- xor-bits 3, 8\n"
802 "]\n"
804 CHECK_TRACE_CONTENTS(
805 "mem: storing 11 in location 1\n"
809 void test_xor_bits_2() {
810 run(
811 "def main [\n"
812 " 1:num <- xor-bits 3, 10\n"
813 "]\n"
815 CHECK_TRACE_CONTENTS(
816 "mem: storing 9 in location 1\n"
820 void test_xor_bits_3() {
821 run(
822 "def main [\n"
823 " 1:num <- xor-bits 4, 6\n"
824 "]\n"
826 CHECK_TRACE_CONTENTS(
827 "mem: storing 2 in location 1\n"
831 :(before "End Primitive Recipe Declarations")
832 FLIP_BITS,
833 :(before "End Primitive Recipe Numbers")
834 put(Recipe_ordinal, "flip-bits", FLIP_BITS);
835 :(before "End Primitive Recipe Checks")
836 case FLIP_BITS: {
837 if (SIZE(inst.ingredients) != 1) {
838 raise << maybe(get(Recipe, r).name) << "'flip-bits' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
839 break;
841 if (!is_mu_number(inst.ingredients.at(0))) {
842 raise << maybe(get(Recipe, r).name) << "'flip-bits' requires a number ingredient, but got '" << to_original_string(inst) << "'\n" << end();
843 break;
845 if (SIZE(inst.products) > 1) {
846 raise << maybe(get(Recipe, r).name) << "'flip-bits' yields one product in '" << to_original_string(inst) << "'\n" << end();
847 break;
849 if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
850 raise << maybe(get(Recipe, r).name) << "'flip-bits' should yield a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
851 goto finish_checking_instruction;
853 break;
855 :(before "End Primitive Recipe Implementations")
856 case FLIP_BITS: {
857 // ingredient must be integer
858 int a = static_cast<int>(ingredients.at(0).at(0));
859 products.resize(1);
860 products.at(0).push_back(~a);
861 break;
864 :(code)
865 void test_flip_bits_zero() {
866 run(
867 "def main [\n"
868 " 1:num <- flip-bits 0\n"
869 "]\n"
871 CHECK_TRACE_CONTENTS(
872 "mem: storing -1 in location 1\n"
876 void test_flip_bits_negative() {
877 run(
878 "def main [\n"
879 " 1:num <- flip-bits -1\n"
880 "]\n"
882 CHECK_TRACE_CONTENTS(
883 "mem: storing 0 in location 1\n"
887 void test_flip_bits_1() {
888 run(
889 "def main [\n"
890 " 1:num <- flip-bits 3\n"
891 "]\n"
893 CHECK_TRACE_CONTENTS(
894 "mem: storing -4 in location 1\n"
898 void test_flip_bits_2() {
899 run(
900 "def main [\n"
901 " 1:num <- flip-bits 12\n"
902 "]\n"
904 CHECK_TRACE_CONTENTS(
905 "mem: storing -13 in location 1\n"
909 :(before "End Primitive Recipe Declarations")
910 ROUND,
911 :(before "End Primitive Recipe Numbers")
912 put(Recipe_ordinal, "round", ROUND);
913 :(before "End Primitive Recipe Checks")
914 case ROUND: {
915 if (SIZE(inst.ingredients) != 1) {
916 raise << maybe(get(Recipe, r).name) << "'round' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
917 break;
919 if (!is_mu_number(inst.ingredients.at(0))) {
920 raise << maybe(get(Recipe, r).name) << "first ingredient of 'round' should be a number, but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
921 break;
923 break;
925 :(before "End Primitive Recipe Implementations")
926 case ROUND: {
927 products.resize(1);
928 products.at(0).push_back(rint(ingredients.at(0).at(0)));
929 break;
932 :(code)
933 void test_round_to_nearest_integer() {
934 run(
935 "def main [\n"
936 " 1:num <- round 12.2\n"
937 "]\n"
939 CHECK_TRACE_CONTENTS(
940 "mem: storing 12 in location 1\n"
944 void test_round_halves_toward_zero() {
945 run(
946 "def main [\n"
947 " 1:num <- round 12.5\n"
948 "]\n"
950 CHECK_TRACE_CONTENTS(
951 "mem: storing 12 in location 1\n"
955 void test_round_halves_toward_zero_2() {
956 run(
957 "def main [\n"
958 " 1:num <- round -12.5\n"
959 "]\n"
961 CHECK_TRACE_CONTENTS(
962 "mem: storing -12 in location 1\n"
966 :(before "End Primitive Recipe Declarations")
967 TRUNCATE,
968 :(before "End Primitive Recipe Numbers")
969 put(Recipe_ordinal, "truncate", TRUNCATE);
970 :(before "End Primitive Recipe Checks")
971 case TRUNCATE: {
972 if (SIZE(inst.ingredients) != 1) {
973 raise << maybe(get(Recipe, r).name) << "'truncate' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
974 break;
976 if (!is_mu_number(inst.ingredients.at(0))) {
977 raise << maybe(get(Recipe, r).name) << "first ingredient of 'truncate' should be a number, but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
978 break;
980 break;
982 :(before "End Primitive Recipe Implementations")
983 case TRUNCATE: {
984 products.resize(1);
985 products.at(0).push_back(trunc(ingredients.at(0).at(0)));
986 break;
989 :(code)
990 void test_truncate_to_nearest_integer() {
991 run(
992 "def main [\n"
993 " 1:num <- truncate 12.2\n"
994 "]\n"
996 CHECK_TRACE_CONTENTS(
997 "mem: storing 12 in location 1\n"
1001 void test_truncate_negative() {
1002 run(
1003 "def main [\n"
1004 " 1:num <- truncate -12.2\n"
1005 "]\n"
1007 CHECK_TRACE_CONTENTS(
1008 "mem: storing -12 in location 1\n"
1012 :(before "End Primitive Recipe Declarations")
1013 SQUARE_ROOT,
1014 :(before "End Primitive Recipe Numbers")
1015 put(Recipe_ordinal, "square-root", SQUARE_ROOT);
1016 :(before "End Primitive Recipe Checks")
1017 case SQUARE_ROOT: {
1018 if (SIZE(inst.ingredients) != 1) {
1019 raise << maybe(get(Recipe, r).name) << "'square-root' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
1020 break;
1022 if (!is_mu_number(inst.ingredients.at(0))) {
1023 raise << maybe(get(Recipe, r).name) << "first ingredient of 'square-root' should be a number, but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
1024 break;
1026 break;
1028 :(before "End Primitive Recipe Implementations")
1029 case SQUARE_ROOT: {
1030 products.resize(1);
1031 products.at(0).push_back(sqrt(ingredients.at(0).at(0)));
1032 break;
1035 :(before "End Primitive Recipe Declarations")
1036 CHARACTER_TO_CODE,
1037 :(before "End Primitive Recipe Numbers")
1038 put(Recipe_ordinal, "character-to-code", CHARACTER_TO_CODE);
1039 :(before "End Primitive Recipe Checks")
1040 case CHARACTER_TO_CODE: {
1041 if (SIZE(inst.ingredients) != 1) {
1042 raise << maybe(get(Recipe, r).name) << "'character-to-code' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
1043 break;
1045 if (!is_mu_character(inst.ingredients.at(0))) {
1046 raise << maybe(get(Recipe, r).name) << "first ingredient of 'character-to-code' should be a character, but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
1047 break;
1049 if (SIZE(inst.products) != 1) {
1050 raise << maybe(get(Recipe, r).name) << "'character-to-code' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end();
1051 break;
1053 if (!is_mu_number(inst.products.at(0))) {
1054 raise << maybe(get(Recipe, r).name) << "first product of 'character-to-code' should be a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
1055 break;
1057 break;
1059 :(before "End Primitive Recipe Implementations")
1060 case CHARACTER_TO_CODE: {
1061 double result = 0;
1062 for (int i = 0; i < SIZE(ingredients); ++i) {
1063 result += ingredients.at(i).at(0);
1065 products.resize(1);
1066 products.at(0).push_back(result);
1067 break;
1070 :(before "End Includes")
1071 #include <math.h>