📄 testwave_app.cpp
字号:
/*=============================================================================
Boost.Wave: A Standard compliant C++ preprocessor library
http://www.boost.org/
Copyright (c) 2001-2005 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)
=============================================================================*/
// system headers
#include <string>
#include <iostream>
#include <vector>
// include boost
#include <boost/config.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/detail/workaround.hpp>
// include Wave
#include <boost/wave.hpp>
// include the lexer related stuff
#include <boost/wave/cpplexer/cpp_lex_token.hpp> // token type
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp> // lexer type
// this header includes the cpplexer::new_lexer_gen template used for the
// explicit template specialisation below
#include <boost/wave/cpplexer/re2clex/cpp_re2c_lexer.hpp>
// test application related headers
#include "cmd_line_utils.hpp"
#include "testwave_app.hpp"
namespace po = boost::program_options;
namespace fs = boost::filesystem;
///////////////////////////////////////////////////////////////////////////////
// testwave version definitions
#define TESTWAVE_VERSION_MAJOR 0
#define TESTWAVE_VERSION_MINOR 3
#define TESTWAVE_VERSION_SUBMINOR 0
///////////////////////////////////////////////////////////////////////////////
// workaround for missing ostringstream
#ifdef BOOST_NO_STRINGSTREAM
#include <strstream>
#define TESTWAVE_OSSTREAM std::ostrstream
std::string TESTWAVE_GETSTRING(std::ostrstream& ss)
{
ss << ends;
std::string rval = ss.str();
ss.freeze(false);
return rval;
}
#else
#include <sstream>
#define TESTWAVE_GETSTRING(ss) ss.str()
#define TESTWAVE_OSSTREAM std::ostringstream
#endif
namespace {
///////////////////////////////////////////////////////////////////////////
template <typename Iterator>
inline bool
handle_next_token(Iterator &it, Iterator const& end,
std::string &result)
{
typedef typename Iterator::value_type token_type;
token_type tok = *it++;
result = result + tok.get_value().c_str();
return (it == end) ? false : true;
}
///////////////////////////////////////////////////////////////////////////
template <typename String>
String const& handle_quoted_filepath(String &name)
{
using boost::wave::util::impl::unescape_lit;
String unesc_name = unescape_lit(name.substr(1, name.size()-2));
fs::path p (unesc_name.c_str(), fs::native);
name = String("\"") + p.leaf().c_str() + String("\"");
return name;
}
///////////////////////////////////////////////////////////////////////////
template <typename String>
String const& handle_filepath(String &name)
{
using boost::wave::util::impl::unescape_lit;
String unesc_name = unescape_lit(name);
fs::path p (unesc_name.c_str(), fs::native);
name = p.leaf().c_str();
return name;
}
///////////////////////////////////////////////////////////////////////////
template <typename Iterator>
bool handle_line_directive(Iterator &it, Iterator const& end,
std::string &result)
{
typedef typename Iterator::value_type token_type;
typedef typename token_type::string_type string_type;
if (!handle_next_token(it, end, result) || // #line
!handle_next_token(it, end, result) || // whitespace
!handle_next_token(it, end, result) || // number
!handle_next_token(it, end, result)) // whitespace
{
return false;
}
using boost::wave::util::impl::unescape_lit;
token_type filename = *it;
string_type name = filename.get_value();
handle_quoted_filepath(name);
result = result + name.c_str();
return true;
}
}
///////////////////////////////////////////////////////////////////////////
//
// This function compares the real result and the expected one but first
// replaces all occurences in the expected result of
// $E: to the result of preprocessing the given expression
// $F: to the passed full filepath
// $P: to the full path
// $V: to the current Boost version number
//
///////////////////////////////////////////////////////////////////////////
bool
testwave_app::got_expected_result(std::string const& filename,
std::string const& result, std::string& expected)
{
using boost::wave::util::impl::escape_lit;
std::string full_result;
std::string::size_type pos = 0;
std::string::size_type pos1 = expected.find_first_of("$");
if (pos1 != std::string::npos) {
do {
switch(expected[pos1+1]) {
case 'E': // preprocess the given token sequence
{
if ('(' == expected[pos1+2]) {
std::size_t p = expected.find_first_of(")", pos1+1);
if (std::string::npos == p) {
std::cerr
<< "testwave: unmatched parenthesis in $E"
" directive" << std::endl;
return false;
}
std::string source = expected.substr(pos1+3, p-pos1-3);
std::string result, error;
bool pp_result = preprocess_file(filename, source, result, error);
if (!pp_result) {
std::cerr
<< "testwave: preprocessing error in $E directive: "
<< error << std::endl;
return false;
}
full_result = full_result +
expected.substr(pos, pos1-pos) + result;
pos1 = expected.find_first_of ("$",
pos = pos1 + 4 + source.size());
}
}
break;
case 'F': // insert base file name
full_result = full_result +
expected.substr(pos, pos1-pos) + escape_lit(filename);
pos1 = expected.find_first_of ("$", pos = pos1 + 2);
break;
case 'P': // insert full path
{
fs::path fullpath = fs::complete(
fs::path(filename, fs::native),
fs::current_path());
if ('(' == expected[pos1+2]) {
// the $P(basename) syntax is used
std::size_t p = expected.find_first_of(")", pos1+1);
if (std::string::npos == p) {
std::cerr
<< "testwave: unmatched parenthesis in $P"
" directive" << std::endl;
return false;
}
std::string base = expected.substr(pos1+3, p-pos1-3);
fullpath = fullpath.branch_path() /
fs::path(base, fs::native);
full_result = full_result +
expected.substr(pos, pos1-pos) +
escape_lit(fullpath.normalize().native_file_string());
pos1 = expected.find_first_of ("$",
pos = pos1 + 4 + base.size());
}
else {
// the $P is used on its own
full_result = full_result +
expected.substr(pos, pos1-pos) +
escape_lit(fullpath.native_file_string());
pos1 = expected.find_first_of ("$", pos = pos1 + 2);
}
}
break;
case 'V': // insert Boost version
full_result = full_result +
expected.substr(pos, pos1-pos) + BOOST_LIB_VERSION;
pos1 = expected.find_first_of ("$", pos = pos1 + 2);
break;
default:
full_result = full_result +
expected.substr(pos, pos1-pos);
pos1 = expected.find_first_of ("$", (pos = pos1) + 1);
break;
}
} while(pos1 != std::string::npos);
full_result += expected.substr(pos);
}
else {
full_result = expected;
}
expected = full_result;
return full_result == result;
}
///////////////////////////////////////////////////////////////////////////////
testwave_app::testwave_app(po::variables_map const& vm)
: debuglevel(1), desc_options("Preprocessor configuration options"),
global_vm(vm)
{
desc_options.add_options()
("include,I", po::value<cmd_line_utils::include_paths>()->composing(),
"specify an additional include directory")
("sysinclude,S", po::value<std::vector<std::string> >()->composing(),
"specify an additional system include directory")
("define,D", po::value<std::vector<std::string> >()->composing(),
"specify a macro to define (as macro[=[value]])")
("predefine,P", po::value<std::vector<std::string> >()->composing(),
"specify a macro to predefine (as macro[=[value]])")
("undefine,U", po::value<std::vector<std::string> >()->composing(),
"specify a macro to undefine")
("nesting,n", po::value<int>(),
"specify a new maximal include nesting depth")
("long_long", "enable long long support in C++ mode")
("preserve", "preserve comments")
#if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
("variadics", "enable certain C99 extensions in C++ mode")
("c99", "enable C99 mode (implies --variadics)")
#endif
;
}
///////////////////////////////////////////////////////////////////////////////
//
// Test the given file (i.e. preprocess the file and compare the result
// against the embedded 'R' comments, if an error occurs compare the error
// message against the given 'E' comments).
//
///////////////////////////////////////////////////////////////////////////////
bool
testwave_app::test_a_file(std::string filename)
{
// read the input file into a string
std::string instr;
if (!read_file(filename, instr))
return false; // error was reported already
// extract expected output, preprocess the data and compare results
std::string expected;
if (extract_expected_output(filename, instr, expected)) {
bool retval = true; // assume success
std::string result, error;
bool pp_result = preprocess_file(filename, instr, result, error);
if (pp_result || !result.empty()) {
// did we expect an error?
std::string expected_error;
if (!extract_special_information(filename, instr, 'E', expected_error))
return false;
if (!expected_error.empty() &&
!got_expected_result(filename, error, expected_error))
{
// we expected an error but got none (or a different one)
if (debuglevel > 2) {
std::cerr
<< filename << ": failed" << std::endl
<< "result: " << std::endl << result << std::endl;
if (!error.empty()) {
std::cerr << "expected result: " << std::endl
<< expected << std::endl;
}
if (!expected_error.empty()) {
std::cerr << "expected error: " << std::endl
<< expected_error << std::endl;
}
}
else if (debuglevel > 1) {
std::cerr << filename << ": failed" << std::endl;
}
retval = false;
}
else if (!got_expected_result(filename, result, expected)) {
// no preprocessing error encountered
if (debuglevel > 2) {
std::cerr
<< filename << ": failed" << std::endl
<< "result: " << std::endl << result << std::endl
<< "expected: " << std::endl << expected << std::endl;
}
else if (debuglevel > 1) {
std::cerr << filename << ": failed" << std::endl;
}
retval = false;
}
else if (debuglevel > 4) {
std::cerr
<< filename << ": succeeded" << std::endl
<< "result: " << std::endl << result << std::endl;
}
else if (debuglevel > 3) {
std::cerr << filename << ": succeeded" << std::endl;
}
}
if (!pp_result) {
// there was a preprocessing error, was it expected?
std::string expected_error;
if (!extract_special_information(filename, instr, 'E', expected_error))
return false;
if (!got_expected_result(filename, error, expected_error)) {
// the error was unexpected
if (debuglevel > 2) {
std::cerr
<< filename << ": failed" << std::endl;
if (!expected_error.empty()) {
std::cerr
<< "result: " << std::endl << error << std::endl
<< "expected error: " << std::endl
<< expected_error << std::endl;
}
else {
std::cerr << "unexpected error: " << error << std::endl;
}
}
else if (debuglevel > 1) {
std::cerr << filename << ": failed" << std::endl;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -