//  Copyright (c) 2001-2009 Hartmut Kaiser
// 
//  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)

#if !defined(BOOST_SPIRIT_LEX_TOKEN_DEF_MAR_13_2007_0145PM)
#define BOOST_SPIRIT_LEX_TOKEN_DEF_MAR_13_2007_0145PM

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once      // MS compatible compilers support #pragma once
#endif

#include <boost/spirit/home/lex/lexer/lexer_fwd.hpp>
#include <boost/spirit/home/lex/lexer/terminal_holder.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/spirit/home/qi/skip.hpp>
#include <boost/spirit/home/qi/detail/construct.hpp>
#include <boost/spirit/home/qi/detail/assign_to.hpp>
#include <boost/mpl/if.hpp>
#include <boost/detail/iterator.hpp>
#include <boost/type_traits/is_same.hpp>
#include <string>
#include <cstdlib>

namespace boost { namespace spirit { namespace lex
{
    ///////////////////////////////////////////////////////////////////////////
    //  create a unique token id, note this is not thread safe
    ///////////////////////////////////////////////////////////////////////////
    enum tokenids {
        // this is the first token id automatically assigned by the library
        // if needed
        min_token_id = 0x10000
    };
    
    ///////////////////////////////////////////////////////////////////////////
    //  The next_id template needs to be specialized for any non-default token 
    //  id type used by a custom token type. It need to expose a function
    //  'static Idtype get()' returning the next available token id each time 
    //  it is called.
    template <typename Idtype>
    struct next_id;
    
    ///////////////////////////////////////////////////////////////////////////
    //  Default specialization for the next_id template returning the next 
    //  available token id.
    template <>
    struct next_id<std::size_t>
    {
        static std::size_t get()
        {
            static std::size_t next_token_id = min_token_id;
            return next_token_id++;   
        }
    };
    
    ///////////////////////////////////////////////////////////////////////////
    //  This component represents a token definition
    ///////////////////////////////////////////////////////////////////////////
    template<typename Attribute, typename Char, typename Idtype>
    class token_def
      : public proto::extends<
            typename make_terminal_holder<
                token_def<Attribute, Char, Idtype>*, 
                token_def<Attribute, Char, Idtype>
            >::type,
            token_def<Attribute, Char, Idtype>
        >
    {
    private:
        // initialize proto base class
        typedef terminal_holder<token_def*, token_def> terminal_holder_;
        typedef typename proto::terminal<terminal_holder_>::type tag;
        typedef proto::extends<tag, token_def> base_type;

        tag make_tag() 
        {
            tag xpr = {{ this }};
            return xpr;
        }

    public:
        // Qi interface: metafunction calculating parser return type
        template <typename Component, typename Context, typename Iterator>
        struct attribute
        {
            //  The return value of the token_def is either the specified 
            //  attribute type, or the pair of iterators from the match of the 
            //  corresponding token (if no attribute type has been specified),
            //  or unused_type (if omitted has been specified).
            typedef typename Iterator::base_iterator_type iterator_type;
            typedef typename
                mpl::if_<
                    is_same<Attribute, unused_type>,
                    iterator_range<iterator_type>,
                    typename mpl::if_<
                        is_same<Attribute, omitted>,
                        unused_type,
                        Attribute
                    >::type
                >::type
            type;
        };

    private:
        // Qi interface: parse functionality
        template <typename Iterator, typename Context, typename Skipper, 
            typename Attribute1>
        bool parse(Iterator& first, Iterator const& last, 
            Context& /*context*/, Skipper const& skipper, Attribute1& attr) const
        {
            qi::skip(first, last, skipper);   // always do a pre-skip

            if (first != last) {
                typedef typename 
                    boost::detail::iterator_traits<Iterator>::value_type 
                token_type;

                //  If the following assertion fires you probably forgot to  
                //  associate this token definition with a lexer instance.
                BOOST_ASSERT((std::size_t)(~0) != token_state);

                token_type &t = *first;
                if (token_id == t.id() && token_state == t.state()) {
                    qi::detail::assign_to(t, attr);
                    ++first;
                    return true;
                }
            }
            return false;
        }
        friend struct terminal_director;
        
        std::string what() const
        {
            std::string result = "token_def(\"";
            result += def;
            result += "\")";
            return result;
        }
        
        ///////////////////////////////////////////////////////////////////////
        // Lex interface: collect token definitions and put it into the 
        // provided lexer def
        template <typename LexerDef, typename String>
        void collect(LexerDef& lexdef, String const& state)
        {
            token_state = lexdef.add_state(state.c_str());
            if (0 == token_id)
                token_id = next_id<Idtype>::get();
            lexdef.add_token (state.c_str(), def, token_id);
        }
            
    public:
        typedef Char char_type;
        typedef Idtype id_type;
        typedef std::basic_string<char_type> string_type;
        
        // Lex interface: constructing token definitions
        token_def() 
          : base_type(make_tag()), token_id(0), token_state(~0) 
        {}
        explicit token_def(char_type def_, Idtype id_ = Idtype())
          : base_type(make_tag()), def(lex::detail::escape(def_)), 
            token_id(0 == id_ ? def_ : id_), token_state(~0) 
        {}
        explicit token_def(string_type def_, Idtype id_ = Idtype())
          : base_type(make_tag()), def(def_), token_id(id_), token_state(~0) 
        {}
        
        template <typename String>
        token_def& operator= (String const& definition)
        {
            def = definition;
            token_id = 0;
            return *this;
        }
        token_def& operator= (token_def const& rhs)
        {
            def = rhs.def;
            token_id = rhs.token_id;
            return *this;
        }
        
        // general accessors 
        Idtype id() const { return token_id; }
        void id(Idtype id) { token_id = id; }
        string_type const& definition() const { return def; }
        std::size_t state() const { return token_state; }
        
    private:
        string_type def;
        Idtype token_id;
        std::size_t token_state;
    };

}}}

#endif
