/*============================================================================= Copyright (c) 1998-2003 Joel de Guzman http://spirit.sourceforge.net/ Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ //////////////////////////////////////////////////////////////////////////// // // The calculator using a simple virtual machine and compiler. // // Ported to v1.5 from the original v1.0 code by JDG // [ JDG 9/18/2002 ] // //////////////////////////////////////////////////////////////////////////// #include #include #include #include //////////////////////////////////////////////////////////////////////////// using namespace std; using namespace BOOST_SPIRIT_CLASSIC_NS; /////////////////////////////////////////////////////////////////////////////// // // The VMachine // /////////////////////////////////////////////////////////////////////////////// enum ByteCodes { OP_NEG, // negate the top stack entry OP_ADD, // add top two stack entries OP_SUB, // subtract top two stack entries OP_MUL, // multiply top two stack entries OP_DIV, // divide top two stack entries OP_INT, // push constant integer into the stack OP_RET // return from the interpreter }; class vmachine { public: vmachine(unsigned stackSize = 1024) : stack(new int[stackSize]), stackPtr(stack) {} ~vmachine() { delete [] stack; } int top() const { return stackPtr[-1]; }; void execute(int code[]); private: int* stack; int* stackPtr; }; void vmachine::execute(int code[]) { int const* pc = code; bool running = true; stackPtr = stack; while (running) { switch (*pc++) { case OP_NEG: stackPtr[-1] = -stackPtr[-1]; break; case OP_ADD: stackPtr--; stackPtr[-1] += stackPtr[0]; break; case OP_SUB: stackPtr--; stackPtr[-1] -= stackPtr[0]; break; case OP_MUL: stackPtr--; stackPtr[-1] *= stackPtr[0]; break; case OP_DIV: stackPtr--; stackPtr[-1] /= stackPtr[0]; break; case OP_INT: // Check stack overflow here! *stackPtr++ = *pc++; break; case OP_RET: running = false; break; } } } /////////////////////////////////////////////////////////////////////////////// // // The Compiler // /////////////////////////////////////////////////////////////////////////////// struct push_int { push_int(vector& code_) : code(code_) {} void operator()(char const* str, char const* /*end*/) const { using namespace std; int n = strtol(str, 0, 10); code.push_back(OP_INT); code.push_back(n); cout << "push\t" << int(n) << endl; } vector& code; }; struct push_op { push_op(int op_, vector& code_) : op(op_), code(code_) {} void operator()(char const*, char const*) const { code.push_back(op); switch (op) { case OP_NEG: cout << "neg\n"; break; case OP_ADD: cout << "add\n"; break; case OP_SUB: cout << "sub\n"; break; case OP_MUL: cout << "mul\n"; break; case OP_DIV: cout << "div\n"; break; } } int op; vector& code; }; template static bool compile(GrammarT const& calc, char const* expr) { cout << "\n/////////////////////////////////////////////////////////\n\n"; parse_info result = parse(expr, calc, space_p); if (result.full) { cout << "\t\t" << expr << " Parses OK\n\n\n"; calc.code.push_back(OP_RET); return true; } else { cout << "\t\t" << expr << " Fails parsing\n"; cout << "\t\t"; for (int i = 0; i < (result.stop - expr); i++) cout << " "; cout << "^--Here\n\n\n"; return false; } } //////////////////////////////////////////////////////////////////////////// // // Our calculator grammar // //////////////////////////////////////////////////////////////////////////// struct calculator : public grammar { calculator(vector& code_) : code(code_) {} template struct definition { definition(calculator const& self) { integer = lexeme_d[ (+digit_p)[push_int(self.code)] ] ; factor = integer | '(' >> expression >> ')' | ('-' >> factor)[push_op(OP_NEG, self.code)] | ('+' >> factor) ; term = factor >> *( ('*' >> factor)[push_op(OP_MUL, self.code)] | ('/' >> factor)[push_op(OP_DIV, self.code)] ) ; expression = term >> *( ('+' >> term)[push_op(OP_ADD, self.code)] | ('-' >> term)[push_op(OP_SUB, self.code)] ) ; } rule expression, term, factor, integer; rule const& start() const { return expression; } }; vector& code; }; //////////////////////////////////////////////////////////////////////////// // // Main program // //////////////////////////////////////////////////////////////////////////// int main() { cout << "/////////////////////////////////////////////////////////\n\n"; cout << "\t\tA simple virtual machine...\n\n"; cout << "/////////////////////////////////////////////////////////\n\n"; cout << "Type an expression...or [q or Q] to quit\n\n"; vmachine mach; // Our virtual machine vector code; // Our VM code calculator calc(code); // Our parser string str; while (getline(cin, str)) { if (str.empty() || str[0] == 'q' || str[0] == 'Q') break; code.clear(); if (compile(calc, str.c_str())) { mach.execute(&*code.begin()); cout << "\n\nresult = " << mach.top() << "\n\n"; } } cout << "Bye... :-) \n\n"; return 0; }