From 418d93eafbc9afc2664a316593c74f93eae8deaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Wed, 9 Dec 2009 17:13:48 +0100 Subject: [PATCH] Preprocessor: Refactoring the Preprocessor::expandMacros --- lib/preprocessor.cpp | 737 ++++++++++++++------------------------ lib/preprocessor.h | 4 +- test/testpreprocessor.cpp | 99 ++--- 3 files changed, 330 insertions(+), 510 deletions(-) diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index a9e70ebbd..a5b823736 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -31,206 +31,27 @@ #include #include #include +#include Preprocessor::Preprocessor(Settings *settings, ErrorLogger *errorLogger) : _settings(settings), _errorLogger(errorLogger) { } -void Preprocessor::writeError(const std::string &fileName, const std::string &code, size_t endPos, ErrorLogger *errorLogger, const std::string &errorType, const std::string &errorText) +void Preprocessor::writeError(const std::string &fileName, const int linenr, ErrorLogger *errorLogger, const std::string &errorType, const std::string &errorText) { if (!errorLogger) - { return; - } - - // line number in parsed code - unsigned int lineno = 1; - - // The current token being parsed - std::string CurrentToken; - - // lineNumbers holds line numbers for files in fileIndexes - // every time an include file is complitely parsed, last item in the vector - // is removed and lineno is set to point to that value. - std::vector lineNumbers; - - // fileIndexes holds index for _files vector about currently parsed files - // every time an include file is complitely parsed, last item in the vector - // is removed and FileIndex is set to point to that value. - std::vector fileIndexes; - - // FileIndex. What file in the _files vector is read now? - std::string FileIndex = fileName; - - // Read one byte at a time from code and create tokens - if (endPos > code.length()) - endPos = code.length(); - - for (size_t codePos = 0; codePos < endPos; ++codePos) - { - - char ch = code[codePos]; - - // UTF / extended ASCII => The output from the preprocessor should only be standard ASCII - if (ch < 0) - { - /** - * @todo handle utf better: - * - remove characters that are redundant - * - convert needed characters to standard ASCII - */ - - // Not sure how to handle this character. Bailing out. - if (ch < 0) - continue; - } - - // char/string.. - if (ch == '\'' || ch == '\"') - { - std::string line; - - // read char - bool special = false; - char c = ch; - do - { - // Append token.. - line += c; - - if (c == '\n') - ++lineno; - - // Special sequence '\.' - if (special) - special = false; - else - special = (c == '\\'); - - // Get next character - ++codePos; - c = code[codePos]; - } - while (codePos < endPos && (special || c != ch)); - line += ch; - - // Handle #file "file.h" - if (CurrentToken == "#file") - { - // Extract the filename - line = line.substr(1, line.length() - 2); - - // Has this file been tokenized already? - ++lineno; - fileIndexes.push_back(FileIndex); - FileIndex = FileLister::simplifyPath(line.c_str()); - lineNumbers.push_back(lineno); - lineno = 0; - } - - CurrentToken.clear(); - - continue; - } - - if (strchr("+-*/%&|^?!=<>[](){};:,.~\n ", ch)) - { - if (ch == '.' && - CurrentToken.length() > 0 && - std::isdigit(CurrentToken[0])) - { - // Don't separate doubles "5.4" - } - else if (strchr("+-", ch) && - CurrentToken.length() > 0 && - std::isdigit(CurrentToken[0]) && - CurrentToken[CurrentToken.length()-1] == 'e') - { - // Don't separate doubles "4.2e+10" - } - else - { - if (CurrentToken == "#file") - { - // Handle this where strings are handled - continue; - } - else if (CurrentToken == "#endfile") - { - if (lineNumbers.empty() || fileIndexes.empty()) - { - std::ostringstream line; - line << __LINE__; - - ErrorLogger::ErrorMessage errmsg; - errmsg._severity = "error"; - errmsg._msg = "####### Preprocessor bug! #######"; - errmsg._id = "preprocessor" + line.str(); - errorLogger->reportErr(errmsg); - - std::exit(0); - } - - lineno = lineNumbers.back(); - lineNumbers.pop_back(); - FileIndex = fileIndexes.back(); - fileIndexes.pop_back(); - CurrentToken.clear(); - continue; - } - - // If token contains # characters, split it up - std::string temp; - for (std::string::size_type i = 0; i < CurrentToken.length(); ++i) - { - if (CurrentToken[i] == '#' && CurrentToken.length() + 1 > i && CurrentToken[i+1] == '#') - { - temp.clear(); - ++i; - } - else - temp += CurrentToken[i]; - } - - CurrentToken.clear(); - - if (ch == '\n') - { - ++lineno; - continue; - } - else if (ch == ' ') - { - continue; - } - - CurrentToken += ch; - // Add "++", "--" or ">>" token - if ((ch == '+' || ch == '-' || ch == '>') && (code[codePos+1] == ch)) - { - ++codePos; - CurrentToken += code[codePos]; - } - CurrentToken.clear(); - continue; - } - } - - CurrentToken += ch; - } - std::list locationList; ErrorLogger::ErrorMessage::FileLocation loc; - loc.line = lineno; - loc.file = FileIndex; + loc.line = linenr; + loc.file = fileName; locationList.push_back(loc); - errorLogger->reportErr( - ErrorLogger::ErrorMessage(locationList, - "error", - errorText, - errorType)); + errorLogger->reportErr(ErrorLogger::ErrorMessage(locationList, + "error", + errorText, + errorType)); } static char readChar(std::istream &istr) @@ -1589,7 +1410,7 @@ public: return tokenizer.tokens(); } - const std::vector params() const + const std::vector ¶ms() const { return _params; } @@ -1748,340 +1569,322 @@ public: } }; -std::string Preprocessor::expandMacros(std::string code, const std::string &filename, ErrorLogger *errorLogger) + +static void skipstring(const std::string &line, std::string::size_type &pos) +{ + const char ch = line[pos]; + + ++pos; + while (pos < line.size() && line[pos] != ch) + { + if (line[pos] == '\\') + ++pos; + ++pos; + } +} + + +static bool getlines(std::istream &istr, std::string &line) +{ + if (!istr.good()) + return false; + line = ""; + for (char ch = (char)istr.get(); istr.good(); ch = (char)istr.get()) + { + if (ch == '\'' || ch == '\"') + { + line += ch; + char c = 0; + while (istr.good() && c != ch) + { + if (c == '\\') + { + c = (char)istr.get(); + if (!istr.good()) + return true; + line += c; + } + + c = (char)istr.get(); + if (!istr.good()) + return true; + if (c == '\n' && line.compare(0, 1, "#") == 0) + return true; + line += c; + } + continue; + } + if (ch == '\n') + { + if (line.compare(0, 1, "#") == 0) + return true; + + if ((char)istr.peek() == '#') + { + line += ch; + return true; + } + } + else if (line.compare(0, 1, "#") != 0 && ch == ';') + { + line += ";"; + return true; + } + + line += ch; + } + return true; +} + +std::string Preprocessor::expandMacros(const std::string &code, std::string filename, ErrorLogger *errorLogger) { // Search for macros and expand them.. - // First item is location where macro is located and - // the second is its length. 0 length means that values - // are not set. - size_t macroLoc[2] = {0, 0}; + std::map macros; - std::string::size_type defpos = 0; - bool loopAgain = false; - int counter = 0; - for (;;) + unsigned int linenr = 1; + + // linenr, filename + std::stack< std::pair > fileinfo; + + std::ostringstream ostr; + std::istringstream istr(code.c_str()); + std::string line; + while (getlines(istr, line)) { - if (macroLoc[1] > 0) + if (line.compare(0, 8, "#define ") == 0) { - // Erase previously handled "#define A foo" - code.erase(macroLoc[0], macroLoc[1]); - defpos -= macroLoc[1]; - macroLoc[1] = 0; + PreprocessorMacro *macro = new PreprocessorMacro(line.substr(8)); + if (macro->name().empty()) + delete macro; + else + macros[macro->name()] = macro; + line = "\n"; } - defpos = code.find("#define ", defpos); - if (defpos == std::string::npos) + else if (line.compare(0, 7, "#undef ") == 0) { - if (loopAgain) + std::map::iterator it; + it = macros.find(line.substr(7)); + if (it != macros.end()) { - loopAgain = false; - counter++; - if (counter < 100) - { - defpos = 0; - continue; - } - else - { - std::ostringstream line; - line << __LINE__; - - ErrorLogger::ErrorMessage errmsg; - errmsg._severity = "error"; - errmsg._msg = "### Preprocessor::expandMacros() loop limit exceeded."; - errmsg._id = "preprocessor" + line.str(); - errorLogger->reportErr(errmsg); - } + delete it->second; + macros.erase(it); } - - break; + line = "\n"; } - if (defpos > 0 && code[defpos-1] != '\n') + else if (line.compare(0, 7, "#file \"") == 0) { - defpos++; - continue; + fileinfo.push(std::pair(linenr, filename)); + filename = line.substr(7, line.length() - 8); + linenr = 0; + line += "\n"; } - // Get macro.. - std::string::size_type endpos = code.find("\n", defpos + 6); - if (endpos == std::string::npos) + else if (line == "#endfile") { - code.erase(defpos); - break; - } - - // Extract the whole macro into a separate variable "macro" and then erase it from "code" - const PreprocessorMacro macro(code.substr(defpos + 8, endpos - defpos - 7)); - //code.erase(defpos, endpos - defpos); - macroLoc[0] = defpos; - macroLoc[1] = endpos - defpos; - defpos += (endpos - defpos); - - // No macro name => continue - if (macro.name() == "") - continue; - - // Expand all macros in the code.. - char pattern[5] = "\"'# "; - pattern[3] = macro.name().at(0); - std::string::size_type pos1 = defpos; - while ((pos1 = code.find_first_of(pattern, pos1 + 1)) != std::string::npos) - { - char ch = code[pos1]; - - // #undef => break - if (code[pos1] == '#') + if (fileinfo.size()) { - // Are we at a #undef or #define? - if (code.compare(pos1, 7, "#undef ") == 0) - pos1 += 7; - else if (code.compare(pos1, 8, "#define ") == 0) - pos1 += 8; - else - continue; - - // Compare the macroname with the macroname we're currently parsing (macro.name()) - // If it's the same macroname.. break. - std::string::size_type pos = pos1 + macro.name().length(); - if (pos < code.length() - && code.compare(pos1, macro.name().length(), macro.name()) == 0 - && !std::isalnum(code[pos]) && code[pos] != '_') - break; - - - continue; + linenr = fileinfo.top().first; + filename = fileinfo.top().second; + fileinfo.pop(); } + line += "\n"; + } - // String or char.. - if (code[pos1] == '\"' || code[pos1] == '\'') + else if (line.compare(0, 1, "#") == 0) + { + line += "\n"; + } + + // expand macros.. + else + { + std::map limits; + + std::string::size_type pos = 0; + while (pos < line.size()) { - // Find the end of the string/char.. - ++pos1; - while (pos1 < code.size() && code[pos1] != ch && code[pos1] != '\n') - { - if (code[pos1] == '\\') - ++pos1; - ++pos1; - } + if (line[pos] == '\n') + ++linenr; - // End of line/file was reached without finding pair - if (pos1 >= code.size() || code[pos1] == '\n') + // skip strings.. + if (line[pos] == '\"' || line[pos] == '\'') { - std::string::size_type lineStart = code.rfind('\n', pos1 - 1); - if (lineStart != std::string::npos) + const char ch = line[pos]; + + skipstring(line, pos); + ++pos; + + if (pos >= line.size()) { - if (code.compare(lineStart + 1, 7, "#define") == 0) - { - // There is nothing wrong #define containing quote without - // a pair. - continue; - } + writeError(filename, + linenr, + errorLogger, + "noQuoteCharPair", + std::string("No pair for character (") + ch + "). Can't process file. File is either invalid or unicode, which is currently not supported."); + return ""; } - writeError(filename, - code, - pos1, - errorLogger, - "noQuoteCharPair", - std::string("No pair for character (") + ch + "). Can't process file. File is either invalid or unicode, which is currently not supported."); - - return ""; - } - - continue; - } - - // Matching the macroname? - if (code.compare(pos1, macro.name().length(), macro.name()) != 0) - continue; - - // Previous char must not be alphanumeric nor '_' - if (pos1 != 0 && (std::isalnum(code[pos1-1]) || code[pos1-1] == '_')) - continue; - - // The char after the macroname must not be alphanumeric nor '_' - if (pos1 + macro.name().length() < code.length()) - { - std::string::size_type pos2 = pos1 + macro.name().length(); - if (std::isalnum(code[pos2]) || code[pos2] == '_') - continue; - } - - std::vector params; - std::string::size_type pos2 = pos1 + macro.name().length(); - if (macro.params().size() && pos2 >= code.length()) - continue; - - // Check are we in #define - std::string::size_type startOfLine = code.rfind("\n", pos1); - ++startOfLine; - - bool insideDefine = false; - if (code.compare(startOfLine, 8, "#define ") == 0) - { - // We are inside a define, make sure we don't have name collision - // by e.g. replacing the following code: - // #define B(a) A(a) - // With this: - // #define B(2a) A(2a) - std::string::size_type endOfLine = code.find("\n", pos1); - startOfLine += 8; - - PreprocessorMacro tempMacro(code.substr(startOfLine, endOfLine - startOfLine)); - std::string tempMacroCode; - if (tempMacro.renameMacroVariables(tempMacroCode, macro)) - { - // Change the macro and then start again from the start - // of the line, as code has changed. - code.erase(startOfLine, endOfLine - startOfLine); - code.insert(startOfLine, tempMacroCode); - pos1 = startOfLine; continue; } - insideDefine = true; - } + if (!std::isalpha(line[pos]) && line[pos] != '_') + ++pos; - unsigned int numberOfNewlines = 0; - - if (macro.variadic() || macro.nopar() || macro.params().size()) - { - if (code[pos2] == ' ') - pos2++; - - if (code[pos2] != '(') - continue; - - int parlevel = 0; - std::string par; - bool endFound = false; - for (; pos2 < code.length(); ++pos2) + // found an identifier.. + while (pos < line.length() && (std::isalpha(line[pos]) || line[pos] == '_')) { - if (code[pos2] == '(') + const std::string::size_type pos1 = pos++; + while (pos < line.size() && (std::isalnum(line[pos]) || line[pos] == '_')) + ++pos; + + const std::string id = line.substr(pos1, pos - pos1); + + // is there a macro with this name? + std::map::const_iterator it; + it = macros.find(id); + if (it == macros.end()) + break; + + const PreprocessorMacro * const macro = it->second; + { - ++parlevel; - if (parlevel == 1) - continue; - } - else if (code[pos2] == ')') - { - --parlevel; - if (parlevel <= 0) - { - endFound = true; - params.push_back(par); + const std::map::const_iterator it2 = limits.find(macro); + if (it2 != limits.end() && pos <= line.length() - it2->second) break; - } } - else if (code[pos2] == '\"' || code[pos2] == '\'') + + std::vector params; + std::string::size_type pos2 = pos; + if (macro->params().size() && pos2 >= line.length()) + break; + + unsigned int numberOfNewlines = 0; + + if (macro->variadic() || macro->nopar() || macro->params().size()) { - par += code[pos2]; - char ch = code[pos2]; - ++pos2; - while (pos2 < code.length() && code[pos2] != ch) + if (line[pos2] == ' ') + pos2++; + + if (line[pos2] != '(') + break; + + int parlevel = 0; + std::string par; + bool endFound = false; + for (; pos2 < line.length(); ++pos2) { - if (code[pos2] == '\\') + if (line[pos2] == '(') { - par += code[pos2]; - ++pos2; + ++parlevel; + if (parlevel == 1) + continue; + } + else if (line[pos2] == ')') + { + --parlevel; + if (parlevel <= 0) + { + endFound = true; + params.push_back(par); + break; + } + } + else if (line[pos2] == '\"' || line[pos2] == '\'') + { + const std::string::size_type p = pos2; + skipstring(line, pos2); + if (pos2 == line.length()) + break; + par += line.substr(p, pos2 + 1 - p); + continue; + } + else if (line[pos2] == '\n') + { + ++numberOfNewlines; + continue; } - par += code[pos2]; - ++pos2; + if (parlevel == 1 && line[pos2] == ',') + { + params.push_back(par); + par = ""; + } + else if (line[pos2] == ' ') + { + // Add space only if it is needed + if (par.size() && std::isalnum(par[par.length()-1])) + { + par += ' '; + } + } + else if (parlevel >= 1) + { + par.append(1, line[pos2]); + } } - if (pos2 == code.length()) + + if (!endFound) break; - par += code[pos2]; - continue; } - else if (code[pos2] == '\n') - { - if (insideDefine) - { - // We have code like this "#define B A(" - // so we shouldn't modify the A - // Don't delete the #define - macroLoc[1] = 0; - loopAgain = true; - break; + if (params.size() == 1 && params[0] == "") + params.clear(); + + // Same number of parameters.. + if (!macro->variadic() && params.size() != macro->params().size()) + break; + + // Create macro code.. + std::string tempMacro; + if (!macro->code(params, tempMacro)) + { + // Syntax error in code + writeError(filename, + linenr, + errorLogger, + "syntaxError", + std::string("Syntax error. Not enough parameters for macro '") + macro->name() + "'."); + { + std::map::iterator it; + for (it = macros.begin(); it != macros.end(); ++it) + delete it->second; } - ++numberOfNewlines; - continue; + return ""; } - if (parlevel == 1 && code[pos2] == ',') - { - params.push_back(par); - par = ""; - } - else if (code[pos2] == ' ') - { - // Add space only if it is needed - if (par.size() && std::isalnum(par[par.length()-1])) - { - par += ' '; - } - } - else if (parlevel >= 1) - { - par.append(1, code[pos2]); - } + const std::string macrocode(std::string(numberOfNewlines, '\n') + tempMacro); + + // Insert macro code.. + if (macro->variadic() || macro->nopar() || !macro->params().empty()) + ++pos2; + + limits[macro] = line.length() - pos2; + + line.erase(pos1, pos2 - pos1); + line.insert(pos1, macrocode); + pos = pos1; } - - if (!endFound) - continue; } + } - if (params.size() == 1 && params[0] == "") - params.clear(); - - // Same number of parameters.. - if (!macro.variadic() && params.size() != macro.params().size()) - continue; - - // Create macro code.. - std::string tempMacro; - if (!macro.code(params, tempMacro)) - { - // Syntax error in code - - - writeError(filename, - code, - pos1, - errorLogger, - "syntaxError", - std::string("Syntax error. Not enough parameters for macro '") + macro.name() + "'."); - - - return ""; - } - - const std::string macrocode(std::string(numberOfNewlines, '\n') + tempMacro); - - // Insert macro code.. - if (macro.variadic() || macro.nopar() || !macro.params().empty()) - ++pos2; - - code.erase(pos1, pos2 - pos1); - code.insert(pos1, macrocode); - pos1 += macrocode.length() - 1; + ostr << line; + for (std::string::size_type p = 0; p < line.length(); ++p) + { + if (line[p] == '\n') + ++linenr; } } - // Remove all #undef.. - defpos = 0; - while ((defpos = code.find("\n#undef ", defpos)) != std::string::npos) { - ++defpos; - std::string::size_type pos2 = code.find("\n", defpos); - code.erase(defpos, pos2 - defpos); + std::map::iterator it; + for (it = macros.begin(); it != macros.end(); ++it) + delete it->second; } - return code; + return ostr.str(); } diff --git a/lib/preprocessor.h b/lib/preprocessor.h index 3d3d2e874..12a46b514 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -92,7 +92,7 @@ public: protected: - static void writeError(const std::string &fileName, const std::string &code, size_t pos, ErrorLogger *errorLogger, const std::string &errorType, const std::string &errorText); + static void writeError(const std::string &fileName, const int linenr, ErrorLogger *errorLogger, const std::string &errorType, const std::string &errorText); /** * Replace "#if defined" with "#ifdef" where possible @@ -102,7 +102,7 @@ protected: */ static std::string replaceIfDefined(const std::string &str); - static std::string expandMacros(std::string code, const std::string &filename, ErrorLogger *errorLogger); + static std::string expandMacros(const std::string &code, std::string filename, ErrorLogger *errorLogger); /** * Remove comments from code. This should only be called from read(). diff --git a/test/testpreprocessor.cpp b/test/testpreprocessor.cpp index 0f370937f..288019195 100644 --- a/test/testpreprocessor.cpp +++ b/test/testpreprocessor.cpp @@ -124,12 +124,14 @@ private: TEST_CASE(macro_simple9); TEST_CASE(macro_simple10); TEST_CASE(macro_simple11); + TEST_CASE(macro_simple12); TEST_CASE(macroInMacro); TEST_CASE(macro_mismatch); TEST_CASE(macro_linenumbers); TEST_CASE(macro_nopar); TEST_CASE(string1); TEST_CASE(string2); + TEST_CASE(string3); TEST_CASE(preprocessor_undef); TEST_CASE(defdef); // Defined multiple times TEST_CASE(preprocessor_doublesharp); @@ -979,45 +981,52 @@ private: void macro_simple6() { const char filedata[] = "#define ABC (a+b+c)\n" - "ABC"; - ASSERT_EQUALS("\n(a+b+c)", OurPreprocessor::expandMacros(filedata)); + "ABC\n"; + ASSERT_EQUALS("\n(a+b+c)\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple7() { const char filedata[] = "#define ABC(str) str\n" - "ABC(\"(\")"; - ASSERT_EQUALS("\n\"(\"", OurPreprocessor::expandMacros(filedata)); + "ABC(\"(\")\n"; + ASSERT_EQUALS("\n\"(\"\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple8() { const char filedata[] = "#define ABC 123\n" "#define ABCD 1234\n" - "ABC ABCD"; - ASSERT_EQUALS("\n\n123 1234", OurPreprocessor::expandMacros(filedata)); + "ABC ABCD\n"; + ASSERT_EQUALS("\n\n123 1234\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple9() { const char filedata[] = "#define ABC(a) f(a)\n" "ABC( \"\\\"\" );\n" - "ABC( \"g\" );"; - ASSERT_EQUALS("\nf(\"\\\"\");\nf(\"g\");", OurPreprocessor::expandMacros(filedata)); + "ABC( \"g\" );\n"; + ASSERT_EQUALS("\nf(\"\\\"\");\nf(\"g\");\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple10() { const char filedata[] = "#define ABC(t) t x\n" - "ABC(unsigned long);"; - ASSERT_EQUALS("\nunsigned long x;", OurPreprocessor::expandMacros(filedata)); + "ABC(unsigned long);\n"; + ASSERT_EQUALS("\nunsigned long x;\n", OurPreprocessor::expandMacros(filedata)); } void macro_simple11() { const char filedata[] = "#define ABC(x) delete x\n" - "ABC(a);"; - ASSERT_EQUALS("\ndelete a;", OurPreprocessor::expandMacros(filedata)); + "ABC(a);\n"; + ASSERT_EQUALS("\ndelete a;\n", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple12() + { + const char filedata[] = "#define AB ab.AB\n" + "AB.CD\n"; + ASSERT_EQUALS("\nab.AB.CD\n", OurPreprocessor::expandMacros(filedata)); } void macroInMacro() @@ -1025,36 +1034,36 @@ private: { const char filedata[] = "#define A(m) long n = m; n++;\n" "#define B(n) A(n)\n" - "B(0)"; - ASSERT_EQUALS("\n\nlong n=0;n++;", OurPreprocessor::expandMacros(filedata)); + "B(0)\n"; + ASSERT_EQUALS("\n\nlong n=0;n++;\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A B\n" "#define B 3\n" - "A"; - ASSERT_EQUALS("\n\n3", OurPreprocessor::expandMacros(filedata)); + "A\n"; + ASSERT_EQUALS("\n\n3\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define DBG(fmt, args...) printf(fmt, ## args)\n" "#define D(fmt, args...) DBG(fmt, ## args)\n" - "DBG(\"hello\");"; - ASSERT_EQUALS("\n\nprintf(\"hello\");", OurPreprocessor::expandMacros(filedata)); + "DBG(\"hello\");\n"; + ASSERT_EQUALS("\n\nprintf(\"hello\");\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define DBG(fmt, args...) printf(fmt, ## args)\n" "#define D(fmt, args...) DBG(fmt, ## args)\n" - "DBG(\"hello: %d\",3);"; - ASSERT_EQUALS("\n\nprintf(\"hello: %d\",3);", OurPreprocessor::expandMacros(filedata)); + "DBG(\"hello: %d\",3);\n"; + ASSERT_EQUALS("\n\nprintf(\"hello: %d\",3);\n", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A 4\n" "#define B(a) a,A\n" - "B(2);"; - ASSERT_EQUALS("\n\n2,4;", OurPreprocessor::expandMacros(filedata)); + "B(2);\n"; + ASSERT_EQUALS("\n\n2,4;\n", OurPreprocessor::expandMacros(filedata)); } { @@ -1103,8 +1112,8 @@ private: void macro_nopar() { const char filedata[] = "#define AAA( ) { NULL }\n" - "AAA()"; - ASSERT_EQUALS("\n{ NULL }", OurPreprocessor::expandMacros(filedata)); + "AAA()\n"; + ASSERT_EQUALS("\n{ NULL }\n", OurPreprocessor::expandMacros(filedata)); } @@ -1113,7 +1122,7 @@ private: const char filedata[] = "int main()" "{" " const char *a = \"#define A\n\";" - "}"; + "}\n"; // Preprocess => actual result.. std::istringstream istr(filedata); @@ -1129,10 +1138,18 @@ private: void string2() { const char filedata[] = "#define AAA 123\n" - "str = \"AAA\""; + "str = \"AAA\"\n"; // Compare results.. - ASSERT_EQUALS("\nstr = \"AAA\"", OurPreprocessor::expandMacros(filedata)); + ASSERT_EQUALS("\nstr = \"AAA\"\n", OurPreprocessor::expandMacros(filedata)); + } + + void string3() + { + const char filedata[] = "str(\";\");\n"; + + // Compare results.. + ASSERT_EQUALS("str(\";\");\n", OurPreprocessor::expandMacros(filedata)); } @@ -1212,30 +1229,30 @@ private: void fmt1() { const char filedata[] = "#define DBG(fmt...) printf(fmt)\n" - "DBG(\"[0x%lx-0x%lx)\", pstart, pend);"; + "DBG(\"[0x%lx-0x%lx)\", pstart, pend);\n"; // Preprocess.. std::string actual = OurPreprocessor::expandMacros(filedata); - ASSERT_EQUALS("\nprintf(\"[0x%lx-0x%lx)\",pstart,pend);", actual); + ASSERT_EQUALS("\nprintf(\"[0x%lx-0x%lx)\",pstart,pend);\n", actual); } void fmt2() { const char filedata[] = "#define DBG(fmt, args...) printf(fmt, ## args)\n" - "DBG(\"hello\");"; + "DBG(\"hello\");\n"; // Preprocess.. std::string actual = OurPreprocessor::expandMacros(filedata); - ASSERT_EQUALS("\nprintf(\"hello\");", actual); + ASSERT_EQUALS("\nprintf(\"hello\");\n", actual); } void fmt3() { const char filedata[] = "#define FRED(...) { fred(__VA_ARGS__); }\n" - "FRED(123)"; - ASSERT_EQUALS("\n{ fred(123); }", OurPreprocessor::expandMacros(filedata)); + "FRED(123)\n"; + ASSERT_EQUALS("\n{ fred(123); }\n", OurPreprocessor::expandMacros(filedata)); } @@ -1264,34 +1281,34 @@ private: void stringify() { const char filedata[] = "#define STRINGIFY(x) #x\n" - "STRINGIFY(abc)"; + "STRINGIFY(abc)\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); - ASSERT_EQUALS("\n\"abc\"", actual); + ASSERT_EQUALS("\n\"abc\"\n", actual); } void stringify2() { const char filedata[] = "#define A(x) g(#x)\n" - "A(abc);"; + "A(abc);\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); - ASSERT_EQUALS("\ng(\"abc\");", actual); + ASSERT_EQUALS("\ng(\"abc\");\n", actual); } void stringify3() { const char filedata[] = "#define A(x) g(#x)\n" - "A( abc);"; + "A( abc);\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); - ASSERT_EQUALS("\ng(\"abc\");", actual); + ASSERT_EQUALS("\ng(\"abc\");\n", actual); } void stringify4() @@ -1299,12 +1316,12 @@ private: const char filedata[] = "#define A(x) #x\n" "1 A(\n" "abc\n" - ") 2"; + ") 2\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); - ASSERT_EQUALS("\n1 \n\n\"abc\" 2", actual); + ASSERT_EQUALS("\n1 \n\n\"abc\" 2\n", actual); } void stringify5()