Fixed #2559 (Refactoring Preprocessor::read)
This commit is contained in:
parent
90bf801c83
commit
b3e8ef9d48
|
@ -109,39 +109,19 @@ static std::string join(const std::list<std::string> &list, char separator)
|
||||||
/** Just read the code into a string. Perform simple cleanup of the code */
|
/** Just read the code into a string. Perform simple cleanup of the code */
|
||||||
std::string Preprocessor::read(std::istream &istr, const std::string &filename, Settings *settings)
|
std::string Preprocessor::read(std::istream &istr, const std::string &filename, Settings *settings)
|
||||||
{
|
{
|
||||||
// Get filedata from stream..
|
// ------------------------------------------------------------------------------------------
|
||||||
bool ignoreSpace = true;
|
//
|
||||||
|
|
||||||
// need space.. #if( => #if (
|
|
||||||
bool needSpace = false;
|
|
||||||
|
|
||||||
// handling <backspace><newline>
|
// handling <backspace><newline>
|
||||||
// when this is encountered the <backspace><newline> will be "skipped".
|
// when this is encountered the <backspace><newline> will be "skipped".
|
||||||
// on the next <newline>, extra newlines will be added
|
// on the next <newline>, extra newlines will be added
|
||||||
unsigned int newlines = 0;
|
|
||||||
|
|
||||||
std::ostringstream code;
|
std::ostringstream code;
|
||||||
|
unsigned int newlines = 0;
|
||||||
for (unsigned char ch = readChar(istr); istr.good(); ch = readChar(istr))
|
for (unsigned char ch = readChar(istr); istr.good(); ch = readChar(istr))
|
||||||
{
|
{
|
||||||
// Replace assorted special chars with spaces..
|
// Replace assorted special chars with spaces..
|
||||||
if (((ch & 0x80) == 0) && (ch != '\n') && (std::isspace(ch) || std::iscntrl(ch)))
|
if (((ch & 0x80) == 0) && (ch != '\n') && (std::isspace(ch) || std::iscntrl(ch)))
|
||||||
ch = ' ';
|
ch = ' ';
|
||||||
|
|
||||||
// Skip spaces after ' ' and after '#'
|
|
||||||
if (ch == ' ' && ignoreSpace)
|
|
||||||
continue;
|
|
||||||
ignoreSpace = bool(ch == ' ' || ch == '#' || ch == '\n');
|
|
||||||
|
|
||||||
if (needSpace)
|
|
||||||
{
|
|
||||||
if (ch == '(' || ch == '!')
|
|
||||||
code << " ";
|
|
||||||
else if (!std::isalpha(ch))
|
|
||||||
needSpace = false;
|
|
||||||
}
|
|
||||||
if (ch == '#')
|
|
||||||
needSpace = true;
|
|
||||||
|
|
||||||
// <backspace><newline>..
|
// <backspace><newline>..
|
||||||
// for gcc-compatibility the trailing spaces should be ignored
|
// for gcc-compatibility the trailing spaces should be ignored
|
||||||
// for vs-compatibility the trailing spaces should be kept
|
// for vs-compatibility the trailing spaces should be kept
|
||||||
|
@ -178,8 +158,6 @@ std::string Preprocessor::read(std::istream &istr, const std::string &filename,
|
||||||
else
|
else
|
||||||
code << "\\";
|
code << "\\";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just some code..
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
code << char(ch);
|
code << char(ch);
|
||||||
|
@ -192,8 +170,117 @@ std::string Preprocessor::read(std::istream &istr, const std::string &filename,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
std::string result = code.str();
|
||||||
|
code.str("");
|
||||||
|
|
||||||
return removeParantheses(removeComments(code.str(), filename, settings));
|
// ------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Remove all comments..
|
||||||
|
result = removeComments(result, filename, settings);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Clean up all preprocessor statements
|
||||||
|
result = preprocessCleanupDirectives(result);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Clean up preprocessor #if statements with Parantheses
|
||||||
|
result = removeParantheses(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Preprocessor::preprocessCleanupDirectives(const std::string &processedFile) const
|
||||||
|
{
|
||||||
|
std::ostringstream code;
|
||||||
|
std::istringstream sstr(processedFile);
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(sstr, line))
|
||||||
|
{
|
||||||
|
// Trim lines..
|
||||||
|
if (!line.empty() && line[0] == ' ')
|
||||||
|
line.erase(0, line.find_first_not_of(" "));
|
||||||
|
if (!line.empty() && line[line.size()-1] == ' ')
|
||||||
|
line.erase(line.find_last_not_of(" ") + 1);
|
||||||
|
|
||||||
|
// Preprocessor
|
||||||
|
if (!line.empty() && line[0] == '#')
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
ESC_NONE,
|
||||||
|
ESC_SINGLE,
|
||||||
|
ESC_DOUBLE
|
||||||
|
} escapeStatus = ESC_NONE;
|
||||||
|
|
||||||
|
char prev = ' '; // hack to make it skip spaces between # and the directive
|
||||||
|
code << "#";
|
||||||
|
std::string::const_iterator i = line.begin();
|
||||||
|
i++;
|
||||||
|
|
||||||
|
// need space.. #if( => #if (
|
||||||
|
bool needSpace = true;
|
||||||
|
while(i != line.end())
|
||||||
|
{
|
||||||
|
// disable esc-mode
|
||||||
|
if (escapeStatus != ESC_NONE)
|
||||||
|
{
|
||||||
|
if (prev != '\\' && escapeStatus == ESC_SINGLE && *i == '\'')
|
||||||
|
{
|
||||||
|
escapeStatus = ESC_NONE;
|
||||||
|
}
|
||||||
|
if (prev != '\\' && escapeStatus == ESC_DOUBLE && *i == '"')
|
||||||
|
{
|
||||||
|
escapeStatus = ESC_NONE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// enable esc-mode
|
||||||
|
if (escapeStatus == ESC_NONE && *i == '"')
|
||||||
|
escapeStatus = ESC_DOUBLE;
|
||||||
|
if (escapeStatus == ESC_NONE && *i == '\'')
|
||||||
|
escapeStatus = ESC_SINGLE;
|
||||||
|
}
|
||||||
|
// skip double whitespace between arguments
|
||||||
|
if (escapeStatus == ESC_NONE && prev == ' ' && *i == ' ')
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Convert #if( to "#if ("
|
||||||
|
if (escapeStatus == ESC_NONE)
|
||||||
|
{
|
||||||
|
if (needSpace)
|
||||||
|
{
|
||||||
|
if (*i == '(' || *i == '!')
|
||||||
|
code << " ";
|
||||||
|
else if (!std::isalpha(*i))
|
||||||
|
needSpace = false;
|
||||||
|
}
|
||||||
|
if (*i == '#')
|
||||||
|
needSpace = true;
|
||||||
|
}
|
||||||
|
code << *i;
|
||||||
|
if (escapeStatus != ESC_NONE && prev == '\\' && *i == '\\')
|
||||||
|
{
|
||||||
|
prev = ' ';
|
||||||
|
} else {
|
||||||
|
prev = *i;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (escapeStatus != ESC_NONE)
|
||||||
|
{
|
||||||
|
// unmatched quotes.. compiler should probably complain about this..
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Do not mess with regular code..
|
||||||
|
code << line;
|
||||||
|
}
|
||||||
|
code << (sstr.eof()?"":"\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return code.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hasbom(const std::string &str)
|
static bool hasbom(const std::string &str)
|
||||||
|
@ -668,9 +755,6 @@ void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processe
|
||||||
|
|
||||||
processedFile = read(srcCodeStream, filename, _settings);
|
processedFile = read(srcCodeStream, filename, _settings);
|
||||||
|
|
||||||
// normalize the whitespaces of the file
|
|
||||||
preprocessWhitespaces(processedFile);
|
|
||||||
|
|
||||||
// Remove asm(...)
|
// Remove asm(...)
|
||||||
removeAsm(processedFile);
|
removeAsm(processedFile);
|
||||||
|
|
||||||
|
@ -1635,15 +1719,7 @@ void Preprocessor::handleIncludes(std::string &code, const std::string &filePath
|
||||||
|
|
||||||
if (!processedFile.empty())
|
if (!processedFile.empty())
|
||||||
{
|
{
|
||||||
// Replace all tabs with spaces..
|
|
||||||
std::replace(processedFile.begin(), processedFile.end(), '\t', ' ');
|
|
||||||
|
|
||||||
// Remove all indentation..
|
|
||||||
if (!processedFile.empty() && processedFile[0] == ' ')
|
|
||||||
processedFile.erase(0, processedFile.find_first_not_of(" "));
|
|
||||||
|
|
||||||
// Remove space characters that are after or before new line character
|
// Remove space characters that are after or before new line character
|
||||||
processedFile = removeSpaceNearNL(processedFile);
|
|
||||||
processedFile = "#file \"" + filename + "\"\n" + processedFile + "\n#endfile";
|
processedFile = "#file \"" + filename + "\"\n" + processedFile + "\n#endfile";
|
||||||
code.insert(pos, processedFile);
|
code.insert(pos, processedFile);
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,6 @@ public:
|
||||||
* @param processedFile The data to be processed
|
* @param processedFile The data to be processed
|
||||||
*/
|
*/
|
||||||
static void preprocessWhitespaces(std::string &processedFile);
|
static void preprocessWhitespaces(std::string &processedFile);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -152,6 +151,12 @@ protected:
|
||||||
*/
|
*/
|
||||||
static std::string removeParantheses(const std::string &str);
|
static std::string removeParantheses(const std::string &str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clean up #-preprocessor lines (only)
|
||||||
|
* @param processedFile The data to be processed
|
||||||
|
*/
|
||||||
|
std::string preprocessCleanupDirectives(const std::string &processedFile) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the string between double quote characters or \< \> characters.
|
* Returns the string between double quote characters or \< \> characters.
|
||||||
* @param str e.g. \code#include "menu.h"\endcode or \code#include <menu.h>\endcode
|
* @param str e.g. \code#include "menu.h"\endcode or \code#include <menu.h>\endcode
|
||||||
|
|
|
@ -148,6 +148,7 @@ private:
|
||||||
TEST_CASE(macro_simple11);
|
TEST_CASE(macro_simple11);
|
||||||
TEST_CASE(macro_simple12);
|
TEST_CASE(macro_simple12);
|
||||||
TEST_CASE(macro_simple13);
|
TEST_CASE(macro_simple13);
|
||||||
|
TEST_CASE(macro_simple14);
|
||||||
TEST_CASE(macroInMacro);
|
TEST_CASE(macroInMacro);
|
||||||
TEST_CASE(macro_mismatch);
|
TEST_CASE(macro_mismatch);
|
||||||
TEST_CASE(macro_linenumbers);
|
TEST_CASE(macro_linenumbers);
|
||||||
|
@ -198,6 +199,11 @@ private:
|
||||||
TEST_CASE(endfile);
|
TEST_CASE(endfile);
|
||||||
|
|
||||||
TEST_CASE(redundant_config);
|
TEST_CASE(redundant_config);
|
||||||
|
|
||||||
|
TEST_CASE(testPreprocessorRead1);
|
||||||
|
TEST_CASE(testPreprocessorRead2);
|
||||||
|
TEST_CASE(testPreprocessorRead3);
|
||||||
|
TEST_CASE(testPreprocessorRead4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -209,7 +215,7 @@ private:
|
||||||
Preprocessor preprocessor(&settings, this);
|
Preprocessor preprocessor(&settings, this);
|
||||||
std::istringstream istr(code);
|
std::istringstream istr(code);
|
||||||
std::string codestr(preprocessor.read(istr,"test.c",0));
|
std::string codestr(preprocessor.read(istr,"test.c",0));
|
||||||
ASSERT_EQUALS("a \n#aa b \n", codestr);
|
ASSERT_EQUALS("a\n#aa b\n", codestr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void readCode2()
|
void readCode2()
|
||||||
|
@ -323,7 +329,7 @@ private:
|
||||||
preprocessor.preprocess(istr, actual, "file.c");
|
preprocessor.preprocess(istr, actual, "file.c");
|
||||||
|
|
||||||
// Compare results..
|
// Compare results..
|
||||||
ASSERT_EQUALS("\n\" #ifdef WIN32\"\n\n\n\n", actual[""]);
|
ASSERT_EQUALS("\n\" # ifdef WIN32\"\n\n\n\n", actual[""]);
|
||||||
ASSERT_EQUALS("\n\n\nqwerty\n\n", actual["WIN32"]);
|
ASSERT_EQUALS("\n\n\nqwerty\n\n", actual["WIN32"]);
|
||||||
ASSERT_EQUALS(2, static_cast<unsigned int>(actual.size()));
|
ASSERT_EQUALS(2, static_cast<unsigned int>(actual.size()));
|
||||||
}
|
}
|
||||||
|
@ -1363,7 +1369,7 @@ private:
|
||||||
std::istringstream istr(filedata);
|
std::istringstream istr(filedata);
|
||||||
Settings settings;
|
Settings settings;
|
||||||
Preprocessor preprocessor(&settings, this);
|
Preprocessor preprocessor(&settings, this);
|
||||||
ASSERT_EQUALS("#define str \"abc\" \"def\" \n\nabcdef = str;\n", preprocessor.read(istr, "test.c", 0));
|
ASSERT_EQUALS("#define str \"abc\" \"def\"\n\nabcdef = str;\n", preprocessor.read(istr, "test.c", 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void multiline2()
|
void multiline2()
|
||||||
|
@ -1576,6 +1582,13 @@ private:
|
||||||
ASSERT_EQUALS("\n\n", OurPreprocessor::expandMacros(filedata));
|
ASSERT_EQUALS("\n\n", OurPreprocessor::expandMacros(filedata));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void macro_simple14()
|
||||||
|
{
|
||||||
|
const char filedata[] = "#define A \" a \"\n"
|
||||||
|
"printf(A);\n";
|
||||||
|
ASSERT_EQUALS("\nprintf(\" a \");\n", OurPreprocessor::expandMacros(filedata));
|
||||||
|
}
|
||||||
|
|
||||||
void macroInMacro()
|
void macroInMacro()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
@ -2591,6 +2604,63 @@ private:
|
||||||
ASSERT_EQUALS("A;A;B is NOT checked", "A;A;B is NOT checked");
|
ASSERT_EQUALS("A;A;B is NOT checked", "A;A;B is NOT checked");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void testPreprocessorRead1()
|
||||||
|
{
|
||||||
|
const std::string filedata("/*\n*/ # /*\n*/ defi\\\nne FO\\\nO 10\\\n20");
|
||||||
|
std::istringstream istr(filedata.c_str());
|
||||||
|
Settings settings;
|
||||||
|
Preprocessor preprocessor(&settings, this);
|
||||||
|
ASSERT_EQUALS("#define FOO 1020", preprocessor.read(istr, "test.cpp", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void testPreprocessorRead2()
|
||||||
|
{
|
||||||
|
const std::string filedata("\"foo\\\\\nbar\"");
|
||||||
|
std::istringstream istr(filedata.c_str());
|
||||||
|
Settings settings;
|
||||||
|
Preprocessor preprocessor(&settings, this);
|
||||||
|
ASSERT_EQUALS("\"foo\\bar\"", preprocessor.read(istr, "test.cpp", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void testPreprocessorRead3()
|
||||||
|
{
|
||||||
|
const std::string filedata("#define A \" a \"\n\" b\"");
|
||||||
|
std::istringstream istr(filedata.c_str());
|
||||||
|
Settings settings;
|
||||||
|
Preprocessor preprocessor(&settings, this);
|
||||||
|
ASSERT_EQUALS(filedata, preprocessor.read(istr, "test.cpp", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void testPreprocessorRead4()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
// test < \\> < > (unescaped)
|
||||||
|
const std::string filedata("#define A \" \\\\\"/*space*/ \" \"");
|
||||||
|
std::istringstream istr(filedata.c_str());
|
||||||
|
Settings settings;
|
||||||
|
Preprocessor preprocessor(&settings, this);
|
||||||
|
ASSERT_EQUALS("#define A \" \\\\\" \" \"", preprocessor.read(istr, "test.cpp", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// test <" \\\" "> (unescaped)
|
||||||
|
const std::string filedata("#define A \" \\\\\\\" \"");
|
||||||
|
std::istringstream istr(filedata.c_str());
|
||||||
|
Settings settings;
|
||||||
|
Preprocessor preprocessor(&settings, this);
|
||||||
|
ASSERT_EQUALS("#define A \" \\\\\\\" \"", preprocessor.read(istr, "test.cpp", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// test <" \\\\"> <" "> (unescaped)
|
||||||
|
const std::string filedata("#define A \" \\\\\\\\\"/*space*/ \" \"");
|
||||||
|
std::istringstream istr(filedata.c_str());
|
||||||
|
Settings settings;
|
||||||
|
Preprocessor preprocessor(&settings, this);
|
||||||
|
ASSERT_EQUALS("#define A \" \\\\\\\\\" \" \"", preprocessor.read(istr, "test.cpp", 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestPreprocessor)
|
REGISTER_TEST(TestPreprocessor)
|
||||||
|
|
Loading…
Reference in New Issue