wrote comments about the preprocessor

This commit is contained in:
Daniel Marjamäki 2010-01-13 21:50:44 +01:00
parent cefa695dba
commit bc5800004b
2 changed files with 104 additions and 5 deletions

View File

@ -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> &params() const const std::vector<std::string> &params() 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> &params2, std::string &macrocode) const bool code(const std::vector<std::string> &params2, std::string &macrocode) 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')

View File

@ -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);
/** /**