| /*============================================================================= |
| 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 <cstdlib> |
| #include <cstdio> |
| #include <ctime> |
| |
| #include <list> |
| #include <map> |
| #include <set> |
| #include <vector> |
| #include <iterator> |
| #include <algorithm> |
| |
| #include <boost/assert.hpp> |
| #include <boost/wave/wave_config.hpp> |
| #if BOOST_WAVE_SERIALIZATION != 0 |
| #include <boost/serialization/serialization.hpp> |
| #include <boost/serialization/shared_ptr.hpp> |
| #endif |
| |
| #include <boost/filesystem/path.hpp> |
| |
| #include <boost/wave/util/time_conversion_helper.hpp> |
| #include <boost/wave/util/unput_queue_iterator.hpp> |
| #include <boost/wave/util/macro_helpers.hpp> |
| #include <boost/wave/util/macro_definition.hpp> |
| #include <boost/wave/util/symbol_table.hpp> |
| #include <boost/wave/util/cpp_macromap_utils.hpp> |
| #include <boost/wave/util/cpp_macromap_predef.hpp> |
| #include <boost/wave/util/filesystem_compatibility.hpp> |
| #include <boost/wave/grammars/cpp_defined_grammar_gen.hpp> |
| |
| #include <boost/wave/wave_version.hpp> |
| #include <boost/wave/cpp_exceptions.hpp> |
| #include <boost/wave/language_support.hpp> |
| |
| // this must occur after all of the includes and before any code appears |
| #ifdef BOOST_HAS_ABI_HEADERS |
| #include BOOST_ABI_PREFIX |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| namespace boost { namespace wave { namespace util { |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // macromap |
| // |
| // This class holds all currently defined macros and on demand expands |
| // those macro definitions |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| template <typename ContextT> |
| class macromap { |
| |
| typedef macromap<ContextT> 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<token_type> parameter_container_type; |
| |
| typedef macro_definition<token_type, definition_container_type> |
| macro_definition_type; |
| typedef symbol_table<string_type, macro_definition_type> |
| 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 <typename IteratorT> |
| 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 <typename IteratorT, typename ContainerT> |
| 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 <typename IteratorT, typename ContainerT> |
| 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 = "<Unknown>", |
| 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 <typename IteratorT, typename ContainerT> |
| token_type const &expand_tokensequence_worker(ContainerT &pending, |
| unput_queue_iterator<IteratorT, token_type, ContainerT> &first, |
| unput_queue_iterator<IteratorT, token_type, ContainerT> 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 IteratorT, typename ContainerT, typename SizeT> |
| typename std::vector<ContainerT>::size_type collect_arguments ( |
| token_type const curr_token, std::vector<ContainerT> &arguments, |
| IteratorT &next, IteratorT const &end, SizeT const ¶meter_count, |
| bool& seen_newline); |
| #else |
| template <typename IteratorT, typename ContainerT, typename SizeT> |
| typename std::vector<ContainerT>::size_type collect_arguments ( |
| token_type const curr_token, std::vector<ContainerT> &arguments, |
| IteratorT &next, IteratorT &endparen, IteratorT const &end, |
| SizeT const ¶meter_count, bool& seen_newline); |
| #endif |
| |
| // Expand a single macro name |
| template <typename IteratorT, typename ContainerT> |
| 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 <typename ContainerT> |
| bool expand_predefined_macro(token_type const &curr_token, |
| ContainerT &expanded); |
| |
| // Expand a single macro argument |
| template <typename ContainerT> |
| void expand_argument (typename std::vector<ContainerT>::size_type arg, |
| std::vector<ContainerT> &arguments, |
| std::vector<ContainerT> &expanded_args, bool expand_operator_defined, |
| std::vector<bool> &has_expanded_args); |
| |
| // Expand the replacement list (replaces parameters with arguments) |
| template <typename ContainerT> |
| void expand_replacement_list( |
| macro_definition_type const ¯odefinition, |
| std::vector<ContainerT> &arguments, |
| bool expand_operator_defined, ContainerT &expanded); |
| |
| // Rescans the replacement list for macro expansion |
| template <typename IteratorT, typename ContainerT> |
| 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 <typename IteratorT, typename ContainerT> |
| token_type const &resolve_defined(IteratorT &first, IteratorT const &last, |
| ContainerT &expanded); |
| |
| // Resolve operator _Pragma or the #pragma directive |
| template <typename IteratorT, typename ContainerT> |
| bool resolve_operator_pragma(IteratorT &first, |
| IteratorT const &last, ContainerT &expanded, bool& seen_newline); |
| |
| // Handle the concatenation operator '##' |
| template <typename ContainerT> |
| bool concat_tokensequence(ContainerT &expanded); |
| |
| template <typename ContainerT> |
| 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<typename Archive> |
| void save(Archive &ar, const unsigned int version) const |
| { |
| using namespace boost::serialization; |
| ar & make_nvp("defined_macros", defined_macros); |
| } |
| template<typename Archive> |
| 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_type> 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 <typename ContextT> |
| inline bool |
| macromap<ContextT>::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<typename token_type::string_type> names; |
| |
| typedef typename parameter_container_type::iterator |
| parameter_iterator_type; |
| typedef typename std::set<typename token_type::string_type>::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<typename defined_macros_type::iterator, bool> 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 <typename ContextT> |
| inline bool |
| macromap<ContextT>::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 <typename ContextT> |
| template <typename IteratorT> |
| inline bool |
| macromap<ContextT>::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 <typename ContextT> |
| inline bool |
| macromap<ContextT>::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 <typename ContextT> |
| inline bool |
| macromap<ContextT>::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 <typename ContextT> |
| inline bool |
| macromap<ContextT>::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 <typename ContextT> |
| template <typename IteratorT, typename ContainerT> |
| inline typename ContextT::token_type const & |
| macromap<ContextT>::expand_tokensequence(IteratorT &first, |
| IteratorT const &last, ContainerT &pending, ContainerT &expanded, |
| bool& seen_newline, bool expand_operator_defined) |
| { |
| typedef impl::gen_unput_queue_iterator<IteratorT, token_type, ContainerT> |
| 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<IteratorT, iterator_type> 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 <typename ContextT> |
| template <typename IteratorT, typename ContainerT> |
| inline typename ContextT::token_type const & |
| macromap<ContextT>::expand_tokensequence_worker( |
| ContainerT &pending, |
| unput_queue_iterator<IteratorT, token_type, ContainerT> &first, |
| unput_queue_iterator<IteratorT, token_type, ContainerT> 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<definition_container_type> 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<IteratorT, token_type, ContainerT> 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<definition_container_type> 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<definition_container_type> 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 <typename ContextT> |
| template <typename IteratorT, typename ContainerT, typename SizeT> |
| inline typename std::vector<ContainerT>::size_type |
| macromap<ContextT>::collect_arguments (token_type const curr_token, |
| std::vector<ContainerT> &arguments, IteratorT &next, |
| IteratorT const &end, SizeT const ¶meter_count, bool& seen_newline) |
| #else |
| template <typename ContextT> |
| template <typename IteratorT, typename ContainerT, typename SizeT> |
| inline typename std::vector<ContainerT>::size_type |
| macromap<ContextT>::collect_arguments (token_type const curr_token, |
| std::vector<ContainerT> &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<ContainerT>::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<unsigned int>(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 <typename ContextT> |
| template <typename IteratorT, typename ContainerT> |
| inline void |
| macromap<ContextT>::expand_whole_tokensequence(ContainerT &expanded, |
| IteratorT &first, IteratorT const &last, |
| bool expand_operator_defined) |
| { |
| typedef impl::gen_unput_queue_iterator<IteratorT, token_type, ContainerT> |
| 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<IteratorT, iterator_type> 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 <typename ContextT> |
| template <typename ContainerT> |
| inline void |
| macromap<ContextT>::expand_argument ( |
| typename std::vector<ContainerT>::size_type arg, |
| std::vector<ContainerT> &arguments, std::vector<ContainerT> &expanded_args, |
| bool expand_operator_defined, std::vector<bool> &has_expanded_args) |
| { |
| if (!has_expanded_args[arg]) { |
| // expand the argument only once |
| typedef typename std::vector<ContainerT>::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 <typename ContextT> |
| template <typename ContainerT> |
| inline void |
| macromap<ContextT>::expand_replacement_list( |
| macro_definition_type const ¯odef, |
| std::vector<ContainerT> &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<ContainerT> expanded_args(arguments.size()); |
| std::vector<bool> 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<macro_definition_iter_t> |
| ::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<ContainerT>::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 <typename ContextT> |
| template <typename IteratorT, typename ContainerT> |
| inline void |
| macromap<ContextT>::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<bool> 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 <typename ContextT> |
| template <typename IteratorT, typename ContainerT> |
| inline bool |
| macromap<ContextT>::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<IteratorT>::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<ContainerT> arguments; |
| #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 |
| typename std::vector<ContainerT>::size_type count_args = |
| collect_arguments (curr_token, arguments, first, last, |
| macro_def.macroparameters.size(), seen_newline); |
| #else |
| typename std::vector<ContainerT>::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 <typename ContextT> |
| template <typename ContainerT> |
| inline bool |
| macromap<ContextT>::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 <typename ContextT> |
| template <typename IteratorT, typename ContainerT> |
| inline typename ContextT::token_type const & |
| macromap<ContextT>::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<IteratorT> hit = |
| defined_grammar_gen<typename ContextT::lexer_type>:: |
| parse_operator_defined(start, last, result); |
| |
| if (!hit.hit) { |
| string_type msg ("defined(): "); |
| msg = msg + util::impl::as_string<string_type>(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<IteratorT>::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<definition_container_type> 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 <typename ContextT> |
| template <typename IteratorT, typename ContainerT> |
| inline bool |
| macromap<ContextT>::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<ContainerT> arguments; |
| #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 |
| typename std::vector<ContainerT>::size_type count_args = |
| collect_arguments (pragma_token, arguments, first, last, 1, seen_newline); |
| #else |
| IteratorT endparen = first; |
| typename std::vector<ContainerT>::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<ContainerT>::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 <typename ContextT> |
| template <typename ContainerT> |
| inline bool |
| macromap<ContextT>::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 <typename Context> |
| 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 <typename ContextT> |
| template <typename ContainerT> |
| inline bool |
| macromap<ContextT>::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 <typename ContextT> |
| inline void |
| macromap<ContextT>::predefine_macro(defined_macros_type *scope, |
| string_type const &name, token_type const &t) |
| { |
| definition_container_type macrodefinition; |
| std::vector<token_type> 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 <typename ContextT> |
| inline void |
| macromap<ContextT>::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("<built-in>"); |
| |
| #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) != "<Unknown>") { |
| 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 <typename ContextT> |
| inline void |
| macromap<ContextT>::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<typename ContextT> |
| struct version<boost::wave::util::macromap<ContextT> > |
| { |
| typedef boost::wave::util::macromap<ContextT> target_type; |
| typedef mpl::int_<target_type::version> 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) |