- fix: Can't parse enum values.

- fix: Can't correctly show enum values in the class browser.
  - fix: Can't correctly create project, if template's encoding setting is not valid.
  - enhancement: Add "embed assembly" template.
This commit is contained in:
Roy Qu 2023-02-15 16:24:24 +08:00
parent 1b8ff37a60
commit 25d0f5b782
23 changed files with 501 additions and 375 deletions

View File

@ -20,6 +20,10 @@ Red Panda C++ Version 2.12
- fix: If current editor is empty, parser will parse the file's content on the disk instead from the editor. - fix: If current editor is empty, parser will parse the file's content on the disk instead from the editor.
- fix: Can't show completion when cursor is after "char[" - fix: Can't show completion when cursor is after "char["
- change: Don't confirm rebuild/recompile when run/debug. - change: Don't confirm rebuild/recompile when run/debug.
- fix: Can't parse enum values.
- fix: Can't correctly show enum values in the class browser.
- fix: Can't correctly create project, if template's encoding setting is not valid.
- enhancement: Add "embed assembly" template.
Red Panda C++ Version 2.11 Red Panda C++ Version 2.11

View File

@ -9456,3 +9456,9 @@ void MainWindow::on_actionNew_GAS_File_triggered()
newEditor("s"); newEditor("s");
} }
void MainWindow::on_actionGNU_Assembler_Manual_triggered()
{
QDesktopServices::openUrl(QUrl("https://sourceware.org/binutils/docs/as/index.html"));
}

View File

@ -777,6 +777,8 @@ private slots:
void on_actionNew_GAS_File_triggered(); void on_actionNew_GAS_File_triggered();
void on_actionGNU_Assembler_Manual_triggered();
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
bool mFullInitialized; bool mFullInitialized;

View File

