/*=============================================================================

    Boost.Wave: A Standard compliant C++ preprocessor library



    http://www.boost.org/



    State machine detecting include guards in an included file. 

    This detects two forms of include guards:

    

        #ifndef INCLUDE_GUARD_MACRO

        #define INCLUDE_GUARD_MACRO

        ...

        #endif

    

    or

    

        if !defined(INCLUDE_GUARD_MACRO)

        #define INCLUDE_GUARD_MACRO

        ...

        #endif



    note, that the parenthesis are optional (i.e. !defined INCLUDE_GUARD_MACRO

    will work as well). The code allows for any whitespace, newline and single 

    '#' tokens before the #if/#ifndef and after the final #endif.

    

    Copyright (c) 2001-2008 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(DETECT_INCLUDE_GUARDS_HK060304_INCLUDED)

#define DETECT_INCLUDE_GUARDS_HK060304_INCLUDED



#include <boost/wave/wave_config.hpp>

#include <boost/wave/token_ids.hpp>



// this must occur after all of the includes and before any code appears

#ifdef BOOST_HAS_ABI_HEADERS

#include BOOST_ABI_PREFIX

#endif



///////////////////////////////////////////////////////////////////////////////

namespace boost {

namespace wave {

namespace cpplexer {



template <typename Token>

class include_guards

{

public:

    include_guards()

    :   state(&include_guards::state_0), detected_guards(false), 

        current_state(true), if_depth(0)

    {}



    Token const& detect_guard(Token const& t) 

        { return current_state ? (this->*state)(t) : t; }

    bool detected(std::string& guard_name_) const 

    {

        if (detected_guards) {

            guard_name_ = guard_name.c_str();

            return true;

        }

        return false; 

    }

    

private:

    typedef Token const& state_type(Token const& t);

    state_type include_guards::* state;



    bool detected_guards;

    bool current_state;

    typename Token::string_type guard_name;

    int if_depth;

    

    state_type state_0, state_1, state_2, state_3, state_4, state_5;

    state_type state_1a, state_1b, state_1c, state_1d, state_1e;



    bool is_skippable(token_id id) const

    {

        return (T_POUND == BASE_TOKEN(id) || 

                IS_CATEGORY(id, WhiteSpaceTokenType) ||

                IS_CATEGORY(id, EOLTokenType));

    }

};



///////////////////////////////////////////////////////////////////////////////

//  state 0: beginning of a file, tries to recognize #ifndef or #if tokens

template <typename Token>

inline Token const& 

include_guards<Token>::state_0(Token const& t)

{

    token_id id = token_id(t);

    if (T_PP_IFNDEF == id)

        state = &include_guards::state_1;

    else if (T_PP_IF == id)

        state = &include_guards::state_1a;

    else if (!is_skippable(id))

        current_state = false;

    return t;

}



///////////////////////////////////////////////////////////////////////////////

//  state 1: found #ifndef, looking for T_IDENTIFIER

template <typename Token>

inline Token const& 

include_guards<Token>::state_1(Token const& t)

{

    token_id id = token_id(t);

    if (T_IDENTIFIER == id) {

        guard_name = t.get_value();

        state = &include_guards::state_2;

    }

    else if (!is_skippable(id))

        current_state = false;

    return t;

}



///////////////////////////////////////////////////////////////////////////////

//  state 1a: found T_PP_IF, looking for T_NOT ("!")

template <typename Token>

inline Token const& 

include_guards<Token>::state_1a(Token const& t)

{

    token_id id = token_id(t);

    if (T_NOT == BASE_TOKEN(id)) 

        state = &include_guards::state_1b;

    else if (!is_skippable(id))

        current_state = false;

    return t;

}



///////////////////////////////////////////////////////////////////////////////

//  state 1b: found T_NOT, looking for 'defined'

template <typename Token>

inline Token const& 

include_guards<Token>::state_1b(Token const& t)

{

    token_id id = token_id(t);

    if (T_IDENTIFIER == id && t.get_value() == "defined") 

        state = &include_guards::state_1c;

    else if (!is_skippable(id))

        current_state = false;

    return t;

}



///////////////////////////////////////////////////////////////////////////////

//  state 1c: found 'defined', looking for (optional) T_LEFTPAREN

template <typename Token>

inline Token const& 

include_guards<Token>::state_1c(Token const& t)

{

    token_id id = token_id(t);

    if (T_LEFTPAREN == id) 

        state = &include_guards::state_1d;

    else if (T_IDENTIFIER == id) {

        guard_name = t.get_value();

        state = &include_guards::state_2;

    }

    else if (!is_skippable(id))

        current_state = false;

    return t;

}



///////////////////////////////////////////////////////////////////////////////

//  state 1d: found T_LEFTPAREN, looking for T_IDENTIFIER guard

template <typename Token>

inline Token const& 

include_guards<Token>::state_1d(Token const& t)

{

    token_id id = token_id(t);

    if (T_IDENTIFIER == id) {

        guard_name = t.get_value();

        state = &include_guards::state_1e;

    }

    else if (!is_skippable(id))

        current_state = false;

    return t;

}



///////////////////////////////////////////////////////////////////////////////

//  state 1e: found T_IDENTIFIER guard, looking for T_RIGHTPAREN

template <typename Token>

inline Token const& 

include_guards<Token>::state_1e(Token const& t)

{

    token_id id = token_id(t);

    if (T_RIGHTPAREN == id) 

        state = &include_guards::state_2;

    else if (!is_skippable(id))

        current_state = false;

    return t;

}



///////////////////////////////////////////////////////////////////////////////

//  state 2: found T_IDENTIFIER, looking for #define

template <typename Token>

inline Token const& 

include_guards<Token>::state_2(Token const& t)

{

    token_id id = token_id(t);

    if (T_PP_DEFINE == id) 

        state = &include_guards::state_3;

    else if (!is_skippable(id))

        current_state = false;

    return t;

}



///////////////////////////////////////////////////////////////////////////////

//  state 3: found #define, looking for T_IDENTIFIER as recognized by state 1

template <typename Token>

inline Token const& 

include_guards<Token>::state_3(Token const& t)

{

    token_id id = token_id(t);

    if (T_IDENTIFIER == id && t.get_value() == guard_name)

        state = &include_guards::state_4;

    else if (!is_skippable(id))

        current_state = false;

    return t;

}



///////////////////////////////////////////////////////////////////////////////

//  state 4: found guard T_IDENTIFIER, looking for #endif

template <typename Token>

inline Token const& 

include_guards<Token>::state_4(Token const& t)

{

    token_id id = token_id(t);

    if (T_PP_IF == id || T_PP_IFDEF == id || T_PP_IFNDEF == id)

        ++if_depth;

    else if (T_PP_ENDIF == id) {

        if (if_depth > 0)

            --if_depth;

        else

            state = &include_guards::state_5;

    }

    return t;

}



///////////////////////////////////////////////////////////////////////////////

//  state 5: found final #endif, looking for T_EOF

template <typename Token>

inline Token const& 

include_guards<Token>::state_5(Token const& t)

{

    token_id id = token_id(t);

    if (T_EOF == id) 

        detected_guards = current_state;

    else if (!is_skippable(id))

        current_state = false;

    return t;

}



///////////////////////////////////////////////////////////////////////////////

}   // namespace cpplexer

}   // namespace wave

}   // namespace boost



// the suffix header occurs after all of the code

#ifdef BOOST_HAS_ABI_HEADERS

#include BOOST_ABI_SUFFIX

#endif



#endif // !DETECT_INCLUDE_GUARDS_HK060304_INCLUDED

