/* * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) * * 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 . */ #include "cpppreprocessor.h" #include #include #include #include #include "../utils.h" CppPreprocessor::CppPreprocessor() { } void CppPreprocessor::clear() { //don't use reset(), it will reset(add) defines. clearTempResults(); //Result across processings. //used by parser even preprocess finished mIncludesList.clear(); mFileDefines.clear(); //dictionary to save defines for each headerfile; mScannedFiles.clear(); //option data for the parser //{ List of current project's include path } mDefines.clear(); //mHardDefines.clear(); // set by "cpp -dM -E -xc NUL" mProjectIncludePaths.clear(); //we also need include paths in order (for #include_next) mIncludePathList.clear(); mProjectIncludePathList.clear(); //{ List of current compiler set's include path} mIncludePaths.clear(); } void CppPreprocessor::clearTempResults() { //temporary data when preprocessing single file mFileName=""; mBuffer.clear(); mResult.clear(); mCurrentIncludes=nullptr; mIncludes.clear(); // stack of files we've stepped into. last one is current file, first one is source file mBranchResults.clear();// stack of branch results (boolean). last one is current branch, first one is outermost branch //mDefines.clear(); // working set, editable mProcessed.clear(); // dictionary to save filename already processed } void CppPreprocessor::addDefineByParts(const QString &name, const QString &args, const QString &value, bool hardCoded) { // Check for duplicates PDefine define = std::make_shared(); define->name = name; define->args = args; define->value = value; define->filename = mFileName; //define->argList; define->formatValue = value; define->hardCoded = hardCoded; if (!args.isEmpty()) parseArgs(define); if (hardCoded) { mHardDefines.insert(name,define); mDefines.insert(name,define); } else { PDefineMap defineMap = mFileDefines.value(mFileName,PDefineMap()); if (!defineMap) { defineMap = std::make_shared(); mFileDefines.insert(mFileName,defineMap); } defineMap->insert(define->name,define); mDefines.insert(name,define); } } void CppPreprocessor::getDefineParts(const QString &input, QString &name, QString &args, QString &value) { QString s = input.trimmed(); name = ""; args = ""; value = ""; // Rules: // When the character before the first opening brace is nonblank, a function is defined. // After that point, switch from name to args // The value starts after the first blank character outside of the outermost () pair int i = 0; int level = 0; bool isFunction = false; int argStart = 0; while (i < s.length()) { // When we find the first opening brace, check if this is a function define if (s[i] == '(') { level++; if ((level == 1) && (!isFunction)) { // found a function define! name = s.mid(0,i); argStart = i; isFunction = true; } } else if (s[i]==')') { level--; } else if (isSpaceChar(s[i]) && (level == 0)) { break; } i++; } if (isFunction) { // Name has already been found args = s.mid(argStart,i-argStart); //todo: expand macro (if already have) } else { name = s.mid(0,i); args = ""; } value = removeGCCAttributes(s.mid(i+1).trimmed()); } void CppPreprocessor::addHardDefineByLine(const QString &line) { addDefineByLine(line,true); } void CppPreprocessor::addDefineByLine(const QString &line, bool hardCoded) { // Remove define constexpr int DEFINE_LEN=6; QString s = line.mid(DEFINE_LEN).trimmed(); QString name, args, value; // Get parts from generalized function getDefineParts(s, name, args, value); // Add to the list addDefineByParts(name, args, value, hardCoded); } void CppPreprocessor::setScanOptions(bool parseSystem, bool parseLocal) { mParseSystem = parseSystem; mParseLocal=parseLocal; } void CppPreprocessor::preprocess(const QString &fileName) { clearTempResults(); mFileName = fileName; //mDefines = mHardDefines; openInclude(fileName); // StringsToFile(mBuffer,"f:\\buffer.txt"); preprocessBuffer(); // StringsToFile(mBuffer,"f:\\buffer.txt"); // StringsToFile(mResult,"f:\\log.txt"); } void CppPreprocessor::invalidDefinesInFile(const QString &fileName) { PDefineMap defineMap = mFileDefines.value(fileName,PDefineMap()); if (defineMap) { foreach (const PDefine& define, *defineMap) { const PDefine& p = mDefines.value(define->name); if (p == define) { mDefines.remove(define->name); } } mFileDefines.remove(fileName); } } void CppPreprocessor::dumpDefinesTo(const QString &fileName) const { QFile file(fileName); if (file.open(QIODevice::WriteOnly|QIODevice::Truncate)) { QTextStream stream(&file); for (const PDefine& define:mDefines) { stream<name,define->args,define->value) .arg(define->hardCoded).arg(define->formatValue) #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) <baseFile<<" : " #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) <= QT_VERSION_CHECK(5,15,0) <includeFiles.keys()) { stream<<"\t--"+s #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) <= QT_VERSION_CHECK(5,15,0) <= QT_VERSION_CHECK(5,15,0) <= QT_VERSION_CHECK(5,15,0) // <usings) { // stream<<"\t++"+s // #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) // <= QT_VERSION_CHECK(5,15,0) // <statements) { // if (statement) { // stream<command,statement->fullName) // #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) // <= mBuffer.count()) return ""; // we've gone past the final #preprocessor line. Yay skipToEndOfPreprocessor(); int preProcTo = mIndex; // Calculate index to insert defines in in result file mPreProcIndex = (mResult.count() - 1) + 1; // offset by one for #include rootfile // Assemble whole line, convert newlines to space QString result; for (int i=preProcFrom;i<=preProcTo;i++) { if (mBuffer[i].endsWith('\\')) { result+=mBuffer[i].leftRef(mBuffer[i].size()-1)+' '; } else { result+=mBuffer[i]+' '; } mResult.append("");// defines resolve into empty files, except #define and #include } // Step over mIndex++; return result; } void CppPreprocessor::handleBranch(const QString &line) { if (line.startsWith("ifdef")) { // // if a branch that is not at our level is false, current branch is false too; // for (int i=0;i<=mBranchResults.count()-2;i++) { // if (!mBranchResults[i]) { // setCurrentBranch(false); // return; // } // } if (getCurrentBranch()!=BranchResult::isTrue) { setCurrentBranch(BranchResult::parentIsFalse); } else { constexpr int IFDEF_LEN = 5; //length of ifdef; QString name = line.mid(IFDEF_LEN).trimmed(); setCurrentBranch( getDefine(name)!=nullptr?(BranchResult::isTrue):(BranchResult::isFalse) ); } } else if (line.startsWith("ifndef")) { // // if a branch that is not at our level is false, current branch is false too; // for (int i=0;i<=mBranchResults.count()-2;i++) { // if (!mBranchResults[i]) { // setCurrentBranch(false); // return; // } // } if (getCurrentBranch()!=BranchResult::isTrue) { setCurrentBranch(BranchResult::parentIsFalse); } else { constexpr int IFNDEF_LEN = 6; //length of ifndef; QString name = line.mid(IFNDEF_LEN).trimmed(); setCurrentBranch( getDefine(name)==nullptr?(BranchResult::isTrue):(BranchResult::isFalse) ); } } else if (line.startsWith("if")) { // // if a branch that is not at our level is false, current branch is false too; // for (int i=0;i<=mBranchResults.count()-2;i++) { // if (!mBranchResults[i]) { // setCurrentBranch(false); // return; // } // } if (getCurrentBranch()!=BranchResult::isTrue) {// we are already inside an if that is NOT being taken setCurrentBranch(BranchResult::parentIsFalse);// so don't take this one either } else { constexpr int IF_LEN = 2; //length of if; QString ifLine = line.mid(IF_LEN).trimmed(); bool testResult = evaluateIf(ifLine); setCurrentBranch(testResult?(BranchResult::isTrue):(BranchResult::isFalse)); } } else if (line.startsWith("else")) { BranchResult oldResult = getCurrentBranch(); // take either if or else removeCurrentBranch(); setCurrentBranch(calcElseBranchResult(oldResult)); } else if (line.startsWith("elif")) { BranchResult oldResult = getCurrentBranch(); // take either if or else removeCurrentBranch(); BranchResult elseResult = calcElseBranchResult(oldResult); if (elseResult == BranchResult::isTrue) { // don't take this one, if previous has been taken constexpr int ELIF_LEN = 4; //length of if; QString ifLine = line.mid(ELIF_LEN).trimmed(); bool testResult = evaluateIf(ifLine); setCurrentBranch(testResult?(BranchResult::isTrue):(BranchResult::isFalse)); } else { setCurrentBranch(elseResult); } } else if (line.startsWith("endif")) { removeCurrentBranch(); } } void CppPreprocessor::handleDefine(const QString &line) { if (getCurrentBranch() == BranchResult::isTrue) { addDefineByLine(line, false); mResult[mPreProcIndex] = '#' + line; // add define to result file so the parser can handle it } } void CppPreprocessor::handleInclude(const QString &line, bool fromNext) { if (getCurrentBranch()!=BranchResult::isTrue) // we're skipping due to a branch failure return; PParsedFile file = mIncludes.back(); QString fileName; // Get full header file name QString currentDir = includeTrailingPathDelimiter(extractFileDir(file->fileName)); QStringList includes; QStringList projectIncludes; bool found=false; if (fromNext && mIncludePaths.contains(currentDir)) { foreach(const QString& s, mIncludePathList) { if (found) { includes.append(s); continue; } else if (s == currentDir) found = true; } projectIncludes = mProjectIncludePathList; } else if (fromNext && mProjectIncludePaths.contains(currentDir)) { includes = mIncludePathList; foreach(const QString& s, mProjectIncludePathList) { if (found) { includes.append(s); continue; } else if (s == currentDir) found = true; } } else { includes = mIncludePathList; projectIncludes = mProjectIncludePathList; } fileName = getHeaderFilename( file->fileName, line, includes, projectIncludes); if (fileName.isEmpty()) return; PFileIncludes oldCurrentIncludes = mCurrentIncludes; openInclude(fileName); } void CppPreprocessor::handlePreprocessor(const QString &value) { switch(value[0].unicode()) { case 'd': if (value.startsWith("define")) handleDefine(value); break; case 'e': if (value.startsWith("else") || value.startsWith("elif") || value.startsWith("endif")) handleBranch(value); break; case 'i': if (value.startsWith("if")) handleBranch(value); else if (value.startsWith("include")) handleInclude(value, value.startsWith("include_next")); break; case 'u': if (value.startsWith("undef")) handleUndefine(value); break; } } void CppPreprocessor::handleUndefine(const QString &line) { // Remove undef constexpr int UNDEF_LEN = 5; QString name = line.mid(UNDEF_LEN).trimmed(); // //may be defined many times // while (true) { PDefine define = getDefine(name); if (define) { //remove the define from defines set mDefines.remove(name); //remove the define form the file where it defines if (define->filename == mFileName) { PDefineMap defineMap = mFileDefines.value(mFileName); if (defineMap) { defineMap->remove(name); } } } } QString CppPreprocessor::expandMacros(const QString &line, int depth) { //prevent infinit recursion if (depth > MAX_DEFINE_EXPAND_DEPTH) return line; QString word; QString newLine; int lenLine = line.length(); int i=0; while (i< lenLine) { QChar ch=line[i]; if (isWordChar(ch)) { word += ch; } else { if (!word.isEmpty()) { expandMacro(line,newLine,word,i,depth); } word = ""; if (i< lenLine) { newLine += line[i]; } } i++; } if (!word.isEmpty()) { expandMacro(line,newLine,word,i,depth); } return newLine; } QString CppPreprocessor::expandMacros() { //prevent infinit recursion QString word; QString newLine; QString line = mBuffer[mIndex]; int i=0; while (mIndex < mBuffer.size() && i=mBuffer.length()) return newLine; line = mBuffer[mIndex]; } word = ""; if (i< line.length()) { newLine += line[i]; } } i++; } if (!word.isEmpty()) { expandMacro(newLine,word,i,1); } return newLine; } void CppPreprocessor::expandMacro(const QString &line, QString &newLine, QString &word, int &i, int depth) { int lenLine = line.length(); PDefine define = getDefine(word); if (define && define->args=="" ) { if (define->value != word ) newLine += expandMacros(define->value,depth+1); else newLine += word; } else if (define && (define->args!="")) { while ((iname == "DEFHOOKPODX") // qDebug()<args<argList<formatValue; if (define && define->args=="" ) { if (define->value != word ) newLine += expandMacros(define->value,depth+1); else newLine += word; } else if (define && (define->args!="")) { int origI=i; int origIndex=mIndex; while(true) { while ((i=mBuffer.length()) return; line = mBuffer[mIndex]; i=0; } int argStart=-1; int argEnd=-1; int argLineStart=mIndex; int argLineEnd=mIndex; if ((i=mBuffer.length()) break; line = mBuffer[mIndex]; if (!inString && line.startsWith('#')) { break; } } ; if (level==0) { argEnd = i-1; argLineEnd = mIndex; QString args; if (argLineStart==argLineEnd) { args = line.mid(argStart,argEnd-argStart).trimmed(); //qDebug()<<"--"<name == "DEFHOOK") // qDebug()<name == "DEFHOOKPODX") // qDebug()<0) { PParsedFile topFile = mIncludes.front(); if (topFile->fileIncludes->includeFiles.contains(fileName)) { return; //already included } for (PParsedFile& parsedFile:mIncludes) { parsedFile->fileIncludes->includeFiles.insert(fileName,false); } // Backup old position if we're entering a new file PParsedFile innerMostFile = mIncludes.back(); innerMostFile->index = mIndex; innerMostFile->branches = mBranchResults.count(); innerMostFile->fileIncludes->includeFiles.insert(fileName,true); innerMostFile->fileIncludes->directIncludes.append(fileName); } // // Add the new file to the includes of the current file // // Only add items to the include list of the given file if the file hasn't been scanned yet // // The above is fixed by checking for duplicates. // // The proper way would be to do backtracking of files we have FINISHED scanned. // // These are not the same files as the ones in fScannedFiles. We have STARTED scanning these. // { // if Assigned(fCurrentIncludes) then // with fCurrentIncludes^ do // if not ContainsText(IncludeFiles, FileName) then // IncludeFiles := IncludeFiles + AnsiQuotedStr(FileName, '"') + ','; // } // Create and add new buffer/position PParsedFile parsedFile = std::make_shared(); parsedFile->index = 0; parsedFile->fileName = fileName; parsedFile->branches = 0; // parsedFile->buffer; it's auto initialized // Keep track of files we include here // Only create new items for files we have NOT scanned yet mCurrentIncludes = getFileIncludesEntry(fileName); if (!mCurrentIncludes) { // do NOT create a new item for a file that's already in the list mCurrentIncludes = std::make_shared(); mCurrentIncludes->baseFile = fileName; mIncludesList.insert(fileName,mCurrentIncludes); } parsedFile->fileIncludes = mCurrentIncludes; // Don't parse stuff we have already parsed if (!mScannedFiles.contains(fileName)) { // Parse ONCE //if not Assigned(Stream) then mScannedFiles.insert(fileName); // Only load up the file if we are allowed to parse it bool isSystemFile = isSystemHeaderFile(fileName, mIncludePaths) || isSystemHeaderFile(fileName, mProjectIncludePaths); if ((mParseSystem && isSystemFile) || (mParseLocal && !isSystemFile)) { QStringList bufferedText; if (mOnGetFileStream && mOnGetFileStream(fileName,bufferedText)) { parsedFile->buffer = bufferedText; } else { parsedFile->buffer = readFileToLines(fileName); } } } else { //add defines of already parsed including headers; addDefinesInFile(fileName); PFileIncludes fileIncludes = getFileIncludesEntry(fileName); for (PParsedFile& file:mIncludes) { foreach (const QString& incFile,fileIncludes->includeFiles.keys()) { file->fileIncludes->includeFiles.insert(incFile,false); } } } mIncludes.append(parsedFile); // Process it mIndex = parsedFile->index; mFileName = parsedFile->fileName; parsedFile->buffer = removeComments(parsedFile->buffer); mBuffer = parsedFile->buffer; // for (int i=0;i1) { // include from within a file mResult[mPreProcIndex] = includeLine; } else { mResult.append(includeLine); } } void CppPreprocessor::closeInclude() { if (mIncludes.isEmpty()) return; mIncludes.pop_back(); if (mIncludes.isEmpty()) return; PParsedFile parsedFile = mIncludes.back(); // Continue where we left off mIndex = parsedFile->index; mFileName = parsedFile->fileName; // Point to previous buffer and start past the include we walked into mBuffer = parsedFile->buffer; while (mBranchResults.count()>parsedFile->branches) { mBranchResults.pop_back(); } // Start augmenting previous include list again //fCurrentIncludes := GetFileIncludesEntry(fFileName); mCurrentIncludes = parsedFile->fileIncludes; // Update result file (we've left the previous file) mResult.append( QString("#include %1:%2").arg(parsedFile->fileName) .arg(parsedFile->index+1)); } CppPreprocessor::BranchResult CppPreprocessor::calcElseBranchResult(BranchResult oldResult) { switch(oldResult) { case BranchResult::isTrue: return BranchResult::isFalse_but_trued; case BranchResult::isFalse: return BranchResult::isTrue; case BranchResult::isFalse_but_trued: return BranchResult::isFalse_but_trued; case BranchResult::parentIsFalse: return BranchResult::parentIsFalse; } Q_ASSERT( false ); //We should fail here. return BranchResult::isFalse; } void CppPreprocessor::addDefinesInFile(const QString &fileName) { if (mProcessed.contains(fileName)) return; mProcessed.insert(fileName); // then add the defines defined in it PDefineMap defineList = mFileDefines.value(fileName, PDefineMap()); if (defineList) { foreach (const PDefine& define, defineList->values()) { mDefines.insert(define->name,define); } } PFileIncludes fileIncludes = getFileIncludesEntry(fileName); foreach (const QString& file, fileIncludes->includeFiles.keys()) { addDefinesInFile(file); } } void CppPreprocessor::parseArgs(PDefine define) { QString args=define->args.mid(1,define->args.length()-2).trimmed(); // remove '(' ')' if(args=="") return ; define->argList = args.split(','); for (int i=0;iargList.size();i++) { define->argList[i]=define->argList[i].trimmed(); define->argUsed.append(false); } QList tokens = tokenizeValue(define->value); QString formatStr = ""; DefineArgTokenType lastTokenType=DefineArgTokenType::Other; int index; foreach (const PDefineArgToken& token, tokens) { switch(token->type) { case DefineArgTokenType::Identifier: index = define->argList.indexOf(token->value); if (index>=0) { define->argUsed[index] = true; if (lastTokenType == DefineArgTokenType::Sharp) { formatStr+= "\"%"+QString("%1").arg(index+1)+"\""; break; } else { formatStr+= "%"+QString("%1").arg(index+1); break; } } formatStr += token->value; break; case DefineArgTokenType::DSharp: case DefineArgTokenType::Sharp: break; case DefineArgTokenType::Space: case DefineArgTokenType::Symbol: formatStr+=token->value; break; default: break; } lastTokenType = token->type; } define->formatValue = formatStr; } QList CppPreprocessor::tokenizeValue(const QString &value) { int i=0; PDefineArgToken token = std::make_shared(); token->type = DefineArgTokenType::Other; QList tokens; bool skipSpaces=false; while (itype==DefineArgTokenType::Other) { token->value = " "; token->type = DefineArgTokenType::Space; } else if (token->type!=DefineArgTokenType::Space) { tokens.append(token); token = std::make_shared(); token->value = " "; token->type = DefineArgTokenType::Space; } i++; } else if (ch=='#') { if (token->type!=DefineArgTokenType::Other && token->type!=DefineArgTokenType::Space) { tokens.append(token); token = std::make_shared(); } if ((i+1value = "##"; token->type = DefineArgTokenType::DSharp; } else { i++; token->value = "#"; token->type = DefineArgTokenType::Sharp; } skipSpaces=true; tokens.append(token); token = std::make_shared(); token->value = ""; token->type = DefineArgTokenType::Other; } else if (isWordChar(ch)) { if (token->type==DefineArgTokenType::Other) { token->value = ch ; token->type = DefineArgTokenType::Identifier; } else if (token->type==DefineArgTokenType::Identifier) { token->value+=ch; } else if (skipSpaces && token->type==DefineArgTokenType::Space) { //dont use space; token->value = ch ; token->type = DefineArgTokenType::Identifier; } else { tokens.append(token); token = std::make_shared(); token->value = ch ; token->type = DefineArgTokenType::Identifier; } skipSpaces=false; i++; } else { if (skipSpaces && token->type==DefineArgTokenType::Space) { //dont use space; } else if (token->type!=DefineArgTokenType::Other) { tokens.append(token); token = std::make_shared(); } skipSpaces=false; token->value = ch ; token->type = DefineArgTokenType::Symbol; i++; } } if(token->type!=DefineArgTokenType::Other) tokens.append(token); return tokens; } QStringList CppPreprocessor::removeComments(const QStringList &text) { QStringList result; ContentType currentType = ContentType::Other; QString delimiter; for (const QString& line:text) { QString s; int pos = 0; bool stopProcess=false; int lineLen=line.length(); s.reserve(line.length()); while (pos 0) { QString s; do { s = getNextPreprocessor(); if (s.startsWith('#')) { s = s.mid(1).trimmed(); // remove # if (!s.isEmpty()) { handlePreprocessor(s); } } } while (!s.isEmpty()); closeInclude(); } } void CppPreprocessor::skipToEndOfPreprocessor() { int indexLimit = mBuffer.count()-1; // Skip until last char of line is NOT \ anymore while ((mIndex < indexLimit) && mBuffer[mIndex].endsWith('\\')) mIndex++; } void CppPreprocessor::skipToPreprocessor() { int bufferCount = mBuffer.count(); // Increment until a line begins with a # while ((mIndex < bufferCount) && !mBuffer[mIndex].startsWith('#')) { if (getCurrentBranch()==BranchResult::isTrue) { // if not skipping, expand current macros int startIndex = mIndex; QString expanded = expandMacros(); mResult.append(expanded); for (int i=startIndex;i='a' && ch<='z') || (ch>='A' && ch<='Z') || ch.isLetter() || (ch>='0' && ch<='9')) { return true; } return false; } bool CppPreprocessor::isIdentChar(const QChar &ch) { if (ch=='_' || ch == '*' || ch == '&' || ch == '~' || ch.isLetter() //(ch>='a' && ch<='z') || (ch>='A' && ch<='Z') || (ch>='0' && ch<='9')) { return true; } return false; } bool CppPreprocessor::isLineChar(const QChar &ch) { return ch=='\r' || ch == '\n'; } bool CppPreprocessor::isSpaceChar(const QChar &ch) { return ch == ' ' || ch == '\t'; } //bool CppPreprocessor::isOperatorChar(const QChar &ch) //{ // switch(ch.unicode()) { // case '+': // case '-': // case '*': // case '/': // case '!': // case '=': // case '<': // case '>': // case '&': // case '|': // case '^': // return true; // default: // return false; // } //} bool CppPreprocessor::isMacroIdentChar(const QChar &ch) { //return (ch>='A' && ch<='Z') || (ch>='a' && ch<='z') return ch.isLetter() || ch == '_'; } bool CppPreprocessor::isDigit(const QChar &ch) { return (ch>='0' && ch<='9'); } bool CppPreprocessor::isNumberChar(const QChar &ch) { if (ch>='0' && ch<='9') return true; if (ch>='a' && ch<='f') return true; if (ch>='A' && ch<='F') return true; switch(ch.unicode()) { case 'x': case 'X': case 'u': case 'U': case 'l': case 'L': return true; default: return false; } } QString CppPreprocessor::lineBreak() { return "\n"; } bool CppPreprocessor::evaluateIf(const QString &line) { QString newLine = expandDefines(line); // replace FOO by numerical value of FOO return evaluateExpression(newLine); } QString CppPreprocessor::expandDefines(QString line) { int searchPos = 0; while (searchPos < line.length()) { // We have found an identifier. It is not a number suffix. Try to expand it if (isMacroIdentChar(line[searchPos]) && ( (searchPos == 0) || !isDigit(line[searchPos - 1]))) { int head = searchPos; int tail = searchPos; // Get identifier name (numbers are allowed, but not at the start while ((tail < line.length()) && (isMacroIdentChar(line[tail]) || isDigit(line[head]))) tail++; // qDebug()<<"1 "<=line.length()) || !isMacroIdentChar(line[defineStart])) { line = ""; // broken line break; } while ((tail < line.length()) && (isMacroIdentChar(line[tail]) || isDigit(line[tail]))) tail++; } // qDebug()<<"2 "<value.isEmpty()) insertValue = define->value; else insertValue = "0"; } } // Insert found value at place line.remove(nameStart, nameEnd - nameStart); line.insert(searchPos,insertValue); } } else { searchPos ++ ; } } return line; } bool CppPreprocessor::skipBraces(const QString &line, int &index, int step) { int level = 0; while ((index >= 0) && (index < line.length())) { // Find the corresponding opening brace if (line[index] == '(') { level++; } else if (line[index] == ')') { level--; } if (level == 0) return true; index+=step; } return false; } QString CppPreprocessor::expandFunction(PDefine define, QString args) { // Replace function by this string QString result = define->formatValue; if (args.startsWith('(') && args.endsWith(')')) { args = args.mid(1,args.length()-2); } QStringList argValues; int i=0; bool inString = false; int lastSplit=0; while (iargList.length() && argValues.length()>0) { for (int i=0;iargUsed[i]) { QString argValue = argValues[i]; result=result.arg(argValue.trimmed()); } } } result.replace("%%","%"); return result; } bool CppPreprocessor::skipSpaces(const QString &expr, int &pos) { while (pos>" add_expr */ bool CppPreprocessor::evalShiftExpr(const QString &expr, int &result, int &pos) { if (!evalAddExpr(expr,result,pos)) return false; while (true) { if (!skipSpaces(expr,pos)) break; int rightResult; if (pos+1' && expr[pos+1]=='>') { pos += 2; if (!evalAddExpr(expr,rightResult,pos)) return false; result = (result >> rightResult); } else { break; } } return true; } /* * relation_expr = shift_expr | relation_expr ">=" shift_expr | relation_expr ">" shift_expr | relation_expr "<=" shift_expr | relation_expr "<" shift_expr */ bool CppPreprocessor::evalRelationExpr(const QString &expr, int &result, int &pos) { if (!evalShiftExpr(expr,result,pos)) return false; while (true) { if (!skipSpaces(expr,pos)) break; int rightResult; if (expr[pos]=='<') { if (pos+1') { if (pos+1= rightResult); } else { pos++; if (!evalShiftExpr(expr,rightResult,pos)) return false; result = (result > rightResult); } } else { break; } } return true; } /* * equal_expr = relation_expr | equal_expr "==" relation_expr | equal_expr "!=" relation_expr */ bool CppPreprocessor::evalEqualExpr(const QString &expr, int &result, int &pos) { if (!evalRelationExpr(expr,result,pos)) return false; while (true) { if (!skipSpaces(expr,pos)) break; if (pos+1>" add_expr relation_expr = shift_expr | relation_expr ">=" shift_expr | relation_expr ">" shift_expr | relation_expr "<=" shift_expr | relation_expr "<" shift_expr equal_expr = relation_expr | equal_expr "==" relation_expr | equal_expr "!=" relation_expr bit_and_expr = equal_expr | bit_and_expr "&" equal_expr bit_xor_expr = bit_and_expr | bit_xor_expr "^" bit_and_expr bit_or_expr = bit_xor_expr | bit_or_expr "|" bit_xor_expr logic_and_expr = bit_or_expr | logic_and_expr "&&" bit_or_expr logic_or_expr = logic_and_expr | logic_or_expr "||" logic_and_expr */ int CppPreprocessor::evaluateExpression(QString line) { int pos = 0; int result; bool ok = evalExpr(line,result,pos); if (!ok) return -1; //expr not finished if (skipSpaces(line,pos)) return -1; return result; } void CppPreprocessor::setOnGetFileStream(const GetFileStreamCallBack &newOnGetFileStream) { mOnGetFileStream = newOnGetFileStream; } const QList &CppPreprocessor::projectIncludePathList() const { return mProjectIncludePathList; } const QList &CppPreprocessor::includePathList() const { return mIncludePathList; } const DefineMap &CppPreprocessor::hardDefines() const { return mHardDefines; } const QSet &CppPreprocessor::projectIncludePaths() { return mProjectIncludePaths; } const QSet &CppPreprocessor::includePaths() { return mIncludePaths; } QSet &CppPreprocessor::scannedFiles() { return mScannedFiles; } QHash &CppPreprocessor::includesList() { return mIncludesList; } const QHash &CppPreprocessor::includesList() const { return mIncludesList; }