/*=============================================================================
    Spirit v1.6.0
    Copyright (c) 2001-2003 Daniel Nuffer
    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.
=============================================================================*/
#include "boost/spirit/core.hpp"
#include "boost/spirit/tree/ast.hpp"

#include <iostream>
#include <stack>
#include <functional>
#include <string>

// This example shows how to use an AST and tree_iter_node instead of
// tree_val_node
////////////////////////////////////////////////////////////////////////////
using namespace std;
using namespace boost::spirit;

typedef char const*         iterator_t;
typedef tree_match<iterator_t, node_iter_data_factory<> >
    parse_tree_match_t;

typedef parse_tree_match_t::tree_iterator iter_t;

typedef ast_match_policy<iterator_t, node_iter_data_factory<> > match_policy_t;
typedef scanner<iterator_t, scanner_policies<iter_policy_t, match_policy_t> > scanner_t;
typedef rule<scanner_t> rule_t;


//  grammar rules
rule_t expression, term, factor, integer;

////////////////////////////////////////////////////////////////////////////
long evaluate(parse_tree_match_t hit);
long eval_expression(iter_t const& i);
long eval_term(iter_t const& i);
long eval_factor(iter_t const& i);
long eval_integer(iter_t const& i);

long evaluate(parse_tree_match_t hit)
{
    return eval_expression(hit.trees.begin());
}

long eval_expression(iter_t const& i)
{
    cout << "In eval_expression. i->value = " <<
        string(i->value.begin(), i->value.end()) <<
        " i->children.size() = " << i->children.size() << endl;

    cout << "ID: " << i->value.id().to_long() << endl;

    if (i->value.id() == integer.id())
    {
        assert(i->children.size() == 0);
        return strtol(i->value.begin(), 0, 10);
    }
    else if (i->value.id() == factor.id())
    {
        // factor can only be unary minus
        assert(*i->value.begin() == '-');
        return - eval_expression(i->children.begin());
    }
    else if (i->value.id() == term.id())
    {
        if (*i->value.begin() == '*')
        {
            assert(i->children.size() == 2);
            return eval_expression(i->children.begin()) *
                eval_expression(i->children.begin()+1);
        }
        else if (*i->value.begin() == '/')
        {
            assert(i->children.size() == 2);
            return eval_expression(i->children.begin()) /
                eval_expression(i->children.begin()+1);
        }
        else
            assert(0);
    }
    else if (i->value.id() == expression.id())
    {
        if (*i->value.begin() == '+')
        {
            assert(i->children.size() == 2);
            return eval_expression(i->children.begin()) +
                eval_expression(i->children.begin()+1);
        }
        else if (*i->value.begin() == '-')
        {
            assert(i->children.size() == 2);
            return eval_expression(i->children.begin()) -
                eval_expression(i->children.begin()+1);
        }
        else
            assert(0);
    }
    else
        assert(0); // error

   return 0;
}

////////////////////////////////////////////////////////////////////////////
int
main()
{
    BOOST_SPIRIT_DEBUG_RULE(integer);
    BOOST_SPIRIT_DEBUG_RULE(factor);
    BOOST_SPIRIT_DEBUG_RULE(term);
    BOOST_SPIRIT_DEBUG_RULE(expression);
    //  Start grammar definition
    integer     =   leaf_node_d[ lexeme_d[ (!ch_p('-') >> +digit_p) ] ];
    factor      =   integer
                |   inner_node_d[ch_p('(') >> expression >> ch_p(')')]
                |   (root_node_d[ch_p('-')] >> factor);
    term        =   factor >>
                    *(  (root_node_d[ch_p('*')] >> factor)
                      | (root_node_d[ch_p('/')] >> factor)
                    );
    expression  =   term >>
                    *(  (root_node_d[ch_p('+')] >> term)
                      | (root_node_d[ch_p('-')] >> term)
                    );
    //  End grammar definition


    cout << "/////////////////////////////////////////////////////////\n\n";
    cout << "\t\tThe simplest working calculator...\n\n";
    cout << "/////////////////////////////////////////////////////////\n\n";
    cout << "Type an expression...or [q or Q] to quit\n\n";

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

        const char* str_begin = str.c_str();
        const char* str_end = str.c_str();
        while (*str_end)
            ++str_end;

        scanner_t scan(str_begin, str_end);

        parse_tree_match_t hit = expression.parse(scan);


        if (hit && str_begin == str_end)
        {
#if defined(BOOST_SPIRIT_DUMP_PARSETREE_AS_XML)
            // dump parse tree as XML
            std::map<rule_id, std::string> rule_names;
            rule_names[&integer] = "integer";
            rule_names[&factor] = "factor";
            rule_names[&term] = "term";
            rule_names[&expression] = "expression";
            tree_to_xml(cout, hit.trees, str.c_str(), rule_names);
#endif

            // print the result
            cout << "parsing succeeded\n";
            cout << "result = " << evaluate(hit) << "\n\n";
        }
        else
        {
            cout << "parsing failed\n";
        }
    }

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


