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