blob: 9867007cc860917a132d4dd667d2c5de0c9c7a41 [file] [log] [blame]
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
Token sequence analysis and transformation helper functions
http://www.boost.org/
Copyright (c) 2001-2011 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(CPP_MACROMAP_UTIL_HPP_HK041119)
#define CPP_MACROMAP_UTIL_HPP_HK041119
#include <boost/assert.hpp>
#include <boost/wave/wave_config.hpp>
#include <boost/wave/token_ids.hpp>
#include <boost/wave/util/unput_queue_iterator.hpp>
// this must occur after all of the includes and before any code appears
#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif
///////////////////////////////////////////////////////////////////////////////
//
// This file contains the definition of several token sequence analyze
// and transformation utility functions needed during macro handling.
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
namespace boost {
namespace wave {
namespace util {
///////////////////////////////////////////////////////////////////////////////
namespace on_exit {
///////////////////////////////////////////////////////////////////////////
//
// On destruction pop the first element of the list given as the argument
//
///////////////////////////////////////////////////////////////////////////
template <typename ContainerT>
class pop_front {
public:
pop_front(ContainerT &list_) : list(list_) {}
~pop_front() { list.pop_front(); }
private:
ContainerT &list;
};
///////////////////////////////////////////////////////////////////////////
//
// Append a given list to the list given as argument
// On destruction pop the first element of the list given as argument
//
///////////////////////////////////////////////////////////////////////////
template <typename ContainerT>
class splice_pop_front {
public:
splice_pop_front(ContainerT &list_, ContainerT &queue)
: list(list_)
{
list.splice(list.end(), queue);
}
~splice_pop_front() { list.pop_front(); }
private:
ContainerT &list;
};
///////////////////////////////////////////////////////////////////////////
//
// On destruction reset a referenced value to its initial state
//
///////////////////////////////////////////////////////////////////////////
template <typename TypeT>
class reset {
public:
reset(TypeT &target_value_, TypeT new_value)
: target_value(target_value_), old_value(target_value_)
{
target_value_ = new_value;
}
~reset() { target_value = old_value; }
private:
TypeT &target_value;
TypeT old_value;
};
///////////////////////////////////////////////////////////////////////////
//
// On destruction assign the given iterator back
//
///////////////////////////////////////////////////////////////////////////
template <typename IteratorT, typename UnputIteratorT>
class assign
{
public:
assign(IteratorT &it_, UnputIteratorT const &uit_)
: it(it_), uit(uit_) {}
~assign() { it = uit.base(); }
private:
IteratorT &it;
UnputIteratorT const &uit;
};
template <typename IteratorT>
class assign<IteratorT, IteratorT> {
public:
assign(IteratorT &it_, IteratorT const &uit_)
: it(it_), uit(uit_) {}
~assign() { it = uit; }
private:
IteratorT &it;
IteratorT const &uit;
};
///////////////////////////////////////////////////////////////////////////////
} // namespace on_exit
///////////////////////////////////////////////////////////////////////////////
namespace impl {
///////////////////////////////////////////////////////////////////////////////
//
// Test, whether a given identifier resolves to a predefined name
//
///////////////////////////////////////////////////////////////////////////////
template <typename StringT>
inline bool
is_special_macroname (StringT const &name)
{
if (name.size() < 7)
return false;
if ("defined" == name)
return true;
if ('_' == name[0] && '_' == name[1]) {
StringT str = name.substr(2);
if (str == "cplusplus" || str == "STDC__" ||
str == "TIME__" || str == "DATE__" ||
str == "LINE__" || str == "FILE__" ||
str == "INCLUDE_LEVEL__")
{
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
//
// Test, whether two tokens are to be considered equal (different sequences
// of whitespace are considered to be equal)
//
///////////////////////////////////////////////////////////////////////////////
template <typename TokenT>
inline bool
token_equals(TokenT const &left, TokenT const &right)
{
using namespace boost::wave;
if (IS_CATEGORY(left, ParameterTokenType)) {
// if the existing token is of type T_PARAMETERBASE, then the right token
// must be of type T_IDENTIFIER or a keyword
token_id id = token_id(right);
return (T_IDENTIFIER == id ||
IS_CATEGORY(id, KeywordTokenType) ||
IS_EXTCATEGORY(id, OperatorTokenType|AltExtTokenType) ||
IS_CATEGORY(id, BoolLiteralTokenType)) &&
left.get_value() == right.get_value();
}
// if the left token has whitespace, the value is irrelevant
return token_id(left) == token_id(right) && (
IS_CATEGORY(left, WhiteSpaceTokenType) ||
left.get_value() == right.get_value()
);
}
///////////////////////////////////////////////////////////////////////////////
//
// Tests, whether two macro definitions are equal
//
///////////////////////////////////////////////////////////////////////////////
template <typename ContainerT>
inline bool
definition_equals(ContainerT const &definition,
ContainerT const &new_definition)
{
typedef typename ContainerT::const_iterator const_iterator_type;
const_iterator_type first1 = definition.begin();
const_iterator_type last1 = definition.end();
const_iterator_type first2 = new_definition.begin();
const_iterator_type last2 = new_definition.end();
while (first1 != last1 && first2 != last2 && token_equals(*first1, *first2))
{
// skip whitespace, if both sequences have a whitespace next
token_id id1 = next_token<const_iterator_type>::peek(first1, last1, false);
token_id id2 = next_token<const_iterator_type>::peek(first2, last2, false);
if (IS_CATEGORY(id1, WhiteSpaceTokenType) &&
IS_CATEGORY(id2, WhiteSpaceTokenType))
{
// all consecutive whitespace tokens count as one whitespace
// adjust first1 and first2 accordingly
skip_whitespace(first1, last1);
skip_whitespace(first2, last2);
}
else if (!IS_CATEGORY(id1, WhiteSpaceTokenType) &&
!IS_CATEGORY(id2, WhiteSpaceTokenType))
{
++first1;
++first2;
}
else {
// the sequences differ
break;
}
}
return (first1 == last1 && first2 == last2) ? true : false;
}
///////////////////////////////////////////////////////////////////////////////
//
// Tests, whether two given sets of macro parameters are equal
//
///////////////////////////////////////////////////////////////////////////////
template <typename ContainerT>
inline bool
parameters_equal(ContainerT const &parameters, ContainerT const &new_parameters)
{
if (parameters.size() != new_parameters.size())
return false; // different parameter count
typedef typename ContainerT::const_iterator const_iterator_type;
const_iterator_type first1 = parameters.begin();
const_iterator_type last1 = parameters.end();
const_iterator_type first2 = new_parameters.begin();
const_iterator_type last2 = new_parameters.end();
while (first1 != last1 && first2 != last2) {
// parameters are different, if the corresponding tokens are different
using namespace boost::wave;
if (token_id(*first1) != token_id(*first2) ||
(*first1).get_value() != (*first2).get_value())
{
break;
}
++first1;
++first2;
}
return (first1 == last1 && first2 == last2) ? true : false;
}
///////////////////////////////////////////////////////////////////////////////
//
// Strip leading and trailing whitespace from the given token sequence
//
///////////////////////////////////////////////////////////////////////////////
template <typename ContainerT>
inline void
trim_replacement_list (ContainerT &replacement_list)
{
using namespace boost::wave;
// strip leading whitespace
if (replacement_list.size() > 0) {
typename ContainerT::iterator end = replacement_list.end();
typename ContainerT::iterator it = replacement_list.begin();
while (it != end && IS_CATEGORY(*it, WhiteSpaceTokenType)) {
if (T_PLACEHOLDER != token_id(*it)) {
typename ContainerT::iterator next = it;
++next;
replacement_list.erase(it);
it = next;
}
else {
++it;
}
}
}
// strip trailing whitespace
if (replacement_list.size() > 0) {
typename ContainerT::reverse_iterator rend = replacement_list.rend();
typename ContainerT::reverse_iterator rit = replacement_list.rbegin();
while (rit != rend && IS_CATEGORY(*rit, WhiteSpaceTokenType))
++rit;
typename ContainerT::iterator end = replacement_list.end();
typename ContainerT::iterator it = rit.base();
while (it != end && IS_CATEGORY(*it, WhiteSpaceTokenType)) {
if (T_PLACEHOLDER != token_id(*it)) {
typename ContainerT::iterator next = it;
++next;
replacement_list.erase(it);
it = next;
}
else {
++it;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
//
// Remove all placeholder tokens from the given token sequence
//
///////////////////////////////////////////////////////////////////////////////
template <typename ContainerT>
inline void
remove_placeholders (ContainerT &replacement_list)
{
using namespace boost::wave;
// strip leading whitespace
if (replacement_list.size() > 0) {
typename ContainerT::iterator end = replacement_list.end();
typename ContainerT::iterator it = replacement_list.begin();
while (it != end) {
if (T_PLACEHOLDER == token_id(*it)) {
typename ContainerT::iterator next = it;
++next;
replacement_list.erase(it);
it = next;
}
else {
++it;
}
}
// remove all 'new' leading and trailing whitespace
trim_replacement_list(replacement_list);
}
}
///////////////////////////////////////////////////////////////////////////////
//
// Remove all whitespace tokens on the left side of the given token sequence
//
///////////////////////////////////////////////////////////////////////////////
template <typename ContainerT>
inline void
trim_sequence_left (ContainerT &argument)
{
using namespace boost::wave;
// strip leading whitespace (should be only one token)
if (argument.size() > 0 &&
IS_CATEGORY(argument.front(), WhiteSpaceTokenType))
{
argument.pop_front();
}
}
///////////////////////////////////////////////////////////////////////////////
//
// Remove all whitespace tokens on the right side of the given token sequence
//
///////////////////////////////////////////////////////////////////////////////
template <typename ContainerT>
inline void
trim_sequence_right (ContainerT &argument)
{
using namespace boost::wave;
// strip trailing whitespace (should be only one token)
if (argument.size() > 0 &&
IS_CATEGORY(argument.back(), WhiteSpaceTokenType))
{
argument.pop_back();
}
}
///////////////////////////////////////////////////////////////////////////////
//
// Remove all whitespace tokens on the left and right sides of the given token
// sequence
//
///////////////////////////////////////////////////////////////////////////////
template <typename ContainerT>
inline void
trim_sequence (ContainerT &argument)
{
trim_sequence_left(argument);
trim_sequence_right(argument);
}
///////////////////////////////////////////////////////////////////////////////
//
// Tests, whether the given token sequence consists out of whitespace only
//
///////////////////////////////////////////////////////////////////////////////
template <typename ContainerT>
inline bool
is_whitespace_only (ContainerT const &argument)
{
typename ContainerT::const_iterator end = argument.end();
for (typename ContainerT::const_iterator it = argument.begin();
it != end; ++it)
{
if (!IS_CATEGORY(*it, WhiteSpaceTokenType))
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// call 'skipped_token' preprocessing hook
template <typename ContextT>
void call_skipped_token_hook(ContextT& ctx,
typename ContextT::token_type const& skipped)
{
#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0
ctx.get_hooks().skipped_token(skipped);
#else
ctx.get_hooks().skipped_token(ctx.derived(), skipped);
#endif
}
///////////////////////////////////////////////////////////////////////////////
//
// Skip forward to a given token
//
///////////////////////////////////////////////////////////////////////////////
template <typename ContextT, typename IteratorT>
inline bool
skip_to_token(ContextT& ctx, IteratorT &it, IteratorT const &end,
token_id id, bool& seen_newline)
{
using namespace boost::wave;
if (token_id(*it) == id)
return true;
// call_skipped_token_hook(ctx, *it);
if (++it == end)
return false;
while (IS_CATEGORY(*it, WhiteSpaceTokenType) ||
T_NEWLINE == token_id(*it))
{
if (T_NEWLINE == token_id(*it))
seen_newline = true;
// call_skipped_token_hook(ctx, *it);
if (++it == end)
return false;
}
return token_id(*it) == id;
}
///////////////////////////////////////////////////////////////////////////////
//
// Get the full name of a given macro name (concatenate the string
// representations of the single tokens).
//
///////////////////////////////////////////////////////////////////////////////
template <typename IteratorT>
inline std::string
get_full_name(IteratorT const &begin, IteratorT const &end)
{
std::string full_name;
for (IteratorT err_it = begin; err_it != end; ++err_it)
full_name += (*err_it).get_value().c_str();
return full_name;
}
///////////////////////////////////////////////////////////////////////////////
//
// The following predicate is used in conjunction with the remove_copy_if
// algorithm to allow the detection of an eventually copied operator ##.
// No removal is performed in any case.
//
///////////////////////////////////////////////////////////////////////////////
class find_concat_operator {
public:
find_concat_operator(bool &found_) : found_concat(found_) {}
template <typename TokenT>
bool operator()(TokenT const &tok)
{
using namespace boost::wave;
if (T_POUND_POUND == BASE_TOKEN(token_id(tok)))
found_concat = true;
return false;
}
private:
bool &found_concat;
};
///////////////////////////////////////////////////////////////////////////////
// Convert a string of an arbitrary string compatible type to a internal
// string (BOOST_WAVE_STRING)
template <typename Target, typename Src>
struct to_string_helper
{
typedef Target type;
static Target call(Src const& str)
{
return Target(str.c_str());
}
};
// do nothing if types are equal
template <typename Src>
struct to_string_helper<Src, Src>
{
typedef Src const& type;
static Src const& call(Src const& str)
{
return str;
}
};
template <typename Target>
struct to_string_helper<Target, char const*>
{
typedef Target type;
static Target call(char const* str)
{
return Target(str);
}
};
///////////////////////////////////////////////////////////////////////////////
} // namespace impl
template <typename Target, typename Src>
inline typename impl::to_string_helper<Target, Src>::type
to_string(Src const& src)
{
return impl::to_string_helper<Target, Src>::call(src);
}
///////////////////////////////////////////////////////////////////////////////
} // namespace util
} // namespace wave
} // namespace boost
// the suffix header occurs after all of the code
#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif
#endif // !defined(CPP_MACROMAP_UTIL_HPP_HK041119)