@ -270,6 +270,7 @@
<addaction name="actionDocument"/> <addaction name="actionDocument"/>
<addaction name="actionC_Reference"/> <addaction name="actionC_Reference"/>
<addaction name="actionC_C_Reference"/> <addaction name="actionC_C_Reference"/>
<addaction name="actionGNU_Assembler_Manual"/>
<addaction name="actionRaylib_Manual"/> <addaction name="actionRaylib_Manual"/>
<addaction name="actionEGE_Manual"/> <addaction name="actionEGE_Manual"/>
<addaction name="separator"/> <addaction name="separator"/>
@ -3332,6 +3333,11 @@
<string>New GAS File</string> <string>New GAS File</string>
</property> </property>
</action> </action>
<action name="actionGNU_Assembler_Manual">
<property name="text">
<string>GNU Assembler Manual</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -1535,16 +1535,9 @@ QStringList CppParser::sortFilesByIncludeRelations(const QSet<QString> &files)
continue; continue;
//already removed in interalInvalidateFiles //already removed in interalInvalidateFiles
//mPreprocessor.removeScannedFile(file); //mPreprocessor.removeScannedFile(file);
QStringList buffer;
if (mOnGetFileStream) {
mOnGetFileStream(file,buffer);
//prevent preprocessor to read file
if (buffer.isEmpty())
buffer.append(QString());
}
//we only use local include relations //we only use local include relations
mPreprocessor.setScanOptions(false, true); mPreprocessor.setScanOptions(false, true);
mPreprocessor.preprocess(file,buffer); mPreprocessor.preprocess(file);
mPreprocessor.clearTempResults(); mPreprocessor.clearTempResults();
} }
@ -2267,8 +2260,11 @@ void CppParser::handleEnum(bool isTypedef)
QString args; QString args;
if (mTokenizer[mIndex]->text!='}') { if (mTokenizer[mIndex]->text!='}') {
while ((mIndex < mTokenizer.tokenCount()) && while ((mIndex < mTokenizer.tokenCount()) &&
!isblockChar(mTokenizer[mIndex]->text[0])) { mTokenizer[mIndex]->text!='}') {
if (!mTokenizer[mIndex]->text.startsWith(',')) { if (mTokenizer[mIndex]->text=="=") {
mIndex=indexOfNextPeriodOrSemicolon(mIndex);
continue;
} else if (tokenIsIdentifier(mTokenizer[mIndex]->text)) {
cmd = mTokenizer[mIndex]->text; cmd = mTokenizer[mIndex]->text;
args = ""; args = "";
if (isEnumClass) { if (isEnumClass) {
@ -3793,60 +3789,46 @@ void CppParser::internalParse(const QString &fileName)
// if (!isCfile(fileName) && !isHfile(fileName)) // support only known C/C++ files // if (!isCfile(fileName) && !isHfile(fileName)) // support only known C/C++ files
// return; // return;
QStringList buffer;
if (mOnGetFileStream) {
mOnGetFileStream(fileName,buffer);
//prevent preprocessor to read file
if (buffer.isEmpty())
buffer.append(QString());
}
// Preprocess the file... // Preprocess the file...
{ auto action = finally([this]{
auto action = finally([this]{ mTokenizer.clear();
mTokenizer.clear(); });
}); // Let the preprocessor augment the include records
// Let the preprocessor augment the include records mPreprocessor.setScanOptions(mParseGlobalHeaders, mParseLocalHeaders);
// mPreprocessor.setIncludesList(mIncludesList); mPreprocessor.preprocess(fileName);
// mPreprocessor.setScannedFileList(mScannedFiles);
// mPreprocessor.setIncludePaths(mIncludePaths);
// mPreprocessor.setProjectIncludePaths(mProjectIncludePaths);
mPreprocessor.setScanOptions(mParseGlobalHeaders, mParseLocalHeaders);
mPreprocessor.preprocess(fileName, buffer);
QStringList preprocessResult = mPreprocessor.result(); QStringList preprocessResult = mPreprocessor.result();
#ifdef QT_DEBUG #ifdef QT_DEBUG
// stringsToFile(mPreprocessor.result(),QString("r:\\preprocess-%1.txt").arg(extractFileName(fileName))); // stringsToFile(mPreprocessor.result(),QString("r:\\preprocess-%1.txt").arg(extractFileName(fileName)));
// mPreprocessor.dumpDefinesTo("r:\\defines.txt"); // mPreprocessor.dumpDefinesTo("r:\\defines.txt");
// mPreprocessor.dumpIncludesListTo("r:\\includes.txt"); // mPreprocessor.dumpIncludesListTo("r:\\includes.txt");
#endif #endif
//reduce memory usage //reduce memory usage
mPreprocessor.clearTempResults(); mPreprocessor.clearTempResults();
// Tokenize the preprocessed buffer file // Tokenize the preprocessed buffer file
mTokenizer.tokenize(preprocessResult); mTokenizer.tokenize(preprocessResult);
//reduce memory usage //reduce memory usage
preprocessResult.clear(); preprocessResult.clear();
if (mTokenizer.tokenCount() == 0) if (mTokenizer.tokenCount() == 0)
return; return;
#ifdef QT_DEBUG #ifdef QT_DEBUG
// mTokenizer.dumpTokens(QString("r:\\tokens-%1.txt").arg(extractFileName(fileName))); // mTokenizer.dumpTokens(QString("r:\\tokens-%1.txt").arg(extractFileName(fileName)));
#endif #endif
#ifdef QT_DEBUG #ifdef QT_DEBUG
lastIndex = -1; lastIndex = -1;
#endif #endif
// Process the token list // Process the token list
while(true) { while(true) {
if (!handleStatement()) if (!handleStatement())
break; break;
} }
#ifdef QT_DEBUG #ifdef QT_DEBUG
// mStatementList.dumpAll(QString("r:\\all-stats-%1.txt").arg(extractFileName(fileName))); // mStatementList.dumpAll(QString("r:\\all-stats-%1.txt").arg(extractFileName(fileName)));
// mStatementList.dump(QString("r:\\stats-%1.txt").arg(extractFileName(fileName))); // mStatementList.dump(QString("r:\\stats-%1.txt").arg(extractFileName(fileName)));
#endif #endif
//reduce memory usage //reduce memory usage
internalClear(); internalClear();
}
} }
void CppParser::inheritClassStatement(const PStatement& derived, bool isStruct, void CppParser::inheritClassStatement(const PStatement& derived, bool isStruct,
@ -5732,7 +5714,6 @@ int CppParser::parserId() const
void CppParser::setOnGetFileStream(const GetFileStreamCallBack &newOnGetFileStream) void CppParser::setOnGetFileStream(const GetFileStreamCallBack &newOnGetFileStream)
{ {
mOnGetFileStream = newOnGetFileStream;
mPreprocessor.setOnGetFileStream(newOnGetFileStream); mPreprocessor.setOnGetFileStream(newOnGetFileStream);
} }

View File

@ -671,7 +671,6 @@ private:
#else #else
QMutex mMutex; QMutex mMutex;
#endif #endif
GetFileStreamCallBack mOnGetFileStream;
QMap<QString,KeywordType> mCppKeywords; QMap<QString,KeywordType> mCppKeywords;
QSet<QString> mCppTypeKeywords; QSet<QString> mCppTypeKeywords;
}; };

View File

@ -155,12 +155,12 @@ void CppPreprocessor::setScanOptions(bool parseSystem, bool parseLocal)
mParseLocal=parseLocal; mParseLocal=parseLocal;
} }
void CppPreprocessor::preprocess(const QString &fileName, QStringList buffer) void CppPreprocessor::preprocess(const QString &fileName)
{ {
clearTempResults(); clearTempResults();
mFileName = fileName; mFileName = fileName;
mDefines = mHardDefines; mDefines = mHardDefines;
openInclude(fileName, buffer); openInclude(fileName);
// StringsToFile(mBuffer,"f:\\buffer.txt"); // StringsToFile(mBuffer,"f:\\buffer.txt");
preprocessBuffer(); preprocessBuffer();
// StringsToFile(mBuffer,"f:\\buffer.txt"); // StringsToFile(mBuffer,"f:\\buffer.txt");
@ -456,11 +456,7 @@ void CppPreprocessor::handleInclude(const QString &line, bool fromNext)
return; return;
PFileIncludes oldCurrentIncludes = mCurrentIncludes; PFileIncludes oldCurrentIncludes = mCurrentIncludes;
QStringList buffer; openInclude(fileName);
if (mOnGetFileStream) {
mOnGetFileStream(fileName,buffer);
}
openInclude(fileName, buffer);
} }
void CppPreprocessor::handlePreprocessor(const QString &value) void CppPreprocessor::handlePreprocessor(const QString &value)
@ -659,7 +655,7 @@ void CppPreprocessor::removeGCCAttribute(const QString &line, QString &newLine,
} }
} }
void CppPreprocessor::openInclude(const QString &fileName, QStringList bufferedText) void CppPreprocessor::openInclude(const QString &fileName)
{ {
if (mIncludes.size()>0) { if (mIncludes.size()>0) {
PParsedFile topFile = mIncludes.front(); PParsedFile topFile = mIncludes.front();
@ -719,7 +715,8 @@ void CppPreprocessor::openInclude(const QString &fileName, QStringList bufferedT
// Only load up the file if we are allowed to parse it // Only load up the file if we are allowed to parse it
bool isSystemFile = isSystemHeaderFile(fileName, mIncludePaths) || isSystemHeaderFile(fileName, mProjectIncludePaths); bool isSystemFile = isSystemHeaderFile(fileName, mIncludePaths) || isSystemHeaderFile(fileName, mProjectIncludePaths);
if ((mParseSystem && isSystemFile) || (mParseLocal && !isSystemFile)) { if ((mParseSystem && isSystemFile) || (mParseLocal && !isSystemFile)) {
if (!bufferedText.isEmpty()) { QStringList bufferedText;
if (mOnGetFileStream && mOnGetFileStream(fileName,bufferedText)) {
parsedFile->buffer = bufferedText; parsedFile->buffer = bufferedText;
} else { } else {
parsedFile->buffer = readFileToLines(fileName); parsedFile->buffer = readFileToLines(fileName);

View File

@ -70,7 +70,7 @@ public:
void getDefineParts(const QString& input, QString &name, QString &args, QString &value); void getDefineParts(const QString& input, QString &name, QString &args, QString &value);
void addHardDefineByLine(const QString& line); void addHardDefineByLine(const QString& line);
void setScanOptions(bool parseSystem, bool parseLocal); void setScanOptions(bool parseSystem, bool parseLocal);
void preprocess(const QString& fileName, QStringList buffer = QStringList()); void preprocess(const QString& fileName);
void dumpDefinesTo(const QString& fileName) const; void dumpDefinesTo(const QString& fileName) const;
void dumpIncludesListTo(const QString& fileName) const; void dumpIncludesListTo(const QString& fileName) const;
@ -122,7 +122,7 @@ private:
PParsedFile getInclude(int index) const { PParsedFile getInclude(int index) const {
return mIncludes[index]; return mIncludes[index];
} }
void openInclude(const QString& fileName, QStringList bufferedText=QStringList()); void openInclude(const QString& fileName);
void closeInclude(); void closeInclude();
// branch stuff // branch stuff

View File

@ -921,6 +921,13 @@ bool Project::assignTemplate(const std::shared_ptr<ProjectTemplate> aTemplate, b
updateCompilerSetType(); updateCompilerSetType();
mOptions.icon = aTemplate->icon(); mOptions.icon = aTemplate->icon();
QTextCodec* codec=QTextCodec::codecForName(mOptions.encoding);
if (!codec)
mOptions.encoding=ENCODING_SYSTEM_DEFAULT;
codec=QTextCodec::codecForName(mOptions.execEncoding);
if (!codec)
mOptions.execEncoding=ENCODING_SYSTEM_DEFAULT;
// Copy icon to project directory // Copy icon to project directory
if (!mOptions.icon.isEmpty()) { if (!mOptions.icon.isEmpty()) {
QString originIcon = cleanPath(QFileInfo(aTemplate->fileName()).absoluteDir().absoluteFilePath(mOptions.icon)); QString originIcon = cleanPath(QFileInfo(aTemplate->fileName()).absoluteDir().absoluteFilePath(mOptions.icon));

View File

@ -4992,6 +4992,10 @@
<source>Please check detail info in &quot;Tools Output&quot; panel.</source> <source>Please check detail info in &quot;Tools Output&quot; panel.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>GNU Assembler Manual</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NewClassDialog</name> <name>NewClassDialog</name>

File diff suppressed because it is too large Load Diff

View File

@ -4781,6 +4781,10 @@
<source>Please check detail info in &quot;Tools Output&quot; panel.</source> <source>Please check detail info in &quot;Tools Output&quot; panel.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>GNU Assembler Manual</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>NewClassDialog</name> <name>NewClassDialog</name>

View File

@ -260,14 +260,13 @@ PClassBrowserNode ClassBrowserModel::addChild(ClassBrowserNode *node, const PSta
.arg(statement->noNameArgs) .arg(statement->noNameArgs)
.arg((int)statement->kind),newNode); .arg((int)statement->kind),newNode);
mProcessedStatements.insert(statement.get()); mProcessedStatements.insert(statement.get());
if (statement->kind == StatementKind::skClass if (isScopeStatement(statement)) {
|| statement->kind == StatementKind::skNamespace) {
mScopeNodes.insert(statement->fullName,newNode); mScopeNodes.insert(statement->fullName,newNode);
} }
//don't show enum type's children values (they are displayed in parent scope) //don't show enum type's children values (they are displayed in parent scope)
if (statement->kind != StatementKind::skEnumType) { // if (statement->kind != StatementKind::skEnumType) {
filterChildren(newNode.get(), statement->children); filterChildren(newNode.get(), statement->children);
} // }
return newNode; return newNode;
} }
@ -373,7 +372,7 @@ void ClassBrowserModel::filterChildren(ClassBrowserNode *node, const StatementMa
if (dummyNode) if (dummyNode)
parentNode = dummyNode; parentNode = dummyNode;
} }
if (statement->kind == StatementKind::skNamespace || statement->kind == StatementKind::skClass) { if (isScopeStatement(statement)) {
//PStatement dummy = mDummyStatements.value(statement->fullName,PStatement()); //PStatement dummy = mDummyStatements.value(statement->fullName,PStatement());
PClassBrowserNode scopeNode = mScopeNodes.value(statement->fullName,PClassBrowserNode()); PClassBrowserNode scopeNode = mScopeNodes.value(statement->fullName,PClassBrowserNode());
if (!scopeNode) { if (!scopeNode) {
@ -415,8 +414,7 @@ ClassBrowserNode* ClassBrowserModel::getParentNode(const PStatement &parentState
return nullptr; return nullptr;
if (!parentStatement) if (!parentStatement)
return mRoot; return mRoot;
if (parentStatement->kind!=skClass if (!isScopeStatement(parentStatement)) {
&& parentStatement->kind!=skNamespace) {
return mRoot; return mRoot;
} }
PClassBrowserNode parentNode = mScopeNodes.value(parentStatement->fullName,PClassBrowserNode()); PClassBrowserNode parentNode = mScopeNodes.value(parentStatement->fullName,PClassBrowserNode());
@ -428,6 +426,19 @@ ClassBrowserNode* ClassBrowserModel::getParentNode(const PStatement &parentState
return parentNode.get(); return parentNode.get();
} }
bool ClassBrowserModel::isScopeStatement(const PStatement &statement)
{
switch(statement->kind) {
case StatementKind::skClass:
case StatementKind::skNamespace:
case StatementKind::skEnumClassType:
case StatementKind::skEnumType:
return true;
default:
return false;
}
}
QModelIndex ClassBrowserModel::modelIndexForStatement(const QString &key) QModelIndex ClassBrowserModel::modelIndexForStatement(const QString &key)
{ {
QMutexLocker locker(&mMutex); QMutexLocker locker(&mMutex);

View File

@ -75,6 +75,7 @@ private:
void filterChildren(ClassBrowserNode * node, const StatementMap& statements); void filterChildren(ClassBrowserNode * node, const StatementMap& statements);
PStatement createDummy(const PStatement& statement); PStatement createDummy(const PStatement& statement);
ClassBrowserNode* getParentNode(const PStatement &parentStatement, int depth); ClassBrowserNode* getParentNode(const PStatement &parentStatement, int depth);
bool isScopeStatement(const PStatement& statement);
private: private:
ClassBrowserNode * mRoot; ClassBrowserNode * mRoot;
QHash<QString,PStatement> mDummyStatements; QHash<QString,PStatement> mDummyStatements;

View File

@ -1615,16 +1615,15 @@ int QSynEdit::calcIndentSpaces(int line, const QString& lineText, bool addIndent
QString firstToken = mSyntaxer->getToken(); QString firstToken = mSyntaxer->getToken();
PTokenAttribute attr = mSyntaxer->getTokenAttribute(); PTokenAttribute attr = mSyntaxer->getTokenAttribute();
if ( if (
( (attr->tokenType() == TokenType::Keyword (attr->tokenType() == TokenType::Keyword
&& ( && (
firstToken == "public" || firstToken == "private" firstToken == "public" || firstToken == "private"
|| firstToken == "protected" || firstToken == "case" || firstToken == "protected" || firstToken == "case"
|| firstToken == "default" || firstToken == "default"
) )
) )
|| (attr->tokenType() == TokenType::Identifier)) && lineText.endsWith(':')
&& lineText.endsWith(':') ) {
) {
// public: private: protecte: case: should indents like it's parent statement // public: private: protecte: case: should indents like it's parent statement
mSyntaxer->setState(rangePreceeding); mSyntaxer->setState(rangePreceeding);
mSyntaxer->setLine("}",line-1); mSyntaxer->setLine("}",line-1);

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,22 @@
[Template]
Ver = 3
Name = Inline ASM
Category = Assembly
Description = A simple c program with inline assembly instructions
Name[zh_CN] = 内联汇编
Category[zh_CN] = 汇编
Description[zh_CN] = 简单的C内联汇编程序示例
Icon = app.ico
[Project]
Type = 1
IsCpp = 0
Encoding = SYSTEM
ClassBrowserType = 0
UnitCount = 1
[Unit0]
CName=main.c
C=main.c

View File

@ -0,0 +1,22 @@
#include <stdio.h>
int add(int x,int y) {
int result;
asm (
"movl %%eax, %1 \n"
"addl %%eax, %2 \n"
"movl %0, %%eax \n"
:"=m"(result) //output operands used in the instructions
:"m"(x),"m"(y) //input operands used in the instructions
:"eax" //registers changed by the assembler instructions
);
return x+y;
}
int main() {
int a,b,c;
scanf("%d,%d",&a,&b);
c=a+b;
printf("%d\n",c);
return 0;
}

View File

@ -2,13 +2,19 @@
.text: .text:
main: main:
# the x64 calling convention requires you to allocate 32 bytes of shadow space before each call # Microsoft X86_64 Calling convention:
# https://stackoverflow.com/questions/30190132/what-is-the-shadow-space-in-x64-assembly/ # - The first four integer or pointer parameters are passed in the rcx, rdx, r8, and r9 registers.
sub $32, %rsp # allocate shadow space # - The first four floating-point parameters are passed in the first four SSE registers, xmm0-xmm3.
leaq fmt(%rip), %rcx # first parameter # - The caller reserves space on the stack for arguments passed in registers. The called function can use this space to spill the contents of registers to the stack.
leaq msg(%rip), %rdx # second parameter # - Any additional arguments are passed on the stack.
# - An integer or pointer return value is returned in the rax register, while a floating-point return value is returned in xmm0.
# see https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/x64-architecture
sub $32, %rsp # reserve stack space for the call
leaq fmt(%rip), %rcx # first parameter
leaq msg(%rip), %rdx # second parameter
call printf call printf
add $32, %rsp # remove shadow space add $32, %rsp # restore stack space
xor %eax,%eax # set 0 as exit code xor %eax,%eax # set 0 as exit code
ret ret

View File

@ -8,13 +8,19 @@ fmt: db "%s", 10, 0 ; The printf format, "\n",'0'
section .text: section .text:
main: main:
; the x64 calling convention requires you to allocate 32 bytes of shadow space before each call ; Microsoft X86_64 Calling convention:
; https://stackoverflow.com/questions/30190132/what-is-the-shadow-space-in-x64-assembly/ ; - The first four integer or pointer parameters are passed in the rcx, rdx, r8, and r9 registers.
sub rsp, 32 ; allocate shadow space ; - The first four floating-point parameters are passed in the first four SSE registers, xmm0-xmm3.
; - The caller reserves space on the stack for arguments passed in registers. The called function can use this space to spill the contents of registers to the stack.
; - Any additional arguments are passed on the stack.
; - An integer or pointer return value is returned in the rax register, while a floating-point return value is returned in xmm0.
; see https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/x64-architecture
sub rsp, 32 ; reserve stack space for call
lea rcx, [rel fmt] ; first parameter lea rcx, [rel fmt] ; first parameter
lea rdx, [rel msg] ; secodng parameter lea rdx, [rel msg] ; secodng parameter
call printf call printf
add rsp,32 ; restore stack
add rsp,32 ; remove shadow space
mov eax,0 ; exit code mov eax,0 ; exit code
ret ret

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,22 @@
[Template]
Ver = 3
Name = Inline ASM
Category = Assembly
Description = A simple c program with inline assembly instructions
Name[zh_CN] = 内联汇编
Category[zh_CN] = 汇编
Description[zh_CN] = 简单的C内联汇编程序示例
Icon = app.ico
[Project]
Type = 1
IsCpp = 0
Encoding = SYSTEM
ClassBrowserType = 0
UnitCount = 1
[Unit0]
CName=main.c
C=main.c

View File

@ -0,0 +1,22 @@
#include <stdio.h>
int add(int x,int y) {
int result;
asm (
"movl %%eax, %1 \n"
"addl %%eax, %2 \n"
"movl %0, %%eax \n"
:"=m"(result) //output operands used in the instructions
:"m"(x),"m"(y) //input operands used in the instructions
:"eax" //registers changed by the assembler instructions
);
return x+y;
}
int main() {
int a,b,c;
scanf("%d,%d",&a,&b);
c=a+b;
printf("%d\n",c);
return 0;
}