Preprocessor: Refactoring the Preprocessor::expandMacros
This commit is contained in:
parent
87325799c5
commit
418d93eafb
|
@ -31,206 +31,27 @@
|
|||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
|
||||
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<unsigned int> 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<std::string> 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<ErrorLogger::ErrorMessage::FileLocation> 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<std::string> params() const
|
||||
const std::vector<std::string> ¶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<std::string, PreprocessorMacro *> macros;
|
||||
|
||||
std::string::size_type defpos = 0;
|
||||
bool loopAgain = false;
|
||||
int counter = 0;
|
||||
for (;;)
|
||||
unsigned int linenr = 1;
|
||||
|
||||
// linenr, filename
|
||||
std::stack< std::pair<unsigned int, std::string> > 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<std::string, PreprocessorMacro *>::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<unsigned int, std::string>(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<const PreprocessorMacro *, unsigned int> 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<std::string> 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<std::string, PreprocessorMacro *>::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 PreprocessorMacro *, unsigned int>::const_iterator it2 = limits.find(macro);
|
||||
if (it2 != limits.end() && pos <= line.length() - it2->second)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (code[pos2] == '\"' || code[pos2] == '\'')
|
||||
|
||||
std::vector<std::string> 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<std::string, PreprocessorMacro *>::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<std::string, PreprocessorMacro *>::iterator it;
|
||||
for (it = macros.begin(); it != macros.end(); ++it)
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
return code;
|
||||
return ostr.str();
|
||||
}
|
||||
|
||||
|
|
|
@ -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().
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue