/*=============================================================================
    Spirit v1.6.1
    Copyright (c) 2001-2003 Dan Nuffer
    Copyright (c) 2001-2003 Joel de Guzman
    http://spirit.sourceforge.net/

    Permission to copy, use, modify, sell and distribute this software is
    granted provided this copyright notice appears in all copies. This
    software is provided "as is" without express or implied warranty, and
    with no claim as to its suitability for any purpose.
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
//  Full calculator example with variables
//  [ JDG 9/18/2002 ]
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/spirit/core.hpp>
#include <boost/spirit/symbols/symbols.hpp>
#include <iostream>
#include <stack>
#include <functional>
#include <string>

using namespace std;
using namespace boost::spirit;

///////////////////////////////////////////////////////////////////////////////
//
//  Semantic actions
//
///////////////////////////////////////////////////////////////////////////////
struct push_num
{
    push_num(stack<double>& eval_)
    : eval(eval_) {}

    void operator()(double n) const
    {
        eval.push(n);
        cout << "push\t" << n << endl;
    }

    stack<double>& eval;
};

template <typename op>
struct do_op
{
    do_op(op const& the_op, stack<double>& eval_)
    : m_op(the_op), eval(eval_) {}

    void operator()(char const*, char const*) const
    {
        double rhs = eval.top();
        eval.pop();
        double lhs = eval.top();
        eval.pop();

        cout << "popped " << lhs << " and " << rhs << " from the stack. ";
        cout << "pushing " << m_op(lhs, rhs) << " onto the stack.\n";
        eval.push(m_op(lhs, rhs));
    }

    op m_op;
    stack<double>& eval;
};

template <class op>
do_op<op>
make_op(op const& the_op, stack<double>& eval)
{
    return do_op<op>(the_op, eval);
}

struct do_negate
{
    do_negate(stack<double>& eval_)
    : eval(eval_) {}

    void operator()(char const*, char const*) const
    {
        double lhs = eval.top();
        eval.pop();

        cout << "popped " << lhs << " from the stack. ";
        cout << "pushing " << -lhs << " onto the stack.\n";
        eval.push(-lhs);
    }

    stack<double>& eval;
};

struct get_var
{
    get_var(stack<double>& eval_)
    : eval(eval_) {}

    void operator()(double n) const
    {
        eval.push(n);
        cout << "push\t" << n << endl;
    }

    stack<double>& eval;
};

struct set_var
{
    set_var(double*& var_)
    : var(var_) {}

    void operator()(double& n) const
    {
        var = &n;
    }

    double*& var;
};

struct redecl_var
{
    void operator()(double& /*n*/) const
    {
        cout << "Warning. You are attempting to re-declare a var.\n";
    }
};

struct do_assign
{
    do_assign(double*& var_, stack<double>& eval_)
    : var(var_), eval(eval_) {}

    void operator()(char const*, char const*) const
    {
        if (var != 0)
        {
            *var = eval.top();
            cout << "assigning\n";
        }
    }

    double*& var;
    stack<double>&  eval;
};

///////////////////////////////////////////////////////////////////////////////
//
//  Our calculator grammar
//
///////////////////////////////////////////////////////////////////////////////
struct calculator : public grammar<calculator>
{
    calculator(stack<double>& eval_)
    : eval(eval_) {}

    template <typename ScannerT>
    struct definition
    {
        definition(calculator const& self)
        {
            factor =
                    real_p[push_num(self.eval)]
                |   vars[get_var(self.eval)]
                |   '(' >> expression >> ')'
                |   ('-' >> factor)[do_negate(self.eval)]
                ;

            term =
                factor
                >> *(   ('*' >> factor)[make_op(multiplies<double>(), self.eval)]
                    |   ('/' >> factor)[make_op(divides<double>(), self.eval)]
                    )
                    ;

            expression =
                term
                >> *(  ('+' >> term)[make_op(plus<double>(), self.eval)]
                    |   ('-' >> term)[make_op(minus<double>(), self.eval)]
                    )
                    ;

            assignment =
                vars[set_var(self.var)]
                >> '=' >> expression[do_assign(self.var, self.eval)]
                ;

            var_decl =
                lexeme_d
                [
                    ((alpha_p >> *(alnum_p | '_'))
                        - vars[redecl_var()])[vars.add]
                ]
                ;

            declaration =
                "var" >> var_decl >> *(',' >> var_decl)
                ;

            statement =
                declaration | assignment | '?' >> expression
                ;
        }

        symbols<double>     vars;
        rule<ScannerT>      statement, declaration, var_decl,
                            assignment, expression, term, factor;

        rule<ScannerT> const&
        start() const { return statement; }
    };

    mutable double* var;
    stack<double>&  eval;
};

///////////////////////////////////////////////////////////////////////////////
//
//  Main program
//
///////////////////////////////////////////////////////////////////////////////
int
main()
{
    cout << "/////////////////////////////////////////////////////////\n\n";
    cout << "\t\tThe calculator with variables...\n\n";
    cout << "/////////////////////////////////////////////////////////\n\n";
    cout << "Type a statement...or [q or Q] to quit\n\n";
    cout << "Variables may be declared:\t\tExample: var i, j, k\n";
    cout << "Assigning to a variable:\t\tExample: i = 10 * j\n";
    cout << "To evaluate an expression:\t\tExample: ? i * 3.33E-3\n\n";

    stack<double>   eval;
    calculator      calc(eval); //  Our parser

    string str;
    while (getline(cin, str))
    {
        if (str[0] == 'q' || str[0] == 'Q')
            break;

        parse_info<> info = parse(str.c_str(), calc, space_p);

        if (info.full)
        {
            cout << "-------------------------\n";
            cout << "Parsing succeeded\n";
            cout << "-------------------------\n";
        }
        else
        {
            cout << "-------------------------\n";
            cout << "Parsing failed\n";
            cout << "stopped at: \": " << info.stop << "\"\n";
            cout << "-------------------------\n";
        }
    }

    cout << "Bye... :-) \n\n";
    return 0;
}


