/*============================================================================= Boost.Wave: A Standard compliant C++ preprocessor library Macro expansion engine http://www.boost.org/ Copyright (c) 2001-2010 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_HPP_CB8F51B0_A3F0_411C_AEF4_6FF631B8B414_INCLUDED) #define CPP_MACROMAP_HPP_CB8F51B0_A3F0_411C_AEF4_6FF631B8B414_INCLUDED #include #include #include #include #include #include #include #include #include #include #include #if BOOST_WAVE_SERIALIZATION != 0 #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include // 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 util { /////////////////////////////////////////////////////////////////////////////// // // macromap // // This class holds all currently defined macros and on demand expands // those macro definitions // /////////////////////////////////////////////////////////////////////////////// template class macromap { typedef macromap self_type; typedef typename ContextT::token_type token_type; typedef typename token_type::string_type string_type; typedef typename token_type::position_type position_type; typedef typename ContextT::token_sequence_type definition_container_type; typedef std::vector parameter_container_type; typedef macro_definition macro_definition_type; typedef symbol_table defined_macros_type; typedef typename defined_macros_type::value_type::second_type macro_ref_type; public: macromap(ContextT &ctx_) : current_macros(0), defined_macros(new defined_macros_type(1)), main_pos("", 0), ctx(ctx_), macro_uid(1) { current_macros = defined_macros.get(); } ~macromap() {} // Add a new macro to the given macro scope bool add_macro(token_type const &name, bool has_parameters, parameter_container_type ¶meters, definition_container_type &definition, bool is_predefined = false, defined_macros_type *scope = 0); // Tests, whether the given macro name is defined in the given macro scope bool is_defined(string_type const &name, typename defined_macros_type::iterator &it, defined_macros_type *scope = 0) const; // expects a token sequence as its parameters template bool is_defined(IteratorT const &begin, IteratorT const &end) const; // expects an arbitrary string as its parameter bool is_defined(string_type const &str) const; // Get the macro definition for the given macro scope bool get_macro(string_type const &name, bool &has_parameters, bool &is_predefined, position_type &pos, parameter_container_type ¶meters, definition_container_type &definition, defined_macros_type *scope = 0) const; // Remove a macro name from the given macro scope bool remove_macro(string_type const &name, position_type const& pos, bool even_predefined = false); template token_type const &expand_tokensequence(IteratorT &first, IteratorT const &last, ContainerT &pending, ContainerT &expanded, bool& seen_newline, bool expand_operator_defined); // Expand all macros inside the given token sequence template void expand_whole_tokensequence(ContainerT &expanded, IteratorT &first, IteratorT const &last, bool expand_operator_defined); // Init the predefined macros (add them to the given scope) void init_predefined_macros(char const *fname = "", defined_macros_type *scope = 0, bool at_global_scope = true); void predefine_macro(defined_macros_type *scope, string_type const &name, token_type const &t); // Init the internal macro symbol namespace void reset_macromap(); position_type &get_main_pos() { return main_pos; } // interface for macro name introspection typedef typename defined_macros_type::name_iterator name_iterator; typedef typename defined_macros_type::const_name_iterator const_name_iterator; name_iterator begin() { return defined_macros_type::make_iterator(current_macros->begin()); } name_iterator end() { return defined_macros_type::make_iterator(current_macros->end()); } const_name_iterator begin() const { return defined_macros_type::make_iterator(current_macros->begin()); } const_name_iterator end() const { return defined_macros_type::make_iterator(current_macros->end()); } protected: // Helper functions for expanding all macros in token sequences template token_type const &expand_tokensequence_worker(ContainerT &pending, unput_queue_iterator &first, unput_queue_iterator const &last, bool& seen_newline, bool expand_operator_defined); // Collect all arguments supplied to a macro invocation #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 template typename std::vector::size_type collect_arguments ( token_type const curr_token, std::vector &arguments, IteratorT &next, IteratorT const &end, SizeT const ¶meter_count, bool& seen_newline); #else template typename std::vector::size_type collect_arguments ( token_type const curr_token, std::vector &arguments, IteratorT &next, IteratorT &endparen, IteratorT const &end, SizeT const ¶meter_count, bool& seen_newline); #endif // Expand a single macro name template bool expand_macro(ContainerT &pending, token_type const &name, typename defined_macros_type::iterator it, IteratorT &first, IteratorT const &last, bool& seen_newline, bool expand_operator_defined, defined_macros_type *scope = 0, ContainerT *queue_symbol = 0); // Expand a predefined macro (__LINE__, __FILE__ and __INCLUDE_LEVEL__) template bool expand_predefined_macro(token_type const &curr_token, ContainerT &expanded); // Expand a single macro argument template void expand_argument (typename std::vector::size_type arg, std::vector &arguments, std::vector &expanded_args, bool expand_operator_defined, std::vector &has_expanded_args); // Expand the replacement list (replaces parameters with arguments) template void expand_replacement_list( macro_definition_type const ¯odefinition, std::vector &arguments, bool expand_operator_defined, ContainerT &expanded); // Rescans the replacement list for macro expansion template void rescan_replacement_list(token_type const &curr_token, macro_definition_type ¯odef, ContainerT &replacement_list, ContainerT &expanded, bool expand_operator_defined, IteratorT &nfirst, IteratorT const &nlast); // Resolves the operator defined() and replces the token with "0" or "1" template token_type const &resolve_defined(IteratorT &first, IteratorT const &last, ContainerT &expanded); // Resolve operator _Pragma or the #pragma directive template bool resolve_operator_pragma(IteratorT &first, IteratorT const &last, ContainerT &expanded, bool& seen_newline); // Handle the concatenation operator '##' template bool concat_tokensequence(ContainerT &expanded); template bool is_valid_concat(string_type new_value, position_type const &pos, ContainerT &rescanned); #if BOOST_WAVE_SERIALIZATION != 0 public: BOOST_STATIC_CONSTANT(unsigned int, version = 0x10); BOOST_STATIC_CONSTANT(unsigned int, version_mask = 0x0f); private: friend class boost::serialization::access; template void save(Archive &ar, const unsigned int version) const { using namespace boost::serialization; ar & make_nvp("defined_macros", defined_macros); } template void load(Archive &ar, const unsigned int loaded_version) { using namespace boost::serialization; if (version != (loaded_version & ~version_mask)) { BOOST_WAVE_THROW(preprocess_exception, incompatible_config, "cpp_context state version", get_main_pos()); } ar & make_nvp("defined_macros", defined_macros); current_macros = defined_macros.get(); } BOOST_SERIALIZATION_SPLIT_MEMBER() #endif private: defined_macros_type *current_macros; // current symbol table boost::shared_ptr defined_macros; // global symbol table token_type act_token; // current token position_type main_pos; // last token position in the pp_iterator string_type base_name; // the name to be expanded by __BASE_FILE__ ContextT &ctx; // context object associated with the macromap long macro_uid; predefined_macros predef; // predefined macro support }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // add_macro(): adds a new macro to the macromap // /////////////////////////////////////////////////////////////////////////////// template inline bool macromap::add_macro(token_type const &name, bool has_parameters, parameter_container_type ¶meters, definition_container_type &definition, bool is_predefined, defined_macros_type *scope) { if (!is_predefined && impl::is_special_macroname (name.get_value())) { // exclude special macro names BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception, illegal_redefinition, name.get_value().c_str(), main_pos, name.get_value().c_str()); return false; } if (boost::wave::need_variadics(ctx.get_language()) && "__VA_ARGS__" == name.get_value()) { // can't use __VA_ARGS__ as a macro name BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception, bad_define_statement_va_args, name.get_value().c_str(), main_pos, name.get_value().c_str()); return false; } if (AltExtTokenType == (token_id(name) & ExtTokenOnlyMask)) { // exclude special operator names BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception, illegal_operator_redefinition, name.get_value().c_str(), main_pos, name.get_value().c_str()); return false; } // try to define the new macro defined_macros_type *current_scope = scope ? scope : current_macros; typename defined_macros_type::iterator it = current_scope->find(name.get_value()); if (it != current_scope->end()) { // redefinition, should not be different macro_definition_type* macrodef = (*it).second.get(); if (macrodef->is_functionlike != has_parameters || !impl::parameters_equal(macrodef->macroparameters, parameters) || !impl::definition_equals(macrodef->macrodefinition, definition)) { BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception, macro_redefinition, name.get_value().c_str(), main_pos, name.get_value().c_str()); } return false; } // test the validity of the parameter names if (has_parameters) { std::set names; typedef typename parameter_container_type::iterator parameter_iterator_type; typedef typename std::set::iterator name_iterator_type; parameter_iterator_type end = parameters.end(); for (parameter_iterator_type itp = parameters.begin(); itp != end; ++itp) { name_iterator_type pit = names.find((*itp).get_value()); if (pit != names.end()) { // duplicate parameter name BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception, duplicate_parameter_name, (*pit).c_str(), main_pos, name.get_value().c_str()); return false; } names.insert((*itp).get_value()); } } // insert a new macro node std::pair p = current_scope->insert( typename defined_macros_type::value_type( name.get_value(), macro_ref_type(new macro_definition_type(name, has_parameters, is_predefined, ++macro_uid) ) ) ); if (!p.second) { BOOST_WAVE_THROW_NAME_CTX(ctx, macro_handling_exception, macro_insertion_error, name.get_value().c_str(), main_pos, name.get_value().c_str()); return false; } // add the parameters and the definition std::swap((*p.first).second->macroparameters, parameters); std::swap((*p.first).second->macrodefinition, definition); // call the context supplied preprocessing hook #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 ctx.get_hooks().defined_macro(name, has_parameters, (*p.first).second->macroparameters, (*p.first).second->macrodefinition, is_predefined); #else ctx.get_hooks().defined_macro(ctx.derived(), name, has_parameters, (*p.first).second->macroparameters, (*p.first).second->macrodefinition, is_predefined); #endif return true; } /////////////////////////////////////////////////////////////////////////////// // // is_defined(): returns, whether a given macro is already defined // /////////////////////////////////////////////////////////////////////////////// template inline bool macromap::is_defined(typename token_type::string_type const &name, typename defined_macros_type::iterator &it, defined_macros_type *scope) const { if (0 == scope) scope = current_macros; if ((it = scope->find(name)) != scope->end()) return true; // found in symbol table // quick pre-check if (name.size() < 8 || '_' != name[0] || '_' != name[1]) return false; // quick check failed return name == "__LINE__" || name == "__FILE__" || name == "__INCLUDE_LEVEL__"; } template template inline bool macromap::is_defined(IteratorT const &begin, IteratorT const &end) const { // in normal mode the name under inspection should consist of an identifier // only token_id id = token_id(*begin); if (T_IDENTIFIER != id && !IS_CATEGORY(id, KeywordTokenType) && !IS_EXTCATEGORY(id, OperatorTokenType|AltExtTokenType) && !IS_CATEGORY(id, BoolLiteralTokenType)) { std::string msg(impl::get_full_name(begin, end)); BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, invalid_macroname, msg.c_str(), main_pos); return false; } IteratorT it = begin; string_type name ((*it).get_value()); typename defined_macros_type::iterator cit; if (++it != end) { // there should be only one token as the inspected name std::string msg(impl::get_full_name(begin, end)); BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, invalid_macroname, msg.c_str(), main_pos); return false; } return is_defined(name, cit, 0); } /////////////////////////////////////////////////////////////////////////////// // same as above, only takes an arbitrary string type as its parameter template inline bool macromap::is_defined(string_type const &str) const { typename defined_macros_type::iterator cit; return is_defined(str, cit, 0); } /////////////////////////////////////////////////////////////////////////////// // // Get the macro definition for the given macro scope // /////////////////////////////////////////////////////////////////////////////// template inline bool macromap::get_macro(string_type const &name, bool &has_parameters, bool &is_predefined, position_type &pos, parameter_container_type ¶meters, definition_container_type &definition, defined_macros_type *scope) const { typename defined_macros_type::iterator it; if (!is_defined(name, it, scope)) return false; macro_definition_type ¯o_def = *(*it).second.get(); has_parameters = macro_def.is_functionlike; is_predefined = macro_def.is_predefined; pos = macro_def.macroname.get_position(); parameters = macro_def.macroparameters; definition = macro_def.macrodefinition; return true; } /////////////////////////////////////////////////////////////////////////////// // // remove_macro(): remove a macro from the macromap // /////////////////////////////////////////////////////////////////////////////// template inline bool macromap::remove_macro(string_type const &name, position_type const& pos, bool even_predefined) { typename defined_macros_type::iterator it = current_macros->find(name); if (it != current_macros->end()) { if ((*it).second->is_predefined) { if (!even_predefined || impl::is_special_macroname(name)) { BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, bad_undefine_statement, name.c_str(), main_pos); return false; } } current_macros->erase(it); // call the context supplied preprocessing hook function token_type tok(T_IDENTIFIER, name, pos); #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 ctx.get_hooks().undefined_macro(tok); #else ctx.get_hooks().undefined_macro(ctx.derived(), tok); #endif return true; } else if (impl::is_special_macroname(name)) { BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, bad_undefine_statement, name.c_str(), pos); } return false; // macro was not defined } /////////////////////////////////////////////////////////////////////////////// // // expand_tokensequence // // This function is a helper function which wraps the given iterator // range into corresponding unput_iterator's and calls the main workhorse // of the macro expansion engine (the function expand_tokensequence_worker) // // This is the top level macro expansion function called from the // preprocessing iterator component only. // /////////////////////////////////////////////////////////////////////////////// template template inline typename ContextT::token_type const & macromap::expand_tokensequence(IteratorT &first, IteratorT const &last, ContainerT &pending, ContainerT &expanded, bool& seen_newline, bool expand_operator_defined) { typedef impl::gen_unput_queue_iterator gen_type; typedef typename gen_type::return_type iterator_type; iterator_type first_it = gen_type::generate(expanded, first); iterator_type last_it = gen_type::generate(last); on_exit::assign on_exit(first, first_it); return expand_tokensequence_worker(pending, first_it, last_it, seen_newline, expand_operator_defined); } /////////////////////////////////////////////////////////////////////////////// // // expand_tokensequence_worker // // This function is the main workhorse of the macro expansion engine. It // expands as much tokens as needed to identify the next preprocessed // token to return to the caller. // It returns the next preprocessed token. // // The iterator 'first' is adjusted accordingly. // /////////////////////////////////////////////////////////////////////////////// template template inline typename ContextT::token_type const & macromap::expand_tokensequence_worker( ContainerT &pending, unput_queue_iterator &first, unput_queue_iterator const &last, bool& seen_newline, bool expand_operator_defined) { // if there exist pending tokens (tokens, which are already preprocessed), then // return the next one from there if (!pending.empty()) { on_exit::pop_front pop_front_token(pending); return act_token = pending.front(); } // analyze the next element of the given sequence, if it is an // T_IDENTIFIER token, try to replace this as a macro etc. using namespace boost::wave; typedef unput_queue_iterator iterator_type; if (first != last) { token_id id = token_id(*first); // ignore placeholder tokens if (T_PLACEHOLDER == id) { token_type placeholder = *first; ++first; if (first == last) return act_token = placeholder; id = token_id(*first); } if (T_IDENTIFIER == id || IS_CATEGORY(id, KeywordTokenType) || IS_EXTCATEGORY(id, OperatorTokenType|AltExtTokenType) || IS_CATEGORY(id, BoolLiteralTokenType)) { // try to replace this identifier as a macro if (expand_operator_defined && (*first).get_value() == "defined") { // resolve operator defined() return resolve_defined(first, last, pending); } else if (boost::wave::need_variadics(ctx.get_language()) && (*first).get_value() == "_Pragma") { // in C99 mode only: resolve the operator _Pragma token_type curr_token = *first; if (!resolve_operator_pragma(first, last, pending, seen_newline) || pending.size() > 0) { // unknown to us pragma or supplied replacement, return the // next token on_exit::pop_front pop_token(pending); return act_token = pending.front(); } // the operator _Pragma() was eaten completely, continue return act_token = token_type(T_PLACEHOLDER, "_", curr_token.get_position()); } token_type name_token (*first); typename defined_macros_type::iterator it; if (is_defined(name_token.get_value(), it)) { // the current token contains an identifier, which is currently // defined as a macro if (expand_macro(pending, name_token, it, first, last, seen_newline, expand_operator_defined)) { // the tokens returned by expand_macro should be rescanned // beginning at the last token of the returned replacement list if (first != last) { // splice the last token back into the input queue typename ContainerT::reverse_iterator rit = pending.rbegin(); first.get_unput_queue().splice( first.get_unput_queue().begin(), pending, (++rit).base(), pending.end()); } // fall through ... } else if (!pending.empty()) { // return the first token from the pending queue on_exit::pop_front pop_queue (pending); return act_token = pending.front(); } else { // macro expansion reached the eoi return act_token = token_type(); } // return the next preprocessed token return expand_tokensequence_worker(pending, first, last, seen_newline, expand_operator_defined); } // else if (expand_operator_defined) { // // in preprocessing conditionals undefined identifiers and keywords // // are to be replaced with '0' (see. C++ standard 16.1.4, [cpp.cond]) // return act_token = // token_type(T_INTLIT, "0", (*first++).get_position()); // } else { act_token = name_token; ++first; return act_token; } } else if (expand_operator_defined && IS_CATEGORY(*first, BoolLiteralTokenType)) { // expanding a constant expression inside #if/#elif, special handling // of 'true' and 'false' // all remaining identifiers and keywords, except for true and false, // are replaced with the pp-number 0 (C++ standard 16.1.4, [cpp.cond]) return act_token = token_type(T_INTLIT, T_TRUE != id ? "0" : "1", (*first++).get_position()); } else { act_token = *first; ++first; return act_token; } } return act_token = token_type(); // eoi } /////////////////////////////////////////////////////////////////////////////// // // collect_arguments(): collect the actual arguments of a macro invocation // // return the number of successfully detected non-empty arguments // /////////////////////////////////////////////////////////////////////////////// #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 template template inline typename std::vector::size_type macromap::collect_arguments (token_type const curr_token, std::vector &arguments, IteratorT &next, IteratorT const &end, SizeT const ¶meter_count, bool& seen_newline) #else template template inline typename std::vector::size_type macromap::collect_arguments (token_type const curr_token, std::vector &arguments, IteratorT &next, IteratorT &endparen, IteratorT const &end, SizeT const ¶meter_count, bool& seen_newline) #endif { using namespace boost::wave; arguments.push_back(ContainerT()); // collect the actual arguments typename std::vector::size_type count_arguments = 0; int nested_parenthesis_level = 1; ContainerT *argument = &arguments[0]; bool was_whitespace = false; token_type startof_argument_list = *next; while (++next != end && nested_parenthesis_level) { token_id id = token_id(*next); if (0 == parameter_count && !IS_CATEGORY((*next), WhiteSpaceTokenType) && id != T_NEWLINE && id != T_RIGHTPAREN && id != T_LEFTPAREN) { // there shouldn't be any arguments BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, too_many_macroarguments, curr_token.get_value().c_str(), main_pos); return 0; } switch (static_cast(id)) { case T_LEFTPAREN: ++nested_parenthesis_level; argument->push_back(*next); was_whitespace = false; break; case T_RIGHTPAREN: { if (--nested_parenthesis_level >= 1) argument->push_back(*next); else { // found closing parenthesis // trim_sequence(argument); #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0 endparen = next; #endif if (parameter_count > 0) { if (argument->empty() || impl::is_whitespace_only(*argument)) { #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 if (boost::wave::need_variadics(ctx.get_language())) { // store a placemarker as the argument argument->push_back(token_type(T_PLACEMARKER, "\xA7", (*next).get_position())); ++count_arguments; } #endif } else { ++count_arguments; } } } was_whitespace = false; } break; case T_COMMA: if (1 == nested_parenthesis_level) { // next parameter // trim_sequence(argument); if (argument->empty() || impl::is_whitespace_only(*argument)) { #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 if (boost::wave::need_variadics(ctx.get_language())) { // store a placemarker as the argument argument->push_back(token_type(T_PLACEMARKER, "\xA7", (*next).get_position())); ++count_arguments; } #endif } else { ++count_arguments; } arguments.push_back(ContainerT()); // add new arg argument = &arguments[arguments.size()-1]; } else { // surrounded by parenthesises, so store to current argument argument->push_back(*next); } was_whitespace = false; break; case T_NEWLINE: seen_newline = true; /* fall through */ case T_SPACE: case T_SPACE2: case T_CCOMMENT: if (!was_whitespace) argument->push_back(token_type(T_SPACE, " ", (*next).get_position())); was_whitespace = true; break; // skip whitespace case T_PLACEHOLDER: break; // ignore placeholder default: argument->push_back(*next); was_whitespace = false; break; } } if (nested_parenthesis_level >= 1) { // missing ')': improperly terminated macro invocation BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, improperly_terminated_macro, "missing ')'", main_pos); return 0; } // if no argument was expected and we didn't find any, than remove the empty // element if (0 == parameter_count && 0 == count_arguments) { BOOST_ASSERT(1 == arguments.size()); arguments.clear(); } return count_arguments; } /////////////////////////////////////////////////////////////////////////////// // // expand_whole_tokensequence // // fully expands a given token sequence // /////////////////////////////////////////////////////////////////////////////// template template inline void macromap::expand_whole_tokensequence(ContainerT &expanded, IteratorT &first, IteratorT const &last, bool expand_operator_defined) { typedef impl::gen_unput_queue_iterator gen_type; typedef typename gen_type::return_type iterator_type; ContainerT empty; iterator_type first_it = gen_type::generate(empty, first); iterator_type last_it = gen_type::generate(last); on_exit::assign on_exit(first, first_it); ContainerT pending_queue; bool seen_newline; while (!pending_queue.empty() || first_it != last_it) { expanded.push_back( expand_tokensequence_worker(pending_queue, first_it, last_it, seen_newline, expand_operator_defined) ); } // should have returned all expanded tokens BOOST_ASSERT(pending_queue.empty()/* && unput_queue.empty()*/); } /////////////////////////////////////////////////////////////////////////////// // // expand_argument // // fully expands the given argument of a macro call // /////////////////////////////////////////////////////////////////////////////// template template inline void macromap::expand_argument ( typename std::vector::size_type arg, std::vector &arguments, std::vector &expanded_args, bool expand_operator_defined, std::vector &has_expanded_args) { if (!has_expanded_args[arg]) { // expand the argument only once typedef typename std::vector::value_type::iterator argument_iterator_type; argument_iterator_type begin_it = arguments[arg].begin(); argument_iterator_type end_it = arguments[arg].end(); expand_whole_tokensequence(expanded_args[arg], begin_it, end_it, expand_operator_defined); impl::remove_placeholders(expanded_args[arg]); has_expanded_args[arg] = true; } } /////////////////////////////////////////////////////////////////////////////// // // expand_replacement_list // // fully expands the replacement list of a given macro with the // actual arguments/expanded arguments // handles the '#' [cpp.stringize] and the '##' [cpp.concat] operator // /////////////////////////////////////////////////////////////////////////////// template template inline void macromap::expand_replacement_list( macro_definition_type const ¯odef, std::vector &arguments, bool expand_operator_defined, ContainerT &expanded) { using namespace boost::wave; typedef typename macro_definition_type::const_definition_iterator_t macro_definition_iter_t; std::vector expanded_args(arguments.size()); std::vector has_expanded_args(arguments.size()); bool seen_concat = false; bool adjacent_concat = false; bool adjacent_stringize = false; macro_definition_iter_t cend = macrodef.macrodefinition.end(); for (macro_definition_iter_t cit = macrodef.macrodefinition.begin(); cit != cend; ++cit) { bool use_replaced_arg = true; token_id base_id = BASE_TOKEN(token_id(*cit)); if (T_POUND_POUND == base_id) { // concatenation operator adjacent_concat = true; seen_concat = true; } else if (T_POUND == base_id) { // stringize operator adjacent_stringize = true; } else { if (adjacent_stringize || adjacent_concat || T_POUND_POUND == impl::next_token ::peek(cit, cend)) { use_replaced_arg = false; } if (adjacent_concat) // spaces after '##' ? adjacent_concat = IS_CATEGORY(*cit, WhiteSpaceTokenType); } if (IS_CATEGORY((*cit), ParameterTokenType)) { // copy argument 'i' instead of the parameter token i typename ContainerT::size_type i; #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 bool is_ellipsis = false; if (IS_EXTCATEGORY((*cit), ExtParameterTokenType)) { BOOST_ASSERT(boost::wave::need_variadics(ctx.get_language())); i = token_id(*cit) - T_EXTPARAMETERBASE; is_ellipsis = true; } else #endif { i = token_id(*cit) - T_PARAMETERBASE; } BOOST_ASSERT(i < arguments.size()); if (use_replaced_arg) { #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 if (is_ellipsis) { position_type const &pos = (*cit).get_position(); BOOST_ASSERT(boost::wave::need_variadics(ctx.get_language())); // ensure all variadic arguments to be expanded for (typename vector::size_type arg = i; arg < expanded_args.size(); ++arg) { expand_argument(arg, arguments, expanded_args, expand_operator_defined, has_expanded_args); } impl::replace_ellipsis(expanded_args, i, expanded, pos); } else #endif { // ensure argument i to be expanded expand_argument(i, arguments, expanded_args, expand_operator_defined, has_expanded_args); // replace argument ContainerT const &arg = expanded_args[i]; std::copy(arg.begin(), arg.end(), std::inserter(expanded, expanded.end())); } } else if (adjacent_stringize && !IS_CATEGORY(*cit, WhiteSpaceTokenType)) { // stringize the current argument BOOST_ASSERT(!arguments[i].empty()); // safe a copy of the first tokens position (not a reference!) position_type pos ((*arguments[i].begin()).get_position()); #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 if (is_ellipsis && boost::wave::need_variadics(ctx.get_language())) { impl::trim_sequence_left(arguments[i]); impl::trim_sequence_right(arguments.back()); expanded.push_back(token_type(T_STRINGLIT, impl::as_stringlit(arguments, i, pos), pos)); } else #endif { impl::trim_sequence(arguments[i]); expanded.push_back(token_type(T_STRINGLIT, impl::as_stringlit(arguments[i], pos), pos)); } adjacent_stringize = false; } else { // simply copy the original argument (adjacent '##' or '#') #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 if (is_ellipsis) { position_type const &pos = (*cit).get_position(); impl::trim_sequence_left(arguments[i]); impl::trim_sequence_right(arguments.back()); BOOST_ASSERT(boost::wave::need_variadics(ctx.get_language())); impl::replace_ellipsis(arguments, i, expanded, pos); } else #endif { ContainerT &arg = arguments[i]; impl::trim_sequence(arg); std::copy(arg.begin(), arg.end(), std::inserter(expanded, expanded.end())); } } } else if (!adjacent_stringize || T_POUND != base_id) { // insert the actual replacement token (if it is not the '#' operator) expanded.push_back(*cit); } } if (adjacent_stringize) { // error, '#' should not be the last token BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, ill_formed_operator, "stringize ('#')", main_pos); return; } // handle the cpp.concat operator if (seen_concat) concat_tokensequence(expanded); } /////////////////////////////////////////////////////////////////////////////// // // rescan_replacement_list // // As the name implies, this function is used to rescan the replacement list // after the first macro substitution phase. // /////////////////////////////////////////////////////////////////////////////// template template inline void macromap::rescan_replacement_list(token_type const &curr_token, macro_definition_type ¯o_def, ContainerT &replacement_list, ContainerT &expanded, bool expand_operator_defined, IteratorT &nfirst, IteratorT const &nlast) { if (!replacement_list.empty()) { #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 // remove the placemarkers if (boost::wave::need_variadics(ctx.get_language())) { typename ContainerT::iterator end = replacement_list.end(); typename ContainerT::iterator it = replacement_list.begin(); while (it != end) { using namespace boost::wave; if (T_PLACEMARKER == token_id(*it)) { typename ContainerT::iterator placemarker = it; ++it; replacement_list.erase(placemarker); } else { ++it; } } } #endif // rescan the replacement list, during this rescan the current macro under // expansion isn't available as an expandable macro on_exit::reset on_exit(macro_def.is_available_for_replacement, false); typename ContainerT::iterator begin_it = replacement_list.begin(); typename ContainerT::iterator end_it = replacement_list.end(); expand_whole_tokensequence(expanded, begin_it, end_it, expand_operator_defined); // trim replacement list, leave placeholder tokens untouched impl::trim_replacement_list(expanded); } if (expanded.empty()) { // the resulting replacement list should contain at least a placeholder // token expanded.push_back(token_type(T_PLACEHOLDER, "_", curr_token.get_position())); } } /////////////////////////////////////////////////////////////////////////////// // // expand_macro(): expands a defined macro // // This functions tries to expand the macro, to which points the 'first' // iterator. The functions eats up more tokens, if the macro to expand is // a function-like macro. // /////////////////////////////////////////////////////////////////////////////// template template inline bool macromap::expand_macro(ContainerT &expanded, token_type const &curr_token, typename defined_macros_type::iterator it, IteratorT &first, IteratorT const &last, bool& seen_newline, bool expand_operator_defined, defined_macros_type *scope, ContainerT *queue_symbol) { using namespace boost::wave; if (0 == scope) scope = current_macros; BOOST_ASSERT(T_IDENTIFIER == token_id(curr_token) || IS_CATEGORY(token_id(curr_token), KeywordTokenType) || IS_EXTCATEGORY(token_id(curr_token), OperatorTokenType|AltExtTokenType) || IS_CATEGORY(token_id(curr_token), BoolLiteralTokenType)); if (it == scope->end()) { ++first; // advance // try to expand a predefined macro (__FILE__, __LINE__ or __INCLUDE_LEVEL__) if (expand_predefined_macro(curr_token, expanded)) return false; // not defined as a macro if (0 != queue_symbol) { expanded.splice(expanded.end(), *queue_symbol); } else { expanded.push_back(curr_token); } return false; } // ensure the parameters to be replaced with special parameter tokens macro_definition_type ¯o_def = *(*it).second.get(); macro_def.replace_parameters(); // test if this macro is currently available for replacement if (!macro_def.is_available_for_replacement) { // this macro is marked as non-replaceable // copy the macro name itself if (0 != queue_symbol) { queue_symbol->push_back(token_type(T_NONREPLACABLE_IDENTIFIER, curr_token.get_value(), curr_token.get_position())); expanded.splice(expanded.end(), *queue_symbol); } else { expanded.push_back(token_type(T_NONREPLACABLE_IDENTIFIER, curr_token.get_value(), curr_token.get_position())); } ++first; return false; } // try to replace the current identifier as a function-like macro ContainerT replacement_list; if (T_LEFTPAREN == impl::next_token::peek(first, last)) { // called as a function-like macro impl::skip_to_token(first, last, T_LEFTPAREN, seen_newline); #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0 IteratorT seqstart = first; IteratorT seqend = first; #endif if (macro_def.is_functionlike) { // defined as a function-like macro // collect the arguments std::vector arguments; #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 typename std::vector::size_type count_args = collect_arguments (curr_token, arguments, first, last, macro_def.macroparameters.size(), seen_newline); #else typename std::vector::size_type count_args = collect_arguments (curr_token, arguments, first, seqend, last, macro_def.macroparameters.size(), seen_newline); #endif // verify the parameter count if (count_args < macro_def.macroparameters.size() || arguments.size() < macro_def.macroparameters.size()) { if (count_args != arguments.size()) { // must been at least one empty argument in C++ mode BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, empty_macroarguments, curr_token.get_value().c_str(), main_pos); } else { // too few macro arguments BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, too_few_macroarguments, curr_token.get_value().c_str(), main_pos); } return false; } if (count_args > macro_def.macroparameters.size() || arguments.size() > macro_def.macroparameters.size()) { #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 if (!macro_def.has_ellipsis) #endif { // too many macro arguments BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, too_many_macroarguments, curr_token.get_value().c_str(), main_pos); return false; } } // inject tracing support #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 ctx.get_hooks().expanding_function_like_macro( macro_def.macroname, macro_def.macroparameters, macro_def.macrodefinition, curr_token, arguments); #else if (ctx.get_hooks().expanding_function_like_macro(ctx.derived(), macro_def.macroname, macro_def.macroparameters, macro_def.macrodefinition, curr_token, arguments, seqstart, seqend)) { // do not expand this macro, just copy the whole sequence expanded.push_back(curr_token); std::copy(seqstart, first, std::inserter(expanded, expanded.end())); return false; // no further preprocessing required } #endif // expand the replacement list of this macro expand_replacement_list(macro_def, arguments, expand_operator_defined, replacement_list); } else { // defined as an object-like macro #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 ctx.get_hooks().expanding_object_like_macro( macro_def.macroname, macro_def.macrodefinition, curr_token); #else if (ctx.get_hooks().expanding_object_like_macro(ctx.derived(), macro_def.macroname, macro_def.macrodefinition, curr_token)) { // do not expand this macro, just copy the whole sequence expanded.push_back(curr_token); ++first; // skip macro name return false; // no further preprocessing required } #endif bool found = false; impl::find_concat_operator concat_tag(found); std::remove_copy_if(macro_def.macrodefinition.begin(), macro_def.macrodefinition.end(), std::inserter(replacement_list, replacement_list.end()), concat_tag); // handle concatenation operators if (found && !concat_tokensequence(replacement_list)) return false; } } else { // called as an object like macro if ((*it).second->is_functionlike) { // defined as a function-like macro if (0 != queue_symbol) { queue_symbol->push_back(curr_token); expanded.splice(expanded.end(), *queue_symbol); } else { expanded.push_back(curr_token); } ++first; // skip macro name return false; // no further preprocessing required } else { // defined as an object-like macro (expand it) #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 ctx.get_hooks().expanding_object_like_macro( macro_def.macroname, macro_def.macrodefinition, curr_token); #else if (ctx.get_hooks().expanding_object_like_macro(ctx.derived(), macro_def.macroname, macro_def.macrodefinition, curr_token)) { // do not expand this macro, just copy the whole sequence expanded.push_back(curr_token); ++first; // skip macro name return false; // no further preprocessing required } #endif bool found = false; impl::find_concat_operator concat_tag(found); std::remove_copy_if(macro_def.macrodefinition.begin(), macro_def.macrodefinition.end(), std::inserter(replacement_list, replacement_list.end()), concat_tag); // handle concatenation operators if (found && !concat_tokensequence(replacement_list)) return false; ++first; // skip macro name } } // rescan the replacement list ContainerT expanded_list; #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 ctx.get_hooks().expanded_macro(replacement_list); #else ctx.get_hooks().expanded_macro(ctx.derived(), replacement_list); #endif rescan_replacement_list(curr_token, macro_def, replacement_list, expanded_list, expand_operator_defined, first, last); #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 ctx.get_hooks().rescanned_macro(expanded_list); #else ctx.get_hooks().rescanned_macro(ctx.derived(), expanded_list); #endif expanded.splice(expanded.end(), expanded_list); return true; // rescan is required } /////////////////////////////////////////////////////////////////////////////// // // If the token under inspection points to a certain predefined macro it will // be expanded, otherwise false is returned. // (only __FILE__, __LINE__ and __INCLUDE_LEVEL__ macros are expanded here) // /////////////////////////////////////////////////////////////////////////////// template template inline bool macromap::expand_predefined_macro(token_type const &curr_token, ContainerT &expanded) { using namespace boost::wave; string_type const &value = curr_token.get_value(); if (value.size() < 8 || '_' != value[0] || '_' != value[1]) return false; // quick check failed if (value == "__LINE__") { // expand the __LINE__ macro char buffer[22]; // 21 bytes holds all NUL-terminated unsigned 64-bit numbers using namespace std; // for some systems sprintf is in namespace std sprintf(buffer, "%d", main_pos.get_line()); expanded.push_back(token_type(T_INTLIT, buffer, curr_token.get_position())); return true; } else if (value == "__FILE__") { // expand the __FILE__ macro namespace fs = boost::filesystem; std::string file("\""); fs::path filename(wave::util::create_path(main_pos.get_file().c_str())); using boost::wave::util::impl::escape_lit; file += escape_lit(wave::util::native_file_string(filename)) + "\""; expanded.push_back(token_type(T_STRINGLIT, file.c_str(), curr_token.get_position())); return true; } else if (value == "__INCLUDE_LEVEL__") { // expand the __INCLUDE_LEVEL__ macro char buffer[22]; // 21 bytes holds all NUL-terminated unsigned 64-bit numbers using namespace std; // for some systems sprintf is in namespace std sprintf(buffer, "%d", (int)ctx.get_iteration_depth()); expanded.push_back(token_type(T_INTLIT, buffer, curr_token.get_position())); return true; } return false; // no predefined token } /////////////////////////////////////////////////////////////////////////////// // // resolve_defined(): resolve the operator defined() and replace it with the // correct T_INTLIT token // /////////////////////////////////////////////////////////////////////////////// template template inline typename ContextT::token_type const & macromap::resolve_defined(IteratorT &first, IteratorT const &last, ContainerT &pending) { using namespace boost::wave; using namespace boost::wave::grammars; ContainerT result; IteratorT start = first; boost::spirit::classic::parse_info hit = defined_grammar_gen:: parse_operator_defined(start, last, result); if (!hit.hit) { string_type msg ("defined(): "); msg = msg + util::impl::as_string(first, last); BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, ill_formed_expression, msg.c_str(), main_pos); // insert a dummy token pending.push_back(token_type(T_INTLIT, "0", main_pos)); } else { impl::assign_iterator::do_(first, hit.stop); // insert a token, which reflects the outcome pending.push_back(token_type(T_INTLIT, is_defined(result.begin(), result.end()) ? "1" : "0", main_pos)); } on_exit::pop_front pop_front_token(pending); return act_token = pending.front(); } /////////////////////////////////////////////////////////////////////////////// // // resolve_operator_pragma(): resolve the operator _Pragma() and dispatch to // the associated action // // This function returns true, if the pragma was correctly interpreted. // The iterator 'first' is positioned behind the closing ')'. // This function returns false, if the _Pragma was not known, the // preprocessed token sequence is pushed back to the 'pending' sequence. // /////////////////////////////////////////////////////////////////////////////// template template inline bool macromap::resolve_operator_pragma(IteratorT &first, IteratorT const &last, ContainerT &pending, bool& seen_newline) { // isolate the parameter of the operator _Pragma token_type pragma_token = *first; if (!impl::skip_to_token(first, last, T_LEFTPAREN, seen_newline)) { // illformed operator _Pragma BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, ill_formed_expression, "operator _Pragma()", pragma_token.get_position()); return false; } std::vector arguments; #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 typename std::vector::size_type count_args = collect_arguments (pragma_token, arguments, first, last, 1, seen_newline); #else IteratorT endparen = first; typename std::vector::size_type count_args = collect_arguments (pragma_token, arguments, first, endparen, last, 1, seen_newline); #endif // verify the parameter count if (pragma_token.get_position().get_file().empty()) pragma_token.set_position(act_token.get_position()); if (count_args < 1 || arguments.size() < 1) { // too few macro arguments BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, too_few_macroarguments, pragma_token.get_value().c_str(), pragma_token.get_position()); return false; } if (count_args > 1 || arguments.size() > 1) { // too many macro arguments BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, too_many_macroarguments, pragma_token.get_value().c_str(), pragma_token.get_position()); return false; } // preprocess the pragma token body typedef typename std::vector::value_type::iterator argument_iterator_type; ContainerT expanded; argument_iterator_type begin_it = arguments[0].begin(); argument_iterator_type end_it = arguments[0].end(); expand_whole_tokensequence(expanded, begin_it, end_it, false); // un-escape the parameter of the operator _Pragma typedef typename token_type::string_type string_type; string_type pragma_cmd; typename ContainerT::const_iterator end_exp = expanded.end(); for (typename ContainerT::const_iterator it_exp = expanded.begin(); it_exp != end_exp; ++it_exp) { if (T_EOF == token_id(*it_exp)) break; if (IS_CATEGORY(*it_exp, WhiteSpaceTokenType)) continue; if (T_STRINGLIT != token_id(*it_exp)) { // ill formed operator _Pragma BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, ill_formed_pragma_option, "_Pragma", pragma_token.get_position()); return false; } if (pragma_cmd.size() > 0) { // there should be exactly one string literal (string literals are to // be concatenated at translation phase 6, but _Pragma operators are // to be executed at translation phase 4) BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, ill_formed_pragma_option, "_Pragma", pragma_token.get_position()); return false; } // remove the '\"' and concat all given string literal-values string_type token_str = (*it_exp).get_value(); pragma_cmd += token_str.substr(1, token_str.size() - 2); } string_type pragma_cmd_unesc = impl::unescape_lit(pragma_cmd); // tokenize the pragma body typedef typename ContextT::lexer_type lexer_type; ContainerT pragma; std::string pragma_cmd_str(pragma_cmd_unesc.c_str()); lexer_type it = lexer_type(pragma_cmd_str.begin(), pragma_cmd_str.end(), pragma_token.get_position(), ctx.get_language()); lexer_type end = lexer_type(); for (/**/; it != end; ++it) pragma.push_back(*it); // analyze the preprocessed token sequence and eventually dispatch to the // associated action if (interpret_pragma(ctx, pragma_token, pragma.begin(), pragma.end(), pending)) { return true; // successfully recognized a wave specific pragma } // unknown pragma token sequence, push it back and return to the caller pending.push_front(token_type(T_SPACE, " ", pragma_token.get_position())); pending.push_front(token_type(T_RIGHTPAREN, ")", pragma_token.get_position())); pending.push_front(token_type(T_STRINGLIT, string_type("\"") + pragma_cmd + "\"", pragma_token.get_position())); pending.push_front(token_type(T_LEFTPAREN, "(", pragma_token.get_position())); pending.push_front(pragma_token); return false; } /////////////////////////////////////////////////////////////////////////////// // // Test, whether the result of a concat operator is well formed or not. // // This is done by re-scanning (re-tokenizing) the resulting token sequence, // which should give back exactly one token. // /////////////////////////////////////////////////////////////////////////////// template template inline bool macromap::is_valid_concat(string_type new_value, position_type const &pos, ContainerT &rescanned) { // re-tokenize the newly generated string typedef typename ContextT::lexer_type lexer_type; std::string value_to_test(new_value.c_str()); boost::wave::language_support lang = boost::wave::enable_prefer_pp_numbers(ctx.get_language()); lang = boost::wave::enable_single_line(lang); lexer_type it = lexer_type(value_to_test.begin(), value_to_test.end(), pos, lang); lexer_type end = lexer_type(); for (/**/; it != end && T_EOF != token_id(*it); ++it) { // as of Wave V2.0.7 pasting of tokens is valid only if the resulting // tokens are pp_tokens (as mandated by C++0x) if (!is_pp_token(*it)) return false; rescanned.push_back(*it); } #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 if (boost::wave::need_variadics(ctx.get_language())) return true; // in variadics mode token pasting is well defined #endif // test if the newly generated token sequence contains more than 1 token return 1 == rescanned.size(); } /////////////////////////////////////////////////////////////////////////////// // // Handle all occurrences of the concatenation operator '##' inside the given // token sequence. // /////////////////////////////////////////////////////////////////////////////// template inline void report_invalid_concatenation(Context& ctx, typename Context::token_type const& prev, typename Context::token_type const& next, typename Context::position_type const& main_pos) { typename Context::string_type error_string("\""); error_string += prev.get_value(); error_string += "\" and \""; error_string += next.get_value(); error_string += "\""; BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, invalid_concat, error_string.c_str(), main_pos); } template template inline bool macromap::concat_tokensequence(ContainerT &expanded) { using namespace boost::wave; typedef typename ContainerT::iterator iterator_type; iterator_type end = expanded.end(); iterator_type prev = end; for (iterator_type it = expanded.begin(); it != end; /**/) { if (T_POUND_POUND == BASE_TOKEN(token_id(*it))) { iterator_type next = it; ++next; if (prev == end || next == end) { // error, '##' should be in between two tokens BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, ill_formed_operator, "concat ('##')", main_pos); return false; } // replace prev##next with the concatenated value, skip whitespace // before and after the '##' operator while (IS_CATEGORY(*next, WhiteSpaceTokenType)) { ++next; if (next == end) { // error, '##' should be in between two tokens BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, ill_formed_operator, "concat ('##')", main_pos); return false; } } #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 if (boost::wave::need_variadics(ctx.get_language())) { if (T_PLACEMARKER == token_id(*next)) { // remove the '##' and the next tokens from the sequence iterator_type first_to_delete = prev; expanded.erase(++first_to_delete, ++next); it = next; continue; } else if (T_PLACEMARKER == token_id(*prev)) { // remove the '##' and the next tokens from the sequence iterator_type first_to_delete = prev; *prev = *next; expanded.erase(++first_to_delete, ++next); it = next; continue; } } #endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 // test if the concat operator has to concatenate two unrelated // tokens i.e. the result yields more then one token string_type concat_result; ContainerT rescanned; concat_result = ((*prev).get_value() + (*next).get_value()); // analyze the validity of the concatenation result if (!is_valid_concat(concat_result, (*prev).get_position(), rescanned) && !IS_CATEGORY(*prev, WhiteSpaceTokenType) && !IS_CATEGORY(*next, WhiteSpaceTokenType)) { report_invalid_concatenation(ctx, *prev, *next, main_pos); return false; } #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 if (boost::wave::need_variadics(ctx.get_language())) { // remove the prev, '##' and the next tokens from the sequence expanded.erase(prev, ++next); // remove not needed tokens // some stl implementations clear() the container if we erased all // the elements, which orphans all iterators. we re-initialize these // here if (expanded.empty()) end = next = expanded.end(); // replace the old token (pointed to by *prev) with the re-tokenized // sequence expanded.splice(next, rescanned); // the last token of the inserted sequence is the new previous prev = next; if (next != expanded.end()) --prev; } else #endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 { // we leave the token_id unchanged, but unmark the token as // disabled, if appropriate (*prev).set_value(concat_result); if (T_NONREPLACABLE_IDENTIFIER == token_id(*prev)) (*prev).set_token_id(T_IDENTIFIER); // remove the '##' and the next tokens from the sequence iterator_type first_to_delete = prev; expanded.erase(++first_to_delete, ++next); } it = next; continue; } // save last non-whitespace token position if (!IS_CATEGORY(*it, WhiteSpaceTokenType)) prev = it; ++it; // next token, please } return true; } /////////////////////////////////////////////////////////////////////////////// // // predefine_macro(): predefine a single macro // /////////////////////////////////////////////////////////////////////////////// template inline void macromap::predefine_macro(defined_macros_type *scope, string_type const &name, token_type const &t) { definition_container_type macrodefinition; std::vector param; macrodefinition.push_back(t); add_macro(token_type(T_IDENTIFIER, name, t.get_position()), false, param, macrodefinition, true, scope); } /////////////////////////////////////////////////////////////////////////////// // // init_predefined_macros(): init the predefined macros // /////////////////////////////////////////////////////////////////////////////// template inline void macromap::init_predefined_macros(char const *fname, defined_macros_type *scope, bool at_global_scope) { // if no scope is given, use the current one defined_macros_type *current_scope = scope ? scope : current_macros; // first, add the static macros position_type pos(""); #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 if (boost::wave::need_c99(ctx.get_language())) { // define C99 specifics for (int i = 0; 0 != predef.static_data_c99(i).name; ++i) { predefined_macros::static_macros const& m = predef.static_data_c99(i); predefine_macro(current_scope, m.name, token_type(m.token_id, m.value, pos)); } } else #endif { // define C++ specifics for (int i = 0; 0 != predef.static_data_cpp(i).name; ++i) { predefined_macros::static_macros const& m = predef.static_data_cpp(i); predefine_macro(current_scope, m.name, token_type(m.token_id, m.value, pos)); } #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 // define __WAVE_HAS_VARIADICS__, if appropriate if (boost::wave::need_variadics(ctx.get_language())) { predefine_macro(current_scope, "__WAVE_HAS_VARIADICS__", token_type(T_INTLIT, "1", pos)); } #endif } // predefine the __BASE_FILE__ macro which contains the main file name namespace fs = boost::filesystem; if (string_type(fname) != "") { fs::path filename(create_path(fname)); using boost::wave::util::impl::escape_lit; predefine_macro(current_scope, "__BASE_FILE__", token_type(T_STRINGLIT, string_type("\"") + escape_lit(native_file_string(filename)).c_str() + "\"", pos)); base_name = fname; } else if (!base_name.empty()) { fs::path filename(create_path(base_name.c_str())); using boost::wave::util::impl::escape_lit; predefine_macro(current_scope, "__BASE_FILE__", token_type(T_STRINGLIT, string_type("\"") + escape_lit(native_file_string(filename)).c_str() + "\"", pos)); } // now add the dynamic macros for (int j = 0; 0 != predef.dynamic_data(j).name; ++j) { predefined_macros::dynamic_macros const& m = predef.dynamic_data(j); predefine_macro(current_scope, m.name, token_type(m.token_id, (predef.* m.generator)(), pos)); } } /////////////////////////////////////////////////////////////////////////////// // // reset_macromap(): initialize the internal macro symbol namespace // /////////////////////////////////////////////////////////////////////////////// template inline void macromap::reset_macromap() { current_macros->clear(); predef.reset(); act_token = token_type(); } /////////////////////////////////////////////////////////////////////////////// }}} // namespace boost::wave::util #if BOOST_WAVE_SERIALIZATION != 0 namespace boost { namespace serialization { template struct version > { typedef boost::wave::util::macromap target_type; typedef mpl::int_ type; typedef mpl::integral_c_tag tag; BOOST_STATIC_CONSTANT(unsigned int, value = version::type::value); }; }} // namespace boost::serialization #endif // the suffix header occurs after all of the code #ifdef BOOST_HAS_ABI_HEADERS #include BOOST_ABI_SUFFIX #endif #endif // !defined(CPP_MACROMAP_HPP_CB8F51B0_A3F0_411C_AEF4_6FF631B8B414_INCLUDED)