| /*============================================================================= |
| Copyright (c) 2001-2010 Joel de Guzman |
| |
| Distributed under 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) |
| =============================================================================*/ |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // Yet another calculator example! This time, we will compile to a simple |
| // virtual machine. This is actually one of the very first Spirit example |
| // circa 2000. Now, it's ported to Spirit2. |
| // |
| // [ JDG Sometime 2000 ] pre-boost |
| // [ JDG September 18, 2002 ] spirit1 |
| // [ JDG April 8, 2007 ] spirit2 |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include <boost/config/warning_disable.hpp> |
| #include <boost/spirit/include/qi.hpp> |
| #include <boost/spirit/include/phoenix_core.hpp> |
| #include <boost/spirit/include/phoenix_container.hpp> |
| #include <boost/spirit/include/phoenix_statement.hpp> |
| #include <boost/spirit/include/phoenix_object.hpp> |
| #include <boost/spirit/include/phoenix_operator.hpp> |
| |
| #include <iostream> |
| #include <string> |
| #include <vector> |
| |
| namespace client |
| { |
| namespace qi = boost::spirit::qi; |
| namespace phoenix = boost::phoenix; |
| namespace ascii = boost::spirit::ascii; |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // The Virtual Machine |
| /////////////////////////////////////////////////////////////////////////// |
| enum byte_code |
| { |
| 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 |
| }; |
| |
| class vmachine |
| { |
| public: |
| |
| vmachine(unsigned stackSize = 4096) |
| : stack(stackSize) |
| , stack_ptr(stack.begin()) |
| { |
| } |
| |
| int top() const { return stack_ptr[-1]; }; |
| void execute(std::vector<int> const& code); |
| |
| private: |
| |
| std::vector<int> stack; |
| std::vector<int>::iterator stack_ptr; |
| }; |
| |
| void vmachine::execute(std::vector<int> const& code) |
| { |
| std::vector<int>::const_iterator pc = code.begin(); |
| stack_ptr = stack.begin(); |
| |
| while (pc != code.end()) |
| { |
| switch (*pc++) |
| { |
| case op_neg: |
| stack_ptr[-1] = -stack_ptr[-1]; |
| break; |
| |
| case op_add: |
| --stack_ptr; |
| stack_ptr[-1] += stack_ptr[0]; |
| break; |
| |
| case op_sub: |
| --stack_ptr; |
| stack_ptr[-1] -= stack_ptr[0]; |
| break; |
| |
| case op_mul: |
| --stack_ptr; |
| stack_ptr[-1] *= stack_ptr[0]; |
| break; |
| |
| case op_div: |
| --stack_ptr; |
| stack_ptr[-1] /= stack_ptr[0]; |
| break; |
| |
| case op_int: |
| *stack_ptr++ = *pc++; |
| break; |
| } |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Our calculator grammar and compiler |
| /////////////////////////////////////////////////////////////////////////// |
| template <typename Iterator> |
| struct calculator : qi::grammar<Iterator, ascii::space_type> |
| { |
| calculator(std::vector<int>& code) |
| : calculator::base_type(expression) |
| , code(code) |
| { |
| using namespace qi::labels; |
| using qi::uint_; |
| using qi::on_error; |
| using qi::fail; |
| |
| using phoenix::val; |
| using phoenix::ref; |
| using phoenix::push_back; |
| using phoenix::construct; |
| |
| expression = |
| term |
| >> *( ('+' > term [push_back(ref(code), op_add)]) |
| | ('-' > term [push_back(ref(code), op_sub)]) |
| ) |
| ; |
| |
| term = |
| factor |
| >> *( ('*' > factor [push_back(ref(code), op_mul)]) |
| | ('/' > factor [push_back(ref(code), op_div)]) |
| ) |
| ; |
| |
| factor = |
| uint_ [ |
| push_back(ref(code), op_int), |
| push_back(ref(code), _1) |
| ] |
| | '(' > expression > ')' |
| | ('-' > factor [push_back(ref(code), op_neg)]) |
| | ('+' > factor) |
| ; |
| |
| expression.name("expression"); |
| term.name("term"); |
| factor.name("factor"); |
| |
| on_error<fail> |
| ( |
| expression |
| , std::cout |
| << val("Error! Expecting ") |
| << _4 // what failed? |
| << val(" here: \"") |
| << construct<std::string>(_3, _2) // iterators to error-pos, end |
| << val("\"") |
| << std::endl |
| ); |
| } |
| |
| qi::rule<Iterator, ascii::space_type> expression, term, factor; |
| std::vector<int>& code; |
| }; |
| |
| template <typename Grammar> |
| bool compile(Grammar const& calc, std::string const& expr) |
| { |
| std::string::const_iterator iter = expr.begin(); |
| std::string::const_iterator end = expr.end(); |
| bool r = phrase_parse(iter, end, calc, ascii::space); |
| |
| if (r && iter == end) |
| { |
| std::cout << "-------------------------\n"; |
| std::cout << "Parsing succeeded\n"; |
| std::cout << "-------------------------\n"; |
| return true; |
| } |
| else |
| { |
| std::cout << "-------------------------\n"; |
| std::cout << "Parsing failed\n"; |
| std::cout << "-------------------------\n"; |
| return false; |
| } |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Main program |
| /////////////////////////////////////////////////////////////////////////////// |
| int |
| main() |
| { |
| std::cout << "/////////////////////////////////////////////////////////\n\n"; |
| std::cout << "Expression parser...\n\n"; |
| std::cout << "/////////////////////////////////////////////////////////\n\n"; |
| std::cout << "Type an expression...or [q or Q] to quit\n\n"; |
| |
| typedef std::string::const_iterator iterator_type; |
| typedef client::calculator<iterator_type> calculator; |
| |
| client::vmachine mach; // Our virtual machine |
| std::vector<int> code; // Our VM code |
| calculator calc(code); // Our grammar |
| |
| std::string str; |
| while (std::getline(std::cin, str)) |
| { |
| if (str.empty() || str[0] == 'q' || str[0] == 'Q') |
| break; |
| |
| code.clear(); |
| if (client::compile(calc, str)) |
| { |
| mach.execute(code); |
| std::cout << "\n\nresult = " << mach.top() << std::endl; |
| std::cout << "-------------------------\n\n"; |
| } |
| } |
| |
| std::cout << "Bye... :-) \n\n"; |
| return 0; |
| } |
| |
| |