/*============================================================================= 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(TRACE_MACRO_EXPANSION_HPP_D8469318_8407_4B9D_A19F_13CA60C1661F_INCLUDED) #define TRACE_MACRO_EXPANSION_HPP_D8469318_8407_4B9D_A19F_13CA60C1661F_INCLUDED #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "stop_watch.hpp" #ifdef BOOST_NO_STRINGSTREAM #include #define BOOST_WAVE_OSSTREAM std::ostrstream std::string BOOST_WAVE_GETSTRING(std::ostrstream& ss) { ss << std::ends; std::string rval = ss.str(); ss.freeze(false); return rval; } #else #include #define BOOST_WAVE_GETSTRING(ss) ss.str() #define BOOST_WAVE_OSSTREAM std::ostringstream #endif // trace_flags: enable single tracing functionality enum trace_flags { trace_nothing = 0, // disable tracing trace_macros = 1, // enable macro tracing trace_macro_counts = 2, // enable invocation counting trace_includes = 4, // enable include file tracing trace_guards = 8 // enable include guard tracing }; /////////////////////////////////////////////////////////////////////////////// // // Special error thrown whenever the #pragma wave system() directive is // disabled // /////////////////////////////////////////////////////////////////////////////// class bad_pragma_exception : public boost::wave::preprocess_exception { public: enum error_code { pragma_system_not_enabled = boost::wave::preprocess_exception::last_error_number + 1, pragma_mismatched_push_pop, }; bad_pragma_exception(char const *what_, error_code code, int line_, int column_, char const *filename_) throw() : boost::wave::preprocess_exception(what_, (boost::wave::preprocess_exception::error_code)code, line_, column_, filename_) { } ~bad_pragma_exception() throw() {} virtual char const *what() const throw() { return "boost::wave::bad_pragma_exception"; } virtual bool is_recoverable() const throw() { return true; } virtual int get_severity() const throw() { return boost::wave::util::severity_remark; } static char const *error_text(int code) { switch(code) { case pragma_system_not_enabled: return "the directive '#pragma wave system()' was not enabled, use the " "-x command line argument to enable the execution of"; case pragma_mismatched_push_pop: return "unbalanced #pragma push/pop in input file(s) for option"; } return "Unknown exception"; } static boost::wave::util::severity severity_level(int code) { switch(code) { case pragma_system_not_enabled: return boost::wave::util::severity_remark; case pragma_mismatched_push_pop: return boost::wave::util::severity_error; } return boost::wave::util::severity_fatal; } static char const *severity_text(int code) { return boost::wave::util::get_severity(boost::wave::util::severity_remark); } }; /////////////////////////////////////////////////////////////////////////////// // // The trace_macro_expansion policy is used to trace the macro expansion of // macros whenever it is requested from inside the input stream to preprocess // through the '#pragma wave_option(trace: enable)' directive. The macro // tracing is disabled with the help of a '#pragma wave_option(trace: disable)' // directive. // // This policy type is used as a template parameter to the boost::wave::context<> // object. // /////////////////////////////////////////////////////////////////////////////// template class trace_macro_expansion : public boost::wave::context_policies::eat_whitespace { typedef boost::wave::context_policies::eat_whitespace base_type; public: trace_macro_expansion( bool preserve_whitespace_, bool preserve_bol_whitespace_, std::ofstream &output_, std::ostream &tracestrm_, std::ostream &includestrm_, std::ostream &guardstrm_, trace_flags flags_, bool enable_system_command_, bool& generate_output_, std::string const& default_outfile_) : outputstrm(output_), tracestrm(tracestrm_), includestrm(includestrm_), guardstrm(guardstrm_), level(0), flags(flags_), logging_flags(trace_nothing), enable_system_command(enable_system_command_), preserve_whitespace(preserve_whitespace_), preserve_bol_whitespace(preserve_bol_whitespace_), generate_output(generate_output_), default_outfile(default_outfile_), emit_relative_filenames(false) { } ~trace_macro_expansion() { } void enable_macro_counting() { logging_flags = trace_flags(logging_flags | trace_macro_counts); } std::map const& get_macro_counts() const { return counts; } void enable_relative_names_in_line_directives(bool flag) { emit_relative_filenames = flag; } bool enable_relative_names_in_line_directives() const { return emit_relative_filenames; } // add a macro name, which should not be expanded at all (left untouched) void add_noexpandmacro(std::string const& name) { noexpandmacros.insert(name); } /////////////////////////////////////////////////////////////////////////// // // The function 'expanding_function_like_macro' is called whenever a // function-like macro is to be expanded. // // The parameter 'ctx' is a reference to the context object used for // instantiating the preprocessing iterators by the user. // // The parameter 'macrodef' marks the position, where the macro to expand // is defined. // // The parameter 'formal_args' holds the formal arguments used during the // definition of the macro. // // The parameter 'definition' holds the macro definition for the macro to // trace. // // The parameter 'macro_call' marks the position, where this macro invoked. // // The parameter 'arguments' holds the macro arguments used during the // invocation of the macro // // The parameters 'seqstart' and 'seqend' point into the input token // stream allowing to access the whole token sequence comprising the macro // invocation (starting with the opening parenthesis and ending after the // closing one). // // The return value defines whether the corresponding macro will be // expanded (return false) or will be copied to the output (return true). // Note: the whole argument list is copied unchanged to the output as well // without any further processing. // /////////////////////////////////////////////////////////////////////////// #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 // old signature template void expanding_function_like_macro( TokenT const ¯odef, std::vector const &formal_args, ContainerT const &definition, TokenT const ¯ocall, std::vector const &arguments) { if (enabled_macro_counting()) count_invocation(macrodef.get_value().c_str()); if (!enabled_macro_tracing()) return; #else // new signature template bool expanding_function_like_macro(ContextT const& ctx, TokenT const ¯odef, std::vector const &formal_args, ContainerT const &definition, TokenT const ¯ocall, std::vector const &arguments, IteratorT const& seqstart, IteratorT const& seqend) { if (enabled_macro_counting() || !noexpandmacros.empty()) { std::string name (macrodef.get_value().c_str()); if (noexpandmacros.find(name.c_str()) != noexpandmacros.end()) return true; // do not expand this macro if (enabled_macro_counting()) count_invocation(name.c_str()); } if (!enabled_macro_tracing()) return false; #endif if (0 == get_level()) { // output header line BOOST_WAVE_OSSTREAM stream; stream << macrocall.get_position() << ": " << macrocall.get_value() << "("; // argument list for (typename ContainerT::size_type i = 0; i < arguments.size(); ++i) { stream << boost::wave::util::impl::as_string(arguments[i]); if (i < arguments.size()-1) stream << ", "; } stream << ")" << std::endl; output(BOOST_WAVE_GETSTRING(stream)); increment_level(); } // output definition reference { BOOST_WAVE_OSSTREAM stream; stream << macrodef.get_position() << ": see macro definition: " << macrodef.get_value() << "("; // formal argument list for (typename std::vector::size_type i = 0; i < formal_args.size(); ++i) { stream << formal_args[i].get_value(); if (i < formal_args.size()-1) stream << ", "; } stream << ")" << std::endl; output(BOOST_WAVE_GETSTRING(stream)); } if (formal_args.size() > 0) { // map formal and real arguments open_trace_body("invoked with\n"); for (typename std::vector::size_type j = 0; j < formal_args.size(); ++j) { using namespace boost::wave; BOOST_WAVE_OSSTREAM stream; stream << formal_args[j].get_value() << " = "; #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0 if (T_ELLIPSIS == token_id(formal_args[j])) { // ellipsis for (typename ContainerT::size_type k = j; k < arguments.size(); ++k) { stream << boost::wave::util::impl::as_string(arguments[k]); if (k < arguments.size()-1) stream << ", "; } } else #endif { stream << boost::wave::util::impl::as_string(arguments[j]); } stream << std::endl; output(BOOST_WAVE_GETSTRING(stream)); } close_trace_body(); } open_trace_body(); #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0 return false; #endif } /////////////////////////////////////////////////////////////////////////// // // The function 'expanding_object_like_macro' is called whenever a // object-like macro is to be expanded . // // The parameter 'ctx' is a reference to the context object used for // instantiating the preprocessing iterators by the user. // // The parameter 'macrodef' marks the position, where the macro to expand // is defined. // // The definition 'definition' holds the macro definition for the macro to // trace. // // The parameter 'macrocall' marks the position, where this macro invoked. // /////////////////////////////////////////////////////////////////////////// #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 // old signature template void expanding_object_like_macro(TokenT const ¯odef, ContainerT const &definition, TokenT const ¯ocall) { if (enabled_macro_counting()) count_invocation(macrodef.get_value().c_str()); if (!enabled_macro_tracing()) return; #else // new signature template bool expanding_object_like_macro(ContextT const& ctx, TokenT const ¯odef, ContainerT const &definition, TokenT const ¯ocall) { if (enabled_macro_counting() || !noexpandmacros.empty()) { std::string name (macrodef.get_value().c_str()); if (noexpandmacros.find(name.c_str()) != noexpandmacros.end()) return true; // do not expand this macro if (enabled_macro_counting()) count_invocation(name.c_str()); } if (!enabled_macro_tracing()) return false; #endif if (0 == get_level()) { // output header line BOOST_WAVE_OSSTREAM stream; stream << macrocall.get_position() << ": " << macrocall.get_value() << std::endl; output(BOOST_WAVE_GETSTRING(stream)); increment_level(); } // output definition reference { BOOST_WAVE_OSSTREAM stream; stream << macrodef.get_position() << ": see macro definition: " << macrodef.get_value() << std::endl; output(BOOST_WAVE_GETSTRING(stream)); } open_trace_body(); #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS == 0 return false; #endif } /////////////////////////////////////////////////////////////////////////// // // The function 'expanded_macro' is called whenever the expansion of a // macro is finished but before the rescanning process starts. // // The parameter 'ctx' is a reference to the context object used for // instantiating the preprocessing iterators by the user. // // The parameter 'result' contains the token sequence generated as the // result of the macro expansion. // /////////////////////////////////////////////////////////////////////////// #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 // old signature template void expanded_macro(ContainerT const &result) #else // new signature template void expanded_macro(ContextT const& ctx,ContainerT const &result) #endif { if (!enabled_macro_tracing()) return; BOOST_WAVE_OSSTREAM stream; stream << boost::wave::util::impl::as_string(result) << std::endl; output(BOOST_WAVE_GETSTRING(stream)); open_trace_body("rescanning\n"); } /////////////////////////////////////////////////////////////////////////// // // The function 'rescanned_macro' is called whenever the rescanning of a // macro is finished. // // The parameter 'ctx' is a reference to the context object used for // instantiating the preprocessing iterators by the user. // // The parameter 'result' contains the token sequence generated as the // result of the rescanning. // /////////////////////////////////////////////////////////////////////////// #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 // old signature template void rescanned_macro(ContainerT const &result) #else // new signature template void rescanned_macro(ContextT const& ctx,ContainerT const &result) #endif { if (!enabled_macro_tracing() || get_level() == 0) return; BOOST_WAVE_OSSTREAM stream; stream << boost::wave::util::impl::as_string(result) << std::endl; output(BOOST_WAVE_GETSTRING(stream)); close_trace_body(); close_trace_body(); if (1 == get_level()) decrement_level(); } /////////////////////////////////////////////////////////////////////////// // // The function 'interpret_pragma' is called whenever a #pragma command // directive is found which isn't known to the core Wave library, where // command is the value defined as the BOOST_WAVE_PRAGMA_KEYWORD constant // which defaults to "wave". // // The parameter 'ctx' is a reference to the context object used for // instantiating the preprocessing iterators by the user. // // The parameter 'pending' may be used to push tokens back into the input // stream, which are to be used as the replacement text for the whole // #pragma directive. // // The parameter 'option' contains the name of the interpreted pragma. // // The parameter 'values' holds the values of the parameter provided to // the pragma operator. // // The parameter 'act_token' contains the actual #pragma token, which may // be used for error output. // // If the return value is 'false', the whole #pragma directive is // interpreted as unknown and a corresponding error message is issued. A // return value of 'true' signs a successful interpretation of the given // #pragma. // /////////////////////////////////////////////////////////////////////////// template bool interpret_pragma(ContextT &ctx, ContainerT &pending, typename ContextT::token_type const &option, ContainerT const &valuetokens, typename ContextT::token_type const &act_token) { typedef typename ContextT::token_type token_type; ContainerT values(valuetokens); boost::wave::util::impl::trim_sequence(values); // trim whitespace if (option.get_value() == "timer") { // #pragma wave timer(value) if (0 == values.size()) { // no value means '1' using namespace boost::wave; timer(token_type(T_INTLIT, "1", act_token.get_position())); } else { timer(values.front()); } return true; } if (option.get_value() == "trace") { // enable/disable tracing option return interpret_pragma_trace(ctx, values, act_token); } if (option.get_value() == "system") { if (!enable_system_command) { // if the #pragma wave system() directive is not enabled, throw // a corresponding error (actually its a remark), typename ContextT::string_type msg( boost::wave::util::impl::as_string(values)); BOOST_WAVE_THROW_CTX(ctx, bad_pragma_exception, pragma_system_not_enabled, msg.c_str(), act_token.get_position()); return false; } // try to spawn the given argument as a system command and return the // std::cout of this process as the replacement of this _Pragma return interpret_pragma_system(ctx, pending, values, act_token); } if (option.get_value() == "stop") { // stop the execution and output the argument typename ContextT::string_type msg( boost::wave::util::impl::as_string(values)); BOOST_WAVE_THROW_CTX(ctx, boost::wave::preprocess_exception, error_directive, msg.c_str(), act_token.get_position()); return false; } if (option.get_value() == "option") { // handle different options return interpret_pragma_option(ctx, values, act_token); } return false; } /////////////////////////////////////////////////////////////////////////// // // The function 'emit_line_directive' is called whenever a #line directive // has to be emitted into the generated output. // // The parameter 'ctx' is a reference to the context object used for // instantiating the preprocessing iterators by the user. // // The parameter 'pending' may be used to push tokens back into the input // stream, which are to be used instead of the default output generated // for the #line directive. // // The parameter 'act_token' contains the actual #pragma token, which may // be used for error output. The line number stored in this token can be // used as the line number emitted as part of the #line directive. // // If the return value is 'false', a default #line directive is emitted // by the library. A return value of 'true' will inhibit any further // actions, the tokens contained in 'pending' will be copied verbatim // to the output. // /////////////////////////////////////////////////////////////////////////// template bool emit_line_directive(ContextT const& ctx, ContainerT &pending, typename ContextT::token_type const& act_token) { if (!need_emit_line_directives(ctx.get_language()) || !enable_relative_names_in_line_directives()) { return false; } // emit a #line directive showing the relative filename instead typename ContextT::position_type pos = act_token.get_position(); unsigned int column = 6; typedef typename ContextT::token_type result_type; using namespace boost::wave; 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 pending.push_back(result_type(T_SPACE, " ", pos)); pos.set_column(++column); // account for ' ' std::string file("\""); boost::filesystem::path filename( boost::wave::util::create_path(ctx.get_current_relative_filename().c_str())); using boost::wave::util::impl::escape_lit; file += escape_lit(boost::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)); return true; } /////////////////////////////////////////////////////////////////////////// // // The function 'opened_include_file' is called whenever a file referred // by an #include directive was successfully located and opened. // // The parameter 'ctx' is a reference to the context object used for // instantiating the preprocessing iterators by the user. // // The parameter 'filename' contains the file system path of the // opened file (this is relative to the directory of the currently // processed file or a absolute path depending on the paths given as the // include search paths). // // The include_depth parameter contains the current include file depth. // // The is_system_include parameter denotes, whether the given file was // found as a result of a #include <...> directive. // /////////////////////////////////////////////////////////////////////////// #if BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS != 0 // old signature void opened_include_file(std::string const &relname, std::string const &absname, std::size_t include_depth, bool is_system_include) { #else // new signature template void opened_include_file(ContextT const& ctx, std::string const &relname, std::string const &absname, bool is_system_include) { std::size_t include_depth = ctx.get_iteration_depth(); #endif if (enabled_include_tracing()) { // print indented filename for (std::size_t i = 0; i < include_depth; ++i) includestrm << " "; if (is_system_include) includestrm << "<" << relname << "> (" << absname << ")"; else includestrm << "\"" << relname << "\" (" << absname << ")"; includestrm << std::endl; } } #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 /////////////////////////////////////////////////////////////////////////// // // The function 'detected_include_guard' is called whenever either a // include file is about to be added to the list of #pragma once headers. // That means this header file will not be opened and parsed again even // if it is specified in a later #include directive. // This function is called as the result of a detected include guard // scheme. // // The implemented heuristics for include guards detects two forms of // include guards: // // #ifndef INCLUDE_GUARD_MACRO // #define INCLUDE_GUARD_MACRO // ... // #endif // // or // // if !defined(INCLUDE_GUARD_MACRO) // #define INCLUDE_GUARD_MACRO // ... // #endif // // note, that the parenthesis are optional (i.e. !defined INCLUDE_GUARD_MACRO // will work as well). The code allows for any whitespace, newline and single // '#' tokens before the #if/#ifndef and after the final #endif. // // The parameter 'ctx' is a reference to the context object used for // instantiating the preprocessing iterators by the user. // // The parameter 'filename' contains the file system path of the // opened file (this is relative to the directory of the currently // processed file or a absolute path depending on the paths given as the // include search paths). // // The parameter contains the name of the detected include guard. // /////////////////////////////////////////////////////////////////////////// template void detected_include_guard(ContextT const& ctx, std::string const& filename, std::string const& include_guard) { if (enabled_guard_tracing()) { guardstrm << include_guard << ":" << std::endl << " " << filename << std::endl; } } #endif /////////////////////////////////////////////////////////////////////////// // // The function 'may_skip_whitespace' will be called by the // library whenever a token is about to be returned to the calling // application. // // The parameter 'ctx' is a reference to the context object used for // instantiating the preprocessing iterators by the user. // // The 'token' parameter holds a reference to the current token. The policy // is free to change this token if needed. // // The 'skipped_newline' parameter holds a reference to a boolean value // which should be set to true by the policy function whenever a newline // is going to be skipped. // // If the return value is true, the given token is skipped and the // preprocessing continues to the next token. If the return value is // false, the given token is returned to the calling application. // // ATTENTION! // Caution has to be used, because by returning true the policy function // is able to force skipping even significant tokens, not only whitespace. // /////////////////////////////////////////////////////////////////////////// template bool may_skip_whitespace(ContextT const &ctx, TokenT &token, bool &skipped_newline) { return this->base_type::may_skip_whitespace( ctx, token, need_preserve_comments(ctx.get_language()), preserve_bol_whitespace, skipped_newline) ? !preserve_whitespace : false; } /////////////////////////////////////////////////////////////////////////// // // The function 'throw_exception' will be called by the library whenever a // preprocessing exception occurs. // // The parameter 'ctx' is a reference to the context object used for // instantiating the preprocessing iterators by the user. // // The parameter 'e' is the exception object containing detailed error // information. // // The default behavior is to call the function boost::throw_exception. // /////////////////////////////////////////////////////////////////////////// template void throw_exception(ContextT const& ctx, boost::wave::preprocess_exception const& e) { #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0 if (!is_import_directive_error(e)) boost::throw_exception(e); #else boost::throw_exception(e); #endif } using base_type::throw_exception; protected: #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0 /////////////////////////////////////////////////////////////////////////// // Avoid throwing an error from a #import directive bool is_import_directive_error(boost::wave::preprocess_exception const& e) { using namespace boost::wave; if (e.get_errorcode() != preprocess_exception::ill_formed_directive) return false; // the error string is formatted as 'severity: error: directive' std::string error(e.description()); std::string::size_type p = error.find_last_of(":"); return p != std::string::npos && error.substr(p+2) == "import"; } #endif /////////////////////////////////////////////////////////////////////////// // Interpret the different Wave specific pragma directives/operators template bool interpret_pragma_trace(ContextT& ctx, ContainerT const &values, typename ContextT::token_type const &act_token) { typedef typename ContextT::token_type token_type; typedef typename token_type::string_type string_type; bool valid_option = false; if (1 == values.size()) { token_type const &value = values.front(); if (value.get_value() == "enable" || value.get_value() == "on" || value.get_value() == "1") { // #pragma wave trace(enable) enable_tracing(static_cast( tracing_enabled() | trace_macros)); valid_option = true; } else if (value.get_value() == "disable" || value.get_value() == "off" || value.get_value() == "0") { // #pragma wave trace(disable) enable_tracing(static_cast( tracing_enabled() & ~trace_macros)); valid_option = true; } } if (!valid_option) { // unknown option value string_type option_str ("trace"); if (values.size() > 0) { option_str += "("; option_str += boost::wave::util::impl::as_string(values); option_str += ")"; } BOOST_WAVE_THROW_CTX(ctx, boost::wave::preprocess_exception, ill_formed_pragma_option, option_str.c_str(), act_token.get_position()); return false; } return true; } /////////////////////////////////////////////////////////////////////////// // interpret the pragma wave option(preserve: [0|1|2|3|push|pop]) directive template static bool interpret_pragma_option_preserve_set(int mode, bool &preserve_whitespace, bool& preserve_bol_whitespace, ContextT &ctx) { switch(mode) { // preserve no whitespace case 0: preserve_whitespace = false; preserve_bol_whitespace = false; ctx.set_language( enable_preserve_comments(ctx.get_language(), false), false); break; // preserve BOL whitespace only case 1: preserve_whitespace = false; preserve_bol_whitespace = true; ctx.set_language( enable_preserve_comments(ctx.get_language(), false), false); break; // preserve comments and BOL whitespace only case 2: preserve_whitespace = false; preserve_bol_whitespace = true; ctx.set_language( enable_preserve_comments(ctx.get_language()), false); break; // preserve all whitespace case 3: preserve_whitespace = true; preserve_bol_whitespace = true; ctx.set_language( enable_preserve_comments(ctx.get_language()), false); break; default: return false; } return true; } template bool interpret_pragma_option_preserve(ContextT &ctx, IteratorT &it, IteratorT end, typename ContextT::token_type const &act_token) { using namespace boost::wave; token_id id = util::impl::skip_whitespace(it, end); if (T_COLON == id) id = util::impl::skip_whitespace(it, end); // implement push/pop if (T_IDENTIFIER == id) { if ((*it).get_value() == "push") { // push current preserve option onto the internal option stack if (need_preserve_comments(ctx.get_language())) { if (preserve_whitespace) preserve_options.push(3); else preserve_options.push(2); } else if (preserve_bol_whitespace) { preserve_options.push(1); } else { preserve_options.push(0); } return true; } else if ((*it).get_value() == "pop") { // test for mismatched push/pop #pragmas if (preserve_options.empty()) { BOOST_WAVE_THROW_CTX(ctx, bad_pragma_exception, pragma_mismatched_push_pop, "preserve", act_token.get_position()); } // pop output preserve from the internal option stack bool result = interpret_pragma_option_preserve_set( preserve_options.top(), preserve_whitespace, preserve_bol_whitespace, ctx); preserve_options.pop(); return result; } return false; } if (T_PP_NUMBER != id) return false; using namespace std; // some platforms have atoi in namespace std return interpret_pragma_option_preserve_set( atoi((*it).get_value().c_str()), preserve_whitespace, preserve_bol_whitespace, ctx); } // interpret the pragma wave option(line: [0|1|2|push|pop]) directive template bool interpret_pragma_option_line(ContextT &ctx, IteratorT &it, IteratorT end, typename ContextT::token_type const &act_token) { using namespace boost::wave; token_id id = util::impl::skip_whitespace(it, end); if (T_COLON == id) id = util::impl::skip_whitespace(it, end); // implement push/pop if (T_IDENTIFIER == id) { if ((*it).get_value() == "push") { // push current line option onto the internal option stack int mode = 0; if (need_emit_line_directives(ctx.get_language())) { mode = 1; if (enable_relative_names_in_line_directives()) mode = 2; } line_options.push(mode); return true; } else if ((*it).get_value() == "pop") { // test for mismatched push/pop #pragmas if (line_options.empty()) { BOOST_WAVE_THROW_CTX(ctx, bad_pragma_exception, pragma_mismatched_push_pop, "line", act_token.get_position()); } // pop output line from the internal option stack ctx.set_language( enable_emit_line_directives(ctx.get_language(), 0 != line_options.top()), false); enable_relative_names_in_line_directives(2 == line_options.top()); line_options.pop(); return true; } return false; } if (T_PP_NUMBER != id) return false; using namespace std; // some platforms have atoi in namespace std int emit_lines = atoi((*it).get_value().c_str()); if (0 == emit_lines || 1 == emit_lines || 2 == emit_lines) { // set the new emit #line directive mode ctx.set_language( enable_emit_line_directives(ctx.get_language(), emit_lines), false); return true; } return false; } // interpret the pragma wave option(output: ["filename"|null|default|push|pop]) // directive template bool interpret_pragma_option_output_open(boost::filesystem::path &fpath, ContextT& ctx, typename ContextT::token_type const &act_token) { namespace fs = boost::filesystem; // ensure all directories for this file do exist fs::create_directories(boost::wave::util::branch_path(fpath)); // figure out, whether the file has been written to by us, if yes, we // append any output to this file, otherwise we overwrite it std::ios::openmode mode = std::ios::out; if (fs::exists(fpath) && written_by_us.find(fpath) != written_by_us.end()) mode = (std::ios::openmode)(std::ios::out | std::ios::app); written_by_us.insert(fpath); // close the current file if (outputstrm.is_open()) outputstrm.close(); // open the new file outputstrm.open(fpath.string().c_str(), mode); if (!outputstrm.is_open()) { BOOST_WAVE_THROW_CTX(ctx, boost::wave::preprocess_exception, could_not_open_output_file, fpath.string().c_str(), act_token.get_position()); return false; } generate_output = true; current_outfile = fpath; return true; } bool interpret_pragma_option_output_close(bool generate) { if (outputstrm.is_open()) outputstrm.close(); current_outfile = boost::filesystem::path(); generate_output = generate; return true; } template bool interpret_pragma_option_output(ContextT &ctx, IteratorT &it, IteratorT end, typename ContextT::token_type const &act_token) { using namespace boost::wave; namespace fs = boost::filesystem; typedef typename ContextT::token_type token_type; typedef typename token_type::string_type string_type; token_id id = util::impl::skip_whitespace(it, end); if (T_COLON == id) id = util::impl::skip_whitespace(it, end); bool result = false; if (T_STRINGLIT == id) { namespace fs = boost::filesystem; string_type fname ((*it).get_value()); fs::path fpath (boost::wave::util::create_path( util::impl::unescape_lit(fname.substr(1, fname.size()-2)).c_str())); fpath = boost::wave::util::complete_path(fpath, ctx.get_current_directory()); result = interpret_pragma_option_output_open(fpath, ctx, act_token); } else if (T_IDENTIFIER == id) { if ((*it).get_value() == "null") { // suppress all output from this point on result = interpret_pragma_option_output_close(false); } else if ((*it).get_value() == "push") { // initialize the current_outfile, if appropriate if (output_options.empty() && current_outfile.empty() && !default_outfile.empty() && default_outfile != "-") { current_outfile = boost::wave::util::complete_path( default_outfile, ctx.get_current_directory()); } // push current output option onto the internal option stack output_options.push( output_option_type(generate_output, current_outfile)); result = true; } else if ((*it).get_value() == "pop") { // test for mismatched push/pop #pragmas if (output_options.empty()) { BOOST_WAVE_THROW_CTX(ctx, bad_pragma_exception, pragma_mismatched_push_pop, "output", act_token.get_position()); return false; } // pop output option from the internal option stack output_option_type const& opts = output_options.top(); generate_output = opts.first; current_outfile = opts.second; if (!current_outfile.empty()) { // re-open the last file result = interpret_pragma_option_output_open(current_outfile, ctx, act_token); } else { // either no output or generate to std::cout result = interpret_pragma_option_output_close(generate_output); } output_options.pop(); } } else if (T_DEFAULT == id) { // re-open the default output given on command line if (!default_outfile.empty()) { if (default_outfile == "-") { // the output was suppressed on the command line result = interpret_pragma_option_output_close(false); } else { // there was a file name on the command line fs::path fpath(boost::wave::util::create_path(default_outfile)); result = interpret_pragma_option_output_open(fpath, ctx, act_token); } } else { // generate the output to std::cout result = interpret_pragma_option_output_close(true); } } return result; } /////////////////////////////////////////////////////////////////////////// // join all adjacent string tokens into the first one template StringT unlit(StringT const& str) { return str.substr(1, str.size()-2); } template StringT merge_string_lits(StringT const& lhs, StringT const& rhs) { StringT result ("\""); result += unlit(lhs); result += unlit(rhs); result += "\""; return result; } template void join_adjacent_string_tokens(ContextT &ctx, ContainerT const& values, ContainerT& joined_values) { using namespace boost::wave; typedef typename ContextT::token_type token_type; typedef typename token_type::string_type string_type; typedef typename ContainerT::const_iterator const_iterator; typedef typename ContainerT::iterator iterator; token_type* current = 0; const_iterator end = values.end(); for (const_iterator it = values.begin(); it != end; ++it) { token_id id(*it); if (id == T_STRINGLIT) { if (!current) { joined_values.push_back(*it); current = &joined_values.back(); } else { current->set_value(merge_string_lits( current->get_value(), (*it).get_value())); } } else if (current) { typedef util::impl::next_token next_token_type; token_id next_id (next_token_type::peek(it, end, true)); if (next_id != T_STRINGLIT) { current = 0; joined_values.push_back(*it); } } else { joined_values.push_back(*it); } } } /////////////////////////////////////////////////////////////////////////// // interpret the pragma wave option() directives template bool interpret_pragma_option(ContextT &ctx, ContainerT const &cvalues, typename ContextT::token_type const &act_token) { using namespace boost::wave; typedef typename ContextT::token_type token_type; typedef typename token_type::string_type string_type; typedef typename ContainerT::const_iterator const_iterator; ContainerT values; join_adjacent_string_tokens(ctx, cvalues, values); const_iterator end = values.end(); for (const_iterator it = values.begin(); it != end; /**/) { bool valid_option = false; token_type const &value = *it; if (value.get_value() == "preserve") { // #pragma wave option(preserve: [0|1|2|3|push|pop]) valid_option = interpret_pragma_option_preserve(ctx, it, end, act_token); } else if (value.get_value() == "line") { // #pragma wave option(line: [0|1|2|push|pop]) valid_option = interpret_pragma_option_line(ctx, it, end, act_token); } else if (value.get_value() == "output") { // #pragma wave option(output: ["filename"|null|default|push|pop]) valid_option = interpret_pragma_option_output(ctx, it, end, act_token); } if (!valid_option) { // unknown option value string_type option_str ("option"); if (values.size() > 0) { option_str += "("; option_str += util::impl::as_string(values); option_str += ")"; } BOOST_WAVE_THROW_CTX(ctx, boost::wave::preprocess_exception, ill_formed_pragma_option, option_str.c_str(), act_token.get_position()); return false; } token_id id = util::impl::skip_whitespace(it, end); if (id == T_COMMA) util::impl::skip_whitespace(it, end); } return true; } /////////////////////////////////////////////////////////////////////////// // interpret the #pragma wave system() directive template bool interpret_pragma_system(ContextT& ctx, ContainerT &pending, ContainerT const &values, typename ContextT::token_type const &act_token) { typedef typename ContextT::token_type token_type; typedef typename token_type::string_type string_type; if (0 == values.size()) return false; // ill_formed_pragma_option string_type stdout_file(std::tmpnam(0)); string_type stderr_file(std::tmpnam(0)); string_type system_str(boost::wave::util::impl::as_string(values)); string_type native_cmd(system_str); system_str += " >" + stdout_file + " 2>" + stderr_file; if (0 != std::system(system_str.c_str())) { // unable to spawn the command string_type error_str("unable to spawn command: "); error_str += native_cmd; BOOST_WAVE_THROW_CTX(ctx, boost::wave::preprocess_exception, ill_formed_pragma_option, error_str.c_str(), act_token.get_position()); return false; } // rescan the content of the stdout_file and insert it as the // _Pragma replacement typedef typename ContextT::lexer_type lexer_type; typedef typename ContextT::input_policy_type input_policy_type; typedef boost::wave::iteration_context< ContextT, lexer_type, input_policy_type> iteration_context_type; iteration_context_type iter_ctx(ctx, stdout_file.c_str(), act_token.get_position(), ctx.get_language()); ContainerT pragma; for (/**/; iter_ctx.first != iter_ctx.last; ++iter_ctx.first) pragma.push_back(*iter_ctx.first); // prepend the newly generated token sequence to the 'pending' container pending.splice(pending.begin(), pragma); // erase the created tempfiles std::remove(stdout_file.c_str()); std::remove(stderr_file.c_str()); return true; } /////////////////////////////////////////////////////////////////////////// // The function enable_tracing is called, whenever the status of the // tracing was changed. // The parameter 'enable' is to be used as the new tracing status. void enable_tracing(trace_flags flags) { logging_flags = flags; } // The function tracing_enabled should return the current tracing status. trace_flags tracing_enabled() { return logging_flags; } // Helper functions for generating the trace output void open_trace_body(char const *label = 0) { if (label) output(label); output("[\n"); increment_level(); } void close_trace_body() { if (get_level() > 0) { decrement_level(); output("]\n"); tracestrm << std::flush; // flush the stream buffer } } template void output(StringT const &outstr) const { indent(get_level()); tracestrm << outstr; // output the given string } void indent(int level) const { for (int i = 0; i < level; ++i) tracestrm << " "; // indent } int increment_level() { return ++level; } int decrement_level() { BOOST_ASSERT(level > 0); return --level; } int get_level() const { return level; } bool enabled_macro_tracing() const { return (flags & trace_macros) && (logging_flags & trace_macros); } bool enabled_include_tracing() const { return (flags & trace_includes); } bool enabled_guard_tracing() const { return (flags & trace_guards); } bool enabled_macro_counting() const { return logging_flags & trace_macro_counts; } void count_invocation(std::string const& name) { typedef std::map::iterator iterator; typedef std::map::value_type value_type; iterator it = counts.find(name); if (it == counts.end()) { std::pair p = counts.insert(value_type(name, 0)); if (p.second) it = p.first; } if (it != counts.end()) ++(*it).second; } void timer(TokenT const &value) { if (value.get_value() == "0" || value.get_value() == "restart") { // restart the timer elapsed_time.restart(); } else if (value.get_value() == "1") { // print out the current elapsed time std::cerr << value.get_position() << ": " << elapsed_time.format_elapsed_time() << std::endl; } else if (value.get_value() == "suspend") { // suspend the timer elapsed_time.suspend(); } else if (value.get_value() == "resume") { // resume the timer elapsed_time.resume(); } } private: std::ofstream &outputstrm; // main output stream std::ostream &tracestrm; // trace output stream std::ostream &includestrm; // included list output stream std::ostream &guardstrm; // include guard output stream int level; // indentation level trace_flags flags; // enabled globally trace_flags logging_flags; // enabled by a #pragma bool enable_system_command; // enable #pragma wave system() command bool preserve_whitespace; // enable whitespace preservation bool preserve_bol_whitespace; // enable begin of line whitespace preservation bool& generate_output; // allow generated tokens to be streamed to output std::string const& default_outfile; // name of the output file given on command line boost::filesystem::path current_outfile; // name of the current output file stop_watch elapsed_time; // trace timings std::set written_by_us; // all files we have written to typedef std::pair output_option_type; std::stack output_options; // output option stack std::stack line_options; // line option stack std::stack preserve_options; // preserve option stack std::map counts; // macro invocation counts bool emit_relative_filenames; // emit relative names in #line directives std::set noexpandmacros; // list of macros not to expand }; #undef BOOST_WAVE_GETSTRING #undef BOOST_WAVE_OSSTREAM #endif // !defined(TRACE_MACRO_EXPANSION_HPP_D8469318_8407_4B9D_A19F_13CA60C1661F_INCLUDED)