#ifndef BIN_OP_HPP__ #define BIN_OP_HPP__ /** * \file arith.hpp * \author Mason Smith * defines various arithmetic instructions */ #include "vm.hpp" #include "instr.hpp" #include namespace BMD { class Value; /** * bin_op_instr. * Creates an instruction which executes the binary operator visitor * on the accumulator and top stack element */ template class binary_op_instr : public Instruction { public: void execute(VM* vm) { Value arg1 = vm->get_accumulator(); Value arg2 = vm->get_stack_element(0); Value res = boost::apply_visitor(bin_op_visitor(), arg1, arg2); vm->set_accumulator(res); vm->pop(); } }; /** * un_op_instr. * Creates an instruction which executes the unary operator visitor * on the accumulator */ template class unary_op_instr : public Instruction { public: void execute(VM* vm) { Value arg1 = vm->get_accumulator(); Value res = boost::apply_visitor(un_op_visitor(), arg1); vm->set_accumulator(res); } }; /************************************** * arithmetic instructions *************************************/ /* Makes a class which does some simple binary operation on numbers. */ #define MAKE_BINARY_ARITH_INSTR(v, instr, op, name) class v : \ public boost::static_visitor \ { \ public: \ Value operator() (int i, int j) const { return Value(i op j); } \ Value operator() (double i, double j) const { return Value(i op j); } \ Value operator() (int i, double j) const { return Value(i op j); } \ Value operator() (double i, int j) const { return Value(i op j); } \ BINARY_DEFAULT_TO_TYPE_ERROR(name, name + string(": arguments are not numbers")) \ virtual ~v() { } \ }; \ typedef binary_op_instr instr /* Makes a class for comparisons on various values */ #define MAKE_COMPARISON_INSTR(v, instr, op, name) class v : \ public boost::static_visitor \ { \ public: \ Value operator() (int i, int j) const { return Value(i op j); } \ Value operator() (double i, double j) const { return Value(i op j); } \ Value operator() (int i, double j) const { return Value(i op j); } \ Value operator() (double i, int j) const { return Value(i op j); } \ Value operator() (string s, string t) const { return Value(s op t); } \ Value operator() (ListRef l1, ListRef l2) const { return Value(l1 op l2); } \ BINARY_DEFAULT_TO_TYPE_ERROR(name, name + string(": arguments are not comparable")) \ virtual ~v() { } \ }; \ typedef binary_op_instr instr /************************************* * basic arithmetic operators ************************************/ MAKE_BINARY_ARITH_INSTR(add_visitor, add_instr, +, "add"); MAKE_BINARY_ARITH_INSTR(sub_visitor, sub_instr, -, "sub"); MAKE_BINARY_ARITH_INSTR(mul_visitor, mul_instr, *, "mul"); /************************************* * numeric comparisons ************************************/ MAKE_BINARY_ARITH_INSTR(lt_visitor, lt_instr, <, "lt"); MAKE_BINARY_ARITH_INSTR(gt_visitor, gt_instr, >, "gt"); MAKE_BINARY_ARITH_INSTR(lte_visitor, lte_instr, <=, "lte"); MAKE_BINARY_ARITH_INSTR(gte_visitor, gte_instr, >=, "gte"); /************************************* * arbitrary comparisons ************************************/ MAKE_COMPARISON_INSTR(eq_visitor, eq_instr, ==, "eq"); MAKE_COMPARISON_INSTR(neq_visitor, neq_instr, !=, "neq"); // division needs to be handled separately // values with two integer arguments may still need to be promoted class div_visitor : public boost::static_visitor { public: Value operator() (int i, int j) const { return Value(double(i) / double(j)); } Value operator() (double i, double j) const { return Value(i / j); } Value operator() (int i, double j) const { return Value(i / j); } Value operator() (double i, int j) const { return Value(i / j); } BINARY_DEFAULT_TO_TYPE_ERROR("div", "div: values are not numbers") }; typedef binary_op_instr div_instr; /* *Makes a class, which does some simple binary operation on integers. */ #define MAKE_INT_BINARY_ARITH_INSTR(v, instr, op, name) \ class v : public boost::static_visitor \ { \ public: \ Value operator() (int i, int j) const { return Value(i op j); } \ BINARY_DEFAULT_TO_TYPE_ERROR(name, \ name + string(": arguments are not integers")) \ virtual ~v() { } \ }; \ typedef binary_op_instr instr MAKE_INT_BINARY_ARITH_INSTR(int_div_visitor, divi_instr, /, "divi"); MAKE_INT_BINARY_ARITH_INSTR(int_rem_visitor, remi_instr, %, "remi"); MAKE_INT_BINARY_ARITH_INSTR(and_visitor, and_instr, &&, "remi"); MAKE_INT_BINARY_ARITH_INSTR(or_visitor, or_instr, ||, "remi"); /** * neg instruction. * take the negative of a number */ class neg_visitor : public boost::static_visitor { public: Value operator() (int i) const { return Value(-i); } Value operator() (double d) const { return Value(-d); } UNARY_DEFAULT_TO_TYPE_ERROR("neg", Value, "neg: argument not a number") }; /** * frac instruction. * Get the fractional part of the number */ class frac_visitor : public boost::static_visitor { public: Value operator() (int i) const { return Value(0.0); } Value operator() (double d) const { double x; return Value(modf(d, &x)); } UNARY_DEFAULT_TO_TYPE_ERROR("frac", Value, "frac: argument not a number") }; /** * int instruction. * Get the integer part of the number */ class int_visitor: public boost::static_visitor { public: Value operator() (int i) const { return Value(i); } Value operator() (double d) const { double x; modf(d, &x); return Value((int)x); } UNARY_DEFAULT_TO_TYPE_ERROR("int", Value, "int: argument not a number") }; /** * not instruction * Get the opposite of an integer, treated as a binary value */ class not_visitor: public boost::static_visitor { public: Value operator() (int i) const { return Value(!i); } UNARY_DEFAULT_TO_TYPE_ERROR("not", Value, "not: argument not an integer") }; typedef unary_op_instr neg_instr; typedef unary_op_instr frac_instr; typedef unary_op_instr int_instr; typedef unary_op_instr not_instr; } #endif