diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-10-30 12:57:26 -0700 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-10-30 12:57:26 -0700 |
commit | 1a78a62555be32868418fe52f8e330c9d0f95d5a (patch) | |
tree | d3765a80e7d3b9640ec2e930743630cd6b9fce2b /boost/wave/util | |
download | boost-1a78a62555be32868418fe52f8e330c9d0f95d5a.tar.gz boost-1a78a62555be32868418fe52f8e330c9d0f95d5a.tar.bz2 boost-1a78a62555be32868418fe52f8e330c9d0f95d5a.zip |
Imported Upstream version 1.49.0upstream/1.49.0
Diffstat (limited to 'boost/wave/util')
20 files changed, 11322 insertions, 0 deletions
diff --git a/boost/wave/util/cpp_ifblock.hpp b/boost/wave/util/cpp_ifblock.hpp new file mode 100644 index 0000000000..f89f9eaf66 --- /dev/null +++ b/boost/wave/util/cpp_ifblock.hpp @@ -0,0 +1,161 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(CPP_IFBLOCK_HPP_D4676B36_00C5_41F4_BC9F_9CBBAE3B8006_INCLUDED) +#define CPP_IFBLOCK_HPP_D4676B36_00C5_41F4_BC9F_9CBBAE3B8006_INCLUDED + +#include <stack> +#include <boost/wave/wave_config.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 { + +/////////////////////////////////////////////////////////////////////////////// +// the class if_blocks handles recursive conditional compilation contexts +class if_block +{ +public: + if_block() : + status(true), some_part_status(true), + enclosing_status(true), is_in_else(false) + { + } + if_block(bool status_, bool enclosing_status_) : + status(status_), + some_part_status(status_), + enclosing_status(enclosing_status_), + is_in_else(false) + { + } + + void set_status(bool status_) + { + status = status_; + if (status_) + some_part_status = true; + } + bool get_status() const { return status; } + bool get_some_part_status() const { return some_part_status; } + bool get_enclosing_status() const { return enclosing_status; } + bool get_in_else() const { return is_in_else; } + void set_in_else() { is_in_else = true; } + +private: + bool status; // Current block is true + bool some_part_status; // One of the preceding or current #if/#elif was true + bool enclosing_status; // Enclosing #if block is true + bool is_in_else; // Inside the #else part +}; + +/////////////////////////////////////////////////////////////////////////////// +// stack of conditional compilation contexts +class if_block_stack +: private std::stack<if_block> +{ +public: + typedef std::stack<if_block>::size_type size_type; + + void enter_if_block(bool new_status) + { + // If enclosing block is false, then this block is also false + bool enclosing_status = get_status(); + this->push (value_type (new_status && enclosing_status, enclosing_status)); + } + bool enter_elif_block(bool new_status) + { + if (!is_inside_ifpart()) + return false; // #elif without matching #if + + if (get_enclosing_status()) { + if (get_status()) { + // entered a (false) #elif block from a true block + this->top().set_status(false); + } + else if (new_status && !this->top().get_some_part_status()) { + // Entered true #elif block and no previous block was true + this->top().set_status(new_status); + } + } + return true; + } + bool enter_else_block() + { + if (!is_inside_ifpart()) + return false; // #else without matching #if + + if (get_enclosing_status()) { + if (!this->top().get_some_part_status()) { + // Entered (true) #else block and no previous block was true + this->top().set_status(true); + } + else if (get_status()) { + // Entered (false) #else block from true block + this->top().set_status(false); + } + + // Set else flag + this->top().set_in_else(); + } + return true; + } + bool exit_if_block() + { + if (0 == this->size()) + return false; // #endif without matching #if + + this->pop(); + return true; + } + +// return, whether the top (innermost) condition is true or false + bool get_status() const + { + return 0 == this->size() || this->top().get_status(); + } + bool get_some_part_status() const + { + return 0 == this->size() || this->top().get_some_part_status(); + } + bool get_enclosing_status() const + { + return 0 == this->size() || this->top().get_enclosing_status(); + } + + size_type get_if_block_depth() const { return this->size(); } + +protected: + bool is_inside_ifpart() const + { + return 0 != this->size() && !this->top().get_in_else(); + } + bool is_inside_elsepart() const + { + return 0 != this->size() && this->top().get_in_else(); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(CPP_IFBLOCK_HPP_D4676B36_00C5_41F4_BC9F_9CBBAE3B8006_INCLUDED) diff --git a/boost/wave/util/cpp_include_paths.hpp b/boost/wave/util/cpp_include_paths.hpp new file mode 100644 index 0000000000..29c2bec3c9 --- /dev/null +++ b/boost/wave/util/cpp_include_paths.hpp @@ -0,0 +1,553 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(CPP_INCLUDE_PATHS_HPP_AF620DA4_B3D2_4221_AD91_8A1ABFFB6944_INCLUDED) +#define CPP_INCLUDE_PATHS_HPP_AF620DA4_B3D2_4221_AD91_8A1ABFFB6944_INCLUDED + +#include <string> +#include <list> +#include <utility> + +#include <boost/wave/wave_config.hpp> +#include <boost/wave/util/filesystem_compatibility.hpp> + +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 +#include <boost/multi_index_container.hpp> +#include <boost/multi_index/member.hpp> +#include <boost/multi_index/ordered_index.hpp> +#endif + +#if BOOST_WAVE_SERIALIZATION != 0 +#include <boost/serialization/serialization.hpp> +#include <boost/serialization/utility.hpp> +#include <boost/serialization/collections_save_imp.hpp> +#include <boost/serialization/collections_load_imp.hpp> +#include <boost/serialization/split_free.hpp> +#endif + +#include <boost/filesystem/path.hpp> +#include <boost/filesystem/operations.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 { + +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 +/////////////////////////////////////////////////////////////////////////////// +// Tags for accessing both sides of a bidirectional map +struct from {}; +struct to {}; + +/////////////////////////////////////////////////////////////////////////////// +// The class template bidirectional_map wraps the specification +// of a bidirectional map based on multi_index_container. +template<typename FromType, typename ToType> +struct bidirectional_map +{ + typedef std::pair<FromType, ToType> value_type; + +#if defined(BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS) || \ + (defined(BOOST_MSVC) && \ + ( (BOOST_MSVC < 1300) || (BOOST_MSVC == 1600) )) || \ + (defined(BOOST_INTEL_CXX_VERSION) && \ + (defined(_MSC_VER) && (BOOST_INTEL_CXX_VERSION <= 700))) + + BOOST_STATIC_CONSTANT(unsigned, from_offset = offsetof(value_type, first)); + BOOST_STATIC_CONSTANT(unsigned, to_offset = offsetof(value_type, second)); + + typedef boost::multi_index::multi_index_container< + value_type, + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique< + boost::multi_index::tag<from>, + boost::multi_index::member_offset<value_type, FromType, from_offset> + >, + boost::multi_index::ordered_non_unique< + boost::multi_index::tag<to>, + boost::multi_index::member_offset<value_type, ToType, to_offset> + > + > + > type; + +#else + + typedef boost::multi_index::multi_index_container< + value_type, + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique< + boost::multi_index::tag<from>, + boost::multi_index::member<value_type, FromType, &value_type::first> + >, + boost::multi_index::ordered_non_unique< + boost::multi_index::tag<to>, + boost::multi_index::member<value_type, ToType, &value_type::second> + > + > + > type; + +#endif +}; +#endif // BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + +#if BOOST_WAVE_SERIALIZATION != 0 +struct load_filepos +{ + static unsigned int get_line() { return 0; } + static unsigned int get_column() { return 0; } + static std::string get_file() { return "<loading-state>"; } +}; +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +// include_paths - controlling the include path search order +// +// General notes: +// +// Any directories specified with the 'add_include_path()' function before +// the function 'set_sys_include_delimiter()' is called are searched only +// for the case of '#include "file"' directives, they are not searched for +// '#include <file>' directives. If additional directories are specified +// with the 'add_include_path()' function after a call to the function +// 'set_sys_include_delimiter()', these directories are searched for all +// '#include' directives. +// +// In addition, a call to the function 'set_sys_include_delimiter()' +// inhibits the use of the current directory as the first search directory +// for '#include "file"' directives. Therefore, the current directory is +// searched only if it is requested explicitly with a call to the function +// 'add_include_path(".")'. +// +// Calling both functions, the 'set_sys_include_delimiter()' and +// 'add_include_path(".")' allows you to control precisely which +// directories are searched before the current one and which are searched +// after. +// +/////////////////////////////////////////////////////////////////////////////// +class include_paths +{ +private: + typedef std::list<std::pair<boost::filesystem::path, std::string> > + include_list_type; + typedef include_list_type::value_type include_value_type; + +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + typedef bidirectional_map<std::string, std::string>::type + pragma_once_set_type; +#endif + +public: + include_paths() + : was_sys_include_path(false), + current_dir(initial_path()), + current_rel_dir(initial_path()) + {} + + bool add_include_path(char const *path_, bool is_system = false) + { + return add_include_path(path_, (is_system || was_sys_include_path) ? + system_include_paths : user_include_paths); + } + void set_sys_include_delimiter() { was_sys_include_path = true; } + bool find_include_file (std::string &s, std::string &dir, bool is_system, + char const *current_file) const; + void set_current_directory(char const *path_); + boost::filesystem::path get_current_directory() const + { return current_dir; } + +protected: + bool find_include_file (std::string &s, std::string &dir, + include_list_type const &pathes, char const *) const; + bool add_include_path(char const *path_, include_list_type &pathes_); + +private: + include_list_type user_include_paths; + include_list_type system_include_paths; + bool was_sys_include_path; // saw a set_sys_include_delimiter() + boost::filesystem::path current_dir; + boost::filesystem::path current_rel_dir; + +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 +public: + bool has_pragma_once(std::string const &filename) + { + using boost::multi_index::get; + return get<from>(pragma_once_files).find(filename) != pragma_once_files.end(); + } + bool add_pragma_once_header(std::string const &filename, + std::string const& guard_name) + { + typedef pragma_once_set_type::value_type value_type; + return pragma_once_files.insert(value_type(filename, guard_name)).second; + } + bool remove_pragma_once_header(std::string const& guard_name) + { + typedef pragma_once_set_type::index_iterator<to>::type to_iterator; + typedef std::pair<to_iterator, to_iterator> range_type; + + range_type r = pragma_once_files.get<to>().equal_range(guard_name); + if (r.first != r.second) { + using boost::multi_index::get; + get<to>(pragma_once_files).erase(r.first, r.second); + return true; + } + return false; + } + +private: + pragma_once_set_type pragma_once_files; +#endif + +#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; +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + ar & make_nvp("pragma_once_files", pragma_once_files); +#endif + ar & make_nvp("user_include_paths", user_include_paths); + ar & make_nvp("system_include_paths", system_include_paths); + ar & make_nvp("was_sys_include_path", was_sys_include_path); + } + 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_include_path state version", load_filepos()); + return; + } + +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + ar & make_nvp("pragma_once_files", pragma_once_files); +#endif + // verify that the old include paths match the current ones + include_list_type user_paths, system_paths; + ar & make_nvp("user_include_paths", user_paths); + ar & make_nvp("system_include_paths", system_paths); + + if (user_paths != user_include_paths) + { + BOOST_WAVE_THROW(preprocess_exception, incompatible_config, + "user include paths", load_filepos()); + return; + } + if (system_paths != system_include_paths) + { + BOOST_WAVE_THROW(preprocess_exception, incompatible_config, + "system include paths", load_filepos()); + return; + } + + ar & make_nvp("was_sys_include_path", was_sys_include_path); + } + BOOST_SERIALIZATION_SPLIT_MEMBER() +#endif +}; + +/////////////////////////////////////////////////////////////////////////////// +// Add an include path to one of the search lists (user include path or system +// include path). +inline +bool include_paths::add_include_path ( + char const *path_, include_list_type &pathes_) +{ + namespace fs = boost::filesystem; + if (path_) { + fs::path newpath = util::complete_path(create_path(path_), current_dir); + + if (!fs::exists(newpath) || !fs::is_directory(newpath)) { + // the given path does not form a name of a valid file system directory + // item + return false; + } + + pathes_.push_back (include_value_type(newpath, path_)); + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// Find an include file by traversing the list of include directories +inline +bool include_paths::find_include_file (std::string &s, std::string &dir, + include_list_type const &pathes, char const *current_file) const +{ + namespace fs = boost::filesystem; + typedef include_list_type::const_iterator const_include_list_iter_t; + + const_include_list_iter_t it = pathes.begin(); + const_include_list_iter_t include_paths_end = pathes.end(); + +#if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0 + if (0 != current_file) { + // re-locate the directory of the current file (#include_next handling) + + // #include_next does not distinguish between <file> and "file" + // inclusion, nor does it check that the file you specify has the same + // name as the current file. It simply looks for the file named, starting + // with the directory in the search path after the one where the current + // file was found. + + fs::path file_path (create_path(current_file)); + for (/**/; it != include_paths_end; ++it) { + fs::path currpath (create_path((*it).first.string())); + if (std::equal(currpath.begin(), currpath.end(), file_path.begin())) + { + ++it; // start searching with the next directory + break; + } + } + } +#endif + + for (/**/; it != include_paths_end; ++it) { + fs::path currpath (create_path(s)); + if (!currpath.has_root_directory()) { + currpath = create_path((*it).first.string()); + currpath /= create_path(s); // append filename + } + + if (fs::exists(currpath)) { + fs::path dirpath (create_path(s)); + if (!dirpath.has_root_directory()) { + dirpath = create_path((*it).second); + dirpath /= create_path(s); + } + + dir = dirpath.string(); + s = normalize(currpath).string(); // found the required file + return true; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// Find an include file by searching the user and system includes in the +// correct sequence (as it was configured by the user of the driver program) +inline bool +include_paths::find_include_file (std::string &s, std::string &dir, + bool is_system, char const *current_file) const +{ + namespace fs = boost::filesystem; + +// if not system include (<...>), then search current directory first + if (!is_system) { + if (!was_sys_include_path) { // set_sys_include_delimiter() not called + // first have a look at the current directory + fs::path currpath (create_path(s)); + if (!currpath.has_root_directory()) { + currpath = create_path(current_dir.string()); + currpath /= create_path(s); + } + + if (fs::exists(currpath) && 0 == current_file) { + // if 0 != current_path (#include_next handling) it can't be + // the file in the current directory + fs::path dirpath (create_path(s)); + if (!dirpath.has_root_directory()) { + dirpath = create_path(current_rel_dir.string()); + dirpath /= create_path(s); + } + + dir = dirpath.string(); + s = normalize(currpath).string(); // found in local directory + return true; + } + + // iterate all user include file directories to find the file + if (find_include_file(s, dir, user_include_paths, current_file)) + return true; + + // ... fall through + } + else { + // if set_sys_include_delimiter() was called, then user include files + // are searched in the user search path only + return find_include_file(s, dir, user_include_paths, current_file); + } + + // if nothing found, fall through + // ... + } + +// iterate all system include file directories to find the file + return find_include_file (s, dir, system_include_paths, current_file); +} + +/////////////////////////////////////////////////////////////////////////////// +// Set current directory from a given file name + +inline bool +as_relative_to(boost::filesystem::path const& path, + boost::filesystem::path const& base, boost::filesystem::path& result) +{ + if (path.has_root_path()) { + if (path.root_path() == base.root_path()) + return as_relative_to(path.relative_path(), base.relative_path(), result); + + result = path; // that's our result + } + else { + if (base.has_root_path()) { + // cannot find relative path from a relative path and a rooted base + return false; + } + else { + typedef boost::filesystem::path::const_iterator path_iterator; + path_iterator path_it = path.begin(); + path_iterator base_it = base.begin(); + while (path_it != path.end() && base_it != base.end() ) { + if (*path_it != *base_it) + break; + ++path_it; ++base_it; + } + + for (/**/; base_it != base.end(); ++base_it) + result /= ".."; + + for (/**/; path_it != path.end(); ++path_it) + result /= *path_it; + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +inline +void include_paths::set_current_directory(char const *path_) +{ + namespace fs = boost::filesystem; + + fs::path filepath (create_path(path_)); + fs::path filename = util::complete_path(filepath, current_dir); + if (fs::exists(filename) && fs::is_directory(filename)) { + current_rel_dir.clear(); + if (!as_relative_to(filepath, current_dir, current_rel_dir)) + current_rel_dir = filepath; + current_dir = filename; + } + else { + current_rel_dir.clear(); + if (!as_relative_to(branch_path(filepath), current_dir, current_rel_dir)) + current_rel_dir = branch_path(filepath); + current_dir = branch_path(filename); + } +} + +/////////////////////////////////////////////////////////////////////////////// +}}} // namespace boost::wave::util + +#if BOOST_WAVE_SERIALIZATION != 0 +/////////////////////////////////////////////////////////////////////////////// +namespace boost { namespace serialization { + +/////////////////////////////////////////////////////////////////////////////// +// Serialization support for boost::filesystem::path +template<class Archive> +inline void save (Archive & ar, boost::filesystem::path const& p, + const unsigned int /* file_version */) +{ + using namespace boost::serialization; + std::string path_str(p.native_file_string()); + ar & make_nvp("filepath", path_str); +} + +template<class Archive> +inline void load (Archive & ar, boost::filesystem::path &p, + const unsigned int /* file_version */) +{ + using namespace boost::serialization; + std::string path_str; + ar & make_nvp("filepath", path_str); + p = wave::util::create_path(path_str); +} + +// split non-intrusive serialization function member into separate +// non intrusive save/load member functions +template<class Archive> +inline void serialize (Archive & ar, boost::filesystem::path &p, + const unsigned int file_version) +{ + boost::serialization::split_free(ar, p, file_version); +} + +/////////////////////////////////////////////////////////////////////////////// +// Serialization support for the used multi_index +template<class Archive> +inline void save (Archive & ar, + const typename boost::wave::util::bidirectional_map< + std::string, std::string + >::type &t, + const unsigned int /* file_version */) +{ + boost::serialization::stl::save_collection< + Archive, + typename boost::wave::util::bidirectional_map< + std::string, std::string + >::type + >(ar, t); +} + +template<class Archive> +inline void load (Archive & ar, + typename boost::wave::util::bidirectional_map<std::string, std::string>::type &t, + const unsigned int /* file_version */) +{ + typedef typename boost::wave::util::bidirectional_map< + std::string, std::string + >::type map_type; + boost::serialization::stl::load_collection< + Archive, map_type, + boost::serialization::stl::archive_input_unique<Archive, map_type>, + boost::serialization::stl::no_reserve_imp<map_type> + >(ar, t); +} + +// split non-intrusive serialization function member into separate +// non intrusive save/load member functions +template<class Archive> +inline void serialize (Archive & ar, + typename boost::wave::util::bidirectional_map< + std::string, std::string + >::type &t, + const unsigned int file_version) +{ + boost::serialization::split_free(ar, t, file_version); +} + +/////////////////////////////////////////////////////////////////////////////// +}} // namespace boost::serialization + +BOOST_CLASS_VERSION(boost::wave::util::include_paths, + boost::wave::util::include_paths::version); + +#endif // BOOST_WAVE_SERIALIZATION != 0 + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(CPP_INCLUDE_PATHS_HPP_AF620DA4_B3D2_4221_AD91_8A1ABFFB6944_INCLUDED) diff --git a/boost/wave/util/cpp_iterator.hpp b/boost/wave/util/cpp_iterator.hpp new file mode 100644 index 0000000000..8441b41faa --- /dev/null +++ b/boost/wave/util/cpp_iterator.hpp @@ -0,0 +1,2571 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + Definition of the preprocessor iterator + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(CPP_ITERATOR_HPP_175CA88F_7273_43FA_9039_BCF7459E1F29_INCLUDED) +#define CPP_ITERATOR_HPP_175CA88F_7273_43FA_9039_BCF7459E1F29_INCLUDED + +#include <string> +#include <vector> +#include <list> +#include <cstdlib> +#include <cctype> + +#include <boost/assert.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/filesystem/path.hpp> +#include <boost/filesystem/operations.hpp> +#include <boost/spirit/include/classic_multi_pass.hpp> +#include <boost/spirit/include/classic_parse_tree_utils.hpp> + +#include <boost/wave/wave_config.hpp> +#include <boost/pool/pool_alloc.hpp> + +#include <boost/wave/util/insert_whitespace_detection.hpp> +#include <boost/wave/util/macro_helpers.hpp> +#include <boost/wave/util/cpp_macromap_utils.hpp> +#include <boost/wave/util/interpret_pragma.hpp> +#include <boost/wave/util/transform_iterator.hpp> +#include <boost/wave/util/functor_input.hpp> +#include <boost/wave/util/filesystem_compatibility.hpp> + +#include <boost/wave/grammars/cpp_grammar_gen.hpp> +#include <boost/wave/grammars/cpp_expression_grammar_gen.hpp> +#if BOOST_WAVE_ENABLE_COMMANDLINE_MACROS != 0 +#include <boost/wave/grammars/cpp_predef_macros_gen.hpp> +#endif + +#include <boost/wave/whitespace_handling.hpp> +#include <boost/wave/cpp_iteration_context.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 { + +/////////////////////////////////////////////////////////////////////////////// +// retrieve the macro name from the parse tree +template < + typename ContextT, typename ParseNodeT, typename TokenT, + typename PositionT +> +inline bool +retrieve_macroname(ContextT& ctx, ParseNodeT const &node, + boost::spirit::classic::parser_id id, TokenT ¯oname, PositionT& act_pos, + bool update_position) +{ +ParseNodeT const *name_node = 0; + + using boost::spirit::classic::find_node; + if (!find_node(node, id, &name_node)) + { + // ill formed define statement (unexpected, should not happen) + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, bad_define_statement, + "bad parse tree (unexpected)", act_pos); + return false; + } + +typename ParseNodeT::children_t const &children = name_node->children; + + if (0 == children.size() || + children.front().value.begin() == children.front().value.end()) + { + // ill formed define statement (unexpected, should not happen) + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, bad_define_statement, + "bad parse tree (unexpected)", act_pos); + return false; + } + +// retrieve the macro name + macroname = *children.front().value.begin(); + if (update_position) { + macroname.set_position(act_pos); + act_pos.set_column(act_pos.get_column() + macroname.get_value().size()); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// retrieve the macro parameters or the macro definition from the parse tree +template <typename ParseNodeT, typename ContainerT, typename PositionT> +inline bool +retrieve_macrodefinition( + ParseNodeT const &node, boost::spirit::classic::parser_id id, + ContainerT ¯odefinition, PositionT& act_pos, bool update_position) +{ + using namespace boost::wave; + typedef typename ParseNodeT::const_tree_iterator const_tree_iterator; + +// find macro parameters/macro definition inside the parse tree +std::pair<const_tree_iterator, const_tree_iterator> nodes; + + using boost::spirit::classic::get_node_range; + if (get_node_range(node, id, nodes)) { + // copy all parameters to the supplied container + typename ContainerT::iterator last_nonwhite = macrodefinition.end(); + const_tree_iterator end = nodes.second; + + for (const_tree_iterator cit = nodes.first; cit != end; ++cit) { + if ((*cit).value.begin() != (*cit).value.end()) { + typename ContainerT::iterator inserted = macrodefinition.insert( + macrodefinition.end(), *(*cit).value.begin()); + + if (!IS_CATEGORY(macrodefinition.back(), WhiteSpaceTokenType) && + T_NEWLINE != token_id(macrodefinition.back()) && + T_EOF != token_id(macrodefinition.back())) + { + last_nonwhite = inserted; + } + + if (update_position) { + (*inserted).set_position(act_pos); + act_pos.set_column( + act_pos.get_column() + (*inserted).get_value().size()); + } + } + } + + // trim trailing whitespace (leading whitespace is trimmed by the grammar) + if (last_nonwhite != macrodefinition.end()) { + if (update_position) { + act_pos.set_column((*last_nonwhite).get_position().get_column() + + (*last_nonwhite).get_value().size()); + } + macrodefinition.erase(++last_nonwhite, macrodefinition.end()); + } + return true; + } + return false; +} + +#if BOOST_WAVE_ENABLE_COMMANDLINE_MACROS != 0 +/////////////////////////////////////////////////////////////////////////////// +// add an additional predefined macro given by a string (MACRO(x)=definition) +template <typename ContextT> +bool add_macro_definition(ContextT &ctx, std::string macrostring, + bool is_predefined, boost::wave::language_support language) +{ + typedef typename ContextT::token_type token_type; + typedef typename ContextT::lexer_type lexer_type; + typedef typename token_type::position_type position_type; + typedef boost::wave::grammars::predefined_macros_grammar_gen<lexer_type> + predef_macros_type; + + using namespace boost::wave; + using namespace std; // isspace is in std namespace for some systems + +// skip leading whitespace +std::string::iterator begin = macrostring.begin(); +std::string::iterator end = macrostring.end(); + + while(begin != end && isspace(*begin)) + ++begin; + +// parse the macro definition +position_type act_pos("<command line>"); +boost::spirit::classic::tree_parse_info<lexer_type> hit = + predef_macros_type::parse_predefined_macro( + lexer_type(begin, end, position_type(), language), lexer_type()); + + if (!hit.match || (!hit.full && T_EOF != token_id(*hit.stop))) { + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, bad_macro_definition, + macrostring.c_str(), act_pos); + return false; + } + +// retrieve the macro definition from the parse tree +token_type macroname; +std::vector<token_type> macroparameters; +typename ContextT::token_sequence_type macrodefinition; +bool has_parameters = false; + + if (!boost::wave::util::retrieve_macroname(ctx, *hit.trees.begin(), + BOOST_WAVE_PLAIN_DEFINE_ID, macroname, act_pos, true)) + return false; + has_parameters = boost::wave::util::retrieve_macrodefinition(*hit.trees.begin(), + BOOST_WAVE_MACRO_PARAMETERS_ID, macroparameters, act_pos, true); + boost::wave::util::retrieve_macrodefinition(*hit.trees.begin(), + BOOST_WAVE_MACRO_DEFINITION_ID, macrodefinition, act_pos, true); + +// If no macrodefinition is given, and the macro string does not end with a +// '=', then the macro should be defined with the value '1' + if (0 == macrodefinition.size() && + '=' != macrostring[macrostring.size()-1]) + { + macrodefinition.push_back(token_type(T_INTLIT, "1", act_pos)); + } + +// add the new macro to the macromap + return ctx.add_macro_definition(macroname, has_parameters, macroparameters, + macrodefinition, is_predefined); +} +#endif // BOOST_WAVE_ENABLE_COMMANDLINE_MACROS != 0 + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util + +/////////////////////////////////////////////////////////////////////////////// +// forward declaration +template <typename ContextT> class pp_iterator; + +namespace impl { + +/////////////////////////////////////////////////////////////////////////////// +// +// pp_iterator_functor +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +class pp_iterator_functor { + +public: +// interface to the boost::spirit::classic::iterator_policies::functor_input policy + typedef typename ContextT::token_type result_type; + +// eof token + static result_type const eof; + +private: +// type of a token sequence + typedef typename ContextT::token_sequence_type token_sequence_type; + + typedef typename ContextT::lexer_type lexer_type; + typedef typename result_type::string_type string_type; + typedef typename result_type::position_type position_type; + typedef boost::wave::grammars::cpp_grammar_gen<lexer_type, token_sequence_type> + cpp_grammar_type; + +// iteration context related types (an iteration context represents a current +// position in an included file) + typedef base_iteration_context<ContextT, lexer_type> + base_iteration_context_type; + typedef iteration_context<ContextT, lexer_type> iteration_context_type; + +// parse tree related types + typedef typename cpp_grammar_type::node_factory_type node_factory_type; + typedef boost::spirit::classic::tree_parse_info<lexer_type, node_factory_type> + tree_parse_info_type; + typedef boost::spirit::classic::tree_match<lexer_type, node_factory_type> + parse_tree_match_type; + typedef typename parse_tree_match_type::node_t parse_node_type; // tree_node<node_val_data<> > + typedef typename parse_tree_match_type::parse_node_t parse_node_value_type; // node_val_data<> + typedef typename parse_tree_match_type::container_t parse_tree_type; // parse_node_type::children_t + +public: + template <typename IteratorT> + pp_iterator_functor(ContextT &ctx_, IteratorT const &first_, + IteratorT const &last_, typename ContextT::position_type const &pos_) + : ctx(ctx_), + iter_ctx(new base_iteration_context_type(ctx, + lexer_type(first_, last_, pos_, + boost::wave::enable_prefer_pp_numbers(ctx.get_language())), + lexer_type(), + pos_.get_file().c_str() + )), + seen_newline(true), skipped_newline(false), + must_emit_line_directive(false), act_pos(ctx_.get_main_pos()), + whitespace(boost::wave::need_insert_whitespace(ctx.get_language())) + { + act_pos.set_file(pos_.get_file()); +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + ctx_.set_current_filename(pos_.get_file().c_str()); +#endif + iter_ctx->emitted_lines = (unsigned int)(-1); // force #line directive + } + +// get the next preprocessed token + result_type const &operator()(); + +// get the last recognized token (for error processing etc.) + result_type const ¤t_token() const { return act_token; } + +protected: + friend class pp_iterator<ContextT>; + bool on_include_helper(char const *t, char const *s, bool is_system, + bool include_next); + +protected: + result_type const &get_next_token(); + result_type const &pp_token(); + + template <typename IteratorT> + bool extract_identifier(IteratorT &it); + template <typename IteratorT> + bool ensure_is_last_on_line(IteratorT& it, bool call_hook = true); + template <typename IteratorT> + bool skip_to_eol_with_check(IteratorT &it, bool call_hook = true); + + bool pp_directive(); + template <typename IteratorT> + bool handle_pp_directive(IteratorT &it); + bool dispatch_directive(tree_parse_info_type const &hit, + result_type const& found_directive, + token_sequence_type const& found_eoltokens); + void replace_undefined_identifiers(token_sequence_type &expanded); + + void on_include(string_type const &s, bool is_system, bool include_next); + void on_include(typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end, bool include_next); + + void on_define(parse_node_type const &node); + void on_undefine(lexer_type const &it); + + void on_ifdef(result_type const& found_directive, lexer_type const &it); +// typename parse_tree_type::const_iterator const &end); + void on_ifndef(result_type const& found_directive, lexer_type const& it); +// typename parse_tree_type::const_iterator const &end); + void on_else(); + void on_endif(); + void on_illformed(typename result_type::string_type s); + + void on_line(typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end); + void on_if(result_type const& found_directive, + typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end); + void on_elif(result_type const& found_directive, + typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end); + void on_error(typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end); +#if BOOST_WAVE_SUPPORT_WARNING_DIRECTIVE != 0 + void on_warning(typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end); +#endif + bool on_pragma(typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end); + + bool emit_line_directive(); + bool returned_from_include(); + + bool interpret_pragma(token_sequence_type const &pragma_body, + token_sequence_type &result); + +private: + ContextT &ctx; // context, this iterator is associated with + boost::shared_ptr<base_iteration_context_type> iter_ctx; + + bool seen_newline; // needed for recognizing begin of line + bool skipped_newline; // a newline has been skipped since last one + bool must_emit_line_directive; // must emit a line directive + result_type act_token; // current token + typename result_type::position_type &act_pos; // current fileposition (references the macromap) + + token_sequence_type unput_queue; // tokens to be preprocessed again + token_sequence_type pending_queue; // tokens already preprocessed + + // detect whether to insert additional whitespace in between two adjacent + // tokens, which otherwise would form a different token type, if + // re-tokenized + boost::wave::util::insert_whitespace_detection whitespace; +}; + +/////////////////////////////////////////////////////////////////////////////// +// eof token +template <typename ContextT> +typename pp_iterator_functor<ContextT>::result_type const + pp_iterator_functor<ContextT>::eof; + +/////////////////////////////////////////////////////////////////////////////// +// +// returned_from_include() +// +// Tests if it is necessary to pop the include file context (eof inside +// a file was reached). If yes, it pops this context. Preprocessing will +// continue with the next outer file scope. +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline bool +pp_iterator_functor<ContextT>::returned_from_include() +{ + if (iter_ctx->first == iter_ctx->last && ctx.get_iteration_depth() > 0) { + // call the include policy trace function +#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 + ctx.get_hooks().returning_from_include_file(); +#else + ctx.get_hooks().returning_from_include_file(ctx.derived()); +#endif + + // restore the previous iteration context after finishing the preprocessing + // of the included file + BOOST_WAVE_STRINGTYPE oldfile = iter_ctx->real_filename; + position_type old_pos (act_pos); + + // if this file has include guards handle it as if it had a #pragma once +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + if (need_include_guard_detection(ctx.get_language())) { + std::string guard_name; + if (iter_ctx->first.has_include_guards(guard_name)) + ctx.add_pragma_once_header(ctx.get_current_filename(), guard_name); + } +#endif + iter_ctx = ctx.pop_iteration_context(); + + must_emit_line_directive = true; + iter_ctx->emitted_lines = (unsigned int)(-1); // force #line directive + seen_newline = true; + + // restore current file position + act_pos.set_file(iter_ctx->filename); + act_pos.set_line(iter_ctx->line); + act_pos.set_column(0); + + // restore the actual current file and directory +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + namespace fs = boost::filesystem; + fs::path rfp(wave::util::create_path(iter_ctx->real_filename.c_str())); + std::string real_filename(rfp.string()); + ctx.set_current_filename(real_filename.c_str()); +#endif + ctx.set_current_directory(iter_ctx->real_filename.c_str()); + ctx.set_current_relative_filename(iter_ctx->real_relative_filename.c_str()); + + // ensure the integrity of the #if/#endif stack + // report unbalanced #if/#endif now to make it possible to recover properly + if (iter_ctx->if_block_depth != ctx.get_if_block_depth()) { + using boost::wave::util::impl::escape_lit; + BOOST_WAVE_STRINGTYPE msg(escape_lit(oldfile)); + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, unbalanced_if_endif, + msg.c_str(), old_pos); + } + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// operator()(): get the next preprocessed token +// +// throws a preprocess_exception, if appropriate +// +/////////////////////////////////////////////////////////////////////////////// +namespace impl { + + // It may be necessary to emit a #line directive either + // - when comments need to be preserved: if the current token is not a + // whitespace, except comments + // - when comments are to be skipped: if the current token is not a + // whitespace token. + template <typename ContextT> + bool consider_emitting_line_directive(ContextT const& ctx, token_id id) + { + if (need_preserve_comments(ctx.get_language())) { + if (!IS_CATEGORY(id, EOLTokenType) && !IS_CATEGORY(id, EOFTokenType)) + { + return true; + } + } + if (!IS_CATEGORY(id, WhiteSpaceTokenType) && + !IS_CATEGORY(id, EOLTokenType) && !IS_CATEGORY(id, EOFTokenType)) + { + return true; + } + return false; + } +} + +template <typename ContextT> +inline typename pp_iterator_functor<ContextT>::result_type const & +pp_iterator_functor<ContextT>::operator()() +{ + using namespace boost::wave; + + // make sure the cwd has been initialized + ctx.init_context(); + + // loop over skip able whitespace until something significant is found + bool was_seen_newline = seen_newline; + token_id id = T_UNKNOWN; + + try { // catch lexer exceptions + do { + // get_next_token assigns result to act_token member + if (skipped_newline) + seen_newline = true; + get_next_token(); + + // if comments shouldn't be preserved replace them with newlines + id = token_id(act_token); + if (!need_preserve_comments(ctx.get_language()) && + (T_CPPCOMMENT == id || context_policies::util::ccomment_has_newline(act_token))) + { + act_token.set_token_id(id = T_NEWLINE); + act_token.set_value("\n"); + } + + } while (ctx.get_hooks().may_skip_whitespace(ctx.derived(), act_token, skipped_newline)); + } + catch (boost::wave::cpplexer::lexing_exception const& e) { + // dispatch any lexer exceptions to the context hook function + ctx.get_hooks().throw_exception(ctx.derived(), e); + return act_token; + } + +// if there were skipped any newlines, we must emit a #line directive + if ((must_emit_line_directive || (was_seen_newline && skipped_newline)) && + impl::consider_emitting_line_directive(ctx, id)) + { + // must emit a #line directive + if (need_emit_line_directives(ctx.get_language()) && emit_line_directive()) + { + skipped_newline = false; + ctx.get_hooks().may_skip_whitespace(ctx.derived(), act_token, skipped_newline); // feed ws eater FSM + id = token_id(act_token); + } + } + +// cleanup of certain tokens required + seen_newline = false; + switch (static_cast<unsigned int>(id)) { + case T_NONREPLACABLE_IDENTIFIER: + act_token.set_token_id(id = T_IDENTIFIER); + break; + + case T_GENERATEDNEWLINE: // was generated by emit_line_directive() + act_token.set_token_id(id = T_NEWLINE); + ++iter_ctx->emitted_lines; + seen_newline = true; + break; + + case T_NEWLINE: + case T_CPPCOMMENT: + seen_newline = true; + ++iter_ctx->emitted_lines; + break; + +#if BOOST_WAVE_SUPPORT_CPP0X != 0 + case T_RAWSTRINGLIT: + iter_ctx->emitted_lines += + context_policies::util::rawstring_count_newlines(act_token); + break; +#endif + + case T_CCOMMENT: // will come here only if whitespace is preserved + iter_ctx->emitted_lines += + context_policies::util::ccomment_count_newlines(act_token); + break; + + case T_PP_NUMBER: // re-tokenize the pp-number + { + token_sequence_type rescanned; + + std::string pp_number( + util::to_string<std::string>(act_token.get_value())); + + lexer_type it = lexer_type(pp_number.begin(), + pp_number.end(), act_token.get_position(), + ctx.get_language()); + lexer_type end = lexer_type(); + + for (/**/; it != end && T_EOF != token_id(*it); ++it) + rescanned.push_back(*it); + + pending_queue.splice(pending_queue.begin(), rescanned); + act_token = pending_queue.front(); + id = token_id(act_token); + pending_queue.pop_front(); + } + break; + + case T_EOF: + seen_newline = true; + break; + + default: // make sure whitespace at line begin keeps seen_newline status + if (IS_CATEGORY(id, WhiteSpaceTokenType)) + seen_newline = was_seen_newline; + break; + } + + if (whitespace.must_insert(id, act_token.get_value())) { + // must insert some whitespace into the output stream to avoid adjacent + // tokens, which would form different (and wrong) tokens + whitespace.shift_tokens(T_SPACE); + pending_queue.push_front(act_token); // push this token back + return act_token = result_type(T_SPACE, + typename result_type::string_type(" "), + act_token.get_position()); + } + whitespace.shift_tokens(id); + return ctx.get_hooks().generated_token(ctx.derived(), act_token); +} + +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline typename pp_iterator_functor<ContextT>::result_type const & +pp_iterator_functor<ContextT>::get_next_token() +{ + using namespace boost::wave; + +// if there is something in the unput_queue, then return the next token from +// there (all tokens in the queue are preprocessed already) + if (!pending_queue.empty() || !unput_queue.empty()) + return pp_token(); // return next token + +// test for EOF, if there is a pending input context, pop it back and continue +// parsing with it +bool returned_from_include_file = returned_from_include(); + +// try to generate the next token + if (iter_ctx->first != iter_ctx->last) { + do { + // If there are pending tokens in the queue, we'll have to return + // these. This may happen from a #pragma directive, which got replaced + // by some token sequence. + if (!pending_queue.empty()) { + util::on_exit::pop_front<token_sequence_type> + pop_front_token(pending_queue); + + return act_token = pending_queue.front(); + } + + // adjust the current position (line and column) + bool was_seen_newline = seen_newline || returned_from_include_file; + + // fetch the current token + act_token = *iter_ctx->first; + act_pos = act_token.get_position(); + + // act accordingly on the current token + token_id id = token_id(act_token); + + if (T_EOF == id) { + // returned from an include file, continue with the next token + whitespace.shift_tokens(T_EOF); + ++iter_ctx->first; + + // now make sure this line has a newline + if ((!seen_newline || act_pos.get_column() > 1) && + !need_single_line(ctx.get_language())) + { + // warn, if this file does not end with a newline + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + last_line_not_terminated, "", act_pos); + } + continue; // if this is the main file, the while loop breaks + } + else if (T_NEWLINE == id || T_CPPCOMMENT == id) { + // a newline is to be returned ASAP, a C++ comment too + // (the C++ comment token includes the trailing newline) + seen_newline = true; + ++iter_ctx->first; + + if (!ctx.get_if_block_status()) { + // skip this token because of the disabled #if block + whitespace.shift_tokens(id); // whitespace controller + util::impl::call_skipped_token_hook(ctx, act_token); + continue; + } + return act_token; + } + seen_newline = false; + + if (was_seen_newline && pp_directive()) { + // a pp directive was found +// pending_queue.push_back(result_type(T_NEWLINE, "\n", act_pos)); +// seen_newline = true; +// must_emit_line_directive = true; + + // loop to the next token to analyze + // simply fall through, since the iterator was already adjusted + // correctly + } + else if (ctx.get_if_block_status()) { + // preprocess this token, eat up more, if appropriate, return + // the next preprocessed token + return pp_token(); + } + else { + // compilation condition is false: if the current token is a + // newline, account for it, otherwise discard the actual token and + // try the next one + if (T_NEWLINE == token_id(act_token)) { + seen_newline = true; + must_emit_line_directive = true; + } + + // next token + util::impl::call_skipped_token_hook(ctx, act_token); + ++iter_ctx->first; + } + + } while ((iter_ctx->first != iter_ctx->last) || + (returned_from_include_file = returned_from_include())); + + // overall eof reached + if (ctx.get_if_block_depth() > 0 && !need_single_line(ctx.get_language())) + { + // missing endif directive(s) + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + missing_matching_endif, "", act_pos); + } + } + else { + act_token = eof; // this is the last token + } + +// whitespace.shift_tokens(T_EOF); // whitespace controller + return act_token; // return eof token +} + +/////////////////////////////////////////////////////////////////////////////// +// +// emit_line_directive(): emits a line directive from the act_token data +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline bool +pp_iterator_functor<ContextT>::emit_line_directive() +{ + using namespace boost::wave; + +typename ContextT::position_type pos = act_token.get_position(); + +// if (must_emit_line_directive && +// iter_ctx->emitted_lines+1 == act_pos.get_line() && +// iter_ctx->filename == act_pos.get_file()) +// { +// must_emit_line_directive = false; +// return false; +// } + + if (must_emit_line_directive || + iter_ctx->emitted_lines+1 != act_pos.get_line()) + { + // unput the current token + pending_queue.push_front(act_token); + pos.set_line(act_pos.get_line()); + + if (iter_ctx->emitted_lines+2 == act_pos.get_line()) { + // prefer to output a single newline instead of the #line directive +// whitespace.shift_tokens(T_NEWLINE); + act_token = result_type(T_NEWLINE, "\n", pos); + } + else { + // account for the newline emitted here + act_pos.set_line(act_pos.get_line()-1); + iter_ctx->emitted_lines = act_pos.get_line()-1; + + token_sequence_type pending; + + if (!ctx.get_hooks().emit_line_directive(ctx, pending, act_token)) + { + unsigned int column = 6; + + // the hook did not generate anything, emit default #line + pos.set_column(1); + pending.push_back(result_type(T_PP_LINE, "#line", pos)); + + pos.set_column(column); // account for '#line' + pending.push_back(result_type(T_SPACE, " ", pos)); + + // 21 is the max required size for a 64 bit integer represented as a + // string + char buffer[22]; + + using namespace std; // for some systems sprintf is in namespace std + sprintf (buffer, "%d", pos.get_line()); + + pos.set_column(++column); // account for ' ' + pending.push_back(result_type(T_INTLIT, buffer, pos)); + pos.set_column(column += (unsigned int)strlen(buffer)); // account for <number> + pending.push_back(result_type(T_SPACE, " ", pos)); + pos.set_column(++column); // account for ' ' + + std::string file("\""); + boost::filesystem::path filename( + wave::util::create_path(act_pos.get_file().c_str())); + + using wave::util::impl::escape_lit; + file += escape_lit(wave::util::native_file_string(filename)) + "\""; + + pending.push_back(result_type(T_STRINGLIT, file.c_str(), pos)); + pos.set_column(column += (unsigned int)file.size()); // account for filename + pending.push_back(result_type(T_GENERATEDNEWLINE, "\n", pos)); + } + + // if there is some replacement text, insert it into the pending queue + if (!pending.empty()) { + pending_queue.splice(pending_queue.begin(), pending); + act_token = pending_queue.front(); + pending_queue.pop_front(); + } + } + + must_emit_line_directive = false; // we are now in sync + return true; + } + + must_emit_line_directive = false; // we are now in sync + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// pptoken(): return the next preprocessed token +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline typename pp_iterator_functor<ContextT>::result_type const & +pp_iterator_functor<ContextT>::pp_token() +{ + using namespace boost::wave; + +token_id id = token_id(*iter_ctx->first); + + // eat all T_PLACEHOLDER tokens, eventually slipped through out of the + // macro engine + do { + if (!pending_queue.empty()) { + // if there are pending tokens in the queue, return the first one + act_token = pending_queue.front(); + pending_queue.pop_front(); + act_pos = act_token.get_position(); + } + else if (!unput_queue.empty() + || T_IDENTIFIER == id + || IS_CATEGORY(id, KeywordTokenType) + || IS_EXTCATEGORY(id, OperatorTokenType|AltExtTokenType) + || IS_CATEGORY(id, BoolLiteralTokenType)) + { + // call the lexer, preprocess the required number of tokens, put them + // into the unput queue + act_token = ctx.expand_tokensequence(iter_ctx->first, + iter_ctx->last, pending_queue, unput_queue, skipped_newline); + } + else { + // simply return the next token + act_token = *iter_ctx->first; + ++iter_ctx->first; + } + id = token_id(act_token); + + } while (T_PLACEHOLDER == id); + return act_token; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// pp_directive(): recognize a preprocessor directive +// +/////////////////////////////////////////////////////////////////////////////// +namespace impl { + + // call 'found_directive' preprocessing hook + template <typename ContextT> + bool call_found_directive_hook(ContextT& ctx, + typename ContextT::token_type const& found_directive) + { +#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 + ctx.get_hooks().found_directive(found_directive); +#else + if (ctx.get_hooks().found_directive(ctx.derived(), found_directive)) + return true; // skip this directive and return newline only +#endif + return false; + } + +// // call 'skipped_token' preprocessing hook +// template <typename ContextT> +// void call_skipped_token_hook(ContextT& ctx, +// typename ContextT::token_type const& skipped) +// { +// #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 +// ctx.get_hooks().skipped_token(skipped); +// #else +// ctx.get_hooks().skipped_token(ctx.derived(), skipped); +// #endif +// } + + template <typename ContextT, typename IteratorT> + bool next_token_is_pp_directive(ContextT &ctx, IteratorT &it, IteratorT const &end) + { + using namespace boost::wave; + + token_id id = T_UNKNOWN; + for (/**/; it != end; ++it) { + id = token_id(*it); + if (!IS_CATEGORY(id, WhiteSpaceTokenType)) + break; // skip leading whitespace + if (IS_CATEGORY(id, EOLTokenType) || IS_CATEGORY(id, EOFTokenType)) + break; // do not enter a new line + if (T_CPPCOMMENT == id || + context_policies::util::ccomment_has_newline(*it)) + { + break; + } + + // this token gets skipped + util::impl::call_skipped_token_hook(ctx, *it); + } + BOOST_ASSERT(it == end || id != T_UNKNOWN); + return it != end && IS_CATEGORY(id, PPTokenType); + } + + // verify that there isn't anything significant left on the line + template <typename ContextT, typename IteratorT> + bool pp_is_last_on_line(ContextT &ctx, IteratorT &it, IteratorT const &end, + bool call_hook = true) + { + using namespace boost::wave; + + // this token gets skipped + if (call_hook) + util::impl::call_skipped_token_hook(ctx, *it); + + for (++it; it != end; ++it) { + token_id id = token_id(*it); + + if (T_CPPCOMMENT == id || T_NEWLINE == id || + context_policies::util::ccomment_has_newline(*it)) + { + if (call_hook) + util::impl::call_skipped_token_hook(ctx, *it); + ++it; // skip eol/C/C++ comment + return true; // no more significant tokens on this line + } + + if (!IS_CATEGORY(id, WhiteSpaceTokenType)) + break; + + // this token gets skipped + if (call_hook) + util::impl::call_skipped_token_hook(ctx, *it); + } + return false; + } + + /////////////////////////////////////////////////////////////////////////// + template <typename ContextT, typename IteratorT> + bool skip_to_eol(ContextT &ctx, IteratorT &it, IteratorT const &end, + bool call_hook = true) + { + using namespace boost::wave; + + for (/**/; it != end; ++it) { + token_id id = token_id(*it); + + if (T_CPPCOMMENT == id || T_NEWLINE == id || + context_policies::util::ccomment_has_newline(*it)) + { + // always call hook for eol + util::impl::call_skipped_token_hook(ctx, *it); + ++it; // skip eol/C/C++ comment + return true; // found eol + } + + if (call_hook) + util::impl::call_skipped_token_hook(ctx, *it); + } + return false; + } + + /////////////////////////////////////////////////////////////////////////// + template <typename ContextT, typename ContainerT> + inline void + remove_leading_whitespace(ContextT &ctx, ContainerT& c, bool call_hook = true) + { + typename ContainerT::iterator it = c.begin(); + while (IS_CATEGORY(*it, WhiteSpaceTokenType)) { + typename ContainerT::iterator save = it++; + if (call_hook) + util::impl::call_skipped_token_hook(ctx, *save); + c.erase(save); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +template <typename IteratorT> +inline bool +pp_iterator_functor<ContextT>::extract_identifier(IteratorT &it) +{ + token_id id = util::impl::skip_whitespace(it, iter_ctx->last); + if (T_IDENTIFIER == id || IS_CATEGORY(id, KeywordTokenType) || + IS_EXTCATEGORY(id, OperatorTokenType|AltExtTokenType) || + IS_CATEGORY(id, BoolLiteralTokenType)) + { + IteratorT save = it; + if (impl::pp_is_last_on_line(ctx, save, iter_ctx->last, false)) + return true; + } + + // report the ill formed directive + impl::skip_to_eol(ctx, it, iter_ctx->last); + +string_type str(util::impl::as_string<string_type>(iter_ctx->first, it)); + + seen_newline = true; + iter_ctx->first = it; + on_illformed(str); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +template <typename IteratorT> +inline bool +pp_iterator_functor<ContextT>::ensure_is_last_on_line(IteratorT& it, bool call_hook) +{ + if (!impl::pp_is_last_on_line(ctx, it, iter_ctx->last, call_hook)) + { + // enable error recovery (start over with the next line) + impl::skip_to_eol(ctx, it, iter_ctx->last); + + string_type str(util::impl::as_string<string_type>( + iter_ctx->first, it)); + + seen_newline = true; + iter_ctx->first = it; + + // report an invalid directive + on_illformed(str); + return false; + } + + if (it == iter_ctx->last && !need_single_line(ctx.get_language())) + { + // The line doesn't end with an eol but eof token. + seen_newline = true; // allow to resume after warning + iter_ctx->first = it; + + // Trigger a warning that the last line was not terminated with a + // newline. + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + last_line_not_terminated, "", act_pos); + + return false; + } + return true; +} + +template <typename ContextT> +template <typename IteratorT> +inline bool +pp_iterator_functor<ContextT>::skip_to_eol_with_check(IteratorT &it, bool call_hook) +{ + typename ContextT::string_type value ((*it).get_value()); + if (!impl::skip_to_eol(ctx, it, iter_ctx->last, call_hook) && + !need_single_line(ctx.get_language())) + { + // The line doesn't end with an eol but eof token. + seen_newline = true; // allow to resume after warning + iter_ctx->first = it; + + // Trigger a warning, that the last line was not terminated with a + // newline. + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + last_line_not_terminated, "", act_pos); + return false; + } + +// normal line ending reached, adjust iterator and flag + seen_newline = true; + iter_ctx->first = it; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// handle_pp_directive: handle certain pp_directives +template <typename ContextT> +template <typename IteratorT> +inline bool +pp_iterator_functor<ContextT>::handle_pp_directive(IteratorT &it) +{ + token_id id = token_id(*it); + bool can_exit = true; + bool call_hook_in_skip = true; + if (!ctx.get_if_block_status()) { + if (IS_EXTCATEGORY(*it, PPConditionalTokenType)) { + // simulate the if block hierarchy + switch (static_cast<unsigned int>(id)) { + case T_PP_IFDEF: // #ifdef + case T_PP_IFNDEF: // #ifndef + case T_PP_IF: // #if + ctx.enter_if_block(false); + break; + + case T_PP_ELIF: // #elif + if (!ctx.get_enclosing_if_block_status()) { + if (!ctx.enter_elif_block(false)) { + // #else without matching #if + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + missing_matching_if, "#elif", act_pos); + return true; // do not analyze this directive any further + } + } + else { + can_exit = false; // #elif is not always safe to skip + } + break; + + case T_PP_ELSE: // #else + case T_PP_ENDIF: // #endif + { + // handle this directive + if (T_PP_ELSE == token_id(*it)) + on_else(); + else + on_endif(); + + // make sure, there are no (non-whitespace) tokens left on + // this line + ensure_is_last_on_line(it); + + // we skipped to the end of this line already + seen_newline = true; + iter_ctx->first = it; + } + return true; + + default: // #something else + on_illformed((*it).get_value()); + break; + } + } + else { + util::impl::call_skipped_token_hook(ctx, *it); + ++it; + } + } + else { + // try to handle the simple pp directives without parsing + result_type directive = *it; + bool include_next = false; + switch (static_cast<unsigned int>(id)) { + case T_PP_QHEADER: // #include "..." +#if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0 + case T_PP_QHEADER_NEXT: +#endif + include_next = (T_PP_QHEADER_NEXT == id) ? true : false; + if (!impl::call_found_directive_hook(ctx, *it)) + { + string_type dir((*it).get_value()); + + // make sure, there are no (non-whitespace) tokens left on + // this line + if (ensure_is_last_on_line(it)) + { + seen_newline = true; + iter_ctx->first = it; + on_include (dir, false, include_next); + } + return true; + } + break; + + case T_PP_HHEADER: // #include <...> +#if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0 + case T_PP_HHEADER_NEXT: +#endif + include_next = (T_PP_HHEADER_NEXT == id) ? true : false; + if (!impl::call_found_directive_hook(ctx, *it)) + { + string_type dir((*it).get_value()); + + // make sure, there are no (non-whitespace) tokens left on + // this line + if (ensure_is_last_on_line(it)) + { + seen_newline = true; + iter_ctx->first = it; + on_include (dir, true, include_next); + } + return true; + } + break; + + case T_PP_ELSE: // #else + case T_PP_ENDIF: // #endif + if (!impl::call_found_directive_hook(ctx, *it)) + { + // handle this directive + if (T_PP_ELSE == token_id(*it)) + on_else(); + else + on_endif(); + + // make sure, there are no (non-whitespace) tokens left on + // this line + ensure_is_last_on_line(it); + + // we skipped to the end of this line already + seen_newline = true; + iter_ctx->first = it; + return true; + } + break; + + // extract everything on this line as arguments +// case T_PP_IF: // #if +// case T_PP_ELIF: // #elif +// case T_PP_ERROR: // #error +// case T_PP_WARNING: // #warning +// case T_PP_PRAGMA: // #pragma +// case T_PP_LINE: // #line +// break; + + // extract first non-whitespace token as argument + case T_PP_UNDEF: // #undef + if (!impl::call_found_directive_hook(ctx, *it) && + extract_identifier(it)) + { + on_undefine(it); + } + call_hook_in_skip = false; + break; + + case T_PP_IFDEF: // #ifdef + if (!impl::call_found_directive_hook(ctx, *it) && + extract_identifier(it)) + { + on_ifdef(directive, it); + } + call_hook_in_skip = false; + break; + + case T_PP_IFNDEF: // #ifndef + if (!impl::call_found_directive_hook(ctx, *it) && + extract_identifier(it)) + { + on_ifndef(directive, it); + } + call_hook_in_skip = false; + break; + +#if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0 +// case T_MSEXT_PP_REGION: // #region ... +// break; +// +// case T_MSEXT_PP_ENDREGION: // #endregion +// break; +#endif + + default: + can_exit = false; + break; + } + } + +// start over with the next line, if only possible + if (can_exit) { + skip_to_eol_with_check(it, call_hook_in_skip); + return true; // may be safely ignored + } + return false; // do not ignore this pp directive +} + +/////////////////////////////////////////////////////////////////////////////// +// pp_directive(): recognize a preprocessor directive +template <typename ContextT> +inline bool +pp_iterator_functor<ContextT>::pp_directive() +{ + using namespace cpplexer; + +// test, if the next non-whitespace token is a pp directive +lexer_type it = iter_ctx->first; + + if (!impl::next_token_is_pp_directive(ctx, it, iter_ctx->last)) { + // skip null pp directive (no need to do it via the parser) + if (it != iter_ctx->last && T_POUND == BASE_TOKEN(token_id(*it))) { + if (impl::pp_is_last_on_line(ctx, it, iter_ctx->last)) { + // start over with the next line + seen_newline = true; + iter_ctx->first = it; + return true; + } + else if (ctx.get_if_block_status()) { + // report invalid pp directive + impl::skip_to_eol(ctx, it, iter_ctx->last); + seen_newline = true; + + string_type str(boost::wave::util::impl::as_string<string_type>( + iter_ctx->first, it)); + + token_sequence_type faulty_line; + + for (/**/; iter_ctx->first != it; ++iter_ctx->first) + faulty_line.push_back(*iter_ctx->first); + + token_sequence_type pending; + if (ctx.get_hooks().found_unknown_directive(ctx, faulty_line, pending)) + { + // if there is some replacement text, insert it into the pending queue + if (!pending.empty()) + pending_queue.splice(pending_queue.begin(), pending); + return true; + } + + // default behavior is to throw an exception + on_illformed(str); + } + } + + // this line does not contain a pp directive, so simply return + return false; + } + +// found eof + if (it == iter_ctx->last) + return false; + +// ignore/handle all pp directives not related to conditional compilation while +// if block status is false + if (handle_pp_directive(it)) { + // we may skip pp directives only if the current if block status is + // false or if it was a #include directive we could handle directly + return true; // the pp directive has been handled/skipped + } + +// found a pp directive, so try to identify it, start with the pp_token +bool found_eof = false; +result_type found_directive; +token_sequence_type found_eoltokens; + +tree_parse_info_type hit = cpp_grammar_type::parse_cpp_grammar( + it, iter_ctx->last, act_pos, found_eof, found_directive, found_eoltokens); + + if (hit.match) { + // position the iterator past the matched sequence to allow + // resynchronization, if an error occurs + iter_ctx->first = hit.stop; + seen_newline = true; + must_emit_line_directive = true; + + // found a valid pp directive, dispatch to the correct function to handle + // the found pp directive + bool result = dispatch_directive (hit, found_directive, found_eoltokens); + + if (found_eof && !need_single_line(ctx.get_language())) { + // The line was terminated with an end of file token. + // So trigger a warning, that the last line was not terminated with a + // newline. + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + last_line_not_terminated, "", act_pos); + } + return result; + } + else if (token_id(found_directive) != T_EOF) { + // recognized invalid directive + impl::skip_to_eol(ctx, it, iter_ctx->last); + seen_newline = true; + + string_type str(boost::wave::util::impl::as_string<string_type>( + iter_ctx->first, it)); + iter_ctx->first = it; + + // report the ill formed directive + on_illformed(str); + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// dispatch_directive(): dispatch a recognized preprocessor directive +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline bool +pp_iterator_functor<ContextT>::dispatch_directive( + tree_parse_info_type const &hit, result_type const& found_directive, + token_sequence_type const& found_eoltokens) +{ + using namespace cpplexer; + + typedef typename parse_tree_type::const_iterator const_child_iterator_t; + +// this iterator points to the root node of the parse tree +const_child_iterator_t begin = hit.trees.begin(); + +// decide, which preprocessor directive was found +parse_tree_type const &root = (*begin).children; +parse_node_value_type const &nodeval = get_first_leaf(*root.begin()).value; +//long node_id = nodeval.id().to_long(); + +const_child_iterator_t begin_child_it = (*root.begin()).children.begin(); +const_child_iterator_t end_child_it = (*root.begin()).children.end(); + +token_id id = token_id(found_directive); + + // call preprocessing hook + if (impl::call_found_directive_hook(ctx, found_directive)) + return true; // skip this directive and return newline only + + switch (static_cast<unsigned int>(id)) { +// case T_PP_QHEADER: // #include "..." +// #if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0 +// case T_PP_QHEADER_NEXT: // #include_next "..." +// #endif +// on_include ((*nodeval.begin()).get_value(), false, +// T_PP_QHEADER_NEXT == id); +// break; + +// case T_PP_HHEADER: // #include <...> +// #if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0 +// case T_PP_HHEADER_NEXT: // #include_next <...> +// #endif +// on_include ((*nodeval.begin()).get_value(), true, +// T_PP_HHEADER_NEXT == id); +// break; + + case T_PP_INCLUDE: // #include ... +#if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0 + case T_PP_INCLUDE_NEXT: // #include_next ... +#endif + on_include (begin_child_it, end_child_it, T_PP_INCLUDE_NEXT == id); + break; + + case T_PP_DEFINE: // #define + on_define (*begin); + break; + +// case T_PP_UNDEF: // #undef +// on_undefine(*nodeval.begin()); +// break; +// +// case T_PP_IFDEF: // #ifdef +// on_ifdef(found_directive, begin_child_it, end_child_it); +// break; +// +// case T_PP_IFNDEF: // #ifndef +// on_ifndef(found_directive, begin_child_it, end_child_it); +// break; + + case T_PP_IF: // #if + on_if(found_directive, begin_child_it, end_child_it); + break; + + case T_PP_ELIF: // #elif + on_elif(found_directive, begin_child_it, end_child_it); + break; + +// case T_PP_ELSE: // #else +// on_else(); +// break; + +// case T_PP_ENDIF: // #endif +// on_endif(); +// break; + + case T_PP_LINE: // #line + on_line(begin_child_it, end_child_it); + break; + + case T_PP_ERROR: // #error + on_error(begin_child_it, end_child_it); + break; + +#if BOOST_WAVE_SUPPORT_WARNING_DIRECTIVE != 0 + case T_PP_WARNING: // #warning + on_warning(begin_child_it, end_child_it); + break; +#endif + + case T_PP_PRAGMA: // #pragma + return on_pragma(begin_child_it, end_child_it); + +#if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0 + case T_MSEXT_PP_REGION: + case T_MSEXT_PP_ENDREGION: + break; // ignore these +#endif + + default: // #something else + on_illformed((*nodeval.begin()).get_value()); + + // if we end up here, we have been instructed to ignore the error, so + // we simply copy the whole construct to the output + { + token_sequence_type expanded; + get_token_value<result_type, parse_node_type> get_value; + + std::copy(make_ref_transform_iterator(begin_child_it, get_value), + make_ref_transform_iterator(end_child_it, get_value), + std::inserter(expanded, expanded.end())); + pending_queue.splice(pending_queue.begin(), expanded); + } + break; + } + + // properly skip trailing newline for all directives + typename token_sequence_type::const_iterator eol = found_eoltokens.begin(); + impl::skip_to_eol(ctx, eol, found_eoltokens.end()); + return true; // return newline only +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_include: handle #include <...> or #include "..." directives +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_include (string_type const &s, + bool is_system, bool include_next) +{ + BOOST_ASSERT(ctx.get_if_block_status()); + +// strip quotes first, extract filename +typename string_type::size_type pos_end = s.find_last_of(is_system ? '>' : '\"'); + + if (string_type::npos == pos_end) { + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, bad_include_statement, + s.c_str(), act_pos); + return; + } + +typename string_type::size_type pos_begin = + s.find_last_of(is_system ? '<' : '\"', pos_end-1); + + if (string_type::npos == pos_begin) { + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, bad_include_statement, + s.c_str(), act_pos); + return; + } + +std::string file_token(s.substr(pos_begin, pos_end-pos_begin+1).c_str()); +std::string file_path(s.substr(pos_begin+1, pos_end-pos_begin-1).c_str()); + +// finally include the file + on_include_helper(file_token.c_str(), file_path.c_str(), is_system, + include_next); +} + +template <typename ContextT> +inline bool +pp_iterator_functor<ContextT>::on_include_helper (char const *f, char const *s, + bool is_system, bool include_next) +{ + namespace fs = boost::filesystem; + +// try to locate the given file, searching through the include path lists +std::string file_path(s); +std::string dir_path; +#if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0 +char const *current_name = include_next ? iter_ctx->real_filename.c_str() : 0; +#else +char const *current_name = 0; // never try to match current file name +#endif + +// call the 'found_include_directive' hook function +#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 + ctx.get_hooks().found_include_directive(f, include_next); +#else + if (ctx.get_hooks().found_include_directive(ctx.derived(), f, include_next)) + return true; // client returned false: skip file to include +#endif + + file_path = util::impl::unescape_lit(file_path); + std::string native_path_str; + + if (!ctx.get_hooks().locate_include_file(ctx, file_path, is_system, + current_name, dir_path, native_path_str)) + { + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, bad_include_file, + file_path.c_str(), act_pos); + return false; + } + +// test, if this file is known through a #pragma once directive +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + if (!ctx.has_pragma_once(native_path_str)) +#endif + { + // the new include file determines the actual current directory + ctx.set_current_directory(native_path_str.c_str()); + + // preprocess the opened file + boost::shared_ptr<base_iteration_context_type> new_iter_ctx ( + new iteration_context_type(ctx, native_path_str.c_str(), act_pos, + boost::wave::enable_prefer_pp_numbers(ctx.get_language()), + is_system ? base_iteration_context_type::system_header : + base_iteration_context_type::user_header)); + + // call the include policy trace function +#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 + ctx.get_hooks().opened_include_file(dir_path, file_path, + ctx.get_iteration_depth(), is_system); +#else + ctx.get_hooks().opened_include_file(ctx.derived(), dir_path, file_path, + is_system); +#endif + + // store current file position + iter_ctx->real_relative_filename = ctx.get_current_relative_filename().c_str(); + iter_ctx->filename = act_pos.get_file(); + iter_ctx->line = act_pos.get_line(); + iter_ctx->if_block_depth = ctx.get_if_block_depth(); + iter_ctx->emitted_lines = (unsigned int)(-1); // force #line directive + + // push the old iteration context onto the stack and continue with the new + ctx.push_iteration_context(act_pos, iter_ctx); + iter_ctx = new_iter_ctx; + seen_newline = true; // fake a newline to trigger pp_directive + must_emit_line_directive = true; + + act_pos.set_file(iter_ctx->filename); // initialize file position +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + fs::path rfp(wave::util::create_path(iter_ctx->real_filename.c_str())); + std::string real_filename(rfp.string()); + ctx.set_current_filename(real_filename.c_str()); +#endif + + ctx.set_current_relative_filename(dir_path.c_str()); + iter_ctx->real_relative_filename = dir_path.c_str(); + + act_pos.set_line(iter_ctx->line); + act_pos.set_column(0); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_include(): handle #include ... directives +// +/////////////////////////////////////////////////////////////////////////////// + +namespace impl { + + // trim all whitespace from the beginning and the end of the given string + template <typename StringT> + inline StringT + trim_whitespace(StringT const &s) + { + typedef typename StringT::size_type size_type; + + size_type first = s.find_first_not_of(" \t\v\f"); + if (StringT::npos == first) + return StringT(); + size_type last = s.find_last_not_of(" \t\v\f"); + return s.substr(first, last-first+1); + } +} + +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_include( + typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end, bool include_next) +{ + BOOST_ASSERT(ctx.get_if_block_status()); + +// preprocess the given token sequence (the body of the #include directive) +get_token_value<result_type, parse_node_type> get_value; +token_sequence_type expanded; +token_sequence_type toexpand; + + std::copy(make_ref_transform_iterator(begin, get_value), + make_ref_transform_iterator(end, get_value), + std::inserter(toexpand, toexpand.end())); + + typename token_sequence_type::iterator begin2 = toexpand.begin(); + ctx.expand_whole_tokensequence(begin2, toexpand.end(), expanded, + false); + +// now, include the file +string_type s (impl::trim_whitespace(boost::wave::util::impl::as_string(expanded))); +bool is_system = '<' == s[0] && '>' == s[s.size()-1]; + + if (!is_system && !('\"' == s[0] && '\"' == s[s.size()-1])) { + // should resolve into something like <...> or "..." + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, bad_include_statement, + s.c_str(), act_pos); + return; + } + on_include(s, is_system, include_next); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_define(): handle #define directives +// +/////////////////////////////////////////////////////////////////////////////// + +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_define (parse_node_type const &node) +{ + BOOST_ASSERT(ctx.get_if_block_status()); + +// retrieve the macro definition from the parse tree +result_type macroname; +std::vector<result_type> macroparameters; +token_sequence_type macrodefinition; +bool has_parameters = false; +position_type pos(act_token.get_position()); + + if (!boost::wave::util::retrieve_macroname(ctx, node, + BOOST_WAVE_PLAIN_DEFINE_ID, macroname, pos, false)) + return; + has_parameters = boost::wave::util::retrieve_macrodefinition(node, + BOOST_WAVE_MACRO_PARAMETERS_ID, macroparameters, pos, false); + boost::wave::util::retrieve_macrodefinition(node, + BOOST_WAVE_MACRO_DEFINITION_ID, macrodefinition, pos, false); + + if (has_parameters) { +#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + if (boost::wave::need_variadics(ctx.get_language())) { + // test whether ellipsis are given, and if yes, if these are placed as the + // last argument, test if __VA_ARGS__ is used as a macro parameter name + using namespace cpplexer; + typedef typename std::vector<result_type>::iterator + parameter_iterator_t; + + bool seen_ellipses = false; + parameter_iterator_t end = macroparameters.end(); + for (parameter_iterator_t pit = macroparameters.begin(); + pit != end; ++pit) + { + if (seen_ellipses) { + // ellipses are not the last given formal argument + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + bad_define_statement, macroname.get_value().c_str(), + (*pit).get_position()); + return; + } + if (T_ELLIPSIS == token_id(*pit)) + seen_ellipses = true; + + // can't use __VA_ARGS__ as a argument name + if ("__VA_ARGS__" == (*pit).get_value()) { + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + bad_define_statement_va_args, + macroname.get_value().c_str(), (*pit).get_position()); + return; + } + } + + // if there wasn't an ellipsis, then there shouldn't be a __VA_ARGS__ + // placeholder in the definition too [C99 Standard 6.10.3.5] + if (!seen_ellipses) { + typedef typename token_sequence_type::iterator definition_iterator_t; + + bool seen_va_args = false; + definition_iterator_t pend = macrodefinition.end(); + for (definition_iterator_t dit = macrodefinition.begin(); + dit != pend; ++dit) + { + if (T_IDENTIFIER == token_id(*dit) && + "__VA_ARGS__" == (*dit).get_value()) + { + seen_va_args = true; + } + } + if (seen_va_args) { + // must not have seen __VA_ARGS__ placeholder + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + bad_define_statement_va_args, + macroname.get_value().c_str(), act_token.get_position()); + return; + } + } + } + else +#endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + { + // test, that there is no T_ELLIPSES given + using namespace cpplexer; + typedef typename std::vector<result_type>::iterator + parameter_iterator_t; + + parameter_iterator_t end = macroparameters.end(); + for (parameter_iterator_t pit = macroparameters.begin(); + pit != end; ++pit) + { + if (T_ELLIPSIS == token_id(*pit)) { + // if variadics are disabled, no ellipses should be given + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + bad_define_statement, macroname.get_value().c_str(), + (*pit).get_position()); + return; + } + } + } + } + +// add the new macro to the macromap + ctx.add_macro_definition(macroname, has_parameters, macroparameters, + macrodefinition); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_undefine(): handle #undef directives +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_undefine (lexer_type const &it) +{ + BOOST_ASSERT(ctx.get_if_block_status()); + +// retrieve the macro name to undefine from the parse tree + ctx.remove_macro_definition((*it).get_value()); // throws for predefined macros +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_ifdef(): handle #ifdef directives +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_ifdef( + result_type const& found_directive, lexer_type const &it) +// typename parse_tree_type::const_iterator const &it) +// typename parse_tree_type::const_iterator const &end) +{ +// get_token_value<result_type, parse_node_type> get_value; +// token_sequence_type toexpand; +// +// std::copy(make_ref_transform_iterator((*begin).children.begin(), get_value), +// make_ref_transform_iterator((*begin).children.end(), get_value), +// std::inserter(toexpand, toexpand.end())); + +bool is_defined = false; +token_sequence_type directive; + + directive.insert(directive.end(), *it); + +#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 + is_defined = ctx.is_defined_macro((*it).get_value()); // toexpand.begin(), toexpand.end()); + ctx.get_hooks().evaluated_conditional_expression(directive, is_defined); +#else + do { + is_defined = ctx.is_defined_macro((*it).get_value()); // toexpand.begin(), toexpand.end()); + } while (ctx.get_hooks().evaluated_conditional_expression(ctx.derived(), + found_directive, directive, is_defined)); +#endif + ctx.enter_if_block(is_defined); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_ifndef(): handle #ifndef directives +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_ifndef( + result_type const& found_directive, lexer_type const &it) +// typename parse_tree_type::const_iterator const &it) +// typename parse_tree_type::const_iterator const &end) +{ +// get_token_value<result_type, parse_node_type> get_value; +// token_sequence_type toexpand; +// +// std::copy(make_ref_transform_iterator((*begin).children.begin(), get_value), +// make_ref_transform_iterator((*begin).children.end(), get_value), +// std::inserter(toexpand, toexpand.end())); + +bool is_defined = false; +token_sequence_type directive; + + directive.insert(directive.end(), *it); + +#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 + is_defined = ctx.is_defined_macro((*it).get_value()); // toexpand.begin(), toexpand.end()); + ctx.get_hooks().evaluated_conditional_expression(directive, is_defined); +#else + do { + is_defined = ctx.is_defined_macro((*it).get_value()); // toexpand.begin(), toexpand.end()); + } while (ctx.get_hooks().evaluated_conditional_expression(ctx.derived(), + found_directive, directive, is_defined)); +#endif + ctx.enter_if_block(!is_defined); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_else(): handle #else directives +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_else() +{ + if (!ctx.enter_else_block()) { + // #else without matching #if + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, missing_matching_if, + "#else", act_pos); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_endif(): handle #endif directives +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_endif() +{ + if (!ctx.exit_if_block()) { + // #endif without matching #if + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, missing_matching_if, + "#endif", act_pos); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// replace all remaining (== undefined) identifiers with an integer literal '0' +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::replace_undefined_identifiers( + token_sequence_type &expanded) +{ + typename token_sequence_type::iterator exp_end = expanded.end(); + for (typename token_sequence_type::iterator exp_it = expanded.begin(); + exp_it != exp_end; ++exp_it) + { + using namespace boost::wave; + + token_id id = token_id(*exp_it); + if (IS_CATEGORY(id, IdentifierTokenType) || + IS_CATEGORY(id, KeywordTokenType)) + { + (*exp_it).set_token_id(T_INTLIT); + (*exp_it).set_value("0"); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_if(): handle #if directives +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_if( + result_type const& found_directive, + typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end) +{ +// preprocess the given sequence into the provided list +get_token_value<result_type, parse_node_type> get_value; +token_sequence_type toexpand; + + std::copy(make_ref_transform_iterator(begin, get_value), + make_ref_transform_iterator(end, get_value), + std::inserter(toexpand, toexpand.end())); + + impl::remove_leading_whitespace(ctx, toexpand); + +bool if_status = false; +grammars::value_error status = grammars::error_noerror; +token_sequence_type expanded; + + do { + expanded.clear(); + + typename token_sequence_type::iterator begin2 = toexpand.begin(); + ctx.expand_whole_tokensequence(begin2, toexpand.end(), expanded); + + // replace all remaining (== undefined) identifiers with an integer literal '0' + replace_undefined_identifiers(expanded); + +#if BOOST_WAVE_DUMP_CONDITIONAL_EXPRESSIONS != 0 + { + string_type outstr(boost::wave::util::impl::as_string(toexpand)); + outstr += "(" + boost::wave::util::impl::as_string(expanded) + ")"; + BOOST_WAVE_DUMP_CONDITIONAL_EXPRESSIONS_OUT << "#if " << outstr + << std::endl; + } +#endif + try { + // parse the expression and enter the #if block + if_status = grammars::expression_grammar_gen<result_type>:: + evaluate(expanded.begin(), expanded.end(), act_pos, + ctx.get_if_block_status(), status); + } + catch (boost::wave::preprocess_exception const& e) { + // any errors occurred have to be dispatched to the context hooks + ctx.get_hooks().throw_exception(ctx.derived(), e); + break; + } + +#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 + ctx.get_hooks().evaluated_conditional_expression(toexpand, if_status); + } while (false); +#else + } while (ctx.get_hooks().evaluated_conditional_expression(ctx.derived(), + found_directive, toexpand, if_status) + && status == grammars::error_noerror); +#endif + + ctx.enter_if_block(if_status); + if (grammars::error_noerror != status) { + // division or other error by zero occurred + string_type expression = util::impl::as_string(expanded); + if (0 == expression.size()) + expression = "<empty expression>"; + + if (grammars::error_division_by_zero & status) { + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, division_by_zero, + expression.c_str(), act_pos); + } + else if (grammars::error_integer_overflow & status) { + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, integer_overflow, + expression.c_str(), act_pos); + } + else if (grammars::error_character_overflow & status) { + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + character_literal_out_of_range, expression.c_str(), act_pos); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_elif(): handle #elif directives +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_elif( + result_type const& found_directive, + typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end) +{ +// preprocess the given sequence into the provided list +get_token_value<result_type, parse_node_type> get_value; +token_sequence_type toexpand; + + std::copy(make_ref_transform_iterator(begin, get_value), + make_ref_transform_iterator(end, get_value), + std::inserter(toexpand, toexpand.end())); + + impl::remove_leading_whitespace(ctx, toexpand); + +// check current if block status + if (ctx.get_if_block_some_part_status()) { + if (!ctx.enter_elif_block(false)) { + // #else without matching #if + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + missing_matching_if, "#elif", act_pos); + // fall through... + } + + // skip all the expression and the trailing whitespace + typename token_sequence_type::iterator begin2 = toexpand.begin(); + + impl::skip_to_eol(ctx, begin2, toexpand.end()); + return; // one of previous #if/#elif was true, so don't enter this #elif + } + +// preprocess the given sequence into the provided list +bool if_status = false; +grammars::value_error status = grammars::error_noerror; +token_sequence_type expanded; + + do { + expanded.clear(); + + typename token_sequence_type::iterator begin2 = toexpand.begin(); + ctx.expand_whole_tokensequence(begin2, toexpand.end(), expanded); + + // replace all remaining (== undefined) identifiers with an integer literal '0' + replace_undefined_identifiers(expanded); + +#if BOOST_WAVE_DUMP_CONDITIONAL_EXPRESSIONS != 0 + { + string_type outstr(boost::wave::util::impl::as_string(toexpand)); + outstr += "(" + boost::wave::util::impl::as_string(expanded) + ")"; + BOOST_WAVE_DUMP_CONDITIONAL_EXPRESSIONS_OUT << "#elif " << outstr << std::endl; + } +#endif + + try { + // parse the expression and enter the #elif block + if_status = grammars::expression_grammar_gen<result_type>:: + evaluate(expanded.begin(), expanded.end(), act_pos, + ctx.get_if_block_status(), status); + } + catch (boost::wave::preprocess_exception const& e) { + // any errors occurred have to be dispatched to the context hooks + ctx.get_hooks().throw_exception(ctx.derived(), e); + } + +#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 + ctx.get_hooks().evaluated_conditional_expression(toexpand, if_status); + } while (false); +#else + } while (ctx.get_hooks().evaluated_conditional_expression(ctx.derived(), + found_directive, toexpand, if_status) + && status == grammars::error_noerror); +#endif + + if (!ctx.enter_elif_block(if_status)) { + // #elif without matching #if + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, missing_matching_if, + "#elif", act_pos); + return; + } + + if (grammars::error_noerror != status) { + // division or other error by zero occurred + string_type expression = util::impl::as_string(expanded); + if (0 == expression.size()) + expression = "<empty expression>"; + + if (grammars::error_division_by_zero & status) { + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, division_by_zero, + expression.c_str(), act_pos); + } + else if (grammars::error_integer_overflow & status) { + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + integer_overflow, expression.c_str(), act_pos); + } + else if (grammars::error_character_overflow & status) { + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + character_literal_out_of_range, expression.c_str(), act_pos); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_illformed(): handles the illegal directive +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_illformed( + typename result_type::string_type s) +{ + BOOST_ASSERT(ctx.get_if_block_status()); + + // some messages have more than one newline at the end + typename string_type::size_type p = s.find_last_not_of('\n'); + if (string_type::npos != p) + s = s.substr(0, p+1); + + // throw the exception + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, ill_formed_directive, + s.c_str(), act_pos); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_line(): handle #line directives +// +/////////////////////////////////////////////////////////////////////////////// + +namespace impl { + + template <typename IteratorT, typename StringT> + bool retrieve_line_info (IteratorT first, IteratorT const &last, + unsigned int &line, StringT &file, + boost::wave::preprocess_exception::error_code& error) + { + using namespace boost::wave; + token_id id = token_id(*first); + if (T_PP_NUMBER == id || T_INTLIT == id) { + // extract line number + using namespace std; // some systems have atoi in namespace std + line = (unsigned int)atoi((*first).get_value().c_str()); + if (0 == line) + error = preprocess_exception::bad_line_number; + + // re-extract line number with spirit to diagnose overflow + using namespace boost::spirit::classic; + if (!parse((*first).get_value().c_str(), int_p).full) + error = preprocess_exception::bad_line_number; + + // extract file name (if it is given) + while (++first != last && IS_CATEGORY(*first, WhiteSpaceTokenType)) + /**/; // skip whitespace + + if (first != last) { + if (T_STRINGLIT != token_id(*first)) { + error = preprocess_exception::bad_line_filename; + return false; + } + + StringT const &file_lit = (*first).get_value(); + + if ('L' == file_lit[0]) { + error = preprocess_exception::bad_line_filename; + return false; // shouldn't be a wide character string + } + + file = file_lit.substr(1, file_lit.size()-2); + + // test if there is other junk on this line + while (++first != last && IS_CATEGORY(*first, WhiteSpaceTokenType)) + /**/; // skip whitespace + } + return first == last; + } + error = preprocess_exception::bad_line_statement; + return false; + } +} + +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_line( + typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end) +{ + BOOST_ASSERT(ctx.get_if_block_status()); + +// Try to extract the line number and file name from the given token list +// directly. If that fails, preprocess the whole token sequence and try again +// to extract this information. +token_sequence_type expanded; +get_token_value<result_type, parse_node_type> get_value; + + typedef typename ref_transform_iterator_generator< + get_token_value<result_type, parse_node_type>, + typename parse_tree_type::const_iterator + >::type const_tree_iterator_t; + +const_tree_iterator_t first = make_ref_transform_iterator(begin, get_value); +const_tree_iterator_t last = make_ref_transform_iterator(end, get_value); + +// try to interpret the #line body as a number followed by an optional +// string literal +unsigned int line = 0; +preprocess_exception::error_code error = preprocess_exception::no_error; +string_type file_name; +token_sequence_type toexpand; + + std::copy(first, last, std::inserter(toexpand, toexpand.end())); + if (!impl::retrieve_line_info(first, last, line, file_name, error)) { + // preprocess the body of this #line message + typename token_sequence_type::iterator begin2 = toexpand.begin(); + ctx.expand_whole_tokensequence(begin2, toexpand.end(), + expanded, false); + + error = preprocess_exception::no_error; + if (!impl::retrieve_line_info(expanded.begin(), expanded.end(), + line, file_name, error)) + { + typename ContextT::string_type msg( + boost::wave::util::impl::as_string(expanded)); + BOOST_WAVE_THROW_VAR_CTX(ctx, preprocess_exception, error, + msg.c_str(), act_pos); + return; + } + + // call the corresponding pp hook function + ctx.get_hooks().found_line_directive(ctx.derived(), expanded, line, + file_name.c_str()); + } + else { + // call the corresponding pp hook function + ctx.get_hooks().found_line_directive(ctx.derived(), toexpand, line, + file_name.c_str()); + } + +// the queues should be empty at this point + BOOST_ASSERT(unput_queue.empty()); + BOOST_ASSERT(pending_queue.empty()); + +// make sure error recovery starts on the next line + must_emit_line_directive = true; + +// diagnose possible error in detected line directive + if (error != preprocess_exception::no_error) { + typename ContextT::string_type msg( + boost::wave::util::impl::as_string(expanded)); + BOOST_WAVE_THROW_VAR_CTX(ctx, preprocess_exception, error, + msg.c_str(), act_pos); + return; + } + +// set new line number/filename only if ok + if (!file_name.empty()) { // reuse current file name + using boost::wave::util::impl::unescape_lit; + act_pos.set_file(unescape_lit(file_name).c_str()); + } + act_pos.set_line(line); + iter_ctx->first.set_position(act_pos); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// on_error(): handle #error directives +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_error( + typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end) +{ + BOOST_ASSERT(ctx.get_if_block_status()); + +// preprocess the given sequence into the provided list +token_sequence_type expanded; +get_token_value<result_type, parse_node_type> get_value; + +typename ref_transform_iterator_generator< + get_token_value<result_type, parse_node_type>, + typename parse_tree_type::const_iterator + >::type first = make_ref_transform_iterator(begin, get_value); + +#if BOOST_WAVE_PREPROCESS_ERROR_MESSAGE_BODY != 0 +// preprocess the body of this #error message +token_sequence_type toexpand; + + std::copy(first, make_ref_transform_iterator(end, get_value), + std::inserter(toexpand, toexpand.end())); + + typename token_sequence_type::iterator begin2 = toexpand.begin(); + ctx.expand_whole_tokensequence(begin2, toexpand.end(), expanded, + false); + if (!ctx.get_hooks().found_error_directive(ctx.derived(), toexpand)) +#else +// simply copy the body of this #error message to the issued diagnostic +// message + std::copy(first, make_ref_transform_iterator(end, get_value), + std::inserter(expanded, expanded.end())); + if (!ctx.get_hooks().found_error_directive(ctx.derived(), expanded)) +#endif + { + // report the corresponding error + BOOST_WAVE_STRINGTYPE msg(boost::wave::util::impl::as_string(expanded)); + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, error_directive, + msg.c_str(), act_pos); + } +} + +#if BOOST_WAVE_SUPPORT_WARNING_DIRECTIVE != 0 +/////////////////////////////////////////////////////////////////////////////// +// +// on_warning(): handle #warning directives +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline void +pp_iterator_functor<ContextT>::on_warning( + typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end) +{ + BOOST_ASSERT(ctx.get_if_block_status()); + +// preprocess the given sequence into the provided list +token_sequence_type expanded; +get_token_value<result_type, parse_node_type> get_value; + +typename ref_transform_iterator_generator< + get_token_value<result_type, parse_node_type>, + typename parse_tree_type::const_iterator + >::type first = make_ref_transform_iterator(begin, get_value); + +#if BOOST_WAVE_PREPROCESS_ERROR_MESSAGE_BODY != 0 +// preprocess the body of this #warning message +token_sequence_type toexpand; + + std::copy(first, make_ref_transform_iterator(end, get_value), + std::inserter(toexpand, toexpand.end())); + + typename token_sequence_type::iterator begin2 = toexpand.begin(); + ctx.expand_whole_tokensequence(begin2, toexpand.end(), expanded, + false); + if (!ctx.get_hooks().found_warning_directive(ctx.derived(), toexpand)) +#else +// simply copy the body of this #warning message to the issued diagnostic +// message + std::copy(first, make_ref_transform_iterator(end, get_value), + std::inserter(expanded, expanded.end())); + if (!ctx.get_hooks().found_warning_directive(ctx.derived(), expanded)) +#endif + { + // report the corresponding error + BOOST_WAVE_STRINGTYPE msg(boost::wave::util::impl::as_string(expanded)); + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, warning_directive, + msg.c_str(), act_pos); + } +} +#endif // BOOST_WAVE_SUPPORT_WARNING_DIRECTIVE != 0 + +/////////////////////////////////////////////////////////////////////////////// +// +// on_pragma(): handle #pragma directives +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT> +inline bool +pp_iterator_functor<ContextT>::on_pragma( + typename parse_tree_type::const_iterator const &begin, + typename parse_tree_type::const_iterator const &end) +{ + using namespace boost::wave; + + BOOST_ASSERT(ctx.get_if_block_status()); + +// Look at the pragma token sequence and decide, if the first token is STDC +// (see C99 standard [6.10.6.2]), if it is, the sequence must _not_ be +// preprocessed. +token_sequence_type expanded; +get_token_value<result_type, parse_node_type> get_value; + + typedef typename ref_transform_iterator_generator< + get_token_value<result_type, parse_node_type>, + typename parse_tree_type::const_iterator + >::type const_tree_iterator_t; + +const_tree_iterator_t first = make_ref_transform_iterator(begin, get_value); +const_tree_iterator_t last = make_ref_transform_iterator(end, get_value); + + expanded.push_back(result_type(T_PP_PRAGMA, "#pragma", act_token.get_position())); + expanded.push_back(result_type(T_SPACE, " ", act_token.get_position())); + + while (++first != last && IS_CATEGORY(*first, WhiteSpaceTokenType)) + expanded.push_back(*first); // skip whitespace + + if (first != last) { + if (T_IDENTIFIER == token_id(*first) && + boost::wave::need_c99(ctx.get_language()) && + (*first).get_value() == "STDC") + { + // do _not_ preprocess the token sequence + std::copy(first, last, std::inserter(expanded, expanded.end())); + } + else { +#if BOOST_WAVE_PREPROCESS_PRAGMA_BODY != 0 + // preprocess the given tokensequence + token_sequence_type toexpand; + + std::copy(first, last, std::inserter(toexpand, toexpand.end())); + + typename token_sequence_type::iterator begin2 = toexpand.begin(); + ctx.expand_whole_tokensequence(begin2, toexpand.end(), + expanded, false); +#else + // do _not_ preprocess the token sequence + std::copy(first, last, std::inserter(expanded, expanded.end())); +#endif + } + } + expanded.push_back(result_type(T_NEWLINE, "\n", act_token.get_position())); + +// the queues should be empty at this point + BOOST_ASSERT(unput_queue.empty()); + BOOST_ASSERT(pending_queue.empty()); + +// try to interpret the expanded #pragma body + token_sequence_type pending; + if (interpret_pragma(expanded, pending)) { + // if there is some replacement text, insert it into the pending queue + if (!pending.empty()) + pending_queue.splice(pending_queue.begin(), pending); + return true; // this #pragma was successfully recognized + } + +#if BOOST_WAVE_EMIT_PRAGMA_DIRECTIVES != 0 +// Move the resulting token sequence into the pending_queue, so it will be +// returned to the caller. + if (boost::wave::need_emit_pragma_directives(ctx.get_language())) { + pending_queue.splice(pending_queue.begin(), expanded); + return false; // return the whole #pragma directive + } +#endif + return true; // skip the #pragma at all +} + +template <typename ContextT> +inline bool +pp_iterator_functor<ContextT>::interpret_pragma( + token_sequence_type const &pragma_body, token_sequence_type &result) +{ + using namespace cpplexer; + + typename token_sequence_type::const_iterator end = pragma_body.end(); + typename token_sequence_type::const_iterator it = pragma_body.begin(); + for (++it; it != end && IS_CATEGORY(*it, WhiteSpaceTokenType); ++it) + /**/; // skip whitespace + + if (it == end) // eof reached + return false; + + return boost::wave::util::interpret_pragma( + ctx.derived(), act_token, it, end, result); +} + +/////////////////////////////////////////////////////////////////////////////// +} // namespace impl + +/////////////////////////////////////////////////////////////////////////////// +// +// pp_iterator +// +// The boost::wave::pp_iterator template is the iterator, through which +// the resulting preprocessed input stream is accessible. +// +/////////////////////////////////////////////////////////////////////////////// + +template <typename ContextT> +class pp_iterator +: public boost::spirit::classic::multi_pass< + boost::wave::impl::pp_iterator_functor<ContextT>, + boost::wave::util::functor_input + > +{ +public: + typedef boost::wave::impl::pp_iterator_functor<ContextT> input_policy_type; + +private: + typedef + boost::spirit::classic::multi_pass<input_policy_type, boost::wave::util::functor_input> + base_type; + typedef pp_iterator<ContextT> self_type; + typedef boost::wave::util::functor_input functor_input_type; + +public: + pp_iterator() + {} + + template <typename IteratorT> + pp_iterator(ContextT &ctx, IteratorT const &first, IteratorT const &last, + typename ContextT::position_type const &pos) + : base_type(input_policy_type(ctx, first, last, pos)) + {} + + bool force_include(char const *path_, bool is_last) + { + bool result = this->get_functor().on_include_helper(path_, path_, + false, false); + if (is_last) { + this->functor_input_type:: + template inner<input_policy_type>::advance_input(); + } + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(CPP_ITERATOR_HPP_175CA88F_7273_43FA_9039_BCF7459E1F29_INCLUDED) diff --git a/boost/wave/util/cpp_macromap.hpp b/boost/wave/util/cpp_macromap.hpp new file mode 100644 index 0000000000..8b155b83af --- /dev/null +++ b/boost/wave/util/cpp_macromap.hpp @@ -0,0 +1,1942 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + Macro expansion engine + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(CPP_MACROMAP_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())); + 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, "%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(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++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 + { +#if BOOST_WAVE_SUPPORT_CPP0X != 0 + if (boost::wave::need_cpp0x(ctx.get_language())) { + // define C++0x 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) diff --git a/boost/wave/util/cpp_macromap_predef.hpp b/boost/wave/util/cpp_macromap_predef.hpp new file mode 100644 index 0000000000..0725a36306 --- /dev/null +++ b/boost/wave/util/cpp_macromap_predef.hpp @@ -0,0 +1,288 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + Definition of the predefined macros + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(CPP_MACROMAP_PREDEF_HPP_HK041119) +#define CPP_MACROMAP_PREDEF_HPP_HK041119 + +#include <cstdio> +#include <boost/assert.hpp> + +#include <boost/wave/wave_config.hpp> +#include <boost/wave/wave_config_constant.hpp> +#include <boost/wave/token_ids.hpp> +#include <boost/wave/util/time_conversion_helper.hpp> // time_conversion_helper + +// this must occur after all of the includes and before any code appears +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_PREFIX +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +// This file contains the definition of functions needed for the management +// of static and dynamic predefined macros, such as __DATE__, __TIME__ etc. +// +// Note: __FILE__, __LINE__ and __INCLUDE_LEVEL__ are handled in the file +// cpp_macromap.hpp. +// +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +namespace boost { +namespace wave { +namespace util { + + /////////////////////////////////////////////////////////////////////////// + class predefined_macros + { + typedef BOOST_WAVE_STRINGTYPE string_type; + + public: + // list of static predefined macros + struct static_macros { + char const *name; + boost::wave::token_id token_id; + char const *value; + }; + + // list of dynamic predefined macros + struct dynamic_macros { + char const *name; + boost::wave::token_id token_id; + string_type (predefined_macros:: *generator)() const; + }; + + private: + boost::wave::util::time_conversion_helper compilation_time_; + string_type datestr_; // __DATE__ + string_type timestr_; // __TIME__ + string_type version_; // __SPIRIT_PP_VERSION__/__WAVE_VERSION__ + string_type versionstr_; // __SPIRIT_PP_VERSION_STR__/__WAVE_VERSION_STR__ + + protected: + void reset_datestr() + { + static const char *const monthnames[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + // for some systems sprintf, time_t etc. is in namespace std + using namespace std; + + time_t tt = time(0); + struct tm *tb = 0; + + if (tt != (time_t)-1) { + char buffer[sizeof("\"Oct 11 1347\"")+1]; + + tb = localtime (&tt); + sprintf (buffer, "\"%s %2d %4d\"", + monthnames[tb->tm_mon], tb->tm_mday, tb->tm_year + 1900); + datestr_ = buffer; + } + else { + datestr_ = "\"??? ?? ????\""; + } + } + + void reset_timestr() + { + // for some systems sprintf, time_t etc. is in namespace std + using namespace std; + + time_t tt = time(0); + struct tm *tb = 0; + + if (tt != (time_t)-1) { + char buffer[sizeof("\"12:34:56\"")+1]; + + tb = localtime (&tt); + sprintf (buffer, "\"%02d:%02d:%02d\"", tb->tm_hour, + tb->tm_min, tb->tm_sec); + timestr_ = buffer; + } + else { + timestr_ = "\"??:??:??\""; + } + } + + void reset_version() + { + char buffer[sizeof("0x00000000")+1]; + + // for some systems sprintf, time_t etc. is in namespace std + using namespace std; + + // calculate the number of days since Dec 13 2001 + // (the day the Wave project was started) + tm first_day; + + using namespace std; // for some systems memset is in namespace std + memset (&first_day, 0, sizeof(tm)); + first_day.tm_mon = 11; // Dec + first_day.tm_mday = 13; // 13 + first_day.tm_year = 101; // 2001 + + long seconds = long(difftime(compilation_time_.get_time(), mktime(&first_day))); + + sprintf(buffer, "0x%02d%1d%1d%04ld", BOOST_WAVE_VERSION_MAJOR, + BOOST_WAVE_VERSION_MINOR, BOOST_WAVE_VERSION_SUBMINOR, + seconds/(3600*24)); + version_ = buffer; + } + + void reset_versionstr() + { + char buffer[sizeof("\"00.00.00.0000 \"")+sizeof(BOOST_PLATFORM)+sizeof(BOOST_COMPILER)+4]; + + // for some systems sprintf, time_t etc. is in namespace std + using namespace std; + + // calculate the number of days since Dec 13 2001 + // (the day the Wave project was started) + tm first_day; + + memset (&first_day, 0, sizeof(tm)); + first_day.tm_mon = 11; // Dec + first_day.tm_mday = 13; // 13 + first_day.tm_year = 101; // 2001 + + long seconds = long(difftime(compilation_time_.get_time(), mktime(&first_day))); + + sprintf(buffer, "\"%d.%d.%d.%ld [%s/%s]\"", BOOST_WAVE_VERSION_MAJOR, + BOOST_WAVE_VERSION_MINOR, BOOST_WAVE_VERSION_SUBMINOR, + seconds/(3600*24), BOOST_PLATFORM, BOOST_COMPILER); + versionstr_ = buffer; + } + + // dynamic predefined macros + string_type get_date() const { return datestr_; } // __DATE__ + string_type get_time() const { return timestr_; } // __TIME__ + + // __SPIRIT_PP__/__WAVE__ + string_type get_version() const + { + char buffer[sizeof("0x0000")+1]; + + using namespace std; // for some systems sprintf is in namespace std + sprintf(buffer, "0x%02d%1d%1d", BOOST_WAVE_VERSION_MAJOR, + BOOST_WAVE_VERSION_MINOR, BOOST_WAVE_VERSION_SUBMINOR); + return buffer; + } + + // __WAVE_CONFIG__ + string_type get_config() const + { + char buffer[sizeof("0x00000000")+1]; + + using namespace std; // for some systems sprintf is in namespace std + sprintf(buffer, "0x%08x", BOOST_WAVE_CONFIG); + return buffer; + } + + public: + predefined_macros() + : compilation_time_(__DATE__ " " __TIME__) + { + reset(); + reset_version(); + reset_versionstr(); + } + + void reset() + { + reset_datestr(); + reset_timestr(); + } + + // __SPIRIT_PP_VERSION__/__WAVE_VERSION__ + string_type get_fullversion() const { return version_; } + + // __SPIRIT_PP_VERSION_STR__/__WAVE_VERSION_STR__ + string_type get_versionstr() const { return versionstr_; } + + // C++ mode + static_macros const& static_data_cpp(std::size_t i) const + { + static static_macros data[] = { + { "__STDC__", T_INTLIT, "1" }, + { "__cplusplus", T_INTLIT, "199711L" }, + { 0, T_EOF, 0 } + }; + BOOST_ASSERT(i < sizeof(data)/sizeof(data[0])); + return data[i]; + } + +#if BOOST_WAVE_SUPPORT_CPP0X != 0 + // C++0x mode + static_macros const& static_data_cpp0x(std::size_t i) const + { + static static_macros data[] = { + { "__STDC__", T_INTLIT, "1" }, + { "__cplusplus", T_INTLIT, "201103L" }, + { "__STDC_VERSION__", T_INTLIT, "199901L" }, + { "__STDC_HOSTED__", T_INTLIT, "0" }, + { "__WAVE_HAS_VARIADICS__", T_INTLIT, "1" }, + { 0, T_EOF, 0 } + }; + BOOST_ASSERT(i < sizeof(data)/sizeof(data[0])); + return data[i]; + } +#endif + +#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + // C99 mode + static_macros const& static_data_c99(std::size_t i) const + { + static static_macros data[] = { + { "__STDC__", T_INTLIT, "1" }, + { "__STDC_VERSION__", T_INTLIT, "199901L" }, + { "__STDC_HOSTED__", T_INTLIT, "0" }, + { "__WAVE_HAS_VARIADICS__", T_INTLIT, "1" }, + { 0, T_EOF, 0 } + }; + BOOST_ASSERT(i < sizeof(data)/sizeof(data[0])); + return data[i]; + } +#endif + + dynamic_macros const& dynamic_data(std::size_t i) const + { + static dynamic_macros data[] = { + { "__DATE__", T_STRINGLIT, &predefined_macros::get_date }, + { "__TIME__", T_STRINGLIT, &predefined_macros::get_time }, + { "__SPIRIT_PP__", T_INTLIT, &predefined_macros::get_version }, + { "__SPIRIT_PP_VERSION__", T_INTLIT, &predefined_macros::get_fullversion }, + { "__SPIRIT_PP_VERSION_STR__", T_STRINGLIT, &predefined_macros::get_versionstr }, + { "__WAVE__", T_INTLIT, &predefined_macros::get_version }, + { "__WAVE_VERSION__", T_INTLIT, &predefined_macros::get_fullversion }, + { "__WAVE_VERSION_STR__", T_STRINGLIT, &predefined_macros::get_versionstr }, + { "__WAVE_CONFIG__", T_INTLIT, &predefined_macros::get_config }, + { 0, T_EOF, 0 } + }; + BOOST_ASSERT(i < sizeof(data)/sizeof(data[0])); + return data[i]; + } + }; // predefined_macros + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(CPP_MACROMAP_PREDEF_HPP_HK041119) diff --git a/boost/wave/util/cpp_macromap_utils.hpp b/boost/wave/util/cpp_macromap_utils.hpp new file mode 100644 index 0000000000..84b5b03de6 --- /dev/null +++ b/boost/wave/util/cpp_macromap_utils.hpp @@ -0,0 +1,575 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + Token sequence analysis and transformation helper functions + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(CPP_MACROMAP_UTIL_HPP_HK041119) +#define CPP_MACROMAP_UTIL_HPP_HK041119 + +#include <boost/assert.hpp> + +#include <boost/wave/wave_config.hpp> +#include <boost/wave/token_ids.hpp> +#include <boost/wave/util/unput_queue_iterator.hpp> + +// this must occur after all of the includes and before any code appears +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_PREFIX +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +// This file contains the definition of several token sequence analyze +// and transformation utility functions needed during macro handling. +// +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +namespace boost { +namespace wave { +namespace util { + +/////////////////////////////////////////////////////////////////////////////// +namespace on_exit { + + /////////////////////////////////////////////////////////////////////////// + // + // On destruction pop the first element of the list given as the argument + // + /////////////////////////////////////////////////////////////////////////// + template <typename ContainerT> + class pop_front { + public: + pop_front(ContainerT &list_) : list(list_) {} + ~pop_front() { list.pop_front(); } + + private: + ContainerT &list; + }; + + /////////////////////////////////////////////////////////////////////////// + // + // Append a given list to the list given as argument + // On destruction pop the first element of the list given as argument + // + /////////////////////////////////////////////////////////////////////////// + template <typename ContainerT> + class splice_pop_front { + public: + splice_pop_front(ContainerT &list_, ContainerT &queue) + : list(list_) + { + list.splice(list.end(), queue); + } + ~splice_pop_front() { list.pop_front(); } + + private: + ContainerT &list; + }; + + /////////////////////////////////////////////////////////////////////////// + // + // On destruction reset a referenced value to its initial state + // + /////////////////////////////////////////////////////////////////////////// + template <typename TypeT> + class reset { + public: + reset(TypeT &target_value_, TypeT new_value) + : target_value(target_value_), old_value(target_value_) + { + target_value_ = new_value; + } + ~reset() { target_value = old_value; } + + private: + TypeT &target_value; + TypeT old_value; + }; + + /////////////////////////////////////////////////////////////////////////// + // + // On destruction assign the given iterator back + // + /////////////////////////////////////////////////////////////////////////// + template <typename IteratorT, typename UnputIteratorT> + class assign + { + public: + assign(IteratorT &it_, UnputIteratorT const &uit_) + : it(it_), uit(uit_) {} + ~assign() { it = uit.base(); } + + private: + IteratorT ⁢ + UnputIteratorT const &uit; + }; + + template <typename IteratorT> + class assign<IteratorT, IteratorT> { + public: + assign(IteratorT &it_, IteratorT const &uit_) + : it(it_), uit(uit_) {} + ~assign() { it = uit; } + + private: + IteratorT ⁢ + IteratorT const &uit; + }; + +/////////////////////////////////////////////////////////////////////////////// +} // namespace on_exit + +/////////////////////////////////////////////////////////////////////////////// +namespace impl { + +/////////////////////////////////////////////////////////////////////////////// +// +// Test, whether a given identifier resolves to a predefined name +// +/////////////////////////////////////////////////////////////////////////////// +template <typename StringT> +inline bool +is_special_macroname (StringT const &name) +{ + if (name.size() < 7) + return false; + + if ("defined" == name) + return true; + + if ('_' == name[0] && '_' == name[1]) { + StringT str = name.substr(2); + + if (str == "cplusplus" || str == "STDC__" || + str == "TIME__" || str == "DATE__" || + str == "LINE__" || str == "FILE__" || + str == "INCLUDE_LEVEL__") + { + return true; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Test, whether two tokens are to be considered equal (different sequences +// of whitespace are considered to be equal) +// +/////////////////////////////////////////////////////////////////////////////// +template <typename TokenT> +inline bool +token_equals(TokenT const &left, TokenT const &right) +{ + using namespace boost::wave; + + if (IS_CATEGORY(left, ParameterTokenType)) { + // if the existing token is of type T_PARAMETERBASE, then the right token + // must be of type T_IDENTIFIER or a keyword + token_id id = token_id(right); + + return (T_IDENTIFIER == id || + IS_CATEGORY(id, KeywordTokenType) || + IS_EXTCATEGORY(id, OperatorTokenType|AltExtTokenType) || + IS_CATEGORY(id, BoolLiteralTokenType)) && + left.get_value() == right.get_value(); + } + + // if the left token has whitespace, the value is irrelevant + return token_id(left) == token_id(right) && ( + IS_CATEGORY(left, WhiteSpaceTokenType) || + left.get_value() == right.get_value() + ); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Tests, whether two macro definitions are equal +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContainerT> +inline bool +definition_equals(ContainerT const &definition, + ContainerT const &new_definition) +{ + typedef typename ContainerT::const_iterator const_iterator_type; + +const_iterator_type first1 = definition.begin(); +const_iterator_type last1 = definition.end(); +const_iterator_type first2 = new_definition.begin(); +const_iterator_type last2 = new_definition.end(); + + while (first1 != last1 && first2 != last2 && token_equals(*first1, *first2)) + { + // skip whitespace, if both sequences have a whitespace next + token_id id1 = next_token<const_iterator_type>::peek(first1, last1, false); + token_id id2 = next_token<const_iterator_type>::peek(first2, last2, false); + + if (IS_CATEGORY(id1, WhiteSpaceTokenType) && + IS_CATEGORY(id2, WhiteSpaceTokenType)) + { + // all consecutive whitespace tokens count as one whitespace + // adjust first1 and first2 accordingly + skip_whitespace(first1, last1); + skip_whitespace(first2, last2); + } + else if (!IS_CATEGORY(id1, WhiteSpaceTokenType) && + !IS_CATEGORY(id2, WhiteSpaceTokenType)) + { + ++first1; + ++first2; + } + else { + // the sequences differ + break; + } + } + return (first1 == last1 && first2 == last2) ? true : false; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Tests, whether two given sets of macro parameters are equal +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContainerT> +inline bool +parameters_equal(ContainerT const ¶meters, ContainerT const &new_parameters) +{ + if (parameters.size() != new_parameters.size()) + return false; // different parameter count + + typedef typename ContainerT::const_iterator const_iterator_type; + +const_iterator_type first1 = parameters.begin(); +const_iterator_type last1 = parameters.end(); +const_iterator_type first2 = new_parameters.begin(); +const_iterator_type last2 = new_parameters.end(); + + while (first1 != last1 && first2 != last2) { + // parameters are different, if the corresponding tokens are different + using namespace boost::wave; + if (token_id(*first1) != token_id(*first2) || + (*first1).get_value() != (*first2).get_value()) + { + break; + } + ++first1; + ++first2; + } + return (first1 == last1 && first2 == last2) ? true : false; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Strip leading and trailing whitespace from the given token sequence +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContainerT> +inline void +trim_replacement_list (ContainerT &replacement_list) +{ + using namespace boost::wave; + +// strip leading whitespace + if (replacement_list.size() > 0) { + typename ContainerT::iterator end = replacement_list.end(); + typename ContainerT::iterator it = replacement_list.begin(); + + while (it != end && IS_CATEGORY(*it, WhiteSpaceTokenType)) { + token_id id(*it); + if (T_PLACEHOLDER != id && T_PLACEMARKER != id) { + typename ContainerT::iterator next = it; + ++next; + replacement_list.erase(it); + it = next; + } + else { + ++it; + } + } + } + +// strip trailing whitespace + if (replacement_list.size() > 0) { + typename ContainerT::reverse_iterator rend = replacement_list.rend(); + typename ContainerT::reverse_iterator rit = replacement_list.rbegin(); + + while (rit != rend && IS_CATEGORY(*rit, WhiteSpaceTokenType)) + ++rit; + + typename ContainerT::iterator end = replacement_list.end(); + typename ContainerT::iterator it = rit.base(); + + while (it != end && IS_CATEGORY(*it, WhiteSpaceTokenType)) { + token_id id(*it); + if (T_PLACEHOLDER != id && T_PLACEMARKER != id) { + typename ContainerT::iterator next = it; + ++next; + replacement_list.erase(it); + it = next; + } + else { + ++it; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Tests, whether the given token sequence consists out of whitespace only +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContainerT> +inline bool +is_whitespace_only (ContainerT const &argument) +{ + typename ContainerT::const_iterator end = argument.end(); + for (typename ContainerT::const_iterator it = argument.begin(); + it != end; ++it) + { + if (!IS_CATEGORY(*it, WhiteSpaceTokenType)) + return false; + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Remove all placeholder tokens from the given token sequence +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContainerT> +inline void +remove_placeholders (ContainerT &replacement_list) +{ + using namespace boost::wave; + +// strip leading whitespace + if (replacement_list.size() > 0) { + typename ContainerT::iterator end = replacement_list.end(); + typename ContainerT::iterator it = replacement_list.begin(); + + while (it != end) { + token_id id(*it); + if (T_PLACEHOLDER == id || T_PLACEMARKER == id) { + typename ContainerT::iterator next = it; + ++next; + replacement_list.erase(it); + it = next; + } + else { + ++it; + } + } + + // remove all 'new' leading and trailing whitespace + if (is_whitespace_only(replacement_list)) + trim_replacement_list(replacement_list); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Remove all whitespace tokens on the left side of the given token sequence +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContainerT> +inline void +trim_sequence_left (ContainerT &argument) +{ + using namespace boost::wave; + +// strip leading whitespace (should be only one token) + if (argument.size() > 0 && + IS_CATEGORY(argument.front(), WhiteSpaceTokenType)) + { + argument.pop_front(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Remove all whitespace tokens on the right side of the given token sequence +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContainerT> +inline void +trim_sequence_right (ContainerT &argument) +{ + using namespace boost::wave; + +// strip trailing whitespace (should be only one token) + if (argument.size() > 0 && + IS_CATEGORY(argument.back(), WhiteSpaceTokenType)) + { + argument.pop_back(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Remove all whitespace tokens on the left and right sides of the given token +// sequence +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContainerT> +inline void +trim_sequence (ContainerT &argument) +{ + trim_sequence_left(argument); + trim_sequence_right(argument); +} + +/////////////////////////////////////////////////////////////////////////////// +// call 'skipped_token' preprocessing hook +template <typename ContextT> +void call_skipped_token_hook(ContextT& ctx, + typename ContextT::token_type const& skipped) +{ +#if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 + ctx.get_hooks().skipped_token(skipped); +#else + ctx.get_hooks().skipped_token(ctx.derived(), skipped); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Skip forward to a given token +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT, typename IteratorT> +inline bool +skip_to_token(ContextT& ctx, IteratorT &it, IteratorT const &end, + token_id id, bool& seen_newline) +{ + using namespace boost::wave; + if (token_id(*it) == id) + return true; + +// call_skipped_token_hook(ctx, *it); + if (++it == end) + return false; + + while (IS_CATEGORY(*it, WhiteSpaceTokenType) || + T_NEWLINE == token_id(*it)) + { + if (T_NEWLINE == token_id(*it)) + seen_newline = true; + +// call_skipped_token_hook(ctx, *it); + if (++it == end) + return false; + } + return token_id(*it) == id; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Get the full name of a given macro name (concatenate the string +// representations of the single tokens). +// +/////////////////////////////////////////////////////////////////////////////// +template <typename IteratorT> +inline std::string +get_full_name(IteratorT const &begin, IteratorT const &end) +{ + std::string full_name; + for (IteratorT err_it = begin; err_it != end; ++err_it) + full_name += (*err_it).get_value().c_str(); + + return full_name; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// The following predicate is used in conjunction with the remove_copy_if +// algorithm to allow the detection of an eventually copied operator ##. +// No removal is performed in any case. +// +/////////////////////////////////////////////////////////////////////////////// +class find_concat_operator { +public: + find_concat_operator(bool &found_) : found_concat(found_) {} + + template <typename TokenT> + bool operator()(TokenT const &tok) + { + using namespace boost::wave; + if (T_POUND_POUND == BASE_TOKEN(token_id(tok))) + found_concat = true; + return false; + } + +private: + bool &found_concat; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Convert a string of an arbitrary string compatible type to a internal +// string (BOOST_WAVE_STRING) +template <typename Target, typename Src> +struct to_string_helper +{ + typedef Target type; + + static Target call(Src const& str) + { + return Target(str.c_str()); + } +}; + +// do nothing if types are equal +template <typename Src> +struct to_string_helper<Src, Src> +{ + typedef Src const& type; + + static Src const& call(Src const& str) + { + return str; + } +}; + +template <typename Target> +struct to_string_helper<Target, char const*> +{ + typedef Target type; + + static Target call(char const* str) + { + return Target(str); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +} // namespace impl + +template <typename Target, typename Src> +inline typename impl::to_string_helper<Target, Src>::type +to_string(Src const& src) +{ + return impl::to_string_helper<Target, Src>::call(src); +} + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(CPP_MACROMAP_UTIL_HPP_HK041119) diff --git a/boost/wave/util/file_position.hpp b/boost/wave/util/file_position.hpp new file mode 100644 index 0000000000..87f6f77375 --- /dev/null +++ b/boost/wave/util/file_position.hpp @@ -0,0 +1,195 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + Definition of the position_iterator and file_position templates + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(FILE_POSITION_H_52BDEDF7_DAD3_4F24_802F_E66BB8098F68_INCLUDED) +#define FILE_POSITION_H_52BDEDF7_DAD3_4F24_802F_E66BB8098F68_INCLUDED + +#include <string> +#include <ostream> + +#include <boost/assert.hpp> +#include <boost/spirit/include/classic_version.hpp> +#include <boost/spirit/include/classic_position_iterator.hpp> +#include <boost/wave/wave_config.hpp> +#if BOOST_WAVE_SERIALIZATION != 0 +#include <boost/serialization/serialization.hpp> +#endif + +// 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 { + +/////////////////////////////////////////////////////////////////////////////// +// +// file_position +// +// A structure to hold positional information. This includes the filename, +// line number and column number of a current token position. +// +/////////////////////////////////////////////////////////////////////////////// + +template <typename StringT> +struct file_position { + +public: + typedef StringT string_type; + + file_position() + : file(), line(1), column(1) + {} + explicit file_position(string_type const& file_, std::size_t line_ = 1, + std::size_t column_ = 1) + : file(file_), line(line_), column(column_) + {} + +// accessors + string_type const &get_file() const { return file; } + std::size_t get_line() const { return line; } + std::size_t get_column() const { return column; } + + void set_file(string_type const &file_) + { + file = file_; + } + void set_line(std::size_t line_) { line = line_; } + void set_column(std::size_t column_) { column = column_; } + +private: +#if BOOST_WAVE_SERIALIZATION != 0 + friend class boost::serialization::access; + template<typename Archive> + void serialize(Archive &ar, const unsigned int version) + { + using namespace boost::serialization; + ar & make_nvp("filename", file); + ar & make_nvp("line", line); + ar & make_nvp("column", column); + } +#endif + + string_type file; + std::size_t line; + std::size_t column; +}; + +template <typename StringT> +bool operator== (file_position<StringT> const &lhs, + file_position<StringT> const &rhs) +{ + return lhs.get_column() == rhs.get_column() && + lhs.get_line() == rhs.get_line() && lhs.get_file() == rhs.get_file(); +} + +template <typename StringT> +inline std::ostream & +operator<< (std::ostream &o, file_position<StringT> const &pos) +{ + o << pos.get_file() << ":" << pos.get_line() << ":" << pos.get_column(); + return o; +} + +typedef file_position<BOOST_WAVE_STRINGTYPE> file_position_type; + +/////////////////////////////////////////////////////////////////////////////// +// +// position_iterator +// +// The position_iterator used by Wave is now based on the corresponding Spirit +// type. This type is used with our own file_position though. The needed +// specialization of the boost::spirit::classic::position_policy class is +// provided below. +// +/////////////////////////////////////////////////////////////////////////////// + +template <typename IteratorT, typename PositionT> +struct position_iterator +: boost::spirit::classic::position_iterator<IteratorT, PositionT> +{ + typedef boost::spirit::classic::position_iterator<IteratorT, PositionT> base_type; + + position_iterator() + { + } + + position_iterator(IteratorT const &begin, IteratorT const &end, + PositionT const &pos) + : base_type(begin, end, pos) + { + } +}; + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave + +/////////////////////////////////////////////////////////////////////////////// + +namespace spirit { namespace classic { + +/////////////////////////////////////////////////////////////////////////////// +// +// The boost::spirit::classic::position_policy has to be specialized for our +// file_position class +// +/////////////////////////////////////////////////////////////////////////////// + + template <> + class position_policy<boost::wave::util::file_position_type> { + + public: + position_policy() + : m_CharsPerTab(4) + {} + + void next_line(boost::wave::util::file_position_type &pos) + { + pos.set_line(pos.get_line() + 1); + pos.set_column(1); + } + + void set_tab_chars(unsigned int chars) + { + m_CharsPerTab = chars; + } + + void next_char(boost::wave::util::file_position_type &pos) + { + pos.set_column(pos.get_column() + 1); + } + + void tabulation(boost::wave::util::file_position_type &pos) + { + pos.set_column(pos.get_column() + m_CharsPerTab - + (pos.get_column() - 1) % m_CharsPerTab); + } + + private: + unsigned int m_CharsPerTab; + }; + +/////////////////////////////////////////////////////////////////////////////// +}} // namespace spirit::classic + +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(FILE_POSITION_H_52BDEDF7_DAD3_4F24_802F_E66BB8098F68_INCLUDED) diff --git a/boost/wave/util/filesystem_compatibility.hpp b/boost/wave/util/filesystem_compatibility.hpp new file mode 100644 index 0000000000..5bd924af72 --- /dev/null +++ b/boost/wave/util/filesystem_compatibility.hpp @@ -0,0 +1,172 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(BOOST_WAVE_FILESYSTEM_COMPATIBILITY_MAR_09_2009_0142PM) +#define BOOST_WAVE_FILESYSTEM_COMPATIBILITY_MAR_09_2009_0142PM + +#include <string> + +#include <boost/version.hpp> +#include <boost/filesystem/path.hpp> +#include <boost/filesystem/operations.hpp> + +namespace boost { namespace wave { namespace util +{ +/////////////////////////////////////////////////////////////////////////////// +// filesystem wrappers allowing to handle different Boost versions +#if !defined(BOOST_FILESYSTEM_NO_DEPRECATED) +// interface wrappers for older Boost versions + inline boost::filesystem::path initial_path() + { + return boost::filesystem::initial_path(); + } + + inline boost::filesystem::path current_path() + { + return boost::filesystem::current_path(); + } + + template <typename String> + inline boost::filesystem::path create_path(String const& p) + { +#if BOOST_FILESYSTEM_VERSION >= 3 + return boost::filesystem::path(p); +#else + return boost::filesystem::path(p, boost::filesystem::native); +#endif + } + + inline std::string leaf(boost::filesystem::path const& p) + { +#if BOOST_FILESYSTEM_VERSION >= 3 + return p.leaf().string(); +#else + return p.leaf(); +#endif + } + + inline boost::filesystem::path branch_path(boost::filesystem::path const& p) + { + return p.branch_path(); + } + + inline boost::filesystem::path normalize(boost::filesystem::path& p) + { + return p.normalize(); + } + + inline std::string native_file_string(boost::filesystem::path const& p) + { +#if BOOST_FILESYSTEM_VERSION >= 3 + return p.string(); +#else + return p.native_file_string(); +#endif + } + + inline boost::filesystem::path complete_path( + boost::filesystem::path const& p) + { +#if BOOST_FILESYSTEM_VERSION >= 3 + return boost::filesystem3::complete(p, initial_path()); +#else + return boost::filesystem::complete(p, initial_path()); +#endif + } + + inline boost::filesystem::path complete_path( + boost::filesystem::path const& p, boost::filesystem::path const& base) + { +#if BOOST_FILESYSTEM_VERSION >= 3 + return boost::filesystem3::complete(p, base); +#else + return boost::filesystem::complete(p, base); +#endif + } + +#else + +// interface wrappers if deprecated functions do not exist + inline boost::filesystem::path initial_path() + { +#if BOOST_FILESYSTEM_VERSION >= 3 + return boost::filesystem3::detail::initial_path(); +#else + return boost::filesystem::initial_path<boost::filesystem::path>(); +#endif + } + + inline boost::filesystem::path current_path() + { +#if BOOST_FILESYSTEM_VERSION >= 3 + return boost::filesystem3::current_path(); +#else + return boost::filesystem::current_path<boost::filesystem::path>(); +#endif + } + + template <typename String> + inline boost::filesystem::path create_path(String const& p) + { + return boost::filesystem::path(p); + } + + inline std::string leaf(boost::filesystem::path const& p) + { +#if BOOST_VERSION >= 104600 && BOOST_FILESYSTEM_VERSION >= 3 + return p.filename().string(); +#else + return p.filename(); +#endif + } + + inline boost::filesystem::path branch_path(boost::filesystem::path const& p) + { + return p.parent_path(); + } + + inline boost::filesystem::path normalize(boost::filesystem::path& p) + { + return p; // function doesn't exist anymore + } + + inline std::string native_file_string(boost::filesystem::path const& p) + { +#if BOOST_VERSION >= 104600 + return p.string(); +#else + return p.file_string(); +#endif + } + + inline boost::filesystem::path complete_path( + boost::filesystem::path const& p) + { +#if BOOST_VERSION >= 104600 && BOOST_FILESYSTEM_VERSION >= 3 + return boost::filesystem::absolute(p, initial_path()); +#else + return boost::filesystem::complete(p, initial_path()); +#endif + } + + inline boost::filesystem::path complete_path( + boost::filesystem::path const& p, boost::filesystem::path const& base) + { +#if BOOST_VERSION >= 104600 && BOOST_FILESYSTEM_VERSION >= 3 + return boost::filesystem::absolute(p, base); +#else + return boost::filesystem::complete(p, base); +#endif + } +#endif + +}}} + +#endif diff --git a/boost/wave/util/flex_string.hpp b/boost/wave/util/flex_string.hpp new file mode 100644 index 0000000000..da16235676 --- /dev/null +++ b/boost/wave/util/flex_string.hpp @@ -0,0 +1,2672 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + http://www.boost.org/ + + Copyright (c) 2001 by Andrei Alexandrescu. 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) +=============================================================================*/ + +// This code is taken from: +// Andrei Alexandrescu, Generic<Programming>: A Policy-Based basic_string +// Implementation. http://www.cuj.com/documents/s=7994/cujcexp1906alexandr/ +// +// #HK030306: +// - Moved into the namespace boost::wave::util +// - Added a bunch of missing typename(s) +// - Integrated with boost config +// - Added a missing header include +// - Added special constructors and operator= to allow CowString to be +// a real COW-string (removed unnecessary data copying) +// - Fixed a string terminating bug in append +// +// #HK040109: +// - Incorporated the changes from Andrei's latest version of this class +// +// #HK070307: +// - Once again incorporated the changes from Andrei's latest version of +// this class +// +// #HK090523: +// - Incorporated the changes from latest version of flex_string as +// maintained in Loki + +#ifndef FLEX_STRING_INC_ +#define FLEX_STRING_INC_ + +/* +//////////////////////////////////////////////////////////////////////////////// +template <typename E, class A = @> +class StoragePolicy +{ + typedef E value_type; + typedef @ iterator; + typedef @ const_iterator; + typedef A allocator_type; + typedef @ size_type; + + StoragePolicy(const StoragePolicy& s); + StoragePolicy(const A&); + StoragePolicy(const E* s, size_type len, const A&); + StoragePolicy(size_type len, E c, const A&); + ~StoragePolicy(); + + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + + size_type size() const; + size_type max_size() const; + size_type capacity() const; + + void reserve(size_type res_arg); + + void append(const E* s, size_type sz); + + template <class InputIterator> + void append(InputIterator b, InputIterator e); + + void resize(size_type newSize, E fill); + + void swap(StoragePolicy& rhs); + + const E* c_str() const; + const E* data() const; + + A get_allocator() const; +}; +//////////////////////////////////////////////////////////////////////////////// +*/ + +#include <boost/config.hpp> +#include <boost/assert.hpp> +#include <boost/throw_exception.hpp> + +#include <boost/iterator/reverse_iterator.hpp> + +#include <boost/wave/wave_config.hpp> +#if BOOST_WAVE_SERIALIZATION != 0 +#include <boost/serialization/serialization.hpp> +#include <boost/serialization/split_free.hpp> +#include <boost/serialization/collections_save_imp.hpp> +#include <boost/serialization/collections_load_imp.hpp> +#define BOOST_WAVE_FLEX_STRING_SERIALIZATION_HACK 1 +#endif + +#include <memory> +#include <new> +#include <string> +#include <vector> +#include <algorithm> +#include <functional> +#include <limits> +#include <stdexcept> +#include <ios> + +#include <cstddef> +#include <cstring> +#include <cstdlib> + +// 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 { + +namespace flex_string_details +{ + template <class InIt, class OutIt> + OutIt copy_n(InIt b, + typename std::iterator_traits<InIt>::difference_type n, OutIt d) + { + for (/**/; n != 0; --n, ++b, ++d) + { + *d = *b; + } + return d; + } + + template <class Pod, class T> + inline void pod_fill(Pod* b, Pod* e, T c) + { + switch ((e - b) & 7) + { + case 0: + while (b != e) + { + *b = c; ++b; + case 7: *b = c; ++b; + case 6: *b = c; ++b; + case 5: *b = c; ++b; + case 4: *b = c; ++b; + case 3: *b = c; ++b; + case 2: *b = c; ++b; + case 1: *b = c; ++b; + } + } + } + + template <class Pod> + inline void pod_move(const Pod* b, const Pod* e, Pod* d) + { + using namespace std; + memmove(d, b, (e - b) * sizeof(*b)); + } + + template <class Pod> + inline Pod* pod_copy(const Pod* b, const Pod* e, Pod* d) + { + const std::size_t s = e - b; + using namespace std; + memcpy(d, b, s * sizeof(*b)); + return d + s; + } + + template <typename T> struct get_unsigned + { + typedef T result; + }; + + template <> struct get_unsigned<char> + { + typedef unsigned char result; + }; + + template <> struct get_unsigned<signed char> + { + typedef unsigned char result; + }; + + template <> struct get_unsigned<short int> + { + typedef unsigned short int result; + }; + + template <> struct get_unsigned<int> + { + typedef unsigned int result; + }; + + template <> struct get_unsigned<long int> + { + typedef unsigned long int result; + }; + + enum Shallow {}; +} + +template <class T> class mallocator +{ +public: + typedef T value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef std::size_t size_type; + //typedef unsigned int size_type; + //typedef std::ptrdiff_t difference_type; + typedef int difference_type; + + template <class U> + struct rebind { typedef mallocator<U> other; }; + + mallocator() {} + mallocator(const mallocator&) {} + //template <class U> + //mallocator(const mallocator<U>&) {} + ~mallocator() {} + + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const + { + return x; + } + + pointer allocate(size_type n, const_pointer = 0) + { + using namespace std; + void* p = malloc(n * sizeof(T)); + if (!p) boost::throw_exception(std::bad_alloc()); + return static_cast<pointer>(p); + } + + void deallocate(pointer p, size_type) + { + using namespace std; + free(p); + } + + size_type max_size() const + { + return static_cast<size_type>(-1) / sizeof(T); + } + + void construct(pointer p, const value_type& x) + { + new(p) value_type(x); + } + + void destroy(pointer p) + { + p->~value_type(); + } + +private: + void operator=(const mallocator&); +}; + +template<> class mallocator<void> +{ + typedef void value_type; + typedef void* pointer; + typedef const void* const_pointer; + + template <class U> + struct rebind { typedef mallocator<U> other; }; +}; + +template <class T> +inline bool operator==(const mallocator<T>&, + const mallocator<T>&) { + return true; +} + +template <class T> +inline bool operator!=(const mallocator<T>&, + const mallocator<T>&) { + return false; +} + +template <class Allocator> +typename Allocator::pointer Reallocate( + Allocator& alloc, + typename Allocator::pointer p, + typename Allocator::size_type oldObjCount, + typename Allocator::size_type newObjCount, + void*) +{ + // @@@ not implemented + return NULL; +} + +template <class Allocator> +typename Allocator::pointer Reallocate( + Allocator& alloc, + typename Allocator::pointer p, + typename Allocator::size_type oldObjCount, + typename Allocator::size_type newObjCount, + mallocator<void>*) +{ + // @@@ not implemented + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// class template SimpleStringStorage +// Allocates memory with malloc +//////////////////////////////////////////////////////////////////////////////// + +template <typename E, class A = std::allocator<E> > +class SimpleStringStorage +{ + // The "public" below exists because MSVC can't do template typedefs +public: + struct Data + { + Data() : pEnd_(buffer_), pEndOfMem_(buffer_) { buffer_[0] = E(0); } + + E* pEnd_; + E* pEndOfMem_; + E buffer_[1]; + }; + static const Data emptyString_; + + typedef typename A::size_type size_type; + +private: + Data* pData_; + + void Init(size_type size, size_type capacity) + { + BOOST_ASSERT(size <= capacity); + if (capacity == 0) + { + pData_ = const_cast<Data*>(&emptyString_); + } + else + { + // 11-17-2000: comment added: + // No need to allocate (capacity + 1) to + // accommodate the terminating 0, because Data already + // has one one character in there + pData_ = static_cast<Data*>( + malloc(sizeof(Data) + capacity * sizeof(E))); + if (!pData_) boost::throw_exception(std::bad_alloc()); + pData_->pEnd_ = pData_->buffer_ + size; + pData_->pEndOfMem_ = pData_->buffer_ + capacity; + } + } + +private: + // Warning - this doesn't initialize pData_. Used in reserve() + SimpleStringStorage() + { } + +public: + typedef E value_type; + typedef E* iterator; + typedef const E* const_iterator; + typedef A allocator_type; + + SimpleStringStorage(const SimpleStringStorage& rhs) + { + const size_type sz = rhs.size(); + Init(sz, sz); + if (sz) flex_string_details::pod_copy(rhs.begin(), rhs.end(), begin()); + } + + SimpleStringStorage(const SimpleStringStorage& s, + flex_string_details::Shallow) + : pData_(s.pData_) + { + } + + SimpleStringStorage(const A&) + { pData_ = const_cast<Data*>(&emptyString_); } + + SimpleStringStorage(const E* s, size_type len, const A&) + { + Init(len, len); + flex_string_details::pod_copy(s, s + len, begin()); + } + + SimpleStringStorage(size_type len, E c, const A&) + { + Init(len, len); + flex_string_details::pod_fill(begin(), end(), c); + } + + SimpleStringStorage& operator=(const SimpleStringStorage& rhs) + { + const size_type sz = rhs.size(); + reserve(sz); + flex_string_details::pod_copy(&*rhs.begin(), &*rhs.end(), begin()); + pData_->pEnd_ = &*begin() + sz; + return *this; + } + + ~SimpleStringStorage() + { + BOOST_ASSERT(begin() <= end()); + if (pData_ != &emptyString_) free(pData_); + } + + iterator begin() + { return pData_->buffer_; } + + const_iterator begin() const + { return pData_->buffer_; } + + iterator end() + { return pData_->pEnd_; } + + const_iterator end() const + { return pData_->pEnd_; } + + size_type size() const + { return pData_->pEnd_ - pData_->buffer_; } + + size_type max_size() const + { return std::size_t(-1) / sizeof(E) - sizeof(Data) - 1; } + + size_type capacity() const + { return pData_->pEndOfMem_ - pData_->buffer_; } + + void reserve(size_type res_arg) + { + if (res_arg <= capacity()) + { + // @@@ insert shrinkage here if you wish + return; + } + + if (pData_ == &emptyString_) + { + Init(0, res_arg); + } + else + { + const size_type sz = size(); + + void* p = realloc(pData_, + sizeof(Data) + res_arg * sizeof(E)); + if (!p) boost::throw_exception(std::bad_alloc()); + + if (p != pData_) + { + pData_ = static_cast<Data*>(p); + pData_->pEnd_ = pData_->buffer_ + sz; + } + pData_->pEndOfMem_ = pData_->buffer_ + res_arg; + } + } + + void append(const E* s, size_type sz) + { + const size_type neededCapacity = size() + sz; + + if (capacity() < neededCapacity) + { + const iterator b = begin(); + static std::less_equal<const E*> le; + if (le(b, s) && le(s, end())) + { + // aliased + const size_type offset = s - b; + reserve(neededCapacity); + s = begin() + offset; + } + else + { + reserve(neededCapacity); + } + } + flex_string_details::pod_copy(s, s + sz, end()); + pData_->pEnd_ += sz; + } + + template <class InputIterator> + void append(InputIterator b, InputIterator e) + { + // @@@ todo: optimize this depending on iterator type + for (; b != e; ++b) + { + *this += *b; + } + } + + void resize(size_type newSize, E fill) + { + const int delta = int(newSize - size()); + if (delta == 0) return; + + if (delta > 0) + { + if (newSize > capacity()) + { + reserve(newSize); + } + E* e = &*end(); + flex_string_details::pod_fill(e, e + delta, fill); + } + pData_->pEnd_ = pData_->buffer_ + newSize; + } + + void swap(SimpleStringStorage& rhs) + { + std::swap(pData_, rhs.pData_); + } + + const E* c_str() const + { + if (pData_ != &emptyString_) *pData_->pEnd_ = E(); + return pData_->buffer_; + } + + const E* data() const + { return pData_->buffer_; } + + A get_allocator() const + { return A(); } +}; + +template <typename E, class A> +const typename SimpleStringStorage<E, A>::Data +SimpleStringStorage<E, A>::emptyString_ = + typename SimpleStringStorage<E, A>::Data(); + +//////////////////////////////////////////////////////////////////////////////// +// class template AllocatorStringStorage +// Allocates with your allocator +// Takes advantage of the Empty Base Optimization if available +//////////////////////////////////////////////////////////////////////////////// + +template <typename E, class A = std::allocator<E> > +class AllocatorStringStorage : public A +{ + typedef typename A::size_type size_type; + typedef typename SimpleStringStorage<E, A>::Data Data; + + void* Alloc(size_type sz, const void* p = 0) + { + return A::allocate(1 + (sz - 1) / sizeof(E), + static_cast<const char*>(p)); + } + + void* Realloc(void* p, size_type oldSz, size_type newSz) + { + void* r = Alloc(newSz); + flex_string_details::pod_copy(p, p + Min(oldSz, newSz), r); + Free(p, oldSz); + return r; + } + + void Free(void* p, size_type sz) + { + A::deallocate(static_cast<E*>(p), sz); + } + + Data* pData_; + + void Init(size_type size, size_type cap) + { + BOOST_ASSERT(size <= cap); + + if (cap == 0) + { + pData_ = const_cast<Data*>( + &SimpleStringStorage<E, A>::emptyString_); + } + else + { + pData_ = static_cast<Data*>(Alloc( + cap * sizeof(E) + sizeof(Data))); + pData_->pEnd_ = pData_->buffer_ + size; + pData_->pEndOfMem_ = pData_->buffer_ + cap; + } + } + +public: + typedef E value_type; + typedef E* iterator; + typedef const E* const_iterator; + typedef A allocator_type; + + AllocatorStringStorage() + : A(), pData_(0) + { + } + + AllocatorStringStorage(const AllocatorStringStorage& rhs) + : A(rhs.get_allocator()) + { + const size_type sz = rhs.size(); + Init(sz, sz); + if (sz) flex_string_details::pod_copy(rhs.begin(), rhs.end(), begin()); + } + + AllocatorStringStorage(const AllocatorStringStorage& s, + flex_string_details::Shallow) + : A(s.get_allocator()) + { + pData_ = s.pData_; + } + + AllocatorStringStorage(const A& a) : A(a) + { + pData_ = const_cast<Data*>( + &SimpleStringStorage<E, A>::emptyString_); + } + + AllocatorStringStorage(const E* s, size_type len, const A& a) + : A(a) + { + Init(len, len); + flex_string_details::pod_copy(s, s + len, begin()); + } + + AllocatorStringStorage(size_type len, E c, const A& a) + : A(a) + { + Init(len, len); + flex_string_details::pod_fill(&*begin(), &*end(), c); + } + + AllocatorStringStorage& operator=(const AllocatorStringStorage& rhs) + { + const size_type sz = rhs.size(); + reserve(sz); + flex_string_details::pod_copy(&*rhs.begin(), &*rhs.end(), begin()); + pData_->pEnd_ = &*begin() + rhs.size(); + return *this; + } + + ~AllocatorStringStorage() + { + if (capacity()) + { + Free(pData_, + sizeof(Data) + capacity() * sizeof(E)); + } + } + + iterator begin() + { return pData_->buffer_; } + + const_iterator begin() const + { return pData_->buffer_; } + + iterator end() + { return pData_->pEnd_; } + + const_iterator end() const + { return pData_->pEnd_; } + + size_type size() const + { return size_type(end() - begin()); } + + size_type max_size() const + { return A::max_size(); } + + size_type capacity() const + { return size_type(pData_->pEndOfMem_ - pData_->buffer_); } + + void resize(size_type n, E c) + { + reserve(n); + iterator newEnd = begin() + n; + iterator oldEnd = end(); + if (newEnd > oldEnd) + { + // Copy the characters + flex_string_details::pod_fill(oldEnd, newEnd, c); + } + if (capacity()) pData_->pEnd_ = newEnd; + } + + void reserve(size_type res_arg) + { + if (res_arg <= capacity()) + { + // @@@ shrink to fit here + return; + } + + A& myAlloc = *this; + AllocatorStringStorage newStr(myAlloc); + newStr.Init(size(), res_arg); + + flex_string_details::pod_copy(begin(), end(), newStr.begin()); + + swap(newStr); + } + + template <class ForwardIterator> + void append(ForwardIterator b, ForwardIterator e) + { + const size_type + sz = std::distance(b, e), + neededCapacity = size() + sz; + + if (capacity() < neededCapacity) + { +// typedef std::less_equal<const E*> le_type; +// BOOST_ASSERT(!(le_type()(begin(), &*b) && le_type()(&*b, end()))); + reserve(neededCapacity); + } + std::copy(b, e, end()); + pData_->pEnd_ += sz; + } + + void swap(AllocatorStringStorage& rhs) + { + // @@@ The following line is commented due to a bug in MSVC + //std::swap(lhsAlloc, rhsAlloc); + std::swap(pData_, rhs.pData_); + } + + const E* c_str() const + { + if (pData_ != &SimpleStringStorage<E, A>::emptyString_) + { + *pData_->pEnd_ = E(); + } + return &*begin(); + } + + const E* data() const + { return &*begin(); } + + A get_allocator() const + { return *this; } +}; + +//////////////////////////////////////////////////////////////////////////////// +// class template VectorStringStorage +// Uses std::vector +// Takes advantage of the Empty Base Optimization if available +//////////////////////////////////////////////////////////////////////////////// + +template <typename E, class A = std::allocator<E> > +class VectorStringStorage : protected std::vector<E, A> +{ + typedef std::vector<E, A> base; + +public: // protected: + typedef E value_type; + typedef typename base::iterator iterator; + typedef typename base::const_iterator const_iterator; + typedef A allocator_type; + typedef typename A::size_type size_type; + + VectorStringStorage(const VectorStringStorage& s) : base(s) + { } + + VectorStringStorage(const A& a) : base(1, E(), a) + { } + + VectorStringStorage(const E* s, size_type len, const A& a) + : base(a) + { + base::reserve(len + 1); + base::insert(base::end(), s, s + len); + // Terminating zero + base::insert(base::end(), E()); + } + + VectorStringStorage(size_type len, E c, const A& a) + : base(len + 1, c, a) + { + // Terminating zero + base::back() = E(); + } + + VectorStringStorage& operator=(const VectorStringStorage& rhs) + { + base& v = *this; + v = rhs; + return *this; + } + + iterator begin() + { return base::begin(); } + + const_iterator begin() const + { return base::begin(); } + + iterator end() + { return base::end() - 1; } + + const_iterator end() const + { return base::end() - 1; } + + size_type size() const + { return base::size() - 1; } + + size_type max_size() const + { return base::max_size() - 1; } + + size_type capacity() const + { return base::capacity() - 1; } + + void reserve(size_type res_arg) + { + BOOST_ASSERT(res_arg < max_size()); + base::reserve(res_arg + 1); + } + + void append(const E* s, size_type sz) + { + // Check for aliasing because std::vector doesn't do it. + static std::less_equal<const E*> le; + if (!base::empty()) + { + const E* start = &base::front(); + if (le(start, s) && le(s, start + size())) + { + // aliased + const size_type offset = s - start; + reserve(size() + sz); + s = &base::front() + offset; + } + } + base::insert(end(), s, s + sz); + } + + template <class InputIterator> + void append(InputIterator b, InputIterator e) + { + base::insert(end(), b, e); + } + + void resize(size_type n, E c) + { + base::reserve(n + 1); + base::back() = c; + base::resize(n + 1, c); + base::back() = E(); + } + + void swap(VectorStringStorage& rhs) + { base::swap(rhs); } + + const E* c_str() const + { return &*begin(); } + + const E* data() const + { return &*begin(); } + + A get_allocator() const + { return base::get_allocator(); } +}; + +//////////////////////////////////////////////////////////////////////////////// +// class template SmallStringOpt +// Builds the small string optimization over any other storage +//////////////////////////////////////////////////////////////////////////////// + +template <class Storage, unsigned int threshold, + typename Align = typename Storage::value_type*> +class SmallStringOpt +{ +public: + typedef typename Storage::value_type value_type; + typedef value_type* iterator; + typedef const value_type* const_iterator; + typedef typename Storage::allocator_type allocator_type; + typedef typename allocator_type::size_type size_type; + +private: + enum { temp1 = threshold * sizeof(value_type) > sizeof(Storage) + ? threshold * sizeof(value_type) + : sizeof(Storage) }; + + enum { temp2 = temp1 > sizeof(Align) ? temp1 : sizeof(Align) }; + +public: + enum { maxSmallString = + (temp2 + sizeof(value_type) - 1) / sizeof(value_type) }; + +private: + enum { magic = maxSmallString + 1 }; + + union + { + mutable value_type buf_[maxSmallString + 1]; + Align align_; + }; + + Storage& GetStorage() + { + BOOST_ASSERT(buf_[maxSmallString] == magic); + Storage* p = reinterpret_cast<Storage*>(&buf_[0]); + return *p; + } + + const Storage& GetStorage() const + { + BOOST_ASSERT(buf_[maxSmallString] == magic); + const Storage *p = reinterpret_cast<const Storage*>(&buf_[0]); + return *p; + } + + bool Small() const + { + return buf_[maxSmallString] != magic; + } + +public: + SmallStringOpt(const SmallStringOpt& s) + { + if (s.Small()) + { + flex_string_details::pod_copy( + s.buf_, + s.buf_ + s.size(), + buf_); + } + else + { + new(buf_) Storage(s.GetStorage()); + } + buf_[maxSmallString] = s.buf_[maxSmallString]; + } + + SmallStringOpt(const allocator_type&) + { + buf_[maxSmallString] = maxSmallString; + } + + SmallStringOpt(const value_type* s, size_type len, const allocator_type& a) + { + if (len <= maxSmallString) + { + flex_string_details::pod_copy(s, s + len, buf_); + buf_[maxSmallString] = value_type(maxSmallString - len); + } + else + { + new(buf_) Storage(s, len, a); + buf_[maxSmallString] = magic; + } + } + + SmallStringOpt(size_type len, value_type c, const allocator_type& a) + { + if (len <= maxSmallString) + { + flex_string_details::pod_fill(buf_, buf_ + len, c); + buf_[maxSmallString] = value_type(maxSmallString - len); + } + else + { + new(buf_) Storage(len, c, a); + buf_[maxSmallString] = magic; + } + } + + SmallStringOpt& operator=(const SmallStringOpt& rhs) + { + reserve(rhs.size()); + resize(0, 0); + append(rhs.data(), rhs.size()); + return *this; + } + + ~SmallStringOpt() + { + if (!Small()) GetStorage().~Storage(); + } + + iterator begin() + { + if (Small()) return buf_; + return &*GetStorage().begin(); + } + + const_iterator begin() const + { + if (Small()) return buf_; + return &*GetStorage().begin(); + } + + iterator end() + { + if (Small()) return buf_ + maxSmallString - buf_[maxSmallString]; + return &*GetStorage().end(); + } + + const_iterator end() const + { + if (Small()) return buf_ + maxSmallString - buf_[maxSmallString]; + return &*GetStorage().end(); + } + + size_type size() const + { + BOOST_ASSERT(!Small() || maxSmallString >= buf_[maxSmallString]); + return Small() + ? maxSmallString - buf_[maxSmallString] + : GetStorage().size(); + } + + size_type max_size() const + { return get_allocator().max_size(); } + + size_type capacity() const + { return Small() ? maxSmallString : GetStorage().capacity(); } + + void reserve(size_type res_arg) + { + if (Small()) + { + if (res_arg <= maxSmallString) return; + SmallStringOpt temp(*this); + this->~SmallStringOpt(); + new(buf_) Storage(temp.data(), temp.size(), + temp.get_allocator()); + buf_[maxSmallString] = magic; + GetStorage().reserve(res_arg); + } + else + { + GetStorage().reserve(res_arg); + } + BOOST_ASSERT(capacity() >= res_arg); + } + + void append(const value_type* s, size_type sz) + { + if (!Small()) + { + GetStorage().append(s, sz); + } + else + { + // append to a small string + const size_type neededCapacity = + maxSmallString - buf_[maxSmallString] + sz; + + if (maxSmallString < neededCapacity) + { + // need to change storage strategy + allocator_type alloc; + Storage temp(alloc); + temp.reserve(neededCapacity); + temp.append(buf_, maxSmallString - buf_[maxSmallString]); + temp.append(s, sz); + buf_[maxSmallString] = magic; + new(buf_) Storage(temp.get_allocator()); + GetStorage().swap(temp); + } + else + { + flex_string_details::pod_move(s, s + sz, + buf_ + maxSmallString - buf_[maxSmallString]); + buf_[maxSmallString] -= value_type(sz); + } + } + } + + template <class InputIterator> + void append(InputIterator b, InputIterator e) + { + // @@@ todo: optimize this depending on iterator type + for (; b != e; ++b) + { + *this += *b; + } + } + + void resize(size_type n, value_type c) + { + if (Small()) + { + if (n > maxSmallString) + { + // Small string resized to big string + SmallStringOpt temp(*this); // can't throw + // 11-17-2001: correct exception safety bug + Storage newString(temp.data(), temp.size(), + temp.get_allocator()); + newString.resize(n, c); + // We make the reasonable assumption that an empty Storage + // constructor won't throw + this->~SmallStringOpt(); + new(&buf_[0]) Storage(temp.get_allocator()); + buf_[maxSmallString] = value_type(magic); + GetStorage().swap(newString); + } + else + { + // Small string resized to small string + // 11-17-2001: bug fix: terminating zero not copied + size_type toFill = n > size() ? n - size() : 0; + flex_string_details::pod_fill(end(), end() + toFill, c); + buf_[maxSmallString] = value_type(maxSmallString - n); + } + } + else + { + if (n > maxSmallString) + { + // Big string resized to big string + GetStorage().resize(n, c); + } + else + { + // Big string resized to small string + // 11-17=2001: bug fix in the BOOST_ASSERTion below + BOOST_ASSERT(capacity() > n); + SmallStringOpt newObj(data(), n, get_allocator()); + newObj.swap(*this); + } + } + } + + void swap(SmallStringOpt& rhs) + { + if (Small()) + { + if (rhs.Small()) + { + // Small swapped with small + std::swap_ranges(buf_, buf_ + maxSmallString + 1, + rhs.buf_); + } + else + { + // Small swapped with big + // Make a copy of myself - can't throw + SmallStringOpt temp(*this); + // Nuke myself + this->~SmallStringOpt(); + // Make an empty storage for myself (likely won't throw) + new(buf_) Storage(0, value_type(), rhs.get_allocator()); + buf_[maxSmallString] = magic; + // Recurse to this same function + swap(rhs); + // Nuke rhs + rhs.~SmallStringOpt(); + // Build the new small string into rhs + new(&rhs) SmallStringOpt(temp); + } + } + else + { + if (rhs.Small()) + { + // Big swapped with small + // Already implemented, recurse with reversed args + rhs.swap(*this); + } + else + { + // Big swapped with big + GetStorage().swap(rhs.GetStorage()); + } + } + } + + const value_type* c_str() const + { + if (!Small()) return GetStorage().c_str(); + buf_[maxSmallString - buf_[maxSmallString]] = value_type(); + return buf_; + } + + const value_type* data() const + { return Small() ? buf_ : GetStorage().data(); } + + allocator_type get_allocator() const + { return allocator_type(); } +}; + +//////////////////////////////////////////////////////////////////////////////// +// class template CowString +// Implements Copy on Write over any storage +//////////////////////////////////////////////////////////////////////////////// + +template < + typename Storage, + typename Align = BOOST_DEDUCED_TYPENAME Storage::value_type* +> +class CowString +{ + typedef typename Storage::value_type E; + typedef typename flex_string_details::get_unsigned<E>::result RefCountType; + +public: + typedef E value_type; + typedef typename Storage::iterator iterator; + typedef typename Storage::const_iterator const_iterator; + typedef typename Storage::allocator_type allocator_type; + typedef typename allocator_type::size_type size_type; + typedef typename Storage::reference reference; + +private: + union + { + mutable char buf_[sizeof(Storage)]; + Align align_; + }; + + Storage& Data() const + { + Storage* p = reinterpret_cast<Storage*>(&buf_[0]); + return *p; + } + + RefCountType GetRefs() const + { + const Storage& d = Data(); + BOOST_ASSERT(d.size() > 0); + BOOST_ASSERT(static_cast<RefCountType>(*d.begin()) != 0); + return *d.begin(); + } + + RefCountType& Refs() + { + Storage& d = Data(); + BOOST_ASSERT(d.size() > 0); + return reinterpret_cast<RefCountType&>(*d.begin()); + } + + void MakeUnique() const + { + BOOST_ASSERT(GetRefs() >= 1); + if (GetRefs() == 1) return; + + union + { + char buf_[sizeof(Storage)]; + Align align_; + } temp; + + --(*Data().begin()); // decrement the use count of the remaining object + + Storage* p = reinterpret_cast<Storage*>(&temp.buf_[0]); + new(buf_) Storage( + *new(p) Storage(Data()), + flex_string_details::Shallow()); + *Data().begin() = 1; + } + +public: + CowString(const CowString& s) + { + if (s.GetRefs() == (std::numeric_limits<RefCountType>::max)()) + { + // must make a brand new copy + new(buf_) Storage(s.Data()); // non shallow + Refs() = 1; + } + else + { + new(buf_) Storage(s.Data(), flex_string_details::Shallow()); + ++Refs(); + } + BOOST_ASSERT(Data().size() > 0); + } + + CowString(const allocator_type& a) + { + new(buf_) Storage(1, 1, a); + } + + CowString(const E* s, size_type len, const allocator_type& a) + { + // Warning - MSVC's debugger has trouble tracing through the code below. + // It seems to be a const-correctness issue + // + new(buf_) Storage(a); + Data().reserve(len + 1); + Data().resize(1, 1); + Data().append(s, s + len); + } + + CowString(size_type len, E c, const allocator_type& a) + { + new(buf_) Storage(len + 1, c, a); + Refs() = 1; + } + + CowString& operator=(const CowString& rhs) + { +// CowString(rhs).swap(*this); + if (--Refs() == 0) + Data().~Storage(); + if (rhs.GetRefs() == (std::numeric_limits<RefCountType>::max)()) + { + // must make a brand new copy + new(buf_) Storage(rhs.Data()); // non shallow + Refs() = 1; + } + else + { + new(buf_) Storage(rhs.Data(), flex_string_details::Shallow()); + ++Refs(); + } + BOOST_ASSERT(Data().size() > 0); + return *this; + } + + ~CowString() + { + BOOST_ASSERT(Data().size() > 0); + if (--Refs() == 0) + Data().~Storage(); + } + + iterator begin() + { + BOOST_ASSERT(Data().size() > 0); + MakeUnique(); + return Data().begin() + 1; + } + + const_iterator begin() const + { + BOOST_ASSERT(Data().size() > 0); + return Data().begin() + 1; + } + + iterator end() + { + MakeUnique(); + return Data().end(); + } + + const_iterator end() const + { + return Data().end(); + } + + size_type size() const + { + BOOST_ASSERT(Data().size() > 0); + return Data().size() - 1; + } + + size_type max_size() const + { + BOOST_ASSERT(Data().max_size() > 0); + return Data().max_size() - 1; + } + + size_type capacity() const + { + BOOST_ASSERT(Data().capacity() > 0); + return Data().capacity() - 1; + } + + void resize(size_type n, E c) + { + BOOST_ASSERT(Data().size() > 0); + MakeUnique(); + Data().resize(n + 1, c); + } + + template <class FwdIterator> + void append(FwdIterator b, FwdIterator e) + { + MakeUnique(); + Data().append(b, e); + } + + void reserve(size_type res_arg) + { + if (capacity() > res_arg) return; + MakeUnique(); + Data().reserve(res_arg + 1); + } + + void swap(CowString& rhs) + { + Data().swap(rhs.Data()); + } + + const E* c_str() const + { + BOOST_ASSERT(Data().size() > 0); + return Data().c_str() + 1; + } + + const E* data() const + { + BOOST_ASSERT(Data().size() > 0); + return Data().data() + 1; + } + + allocator_type get_allocator() const + { + return Data().get_allocator(); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// class template flex_string +// a std::basic_string compatible implementation +// Uses a Storage policy +//////////////////////////////////////////////////////////////////////////////// + +template <typename E, + class T = std::char_traits<E>, + class A = std::allocator<E>, + class Storage = AllocatorStringStorage<E, A> > +class flex_string : private Storage +{ +#if defined(BOOST_WAVE_FLEXSTRING_THROW_ON_ENFORCE) + template <typename Exception> + static void Enforce(bool condition, Exception*, const char* msg) + { if (!condition) boost::throw_exception(Exception(msg)); } +#else + template <typename Exception> + static inline void Enforce(bool condition, Exception*, const char* msg) + { BOOST_ASSERT(condition && msg); } +#endif // defined(BOOST_WAVE_FLEXSTRING_THROW_ON_ENFORCE) + +#ifndef NDEBUG + bool Sane() const + { + return + begin() <= end() && + empty() == (size() == 0) && + empty() == (begin() == end()) && + size() <= max_size() && + capacity() <= max_size() && + size() <= capacity(); + } + + struct Invariant; + friend struct Invariant; + struct Invariant + { + Invariant(const flex_string& s) : s_(s) + { + BOOST_ASSERT(s_.Sane()); + } + ~Invariant() + { + BOOST_ASSERT(s_.Sane()); + } + private: + const flex_string& s_; + Invariant& operator=(const Invariant&); + }; +#endif + +public: + // types + typedef T traits_type; + typedef typename traits_type::char_type value_type; + typedef A allocator_type; + typedef typename A::size_type size_type; + typedef typename A::difference_type difference_type; + + typedef typename A::reference reference; + typedef typename A::const_reference const_reference; + typedef typename A::pointer pointer; + typedef typename A::const_pointer const_pointer; + + typedef typename Storage::iterator iterator; + typedef typename Storage::const_iterator const_iterator; + + typedef boost::reverse_iterator<iterator> reverse_iterator; + typedef boost::reverse_iterator<const_iterator> const_reverse_iterator; + + static const size_type npos; // = size_type(-1) + +private: + static size_type Min(size_type lhs, size_type rhs) + { return lhs < rhs ? lhs : rhs; } + static void Procust(size_type& n, size_type nmax) + { if (n > nmax) n = nmax; } + +public: + // 21.3.1 construct/copy/destroy + explicit flex_string(const A& a = A()) + : Storage(a) + {} + + flex_string(const flex_string& str) + : Storage(str) + { + } + + flex_string(const flex_string& str, size_type pos, + size_type n = npos, const A& a = A()) + : Storage(a) + { + Enforce(pos <= str.size(), (std::out_of_range*)0, ""); + assign(str, pos, n); + } + + flex_string(const value_type* s, const A& a = A()) + : Storage(s, traits_type::length(s), a) + {} + + flex_string(const value_type* s, size_type n, const A& a = A()) + : Storage(s, n, a) + {} + + flex_string(size_type n, value_type c, const A& a = A()) + : Storage(n, c, a) + {} + + template <class InputIterator> + flex_string(InputIterator begin, InputIterator end, const A& a = A()) + : Storage(a) + { + assign(begin, end); + } + + ~flex_string() + {} + + flex_string& operator=(const flex_string& str) + { + if (this != &str) { + Storage& s = *this; + s = str; + } + return *this; + } + + flex_string& operator=(const value_type* s) + { + assign(s); + return *this; + } + + flex_string& operator=(value_type c) + { + assign(1, c); + return *this; + } + + // 21.3.2 iterators: + iterator begin() + { return Storage::begin(); } + + const_iterator begin() const + { return Storage::begin(); } + + iterator end() + { return Storage::end(); } + + const_iterator end() const + { return Storage::end(); } + + reverse_iterator rbegin() + { return reverse_iterator(end()); } + + const_reverse_iterator rbegin() const + { return const_reverse_iterator(end()); } + + reverse_iterator rend() + { return reverse_iterator(begin()); } + + const_reverse_iterator rend() const + { return const_reverse_iterator(begin()); } + +#if BOOST_WAVE_FLEX_STRING_SERIALIZATION_HACK != 0 + // temporary hack to make it easier to serialize flex_string's using + // the Boost.Serialization library + value_type & back() { return *(begin()+size()-1); } + value_type const& back() const { return *(begin()+size()-1); } +#endif + + // 21.3.3 capacity: + size_type size() const + { return Storage::size(); } + + size_type length() const + { return size(); } + + size_type max_size() const + { return Storage::max_size(); } + + void resize(size_type n, value_type c) + { Storage::resize(n, c); } + + void resize(size_type n) + { resize(n, value_type()); } + + size_type capacity() const + { return Storage::capacity(); } + + void reserve(size_type res_arg = 0) + { + Enforce(res_arg <= max_size(), (std::length_error*)0, ""); + Storage::reserve(res_arg); + } + + void clear() + { resize(0); } + + bool empty() const + { return size() == 0; } + + // 21.3.4 element access: + const_reference operator[](size_type pos) const + { return *(begin() + pos); } + + reference operator[](size_type pos) + { return *(begin() + pos); } + + const_reference at(size_type n) const + { + Enforce(n < size(), (std::out_of_range*)0, ""); + return (*this)[n]; + } + + reference at(size_type n) + { + Enforce(n < size(), (std::out_of_range*)0, ""); + return (*this)[n]; + } + + // 21.3.5 modifiers: + flex_string& operator+=(const flex_string& str) + { return append(str); } + + flex_string& operator+=(const value_type* s) + { return append(s); } + + flex_string& operator+=(value_type c) + { + push_back(c); + return *this; + } + + flex_string& append(const flex_string& str) + { return append(str, 0, npos); } + + flex_string& append(const flex_string& str, const size_type pos, + size_type n) + { + const size_type sz = str.size(); + Enforce(pos <= sz, (std::out_of_range*)0, ""); + Procust(n, sz - pos); + return append(str.c_str() + pos, n); + } + + flex_string& append(const value_type* s, const size_type n) + { +#ifndef NDEBUG + Invariant checker(*this); +#endif + if (IsAliasedRange(s, s + n)) + { + const size_type offset = s - &*begin(); + Storage::reserve(size() + n); + s = &*begin() + offset; + } + Storage::append(s, s+ n); + return *this; + } + + flex_string& append(const value_type* s) + { return append(s, traits_type::length(s)); } + + flex_string& append(size_type n, value_type c) + { + resize(size() + n, c); + return *this; + } + + template<class InputIterator> + flex_string& append(InputIterator first, InputIterator last) + { + insert(end(), first, last); + return *this; + } + + void push_back(value_type c) + { + const size_type cap = capacity(); + if (size() == cap) + { + reserve(cap << 1u); + } + Storage::append(&c, &c + 1); + } + + flex_string& assign(const flex_string& str) + { + if (&str == this) return *this; + return assign(str.data(), str.size()); + } + + flex_string& assign(const flex_string& str, size_type pos, + size_type n) + { + const size_type sz = str.size(); + Enforce(pos <= str.size(), (std::out_of_range*)0, ""); + Procust(n, sz - pos); + return assign(str.data() + pos, n); + } + + flex_string& assign(const value_type* s, size_type n) + { +#ifndef NDEBUG + Invariant checker(*this); +#endif + if (size() >= n) + { + std::copy(s, s + n, begin()); + resize(n); + } + else + { + const value_type *const s2 = s + size(); + std::copy(s, s2, begin()); + append(s2, n - size()); + } + return *this; + } + + flex_string& assign(const value_type* s) + { return assign(s, traits_type::length(s)); } + + template <class ItOrLength, class ItOrChar> + flex_string& assign(ItOrLength first_or_n, ItOrChar last_or_c) + { return replace(begin(), end(), first_or_n, last_or_c); } + + flex_string& insert(size_type pos1, const flex_string& str) + { return insert(pos1, str.data(), str.size()); } + + flex_string& insert(size_type pos1, const flex_string& str, + size_type pos2, size_type n) + { + Enforce(pos2 <= str.length(), (std::out_of_range*)0, ""); + Procust(n, str.length() - pos2); + return insert(pos1, str.data() + pos2, n); + } + + flex_string& insert(size_type pos, const value_type* s, size_type n) + { + Enforce(pos <= length(), (std::out_of_range*)0, ""); + insert(begin() + pos, s, s + n); + return *this; + } + + flex_string& insert(size_type pos, const value_type* s) + { return insert(pos, s, traits_type::length(s)); } + + flex_string& insert(size_type pos, size_type n, value_type c) + { + Enforce(pos <= length(), (std::out_of_range*)0, ""); + insert(begin() + pos, n, c); + return *this; + } + + iterator insert(iterator p, value_type c = value_type()) + { + const size_type pos = p - begin(); + insert(pos, &c, 1); + return begin() + pos; + } + +private: + // Care must be taken when dereferencing some iterator types. + // + // Users can implement this function in their namespace if their storage + // uses a special iterator type, the function will be found through ADL. + template<class Iterator> + const typename std::iterator_traits<Iterator>::value_type* + DereferenceValidIterator(Iterator it) const + { + return &*it; + } + + // Care must be taken when dereferencing a reverse iterators, hence this + // special case. This isn't in the std namespace so as not to pollute it or + // create name clashes. + template<typename Iterator> + const typename std::iterator_traits<Iterator>::value_type* + DereferenceValidIterator(std::reverse_iterator<Iterator> it) const + { + return &*--it; + } + + // Determine if the range aliases the current string. + // + // This method cannot be const because calling begin/end on copy-on-write + // implementations must have side effects. + // A const version wouldn't make the string unique through this call. + template<class Iterator> + bool IsAliasedRange(Iterator beginIterator, Iterator endIterator) + { + if(!empty() && beginIterator != endIterator) + { + typedef const typename std::iterator_traits<Iterator>::value_type * + pointer; + + pointer myBegin(&*begin()); + pointer myEnd(&*begin() + size()); + pointer rangeBegin(DereferenceValidIterator(beginIterator)); + + const std::less_equal<pointer> less_equal = std::less_equal<pointer>(); + if(less_equal(myBegin, rangeBegin) && less_equal(rangeBegin, myEnd)) + return true; + } + return false; + } + + template <int i> class Selector {}; + + flex_string& InsertImplDiscr(iterator p, + size_type n, value_type c, Selector<1>) + { +#ifndef NDEBUG + Invariant checker(*this); +#endif + BOOST_ASSERT(begin() <= p && p <= end()); + const size_type insertOffset(p - begin()); + const size_type originalSize(size()); + if(n < originalSize - insertOffset) + { + // The new characters fit within the original string. + // The characters that are pushed back need to be moved because + // they're aliased. + // The appended characters will all be overwritten by the move. + append(n, value_type(0)); + value_type* begin(&*begin()); + flex_string_details::pod_move(begin + insertOffset, + begin + originalSize, begin + insertOffset + n); + std::fill(begin + insertOffset, begin + insertOffset + n, c); + } + else + { + // The new characters exceed the original string. + // The characters that are pushed back can simply be copied since + // they aren't aliased. + // The appended characters will partly be overwritten by the copy. + append(n, c); + value_type* begin(&*begin()); + flex_string_details::pod_copy(begin + insertOffset, + begin + originalSize, begin + insertOffset + n); + std::fill(begin + insertOffset, begin + originalSize, c); + } + return *this; + } + + template<class InputIterator> + flex_string& InsertImplDiscr(iterator i, + InputIterator b, InputIterator e, Selector<0>) + { + InsertImpl(i, b, e, + typename std::iterator_traits<InputIterator>::iterator_category()); + return *this; + } + + template <class FwdIterator> + void InsertImpl(iterator i, + FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) + { + if(s1 == s2) + { + // Insert an empty range. + return; + } + + if(IsAliasedRange(s1, s2)) + { + // The source range is contained in the current string, copy it + // and recurse. + const flex_string temporary(s1, s2); + InsertImpl(i, temporary.begin(), temporary.end(), + typename std::iterator_traits<FwdIterator>::iterator_category()); + return; + } + +#ifndef NDEBUG + Invariant checker(*this); +#endif + const size_type pos = i - begin(); + const typename std::iterator_traits<FwdIterator>::difference_type n2 = + std::distance(s1, s2); + + BOOST_ASSERT(n2 >= 0); + using namespace flex_string_details; + BOOST_ASSERT(pos <= size()); + + const typename std::iterator_traits<FwdIterator>::difference_type maxn2 = + capacity() - size(); + if (maxn2 < n2) + { + // Reallocate the string. + BOOST_ASSERT(!IsAliasedRange(s1, s2)); + reserve(size() + n2); + i = begin() + pos; + } + if (pos + n2 <= size()) + { + const iterator tailBegin = end() - n2; + Storage::append(tailBegin, tailBegin + n2); + std::copy(reverse_iterator(tailBegin), reverse_iterator(i), + reverse_iterator(tailBegin + n2)); + std::copy(s1, s2, i); + } + else + { + FwdIterator t = s1; + const size_type old_size = size(); + std::advance(t, old_size - pos); + BOOST_ASSERT(std::distance(t, s2) >= 0); + Storage::append(t, s2); + Storage::append(data() + pos, data() + old_size); + std::copy(s1, t, i); + } + } + + template <class InputIterator> + void InsertImpl(iterator insertPosition, + InputIterator inputBegin, InputIterator inputEnd, + std::input_iterator_tag) + { + flex_string temporary(begin(), insertPosition); + for (; inputBegin != inputEnd; ++inputBegin) + { + temporary.push_back(*inputBegin); + } + temporary.append(insertPosition, end()); + swap(temporary); + } + +public: + template <class ItOrLength, class ItOrChar> + void insert(iterator p, ItOrLength first_or_n, ItOrChar last_or_c) + { + Selector<std::numeric_limits<ItOrLength>::is_specialized> sel; + InsertImplDiscr(p, first_or_n, last_or_c, sel); + } + + flex_string& erase(size_type pos = 0, size_type n = npos) + { +#ifndef NDEBUG + Invariant checker(*this); +#endif + Enforce(pos <= length(), (std::out_of_range*)0, ""); + Procust(n, length() - pos); + std::copy(begin() + pos + n, end(), begin() + pos); + resize(length() - n); + return *this; + } + + iterator erase(iterator position) + { + const size_type pos(position - begin()); + erase(pos, 1); + return begin() + pos; + } + + iterator erase(iterator first, iterator last) + { + const size_type pos(first - begin()); + erase(pos, last - first); + return begin() + pos; + } + + // Replaces at most n1 chars of *this, starting with pos1 with the content of str + flex_string& replace(size_type pos1, size_type n1, const flex_string& str) + { return replace(pos1, n1, str, 0, npos); } + + // Replaces at most n1 chars of *this, starting with pos1, + // with at most n2 chars of str starting with pos2 + flex_string& replace(size_type pos1, size_type n1, const flex_string& str, + size_type pos2, size_type n2) + { + Enforce(pos2 <= str.length(), (std::out_of_range*)0, ""); + return replace(pos1, n1, str.data() + pos2, + Min(n2, str.size() - pos2)); + } + + // Replaces at most n1 chars of *this, starting with pos, with chars from s + flex_string& replace(size_type pos, size_type n1, const value_type* s) + { return replace(pos, n1, s, traits_type::length(s)); } + + // Replaces at most n1 chars of *this, starting with pos, with n2 occurences of c + // consolidated with + // Replaces at most n1 chars of *this, starting with pos, + // with at most n2 chars of str. + // str must have at least n2 chars. + template <class StrOrLength, class NumOrChar> + flex_string& replace(size_type pos, size_type n1, + StrOrLength s_or_n2, NumOrChar n_or_c) + { +#ifndef NDEBUG + Invariant checker(*this); +#endif + Enforce(pos <= size(), (std::out_of_range*)0, ""); + Procust(n1, length() - pos); + const iterator b = begin() + pos; + return replace(b, b + n1, s_or_n2, n_or_c); + } + + flex_string& replace(iterator i1, iterator i2, const flex_string& str) + { return replace(i1, i2, str.c_str(), str.length()); } + + flex_string& replace(iterator i1, iterator i2, const value_type* s) + { return replace(i1, i2, s, traits_type::length(s)); } + +private: + flex_string& ReplaceImplDiscr(iterator i1, iterator i2, + const value_type* s, size_type n, Selector<2>) + { + BOOST_ASSERT(i1 <= i2); + BOOST_ASSERT(begin() <= i1 && i1 <= end()); + BOOST_ASSERT(begin() <= i2 && i2 <= end()); + return replace(i1, i2, s, s + n); + } + + flex_string& ReplaceImplDiscr(iterator i1, iterator i2, + size_type n2, value_type c, Selector<1>) + { + const size_type n1 = i2 - i1; + if (n1 > n2) + { + std::fill(i1, i1 + n2, c); + erase(i1 + n2, i2); + } + else + { + std::fill(i1, i2, c); + insert(i2, n2 - n1, c); + } + return *this; + } + + template <class InputIterator> + flex_string& ReplaceImplDiscr(iterator i1, iterator i2, + InputIterator b, InputIterator e, Selector<0>) + { + ReplaceImpl(i1, i2, b, e, + typename std::iterator_traits<InputIterator>::iterator_category()); + return *this; + } + + template <class FwdIterator> + void ReplaceImpl(iterator i1, iterator i2, + FwdIterator s1, FwdIterator s2, std::forward_iterator_tag) + { +#ifndef NDEBUG + Invariant checker(*this); +#endif + const typename std::iterator_traits<iterator>::difference_type n1 = + i2 - i1; + BOOST_ASSERT(n1 >= 0); + const typename std::iterator_traits<FwdIterator>::difference_type n2 = + std::distance(s1, s2); + BOOST_ASSERT(n2 >= 0); + + if (IsAliasedRange(s1, s2)) + { + // Aliased replace, copy to new string. + flex_string temporary; + temporary.reserve(size() - n1 + n2); + temporary.append(begin(), i1).append(s1, s2).append(i2, end()); + swap(temporary); + return; + } + + if (n1 > n2) + { + // Shrinks + std::copy(s1, s2, i1); + erase(i1 + n2, i2); + } + else + { + // Grows + flex_string_details::copy_n(s1, n1, i1); + std::advance(s1, n1); + insert(i2, s1, s2); + } + } + + template <class InputIterator> + void ReplaceImpl(iterator i1, iterator i2, + InputIterator b, InputIterator e, std::input_iterator_tag) + { + flex_string temp(begin(), i1); + temp.append(b, e).append(i2, end()); + swap(temp); + } + +public: + template <class T1, class T2> + flex_string& replace(iterator i1, iterator i2, + T1 first_or_n_or_s, T2 last_or_c_or_n) + { + const bool + num1 = std::numeric_limits<T1>::is_specialized, + num2 = std::numeric_limits<T2>::is_specialized; + return ReplaceImplDiscr(i1, i2, first_or_n_or_s, last_or_c_or_n, + Selector<num1 ? (num2 ? 1 : -1) : (num2 ? 2 : 0)>()); + } + + size_type copy(value_type* s, size_type n, size_type pos = 0) const + { + Enforce(pos <= size(), (std::out_of_range*)0, ""); + n = Min(n, size() - pos); + + flex_string_details::pod_copy( + &*begin() + pos, + &*begin() + pos + n, + s); + return n; + } + + void swap(flex_string& rhs) + { + Storage& srhs = rhs; + this->Storage::swap(srhs); + } + + // 21.3.6 string operations: + const value_type* c_str() const + { return Storage::c_str(); } + + const value_type* data() const + { return Storage::data(); } + + allocator_type get_allocator() const + { return Storage::get_allocator(); } + + size_type find(const flex_string& str, size_type pos = 0) const + { return find(str.data(), pos, str.length()); } + + size_type find (const value_type* s, size_type pos, size_type n) const + { + const size_type size_(size()); + if (n + pos > size_) + return npos; + for (; pos < size_; ++pos) + { + if (traits_type::compare(&*begin() + pos, s, n) == 0) + { + return pos; + } + } + return npos; + } + + size_type find (const value_type* s, size_type pos = 0) const + { return find(s, pos, traits_type::length(s)); } + + size_type find (value_type c, size_type pos = 0) const + { return find(&c, pos, 1); } + + size_type rfind(const flex_string& str, size_type pos = npos) const + { return rfind(str.c_str(), pos, str.length()); } + + size_type rfind(const value_type* s, size_type pos, size_type n) const + { + if (n > length()) return npos; + pos = Min(pos, length() - n); + if (n == 0) return pos; + + const_iterator i(begin() + pos); + for (; ; --i) + { + if (traits_type::eq(*i, *s) + && traits_type::compare(&*i, s, n) == 0) + { + return i - begin(); + } + if (i == begin()) break; + } + return npos; + } + + size_type rfind(const value_type* s, size_type pos = npos) const + { return rfind(s, pos, traits_type::length(s)); } + + size_type rfind(value_type c, size_type pos = npos) const + { return rfind(&c, pos, 1); } + + size_type find_first_of(const flex_string& str, size_type pos = 0) const + { return find_first_of(str.c_str(), pos, str.length()); } + + size_type find_first_of(const value_type* s, + size_type pos, size_type n) const + { + if (pos > length() || n == 0) return npos; + const_iterator i(begin() + pos), + finish(end()); + for (; i != finish; ++i) + { + if (traits_type::find(s, n, *i) != 0) + { + return i - begin(); + } + } + return npos; + } + + size_type find_first_of(const value_type* s, size_type pos = 0) const + { return find_first_of(s, pos, traits_type::length(s)); } + + size_type find_first_of(value_type c, size_type pos = 0) const + { return find_first_of(&c, pos, 1); } + + size_type find_last_of (const flex_string& str, + size_type pos = npos) const + { return find_last_of(str.c_str(), pos, str.length()); } + + size_type find_last_of (const value_type* s, size_type pos, + size_type n) const + { + if (!empty() && n > 0) + { + pos = Min(pos, length() - 1); + const_iterator i(begin() + pos); + for (;; --i) + { + if (traits_type::find(s, n, *i) != 0) + { + return i - begin(); + } + if (i == begin()) break; + } + } + return npos; + } + + size_type find_last_of (const value_type* s, + size_type pos = npos) const + { return find_last_of(s, pos, traits_type::length(s)); } + + size_type find_last_of (value_type c, size_type pos = npos) const + { return find_last_of(&c, pos, 1); } + + size_type find_first_not_of(const flex_string& str, + size_type pos = 0) const + { return find_first_not_of(str.data(), pos, str.size()); } + + size_type find_first_not_of(const value_type* s, size_type pos, + size_type n) const + { + if (pos < length()) + { + const_iterator + i(begin() + pos), + finish(end()); + for (; i != finish; ++i) + { + if (traits_type::find(s, n, *i) == 0) + { + return i - begin(); + } + } + } + return npos; + } + + size_type find_first_not_of(const value_type* s, + size_type pos = 0) const + { return find_first_not_of(s, pos, traits_type::length(s)); } + + size_type find_first_not_of(value_type c, size_type pos = 0) const + { return find_first_not_of(&c, pos, 1); } + + size_type find_last_not_of(const flex_string& str, + size_type pos = npos) const + { return find_last_not_of(str.c_str(), pos, str.length()); } + + size_type find_last_not_of(const value_type* s, size_type pos, + size_type n) const + { + if (!empty()) + { + pos = Min(pos, size() - 1); + const_iterator i(begin() + pos); + for (;; --i) + { + if (traits_type::find(s, n, *i) == 0) + { + return i - begin(); + } + if (i == begin()) break; + } + } + return npos; + } + + size_type find_last_not_of(const value_type* s, + size_type pos = npos) const + { return find_last_not_of(s, pos, traits_type::length(s)); } + + size_type find_last_not_of (value_type c, size_type pos = npos) const + { return find_last_not_of(&c, pos, 1); } + + flex_string substr(size_type pos = 0, size_type n = npos) const + { + Enforce(pos <= size(), (std::out_of_range*)0, ""); + return flex_string(data() + pos, Min(n, size() - pos)); + } + + std::ptrdiff_t compare(const flex_string& str) const + { + // FIX due to Goncalo N M de Carvalho July 18, 2005 + return compare(0, size(), str); + } + + std::ptrdiff_t compare(size_type pos1, size_type n1, + const flex_string& str) const + { return compare(pos1, n1, str.data(), str.size()); } + + // FIX to compare: added the TC + // (http://www.comeaucomputing.com/iso/lwg-defects.html number 5) + // Thanks to Caleb Epstein for the fix + std::ptrdiff_t compare(size_type pos1, size_type n1, + const value_type* s) const + { + return compare(pos1, n1, s, traits_type::length(s)); + } + + std::ptrdiff_t compare(size_type pos1, size_type n1, + const value_type* s, size_type n2) const + { + Enforce(pos1 <= size(), (std::out_of_range*)0, ""); + Procust(n1, size() - pos1); + const int r = traits_type::compare(pos1 + data(), s, Min(n1, n2)); + return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0; + } + + std::ptrdiff_t compare(size_type pos1, size_type n1, + const flex_string& str, + size_type pos2, size_type n2) const + { + Enforce(pos2 <= str.size(), (std::out_of_range*)0, ""); + return compare(pos1, n1, str.data() + pos2, Min(n2, str.size() - pos2)); + } + + std::ptrdiff_t compare(const value_type* s) const + { + // Could forward to compare(0, size(), s, traits_type::length(s)) + // but that does two extra checks + const size_type n1(size()), n2(traits_type::length(s)); + const int r = traits_type::compare(data(), s, Min(n1, n2)); + return r != 0 ? r : n1 > n2 ? 1 : n1 < n2 ? -1 : 0; + } +}; + +// non-member functions +template <typename E, class T, class A, class S> +flex_string<E, T, A, S> operator+(const flex_string<E, T, A, S>& lhs, + const flex_string<E, T, A, S>& rhs) +{ + flex_string<E, T, A, S> result; + result.reserve(lhs.size() + rhs.size()); + result.append(lhs).append(rhs); + return result; +} + +template <typename E, class T, class A, class S> +flex_string<E, T, A, S> operator+(const typename flex_string<E, T, A, S>::value_type* lhs, + const flex_string<E, T, A, S>& rhs) +{ + flex_string<E, T, A, S> result; + const typename flex_string<E, T, A, S>::size_type len = + flex_string<E, T, A, S>::traits_type::length(lhs); + result.reserve(len + rhs.size()); + result.append(lhs, len).append(rhs); + return result; +} + +template <typename E, class T, class A, class S> +flex_string<E, T, A, S> operator+( + typename flex_string<E, T, A, S>::value_type lhs, + const flex_string<E, T, A, S>& rhs) +{ + flex_string<E, T, A, S> result; + result.reserve(1 + rhs.size()); + result.push_back(lhs); + result.append(rhs); + return result; +} + +template <typename E, class T, class A, class S> +flex_string<E, T, A, S> operator+(const flex_string<E, T, A, S>& lhs, + const typename flex_string<E, T, A, S>::value_type* rhs) +{ + typedef typename flex_string<E, T, A, S>::size_type size_type; + typedef typename flex_string<E, T, A, S>::traits_type traits_type; + + flex_string<E, T, A, S> result; + const size_type len = traits_type::length(rhs); + result.reserve(lhs.size() + len); + result.append(lhs).append(rhs, len); + return result; +} + +template <typename E, class T, class A, class S> +flex_string<E, T, A, S> operator+(const flex_string<E, T, A, S>& lhs, + typename flex_string<E, T, A, S>::value_type rhs) +{ + flex_string<E, T, A, S> result; + result.reserve(lhs.size() + 1); + result.append(lhs); + result.push_back(rhs); + return result; +} + +template <typename E, class T, class A, class S> +inline bool operator==(const flex_string<E, T, A, S>& lhs, + const flex_string<E, T, A, S>& rhs) +{ return lhs.compare(rhs) == 0; } + +template <typename E, class T, class A, class S> +inline bool operator==(const typename flex_string<E, T, A, S>::value_type* lhs, + const flex_string<E, T, A, S>& rhs) +{ return rhs == lhs; } + +template <typename E, class T, class A, class S> +inline bool operator==(const flex_string<E, T, A, S>& lhs, + const typename flex_string<E, T, A, S>::value_type* rhs) +{ return lhs.compare(rhs) == 0; } + +template <typename E, class T, class A, class S> +inline bool operator!=(const flex_string<E, T, A, S>& lhs, + const flex_string<E, T, A, S>& rhs) +{ return !(lhs == rhs); } + +template <typename E, class T, class A, class S> +inline bool operator!=(const typename flex_string<E, T, A, S>::value_type* lhs, + const flex_string<E, T, A, S>& rhs) +{ return !(lhs == rhs); } + +template <typename E, class T, class A, class S> +inline bool operator!=(const flex_string<E, T, A, S>& lhs, + const typename flex_string<E, T, A, S>::value_type* rhs) +{ return !(lhs == rhs); } + +template <typename E, class T, class A, class S> +inline bool operator<(const flex_string<E, T, A, S>& lhs, + const flex_string<E, T, A, S>& rhs) +{ return lhs.compare(rhs) < 0; } + +template <typename E, class T, class A, class S> +inline bool operator<(const flex_string<E, T, A, S>& lhs, + const typename flex_string<E, T, A, S>::value_type* rhs) +{ return lhs.compare(rhs) < 0; } + +template <typename E, class T, class A, class S> +inline bool operator<(const typename flex_string<E, T, A, S>::value_type* lhs, + const flex_string<E, T, A, S>& rhs) +{ return rhs.compare(lhs) > 0; } + +template <typename E, class T, class A, class S> +inline bool operator>(const flex_string<E, T, A, S>& lhs, + const flex_string<E, T, A, S>& rhs) +{ return rhs < lhs; } + +template <typename E, class T, class A, class S> +inline bool operator>(const flex_string<E, T, A, S>& lhs, + const typename flex_string<E, T, A, S>::value_type* rhs) +{ return rhs < lhs; } + +template <typename E, class T, class A, class S> +bool operator>(const typename flex_string<E, T, A, S>::value_type* lhs, + const flex_string<E, T, A, S>& rhs) +{ return rhs < lhs; } + +template <typename E, class T, class A, class S> +inline bool operator<=(const flex_string<E, T, A, S>& lhs, + const flex_string<E, T, A, S>& rhs) +{ return !(rhs < lhs); } + +template <typename E, class T, class A, class S> +inline bool operator<=(const flex_string<E, T, A, S>& lhs, + const typename flex_string<E, T, A, S>::value_type* rhs) +{ return !(rhs < lhs); } + +template <typename E, class T, class A, class S> +bool operator<=(const typename flex_string<E, T, A, S>::value_type* lhs, + const flex_string<E, T, A, S>& rhs) +{ return !(rhs < lhs); } + +template <typename E, class T, class A, class S> +bool operator>=(const flex_string<E, T, A, S>& lhs, + const flex_string<E, T, A, S>& rhs) +{ return !(lhs < rhs); } + +template <typename E, class T, class A, class S> +bool operator>=(const flex_string<E, T, A, S>& lhs, + const typename flex_string<E, T, A, S>::value_type* rhs) +{ return !(lhs < rhs); } + +template <typename E, class T, class A, class S> +inline bool operator>=(const typename flex_string<E, T, A, S>::value_type* lhs, + const flex_string<E, T, A, S>& rhs) +{ return !(lhs < rhs); } + +template <typename E, class T, class A, class S> +void swap(flex_string<E, T, A, S>& lhs, flex_string<E, T, A, S>& rhs) +{ + // subclause 21.3.7.8: + lhs.swap(rhs); +} + +template <typename E, class T, class A, class S> +inline std::basic_istream<typename flex_string<E, T, A, S>::value_type, + typename flex_string<E, T, A, S>::traits_type>& +operator>>( + std::basic_istream<typename flex_string<E, T, A, S>::value_type, + typename flex_string<E, T, A, S>::traits_type>& is, + flex_string<E, T, A, S>& str); + +template <typename E, class T, class A, class S> +std::basic_ostream<typename flex_string<E, T, A, S>::value_type, + typename flex_string<E, T, A, S>::traits_type>& +operator<<( + std::basic_ostream<typename flex_string<E, T, A, S>::value_type, + typename flex_string<E, T, A, S>::traits_type>& os, + const flex_string<E, T, A, S>& str) +{ return os << str.c_str(); } + + +// The getline below implementations are from the SGI STL (http://www.sgi.com/tech/stl/) +// and come with the following copyright: +// +// Permission to use, copy, modify, distribute and sell this software and its +// documentation for any purpose is hereby granted without fee, provided that +// the below copyright notice appears in all copies and that both the copyright +// notice and this permission notice appear in supporting documentation. Silicon +// Graphics makes no representations about the suitability of this software for +// any purpose. It is provided "as is" without express or implied warranty. +// +// Copyright (c) 1997-1999 +// Silicon Graphics Computer Systems, Inc. +// +// Copyright (c) 1994 +// Hewlett-Packard Company + +template <typename E, class T, class A, class S> +std::basic_istream<typename flex_string<E, T, A, S>::value_type, + typename flex_string<E, T, A, S>::traits_type>& +getline( + std::basic_istream<typename flex_string<E, T, A, S>::value_type, + typename flex_string<E, T, A, S>::traits_type>& is, + flex_string<E, T, A, S>& str, + typename flex_string<E, T, A, S>::value_type delim) +{ + size_t nread = 0; + typename std::basic_istream<typename flex_string<E, T, A, S>::value_type, + typename flex_string<E, T, A, S>::traits_type>::sentry sentry(is, true); + + if (sentry) { + std::basic_streambuf<typename flex_string<E, T, A, S>::value_type, + typename flex_string<E, T, A, S>::traits_type>* buf = is.rdbuf(); + str.clear(); + + while (nread < str.max_size()) { + int c1 = buf->sbumpc(); + if (flex_string<E, T, A, S>::traits_type::eq_int_type(c1, + flex_string<E, T, A, S>::traits_type::eof())) + { + is.setstate(std::ios_base::eofbit); + break; + } + else { + ++nread; + typename flex_string<E, T, A, S>::value_type c = + flex_string<E, T, A, S>::traits_type::to_char_type(c1); + + if (!flex_string<E, T, A, S>::traits_type::eq(c, delim)) + str.push_back(c); + else + break; // Character is extracted but not appended. + } + } + } + if (nread == 0 || nread >= str.max_size()) + is.setstate(std::ios_base::failbit); + + return is; +} + +template <typename E, class T, class A, class S> +std::basic_istream<typename flex_string<E, T, A, S>::value_type, + typename flex_string<E, T, A, S>::traits_type>& +getline( + std::basic_istream<typename flex_string<E, T, A, S>::value_type, + typename flex_string<E, T, A, S>::traits_type>& is, + flex_string<E, T, A, S>& str) +{ + return getline(is, str, is.widen('\n')); +} + +template <typename E1, class T, class A, class S> +const typename flex_string<E1, T, A, S>::size_type +flex_string<E1, T, A, S>::npos = (typename flex_string<E1, T, A, S>::size_type)(-1); + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +#if BOOST_WAVE_SERIALIZATION != 0 +/////////////////////////////////////////////////////////////////////////////// +namespace boost { namespace serialization { + +#if !defined(BOOST_WAVE_FLEX_STRING_SERIALIZATION_HACK) + +// FIXME: This doesn't work because of the missing flex_string::operator>>() +template <typename E, class T, class A, class S> +struct implementation_level<boost::wave::util::flex_string<E, T, A, S> > +{ + typedef mpl::integral_c_tag tag; + typedef mpl::int_<boost::serialization::primitive_type> type; + BOOST_STATIC_CONSTANT( + int, + value = implementation_level::type::value + ); +}; + +#else + +// We serialize flex_strings as vectors of char's for now +template<class Archive, typename E, class T, class A, class S> +inline void save(Archive & ar, + boost::wave::util::flex_string<E, T, A, S> const &t, + const unsigned int file_version) +{ + boost::serialization::stl::save_collection< + Archive, wave::util::flex_string<E, T, A, S> >(ar, t); +} + +template<class Archive, typename E, class T, class A, class S> +inline void load(Archive & ar, boost::wave::util::flex_string<E, T, A, S> &t, + const unsigned int file_version) +{ + boost::serialization::stl::load_collection< + Archive, boost::wave::util::flex_string<E, T, A, S>, + boost::serialization::stl::archive_input_seq< + Archive, boost::wave::util::flex_string<E, T, A, S> >, + boost::serialization::stl::reserve_imp< + boost::wave::util::flex_string<E, T, A, S> > + >(ar, t); +} + +// split non-intrusive serialization function member into separate +// non intrusive save/load member functions +template<class Archive, typename E, class T, class A, class S> +inline void serialize(Archive & ar, boost::wave::util::flex_string<E, T, A, S> &t, + const unsigned int file_version) +{ + boost::serialization::split_free(ar, t, file_version); +} + +#endif + +/////////////////////////////////////////////////////////////////////////////// +}} // boost::serialization +#endif + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // FLEX_STRING_INC_ diff --git a/boost/wave/util/functor_input.hpp b/boost/wave/util/functor_input.hpp new file mode 100644 index 0000000000..d30a090a1a --- /dev/null +++ b/boost/wave/util/functor_input.hpp @@ -0,0 +1,155 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(FUNCTOR_INPUT_HPP_ED3A4C21_8F8A_453F_B438_08214FAC106A_INCLUDED) +#define FUNCTOR_INPUT_HPP_ED3A4C21_8F8A_453F_B438_08214FAC106A_INCLUDED + +#include <boost/assert.hpp> +#include <boost/spirit/include/classic_multi_pass.hpp> +#include <boost/wave/wave_config.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 { + +/////////////////////////////////////////////////////////////////////////////// +// +// class functor_input +// +// Implementation of the InputPolicy used by multi_pass +// functor_input gets tokens from a functor +// Note: the functor must have a typedef for result_type +// It also must have a static variable of type result_type defined +// to represent eof that is called eof. +// +// This functor input policy template is essentially the same as the +// predefined multi_pass functor_input policy. The difference is, +// that the first token is not read at initialization time, but only +// just before returning the first token. Additionally it does not +// call operator new() twice but only once. +// +/////////////////////////////////////////////////////////////////////////////// +struct functor_input { + + template <typename FunctorT> + class inner { + private: + typedef typename FunctorT::result_type result_type; + + public: + typedef result_type value_type; + + private: + struct Data { + Data(FunctorT const &ftor_) + : ftor(ftor_), was_initialized(false) + {} + + FunctorT ftor; + value_type curtok; + bool was_initialized; + }; + + // Needed by compilers not implementing the resolution to DR45. For + // reference, see + // http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#45. + + friend struct Data; + + public: + typedef std::ptrdiff_t difference_type; + typedef result_type *pointer; + typedef result_type &reference; + + protected: + inner() + : data(0) + {} + + inner(FunctorT const &x) + : data(new Data(x)) + {} + + inner(inner const &x) + : data(x.data) + {} + + void destroy() + { + delete data; + data = 0; + } + + bool same_input(inner const &x) const + { + return data == x.data; + } + + void swap(inner &x) + { + boost::spirit::classic::impl::mp_swap(data, x.data); + } + + void ensure_initialized() const + { + if (data && !data->was_initialized) { + data->curtok = (data->ftor)(); // get the first token + data->was_initialized = true; + } + } + + public: + reference get_input() const + { + ensure_initialized(); + return data->curtok; + } + + void advance_input() + { + BOOST_ASSERT(0 != data); + data->curtok = (data->ftor)(); + data->was_initialized = true; + } + + bool input_at_eof() const + { + ensure_initialized(); + return !data || data->curtok == data->ftor.eof; + } + + FunctorT& get_functor() const + { + BOOST_ASSERT(0 != data); + return data->ftor; + } + + private: + mutable Data *data; + }; +}; + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(FUNCTOR_INPUT_HPP_ED3A4C21_8F8A_453F_B438_08214FAC106A_INCLUDED) diff --git a/boost/wave/util/insert_whitespace_detection.hpp b/boost/wave/util/insert_whitespace_detection.hpp new file mode 100644 index 0000000000..8485e9c1d1 --- /dev/null +++ b/boost/wave/util/insert_whitespace_detection.hpp @@ -0,0 +1,518 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + Detect the need to insert a whitespace token into the output stream + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(INSERT_WHITESPACE_DETECTION_HPP_765EF77B_0513_4967_BDD6_6A38148C4C96_INCLUDED) +#define INSERT_WHITESPACE_DETECTION_HPP_765EF77B_0513_4967_BDD6_6A38148C4C96_INCLUDED + +#include <boost/wave/wave_config.hpp> +#include <boost/wave/token_ids.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 { + +namespace impl { + +// T_IDENTIFIER + template <typename StringT> + inline bool + would_form_universal_char (StringT const &value) + { + if ('u' != value[0] && 'U' != value[0]) + return false; + if ('u' == value[0] && value.size() < 5) + return false; + if ('U' == value[0] && value.size() < 9) + return false; + + typename StringT::size_type pos = + value.find_first_not_of("0123456789abcdefABCDEF", 1); + + if (StringT::npos == pos || + ('u' == value[0] && pos > 5) || + ('U' == value[0] && pos > 9)) + { + return true; // would form an universal char + } + return false; + } + template <typename StringT> + inline bool + handle_identifier(boost::wave::token_id prev, + boost::wave::token_id before, StringT const &value) + { + using namespace boost::wave; + switch (static_cast<unsigned int>(prev)) { + case T_IDENTIFIER: + case T_NONREPLACABLE_IDENTIFIER: + case T_COMPL_ALT: + case T_OR_ALT: + case T_AND_ALT: + case T_NOT_ALT: + case T_XOR_ALT: + case T_ANDASSIGN_ALT: + case T_ORASSIGN_ALT: + case T_XORASSIGN_ALT: + case T_NOTEQUAL_ALT: + case T_FIXEDPOINTLIT: + return true; + + case T_FLOATLIT: + case T_INTLIT: + case T_PP_NUMBER: + return (value.size() > 1 || (value[0] != 'e' && value[0] != 'E')); + + // avoid constructing universal characters (\u1234) + case TOKEN_FROM_ID('\\', UnknownTokenType): + return would_form_universal_char(value); + } + return false; + } +// T_INTLIT + inline bool + handle_intlit(boost::wave::token_id prev, boost::wave::token_id /*before*/) + { + using namespace boost::wave; + switch (static_cast<unsigned int>(prev)) { + case T_IDENTIFIER: + case T_NONREPLACABLE_IDENTIFIER: + case T_INTLIT: + case T_FLOATLIT: + case T_FIXEDPOINTLIT: + case T_PP_NUMBER: + return true; + } + return false; + } +// T_FLOATLIT + inline bool + handle_floatlit(boost::wave::token_id prev, + boost::wave::token_id /*before*/) + { + using namespace boost::wave; + switch (static_cast<unsigned int>(prev)) { + case T_IDENTIFIER: + case T_NONREPLACABLE_IDENTIFIER: + case T_INTLIT: + case T_FLOATLIT: + case T_FIXEDPOINTLIT: + case T_PP_NUMBER: + return true; + } + return false; + } +// <% T_LEFTBRACE + inline bool + handle_alt_leftbrace(boost::wave::token_id prev, + boost::wave::token_id /*before*/) + { + using namespace boost::wave; + switch (static_cast<unsigned int>(prev)) { + case T_LESS: // <<% + case T_SHIFTLEFT: // <<<% + return true; + } + return false; + } +// <: T_LEFTBRACKET + inline bool + handle_alt_leftbracket(boost::wave::token_id prev, + boost::wave::token_id /*before*/) + { + using namespace boost::wave; + switch (static_cast<unsigned int>(prev)) { + case T_LESS: // <<: + case T_SHIFTLEFT: // <<<: + return true; + } + return false; + } +// T_FIXEDPOINTLIT + inline bool + handle_fixedpointlit(boost::wave::token_id prev, + boost::wave::token_id /*before*/) + { + using namespace boost::wave; + switch (static_cast<unsigned int>(prev)) { + case T_IDENTIFIER: + case T_NONREPLACABLE_IDENTIFIER: + case T_INTLIT: + case T_FLOATLIT: + case T_FIXEDPOINTLIT: + case T_PP_NUMBER: + return true; + } + return false; + } +// T_DOT + inline bool + handle_dot(boost::wave::token_id prev, boost::wave::token_id before) + { + using namespace boost::wave; + switch (static_cast<unsigned int>(prev)) { + case T_DOT: + if (T_DOT == before) + return true; // ... + break; + } + return false; + } +// T_QUESTION_MARK + inline bool + handle_questionmark(boost::wave::token_id prev, + boost::wave::token_id /*before*/) + { + using namespace boost::wave; + switch(static_cast<unsigned int>(prev)) { + case TOKEN_FROM_ID('\\', UnknownTokenType): // \? + case T_QUESTION_MARK: // ?? + return true; + } + return false; + } +// T_NEWLINE + inline bool + handle_newline(boost::wave::token_id prev, + boost::wave::token_id before) + { + using namespace boost::wave; + switch(static_cast<unsigned int>(prev)) { + case TOKEN_FROM_ID('\\', UnknownTokenType): // \ \n + case T_DIVIDE: + if (T_QUESTION_MARK == before) + return true; // ?/\n // may be \\n + break; + } + return false; + } + + inline bool + handle_parens(boost::wave::token_id prev) + { + switch (static_cast<unsigned int>(prev)) { + case T_LEFTPAREN: + case T_RIGHTPAREN: + case T_LEFTBRACKET: + case T_RIGHTBRACKET: + case T_LEFTBRACE: + case T_RIGHTBRACE: + case T_SEMICOLON: + case T_COMMA: + case T_COLON: + // no insertion between parens/brackets/braces and operators + return false; + + default: + break; + } + return true; + } + +} // namespace impl + +class insert_whitespace_detection +{ +public: + insert_whitespace_detection(bool insert_whitespace_ = true) + : insert_whitespace(insert_whitespace_), + prev(boost::wave::T_EOF), beforeprev(boost::wave::T_EOF) + {} + + template <typename StringT> + bool must_insert(boost::wave::token_id current, StringT const &value) + { + if (!insert_whitespace) + return false; // skip whitespace insertion alltogether + + using namespace boost::wave; + switch (static_cast<unsigned int>(current)) { + case T_NONREPLACABLE_IDENTIFIER: + case T_IDENTIFIER: + return impl::handle_identifier(prev, beforeprev, value); + case T_PP_NUMBER: + case T_INTLIT: + return impl::handle_intlit(prev, beforeprev); + case T_FLOATLIT: + return impl::handle_floatlit(prev, beforeprev); + case T_STRINGLIT: + if (TOKEN_FROM_ID('L', IdentifierTokenType) == prev) // 'L' + return true; + break; + case T_LEFTBRACE_ALT: + return impl::handle_alt_leftbrace(prev, beforeprev); + case T_LEFTBRACKET_ALT: + return impl::handle_alt_leftbracket(prev, beforeprev); + case T_FIXEDPOINTLIT: + return impl::handle_fixedpointlit(prev, beforeprev); + case T_DOT: + return impl::handle_dot(prev, beforeprev); + case T_QUESTION_MARK: + return impl::handle_questionmark(prev, beforeprev); + case T_NEWLINE: + return impl::handle_newline(prev, beforeprev); + + case T_LEFTPAREN: + case T_RIGHTPAREN: + case T_LEFTBRACKET: + case T_RIGHTBRACKET: + case T_SEMICOLON: + case T_COMMA: + case T_COLON: + switch (static_cast<unsigned int>(prev)) { + case T_LEFTPAREN: + case T_RIGHTPAREN: + case T_LEFTBRACKET: + case T_RIGHTBRACKET: + case T_LEFTBRACE: + case T_RIGHTBRACE: + return false; // no insertion between parens/brackets/braces + + default: + if (IS_CATEGORY(prev, OperatorTokenType)) + return false; + break; + } + break; + + case T_LEFTBRACE: + case T_RIGHTBRACE: + switch (static_cast<unsigned int>(prev)) { + case T_LEFTPAREN: + case T_RIGHTPAREN: + case T_LEFTBRACKET: + case T_RIGHTBRACKET: + case T_LEFTBRACE: + case T_RIGHTBRACE: + case T_SEMICOLON: + case T_COMMA: + case T_COLON: + return false; // no insertion between parens/brackets/braces + + case T_QUESTION_MARK: + if (T_QUESTION_MARK == beforeprev) + return true; + if (IS_CATEGORY(prev, OperatorTokenType)) + return false; + break; + + default: + break; + } + break; + + case T_MINUS: + case T_MINUSMINUS: + case T_MINUSASSIGN: + if (T_MINUS == prev || T_MINUSMINUS == prev) + return true; + if (!impl::handle_parens(prev)) + return false; + if (T_QUESTION_MARK == prev && T_QUESTION_MARK == beforeprev) + return true; + break; + + case T_PLUS: + case T_PLUSPLUS: + case T_PLUSASSIGN: + if (T_PLUS == prev || T_PLUSPLUS == prev) + return true; + if (!impl::handle_parens(prev)) + return false; + if (T_QUESTION_MARK == prev && T_QUESTION_MARK == beforeprev) + return true; + break; + + case T_DIVIDE: + case T_DIVIDEASSIGN: + if (T_DIVIDE == prev) + return true; + if (!impl::handle_parens(prev)) + return false; + if (T_QUESTION_MARK == prev && T_QUESTION_MARK == beforeprev) + return true; + break; + + case T_EQUAL: + case T_ASSIGN: + switch (static_cast<unsigned int>(prev)) { + case T_PLUSASSIGN: + case T_MINUSASSIGN: + case T_DIVIDEASSIGN: + case T_STARASSIGN: + case T_SHIFTRIGHTASSIGN: + case T_SHIFTLEFTASSIGN: + case T_EQUAL: + case T_NOTEQUAL: + case T_LESSEQUAL: + case T_GREATEREQUAL: + case T_LESS: + case T_GREATER: + case T_PLUS: + case T_MINUS: + case T_STAR: + case T_DIVIDE: + case T_ORASSIGN: + case T_ANDASSIGN: + case T_XORASSIGN: + case T_OR: + case T_AND: + case T_XOR: + case T_OROR: + case T_ANDAND: + return true; + + case T_QUESTION_MARK: + if (T_QUESTION_MARK == beforeprev) + return true; + break; + + default: + if (!impl::handle_parens(prev)) + return false; + break; + } + break; + + case T_GREATER: + if (T_MINUS == prev || T_GREATER == prev) + return true; // prevent -> or >> + if (!impl::handle_parens(prev)) + return false; + if (T_QUESTION_MARK == prev && T_QUESTION_MARK == beforeprev) + return true; + break; + + case T_LESS: + if (T_LESS == prev) + return true; // prevent << + // fall through + case T_CHARLIT: + case T_NOT: + case T_NOTEQUAL: + if (!impl::handle_parens(prev)) + return false; + if (T_QUESTION_MARK == prev && T_QUESTION_MARK == beforeprev) + return true; + break; + + case T_AND: + case T_ANDAND: + if (!impl::handle_parens(prev)) + return false; + if (T_AND == prev || T_ANDAND == prev) + return true; + break; + + case T_OR: + if (!impl::handle_parens(prev)) + return false; + if (T_OR == prev) + return true; + break; + + case T_XOR: + if (!impl::handle_parens(prev)) + return false; + if (T_XOR == prev) + return true; + break; + + case T_COMPL_ALT: + case T_OR_ALT: + case T_AND_ALT: + case T_NOT_ALT: + case T_XOR_ALT: + case T_ANDASSIGN_ALT: + case T_ORASSIGN_ALT: + case T_XORASSIGN_ALT: + case T_NOTEQUAL_ALT: + switch (static_cast<unsigned int>(prev)) { + case T_LEFTPAREN: + case T_RIGHTPAREN: + case T_LEFTBRACKET: + case T_RIGHTBRACKET: + case T_LEFTBRACE: + case T_RIGHTBRACE: + case T_SEMICOLON: + case T_COMMA: + case T_COLON: + // no insertion between parens/brackets/braces and operators + return false; + + case T_IDENTIFIER: + if (T_NONREPLACABLE_IDENTIFIER == prev || + IS_CATEGORY(prev, KeywordTokenType)) + { + return true; + } + break; + + default: + break; + } + break; + + case T_STAR: + if (T_STAR == prev) + return false; // '*****' do not need to be separated + if (T_GREATER== prev && + (T_MINUS == beforeprev || T_MINUSMINUS == beforeprev) + ) + { + return true; // prevent ->* + } + break; + + case T_POUND: + if (T_POUND == prev) + return true; + break; + } + + // FIXME: else, handle operators separately (will catch to many cases) +// if (IS_CATEGORY(current, OperatorTokenType) && +// IS_CATEGORY(prev, OperatorTokenType)) +// { +// return true; // operators must be delimited always +// } + return false; + } + void shift_tokens (boost::wave::token_id next_id) + { + if (insert_whitespace) { + beforeprev = prev; + prev = next_id; + } + } + +private: + bool insert_whitespace; // enable this component + boost::wave::token_id prev; // the previous analyzed token + boost::wave::token_id beforeprev; // the token before the previous +}; + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(INSERT_WHITESPACE_DETECTION_HPP_765EF77B_0513_4967_BDD6_6A38148C4C96_INCLUDED) diff --git a/boost/wave/util/interpret_pragma.hpp b/boost/wave/util/interpret_pragma.hpp new file mode 100644 index 0000000000..e787040b1a --- /dev/null +++ b/boost/wave/util/interpret_pragma.hpp @@ -0,0 +1,210 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(INTERPRET_PRAGMA_HPP_B1F2315E_C5CE_4ED1_A343_0EF548B7942A_INCLUDED) +#define INTERPRET_PRAGMA_HPP_B1F2315E_C5CE_4ED1_A343_0EF548B7942A_INCLUDED + +#include <string> +#include <list> + +#include <boost/spirit/include/classic_core.hpp> +#include <boost/spirit/include/classic_assign_actor.hpp> +#include <boost/spirit/include/classic_push_back_actor.hpp> +#include <boost/spirit/include/classic_confix.hpp> + +#include <boost/wave/wave_config.hpp> + +#include <boost/wave/util/pattern_parser.hpp> +#include <boost/wave/util/macro_helpers.hpp> + +#include <boost/wave/token_ids.hpp> +#include <boost/wave/cpp_exceptions.hpp> +#include <boost/wave/cpp_iteration_context.hpp> +#include <boost/wave/language_support.hpp> + +#if !defined(spirit_append_actor) +#define spirit_append_actor(actor) boost::spirit::classic::push_back_a(actor) +#define spirit_assign_actor(actor) boost::spirit::classic::assign_a(actor) +#endif // !defined(spirit_append_actor) + +// 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 { + +/////////////////////////////////////////////////////////////////////////////// +// +// The function interpret_pragma interprets the given token sequence as the +// body of a #pragma directive (or parameter to the _Pragma operator) and +// executes the actions associated with recognized Wave specific options. +// +/////////////////////////////////////////////////////////////////////////////// +template <typename ContextT, typename IteratorT, typename ContainerT> +inline bool +interpret_pragma(ContextT &ctx, typename ContextT::token_type const &act_token, + IteratorT it, IteratorT const &end, ContainerT &pending) +{ + typedef typename ContextT::token_type token_type; + typedef typename token_type::string_type string_type; + + using namespace cpplexer; + if (T_IDENTIFIER == token_id(*it)) { + // check for pragma wave ... + if ((*it).get_value() == BOOST_WAVE_PRAGMA_KEYWORD) + { + // this is a wave specific option, it should have the form: + // + // #pragma command option(value) + // + // where + // 'command' is the value of the preprocessor constant + // BOOST_WAVE_PRAGMA_KEYWORD (defaults to "wave") and + // '(value)' is required only for some pragma directives (this is + // optional) + // + // All recognized #pragma operators are forwarded to the supplied + // preprocessing hook. + using namespace boost::spirit::classic; + token_type option; + ContainerT values; + + if (!parse (++it, end, + ( ch_p(T_IDENTIFIER) + [ + spirit_assign_actor(option) + ] + | pattern_p(KeywordTokenType, + TokenTypeMask|PPTokenFlag) + [ + spirit_assign_actor(option) + ] + | pattern_p(OperatorTokenType|AltExtTokenType, + ExtTokenTypeMask|PPTokenFlag) // and, bit_and etc. + [ + spirit_assign_actor(option) + ] + | pattern_p(BoolLiteralTokenType, + TokenTypeMask|PPTokenFlag) + [ + spirit_assign_actor(option) + ] + ) + >> !comment_nest_p( + ch_p(T_LEFTPAREN), + ch_p(T_RIGHTPAREN) + )[spirit_assign_actor(values)], + pattern_p(WhiteSpaceTokenType, TokenTypeMask|PPTokenFlag)).hit) + { + typename ContextT::string_type msg( + impl::as_string<string_type>(it, end)); + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + ill_formed_pragma_option, + msg.c_str(), act_token.get_position()); + return false; + } + + // remove the falsely matched surrounding parenthesis's + if (values.size() >= 2) { + BOOST_ASSERT(T_LEFTPAREN == values.front() && T_RIGHTPAREN == values.back()); + values.erase(values.begin()); + typename ContainerT::reverse_iterator rit = values.rbegin(); + values.erase((++rit).base()); + } + + // decode the option (call the context_policy hook) + if (!ctx.get_hooks().interpret_pragma( + ctx.derived(), pending, option, values, act_token)) + { + // unknown #pragma option + string_type option_str ((*it).get_value()); + + option_str += option.get_value(); + if (values.size() > 0) { + option_str += "("; + option_str += impl::as_string(values); + option_str += ")"; + } + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + ill_formed_pragma_option, + option_str.c_str(), act_token.get_position()); + return false; + } + return true; + } +#if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 + else if ((*it).get_value() == "once") { + // #pragma once + return ctx.add_pragma_once_header(act_token, ctx.get_current_filename()); + } +#endif +#if BOOST_WAVE_SUPPORT_PRAGMA_MESSAGE != 0 + else if ((*it).get_value() == "message") { + // #pragma message(...) or #pragma message ... + using namespace boost::spirit::classic; + ContainerT values; + + if (!parse (++it, end, + ( ( ch_p(T_LEFTPAREN) + >> lexeme_d[ + *(anychar_p[spirit_append_actor(values)] - ch_p(T_RIGHTPAREN)) + ] + >> ch_p(T_RIGHTPAREN) + ) + | lexeme_d[ + *(anychar_p[spirit_append_actor(values)] - ch_p(T_NEWLINE)) + ] + ), + pattern_p(WhiteSpaceTokenType, TokenTypeMask|PPTokenFlag) + ).hit + ) + { + typename ContextT::string_type msg( + impl::as_string<string_type>(it, end)); + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + ill_formed_pragma_message, + msg.c_str(), act_token.get_position()); + return false; + } + + // remove the falsely matched closing parenthesis/newline + if (values.size() > 0) { + BOOST_ASSERT(T_RIGHTPAREN == values.back() || T_NEWLINE == values.back()); + typename ContainerT::reverse_iterator rit = values.rbegin(); + values.erase((++rit).base()); + } + + // output the message itself + typename ContextT::string_type msg(impl::as_string(values)); + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + pragma_message_directive, + msg.c_str(), act_token.get_position()); + return false; + } +#endif + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(INTERPRET_PRAGMA_HPP_B1F2315E_C5CE_4ED1_A343_0EF548B7942A_INCLUDED) diff --git a/boost/wave/util/iteration_context.hpp b/boost/wave/util/iteration_context.hpp new file mode 100644 index 0000000000..8673f46639 --- /dev/null +++ b/boost/wave/util/iteration_context.hpp @@ -0,0 +1,83 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(ITERATION_CONTEXT_HPP_9556CD16_F11E_4ADC_AC8B_FB9A174BE664_INCLUDED) +#define ITERATION_CONTEXT_HPP_9556CD16_F11E_4ADC_AC8B_FB9A174BE664_INCLUDED + +#include <cstdlib> +#include <cstdio> +#include <stack> + +#include <boost/wave/wave_config.hpp> +#include <boost/wave/cpp_exceptions.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 { + +/////////////////////////////////////////////////////////////////////////////// +template <typename IterationContextT> +class iteration_context_stack +{ + typedef std::stack<IterationContextT> base_type; + +public: + typedef typename base_type::size_type size_type; + + iteration_context_stack() + : max_include_nesting_depth(BOOST_WAVE_MAX_INCLUDE_LEVEL_DEPTH) + {} + + void set_max_include_nesting_depth(size_type new_depth) + { max_include_nesting_depth = new_depth; } + size_type get_max_include_nesting_depth() const + { return max_include_nesting_depth; } + + typename base_type::size_type size() const { return iter_ctx.size(); } + typename base_type::value_type &top() { return iter_ctx.top(); } + void pop() { iter_ctx.pop(); } + + template <typename Context, typename PositionT> + void push(Context& ctx, PositionT const &pos, + typename base_type::value_type const &val) + { + if (iter_ctx.size() == max_include_nesting_depth) { + 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)max_include_nesting_depth); + BOOST_WAVE_THROW_CTX(ctx, preprocess_exception, + include_nesting_too_deep, buffer, pos); + } + iter_ctx.push(val); + } + +private: + size_type max_include_nesting_depth; + base_type iter_ctx; +}; + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(ITERATION_CONTEXT_HPP_9556CD16_F11E_4ADC_AC8B_FB9A174BE664_INCLUDED) diff --git a/boost/wave/util/macro_definition.hpp b/boost/wave/util/macro_definition.hpp new file mode 100644 index 0000000000..16be858704 --- /dev/null +++ b/boost/wave/util/macro_definition.hpp @@ -0,0 +1,200 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(MACRO_DEFINITION_HPP_D68A639E_2DA5_4E9C_8ACD_CFE6B903831E_INCLUDED) +#define MACRO_DEFINITION_HPP_D68A639E_2DA5_4E9C_8ACD_CFE6B903831E_INCLUDED + +#include <vector> +#include <list> + +#include <boost/detail/atomic_count.hpp> +#include <boost/intrusive_ptr.hpp> + +#include <boost/wave/wave_config.hpp> +#if BOOST_WAVE_SERIALIZATION != 0 +#include <boost/serialization/serialization.hpp> +#include <boost/serialization/list.hpp> +#include <boost/serialization/vector.hpp> +#endif + +#include <boost/wave/token_ids.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 { + +/////////////////////////////////////////////////////////////////////////////// +// +// macro_definition +// +// This class containes all infos for a defined macro. +// +/////////////////////////////////////////////////////////////////////////////// +template <typename TokenT, typename ContainerT> +struct macro_definition { + + typedef std::vector<TokenT> parameter_container_type; + typedef ContainerT definition_container_type; + + typedef typename parameter_container_type::const_iterator + const_parameter_iterator_t; + typedef typename definition_container_type::const_iterator + const_definition_iterator_t; + + macro_definition(TokenT const &token_, bool has_parameters, + bool is_predefined_, long uid_) + : macroname(token_), uid(uid_), is_functionlike(has_parameters), + replaced_parameters(false), is_available_for_replacement(true), + is_predefined(is_predefined_) +#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + , has_ellipsis(false) +#endif + , use_count(0) + { + } + // generated copy constructor + // generated destructor + // generated assignment operator + + // Replace all occurrences of the parameters throughout the macrodefinition + // with special parameter tokens to simplify later macro replacement. + // Additionally mark all occurrences of the macro name itself throughout + // the macro definition + void replace_parameters() + { + using namespace boost::wave; + + if (!replaced_parameters) { + typename definition_container_type::iterator end = macrodefinition.end(); + typename definition_container_type::iterator it = macrodefinition.begin(); + + for (/**/; it != end; ++it) { + token_id id = *it; + + if (T_IDENTIFIER == id || + IS_CATEGORY(id, KeywordTokenType) || + IS_EXTCATEGORY(id, OperatorTokenType|AltExtTokenType) || + IS_CATEGORY(id, OperatorTokenType)) + { + // may be a parameter to replace + const_parameter_iterator_t cend = macroparameters.end(); + const_parameter_iterator_t cit = macroparameters.begin(); + for (typename parameter_container_type::size_type i = 0; + cit != cend; ++cit, ++i) + { + if ((*it).get_value() == (*cit).get_value()) { + (*it).set_token_id(token_id(T_PARAMETERBASE+i)); + break; + } +#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + else if (T_ELLIPSIS == token_id(*cit) && + "__VA_ARGS__" == (*it).get_value()) + { + // __VA_ARGS__ requires special handling + (*it).set_token_id(token_id(T_EXTPARAMETERBASE+i)); + break; + } +#endif + } + } + } + +#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + // we need to know, if the last of the formal arguments is an ellipsis + if (macroparameters.size() > 0 && + T_ELLIPSIS == token_id(macroparameters.back())) + { + has_ellipsis = true; + } +#endif + replaced_parameters = true; // do it only once + } + } + + TokenT macroname; // macro name + parameter_container_type macroparameters; // formal parameters + definition_container_type macrodefinition; // macro definition token sequence + long uid; // unique id of this macro + bool is_functionlike; + bool replaced_parameters; + bool is_available_for_replacement; + bool is_predefined; +#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + bool has_ellipsis; +#endif + boost::detail::atomic_count use_count; + +#if BOOST_WAVE_SERIALIZATION != 0 + // default constructor is needed for serialization only + macro_definition() + : uid(0), is_functionlike(false), replaced_parameters(false), + is_available_for_replacement(false), is_predefined(false) +#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + , has_ellipsis(false) +#endif + , use_count(0) + {} + +private: + friend class boost::serialization::access; + template<typename Archive> + void serialize(Archive &ar, const unsigned int version) + { + using namespace boost::serialization; + ar & make_nvp("name", macroname); + ar & make_nvp("parameters", macroparameters); + ar & make_nvp("definition", macrodefinition); + ar & make_nvp("uid", uid); + ar & make_nvp("is_functionlike", is_functionlike); + ar & make_nvp("has_replaced_parameters", replaced_parameters); + ar & make_nvp("is_available_for_replacement", is_available_for_replacement); + ar & make_nvp("is_predefined", is_predefined); +#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + ar & make_nvp("has_ellipsis", has_ellipsis); +#endif + } +#endif +}; + +#if BOOST_WAVE_SERIALIZATION == 0 +/////////////////////////////////////////////////////////////////////////////// +template <typename TokenT, typename ContainerT> +inline void +intrusive_ptr_add_ref(macro_definition<TokenT, ContainerT>* p) +{ + ++p->use_count; +} + +template <typename TokenT, typename ContainerT> +inline void +intrusive_ptr_release(macro_definition<TokenT, ContainerT>* p) +{ + if (--p->use_count == 0) + delete p; +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(MACRO_DEFINITION_HPP_D68A639E_2DA5_4E9C_8ACD_CFE6B903831E_INCLUDED) diff --git a/boost/wave/util/macro_helpers.hpp b/boost/wave/util/macro_helpers.hpp new file mode 100644 index 0000000000..4d156638dd --- /dev/null +++ b/boost/wave/util/macro_helpers.hpp @@ -0,0 +1,303 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(MACRO_HELPERS_HPP_931BBC99_EBFA_4692_8FBE_B555998C2C39_INCLUDED) +#define MACRO_HELPERS_HPP_931BBC99_EBFA_4692_8FBE_B555998C2C39_INCLUDED + +#include <vector> + +#include <boost/assert.hpp> +#include <boost/wave/wave_config.hpp> +#include <boost/wave/token_ids.hpp> +#include <boost/wave/cpplexer/validate_universal_char.hpp> +#include <boost/wave/util/unput_queue_iterator.hpp> + +// this must occur after all of the includes and before any code appears +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_PREFIX +#endif + +/////////////////////////////////////////////////////////////////////////////// +namespace boost { +namespace wave { +namespace util { + +namespace impl { + + // escape a string literal (insert '\\' before every '\"', '?' and '\\') + template <typename StringT> + inline StringT + escape_lit(StringT const &value) + { + StringT result; + typename StringT::size_type pos = 0; + typename StringT::size_type pos1 = value.find_first_of ("\"\\?", 0); + if (StringT::npos != pos1) { + do { + result += value.substr(pos, pos1-pos) + + StringT("\\") + + StringT(1, value[pos1]); + pos1 = value.find_first_of ("\"\\?", pos = pos1+1); + } while (StringT::npos != pos1); + result += value.substr(pos); + } + else { + result = value; + } + return result; + } + + // un-escape a string literal (remove '\\' just before '\\', '\"' or '?') + template <typename StringT> + inline StringT + unescape_lit(StringT const &value) + { + StringT result; + typename StringT::size_type pos = 0; + typename StringT::size_type pos1 = value.find_first_of ("\\", 0); + if (StringT::npos != pos1) { + do { + switch (value[pos1+1]) { + case '\\': + case '\"': + case '?': + result = result + value.substr(pos, pos1-pos); + pos1 = value.find_first_of ("\\", (pos = pos1+1)+1); + break; + + case 'n': + result = result + value.substr(pos, pos1-pos) + "\n"; + pos1 = value.find_first_of ("\\", pos = pos1+1); + ++pos; + break; + + default: + result = result + value.substr(pos, pos1-pos+1); + pos1 = value.find_first_of ("\\", pos = pos1+1); + } + + } while (pos1 != StringT::npos); + result = result + value.substr(pos); + } + else { + // the string doesn't contain any escaped character sequences + result = value; + } + return result; + } + + // return the string representation of a token sequence + template <typename ContainerT, typename PositionT> + inline typename ContainerT::value_type::string_type + as_stringlit (ContainerT const &token_sequence, PositionT const &pos) + { + using namespace boost::wave; + typedef typename ContainerT::value_type::string_type string_type; + + string_type result("\""); + bool was_whitespace = false; + typename ContainerT::const_iterator end = token_sequence.end(); + for (typename ContainerT::const_iterator it = token_sequence.begin(); + it != end; ++it) + { + token_id id = token_id(*it); + + if (IS_CATEGORY(*it, WhiteSpaceTokenType) || T_NEWLINE == id) { + if (!was_whitespace) { + // C++ standard 16.3.2.2 [cpp.stringize] + // Each occurrence of white space between the argument's + // preprocessing tokens becomes a single space character in the + // character string literal. + result += " "; + was_whitespace = true; + } + } + else if (T_STRINGLIT == id || T_CHARLIT == id) { + // string literals and character literals have to be escaped + result += impl::escape_lit((*it).get_value()); + was_whitespace = false; + } + else +#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + if (T_PLACEMARKER != id) +#endif + { + // now append this token to the string + result += (*it).get_value(); + was_whitespace = false; + } + } + result += "\""; + + // validate the resulting literal to contain no invalid universal character + // value (throws if invalid chars found) + boost::wave::cpplexer::impl::validate_literal(result, pos.get_line(), + pos.get_column(), pos.get_file()); + return result; + } + +#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + // return the string representation of a token sequence + template <typename ContainerT, typename PositionT> + inline typename ContainerT::value_type::string_type + as_stringlit (std::vector<ContainerT> const &arguments, + typename std::vector<ContainerT>::size_type i, PositionT const &pos) + { + using namespace boost::wave; + typedef typename ContainerT::value_type::string_type string_type; + + BOOST_ASSERT(i < arguments.size()); + + string_type result("\""); + bool was_whitespace = false; + + for (/**/; i < arguments.size(); ++i) { + // stringize all remaining arguments + typename ContainerT::const_iterator end = arguments[i].end(); + for (typename ContainerT::const_iterator it = arguments[i].begin(); + it != end; ++it) + { + token_id id = token_id(*it); + + if (IS_CATEGORY(*it, WhiteSpaceTokenType) || T_NEWLINE == id) { + if (!was_whitespace) { + // C++ standard 16.3.2.2 [cpp.stringize] + // Each occurrence of white space between the argument's + // preprocessing tokens becomes a single space character in the + // character string literal. + result += " "; + was_whitespace = true; + } + } + else if (T_STRINGLIT == id || T_CHARLIT == id) { + // string literals and character literals have to be escaped + result += impl::escape_lit((*it).get_value()); + was_whitespace = false; + } + else if (T_PLACEMARKER != id) { + // now append this token to the string + result += (*it).get_value(); + was_whitespace = false; + } + } + + // append comma, if not last argument + if (i < arguments.size()-1) { + result += ","; + was_whitespace = false; + } + } + result += "\""; + + // validate the resulting literal to contain no invalid universal character + // value (throws if invalid chars found) + boost::wave::cpplexer::impl::validate_literal(result, pos.get_line(), + pos.get_column(), pos.get_file()); + return result; + } +#endif // BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + + // return the string representation of a token sequence + template <typename StringT, typename IteratorT> + inline StringT + as_string(IteratorT it, IteratorT const& end) + { + StringT result; + for (/**/; it != end; ++it) + { + result += (*it).get_value(); + } + return result; + } + + // return the string representation of a token sequence + template <typename ContainerT> + inline typename ContainerT::value_type::string_type + as_string (ContainerT const &token_sequence) + { + typedef typename ContainerT::value_type::string_type string_type; + return as_string<string_type>(token_sequence.begin(), + token_sequence.end()); + } + +#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 + /////////////////////////////////////////////////////////////////////////// + // + // Copies all arguments beginning with the given index to the output + // sequence. The arguments are separated by commas. + // + template <typename ContainerT, typename PositionT> + void replace_ellipsis (std::vector<ContainerT> const &arguments, + typename ContainerT::size_type index, + ContainerT &expanded, PositionT const &pos) + { + using namespace cpplexer; + typedef typename ContainerT::value_type token_type; + + token_type comma(T_COMMA, ",", pos); + for (/**/; index < arguments.size(); ++index) { + ContainerT const &arg = arguments[index]; + + std::copy(arg.begin(), arg.end(), + std::inserter(expanded, expanded.end())); + + if (index < arguments.size()-1) + expanded.push_back(comma); + } + } +#endif + + // Skip all whitespace characters and queue the skipped characters into the + // given container + template <typename IteratorT> + inline boost::wave::token_id + skip_whitespace(IteratorT &first, IteratorT const &last) + { + token_id id = util::impl::next_token<IteratorT>::peek(first, last, false); + if (IS_CATEGORY(id, WhiteSpaceTokenType)) { + do { + ++first; + id = util::impl::next_token<IteratorT>::peek(first, last, false); + } while (IS_CATEGORY(id, WhiteSpaceTokenType)); + } + ++first; + return id; + } + + template <typename IteratorT, typename ContainerT> + inline boost::wave::token_id + skip_whitespace(IteratorT &first, IteratorT const &last, ContainerT &queue) + { + queue.push_back (*first); // queue up the current token + + token_id id = util::impl::next_token<IteratorT>::peek(first, last, false); + if (IS_CATEGORY(id, WhiteSpaceTokenType)) { + do { + queue.push_back(*++first); // queue up the next whitespace + id = util::impl::next_token<IteratorT>::peek(first, last, false); + } while (IS_CATEGORY(id, WhiteSpaceTokenType)); + } + ++first; + return id; + } + +} // namespace impl + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(MACRO_HELPERS_HPP_931BBC99_EBFA_4692_8FBE_B555998C2C39_INCLUDED) diff --git a/boost/wave/util/pattern_parser.hpp b/boost/wave/util/pattern_parser.hpp new file mode 100644 index 0000000000..876726fe4e --- /dev/null +++ b/boost/wave/util/pattern_parser.hpp @@ -0,0 +1,67 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + Global application configuration + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(BOOST_SPIRIT_PATTERN_PARSER_HPP) +#define BOOST_SPIRIT_PATTERN_PARSER_HPP + +#include <boost/spirit/include/classic_primitives.hpp> +#include <boost/wave/wave_config.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 { + + /////////////////////////////////////////////////////////////////////////// + // + // pattern_and class + // + /////////////////////////////////////////////////////////////////////////// + template <typename CharT = char> + struct pattern_and + : public boost::spirit::classic::char_parser<pattern_and<CharT> > + { + pattern_and(CharT pattern_, unsigned long pattern_mask_ = 0UL) + : pattern(pattern_), + pattern_mask((0UL != pattern_mask_) ? + pattern_mask_ : (unsigned long)pattern_) + {} + + template <typename T> + bool test(T pattern_) const + { return ((unsigned long)pattern_ & pattern_mask) == (unsigned long)pattern; } + + CharT pattern; + unsigned long pattern_mask; + }; + + template <typename CharT> + inline pattern_and<CharT> + pattern_p(CharT pattern, unsigned long pattern_mask = 0UL) + { return pattern_and<CharT>(pattern, pattern_mask); } + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // defined(BOOST_SPIRIT_PATTERN_PARSER_HPP) diff --git a/boost/wave/util/symbol_table.hpp b/boost/wave/util/symbol_table.hpp new file mode 100644 index 0000000000..0eda557fba --- /dev/null +++ b/boost/wave/util/symbol_table.hpp @@ -0,0 +1,120 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(SYMBOL_TABLE_HPP_32B0F7C6_3DD6_4113_95A5_E16516C6F45A_INCLUDED) +#define SYMBOL_TABLE_HPP_32B0F7C6_3DD6_4113_95A5_E16516C6F45A_INCLUDED + +#include <map> + +#include <boost/wave/wave_config.hpp> +#include <boost/intrusive_ptr.hpp> + +#if BOOST_WAVE_SERIALIZATION != 0 +#include <boost/serialization/serialization.hpp> +#include <boost/serialization/map.hpp> +#include <boost/shared_ptr.hpp> +#else +#include <boost/intrusive_ptr.hpp> +#endif + +#include <boost/iterator/transform_iterator.hpp> + +// this must occur after all of the includes and before any code appears +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_PREFIX +#endif + +/////////////////////////////////////////////////////////////////////////////// +namespace boost { +namespace wave { +namespace util { + +/////////////////////////////////////////////////////////////////////////////// +// +// The symbol_table class is used for the storage of defined macros. +// +/////////////////////////////////////////////////////////////////////////////// + +template <typename StringT, typename MacroDefT> +struct symbol_table +#if BOOST_WAVE_SERIALIZATION != 0 +: public std::map<StringT, boost::shared_ptr<MacroDefT> > +#else +: public std::map<StringT, boost::intrusive_ptr<MacroDefT> > +#endif +{ +#if BOOST_WAVE_SERIALIZATION != 0 + typedef std::map<StringT, boost::shared_ptr<MacroDefT> > base_type; +#else + typedef std::map<StringT, boost::intrusive_ptr<MacroDefT> > base_type; +#endif + typedef typename base_type::iterator iterator_type; + typedef typename base_type::const_iterator const_iterator_type; + + symbol_table(long uid_ = 0) + {} + +#if BOOST_WAVE_SERIALIZATION != 0 +private: + friend class boost::serialization::access; + template<typename Archive> + void serialize(Archive &ar, const unsigned int version) + { + using namespace boost::serialization; + ar & make_nvp("symbol_table", + boost::serialization::base_object<base_type>(*this)); + } +#endif + +private: + /////////////////////////////////////////////////////////////////////////// + // + // This is a special iterator allowing to iterate the names of all defined + // macros. + // + /////////////////////////////////////////////////////////////////////////// + template <typename StringT1> + struct get_first + { + typedef StringT1 const& result_type; + + template <typename First, typename Second> + StringT1 const& operator() (std::pair<First, Second> const& p) const + { + return p.first; + } + }; + typedef get_first<StringT> unary_functor; + +public: + typedef transform_iterator<unary_functor, iterator_type> + name_iterator; + typedef transform_iterator<unary_functor, const_iterator_type> + const_name_iterator; + + template <typename Iterator> + static + transform_iterator<unary_functor, Iterator> make_iterator(Iterator it) + { + return boost::make_transform_iterator<unary_functor>(it); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(SYMBOL_TABLE_HPP_32B0F7C6_3DD6_4113_95A5_E16516C6F45A_INCLUDED) diff --git a/boost/wave/util/time_conversion_helper.hpp b/boost/wave/util/time_conversion_helper.hpp new file mode 100644 index 0000000000..a477607b95 --- /dev/null +++ b/boost/wave/util/time_conversion_helper.hpp @@ -0,0 +1,153 @@ + /*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(TIME_CONVERSION_HELPER_HPP_DA97E389_1797_43BA_82AE_B071064B3EF4_INCLUDED) +#define TIME_CONVERSION_HELPER_HPP_DA97E389_1797_43BA_82AE_B071064B3EF4_INCLUDED + +#include <ctime> +#include <cstring> + +#include <boost/config.hpp> +#include <boost/spirit/include/classic_core.hpp> +#include <boost/spirit/include/classic_symbols.hpp> +#include <boost/spirit/include/classic_assign_actor.hpp> +#include <boost/spirit/include/classic_push_back_actor.hpp> + +#if !defined(spirit_append_actor) +#define spirit_append_actor(actor) boost::spirit::classic::push_back_a(actor) +#define spirit_assign_actor(actor) boost::spirit::classic::assign_a(actor) +#endif // !defined(spirit_append_actor) + +// 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 { + +namespace time_conversion { + + using namespace std; // some systems have std::tm etc. in namespace std + +/////////////////////////////////////////////////////////////////////////////// +// define, whether the rule's should generate some debug output +#define TRACE_CPP_TIME_CONVERSION \ + (BOOST_SPIRIT_DEBUG_FLAGS_CPP & BOOST_SPIRIT_DEBUG_FLAGS_TIME_CONVERSION) \ + /**/ + +/////////////////////////////////////////////////////////////////////////////// +// Grammar for parsing a date/time string generated by the C++ compiler from +// __DATE__ and __TIME__ + class time_conversion_grammar : + public boost::spirit::classic::grammar<time_conversion_grammar> + { + public: + time_conversion_grammar() : fYearIsCorrected(false) + { + using namespace std; // some systems have memset in std + memset (&time_stamp, 0, sizeof(tm)); + BOOST_SPIRIT_DEBUG_TRACE_RULE_NAME(*this, "time_conversion_grammar", + TRACE_CPP_TIME_CONVERSION); + } + + template <typename ScannerT> + struct definition { + + definition(time_conversion_grammar const &self) + { + using boost::spirit::classic::int_p; + using boost::spirit::classic::add; + + char const *m[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + for (int i = 0; i < 12; ++i) + add (month, m[i], i); + + time_rule // expected format is 'Dec 29 2001 11:23:59' + = month[spirit_assign_actor(self.time_stamp.tm_mon)] + >> int_p[spirit_assign_actor(self.time_stamp.tm_mday)] + >> int_p[spirit_assign_actor(self.time_stamp.tm_year)] + >> int_p[spirit_assign_actor(self.time_stamp.tm_hour)] >> ':' + >> int_p[spirit_assign_actor(self.time_stamp.tm_min)] >> ':' + >> int_p[spirit_assign_actor(self.time_stamp.tm_sec)] + ; + + BOOST_SPIRIT_DEBUG_TRACE_RULE(time_rule, TRACE_CPP_TIME_CONVERSION); + } + + boost::spirit::classic::rule<ScannerT> time_rule; + boost::spirit::classic::symbols<> month; + + boost::spirit::classic::rule<ScannerT> const& + start() const { return time_rule; } + }; + + void correct_year() + { + if (!fYearIsCorrected) { + time_stamp.tm_year -= 1900; + fYearIsCorrected = true; + } + } + + mutable tm time_stamp; + bool fYearIsCorrected; + }; + +/////////////////////////////////////////////////////////////////////////////// +// calculate the time of the compilation as a std::time_t to ensure correctness +// of the saved dfa table + class time_conversion_helper + { + public: + time_conversion_helper(char const *act_time) : compile_time(0) + { + using namespace boost::spirit::classic; + + time_conversion_grammar g; + parse_info<> pi = parse (act_time, g, space_p); + + if (pi.hit) { + g.correct_year(); + compile_time = mktime(&g.time_stamp); + } + BOOST_ASSERT(0 != compile_time); + } + + time_t get_time() const { return compile_time; } + + private: + time_t compile_time; + }; + +/////////////////////////////////////////////////////////////////////////////// +#undef TRACE_CPP_TIME_CONVERSION +} // namespace time_conversion + +// import time_conversion into the boost::wave::util namespace +using namespace time_conversion; + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(TIME_CONVERSION_HELPER_HPP_DA97E389_1797_43BA_82AE_B071064B3EF4_INCLUDED) diff --git a/boost/wave/util/transform_iterator.hpp b/boost/wave/util/transform_iterator.hpp new file mode 100644 index 0000000000..7477bfedbd --- /dev/null +++ b/boost/wave/util/transform_iterator.hpp @@ -0,0 +1,89 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#if !defined(TRANSFORM_ITERATOR_HPP_D492C659_88C7_4258_8C42_192F9AE80EC0_INCLUDED) +#define TRANSFORM_ITERATOR_HPP_D492C659_88C7_4258_8C42_192F9AE80EC0_INCLUDED + +#include <boost/config.hpp> +#include <boost/iterator_adaptors.hpp> +#include <boost/iterator/transform_iterator.hpp> + +#include <boost/assert.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 impl { + +/////////////////////////////////////////////////////////////////////////////// +// +// The new Boost.Iterator library already conatins a transform_iterator usable +// for our needs. The code below wraps this up. +// +/////////////////////////////////////////////////////////////////////////////// + template <class AdaptableUnaryFunctionT, class IteratorT> + class ref_transform_iterator_generator + { + typedef typename AdaptableUnaryFunctionT::result_type return_type; + typedef typename AdaptableUnaryFunctionT::argument_type argument_type; + + public: + typedef boost::transform_iterator< + return_type (*)(argument_type), IteratorT, return_type> + type; + }; + + template <class AdaptableUnaryFunctionT, class IteratorT> + inline + typename ref_transform_iterator_generator< + AdaptableUnaryFunctionT, IteratorT>::type + make_ref_transform_iterator( + IteratorT base, AdaptableUnaryFunctionT const &f) + { + typedef typename ref_transform_iterator_generator< + AdaptableUnaryFunctionT, IteratorT>::type + iterator_type; + return iterator_type(base, f.transform); + } + + // Retrieve the token value given a parse node + // This is used in conjunction with the ref_transform_iterator above, to + // get the token values while iterating directly over the parse tree. + template <typename TokenT, typename ParseTreeNodeT> + struct get_token_value { + + typedef TokenT const &result_type; + typedef ParseTreeNodeT const &argument_type; + + static result_type + transform (argument_type node) + { + BOOST_ASSERT(1 == std::distance(node.value.begin(), + node.value.end())); + return *node.value.begin(); + } + }; + +/////////////////////////////////////////////////////////////////////////////// +} // namespace impl +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(TRANSFORM_ITERATOR_HPP_D492C659_88C7_4258_8C42_192F9AE80EC0_INCLUDED) diff --git a/boost/wave/util/unput_queue_iterator.hpp b/boost/wave/util/unput_queue_iterator.hpp new file mode 100644 index 0000000000..7fc060153b --- /dev/null +++ b/boost/wave/util/unput_queue_iterator.hpp @@ -0,0 +1,295 @@ +/*============================================================================= + Boost.Wave: A Standard compliant C++ preprocessor library + + Definition of the unput queue iterator + + http://www.boost.org/ + + Copyright (c) 2001-2011 Hartmut Kaiser. Distributed under the Boost + Software License, Version 1.0. (See accompanying file + LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(UNPUT_QUEUE_ITERATOR_HPP_76DA23D0_4893_4AD5_ABCC_6CED7CFB89BC_INCLUDED) +#define UNPUT_QUEUE_ITERATOR_HPP_76DA23D0_4893_4AD5_ABCC_6CED7CFB89BC_INCLUDED + +#include <list> + +#include <boost/assert.hpp> +#include <boost/iterator_adaptors.hpp> + +#include <boost/wave/wave_config.hpp> +#include <boost/wave/token_ids.hpp> // token_id + +// 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 { + +/////////////////////////////////////////////////////////////////////////////// +// +// unput_queue_iterator +// +// The unput_queue_iterator templates encapsulates an unput_queue together +// with the direct input to be read after the unput queue is emptied +// +// This version is for the new iterator_adaptors (was released with +// Boost V1.31.0) +// +/////////////////////////////////////////////////////////////////////////////// +template <typename IteratorT, typename TokenT, typename ContainerT> +class unput_queue_iterator +: public boost::iterator_adaptor< + unput_queue_iterator<IteratorT, TokenT, ContainerT>, + IteratorT, TokenT const, std::forward_iterator_tag> +{ + typedef boost::iterator_adaptor< + unput_queue_iterator<IteratorT, TokenT, ContainerT>, + IteratorT, TokenT const, std::forward_iterator_tag> + base_type; + +public: + typedef ContainerT container_type; + typedef IteratorT iterator_type; + + unput_queue_iterator(IteratorT const &it, ContainerT &queue) + : base_type(it), unput_queue(queue) + {} + + ContainerT &get_unput_queue() + { return unput_queue; } + ContainerT const &get_unput_queue() const + { return unput_queue; } + IteratorT &get_base_iterator() + { return base_type::base_reference(); } + IteratorT const &get_base_iterator() const + { return base_type::base_reference(); } + + unput_queue_iterator &operator= (unput_queue_iterator const &rhs) + { + if (this != &rhs) { + unput_queue = rhs.unput_queue; + base_type::operator=(rhs); + } + return *this; + } + + typename base_type::reference dereference() const + { + if (!unput_queue.empty()) + return unput_queue.front(); + return *base_type::base_reference(); + } + + void increment() + { + if (!unput_queue.empty()) { + // there exist pending tokens in the unput queue + unput_queue.pop_front(); + } + else { + // the unput_queue is empty, so advance the base iterator + ++base_type::base_reference(); + } + } + + template < + typename OtherDerivedT, typename OtherIteratorT, + typename V, typename C, typename R, typename D + > + bool equal( + boost::iterator_adaptor<OtherDerivedT, OtherIteratorT, V, C, R, D> + const &x) const + { + // two iterators are equal, if both begin() iterators of the queue + // objects are equal and the base iterators are equal as well + OtherDerivedT const &rhs = static_cast<OtherDerivedT const &>(x); + return + ((unput_queue.empty() && rhs.unput_queue.empty()) || + (&unput_queue == &rhs.unput_queue && + unput_queue.begin() == rhs.unput_queue.begin() + ) + ) && + (get_base_iterator() == rhs.get_base_iterator()); + } + +private: + ContainerT &unput_queue; +}; + +namespace impl { + + /////////////////////////////////////////////////////////////////////////// + template <typename IteratorT, typename TokenT, typename ContainerT> + struct gen_unput_queue_iterator + { + typedef ContainerT container_type; + typedef IteratorT iterator_type; + typedef unput_queue_iterator<IteratorT, TokenT, ContainerT> + return_type; + + static container_type last; + + static return_type + generate(iterator_type const &it) + { + return return_type(it, last); + } + + static return_type + generate(ContainerT &queue, iterator_type const &it) + { + return return_type(it, queue); + } + }; + + template <typename IteratorT, typename TokenT, typename ContainerT> + typename gen_unput_queue_iterator<IteratorT, TokenT, ContainerT>:: + container_type + gen_unput_queue_iterator<IteratorT, TokenT, ContainerT>::last = + typename gen_unput_queue_iterator<IteratorT, TokenT, ContainerT>:: + container_type(); + + /////////////////////////////////////////////////////////////////////////// + template <typename IteratorT, typename TokenT, typename ContainerT> + struct gen_unput_queue_iterator< + unput_queue_iterator<IteratorT, TokenT, ContainerT>, + TokenT, ContainerT> + { + typedef ContainerT container_type; + typedef unput_queue_iterator<IteratorT, TokenT, ContainerT> + iterator_type; + typedef unput_queue_iterator<IteratorT, TokenT, ContainerT> + return_type; + + static container_type last; + + static return_type + generate(iterator_type &it) + { + return return_type(it.base(), last); + } + + static return_type + generate(ContainerT &queue, iterator_type &it) + { + return return_type(it.base(), queue); + } + }; + + /////////////////////////////////////////////////////////////////////////// + template <typename IteratorT> + struct assign_iterator + { + static void + do_ (IteratorT &dest, IteratorT const &src) + { + dest = src; + } + }; + + /////////////////////////////////////////////////////////////////////////// + // + // Look for the first non-whitespace token and return this token id. + // Note though, that the embedded unput_queues are not touched in any way! + // + template <typename IteratorT> + struct next_token + { + static boost::wave::token_id + peek(IteratorT it, IteratorT end, bool skip_whitespace = true) + { + using namespace boost::wave; + if (skip_whitespace) { + for (++it; it != end; ++it) { + if (!IS_CATEGORY(*it, WhiteSpaceTokenType) && + T_NEWLINE != token_id(*it)) + { + break; // stop at the first non-whitespace token + } + } + } + else { + ++it; // we have at least to look ahead + } + if (it != end) + return token_id(*it); + return T_EOI; + } + }; + + template <typename IteratorT, typename TokenT, typename ContainerT> + struct next_token< + unput_queue_iterator<IteratorT, TokenT, ContainerT> > { + + typedef unput_queue_iterator<IteratorT, TokenT, ContainerT> iterator_type; + + static boost::wave::token_id + peek(iterator_type it, iterator_type end, bool skip_whitespace = true) + { + using namespace boost::wave; + + typename iterator_type::container_type &queue = it.get_unput_queue(); + + // first try to find it in the unput_queue + if (0 != queue.size()) { + typename iterator_type::container_type::iterator cit = queue.begin(); + typename iterator_type::container_type::iterator cend = queue.end(); + + if (skip_whitespace) { + for (++cit; cit != cend; ++cit) { + if (!IS_CATEGORY(*cit, WhiteSpaceTokenType) && + T_NEWLINE != token_id(*cit)) + { + break; // stop at the first non-whitespace token + } + } + } + else { + ++cit; // we have at least to look ahead + } + if (cit != cend) + return token_id(*cit); + } + + // second try to move on into the base iterator stream + typename iterator_type::iterator_type base_it = it.get_base_iterator(); + typename iterator_type::iterator_type base_end = end.get_base_iterator(); + + if (0 == queue.size()) + ++base_it; // advance, if the unput queue is empty + + if (skip_whitespace) { + for (/**/; base_it != base_end; ++base_it) { + if (!IS_CATEGORY(*base_it, WhiteSpaceTokenType) && + T_NEWLINE != token_id(*base_it)) + { + break; // stop at the first non-whitespace token + } + } + } + if (base_it == base_end) + return T_EOI; + + return token_id(*base_it); + } + }; + +/////////////////////////////////////////////////////////////////////////////// +} // namespace impl + +/////////////////////////////////////////////////////////////////////////////// +} // namespace util +} // namespace wave +} // namespace boost + +// the suffix header occurs after all of the code +#ifdef BOOST_HAS_ABI_HEADERS +#include BOOST_ABI_SUFFIX +#endif + +#endif // !defined(UNPUT_QUEUE_ITERATOR_HPP_76DA23D0_4893_4AD5_ABCC_6CED7CFB89BC_INCLUDED) |