/* * 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 mFileInfos.clear(); mFileDefines.clear(); //dictionary to save defines for each headerfile; mFileUndefines.clear(); //dictionary to save undefines for each headerfile; mScannedFiles.clear(); //option data for the parser //{ List of current project's include path } mHardDefines.clear(); 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(); mCurrentFileInfo=nullptr; mIncludeStack.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; define->varArgIndex = -1; 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()); name.squeeze(); value.squeeze(); args.squeeze(); } 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::preprocess(const QString &fileName) { clearTempResults(); mFileName = fileName; openInclude(fileName); preprocessBuffer(); } void CppPreprocessor::invalidDefinesInFile(const QString &fileName) { //remove all defines defined in this file 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); PDefine p2 = mHardDefines.value(define->name); if (p2) mDefines.insert(define->name, p2); } } mFileDefines.remove(fileName); } //restore all defines undefined in this file PDefineMap undefineMap = mFileUndefines.value(fileName,PDefineMap()); if (undefineMap) { foreach (const PDefine& define, *undefineMap) { mDefines.insert(define->name, define); } mFileUndefines.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) <fileName()<<" : " #if QT_VERSION >= QT_VERSION_CHECK(5,15,0) <= QT_VERSION_CHECK(5,15,0) <includes()) { stream<<"\t--"+s #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].left(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 = mIncludeStack.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; PParsedFileInfo oldCurrentIncludes = mCurrentFileInfo; 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); if (define->filename == mFileName) { //remove the define form the file where it defines PDefineMap defineMap = mFileDefines.value(mFileName); if (defineMap) { defineMap->remove(name); } } else { // add it to undefine map PDefineMap undefineMap = mFileUndefines.value(mFileName); if (!undefineMap) { undefineMap = std::make_shared(); mFileUndefines.insert(mFileName,undefineMap); } if (!undefineMap->contains(name)) undefineMap->insert(name, define); } } } QString CppPreprocessor::expandMacros(const QString &text, QSet usedMacros) const { QString word; QString newLine; int lenLine = text.length(); int i=0; while (i< lenLine) { QChar ch=text[i]; if (isWordChar(ch)) { word += ch; } else { if (!word.isEmpty()) { expandMacro(text,newLine,word,i,usedMacros); } word = ""; if (i< lenLine) { newLine += text[i]; } } i++; } if (!word.isEmpty()) { expandMacro(text,newLine,word,i, usedMacros); } return newLine; } QString CppPreprocessor::expandMacros() { //prevent infinit recursion QString word; QString newLine; QString line = mBuffer[mIndex]; QSet usedMacros; 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,usedMacros); } return newLine; } void CppPreprocessor::expandMacro(const QString &text, QString &newText, const QString &word, int &i, QSet usedMacros) const { if (usedMacros.contains(word)) return; int lenLine = text.length(); PDefine define = getDefine(word); if (define && define->args=="" ) { usedMacros.insert(word); if (define->value != word ) { newText += expandMacros(define->value,usedMacros); } else newText += word; } else if (define && (define->args!="")) { while ((i usedMacros) { if (usedMacros.contains(word)) return; QString line = mBuffer[mIndex]; PDefine define = getDefine(word); if (define && define->args=="" ) { usedMacros.insert(word); if (define->value != word ) newLine += expandMacros(define->value,usedMacros); 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()<<"--"<fileName(); } else { fileName.squeeze(); fileInfo = std::make_shared(fileName); } if (mIncludeStack.size()>0) { bool alreadyIncluded = false; for (PParsedFile& parsedFile:mIncludeStack) { if (parsedFile->fileInfo->including(fileName)) { alreadyIncluded = true; } parsedFile->fileInfo->addInclude(fileName); foreach (const QString& includedFileName,fileInfo->includes()) { parsedFile->fileInfo->addInclude(includedFileName); } } PParsedFile innerMostFile = mIncludeStack.back(); innerMostFile->fileInfo->addDirectInclude(fileName); if (alreadyIncluded) return; // Backup old position if we're entering a new file innerMostFile->index = mIndex; innerMostFile->branches = mBranchResults.count(); } // 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 mCurrentFileInfo = fileInfo; mFileInfos.insert(fileName,mCurrentFileInfo); parsedFile->fileInfo = mCurrentFileInfo; // 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); } mIncludeStack.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 (mIncludeStack.isEmpty()) return; mIncludeStack.pop_back(); if (mIncludeStack.isEmpty()) return; PParsedFile parsedFile = mIncludeStack.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 mCurrentFileInfo = parsedFile->fileInfo; // 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); } } PParsedFileInfo fileInfo = findFileInfo(fileName); if (fileInfo) { foreach (const QString& file, fileInfo->includes()) { addDefinesInFile(file); } } } void CppPreprocessor::parseArgs(PDefine define) { QString args=define->args.mid(1,define->args.length()-2).trimmed(); // remove '(' ')' if(args=="") return ; QStringList argList = args.split(','); for (int i=0;iargUsed.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: if (token->value == "__VA_ARGS__") { index = argList.indexOf("..."); define->varArgIndex = index; } else { index = 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; define->formatValue.squeeze(); } 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='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; } } 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::skipParenthesis(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, const QString &args) { // Replace function by this string QString result = define->formatValue; // if (args.startsWith('(') && args.endsWith(')')) { // qDebug()<name<argUsed.length()==0) { // do nothing } else if (define->argUsed.length()==1) { if (define->argUsed[0]) result=result.arg(args); } else { QStringList argValues; int i=0; bool inString = false; bool inChar = false; int lastSplit=0; int level=0; while (ivarArgIndex==-1 && argValues.length() != define->argUsed.length()) || (define->varArgIndex!=-1 && argValues.length() < define->argUsed.length()-1) ) { qDebug()<<"*** Expand Macro error ***"; qDebug()<<"Macro: "<name<args; qDebug()<<"Actual param: "<= define->argUsed.length() && argValues.length()>0) { QStringList varArgs; for (int i=0;ivarArgIndex != -1 && i >= define->varArgIndex ) { varArgs.append(argValues[i].trimmed()); } else if (iargUsed.length() && define->argUsed[i]) { QString argValue = argValues[i]; result=result.arg(argValue.trimmed()); } } if (!varArgs.isEmpty() && define->varArgIndex != -1) { result=result.arg(varArgs.join(",")); } } } 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; }