RedPanda-CPP/RedPandaIDE/parser/cpppreprocessor.cpp

1869 lines
57 KiB
C++
Raw Normal View History

2021-12-26 23:18:28 +08:00
/*
* 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 <https://www.gnu.org/licenses/>.
*/
2021-08-05 23:13:21 +08:00
#include "cpppreprocessor.h"
#include <QFile>
2021-08-11 20:06:28 +08:00
#include <QTextCodec>
2021-08-27 16:38:55 +08:00
#include <QDebug>
2021-10-04 22:32:34 +08:00
#include <QMessageBox>
#include "../utils.h"
2021-08-11 20:06:28 +08:00
2021-08-14 22:52:37 +08:00
CppPreprocessor::CppPreprocessor()
2021-08-05 23:13:21 +08:00
{
}
2021-08-07 18:02:57 +08:00
2021-08-12 21:25:13 +08:00
void CppPreprocessor::clear()
{
2022-10-22 10:44:10 +08:00
//don't use reset(), it will reset(add) defines.
clearTempResults();
2022-10-22 10:44:10 +08:00
//Result across processings.
//used by parser even preprocess finished
mFileInfos.clear();
2022-10-22 10:44:10 +08:00
mFileDefines.clear(); //dictionary to save defines for each headerfile;
mFileUndefines.clear(); //dictionary to save undefines for each headerfile;
2022-10-22 10:44:10 +08:00
mScannedFiles.clear();
//option data for the parser
//{ List of current project's include path }
2024-03-26 17:36:17 +08:00
mHardDefines.clear();
mDefines.clear();
//mHardDefines.clear(); // set by "cpp -dM -E -xc NUL"
2022-10-22 10:44:10 +08:00
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();
2021-08-12 21:25:13 +08:00
}
void CppPreprocessor::clearTempResults()
2022-10-22 10:44:10 +08:00
{
//temporary data when preprocessing single file
mFileName="";
mBuffer.clear();
mResult.clear();
mCurrentFileInfo=nullptr;
2024-04-09 19:39:35 +08:00
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
}
2021-08-12 21:25:13 +08:00
void CppPreprocessor::addDefineByParts(const QString &name, const QString &args, const QString &value, bool hardCoded)
{
// Check for duplicates
PDefine define = std::make_shared<Define>();
define->name = name;
define->args = args;
define->value = value;
define->filename = mFileName;
//define->argList;
define->formatValue = value;
define->hardCoded = hardCoded;
define->varArgIndex = -1;
2021-08-12 21:25:13 +08:00
if (!args.isEmpty())
parseArgs(define);
if (hardCoded) {
2021-08-12 21:25:13 +08:00
mHardDefines.insert(name,define);
mDefines.insert(name,define);
} else {
2021-08-12 21:25:13 +08:00
PDefineMap defineMap = mFileDefines.value(mFileName,PDefineMap());
if (!defineMap) {
defineMap = std::make_shared<DefineMap>();
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)
2021-08-12 21:25:13 +08:00
{
QString s = input.trimmed();
name = "";
args = "";
value = "";
2021-08-12 21:25:13 +08:00
// 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());
2023-10-27 20:16:44 +08:00
name.squeeze();
value.squeeze();
args.squeeze();
2021-08-12 21:25:13 +08:00
}
void CppPreprocessor::addDefineByLine(const QString &line, bool hardCoded)
{
// Remove define
constexpr int DEFINE_LEN=6;
QString s = line.mid(DEFINE_LEN).trimmed();
2021-08-12 21:25:13 +08:00
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) {
2021-08-29 00:48:23 +08:00
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);
}
}
2021-08-21 22:15:44 +08:00
void CppPreprocessor::dumpDefinesTo(const QString &fileName) const
{
QFile file(fileName);
if (file.open(QIODevice::WriteOnly|QIODevice::Truncate)) {
QTextStream stream(&file);
2021-08-29 00:48:23 +08:00
for (const PDefine& define:mDefines) {
stream<<QString("%1 %2 %3 %4 %5\n")
.arg(define->name,define->args,define->value)
.arg(define->hardCoded).arg(define->formatValue)
2022-07-24 11:19:11 +08:00
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
2022-07-04 11:39:06 +08:00
<<Qt::endl;
#else
2022-01-04 16:50:54 +08:00
<<endl;
2022-07-04 11:39:06 +08:00
#endif
}
}
}
2021-08-21 22:15:44 +08:00
void CppPreprocessor::dumpIncludesListTo(const QString &fileName) const
{
QFile file(fileName);
if (file.open(QIODevice::WriteOnly|QIODevice::Truncate)) {
QTextStream stream(&file);
for (const PParsedFileInfo& fileInfo:mFileInfos) {
2024-04-09 18:10:44 +08:00
stream<<fileInfo->fileName()<<" : "
2022-07-24 11:19:11 +08:00
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
2022-07-04 11:39:06 +08:00
<<Qt::endl;
#else
<<endl;
#endif
stream<<"\t**includes:**"
2022-07-24 11:19:11 +08:00
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
2022-07-04 11:39:06 +08:00
<<Qt::endl;
#else
<<endl;
#endif
2024-04-09 18:10:44 +08:00
foreach (const QString& s,fileInfo->includes()) {
2022-07-04 11:39:06 +08:00
stream<<"\t--"+s
2022-07-24 11:19:11 +08:00
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
2022-07-04 11:39:06 +08:00
<<Qt::endl;
#else
<<endl;
#endif
}
}
}
}
void CppPreprocessor::addIncludePath(const QString &fileName)
{
2021-10-04 22:32:34 +08:00
if (!mIncludePaths.contains(fileName)) {
mIncludePaths.insert(fileName);
mIncludePathList.append(fileName);
}
}
void CppPreprocessor::addProjectIncludePath(const QString &fileName)
{
2021-10-04 22:32:34 +08:00
if (!mProjectIncludePaths.contains(fileName)) {
mProjectIncludePaths.insert(fileName);
mProjectIncludePathList.append(fileName);
}
}
void CppPreprocessor::removeScannedFile(const QString &filename)
{
invalidDefinesInFile(filename);
mScannedFiles.remove(filename);
mFileInfos.remove(filename);
mFileDefines.remove(filename);
mFileUndefines.remove(filename);
}
2021-08-08 17:22:37 +08:00
QString CppPreprocessor::getNextPreprocessor()
{
skipToPreprocessor(); // skip until # at start of line
int preProcFrom = mIndex;
if (preProcFrom >= 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
2021-08-12 21:25:13 +08:00
// Assemble whole line, convert newlines to space
2021-08-08 17:22:37 +08:00
QString result;
for (int i=preProcFrom;i<=preProcTo;i++) {
2021-08-27 23:51:42 +08:00
if (mBuffer[i].endsWith('\\')) {
result+=mBuffer[i].left(mBuffer[i].size()-1)+' ';
2021-08-12 21:25:13 +08:00
} else {
result+=mBuffer[i]+' ';
}
2021-08-08 17:22:37 +08:00
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")) {
2021-08-08 22:11:09 +08:00
// // 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);
2021-08-08 17:22:37 +08:00
} else {
constexpr int IFDEF_LEN = 5; //length of ifdef;
2021-08-08 22:11:09 +08:00
QString name = line.mid(IFDEF_LEN).trimmed();
setCurrentBranch( getDefine(name)!=nullptr?(BranchResult::isTrue):(BranchResult::isFalse) );
2021-08-08 17:22:37 +08:00
}
2021-08-08 22:11:09 +08:00
} 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);
2021-08-08 22:11:09 +08:00
} else {
constexpr int IFNDEF_LEN = 6; //length of ifndef;
QString name = line.mid(IFNDEF_LEN).trimmed();
setCurrentBranch( getDefine(name)==nullptr?(BranchResult::isTrue):(BranchResult::isFalse) );
2021-08-08 22:11:09 +08:00
}
} 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
2021-08-08 22:11:09 +08:00
} 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));
2021-08-08 22:11:09 +08:00
}
} else if (line.startsWith("else")) {
BranchResult oldResult = getCurrentBranch(); // take either if or else
2021-08-08 22:11:09 +08:00
removeCurrentBranch();
setCurrentBranch(calcElseBranchResult(oldResult));
2021-08-08 22:11:09 +08:00
} else if (line.startsWith("elif")) {
BranchResult oldResult = getCurrentBranch(); // take either if or else
2021-08-08 22:11:09 +08:00
removeCurrentBranch();
BranchResult elseResult = calcElseBranchResult(oldResult);
if (elseResult == BranchResult::isTrue) { // don't take this one, if previous has been taken
2021-08-08 22:11:09 +08:00
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);
2021-08-08 22:11:09 +08:00
}
} else if (line.startsWith("endif")) {
removeCurrentBranch();
2021-08-08 17:22:37 +08:00
}
}
2021-08-10 21:27:24 +08:00
void CppPreprocessor::handleDefine(const QString &line)
{
if (getCurrentBranch() == BranchResult::isTrue) {
2021-08-10 21:27:24 +08:00
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)
2021-08-10 21:27:24 +08:00
{
if (getCurrentBranch()!=BranchResult::isTrue) // we're skipping due to a branch failure
2021-08-10 21:27:24 +08:00
return;
2024-04-09 19:39:35 +08:00
PParsedFile file = mIncludeStack.back();
QString fileName;
2021-08-10 21:27:24 +08:00
// Get full header file name
QString currentDir = includeTrailingPathDelimiter(extractFileDir(file->fileName));
2021-10-04 22:32:34 +08:00
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;
2021-10-04 22:32:34 +08:00
}
fileName = getHeaderFilename(
file->fileName,
line,
includes,
projectIncludes);
2021-08-10 21:27:24 +08:00
if (fileName.isEmpty())
return;
PParsedFileInfo oldCurrentIncludes = mCurrentFileInfo;
openInclude(fileName);
2021-08-10 21:27:24 +08:00
}
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;
}
2021-08-10 21:27:24 +08:00
}
void CppPreprocessor::handleUndefine(const QString &line)
{
// Remove undef
2021-08-11 11:47:47 +08:00
constexpr int UNDEF_LEN = 5;
QString name = line.mid(UNDEF_LEN).trimmed();
// //may be defined many times
// while (true) {
PDefine define = getDefine(name);
2021-08-11 11:47:47 +08:00
if (define) {
//remove the define from defines set
mDefines.remove(name);
if (define->filename == mFileName) {
//remove the define form the file where it defines
2021-08-11 11:47:47 +08:00
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<DefineMap>();
mFileUndefines.insert(mFileName,undefineMap);
}
if (!undefineMap->contains(name))
undefineMap->insert(name, define);
2021-08-11 11:47:47 +08:00
}
}
2021-08-10 21:27:24 +08:00
}
QString CppPreprocessor::expandMacros(const QString &text, QSet<QString> usedMacros) const
2021-08-07 23:30:01 +08:00
{
QString word;
QString newLine;
int lenLine = text.length();
2021-08-07 23:30:01 +08:00
int i=0;
while (i< lenLine) {
QChar ch=text[i];
2021-08-09 09:11:10 +08:00
if (isWordChar(ch)) {
2021-08-07 23:30:01 +08:00
word += ch;
} else {
if (!word.isEmpty()) {
expandMacro(text,newLine,word,i,usedMacros);
2021-08-07 23:30:01 +08:00
}
word = "";
if (i< lenLine) {
newLine += text[i];
2021-08-07 23:30:01 +08:00
}
}
i++;
}
if (!word.isEmpty()) {
expandMacro(text,newLine,word,i, usedMacros);
2021-08-07 23:30:01 +08:00
}
return newLine;
}
QString CppPreprocessor::expandMacros()
{
//prevent infinit recursion
QString word;
QString newLine;
QString line = mBuffer[mIndex];
QSet<QString> usedMacros;
int i=0;
while (mIndex < mBuffer.size() && i<line.length()) {
QChar ch=line[i];
if (isWordChar(ch)) {
word += ch;
} else {
if (!word.isEmpty()) {
expandMacro(newLine,word,i,usedMacros);
if (mIndex>=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<QString> usedMacros) const
2021-08-07 23:30:01 +08:00
{
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<lenLine) && (text[i] == ' ' || text[i]=='\t'))
i++;
int argStart=-1;
int argEnd=-1;
if ((i<lenLine) && (text[i]=='(')) {
argStart =i+1;
int level=0;
bool inString=false;
while (i<lenLine) {
switch(text[i].unicode()) {
case '\\':
if (inString)
i++;
break;
case '"':
inString = !inString;
break;
case '(':
if (!inString)
level++;
break;
case ')':
if (!inString)
level--;
}
2021-08-07 23:30:01 +08:00
i++;
if (level==0)
break;
2021-08-07 23:30:01 +08:00
}
if (level==0) {
argEnd = i-2;
QString args = text.mid(argStart,argEnd-argStart+1).trimmed();
QString formattedValue = expandFunction(define,args);
usedMacros.insert(word);
newText += expandMacros(formattedValue,usedMacros);
}
2021-08-07 23:30:01 +08:00
}
} else {
newText += word;
}
// }
}
void CppPreprocessor::expandMacro(QString &newLine, const QString &word, int &i, QSet<QString> 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<line.length()) && (line[i] == ' ' || line[i]=='\t'))
i++;
if (i<line.length())
break;
mIndex++;
if (mIndex>=mBuffer.length())
return;
line = mBuffer[mIndex];
i=0;
}
int argStart=-1;
int argEnd=-1;
int argLineStart=mIndex;
int argLineEnd=mIndex;
if ((i<line.length()) && (line[i]=='(')) {
argStart =i+1;
int level=0;
bool inString=false;
while (true) {
while (i<line.length()) {
switch(line[i].unicode()) {
case '\\':
if (inString)
i++;
break;
case '"':
inString = !inString;
break;
case '(':
if (!inString)
level++;
break;
case ')':
if (!inString)
level--;
break;
}
i++;
if (level==0)
break;
}
if (level==0)
break;
mIndex++;
i=0;
if (mIndex>=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()<<"--"<<args;
} else {
args = mBuffer[argLineStart].mid(argStart);
for (int i=argLineStart+1;i<argLineEnd;i++) {
args += mBuffer[i];
}
args += mBuffer[argLineEnd].left(argEnd);
}
QString formattedValue = expandFunction(define,args);
usedMacros.insert(word);
newLine += expandMacros(formattedValue,usedMacros);
}
} else {
newLine+=word;
i=origI;
mIndex=origIndex;
}
} else {
newLine += word;
}
2021-08-07 23:30:01 +08:00
}
2021-08-12 21:25:13 +08:00
QString CppPreprocessor::removeGCCAttributes(const QString &line)
{
QString newLine = "";
QString word = "";
int lenLine = line.length();
2021-08-27 16:38:55 +08:00
int i=0;
2021-08-12 21:25:13 +08:00
while(i< lenLine) {
if (isWordChar(line[i])) {
word += line[i];
} else {
if (!word.isEmpty()) {
removeGCCAttribute(line,newLine,i,word);
}
word = "";
2021-08-26 17:48:23 +08:00
if (i<lenLine) {
2021-08-12 21:25:13 +08:00
newLine = newLine+line[i];
}
}
i++;
}
if (!word.isEmpty())
removeGCCAttribute(line,newLine,i,word);
return newLine;
}
void CppPreprocessor::removeGCCAttribute(const QString &line, QString &newLine, int &i, const QString &word)
{
2021-08-27 16:38:55 +08:00
int lenLine = line.length();
int level = 0;
if (word=="__attribute__") {
while ( (i<lenLine) && isSpaceChar(line[i]))
i++;
if ((i<lenLine) && (line[i]=='(')) {
level=0;
while (i<lenLine) {
switch(line[i].unicode()) {
case '(': level++;
break;
case ')': level--;
break;
}
i++;
if (level==0)
break;
}
}
} else {
newLine += word;
}
2021-08-12 21:25:13 +08:00
}
2023-10-28 09:20:01 +08:00
void CppPreprocessor::openInclude(QString fileName)
2021-08-11 20:06:28 +08:00
{
PParsedFileInfo fileInfo = findFileInfo(fileName);
if (fileInfo) {
2024-04-09 18:10:44 +08:00
fileName = fileInfo->fileName();
2023-10-28 09:20:01 +08:00
} else {
fileName.squeeze();
2024-04-09 19:39:35 +08:00
fileInfo = std::make_shared<ParsedFileInfo>(fileName);
2023-10-28 09:20:01 +08:00
}
2024-04-09 19:39:35 +08:00
if (mIncludeStack.size()>0) {
bool alreadyIncluded = false;
2024-04-09 19:39:35 +08:00
for (PParsedFile& parsedFile:mIncludeStack) {
2024-04-09 18:10:44 +08:00
if (parsedFile->fileInfo->including(fileName)) {
alreadyIncluded = true;
}
2024-04-09 18:19:48 +08:00
parsedFile->fileInfo->addInclude(fileName);
2024-04-09 19:39:35 +08:00
foreach (const QString& includedFileName,fileInfo->includes()) {
parsedFile->fileInfo->addInclude(includedFileName);
}
2021-08-11 20:06:28 +08:00
}
2024-04-09 19:39:35 +08:00
PParsedFile innerMostFile = mIncludeStack.back();
2024-04-09 18:19:48 +08:00
innerMostFile->fileInfo->addDirectInclude(fileName);
if (alreadyIncluded)
return;
// Backup old position if we're entering a new file
innerMostFile->index = mIndex;
innerMostFile->branches = mBranchResults.count();
2021-08-11 20:06:28 +08:00
}
// Create and add new buffer/position
PParsedFile parsedFile = std::make_shared<ParsedFile>();
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
2024-04-09 19:39:35 +08:00
mCurrentFileInfo = fileInfo;
mFileInfos.insert(fileName,mCurrentFileInfo);
parsedFile->fileInfo = mCurrentFileInfo;
2021-08-11 20:06:28 +08:00
// Don't parse stuff we have already parsed
if (!mScannedFiles.contains(fileName)) {
2021-08-11 20:06:28 +08:00
// Parse ONCE
//if not Assigned(Stream) then
2021-08-21 22:15:44 +08:00
mScannedFiles.insert(fileName);
2021-08-11 20:06:28 +08:00
// Only load up the file if we are allowed to parse it
bool isSystemFile = isSystemHeaderFile(fileName, mIncludePaths) || isSystemHeaderFile(fileName, mProjectIncludePaths);
2021-08-11 20:06:28 +08:00
if ((mParseSystem && isSystemFile) || (mParseLocal && !isSystemFile)) {
QStringList bufferedText;
if (mOnGetFileStream && mOnGetFileStream(fileName,bufferedText)) {
parsedFile->buffer = bufferedText;
2021-08-11 20:06:28 +08:00
} else {
2021-11-12 10:51:00 +08:00
parsedFile->buffer = readFileToLines(fileName);
2021-08-11 20:06:28 +08:00
}
}
} else {
//add defines of already parsed including headers;
addDefinesInFile(fileName);
}
2024-04-09 19:39:35 +08:00
mIncludeStack.append(parsedFile);
2021-08-11 20:06:28 +08:00
// Process it
mIndex = parsedFile->index;
mFileName = parsedFile->fileName;
parsedFile->buffer = removeComments(parsedFile->buffer);
2021-08-11 20:06:28 +08:00
mBuffer = parsedFile->buffer;
// for (int i=0;i<mBuffer.count();i++) {
// mBuffer[i] = mBuffer[i].trimmed();
// }
2021-08-11 20:06:28 +08:00
// Update result file
QString includeLine = "#include " + fileName + ":1";
2024-04-09 19:39:35 +08:00
if (mIncludeStack.count()>1) { // include from within a file
2021-08-11 20:06:28 +08:00
mResult[mPreProcIndex] = includeLine;
} else {
mResult.append(includeLine);
}
}
2021-08-11 11:47:47 +08:00
2021-08-07 23:30:01 +08:00
void CppPreprocessor::closeInclude()
{
2024-04-09 19:39:35 +08:00
if (mIncludeStack.isEmpty())
2021-08-07 23:30:01 +08:00
return;
2024-04-09 19:39:35 +08:00
mIncludeStack.pop_back();
2021-08-07 23:30:01 +08:00
2024-04-09 19:39:35 +08:00
if (mIncludeStack.isEmpty())
2021-08-07 23:30:01 +08:00
return;
2024-04-09 19:39:35 +08:00
PParsedFile parsedFile = mIncludeStack.back();
2021-08-07 23:30:01 +08:00
// 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;
2021-08-07 23:30:01 +08:00
// 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.
2023-10-01 10:49:59 +08:00
return BranchResult::isFalse;
}
2021-08-07 18:02:57 +08:00
void CppPreprocessor::addDefinesInFile(const QString &fileName)
{
if (mProcessed.contains(fileName))
return;
mProcessed.insert(fileName);
2021-08-08 17:22:37 +08:00
2021-08-11 11:47:47 +08:00
// then add the defines defined in it
PDefineMap defineList = mFileDefines.value(fileName, PDefineMap());
if (defineList) {
foreach (const PDefine& define, defineList->values()) {
2021-08-11 11:47:47 +08:00
mDefines.insert(define->name,define);
}
}
PParsedFileInfo fileInfo = findFileInfo(fileName);
if (fileInfo) {
2024-04-09 18:10:44 +08:00
foreach (const QString& file, fileInfo->includes()) {
addDefinesInFile(file);
}
}
2021-08-07 18:02:57 +08:00
}
2021-08-07 23:30:01 +08:00
2021-08-12 21:25:13 +08:00
void CppPreprocessor::parseArgs(PDefine define)
{
QString args=define->args.mid(1,define->args.length()-2).trimmed(); // remove '(' ')'
if(args=="")
return ;
2023-10-27 20:16:44 +08:00
QStringList argList = args.split(',');
for (int i=0;i<argList.size();i++) {
argList[i]=argList[i].trimmed();
2021-08-27 16:38:55 +08:00
define->argUsed.append(false);
2021-08-12 21:25:13 +08:00
}
2021-08-27 23:51:42 +08:00
QList<PDefineArgToken> tokens = tokenizeValue(define->value);
2021-08-12 21:25:13 +08:00
QString formatStr = "";
2021-08-27 23:51:42 +08:00
DefineArgTokenType lastTokenType=DefineArgTokenType::Other;
int index;
foreach (const PDefineArgToken& token, tokens) {
2021-08-27 23:51:42 +08:00
switch(token->type) {
case DefineArgTokenType::Identifier:
if (token->value == "__VA_ARGS__") {
2023-10-27 20:16:44 +08:00
index = argList.indexOf("...");
define->varArgIndex = index;
} else {
2023-10-27 20:16:44 +08:00
index = argList.indexOf(token->value);
}
2021-08-27 23:51:42 +08:00
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;
}
2021-08-12 21:25:13 +08:00
}
2021-08-27 23:51:42 +08:00
formatStr += token->value;
break;
case DefineArgTokenType::DSharp:
case DefineArgTokenType::Sharp:
break;
case DefineArgTokenType::Space:
case DefineArgTokenType::Symbol:
formatStr+=token->value;
break;
2021-10-20 18:05:43 +08:00
default:
break;
2021-08-12 21:25:13 +08:00
}
2021-08-27 23:51:42 +08:00
lastTokenType = token->type;
2021-08-12 21:25:13 +08:00
}
define->formatValue = formatStr;
2023-10-27 20:16:44 +08:00
define->formatValue.squeeze();
2021-08-12 21:25:13 +08:00
}
2021-08-27 23:51:42 +08:00
QList<PDefineArgToken> CppPreprocessor::tokenizeValue(const QString &value)
2021-08-12 21:25:13 +08:00
{
int i=0;
2021-08-27 23:51:42 +08:00
PDefineArgToken token = std::make_shared<DefineArgToken>();
token->type = DefineArgTokenType::Other;
QList<PDefineArgToken> tokens;
bool skipSpaces=false;
2021-08-12 21:25:13 +08:00
while (i<value.length()) {
QChar ch = value[i];
if (isSpaceChar(ch)) {
2021-08-27 23:51:42 +08:00
if (token->type==DefineArgTokenType::Other) {
token->value = " ";
token->type = DefineArgTokenType::Space;
} else if (token->type!=DefineArgTokenType::Space) {
2021-08-12 21:25:13 +08:00
tokens.append(token);
2021-08-27 23:51:42 +08:00
token = std::make_shared<DefineArgToken>();
token->value = " ";
token->type = DefineArgTokenType::Space;
}
2021-08-12 21:25:13 +08:00
i++;
} else if (ch=='#') {
2021-08-27 23:51:42 +08:00
if (token->type!=DefineArgTokenType::Other
&& token->type!=DefineArgTokenType::Space) {
2021-08-12 21:25:13 +08:00
tokens.append(token);
2021-08-27 23:51:42 +08:00
token = std::make_shared<DefineArgToken>();
}
2021-08-12 21:25:13 +08:00
if ((i+1<value.length()) && (value[i+1]=='#')) {
i+=2;
2021-08-27 23:51:42 +08:00
token->value = "##";
token->type = DefineArgTokenType::DSharp;
2021-08-12 21:25:13 +08:00
} else {
i++;
2021-08-27 23:51:42 +08:00
token->value = "#";
token->type = DefineArgTokenType::Sharp;
2021-08-12 21:25:13 +08:00
}
2021-08-27 23:51:42 +08:00
skipSpaces=true;
tokens.append(token);
token = std::make_shared<DefineArgToken>();
token->value = "";
token->type = DefineArgTokenType::Other;
2021-08-12 21:25:13 +08:00
} else if (isWordChar(ch)) {
2021-08-27 23:51:42 +08:00
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<DefineArgToken>();
token->value = ch ;
token->type = DefineArgTokenType::Identifier;
}
skipSpaces=false;
2021-08-12 21:25:13 +08:00
i++;
} else {
2021-08-27 23:51:42 +08:00
if (skipSpaces && token->type==DefineArgTokenType::Space) {
//dont use space;
} else if (token->type!=DefineArgTokenType::Other) {
2021-08-12 21:25:13 +08:00
tokens.append(token);
2021-08-27 23:51:42 +08:00
token = std::make_shared<DefineArgToken>();
}
skipSpaces=false;
token->value = ch ;
token->type = DefineArgTokenType::Symbol;
2021-08-12 21:25:13 +08:00
i++;
}
}
2021-08-27 23:51:42 +08:00
if(token->type!=DefineArgTokenType::Other)
2021-08-12 21:25:13 +08:00
tokens.append(token);
return tokens;
}
QStringList CppPreprocessor::removeComments(const QStringList &text)
{
QStringList result;
ContentType currentType = ContentType::Other;
QString delimiter;
2021-08-29 00:48:23 +08:00
for (const QString& line:text) {
QString s;
int pos = 0;
bool stopProcess=false;
2023-03-13 00:13:23 +08:00
int lineLen=line.length();
s.reserve(line.length());
while (pos<lineLen) {
QChar ch =line[pos];
if (currentType == ContentType::AnsiCComment) {
2023-03-13 00:13:23 +08:00
if (ch=='*' && (pos+1<lineLen) && line[pos+1]=='/') {
pos+=2;
currentType = ContentType::Other;
} else {
pos+=1;
}
continue;
}
switch (ch.unicode()) {
case '"':
switch (currentType) {
case ContentType::String:
currentType=ContentType::Other;
break;
case ContentType::RawString:
if (line.midRef(0,pos).endsWith(')'+delimiter))
currentType = ContentType::Other;
break;
case ContentType::Other:
currentType=ContentType::String;
break;
case ContentType::RawStringPrefix:
delimiter+=ch;
break;
2021-10-20 18:05:43 +08:00
default:
break;
}
s+=ch;
break;
case '\'':
switch (currentType) {
case ContentType::Character:
currentType=ContentType::Other;
break;
case ContentType::Other:
currentType=ContentType::Character;
break;
case ContentType::RawStringPrefix:
delimiter+=ch;
break;
2021-10-20 18:05:43 +08:00
default:
break;
}
s+=ch;
break;
case 'R':
2023-03-13 00:13:23 +08:00
if (currentType == ContentType::Other && pos+1<lineLen && line[pos+1]=='"') {
s+=ch;
pos++;
ch = line[pos];
currentType=ContentType::RawStringPrefix;
delimiter = "";
}
if (currentType == ContentType::RawStringPrefix ) {
delimiter += ch;
}
s+=ch;
break;
case '(':
switch(currentType) {
case ContentType::RawStringPrefix:
currentType = ContentType::RawString;
break;
2021-10-20 18:05:43 +08:00
default:
break;
}
s+=ch;
break;
case '/':
2021-08-26 17:48:23 +08:00
if (currentType == ContentType::Other) {
2023-03-13 00:13:23 +08:00
if (pos+1<lineLen && line[pos+1]=='/') {
// line comment , skip all remainings of the current line
stopProcess = true;
2021-08-26 17:48:23 +08:00
break;
2023-03-13 00:13:23 +08:00
} else if (pos+1<lineLen && line[pos+1]=='*') {
/* ansi c comment */
pos++;
currentType = ContentType::AnsiCComment;
2021-08-26 17:48:23 +08:00
break;
}
}
2021-08-26 17:48:23 +08:00
s+=ch;
break;
case '\\':
switch (currentType) {
case ContentType::String:
case ContentType::Character:
pos++;
s+=ch;
2023-03-13 00:13:23 +08:00
if (pos<lineLen) {
ch = line[pos];
s+=ch;
}
2021-08-27 23:51:42 +08:00
break;
default:
s+=ch;
}
2021-08-27 23:51:42 +08:00
break;
2021-08-23 17:27:17 +08:00
default:
s+=ch;
}
if (stopProcess)
break;
pos++;
}
result.append(s.trimmed());
}
return result;
}
2021-08-11 20:06:28 +08:00
void CppPreprocessor::preprocessBuffer()
{
2024-04-09 19:39:35 +08:00
while (mIncludeStack.count() > 0) {
2021-08-11 20:06:28 +08:00
QString s;
do {
s = getNextPreprocessor();
if (s.startsWith('#')) {
s = s.mid(1).trimmed(); // remove #
2021-08-11 20:06:28 +08:00
if (!s.isEmpty()) {
handlePreprocessor(s);
}
}
} while (!s.isEmpty());
closeInclude();
}
}
2021-08-12 21:25:13 +08:00
void CppPreprocessor::skipToEndOfPreprocessor()
{
int indexLimit = mBuffer.count()-1;
2021-08-12 21:25:13 +08:00
// Skip until last char of line is NOT \ anymore
while ((mIndex < indexLimit) && mBuffer[mIndex].endsWith('\\'))
2021-08-12 21:25:13 +08:00
mIndex++;
}
void CppPreprocessor::skipToPreprocessor()
{
2023-03-13 00:13:23 +08:00
int bufferCount = mBuffer.count();
2021-08-12 21:25:13 +08:00
// Increment until a line begins with a #
2023-03-13 00:13:23 +08:00
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<mIndex;i++) {
mResult.append("");
}
//mResult.append(expandMacros(mBuffer[mIndex],1));
} else // If skipping due to a failed branch, clear line
2021-08-27 23:51:42 +08:00
mResult.append("");
2021-08-12 21:25:13 +08:00
mIndex++;
}
}
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;
}
2021-08-10 12:09:48 +08:00
}
2021-08-09 09:11:10 +08:00
bool CppPreprocessor::evaluateIf(const QString &line)
{
QString newLine = expandDefines(line); // replace FOO by numerical value of FOO
return evaluateExpression(newLine);
2021-08-09 09:11:10 +08:00
}
2021-08-10 12:09:48 +08:00
QString CppPreprocessor::expandDefines(QString line)
2021-08-09 09:11:10 +08:00
{
int searchPos = 0;
while (searchPos < line.length()) {
// We have found an identifier. It is not a number suffix. Try to expand it
2021-08-10 12:09:48 +08:00
if (isMacroIdentChar(line[searchPos]) && (
2021-08-26 17:48:23 +08:00
(searchPos == 0) || !isDigit(line[searchPos - 1]))) {
2021-08-10 12:09:48 +08:00
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++;
2021-08-27 16:38:55 +08:00
// qDebug()<<"1 "<<head<<tail<<line;
2021-08-10 12:09:48 +08:00
QString name = line.mid(head,tail-head);
int nameStart = head;
int nameEnd = tail;
if (name == "defined") {
//expand define
//tail = searchPos + name.length();
while ((tail < line.length()) && isSpaceChar(line[tail]))
tail++; // skip spaces
int defineStart;
// Skip over its arguments
if ((tail < line.length()) && (line[tail]=='(')) {
//braced argument (next word)
defineStart = tail+1;
2023-10-25 11:49:25 +08:00
if (!skipParenthesis(line, tail)) {
2021-08-10 12:09:48 +08:00
line = ""; // broken line
break;
}
} else {
//none braced argument (next word)
defineStart = tail;
if ((tail>=line.length()) || !isMacroIdentChar(line[defineStart])) {
2021-08-10 12:09:48 +08:00
line = ""; // broken line
break;
}
while ((tail < line.length()) && (isMacroIdentChar(line[tail]) || isDigit(line[tail])))
tail++;
}
2021-08-27 16:38:55 +08:00
// qDebug()<<"2 "<<defineStart<<tail<<line;
2021-08-19 23:49:23 +08:00
name = line.mid(defineStart, tail - defineStart);
PDefine define = getDefine(name);
2021-08-10 12:09:48 +08:00
QString insertValue;
if (!define) {
insertValue = "0";
} else {
insertValue = "1";
}
// Insert found value at place
line.remove(searchPos, tail-searchPos+1);
line.insert(searchPos,insertValue);
} else if ((name == "and") || (name == "or")) {
searchPos = tail; // Skip logical operators
} else {
// We have found a regular define. Replace it by its value
// Does it exist in the database?
PDefine define = getDefine(name);
2021-08-10 12:09:48 +08:00
QString insertValue;
if (!define) {
insertValue = "0";
} else {
while ((tail < line.length()) && isSpaceChar(line[tail]))
tail++;// skip spaces
// It is a function. Expand arguments
if ((tail < line.length()) && (line[tail] == '(')) {
head=tail;
2023-10-25 11:49:25 +08:00
if (skipParenthesis(line, tail)) {
if (name == "__has_builtin") {
insertValue = "0";
} else {
QString args = line.mid(head+1,tail-head-1);
insertValue = expandFunction(define,args);
}
2021-08-10 12:09:48 +08:00
nameEnd = tail+1;
} else {
line = "";// broken line
break;
}
// Replace regular define
} else {
if (!define->value.isEmpty())
insertValue = define->value;
else
insertValue = "0";
}
}
// Insert found value at place
line.remove(nameStart, nameEnd - nameStart);
line.insert(searchPos,insertValue);
2021-08-09 09:11:10 +08:00
}
2021-08-10 12:09:48 +08:00
} else {
searchPos ++ ;
}
}
return line;
}
2023-10-25 11:49:25 +08:00
bool CppPreprocessor::skipParenthesis(const QString &line, int &index, int step)
2021-08-10 12:09:48 +08:00
{
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)
2021-08-10 12:09:48 +08:00
{
// Replace function by this string
QString result = define->formatValue;
// if (args.startsWith('(') && args.endsWith(')')) {
// qDebug()<<define->name<<args;
// args = args.mid(1,args.length()-2);
// }
2021-08-10 12:09:48 +08:00
2023-10-27 20:16:44 +08:00
if (define->argUsed.length()==0) {
// do nothing
2023-10-27 20:16:44 +08:00
} else if (define->argUsed.length()==1) {
2023-10-25 11:49:25 +08:00
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 (i<args.length()) {
switch(args[i].unicode()) {
case '\\':
if (inString || inChar)
i++;
break;
case '(':
case '{':
if (!inString && !inChar)
level++;
break;
case ')':
case '}':
if (!inString && !inChar)
level--;
break;
case '"':
if (!inChar)
inString = !inString;
break;
case '\'':
if (!inString)
inChar = !inChar;
break;
case ',':
if (!inString && !inChar && level == 0) {
argValues.append(args.mid(lastSplit,i-lastSplit));
lastSplit=i+1;
}
break;
}
i++;
}
argValues.append(args.mid(lastSplit,i-lastSplit));
2023-10-25 11:49:25 +08:00
#ifdef QT_DEBUG
if (
2023-10-27 20:16:44 +08:00
(define->varArgIndex==-1 && argValues.length() != define->argUsed.length())
|| (define->varArgIndex!=-1 && argValues.length() < define->argUsed.length()-1)
2023-10-25 11:49:25 +08:00
) {
qDebug()<<"*** Expand Macro error ***";
2023-10-27 20:16:44 +08:00
qDebug()<<"Macro: "<<define->name<<define->args;
2023-10-25 11:49:25 +08:00
qDebug()<<"Actual param: "<<args;
qDebug()<<"Params splitted: "<<argValues;
qDebug()<<"**********";
}
#endif
2023-10-27 20:16:44 +08:00
if (argValues.length() >= define->argUsed.length()
&& argValues.length()>0) {
QStringList varArgs;
for (int i=0;i<argValues.length();i++) {
if (define->varArgIndex != -1
&& i >= define->varArgIndex ) {
varArgs.append(argValues[i].trimmed());
2023-10-27 20:16:44 +08:00
} else if (i<define->argUsed.length()
&& define->argUsed[i]) {
QString argValue = argValues[i];
result=result.arg(argValue.trimmed());
}
}
if (!varArgs.isEmpty() && define->varArgIndex != -1) {
result=result.arg(varArgs.join(","));
2021-08-27 16:38:55 +08:00
}
}
2021-08-10 12:09:48 +08:00
}
2021-08-12 21:25:13 +08:00
result.replace("%%","%");
2021-08-10 12:09:48 +08:00
return result;
}
bool CppPreprocessor::skipSpaces(const QString &expr, int &pos)
{
while (pos<expr.length() && isSpaceChar(expr[pos]))
pos++;
return pos<expr.length();
}
bool CppPreprocessor::evalNumber(const QString &expr, int &result, int &pos)
{
if (!skipSpaces(expr,pos))
return false;
QString s;
while (pos<expr.length() && isNumberChar(expr[pos])) {
s+=expr[pos];
pos++;
}
bool ok;
if (s.endsWith("LL",Qt::CaseInsensitive)) {
s.remove(s.length()-2,2);
result = s.toLongLong(&ok);
} else if (s.endsWith("L",Qt::CaseInsensitive)) {
s.remove(s.length()-1,1);
result = s.toLong(&ok);
} else if (s.endsWith("ULL",Qt::CaseInsensitive)) {
s.remove(s.length()-3,3);
result = s.toULongLong(&ok);
} else if (s.endsWith("UL",Qt::CaseInsensitive)) {
s.remove(s.length()-2,2);
result = s.toULong(&ok);
} else if (s.endsWith("U",Qt::CaseInsensitive)) {
s.remove(s.length()-1,1);
result = s.toUInt(&ok);
} else {
result = s.toInt(&ok);
}
return ok;
}
bool CppPreprocessor::evalTerm(const QString &expr, int &result, int &pos)
{
if (!skipSpaces(expr,pos))
return false;
if (expr[pos]=='(') {
pos++;
if (!evalExpr(expr,result,pos))
return false;
if (!skipSpaces(expr,pos))
return false;
if (expr[pos]!=')')
return false;
pos++;
2021-08-19 23:49:23 +08:00
return true;
} else {
return evalNumber(expr,result,pos);
}
}
/*
* unary_expr = term
| '+' term
| '-' term
| '!' term
| '~' term
*/
bool CppPreprocessor::evalUnaryExpr(const QString &expr, int &result, int &pos)
{
if (!skipSpaces(expr,pos))
return false;
if (expr[pos]=='+') {
pos++;
if (!evalTerm(expr,result,pos))
return false;
} else if (expr[pos]=='-') {
pos++;
if (!evalTerm(expr,result,pos))
return false;
result = -result;
} else if (expr[pos]=='~') {
pos++;
if (!evalTerm(expr,result,pos))
return false;
result = ~result;
} else if (expr[pos]=='!') {
pos++;
if (!evalTerm(expr,result,pos))
return false;
result = !result;
} else {
return evalTerm(expr,result,pos);
}
2021-08-19 23:49:23 +08:00
return true;
}
/*
* mul_expr = unary_expr
| mul_expr '*' unary_expr
| mul_expr '/' unary_expr
| mul_expr '%' unary_expr
*/
bool CppPreprocessor::evalMulExpr(const QString &expr, int &result, int &pos)
{
if (!evalUnaryExpr(expr,result,pos))
return false;
while (true) {
if (!skipSpaces(expr,pos))
break;
int rightResult;
if (expr[pos]=='*') {
pos++;
if (!evalUnaryExpr(expr,rightResult,pos))
return false;
result *= rightResult;
} else if (expr[pos]=='/') {
pos++;
if (!evalUnaryExpr(expr,rightResult,pos))
return false;
if (rightResult != 0)
result /= rightResult;
else
result = 0;
} else if (expr[pos]=='%') {
pos++;
if (!evalUnaryExpr(expr,rightResult,pos))
return false;
if (rightResult != 0)
result %= rightResult;
else
result = 0;
} else {
break;
}
}
return true;
}
/*
* add_expr = mul_expr
| add_expr '+' mul_expr
| add_expr '-' mul_expr
*/
bool CppPreprocessor::evalAddExpr(const QString &expr, int &result, int &pos)
{
2021-08-19 23:49:23 +08:00
if (!evalMulExpr(expr,result,pos))
return false;
while (true) {
if (!skipSpaces(expr,pos))
break;
int rightResult;
if (expr[pos]=='+') {
pos++;
if (!evalMulExpr(expr,rightResult,pos))
return false;
result += rightResult;
} else if (expr[pos]=='-') {
pos++;
if (!evalMulExpr(expr,rightResult,pos))
return false;
result -= rightResult;
} else {
break;
}
}
return true;
}
/*
* shift_expr = add_expr
| shift_expr "<<" add_expr
| shift_expr ">>" 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.length() && expr[pos] == '<' && expr[pos+1]=='<') {
pos += 2;
if (!evalAddExpr(expr,rightResult,pos))
return false;
result = (result << rightResult);
} else if (pos+1<expr.length() && expr[pos] == '>' && 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<expr.length() && expr[pos+1]=='=') {
pos+=2;
if (!evalShiftExpr(expr,rightResult,pos))
return false;
result = (result <= rightResult);
} else {
pos++;
if (!evalShiftExpr(expr,rightResult,pos))
return false;
result = (result < rightResult);
}
} else if (expr[pos]=='>') {
if (pos+1<expr.length() && expr[pos+1]=='=') {
pos+=2;
if (!evalShiftExpr(expr,rightResult,pos))
return false;
result = (result >= 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<expr.length() && expr[pos]=='!' && expr[pos+1]=='=') {
pos+=2;
int rightResult;
if (!evalRelationExpr(expr,rightResult,pos))
return false;
result = (result != rightResult);
} else if (pos+1<expr.length() && expr[pos]=='=' && expr[pos+1]=='=') {
pos+=2;
int rightResult;
if (!evalRelationExpr(expr,rightResult,pos))
return false;
result = (result == rightResult);
} else {
break;
}
}
return true;
}
/*
* bit_and_expr = equal_expr
| bit_and_expr "&" equal_expr
*/
bool CppPreprocessor::evalBitAndExpr(const QString &expr, int &result, int &pos)
{
if (!evalEqualExpr(expr,result,pos))
return false;
while (true) {
if (!skipSpaces(expr,pos))
break;
if (expr[pos]=='&'
&& (pos == expr.length()
|| expr[pos+1]!='&')) {
pos++;
int rightResult;
if (!evalEqualExpr(expr,rightResult,pos))
return false;
result = result & rightResult;
} else {
break;
}
}
return true;
}
2021-08-10 12:09:48 +08:00
/*
* bit_xor_expr = bit_and_expr
| bit_xor_expr "^" bit_and_expr
*/
bool CppPreprocessor::evalBitXorExpr(const QString &expr, int &result, int &pos)
{
if (!evalBitAndExpr(expr,result,pos))
return false;
while (true) {
if (!skipSpaces(expr,pos))
2021-08-10 12:09:48 +08:00
break;
if (expr[pos]=='^') {
pos++;
int rightResult;
if (!evalBitAndExpr(expr,rightResult,pos))
2021-08-10 12:09:48 +08:00
return false;
result = result ^ rightResult;
} else {
break;
}
}
return true;
}
/*
* bit_or_expr = bit_xor_expr
| bit_or_expr "|" bit_xor_expr
*/
bool CppPreprocessor::evalBitOrExpr(const QString &expr, int &result, int &pos)
{
if (!evalBitXorExpr(expr,result,pos))
return false;
while (true) {
if (!skipSpaces(expr,pos))
2021-08-10 12:09:48 +08:00
break;
if (expr[pos] == '|'
&& (pos == expr.length()
|| expr[pos+1]!='|')) {
2021-08-10 12:09:48 +08:00
pos++;
int rightResult;
if (!evalBitXorExpr(expr,rightResult,pos))
2021-08-10 12:09:48 +08:00
return false;
result = result | rightResult;
} else {
break;
}
}
return true;
}
/*
* logic_and_expr = bit_or_expr
| logic_and_expr "&&" bit_or_expr
*/
bool CppPreprocessor::evalLogicAndExpr(const QString &expr, int &result, int &pos)
{
if (!evalBitOrExpr(expr,result,pos))
return false;
while (true) {
if (!skipSpaces(expr,pos))
2021-08-10 12:09:48 +08:00
break;
if (pos+1<expr.length() && expr[pos]=='&' && expr[pos+1] =='&') {
pos+=2;
int rightResult;
if (!evalBitOrExpr(expr,rightResult,pos))
return false;
result = result && rightResult;
} else {
break;
}
}
return true;
}
/*
* logic_or_expr = logic_and_expr
| logic_or_expr "||" logic_and_expr
*/
bool CppPreprocessor::evalLogicOrExpr(const QString &expr, int &result, int &pos)
{
if (!evalLogicAndExpr(expr,result,pos))
return false;
while (true) {
if (!skipSpaces(expr,pos))
2021-08-10 12:09:48 +08:00
break;
if (pos+1<expr.length() && expr[pos]=='|' && expr[pos+1] =='|') {
pos+=2;
int rightResult;
if (!evalLogicAndExpr(expr,rightResult,pos))
return false;
result = result || rightResult;
} else {
break;
}
}
return true;
}
bool CppPreprocessor::evalExpr(const QString &expr, int &result, int &pos)
{
return evalLogicOrExpr(expr,result,pos);
}
/* BNF for C constant expression evaluation
* term = number
| '(' expression ')'
unary_expr = term
| '+' term
| '-' term
| '!' term
| '~' term
mul_expr = term
| mul_expr '*' term
| mul_expr '/' term
| mul_expr '%' term
add_expr = mul_expr
| add_expr '+' mul_expr
| add_expr '-' mul_expr
shift_expr = add_expr
| shift_expr "<<" add_expr
| shift_expr ">>" 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)
2021-08-10 12:09:48 +08:00
{
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;
2021-08-09 09:11:10 +08:00
}
2021-08-10 12:09:48 +08:00