wrote comments about the preprocessor
This commit is contained in:
parent
cefa695dba
commit
bc5800004b
|
@ -1316,11 +1316,22 @@ void Preprocessor::handleIncludes(std::string &code, const std::string &filename
|
||||||
class PreprocessorMacro
|
class PreprocessorMacro
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
/** tokens of this macro */
|
||||||
Tokenizer tokenizer;
|
Tokenizer tokenizer;
|
||||||
|
|
||||||
|
/** macro parameters */
|
||||||
std::vector<std::string> _params;
|
std::vector<std::string> _params;
|
||||||
|
|
||||||
|
/** name of macro */
|
||||||
std::string _name;
|
std::string _name;
|
||||||
std::string _macro;
|
|
||||||
|
/** macro definition in plain text */
|
||||||
|
const std::string _macro;
|
||||||
|
|
||||||
|
/** does this macro take a variable number of parameters? */
|
||||||
bool _variadic;
|
bool _variadic;
|
||||||
|
|
||||||
|
/** prefix that is used by cppcheck to separate macro parameters. Always "__cppcheck__" */
|
||||||
const std::string _prefix;
|
const std::string _prefix;
|
||||||
|
|
||||||
/** The macro has parantheses but no parameters.. "AAA()" */
|
/** The macro has parantheses but no parameters.. "AAA()" */
|
||||||
|
@ -1344,6 +1355,7 @@ public:
|
||||||
if (tokens() && tokens()->isName())
|
if (tokens() && tokens()->isName())
|
||||||
_name = tokens()->str();
|
_name = tokens()->str();
|
||||||
|
|
||||||
|
// initialize parameters to default values
|
||||||
_variadic = _nopar = false;
|
_variadic = _nopar = false;
|
||||||
|
|
||||||
std::string::size_type pos = macro.find_first_of(" (");
|
std::string::size_type pos = macro.find_first_of(" (");
|
||||||
|
@ -1440,31 +1452,42 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** return tokens of this macro */
|
||||||
const Token *tokens() const
|
const Token *tokens() const
|
||||||
{
|
{
|
||||||
return tokenizer.tokens();
|
return tokenizer.tokens();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** read parameters of this macro */
|
||||||
const std::vector<std::string> ¶ms() const
|
const std::vector<std::string> ¶ms() const
|
||||||
{
|
{
|
||||||
return _params;
|
return _params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** check if this is macro has a variable number of parameters */
|
||||||
bool variadic() const
|
bool variadic() const
|
||||||
{
|
{
|
||||||
return _variadic;
|
return _variadic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check if this macro has parantheses but no parameters */
|
||||||
bool nopar() const
|
bool nopar() const
|
||||||
{
|
{
|
||||||
return _nopar;
|
return _nopar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** name of macro */
|
||||||
const std::string &name() const
|
const std::string &name() const
|
||||||
{
|
{
|
||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get expanded code for this macro
|
||||||
|
* @param params2 macro parameters
|
||||||
|
* @param macrocode output string
|
||||||
|
* @return true if the expanding was successful
|
||||||
|
*/
|
||||||
bool code(const std::vector<std::string> ¶ms2, std::string ¯ocode) const
|
bool code(const std::vector<std::string> ¶ms2, std::string ¯ocode) const
|
||||||
{
|
{
|
||||||
if (_nopar)
|
if (_nopar)
|
||||||
|
@ -1605,6 +1628,11 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skip string in line. A string begins and ends with either a " or a '
|
||||||
|
* @param line the string
|
||||||
|
* @param pos in=start position of string, out=end position of string
|
||||||
|
*/
|
||||||
static void skipstring(const std::string &line, std::string::size_type &pos)
|
static void skipstring(const std::string &line, std::string::size_type &pos)
|
||||||
{
|
{
|
||||||
const unsigned char ch = line[pos];
|
const unsigned char ch = line[pos];
|
||||||
|
@ -1619,6 +1647,16 @@ static void skipstring(const std::string &line, std::string::size_type &pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get data from a input string. This is an extended version of std::getline.
|
||||||
|
* The std::getline only get a single line at a time. It can therefore happen that it
|
||||||
|
* contains a partial statement. This function ensures that the returned data
|
||||||
|
* doesn't end in the middle of a statement. The "getlines" name indicate that
|
||||||
|
* this function will return multiple lines if needed.
|
||||||
|
* @param istr input stream
|
||||||
|
* @param line output data
|
||||||
|
* @return success
|
||||||
|
*/
|
||||||
static bool getlines(std::istream &istr, std::string &line)
|
static bool getlines(std::istream &istr, std::string &line)
|
||||||
{
|
{
|
||||||
if (!istr.good())
|
if (!istr.good())
|
||||||
|
@ -1679,19 +1717,26 @@ static bool getlines(std::istream &istr, std::string &line)
|
||||||
std::string Preprocessor::expandMacros(const std::string &code, std::string filename, ErrorLogger *errorLogger)
|
std::string Preprocessor::expandMacros(const std::string &code, std::string filename, ErrorLogger *errorLogger)
|
||||||
{
|
{
|
||||||
// Search for macros and expand them..
|
// Search for macros and expand them..
|
||||||
|
// --------------------------------------------
|
||||||
|
|
||||||
|
// Available macros (key=macroname, value=macro).
|
||||||
std::map<std::string, PreprocessorMacro *> macros;
|
std::map<std::string, PreprocessorMacro *> macros;
|
||||||
|
|
||||||
|
// Current line number
|
||||||
unsigned int linenr = 1;
|
unsigned int linenr = 1;
|
||||||
|
|
||||||
// linenr, filename
|
// linenr, filename
|
||||||
std::stack< std::pair<unsigned int, std::string> > fileinfo;
|
std::stack< std::pair<unsigned int, std::string> > fileinfo;
|
||||||
|
|
||||||
|
// output stream
|
||||||
std::ostringstream ostr;
|
std::ostringstream ostr;
|
||||||
|
|
||||||
|
// read code..
|
||||||
std::istringstream istr(code.c_str());
|
std::istringstream istr(code.c_str());
|
||||||
std::string line;
|
std::string line;
|
||||||
while (getlines(istr, line))
|
while (getlines(istr, line))
|
||||||
{
|
{
|
||||||
|
// defining a macro..
|
||||||
if (line.compare(0, 8, "#define ") == 0)
|
if (line.compare(0, 8, "#define ") == 0)
|
||||||
{
|
{
|
||||||
PreprocessorMacro *macro = new PreprocessorMacro(line.substr(8));
|
PreprocessorMacro *macro = new PreprocessorMacro(line.substr(8));
|
||||||
|
@ -1708,6 +1753,7 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
line = "\n";
|
line = "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// undefining a macro..
|
||||||
else if (line.compare(0, 7, "#undef ") == 0)
|
else if (line.compare(0, 7, "#undef ") == 0)
|
||||||
{
|
{
|
||||||
std::map<std::string, PreprocessorMacro *>::iterator it;
|
std::map<std::string, PreprocessorMacro *>::iterator it;
|
||||||
|
@ -1720,6 +1766,7 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
line = "\n";
|
line = "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// entering a file, update position..
|
||||||
else if (line.compare(0, 7, "#file \"") == 0)
|
else if (line.compare(0, 7, "#file \"") == 0)
|
||||||
{
|
{
|
||||||
fileinfo.push(std::pair<unsigned int, std::string>(linenr, filename));
|
fileinfo.push(std::pair<unsigned int, std::string>(linenr, filename));
|
||||||
|
@ -1728,6 +1775,7 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
line += "\n";
|
line += "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// leaving a file, update position..
|
||||||
else if (line == "#endfile")
|
else if (line == "#endfile")
|
||||||
{
|
{
|
||||||
if (fileinfo.size())
|
if (fileinfo.size())
|
||||||
|
@ -1739,6 +1787,7 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
line += "\n";
|
line += "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all other preprocessor directives are just replaced with a newline
|
||||||
else if (line.compare(0, 1, "#") == 0)
|
else if (line.compare(0, 1, "#") == 0)
|
||||||
{
|
{
|
||||||
line += "\n";
|
line += "\n";
|
||||||
|
@ -1761,11 +1810,13 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
// without updating the limit is safe.
|
// without updating the limit is safe.
|
||||||
// * when pos goes beyond a limit the limit needs to be
|
// * when pos goes beyond a limit the limit needs to be
|
||||||
// deleted because it is unsafe to insert/delete text
|
// deleted because it is unsafe to insert/delete text
|
||||||
// after the limit
|
// after the limit otherwise
|
||||||
std::map<const PreprocessorMacro *, unsigned int> limits;
|
std::map<const PreprocessorMacro *, unsigned int> limits;
|
||||||
|
|
||||||
// pos is the current position in line
|
// pos is the current position in line
|
||||||
std::string::size_type pos = 0;
|
std::string::size_type pos = 0;
|
||||||
|
|
||||||
|
// scan line to see if there are any macros to expand..
|
||||||
while (pos < line.size())
|
while (pos < line.size())
|
||||||
{
|
{
|
||||||
if (line[pos] == '\n')
|
if (line[pos] == '\n')
|
||||||
|
@ -1801,23 +1852,28 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
++pos;
|
++pos;
|
||||||
|
|
||||||
// found an identifier..
|
// found an identifier..
|
||||||
|
// the "while" is used in case the expanded macro will immediately call another macro
|
||||||
while (pos < line.length() && (std::isalpha(line[pos]) || line[pos] == '_'))
|
while (pos < line.length() && (std::isalpha(line[pos]) || line[pos] == '_'))
|
||||||
{
|
{
|
||||||
|
// pos1 = start position of macro
|
||||||
const std::string::size_type pos1 = pos++;
|
const std::string::size_type pos1 = pos++;
|
||||||
|
|
||||||
|
// find the end of the identifier
|
||||||
while (pos < line.size() && (std::isalnum(line[pos]) || line[pos] == '_'))
|
while (pos < line.size() && (std::isalnum(line[pos]) || line[pos] == '_'))
|
||||||
++pos;
|
++pos;
|
||||||
|
|
||||||
|
// get identifier
|
||||||
const std::string id = line.substr(pos1, pos - pos1);
|
const std::string id = line.substr(pos1, pos - pos1);
|
||||||
|
|
||||||
// is there a macro with this name?
|
// is there a macro with this name?
|
||||||
std::map<std::string, PreprocessorMacro *>::const_iterator it;
|
std::map<std::string, PreprocessorMacro *>::const_iterator it;
|
||||||
it = macros.find(id);
|
it = macros.find(id);
|
||||||
if (it == macros.end())
|
if (it == macros.end())
|
||||||
break;
|
break; // no macro with this name exist
|
||||||
|
|
||||||
const PreprocessorMacro * const macro = it->second;
|
const PreprocessorMacro * const macro = it->second;
|
||||||
|
|
||||||
// check if pos is within allowed limits for this
|
// check that pos is within allowed limits for this
|
||||||
// macro
|
// macro
|
||||||
{
|
{
|
||||||
const std::map<const PreprocessorMacro *, unsigned int>::const_iterator it2 = limits.find(macro);
|
const std::map<const PreprocessorMacro *, unsigned int>::const_iterator it2 = limits.find(macro);
|
||||||
|
@ -1825,13 +1881,16 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get parameters from line..
|
||||||
std::vector<std::string> params;
|
std::vector<std::string> params;
|
||||||
std::string::size_type pos2 = pos;
|
std::string::size_type pos2 = pos;
|
||||||
if (macro->params().size() && pos2 >= line.length())
|
if (macro->params().size() && pos2 >= line.length())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// number of newlines within macro use
|
||||||
unsigned int numberOfNewlines = 0;
|
unsigned int numberOfNewlines = 0;
|
||||||
|
|
||||||
|
// if the macro has parantheses, get parameters
|
||||||
if (macro->variadic() || macro->nopar() || macro->params().size())
|
if (macro->variadic() || macro->nopar() || macro->params().size())
|
||||||
{
|
{
|
||||||
if (line[pos2] == ' ')
|
if (line[pos2] == ' ')
|
||||||
|
@ -1840,17 +1899,27 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
if (line[pos2] != '(')
|
if (line[pos2] != '(')
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// parantheses level
|
||||||
int parlevel = 0;
|
int parlevel = 0;
|
||||||
|
|
||||||
|
// current parameter
|
||||||
std::string par;
|
std::string par;
|
||||||
|
|
||||||
|
// is the end paranthesis found?
|
||||||
bool endFound = false;
|
bool endFound = false;
|
||||||
|
|
||||||
|
// scan for parameters..
|
||||||
for (; pos2 < line.length(); ++pos2)
|
for (; pos2 < line.length(); ++pos2)
|
||||||
{
|
{
|
||||||
|
// increase paranthesis level
|
||||||
if (line[pos2] == '(')
|
if (line[pos2] == '(')
|
||||||
{
|
{
|
||||||
++parlevel;
|
++parlevel;
|
||||||
if (parlevel == 1)
|
if (parlevel == 1)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// decrease paranthesis level
|
||||||
else if (line[pos2] == ')')
|
else if (line[pos2] == ')')
|
||||||
{
|
{
|
||||||
--parlevel;
|
--parlevel;
|
||||||
|
@ -1861,6 +1930,8 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// string
|
||||||
else if (line[pos2] == '\"' || line[pos2] == '\'')
|
else if (line[pos2] == '\"' || line[pos2] == '\'')
|
||||||
{
|
{
|
||||||
const std::string::size_type p = pos2;
|
const std::string::size_type p = pos2;
|
||||||
|
@ -1870,17 +1941,22 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
par += line.substr(p, pos2 + 1 - p);
|
par += line.substr(p, pos2 + 1 - p);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// count newlines. the expanded macro must have the same number of newlines
|
||||||
else if (line[pos2] == '\n')
|
else if (line[pos2] == '\n')
|
||||||
{
|
{
|
||||||
++numberOfNewlines;
|
++numberOfNewlines;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// new parameter
|
||||||
if (parlevel == 1 && line[pos2] == ',')
|
if (parlevel == 1 && line[pos2] == ',')
|
||||||
{
|
{
|
||||||
params.push_back(par);
|
params.push_back(par);
|
||||||
par = "";
|
par = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// spaces are only added if needed
|
||||||
else if (line[pos2] == ' ')
|
else if (line[pos2] == ' ')
|
||||||
{
|
{
|
||||||
// Add space only if it is needed
|
// Add space only if it is needed
|
||||||
|
@ -1889,20 +1965,24 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
par += ' ';
|
par += ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add character to current parameter
|
||||||
else if (parlevel >= 1)
|
else if (parlevel >= 1)
|
||||||
{
|
{
|
||||||
par.append(1, line[pos2]);
|
par.append(1, line[pos2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// something went wrong so bail out
|
||||||
if (!endFound)
|
if (!endFound)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Just an empty parameter => clear
|
||||||
if (params.size() == 1 && params[0] == "")
|
if (params.size() == 1 && params[0] == "")
|
||||||
params.clear();
|
params.clear();
|
||||||
|
|
||||||
// Same number of parameters..
|
// Check that it's the same number of parameters..
|
||||||
if (!macro->variadic() && params.size() != macro->params().size())
|
if (!macro->variadic() && params.size() != macro->params().size())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1924,6 +2004,7 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure number of newlines remain the same..
|
||||||
const std::string macrocode(std::string(numberOfNewlines, '\n') + tempMacro);
|
const std::string macrocode(std::string(numberOfNewlines, '\n') + tempMacro);
|
||||||
|
|
||||||
// Insert macro code..
|
// Insert macro code..
|
||||||
|
@ -1960,7 +2041,10 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the line has been processed in various ways. Now add it to the output stream
|
||||||
ostr << line;
|
ostr << line;
|
||||||
|
|
||||||
|
// update linenr
|
||||||
for (std::string::size_type p = 0; p < line.length(); ++p)
|
for (std::string::size_type p = 0; p < line.length(); ++p)
|
||||||
{
|
{
|
||||||
if (line[p] == '\n')
|
if (line[p] == '\n')
|
||||||
|
|
|
@ -93,6 +93,14 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* report error
|
||||||
|
* @param fileName name of file that the error was found in
|
||||||
|
* @param linenr linenr in file
|
||||||
|
* @param errorLogger Error logger to write error to
|
||||||
|
* @param errorType id string for error
|
||||||
|
* @param errorText Plain text
|
||||||
|
*/
|
||||||
static void writeError(const std::string &fileName, const int linenr, 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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,6 +111,13 @@ protected:
|
||||||
*/
|
*/
|
||||||
static std::string replaceIfDefined(const std::string &str);
|
static std::string replaceIfDefined(const std::string &str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* expand macros in code. #ifdefs etc are ignored so the code must be a single configuration
|
||||||
|
* @param code The input code
|
||||||
|
* @param filename filename of source file
|
||||||
|
* @param errorLogger Error logger to write errors to (if any)
|
||||||
|
* @return the expanded string
|
||||||
|
*/
|
||||||
static std::string expandMacros(const std::string &code, std::string filename, ErrorLogger *errorLogger);
|
static std::string expandMacros(const std::string &code, std::string filename, ErrorLogger *errorLogger);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue