1947 lines
74 KiB
C++
1947 lines
74 KiB
C++
/*=============================================================================
|
|
Boost.Wave: A Standard compliant C++ preprocessor library
|
|
|
|
Macro expansion engine
|
|
|
|
http://www.boost.org/
|
|
|
|
Copyright (c) 2001-2012 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; }
|
|
position_type const& get_main_pos() const { 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(ctx, 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()));
|
|
// do not expand macro, just copy macro name and parenthesis
|
|
expanded.push_back(curr_token);
|
|
expanded.push_back(*seqstart);
|
|
first = ++seqstart;
|
|
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);
|
|
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, "%ld", 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(ctx, 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++11)
|
|
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
|
|
{
|
|
#if BOOST_WAVE_SUPPORT_CPP0X != 0
|
|
if (boost::wave::need_cpp0x(ctx.get_language())) {
|
|
// define C++11 specifics
|
|
for (int i = 0; 0 != predef.static_data_cpp0x(i).name; ++i) {
|
|
predefined_macros::static_macros const& m = predef.static_data_cpp0x(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)
|