/*=============================================================================
    Copyright (c) 2001-2011 Hartmut Kaiser
    Copyright (c) 2001-2011 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)
=============================================================================*/
#if !defined(BOOST_SPIRIT_CONJURE_LEXER_HPP)
#define BOOST_SPIRIT_CONJURE_LEXER_HPP

#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/lex_lexertl_position_token.hpp>

#include "config.hpp"
#include "ids.hpp"

#if CONJURE_LEXER_STATIC_TABLES != 0
#include <boost/spirit/include/lex_static_lexertl.hpp>
#include "conjure_static_lexer.hpp"
#elif CONJURE_LEXER_STATIC_SWITCH != 0
#include <boost/spirit/include/lex_static_lexertl.hpp>
#include "conjure_static_switch_lexer.hpp"
#endif
#include <boost/assert.hpp>

namespace client { namespace lexer
{
    namespace lex = boost::spirit::lex;

    ///////////////////////////////////////////////////////////////////////////
    namespace detail
    {
        namespace lex = boost::spirit::lex;

        template <typename BaseIterator>
        struct get_lexer_type
        {
            // Our token needs to be able to carry several token values:
            // std::string, unsigned int, and bool
            typedef boost::mpl::vector<std::string, unsigned int, bool>
                token_value_types;

            // Using the position_token class as the token type to be returned
            // from the lexer iterators allows to retain positional information
            // as every token instance stores an iterator pair pointing to the
            // matched input sequence.
            typedef lex::lexertl::position_token<
                BaseIterator, token_value_types, boost::mpl::false_
            > token_type;

#if CONJURE_LEXER_DYNAMIC_TABLES != 0
            // use the lexer based on runtime generated DFA tables
            typedef lex::lexertl::actor_lexer<token_type> type;
#elif CONJURE_LEXER_STATIC_TABLES != 0
            // use the lexer based on pre-generated static DFA tables
            typedef lex::lexertl::static_actor_lexer<
                token_type
              , boost::spirit::lex::lexertl::static_::lexer_conjure_static
            > type;
#elif CONJURE_LEXER_STATIC_SWITCH != 0
            // use the lexer based on pre-generated static code
            typedef lex::lexertl::static_actor_lexer<
                token_type
              , boost::spirit::lex::lexertl::static_::lexer_conjure_static_switch
            > type;
#else
#error "Configuration problem: please select exactly one type of lexer to build"
#endif
        };
    }

    ///////////////////////////////////////////////////////////////////////////
    template <typename BaseIterator>
    struct conjure_tokens
      : lex::lexer<typename detail::get_lexer_type<BaseIterator>::type>
    {
    private:
        // get the type of any qi::raw_token(...) and qi::token(...) constructs
        typedef typename boost::spirit::result_of::terminal<
            boost::spirit::tag::raw_token(token_ids::type)
        >::type raw_token_spec;

        typedef typename boost::spirit::result_of::terminal<
            boost::spirit::tag::token(token_ids::type)
        >::type token_spec;

        typedef std::map<std::string, token_ids::type> keyword_map_type;

    protected:
        // add a keyword to the mapping table
        bool add_(std::string const& keyword, int id = token_ids::invalid);

        struct keyword_adder
        {
            conjure_tokens& l;
            keyword_adder(conjure_tokens& l) : l(l) {}
            keyword_adder& operator()(
                std::string const& keyword, int id = token_ids::invalid)
            {
                l.add_(keyword, id);
                return *this;
            }
        };

        friend struct keyword_adder;
        keyword_adder add;

    public:
        typedef BaseIterator base_iterator_type;

        conjure_tokens();

        // extract a raw_token(id) for the given registered keyword
        raw_token_spec operator()(std::string const& kwd) const
        {
            namespace qi = boost::spirit::qi;
            qi::raw_token_type raw_token;

            typename keyword_map_type::const_iterator it = keywords_.find(kwd);
            BOOST_ASSERT(it != keywords_.end());
            return raw_token((it != keywords_.end()) ? (*it).second : token_ids::invalid);
        }

        // extract a token(id) for the given registered keyword
        token_spec token(std::string const& kwd) const
        {
            namespace qi = boost::spirit::qi;
            qi::token_type token;

            typename keyword_map_type::const_iterator it = keywords_.find(kwd);
            BOOST_ASSERT(it != keywords_.end());
            return token((it != keywords_.end()) ? (*it).second : token_ids::invalid);
        }

        lex::token_def<std::string> identifier;
        lex::token_def<unsigned int> lit_uint;
        lex::token_def<bool> true_or_false;
        keyword_map_type keywords_;
    };
}}

#endif


