diff --git a/NEWS.md b/NEWS.md index 11662470..d1279b08 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,7 @@ Red Panda C++ Version 2.2 - enhancement: slightly reduce parsing time - fix: Wrong charset name returned when saveing file - fix: 'using =' / 'namespace =' not correctly handled + - fix: Pressing '*' at begin of line will crash app Red Panda C++ Version 2.1 diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index 09bf0347..dbfd03e2 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -2338,7 +2338,7 @@ bool Editor::handleBracketSkip() bool Editor::handleMultilineCommentCompletion() { - if ((caretX()-2 < lineText().length()) && (lineText()[caretX() - 2] == '/')) { + if ((caretX()-2>=0) && (caretX()-2 < lineText().length()) && (lineText()[caretX() - 2] == '/')) { QString text=selText(); beginUpdate(); beginUndoBlock(); diff --git a/RedPandaIDE/parser/cppparser.cpp b/RedPandaIDE/parser/cppparser.cpp index 00f3644a..398d51a5 100644 --- a/RedPandaIDE/parser/cppparser.cpp +++ b/RedPandaIDE/parser/cppparser.cpp @@ -1275,6 +1275,7 @@ PStatement CppParser::addStatement(const PStatement& parent, PStatement CppParser::addStatement(const PStatement &parent, const QString &fileName, const QString &aType, const QString &command, int argStart, int argEnd, const QString &value, int line, StatementKind kind, const StatementScope &scope, const StatementClassScope &classScope, bool isDefinition, bool isStatic) { + Q_ASSERT(mTokenizer[argStart]->text=='('); QString args("("); QString noNameArgs("("); @@ -1284,7 +1285,7 @@ PStatement CppParser::addStatement(const PStatement &parent, const QString &file QString word; for (int i=start;itext[0]; - if (this->isLetterChar(ch)) { + if (this->isIdentChar(ch)) { QString spaces=(i>argStart)?" ":""; args+=spaces; word += mTokenizer[i]->text[0]; @@ -1556,11 +1557,10 @@ QStringList CppParser::sortFilesByIncludeRelations(const QSet &files) bool CppParser::checkForKeyword(KeywordType& keywordType) { - keywordType = mCppKeywords.value(mTokenizer[mIndex]->text,KeywordType::None); + keywordType = mCppKeywords.value(mTokenizer[mIndex]->text,KeywordType::NotKeyword); switch(keywordType) { case KeywordType::Catch: case KeywordType::For: - case KeywordType::None: case KeywordType::Public: case KeywordType::Private: case KeywordType::Enum: @@ -1570,6 +1570,8 @@ bool CppParser::checkForKeyword(KeywordType& keywordType) case KeywordType::Using: case KeywordType::Friend: case KeywordType::Protected: + case KeywordType::None: + case KeywordType::NotKeyword: return false; default: return true; @@ -1625,13 +1627,13 @@ bool CppParser::checkForMethod(QString &sType, QString &sName, int &argStartInde if (currentScope) { //in namespace, it might be function or object initilization if (currentScope->kind == StatementKind::skNamespace - && isNotFuncArgs(mIndex + 1,mTokenizer[mIndex + 1]->matchIndex)) { + && isNotFuncArgs(mIndex + 1)) { break; //not in class, it can't be a valid function definition } else if (currentScope->kind != StatementKind::skClass) break; //variable can't be initialized in class definition, it must be a function - } else if (isNotFuncArgs(mIndex + 1,mTokenizer[mIndex + 1]->matchIndex)) + } else if (isNotFuncArgs(mIndex + 1)) break; } sName = mTokenizer[mIndex]->text; @@ -1708,12 +1710,12 @@ bool CppParser::checkForPreprocessor() return (mTokenizer[mIndex]->text.startsWith('#')); } -bool CppParser::checkForLambda() -{ - return (mIndex+1text.startsWith('[') - && mTokenizer[mIndex+1]->text=='('); -} +//bool CppParser::checkForLambda() +//{ +// return (mIndex+1text.startsWith('[') +// && mTokenizer[mIndex+1]->text=='('); +//} bool CppParser::checkForScope(KeywordType keywordType) { @@ -1727,13 +1729,7 @@ bool CppParser::checkForScope(KeywordType keywordType) void CppParser::checkForSkipStatement() { if ((mSkipList.count()>0) && (mIndex == mSkipList.back())) { // skip to next ';' - do { - if (isLeftParenthesis(mTokenizer[mIndex]->text)) - mIndex=mTokenizer[mIndex]->matchIndex+1; - else - mIndex++; - } while ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text[0] != ';')); - mIndex++; //skip ';' + skipNextSemicolon(mIndex); mSkipList.pop_back(); } } @@ -1819,6 +1815,189 @@ bool CppParser::checkForUsing(KeywordType keywordType) } +void CppParser::checkAndHandleMethodOrVar() +{ + if (mIndex+2>=mTokenizer.tokenCount()) { + mIndex+=2; // left's finish; + return; + } + QString currentText=mTokenizer[mIndex]->text; + + mIndex++; + //next token must be */&/word/(/{ + if (mTokenizer[mIndex]->text=='(') { + int indexAfterParentheis=mTokenizer[mIndex]->matchIndex+1; + if (indexAfterParentheis>=mTokenizer.tokenCount()) { + //error + mIndex=indexAfterParentheis; + } else if (mTokenizer[indexAfterParentheis]->text=='(') { + // operator overloading like (operator int) + if (mTokenizer[mIndex+1]->text=="operator") { + mIndex=indexAfterParentheis; + handleMethod(StatementKind::skFunction,"", + mergeArgs(mIndex+1,mTokenizer[mIndex]->matchIndex-1), + indexAfterParentheis,false,false); + } else if (currentText.endsWith("::operator")) { + mIndex=indexAfterParentheis; + handleMethod(StatementKind::skFunction,"", + mergeArgs(mIndex+1,mTokenizer[mIndex]->matchIndex-1), + indexAfterParentheis,false,false); + } else { + //function pointer var + handleVar(currentText,false,false); + } + } else { + if (currentText.startsWith("operator") + && currentText.length()>8 + && !isIdentChar(currentText[8])) { + // operator overloading + handleMethod(StatementKind::skFunction,"", + mergeArgs(mIndex+1,mTokenizer[mIndex]->matchIndex-1), + indexAfterParentheis,false,false); + return; + } + //check for constructor like Foo::Foo() + QString name; + QString temp,temp2; + QString parentName; + if (splitLastMember(currentText,name,temp)) { + //has '::' + bool isDestructor=false; + if (!splitLastMember(temp,parentName,temp2)) + parentName=temp; + if (name.startsWith('~')) + name=name.mid(1); + if (removeTemplateParams(name)==removeTemplateParams(parentName)) + handleMethod( (isDestructor?StatementKind::skDestructor:StatementKind::skConstructor), + "", + currentText, + mIndex,false,false); + return; + } + // check for constructor like: + // class Foo { + // Foo(); + // }; + PStatement scope=getCurrentScope(); + if (scope && scope->kind==StatementKind::skClass + && removeTemplateParams(scope->command) == removeTemplateParams(currentText)) { + handleMethod(StatementKind::skConstructor,"", + currentText, + mIndex,false,false); + return; + } + + // function call, skip it + skipNextSemicolon(mIndex); + } + } else if (mTokenizer[mIndex]->text.startsWith('*') + || mTokenizer[mIndex]->text.startsWith('&') + || tokenIsIdentifier(mTokenizer[mIndex]->text) + ) { + // it should be function/var + + bool isStatic = false; + bool isFriend = false; + bool isExtern = false; + + QString sType = currentText; // should contain type "int" + QString sName = ""; // should contain function name "foo::function" + + + + // Gather data for the string parts + while (mIndex+1 < mTokenizer.tokenCount()) { + if (mTokenizer[mIndex + 1]->text == '(') { + if (mTokenizer[mIndex+2]->text == '*') { + //foo(*blabla), it's a function pointer var + handleVar(sType,isExtern,isStatic); + return; + } + + int indexAfter=mTokenizer[mIndex + 1]->matchIndex+1; + if (indexAfter>=mTokenizer.tokenCount()) { + //error + mIndex=indexAfter; + return; + } + //if it's like: foo(...)(...) + if (mTokenizer[indexAfter]->text=='(') { + if (mTokenizer[mIndex]->text=="operator") { + //operator()() , it's an operator overload for () + handleMethod(StatementKind::skFunction,sType, + "operator()",indexAfter,isStatic,false); + return; + } + if (mTokenizer[mIndex]->text.endsWith("::operator")) { + // operator overloading + handleMethod(StatementKind::skFunction,"", + mTokenizer[mIndex]->text+"()",indexAfter,isStatic,false); + return; + } + //foo(...)(...), it's a function pointer var + handleVar(sType,isExtern,isStatic); + //Won't implement: ignore function decl like int (text)(int x) { }; + return; + } + //it's not a function define + if (mTokenizer[indexAfter]->text[0] == ',') { + // var decl with init + handleVar(sType,isExtern,isStatic); + return; + } + if (mTokenizer[indexAfter]->text[0] == ';') { + //function can only be defined in global/namespaces/classes + PStatement currentScope=getCurrentScope(); + if (currentScope) { + //in namespace, it might be function or object initilization + if (currentScope->kind == StatementKind::skNamespace + && isNotFuncArgs(mIndex + 1)) { + // var decl with init + handleVar(sType,isExtern,isStatic); + return; + //not in class, it can't be a valid function definition + } else if (currentScope->kind != StatementKind::skClass) { + // var decl with init + handleVar(sType,isExtern,isStatic); + return; + } + //variable can't be initialized in class definition, it must be a function + } else if (isNotFuncArgs(mIndex + 1)){ + // var decl with init + handleVar(sType,isExtern,isStatic); + return; + } + } + sName = mTokenizer[mIndex]->text; + mIndex++; + + handleMethod(StatementKind::skFunction,sType, + sName,mIndex,isStatic,isFriend); + + return; + } else if ( + mTokenizer[mIndex + 1]->text == ',' + ||mTokenizer[mIndex + 1]->text == ';' + ||mTokenizer[mIndex + 1]->text == ':' + ||mTokenizer[mIndex + 1]->text == '{') { + handleVar(sType,isExtern,isStatic); + return; + } else { + QString s = mTokenizer[mIndex]->text; + if (s == "static") + isStatic = true; + else if (s == "friend") + isFriend = true; + else if (s == "extern") + isExtern = true; + if (!s.isEmpty() && !(s=="extern")) + sType = sType + ' '+ s; + mIndex++; + } + } + } +} + int CppParser::getCurrentBlockEndSkip() { if (mBlockEndSkips.isEmpty()) @@ -2232,23 +2411,23 @@ void CppParser::handleKeyword(KeywordType skipType) // skip it; mIndex++; break; - case KeywordType::SkipAfterSemicolon: + case KeywordType::SkipNextSemicolon: // Skip to ; and over it - mIndex = indexOfNextSemicolon(mIndex)+1; + skipNextSemicolon(mIndex); break; - case KeywordType::SkipAfterColon: + case KeywordType::SkipNextColon: // Skip to : and over it mIndex = indexOfNextColon(mIndex)+1; break; - case KeywordType::SkipAfterParenthesis: + case KeywordType::SkipNextParenthesis: // skip pass () - mIndex = indexPassParenthesis(mIndex); + skipParenthesis(mIndex); break; - case KeywordType::SkipToLeftBrace: + case KeywordType::MoveToLeftBrace: // Skip to { mIndex = indexOfNextLeftBrace(mIndex); break; - case KeywordType::SkipAfterBrace: + case KeywordType::MoveToRightBrace: // Skip pass {} mIndex = indexPassBraces(mIndex); break; @@ -2288,19 +2467,21 @@ void CppParser::handleLambda() mIndex=bodyStart+1; // skip '{' } -void CppParser::handleMethod(const QString &sType, const QString &sName, int argStart, int argEnd, bool isStatic, bool isFriend) +void CppParser::handleMethod(StatementKind functionKind,const QString &sType, const QString &sName, int argStart, bool isStatic, bool isFriend) { bool isValid = true; bool isDeclaration = false; // assume it's not a prototype int startLine = mTokenizer[mIndex]->line; + int argEnd = mTokenizer[argStart]->matchIndex; if (mIndex >= mTokenizer.tokenCount()) // not finished define, just skip it; return; - PStatement functionClass = getCurrentScope(); + PStatement scopeStatement = getCurrentScope(); //find start of the function body; bool foundColon=false; + mIndex=argEnd+1; while ((mIndex < mTokenizer.tokenCount()) && !isblockChar(mTokenizer[mIndex]->text.front())) { if (mTokenizer[mIndex]->text=='(') { mIndex=mTokenizer[mIndex]->matchIndex+1; @@ -2335,17 +2516,17 @@ void CppParser::handleMethod(const QString &sType, const QString &sName, int arg QString scopelessName; PStatement functionStatement; - if (isFriend && isDeclaration && functionClass) { + if (isFriend && isDeclaration && scopeStatement) { int delimPos = sName.indexOf("::"); if (delimPos >= 0) { scopelessName = sName.mid(delimPos+2); } else scopelessName = sName; //TODO : we should check namespace - functionClass->friends.insert(scopelessName); + scopeStatement->friends.insert(scopelessName); } else if (isValid) { // Use the class the function belongs to as the parent ID if the function is declared outside of the class body - int delimPos = sName.indexOf("::"); + int delimPos = sName.lastIndexOf("::"); QString scopelessName; QString parentClassName; if (delimPos >= 0) { @@ -2354,24 +2535,15 @@ void CppParser::handleMethod(const QString &sType, const QString &sName, int arg // Check what class this function belongs to parentClassName = sName.mid(0, delimPos); - functionClass = getIncompleteClass(parentClassName,getCurrentScope()); + scopeStatement = getIncompleteClass(parentClassName,getCurrentScope()); } else scopelessName = sName; - StatementKind functionKind; - // Determine function type - if (scopelessName == sType) { - functionKind = StatementKind::skConstructor; - } else if (scopelessName == '~' + sType) { - functionKind = StatementKind::skDestructor; - } else { - functionKind = StatementKind::skFunction; - } // For function definitions, the parent class is given. Only use that as a parent if (!isDeclaration) { functionStatement=addStatement( - functionClass, + scopeStatement, mCurrentFile, sType, scopelessName, @@ -2387,13 +2559,13 @@ void CppParser::handleMethod(const QString &sType, const QString &sName, int arg isStatic); scanMethodArgs(functionStatement, argStart,argEnd); // add variable this to the class function - if (functionClass && functionClass->kind == StatementKind::skClass && + if (scopeStatement && scopeStatement->kind == StatementKind::skClass && !isStatic) { //add this to non-static class member function addStatement( functionStatement, mCurrentFile, - functionClass->command, + scopeStatement->command+"*", "this", "", "", @@ -2405,6 +2577,7 @@ void CppParser::handleMethod(const QString &sType, const QString &sName, int arg true, false); } + // add "__func__ variable" addStatement( functionStatement, @@ -2420,9 +2593,10 @@ void CppParser::handleMethod(const QString &sType, const QString &sName, int arg StatementClassScope::None, true, false); + } else { functionStatement = addStatement( - functionClass, + scopeStatement, mCurrentFile, sType, scopelessName, @@ -2440,7 +2614,6 @@ void CppParser::handleMethod(const QString &sType, const QString &sName, int arg } - if ((mIndex < mTokenizer.tokenCount()) && mTokenizer[mIndex]->text.startsWith('{')) { addSoloScopeLevel(functionStatement,startLine); mIndex++; //skip '{' @@ -2452,7 +2625,6 @@ void CppParser::handleMethod(const QString &sType, const QString &sName, int arg removeScopeLevel(startLine+1); mIndex++; } - } void CppParser::handleNamespace(KeywordType skipType) @@ -2467,9 +2639,9 @@ void CppParser::handleNamespace(KeywordType skipType) mIndex++; //skip 'namespace' - if (!isLetterChar(mTokenizer[mIndex]->text.front())) - //wrong namespace define, stop handling - return; +// if (!tokenIsIdentifier(mTokenizer[mIndex]->text)) +// //wrong namespace define, stop handling +// return; QString command = mTokenizer[mIndex]->text; QString fullName = getFullStatementName(command,getCurrentScope()); @@ -2747,8 +2919,6 @@ void CppParser::handleScope(KeywordType keywordType) bool CppParser::handleStatement() { QString funcType,funcName; - int argStart,argEnd; - bool isStatic, isFriend; int idx=getCurrentBlockEndSkip(); int idx2=getCurrentBlockBeginSkip(); int idx3=getCurrentInlineNamespaceEndSkip(); @@ -2796,10 +2966,30 @@ bool CppParser::handleStatement() mIndex++; } else if (checkForPreprocessor()) { handlePreprocessor(); - } else if (checkForLambda()) { // is lambda - handleLambda(); - } else if (!isLetterChar(mTokenizer[mIndex]->text[0])) { +// } else if (checkForLambda()) { // is lambda +// handleLambda(); + } else if (mTokenizer[mIndex]->text=='(') { + if (mTokenizer[mIndex]->text=="operator") { + // things like (operator int) + mIndex++; //just skip '(' + } else + skipParenthesis(mIndex); + } else if (mTokenizer[mIndex]->text==')') { mIndex++; + } else if (mTokenizer[mIndex]->text.startsWith('~')) { + //it should be a destructor + if (mIndex+1text=='(') { + //dont further check to speed up + handleMethod(StatementKind::skDestructor, "", '~'+mTokenizer[mIndex]->text, mIndex+1, false, false); + } else { + skipNextSemicolon(mIndex); + } + } else if (!isIdentChar(mTokenizer[mIndex]->text[0])) { + skipNextSemicolon(mIndex); + } else if (mTokenizer[mIndex]->text.endsWith('.') + || mTokenizer[mIndex]->text.endsWith("->")) { + skipNextSemicolon(mIndex); } else if (checkForKeyword(keywordType)) { // includes template now handleKeyword(keywordType); } else if (keywordType==KeywordType::For) { // (for/catch) @@ -2828,13 +3018,19 @@ bool CppParser::handleStatement() handleUsing(); } else if (checkForStructs(keywordType)) { handleStructs(false); - } else if (checkForMethod(funcType, funcName, argStart,argEnd, isStatic, isFriend)) { - handleMethod(funcType, funcName, argStart, argEnd, isStatic, isFriend); // don't recalculate parts - } else if (tryHandleVar()) { - //do nothing - } else - mIndex++; + } else { + // it should be method/constructor/var + checkAndHandleMethodOrVar(); + } + Q_ASSERT(mIndex<999999); +// else if (checkForMethod(funcType, funcName, argStart,argEnd, isStatic, isFriend)) { +// handleMethod(funcType, funcName, argStart, argEnd, isStatic, isFriend); // don't recalculate parts +// } else if (tryHandleVar()) { +// //do nothing +// } else +// mIndex++; + //todo: remove mSkipList (we can check '}''s statement type instead) checkForSkipStatement(); return mIndex < mTokenizer.tokenCount(); @@ -2947,6 +3143,7 @@ void CppParser::handleStructs(bool isTypedef) command = ""; } mIndex++; + break; } else if ((mIndex + 2 < mTokenizer.tokenCount()) && (mTokenizer[mIndex + 1]->text == "final") && (mTokenizer[mIndex + 2]->text.front()==',' @@ -2971,6 +3168,7 @@ void CppParser::handleStructs(bool isTypedef) command=""; } mIndex+=2; + break; } else mIndex++; } @@ -2980,8 +3178,7 @@ void CppParser::handleStructs(bool isTypedef) if ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.front() == ':')) { if (firstSynonym) setInheritance(mIndex, firstSynonym, isStruct); // set the _InheritanceList value - while ((mIndex < mTokenizer.tokenCount()) && (mTokenizer[mIndex]->text.front() != '{')) - mIndex++; // skip decl after ':' + mIndex=indexOfNextLeftBrace(mIndex); } // Check for struct synonyms after close brace @@ -3087,7 +3284,7 @@ void CppParser::handleStructs(bool isTypedef) } } if (!firstSynonym) { - //anonymous union/struct/class, add ast a block + //anonymous union/struct/class, add as a block firstSynonym=addStatement( getCurrentScope(), mCurrentFile, @@ -3201,77 +3398,65 @@ void CppParser::handleUsing() } } -bool CppParser::tryHandleVar() +void CppParser::handleVar(const QString& typePrefix,bool isExtern,bool isStatic) { - int indexBackup=mIndex; - KeywordType keywordType; - QString varType=mTokenizer[mIndex]->text; - bool isExtern = false; - bool isStatic = false; QString lastType; - if (isInvalidVarPrefixChar(mTokenizer[mIndex]->text.front()) - || mTokenizer[mIndex]->text.endsWith('.') - || mTokenizer[mIndex]->text.endsWith("->")) - //failed to handle - return false; - if (varType=="extern") { + if (typePrefix=="extern") { isExtern=true; - } else if (varType=="static") { + } else if (typePrefix=="static") { isStatic=true; } else { - lastType=varType; + lastType=typePrefix; } - mIndex++; + //we only check the first token to reduce calculations - if (mIndex>=mTokenizer.tokenCount() - || checkForKeyword(keywordType) - || isInvalidVarPrefixChar(mTokenizer[mIndex]->text.front()) - || mTokenizer[mIndex]->text.endsWith('.') - || mTokenizer[mIndex]->text.endsWith("->")) { - //failed to handle - mIndex=indexBackup; - return false; - } +// if (mIndex>=mTokenizer.tokenCount() +// || mTokenizer[mIndex]->text.endsWith('.') +// || mTokenizer[mIndex]->text.endsWith("->")) { +// //failed to handle +// skipNextSemicolon(mIndex); +// return ; +// } - while (mIndex+1text=='(') { - if ( mTokenizer[mIndex]->matchIndex<=mTokenizer.tokenCount() - && mTokenizer[mTokenizer[mIndex]->matchIndex]->text=='(') { - //function pointer - break; - } - //error break; - mIndex=indexBackup; - return false; - } else if (mTokenizer[mIndex + 1]->text=='(' - || mTokenizer[mIndex + 1]->text==',' - || mTokenizer[mIndex + 1]->text==';' - || mTokenizer[mIndex + 1]->text.front()==':' - || mTokenizer[mIndex + 1]->text=='}' - || mTokenizer[mIndex + 1]->text.front()=='#' - || mTokenizer[mIndex + 1]->text=='{') { - //end of type info - break; - } else if (mTokenizer[mIndex]->text!="struct" - && mTokenizer[mIndex]->text!="class" - && mTokenizer[mIndex]->text!="union") { - QString s=mTokenizer[mIndex]->text; - if (s == "extern") { - isExtern = true; - } else if (s == "static") { - isStatic = true; - } else - lastType += ' '+s; - } - mIndex++; - } +// while (mIndex+1text=='(') { +// if ( mTokenizer[mIndex]->matchIndex<=mTokenizer.tokenCount() +// && mTokenizer[mTokenizer[mIndex]->matchIndex]->text=='(') { +// //function pointer +// break; +// } +// //error break; +// mIndex=indexBackup; +// return false; +// } else if (mTokenizer[mIndex + 1]->text=='(' +// || mTokenizer[mIndex + 1]->text==',' +// || mTokenizer[mIndex + 1]->text==';' +// || mTokenizer[mIndex + 1]->text.front()==':' +// || mTokenizer[mIndex + 1]->text=='}' +// || mTokenizer[mIndex + 1]->text.front()=='#' +// || mTokenizer[mIndex + 1]->text=='{') { +// //end of type info +// break; +// } else if (mTokenizer[mIndex]->text!="struct" +// && mTokenizer[mIndex]->text!="class" +// && mTokenizer[mIndex]->text!="union") { +// QString s=mTokenizer[mIndex]->text; +// if (s == "extern") { +// isExtern = true; +// } else if (s == "static") { +// isStatic = true; +// } else +// lastType += ' '+s; +// } +// mIndex++; +// } - if (mIndex+1 >= mTokenizer.tokenCount() || lastType.isEmpty() - || lastType.endsWith(':')) { - mIndex=indexBackup; - return false; - } +// if (mIndex+1 >= mTokenizer.tokenCount() || lastType.isEmpty() +// || lastType.endsWith(':')) { +// mIndex=indexBackup; +// return false; +// } bool varAdded = false; QString tempType; @@ -3365,8 +3550,6 @@ bool CppParser::tryHandleVar() } // Skip ; mIndex++; - - return true; } void CppParser::internalParse(const QString &fileName) @@ -4505,6 +4688,8 @@ void CppParser::scanMethodArgs(const PStatement& functionStatement, int argStart } } } else { + if (!varType.isEmpty()) + varType+=' '; varType+=cmd; } i++; @@ -4585,118 +4770,49 @@ QString CppParser::splitPhrase(const QString &phrase, QString &sClazz, return result; } +QString CppParser::removeTemplateParams(const QString &phrase) +{ + int pos = phrase.indexOf('<'); + if (pos>=0) { + return phrase.left(pos); + } + return phrase; +} + +bool CppParser::splitLastMember(const QString &token, QString &lastMember, QString &remaining) +{ + int pos = token.lastIndexOf("::"); + if (pos<0) + return false; + lastMember=token.mid(pos+2); + remaining=token.left(pos); + return true; +} + static bool isIdentChar(const QChar& ch) { return ch.isLetter() || ch == '_' || ch.isDigit(); } -static void appendArgWord(QString& args, const QString& word) { - QString s=word.trimmed(); - if (s.isEmpty()) - return; - if (args.isEmpty()) - args.append(s); - else if (isIdentChar(args.back()) && isIdentChar(word.front()) ) { - args+=" "; - args+=s; - } else { - args+=s; - } -} -QString CppParser::removeArgNames(const QString &args) -{ - QString result = ""; - int argsLen = args.length(); - if (argsLen < 2) - return ""; - int i=1; // skip start '(' - QString currentArg; - QString word; - int brackLevel = 0; - bool typeGetted = false; - while (i0) { - word+=args[i]; - } else { - if (!typeGetted) { - appendArgWord(currentArg,word); - } else { - if (isCppKeyword(word)) { - appendArgWord(currentArg,word); - } - } - word = ""; - result += currentArg.trimmed() + ','; - currentArg = ""; - typeGetted = false; - } - break; - case '<': - case '[': - case '(': - brackLevel++; - word+=args[i]; - break; - case '>': - case ']': - case ')': - brackLevel--; - word+=args[i]; - break; - case ' ': - case '\t': - if ((brackLevel >0) && !isSpaceChar(args[i-1])) { - word+=args[i]; - } else if (!word.isEmpty()) { - if (!typeGetted) { - appendArgWord(currentArg,word); - if (mCppTypeKeywords.contains(word) || !isCppKeyword(word)) - typeGetted = true; - } else { - if (isCppKeyword(word)) - appendArgWord(currentArg,word); - } - word = ""; - } - break; - case '&': - case '*': - if (!word.isEmpty()) { - if (!typeGetted) { - appendArgWord(currentArg,word); - if (mCppTypeKeywords.contains(word) || !isCppKeyword(word)) - typeGetted = true; - } else { - if (isCppKeyword(word)) - appendArgWord(currentArg,word); - } - word = ""; - } - currentArg+=args[i]; - break; - default: - if (isIdentChar(args[i])) { - word+=args[i]; - } - } - i++; - } - if (!typeGetted) { - appendArgWord(currentArg,word); - } else { - if (isCppKeyword(word)) { - appendArgWord(currentArg,word); - } - } - result += currentArg.trimmed(); - return result; -} +//static void appendArgWord(QString& args, const QString& word) { +// QString s=word.trimmed(); +// if (s.isEmpty()) +// return; +// if (args.isEmpty()) +// args.append(s); +// else if (isIdentChar(args.back()) && isIdentChar(word.front()) ) { +// args+=" "; +// args+=s; +// } else { +// args+=s; +// } +//} -bool CppParser::isNotFuncArgs(int startIndex, int endIndex) +bool CppParser::isNotFuncArgs(int startIndex) { + Q_ASSERT(mTokenizer[startIndex]->text=='('); + int endIndex=mTokenizer[startIndex]->matchIndex; //no args, it must be a function if (endIndex-startIndex==1) return false; @@ -4709,25 +4825,37 @@ bool CppParser::isNotFuncArgs(int startIndex, int endIndex) // args contains a string/char, can't be a func define case '"': case '\'': - return true; - case '(': - case '[': + case '+': + case '-': + case '/': + case '|': + case '!': case '{': + return true; + case '[': // function args like int f[10] i=mTokenizer[i]->matchIndex+1; + if (itext=='(' + || mTokenizer[i]->text=='{')) //lambda + return true; continue; } if (isDigitChar(ch)) return true; - if (isLetterChar(ch)) { + if (isIdentChar(ch)) { QString currentText=mTokenizer[i]->text; - while (currentText.startsWith('*') - || currentText.startsWith('&')) - currentText.remove(0,1); + if (mTokenizer[i]->text.endsWith('.')) + return true; + if (mTokenizer[i]->text.endsWith("->")) + return true; if (!mCppTypeKeywords.contains(currentText)) { if (currentText=="true" || currentText=="false" || currentText=="nullptr" || currentText=='this') return true; - if (currentText=="const" ) + if (currentText=="const") + return false; + + if (isCppKeyword(currentText)) return false; PStatement statement =findStatementOf(mCurrentFile,word,getCurrentScope(),true); @@ -4867,6 +4995,38 @@ int CppParser::indexPassBraces(int index) return index; } +void CppParser::skipNextSemicolon(int index) +{ + mIndex=index; + while (mIndextext[0].unicode()) { + case ';': + mIndex++; + return; + case '{': + mIndex = mTokenizer[mIndex]->matchIndex+1; + break; + case '(': + mIndex = mTokenizer[mIndex]->matchIndex+1; + break; + default: + mIndex++; + } + } +} + +void CppParser::skipParenthesis(int index) +{ + mIndex=index; + while (mIndextext=='(') { + mIndex=mTokenizer[mIndex]->matchIndex+1; + return; + } + mIndex++; + } +} + QString CppParser::mergeArgs(int startIndex, int endIndex) { QString result; diff --git a/RedPandaIDE/parser/cppparser.h b/RedPandaIDE/parser/cppparser.h index bbbc0600..d2892529 100644 --- a/RedPandaIDE/parser/cppparser.h +++ b/RedPandaIDE/parser/cppparser.h @@ -214,7 +214,7 @@ private: int &argEndIndex, bool &isStatic, bool &isFriend); // caching of results bool checkForNamespace(KeywordType keywordType); bool checkForPreprocessor(); - bool checkForLambda(); +// bool checkForLambda(); bool checkForScope(KeywordType keywordType); void checkForSkipStatement(); bool checkForStructs(KeywordType keywordType); @@ -222,6 +222,8 @@ private: bool checkForTypedefStruct(); bool checkForUsing(KeywordType keywordType); + void checkAndHandleMethodOrVar(); + void fillListOfFunctions(const QString& fileName, int line, const PStatement& statement, const PStatement& scopeStatement, QStringList& list); @@ -321,7 +323,7 @@ private: void doSkipInExpression(const QStringList& expression, int&pos, const QString& startSymbol, const QString& endSymbol); bool isIdentifier(const QString& token) const { - return (!token.isEmpty() && isLetterChar(token.front()) + return (!token.isEmpty() && isIdentChar(token.front()) && !token.contains('\"')); } @@ -333,7 +335,7 @@ private: case '\'': return false; default: - return isLetterChar(term[0]); + return isIdentChar(term[0]); } } @@ -361,6 +363,16 @@ private: return false; return (token.startsWith('\'')); } + + bool isKeyword(const QString& token) const { + return mCppKeywords.contains(token); + } + + bool tokenIsIdentifier(const QString& token) const { + //token won't be empty + return isIdentChar(token[0]); + } + PStatement doParseEvalTypeInfo( const QString& fileName, const PStatement& scope, @@ -402,10 +414,10 @@ private: void handleKeyword(KeywordType skipType); void handleLambda(); void handleMethod( + StatementKind functionKind, const QString& sType, const QString& sName, int argStart, - int argEnd, bool isStatic, bool isFriend); void handleNamespace(KeywordType skipType); @@ -415,7 +427,7 @@ private: bool handleStatement(); void handleStructs(bool isTypedef = false); void handleUsing(); - bool tryHandleVar(); + void handleVar(const QString& typePrefix,bool isExtern,bool isStatic); void internalParse(const QString& fileName); // function FindMacroDefine(const Command: AnsiString): PStatement; void inheritClassStatement( @@ -443,8 +455,9 @@ private: int argEnd); QString splitPhrase(const QString& phrase, QString& sClazz, QString& sOperator, QString &sMember); + QString removeTemplateParams(const QString& phrase); - QString removeArgNames(const QString& args); + bool splitLastMember(const QString& token, QString& lastMember, QString& remaining); bool isSpaceChar(const QChar& ch) const { return ch==' ' || ch =='\t'; @@ -457,7 +470,14 @@ private: || ch == '&'; } - bool isLetterChar(const QChar& ch) const { + bool isIdentifier(const QChar& ch) const { + return ch.isLetter() + || ch == '_' + || ch == '~' + ; + } + + bool isIdentChar(const QChar& ch) const { return ch.isLetter() || ch == '_'; } @@ -499,10 +519,6 @@ private: } } - bool isLeftParenthesis(const QString& text) const { - return text=="("; - } - /*';', '{', '}'*/ bool isblockChar(const QChar& ch) const { switch(ch.unicode()){ @@ -545,7 +561,7 @@ private: return ch=='\n' || ch=='\r'; } - bool isNotFuncArgs(int startIndex, int endIndex); + bool isNotFuncArgs(int startIndex); /** * @brief Test if a statement is a class/struct/union/namespace/function @@ -569,6 +585,8 @@ private: int indexOfNextLeftBrace(int index); int indexPassParenthesis(int index); int indexPassBraces(int index); + void skipNextSemicolon(int index); + void skipParenthesis(int index); QString mergeArgs(int startIndex, int endIndex); void parseCommandTypeAndArgs(QString& command, QString& typeSuffix, diff --git a/RedPandaIDE/parser/cpptokenizer.cpp b/RedPandaIDE/parser/cpptokenizer.cpp index f3e3c417..6f975c5c 100644 --- a/RedPandaIDE/parser/cpptokenizer.cpp +++ b/RedPandaIDE/parser/cpptokenizer.cpp @@ -112,6 +112,7 @@ void CppTokenizer::addToken(const QString &sText, int iLine, TokenType tokenType PToken token = std::make_shared(); token->text = sText; token->line = iLine; + token->matchIndex = 1000000000; switch(tokenType) { case TokenType::LeftBrace: token->matchIndex=-1; @@ -167,15 +168,6 @@ void CppTokenizer::countLines() } } -QString CppTokenizer::getLambdaCaptures() -{ - QChar* offset = mCurrent; - skipPair('[', ']'); - QString result(offset,mCurrent-offset); - skipToNextToken(); - return result; -} - QString CppTokenizer::getForInit() { QChar* startOffset = mCurrent; @@ -290,33 +282,18 @@ QString CppTokenizer::getNextToken(TokenType *pTokenType, bool bSkipArray, bool done = true; break; case '[': - { -// QChar* backup=mCurrent; -// skipPair('[',']'); -// skipToNextToken(); -// qDebug()<<*mCurrent; -// if (*mCurrent!='(') { -// mCurrent=backup; -// advance(); -// } else { -// mCurrent=backup; -// skipPair('[',']'); -// *pTokenType=TokenType::LambdaCaptures; -// countLines(); -// result = QString(backup,mCurrent-backup); -// done = true; -// qDebug()<<"yes"<': + level--; + if (level==0) { + mCurrent++; //skip over > + return true; + } + break; + case '{': + case '}': + case ';': + case '"': + case '\'': + mCurrent=backup; + return false; + case '-': + if (*(mCurrent+1)=='>') { + mCurrent=backup; + return false; + } + break; + case '.': + if (*(mCurrent+1)!='.') { + mCurrent=backup; + return false; + } + // skip + while (*(mCurrent+1)=='.') + mCurrent++; + break; + } + mCurrent++; + } + mCurrent=backup; + return false; +} + void CppTokenizer::skipRawString() { mCurrent++; //skip R @@ -678,17 +711,8 @@ void CppTokenizer::skipTemplateArgs() { if (*mCurrent != '<') return; - QChar* start = mCurrent; - QSet failSet; - failSet.insert('{'); - failSet.insert('}'); - failSet.insert(';'); - skipPair('<', '>', failSet); - - // if we failed, return to where we came from - if (start!=mCurrent && *(mCurrent - 1) != '>') - mCurrent = start; + skipPair('<', '>'); } void CppTokenizer::skipToEOL() diff --git a/RedPandaIDE/parser/cpptokenizer.h b/RedPandaIDE/parser/cpptokenizer.h index d400e0cb..c40c4ae8 100644 --- a/RedPandaIDE/parser/cpptokenizer.h +++ b/RedPandaIDE/parser/cpptokenizer.h @@ -56,7 +56,6 @@ private: void advance(); void countLines(); PToken getToken(int index); - QString getLambdaCaptures(); QString getForInit(); QString getNextToken( @@ -79,6 +78,7 @@ private: void skipAssignment(); void skipDoubleQuotes(); void skipPair(const QChar& cStart, const QChar cEnd, const QSet& failChars = QSet()); + bool skipAngleBracketPair(); void skipRawString(); void skipSingleQuote(); void skipSplitLine(); diff --git a/RedPandaIDE/parser/parserutils.cpp b/RedPandaIDE/parser/parserutils.cpp index f2028b80..21c171a4 100644 --- a/RedPandaIDE/parser/parserutils.cpp +++ b/RedPandaIDE/parser/parserutils.cpp @@ -79,7 +79,6 @@ void initParser() CppKeywords.insert("or_eq",KeywordType::SkipItself); CppKeywords.insert("register",KeywordType::SkipItself); CppKeywords.insert("reinterpret_cast",KeywordType::SkipItself); - CppKeywords.insert("static_assert",KeywordType::SkipItself); CppKeywords.insert("static_cast",KeywordType::SkipItself); CppKeywords.insert("template",KeywordType::SkipItself); //CppKeywords.insert("this",SkipType::skItself); @@ -97,32 +96,34 @@ void initParser() CppKeywords.insert("try",KeywordType::SkipItself); // Skip to ; - CppKeywords.insert("delete",KeywordType::SkipAfterSemicolon); - CppKeywords.insert("delete[]",KeywordType::SkipAfterSemicolon); - CppKeywords.insert("goto",KeywordType::SkipAfterSemicolon); - CppKeywords.insert("new",KeywordType::SkipAfterSemicolon); - CppKeywords.insert("return",KeywordType::SkipAfterSemicolon); - CppKeywords.insert("throw",KeywordType::SkipAfterSemicolon); + CppKeywords.insert("delete",KeywordType::SkipNextSemicolon); + CppKeywords.insert("delete[]",KeywordType::SkipNextSemicolon); + CppKeywords.insert("goto",KeywordType::SkipNextSemicolon); + CppKeywords.insert("new",KeywordType::SkipNextSemicolon); + CppKeywords.insert("return",KeywordType::SkipNextSemicolon); + CppKeywords.insert("throw",KeywordType::SkipNextSemicolon); // CppKeywords.insert("using",SkipType::skToSemicolon); //won't use it // Skip to : - CppKeywords.insert("case",KeywordType::SkipAfterColon); - CppKeywords.insert("default",KeywordType::SkipAfterColon); + CppKeywords.insert("case",KeywordType::SkipNextColon); + CppKeywords.insert("default",KeywordType::SkipNextColon); // Skip to ) - CppKeywords.insert("__attribute__",KeywordType::SkipAfterParenthesis); - CppKeywords.insert("alignas",KeywordType::SkipAfterParenthesis); // not right - CppKeywords.insert("alignof",KeywordType::SkipAfterParenthesis); // not right - CppKeywords.insert("decltype",KeywordType::SkipAfterParenthesis); // not right - CppKeywords.insert("if",KeywordType::SkipAfterParenthesis); - CppKeywords.insert("sizeof",KeywordType::SkipAfterParenthesis); - CppKeywords.insert("switch",KeywordType::SkipAfterParenthesis); - CppKeywords.insert("typeid",KeywordType::SkipAfterParenthesis); - CppKeywords.insert("while",KeywordType::SkipAfterParenthesis); + CppKeywords.insert("__attribute__",KeywordType::SkipNextParenthesis); + CppKeywords.insert("__attribute",KeywordType::SkipNextParenthesis); + CppKeywords.insert("alignas",KeywordType::SkipNextParenthesis); // not right + CppKeywords.insert("alignof",KeywordType::SkipNextParenthesis); // not right + CppKeywords.insert("decltype",KeywordType::SkipNextParenthesis); // not right + CppKeywords.insert("if",KeywordType::SkipNextParenthesis); + CppKeywords.insert("sizeof",KeywordType::SkipNextParenthesis); + CppKeywords.insert("switch",KeywordType::SkipNextParenthesis); + CppKeywords.insert("typeid",KeywordType::SkipNextParenthesis); + CppKeywords.insert("while",KeywordType::SkipNextParenthesis); + CppKeywords.insert("static_assert",KeywordType::SkipNextParenthesis); // Skip to } - CppKeywords.insert("asm",KeywordType::SkipAfterBrace); - //CppKeywords.insert("namespace",SkipType::skToLeftBrace); // won't process it + CppKeywords.insert("asm",KeywordType::MoveToRightBrace); + CppKeywords.insert("__asm",KeywordType::MoveToRightBrace); // Skip to { // wont handle diff --git a/RedPandaIDE/parser/parserutils.h b/RedPandaIDE/parser/parserutils.h index 90d7a607..2004a111 100644 --- a/RedPandaIDE/parser/parserutils.h +++ b/RedPandaIDE/parser/parserutils.h @@ -58,11 +58,11 @@ using PDefineMap = std::shared_ptr; enum class KeywordType { SkipItself, // skip itself - SkipAfterSemicolon, // skip to ; - SkipAfterColon, // skip to : - SkipAfterParenthesis, // skip to ) - SkipToLeftBrace,// Skip to { - SkipAfterBrace, // skip to } + SkipNextSemicolon, // move to ; and skip it + SkipNextColon, // move to : and skip it + SkipNextParenthesis, // move to ) and skip it + MoveToLeftBrace,// move to { + MoveToRightBrace, // move to } For, //for Catch, //catch Public, // public @@ -74,7 +74,8 @@ enum class KeywordType { Namespace, //namespace Typedef, //typedef Using, //using - None // It's a keyword but don't process here + None, // It's a keyword but don't process here + NotKeyword }; //It will be used as hash key. DONT make it enum class!!!!!