/*=============================================================================
    Spirit v1.6.0
    Copyright (c) 2001-2003 Hartmut Kaiser
    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.
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
// This sample show the usage of parser parameters.
//
// Parser parameters are used to pass some values from the outer parsing scope
// to the next inner scope. They can be imagined as the opposite to the return
// value paradigm, which returns some value from the inner to the next outer
// scope.

#include <string>
#include <iostream>
#include <cassert>

#if defined(_MSC_VER) /*&& !defined(__COMO__)*/
#pragma warning(disable: 4244)
#pragma warning(disable: 4355)
#endif // defined(_MSC_VER) && !defined(__COMO__)

#include <boost/spirit/core.hpp>
#include <boost/spirit/symbols/symbols.hpp>

#include <boost/spirit/phoenix/tuples.hpp>
#include <boost/spirit/phoenix/tuple_helpers.hpp>
#include <boost/spirit/phoenix/primitives.hpp>
#include <boost/spirit/attribute/closure.hpp>

///////////////////////////////////////////////////////////////////////////////
// used namespaces
using namespace boost::spirit;
using namespace phoenix;
using namespace std;

///////////////////////////////////////////////////////////////////////////////
// Helper class for encapsulation of the type for the parsed variable names
class declaration_type
{
public:
    enum vartype {
        vartype_unknown = 0,        // unknown variable type
        vartype_int = 1,            // 'int'
        vartype_real = 2            // 'real'
    };

    declaration_type() : type(vartype_unknown)
    {
    }
    template <typename ItT>
    declaration_type(ItT const &first, ItT const &last)
    {
        init(string(first, last-first-1));
    }
    declaration_type(declaration_type const &type_) : type(type_.type)
    {
    }
    declaration_type(string const &type_) : type(vartype_unknown)
    {
        init(type_);
    }

// access to the variable type
    operator vartype const &() const { return type; }
    operator string ()
    {
        switch(type) {
        default:
        case vartype_unknown:   break;
        case vartype_int:       return string("int");
        case vartype_real:      return string("real");
        }
        return string ("unknown");
    }

    void swap(declaration_type &s) { std::swap(type, s.type); }

protected:
    void init (string const &type_)
    {
        if (type_ == "int")
            type = vartype_int;
        else if (type_ == "real")
            type = vartype_real;
        else
            type = vartype_unknown;
    }

private:
    vartype type;
};

///////////////////////////////////////////////////////////////////////////////
//
//  used closure type
//
///////////////////////////////////////////////////////////////////////////////
struct var_decl_closure : boost::spirit::closure<var_decl_closure, declaration_type>
{
    member1 val;
};

///////////////////////////////////////////////////////////////////////////////
//
//  symbols_with_data
//
//      Helper class for inserting an item with data into a symbol table
//
///////////////////////////////////////////////////////////////////////////////
template <typename T, typename InitT>
class symbols_with_data
{
public:
    typedef
        symbol_inserter<T, boost::spirit::impl::tst<T, char> >
        symbol_inserter_t;

    symbols_with_data(symbol_inserter_t const &add_, InitT const &data_) :
        add(add_), data(as_actor<InitT>::convert(data_))
    {
    }

    template <typename IteratorT>
    symbol_inserter_t const &
    operator()(IteratorT const &first_, IteratorT const &last) const
    {
        IteratorT first = first_;
        return add(first, last, data());
    }

private:
    symbol_inserter_t const &add;
    typename as_actor<InitT>::type data;
};

template <typename T, typename CharT, typename InitT>
inline
symbols_with_data<T, InitT>
symbols_gen(symbol_inserter<T, boost::spirit::impl::tst<T, CharT> > const &add_,
    InitT const &data_)
{
    return symbols_with_data<T, InitT>(add_, data_);
}

///////////////////////////////////////////////////////////////////////////////
// The var_decl_list grammar parses variable declaration list

struct var_decl_list :
    public grammar<var_decl_list, var_decl_closure::context_t>
{
    template <typename ScannerT>
    struct definition
    {
        definition(var_decl_list const &self)
        {
        // pass variable type returned from 'type' to list closure member 0
            decl = type[self.val = arg1] >> +space_p >> list(self.val);

        // m0 to access arg 0 of list --> passing variable type down to ident
            list = ident(list.val) >> *(',' >> ident(list.val));

        // store identifier and type into the symbol table
            ident = (*alnum_p)[symbols_gen(symtab.add, ident.val)];

        // the type of the decl is returned in type's closure member 0
            type =
                    str_p("int")[type.val = construct_<string>(arg1, arg2)]
                |   str_p("real")[type.val = construct_<string>(arg1, arg2)]
                ;

            BOOST_SPIRIT_DEBUG_RULE(decl);
            BOOST_SPIRIT_DEBUG_RULE(list);
            BOOST_SPIRIT_DEBUG_RULE(ident);
            BOOST_SPIRIT_DEBUG_RULE(type);
        }

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

    private:
        typedef rule<ScannerT, var_decl_closure::context_t> rule_t;
        rule_t type;
        rule_t list;
        rule_t ident;
        symbols<declaration_type> symtab;

        rule<ScannerT> decl;        // start rule
    };
};

///////////////////////////////////////////////////////////////////////////////
// main entry point
int main()
{
var_decl_list decl;
declaration_type type;
char const *pbegin = "int  var1";

    if (parse (pbegin, decl[assign(type)]).full) {
        cout << endl
             << "Parsed variable declarations successfully!" << endl
             << "Detected type: " << declaration_type::vartype(type)
             << " (" << string(type) << ")"
             << endl;
    } else {
        cout << endl
             << "Parsing the input stream failed!"
             << endl;
    }
    return 0;
}

