From bc5800004b683a00b6680055f895c40cef72d722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Wed, 13 Jan 2010 21:50:44 +0100 Subject: [PATCH] wrote comments about the preprocessor --- lib/preprocessor.cpp | 94 +++++++++++++++++++++++++++++++++++++++++--- lib/preprocessor.h | 15 +++++++ 2 files changed, 104 insertions(+), 5 deletions(-) diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 17ced4f87..0558d120e 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -1316,11 +1316,22 @@ void Preprocessor::handleIncludes(std::string &code, const std::string &filename class PreprocessorMacro { private: + /** tokens of this macro */ Tokenizer tokenizer; + + /** macro parameters */ std::vector _params; + + /** name of macro */ 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; + + /** prefix that is used by cppcheck to separate macro parameters. Always "__cppcheck__" */ const std::string _prefix; /** The macro has parantheses but no parameters.. "AAA()" */ @@ -1344,6 +1355,7 @@ public: if (tokens() && tokens()->isName()) _name = tokens()->str(); + // initialize parameters to default values _variadic = _nopar = false; std::string::size_type pos = macro.find_first_of(" ("); @@ -1440,31 +1452,42 @@ public: return true; } + /** return tokens of this macro */ const Token *tokens() const { return tokenizer.tokens(); } + /** read parameters of this macro */ const std::vector ¶ms() const { return _params; } + /** check if this is macro has a variable number of parameters */ bool variadic() const { return _variadic; } + /** Check if this macro has parantheses but no parameters */ bool nopar() const { return _nopar; } + /** name of macro */ const std::string &name() const { 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 ¶ms2, std::string ¯ocode) const { 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) { 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) { 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) { // Search for macros and expand them.. + // -------------------------------------------- + // Available macros (key=macroname, value=macro). std::map macros; + // Current line number unsigned int linenr = 1; // linenr, filename std::stack< std::pair > fileinfo; + // output stream std::ostringstream ostr; + + // read code.. std::istringstream istr(code.c_str()); std::string line; while (getlines(istr, line)) { + // defining a macro.. if (line.compare(0, 8, "#define ") == 0) { PreprocessorMacro *macro = new PreprocessorMacro(line.substr(8)); @@ -1708,6 +1753,7 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file line = "\n"; } + // undefining a macro.. else if (line.compare(0, 7, "#undef ") == 0) { std::map::iterator it; @@ -1720,6 +1766,7 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file line = "\n"; } + // entering a file, update position.. else if (line.compare(0, 7, "#file \"") == 0) { fileinfo.push(std::pair(linenr, filename)); @@ -1728,6 +1775,7 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file line += "\n"; } + // leaving a file, update position.. else if (line == "#endfile") { if (fileinfo.size()) @@ -1739,6 +1787,7 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file line += "\n"; } + // all other preprocessor directives are just replaced with a newline else if (line.compare(0, 1, "#") == 0) { line += "\n"; @@ -1761,11 +1810,13 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file // without updating the limit is safe. // * when pos goes beyond a limit the limit needs to be // deleted because it is unsafe to insert/delete text - // after the limit + // after the limit otherwise std::map limits; // pos is the current position in line std::string::size_type pos = 0; + + // scan line to see if there are any macros to expand.. while (pos < line.size()) { if (line[pos] == '\n') @@ -1801,23 +1852,28 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file ++pos; // 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] == '_')) { + // pos1 = start position of macro const std::string::size_type pos1 = pos++; + + // find the end of the identifier while (pos < line.size() && (std::isalnum(line[pos]) || line[pos] == '_')) ++pos; + // get identifier 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; + break; // no macro with this name exist 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 { const std::map::const_iterator it2 = limits.find(macro); @@ -1825,13 +1881,16 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file break; } + // get parameters from line.. std::vector params; std::string::size_type pos2 = pos; if (macro->params().size() && pos2 >= line.length()) break; + // number of newlines within macro use unsigned int numberOfNewlines = 0; + // if the macro has parantheses, get parameters if (macro->variadic() || macro->nopar() || macro->params().size()) { if (line[pos2] == ' ') @@ -1840,17 +1899,27 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file if (line[pos2] != '(') break; + // parantheses level int parlevel = 0; + + // current parameter std::string par; + + // is the end paranthesis found? bool endFound = false; + + // scan for parameters.. for (; pos2 < line.length(); ++pos2) { + // increase paranthesis level if (line[pos2] == '(') { ++parlevel; if (parlevel == 1) continue; } + + // decrease paranthesis level else if (line[pos2] == ')') { --parlevel; @@ -1861,6 +1930,8 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file break; } } + + // string else if (line[pos2] == '\"' || line[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); continue; } + + // count newlines. the expanded macro must have the same number of newlines else if (line[pos2] == '\n') { ++numberOfNewlines; continue; } + // new parameter if (parlevel == 1 && line[pos2] == ',') { params.push_back(par); par = ""; } + + // spaces are only added if needed else if (line[pos2] == ' ') { // Add space only if it is needed @@ -1889,20 +1965,24 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file par += ' '; } } + + // add character to current parameter else if (parlevel >= 1) { par.append(1, line[pos2]); } } + // something went wrong so bail out if (!endFound) break; } + // Just an empty parameter => clear if (params.size() == 1 && params[0] == "") params.clear(); - // Same number of parameters.. + // Check that it's the same number of parameters.. if (!macro->variadic() && params.size() != macro->params().size()) break; @@ -1924,6 +2004,7 @@ std::string Preprocessor::expandMacros(const std::string &code, std::string file return ""; } + // make sure number of newlines remain the same.. const std::string macrocode(std::string(numberOfNewlines, '\n') + tempMacro); // 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; + + // update linenr for (std::string::size_type p = 0; p < line.length(); ++p) { if (line[p] == '\n') diff --git a/lib/preprocessor.h b/lib/preprocessor.h index be98fa6ca..e142f316d 100644 --- a/lib/preprocessor.h +++ b/lib/preprocessor.h @@ -93,6 +93,14 @@ public: 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); /** @@ -103,6 +111,13 @@ protected: */ 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); /**