📄 preprocessor.cpp
字号:
/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2009 Daniel Marjamäki, Reijo Tomperi, Nicolas Le Cam, * Leandro Penz, Kimmo Varis, Vesa Pikki * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/ */#include "preprocessor.h"#include "tokenize.h"#include "token.h"#include <algorithm>#include <sstream>#include <fstream>#include <iostream>#include <cstdlib>#include <cctype>Preprocessor::Preprocessor(){}static char readChar(std::istream &istr){ char ch = (char)istr.get(); // Handling of newlines.. if (ch == '\r') { ch = '\n'; if ((char)istr.peek() == '\n') (void)istr.get(); } return ch;}/** Just read the code into a string. Perform simple cleanup of the code */std::string Preprocessor::read(std::istream &istr){ // Get filedata from stream.. bool ignoreSpace = true; // need space.. #if( => #if ( bool needSpace = false; // For the error report int lineno = 1; // handling <backspace><newline> // when this is encountered the <backspace><newline> will be "skipped". // on the next <newline>, extra newlines will be added unsigned int newlines = 0; std::ostringstream code; for (char ch = readChar(istr); istr.good(); ch = readChar(istr)) { if (ch < 0) continue; if (ch == '\n') ++lineno; // Replace assorted special chars with spaces.. if ((ch != '\n') && (std::isspace(ch) || std::iscntrl(ch))) ch = ' '; // Skip spaces after ' ' and after '#' if (ch == ' ' && ignoreSpace) continue; ignoreSpace = bool(ch == ' ' || ch == '#' || ch == '/'); if (needSpace) { if (ch == '(') code << " "; else if (! std::isalpha(ch)) needSpace = false; } if (ch == '#') needSpace = true; // Remove comments.. if (ch == '/') { char chNext = readChar(istr); if (chNext == '/') { while (istr.good() && ch != '\n') ch = readChar(istr); code << "\n"; ++lineno; } else if (chNext == '*') { char chPrev = 0; while (istr.good() && (chPrev != '*' || ch != '/')) { chPrev = ch; ch = readChar(istr); if (ch == '\n') { code << "\n"; ++lineno; } } } else { if (chNext == '\n') ++lineno; code << std::string(1, ch) << std::string(1, chNext); } } // String or char constants.. else if (ch == '\"' || ch == '\'') { code << std::string(1, ch); char chNext; do { chNext = (char)istr.get(); if (chNext == '\\') { char chSeq = readChar(istr); if (chSeq == '\n') ++newlines; else { code << std::string(1, chNext); code << std::string(1, chSeq); } } else code << std::string(1, chNext); } while (istr.good() && chNext != ch); } // <backspace><newline>.. else if (ch == '\\') { char chNext = (char)istr.peek(); if (chNext == '\n' || chNext == '\r') { ++newlines; (void)readChar(istr); // Skip the "<backspace><newline>" } else code << "\\"; } // Just some code.. else { code << std::string(1, ch); // if there has been <backspace><newline> sequences, add extra newlines.. if (ch == '\n' && newlines > 0) { code << std::string(newlines, '\n'); newlines = 0; } } } return code.str();}void Preprocessor::preprocess(std::istream &istr, std::map<std::string, std::string> &result, const std::string &filename, const std::list<std::string> &includePaths){ std::list<std::string> configs; std::string data; preprocess(istr, data, configs, filename, includePaths); for (std::list<std::string>::const_iterator it = configs.begin(); it != configs.end(); ++it) result[ *it ] = Preprocessor::getcode(data, *it);}std::string Preprocessor::removeSpaceNearNL(const std::string &str){ std::string tmp; int prev = -1; for (unsigned int i = 0; i < str.size(); i++) { if (str[i] == ' ' && ((i > 0 && tmp[prev] == '\n') || (i + 1 < str.size() && str[i+1] == '\n') ) ) { // Ignore space that has new line in either side of it } else { tmp.append(1, str[i]); ++prev; } } return tmp;}std::string Preprocessor::replaceIfDefined(const std::string &str){ std::string ret(str); std::string::size_type pos = 0; while ((pos = ret.find("#if defined(", pos)) != std::string::npos) { std::string::size_type pos2 = ret.find(")", pos + 9); if (pos2 > ret.length() - 1) break; if (ret[pos2+1] == '\n') { ret.erase(pos2, 1); ret.erase(pos + 3, 9); ret.insert(pos + 3, "def "); } ++pos; } return ret;}void Preprocessor::preprocess(std::istream &istr, std::string &processedFile, std::list<std::string> &resultConfigurations, const std::string &filename, const std::list<std::string> &includePaths){ processedFile = read(istr); // Replace all tabs with spaces.. std::replace(processedFile.begin(), processedFile.end(), '\t', ' '); // Remove all indentation.. if (!processedFile.empty() && processedFile[0] == ' ') processedFile.erase(0, processedFile.find_first_not_of(" ")); // Remove space characters that are after or before new line character processedFile = removeSpaceNearNL(processedFile); handleIncludes(processedFile, filename, includePaths); processedFile = replaceIfDefined(processedFile); // Get all possible configurations.. resultConfigurations = getcfgs(processedFile);}// Get the DEF in this line: "#ifdef DEF"std::string Preprocessor::getdef(std::string line, bool def){ // If def is true, the line must start with "#ifdef" if (def && line.find("#ifdef ") != 0 && line.find("#if ") != 0 && line.find("#elif ") != 0) { return ""; } // If def is false, the line must start with "#ifndef" if (!def && line.find("#ifndef ") != 0) { return ""; } // Remove the "#ifdef" or "#ifndef" line.erase(0, line.find(" ")); // Remove all spaces. while (line.find(" ") != std::string::npos) line.erase(line.find(" "), 1); // The remaining string is our result. return line;}std::list<std::string> Preprocessor::getcfgs(const std::string &filedata){ std::list<std::string> ret; ret.push_back(""); std::list<std::string> deflist; // How deep into included files are we currently parsing? // 0=>Source file, 1=>Included by source file, 2=>included by header that was included by source file, etc int filelevel = 0; std::istringstream istr(filedata); std::string line; while (getline(istr, line)) { if (line.substr(0, 6) == "#file ") { ++filelevel; continue; } else if (line == "#endfile") { if (filelevel > 0) --filelevel; continue; } if (filelevel > 0) continue; std::string def = getdef(line, true) + getdef(line, false); if (!def.empty()) { if (! deflist.empty() && line.find("#elif ") == 0) deflist.pop_back(); deflist.push_back(def); def = ""; for (std::list<std::string>::const_iterator it = deflist.begin(); it != deflist.end(); ++it) { if (*it == "0") break; if (*it == "1") continue; if (! def.empty()) def += ";"; def += *it; } if (std::find(ret.begin(), ret.end(), def) == ret.end()) ret.push_back(def); } if (line.find("#else") == 0 && ! deflist.empty()) { std::string def((deflist.back() == "1") ? "0" : "1"); deflist.pop_back(); deflist.push_back(def); } if (line.find("#endif") == 0 && ! deflist.empty()) deflist.pop_back(); } return ret;}bool Preprocessor::match_cfg_def(std::string cfg, const std::string &def){ if (def == "0") return false; if (def == "1") return true; if (cfg.empty()) return false; while (! cfg.empty()) { if (cfg.find(";") == std::string::npos) return bool(cfg == def); std::string _cfg = cfg.substr(0, cfg.find(";")); if (_cfg == def) return true; cfg.erase(0, cfg.find(";") + 1); } return false;}std::string Preprocessor::getcode(const std::string &filedata, std::string cfg){ std::ostringstream ret; bool match = true; std::list<bool> matching_ifdef; std::list<bool> matched_ifdef; std::istringstream istr(filedata); std::string line; while (getline(istr, line)) { std::string def = getdef(line, true); std::string ndef = getdef(line, false); if (line.find("#elif ") == 0) { if (matched_ifdef.back()) { matching_ifdef.back() = false; } else { if (match_cfg_def(cfg, def)) { matching_ifdef.back() = true; matched_ifdef.back() = true; } } } else if (! def.empty()) { matching_ifdef.push_back(match_cfg_def(cfg, def)); matched_ifdef.push_back(matching_ifdef.back()); } else if (! ndef.empty()) { matching_ifdef.push_back(! match_cfg_def(cfg, ndef)); matched_ifdef.push_back(matching_ifdef.back()); } else if (line == "#else") { if (! matched_ifdef.empty()) matching_ifdef.back() = ! matched_ifdef.back(); } else if (line == "#endif") { if (! matched_ifdef.empty()) matched_ifdef.pop_back(); if (! matching_ifdef.empty()) matching_ifdef.pop_back(); } if (!line.empty() && line[0] == '#') { match = true; for (std::list<bool>::const_iterator it = matching_ifdef.begin(); it != matching_ifdef.end(); ++it) match &= bool(*it);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -