From f59755e3f05cf0a537b7257c825d003f46666175 Mon Sep 17 00:00:00 2001 From: wingsummer <1326224942@qq.com> Date: Sat, 19 Apr 2025 18:57:10 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=9B=B4=E5=A5=BD=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=8F=90=E7=A4=BA=E6=94=AF=E6=8C=81=EF=BC=9B=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E7=A6=81=E7=94=A8=E8=84=9A=E6=9C=AC=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=9A=84=E7=A8=8B=E5=BA=8F=E5=B4=A9=E6=BA=83?= =?UTF-8?q?=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdparty/WingCodeEdit | 2 +- lang/zh_CN/winghex_zh_CN.ts | 68 +++--- lang/zh_TW/winghex_zh_TW.ts | 68 +++--- src/class/ascompletion.cpp | 39 ++- src/class/ascompletion.h | 2 +- src/class/pluginsystem.cpp | 8 + src/class/qascodeparser.cpp | 467 +++++++++++++----------------------- src/class/qascodeparser.h | 36 ++- src/class/scriptmachine.cpp | 5 +- src/dialog/mainwindow.cpp | 22 +- 10 files changed, 304 insertions(+), 413 deletions(-) diff --git a/3rdparty/WingCodeEdit b/3rdparty/WingCodeEdit index 4307a25..e5cef2d 160000 --- a/3rdparty/WingCodeEdit +++ b/3rdparty/WingCodeEdit @@ -1 +1 @@ -Subproject commit 4307a2552e52d9f7e5c8fc2bd1d3148748e968f9 +Subproject commit e5cef2dcf126037ffdc57a5aaf6a3b1d3f4c70ae diff --git a/lang/zh_CN/winghex_zh_CN.ts b/lang/zh_CN/winghex_zh_CN.ts index 26f316b..5e9678c 100644 --- a/lang/zh_CN/winghex_zh_CN.ts +++ b/lang/zh_CN/winghex_zh_CN.ts @@ -1692,7 +1692,7 @@ 【错误】 - + ConfirmAPPSave 你尝试关闭程序,但仍存在未保存的文件或工作区,你确定保存这些更改吗? @@ -2272,125 +2272,125 @@ 总计发现设备拓展插件数目: - + RegisterScriptFnUnSupportedTypes: 因脚本函数含有未支持的类型而注册失败: - - + + RegisterScriptFnInvalidSig: 因脚本函数签名非法而注册失败: - - + + RegisterScriptFnConflitSig: 因脚本函数签名冲突而注册失败: - + InvalidEnumName: 非法枚举名: - + InvalidEnumValue: 非法枚举值: - + InvalidMarcosRegister: 非法宏注册: - + ErrLoadPluginSDKVersion 插件加载失败:非法插件 SDK 版本! - + ErrLoadPluginNoName 插件加载失败:非法插件名称! - + ErrLoadInitPlugin 插件加载失败:初始化插件失败! - + PluginName : 插件名: - + PluginAuthor : 插件作者: - + PluginWidgetRegister 注册插件对象中…… - + ErrLoadExtPluginSDKVersion 设备拓展插件加载失败:非法插件 SDK 版本! - + ExtPluginAuthor : 设备拓展插件作者: - + ExtPluginWidgetRegister 设备拓展注册插件对象中…… - + ErrLoadInitExtPlugin 设备拓展插件加载失败:初始化插件失败! - + ChooseFile 选择文件 - - + + Error 错误 - + FileNotExist 文件不存在! - + FilePermission 因文件权限无法继续! - + EmptyNameDockWidget: 空的贴边组件名: - + InvalidNameDockWidget: 无效贴边组件名: - + InvalidNullDockWidget: 无效空贴边组件: - + Not allowed operation in non-UI thread 该操作在非 UI 线程非法 @@ -2754,14 +2754,14 @@ - - + + Script failed to build 脚本编译失败 - + Cannot find 'int main()' or 'void main()' 无法找到程序入口点: "int main()" 或者 "void main()" @@ -2777,19 +2777,19 @@ - + The script failed with an exception 异常被抛出,脚本执行失败 - + The script was aborted 脚本被终止 - + The script terminated unexpectedly 脚本异常退出 diff --git a/lang/zh_TW/winghex_zh_TW.ts b/lang/zh_TW/winghex_zh_TW.ts index a70b244..e443ec8 100644 --- a/lang/zh_TW/winghex_zh_TW.ts +++ b/lang/zh_TW/winghex_zh_TW.ts @@ -1692,7 +1692,7 @@ 【錯誤】 - + ConfirmAPPSave 你嘗試關閉程式,但仍存在未保存的檔或工作區,你確定保存這些更改嗎? @@ -2272,125 +2272,125 @@ 總計發現設備拓展插件數目: - + RegisterScriptFnUnSupportedTypes: 因腳本函數含有未支持的類型而註冊失敗: - - + + RegisterScriptFnInvalidSig: 因腳本函數簽名非法而註冊失敗: - - + + RegisterScriptFnConflitSig: 因腳本函數簽名衝突而註冊失敗: - + InvalidEnumName: 非法枚舉名: - + InvalidEnumValue: 非法枚舉值: - + InvalidMarcosRegister: 非法宏註冊: - + ErrLoadPluginSDKVersion 插件加載失敗:非法插件 SDK 版本! - + ErrLoadPluginNoName 插件加載失敗:非法插件名稱! - + ErrLoadInitPlugin 插件加載失敗:初始化插件失敗! - + PluginName : 插件名: - + PluginAuthor : 插件作者: - + PluginWidgetRegister 註冊插件對象中…… - + ErrLoadExtPluginSDKVersion 設備拓展插件加載失敗:非法插件 SDK 版本! - + ExtPluginAuthor : 設備拓展插件作者: - + ExtPluginWidgetRegister 設備拓展註冊插件對象中…… - + ErrLoadInitExtPlugin 設備拓展插件加載失敗:初始化插件失敗! - + ChooseFile 選擇檔 - - + + Error 錯誤 - + FileNotExist 檔不存在! - + FilePermission 因檔許可權無法繼續! - + EmptyNameDockWidget: 空的貼邊組件名: - + InvalidNameDockWidget: 無效貼邊組件名: - + InvalidNullDockWidget: 無效空貼邊組件: - + Not allowed operation in non-UI thread 該操作在非 UI 線程非法 @@ -2754,14 +2754,14 @@ - - + + Script failed to build 腳本編譯失敗 - + Cannot find 'int main()' or 'void main()' 無法找到程式入口點: "int main()" 或者 "void main()" @@ -2777,19 +2777,19 @@ - + The script failed with an exception 異常被拋出,腳本執行失敗 - + The script was aborted 腳本被終止 - + The script terminated unexpectedly 腳本異常退出 diff --git a/src/class/ascompletion.cpp b/src/class/ascompletion.cpp index 5b0455f..0eee59b 100644 --- a/src/class/ascompletion.cpp +++ b/src/class/ascompletion.cpp @@ -139,15 +139,15 @@ QString AsCompletion::wordSeperators() const { return eow; } -void AsCompletion::processTrigger(const QString &trigger, +bool AsCompletion::processTrigger(const QString &trigger, const QString &content) { if (content.isEmpty()) { - return; + return false; } if (trigger == *SEMI_COLON_TRIGGER) { clearFunctionTip(); - return; + return false; } auto len = content.length(); @@ -173,13 +173,14 @@ void AsCompletion::processTrigger(const QString &trigger, QByteArray content; }; + auto engine = ScriptMachine::instance().engine(); + // parse the tokens QVector tokens; qsizetype pos = 0; for (; p < end;) { asUINT tokenLen = 0; - auto tt = - ScriptMachine::instance().engine()->ParseToken(p, len, &tokenLen); + auto tt = engine->ParseToken(p, len, &tokenLen); if (tt == asTC_WHITESPACE) { p += tokenLen; pos += tokenLen; @@ -222,11 +223,16 @@ void AsCompletion::processTrigger(const QString &trigger, QByteArray fn; if (tokens.isEmpty()) { popup()->hide(); - return; + return false; } QString prefix; auto etoken = tokens.back(); + if (etoken.type == asTC_VALUE || etoken.type == asTC_COMMENT || + etoken.type == asTC_UNKNOWN) { + popup()->hide(); + return false; + } // if trigger is empty, it's making editing if (trigger.isEmpty()) { @@ -246,20 +252,21 @@ void AsCompletion::processTrigger(const QString &trigger, if (etoken.content == *DBL_COLON_TRIGGER) { processTrigger(*DBL_COLON_TRIGGER, content.left(etoken.pos)); setCompletionPrefix(prefix); - return; + return true; } else if (etoken.content == *DOT_TRIGGER) { processTrigger(*DOT_TRIGGER, content.left(etoken.pos)); setCompletionPrefix(prefix); - return; + return true; } else { applyEmptyNsNode(nodes, docNodes); } } else if (etoken.type != asTC_IDENTIFIER) { popup()->hide(); - return; + return false; } if (trigger == *DOT_TRIGGER) { + // member guessing ? applyClassNodes(nodes); } else if (etoken.content.length() >= triggerAmount()) { // completion for a.b.c or a::b.c or a::b::c.d or ::a::b.c @@ -269,7 +276,7 @@ void AsCompletion::processTrigger(const QString &trigger, if (idx >= 0) { if (tokens.at(idx).content == *DOT_TRIGGER) { popup()->hide(); - return; + return false; } } nodes = parser.headerNodes().value(ns) + @@ -281,7 +288,7 @@ void AsCompletion::processTrigger(const QString &trigger, } if (nodes.isEmpty()) { - return; + return true; } } else if (trigger == *LEFT_PARE_TRIGGER) { // the first is function name, an identifier @@ -297,7 +304,7 @@ void AsCompletion::processTrigger(const QString &trigger, if (idx >= 0 && idx < tokens.length()) { if (tokens.at(idx).content == *DOT_TRIGGER) { popup()->hide(); - return; + return false; } } @@ -312,6 +319,7 @@ void AsCompletion::processTrigger(const QString &trigger, setModel(new CodeCompletionModel(nodes, this)); setCompletionPrefix(prefix); + return true; } QList AsCompletion::parseDocument() { @@ -387,7 +395,14 @@ QList AsCompletion::parseDocument() { tip.type = CodeInfoTip::Type::Variable; break; case QAsCodeParser::SymbolType::Class: + case QAsCodeParser::SymbolType::Interface: + tip.type = CodeInfoTip::Type::Class; + for (auto &mem : sym.children) { + // TODO + } + break; case QAsCodeParser::SymbolType::Invalid: + case QAsCodeParser::SymbolType::Import: continue; } diff --git a/src/class/ascompletion.h b/src/class/ascompletion.h index 525e01a..a79c8c6 100644 --- a/src/class/ascompletion.h +++ b/src/class/ascompletion.h @@ -38,7 +38,7 @@ public: void clearFunctionTip(); protected: - virtual void processTrigger(const QString &trigger, + virtual bool processTrigger(const QString &trigger, const QString &content) override; virtual QList parseDocument(); diff --git a/src/class/pluginsystem.cpp b/src/class/pluginsystem.cpp index f5a8e63..311a48d 100644 --- a/src/class/pluginsystem.cpp +++ b/src/class/pluginsystem.cpp @@ -2840,6 +2840,10 @@ void PluginSystem::checkDirRootSafe(const QDir &dir) { } void PluginSystem::registerFns(IWingPlugin *plg) { + if (_angelplg == nullptr) { + return; + } + Q_ASSERT(plg); auto fns = plg->registeredScriptFns(); if (fns.isEmpty()) { @@ -2912,6 +2916,10 @@ void PluginSystem::registerFns(IWingPlugin *plg) { } void PluginSystem::registerUnSafeFns(IWingPlugin *plg) { + if (_angelplg == nullptr) { + return; + } + Q_ASSERT(plg); auto fns = plg->registeredScriptUnsafeFns(); diff --git a/src/class/qascodeparser.cpp b/src/class/qascodeparser.cpp index d46bb99..76bb720 100644 --- a/src/class/qascodeparser.cpp +++ b/src/class/qascodeparser.cpp @@ -81,7 +81,6 @@ QAsCodeParser::parseIntell(qsizetype offset, case SymbolType::Function: { sym.type = seg.additonalInfos.at(0); sym.additonalInfo = seg.additonalInfos.at(1); - sym.name = seg.name; if (offset > seg.offset && offset < seg.end()) { sym.children = parseStatementBlock(sym.scope, seg.codes, offset - seg.offset); @@ -121,7 +120,13 @@ QAsCodeParser::parseIntell(qsizetype offset, } break; case SymbolType::Class: + sym.children = parseClassContent(offset, sym.scope, seg.codes); + break; + case SymbolType::Interface: + sym.children = parseInterfaceContent(offset, sym.scope, seg.codes); + break; case SymbolType::Invalid: + case SymbolType::Import: continue; } @@ -440,6 +445,49 @@ QByteArray QAsCodeParser::parseType(bool allowConst, bool allowVariableType, return dtType; } +QAsCodeParser::Symbol +QAsCodeParser::parseFuncDefContent(const QByteArrayList &ns) { + Symbol sym; + + sToken t1; + getToken(&t1); + + if (t1.type != ttFuncDef) { + rewindErrorTo(&t1); + return sym; + } + + sym.type = parseType(true); + if (_isSyntaxError) + return sym; + + parseTypeMod(false); + if (_isSyntaxError) + return sym; + + auto iden = parseIdentifier(); + if (_isSyntaxError) + return sym; + + sym.scope = ns; + sym.name = getSymbolString(iden); + sym.offset = iden.pos; + + auto args = parseParameterListContent(); + if (_isSyntaxError) + return sym; + sym.children = args; + + getToken(&t1); + if (t1.type != ttEndStatement) { + rewindErrorTo(&t1); + return sym; + } + + sym.symtype = SymbolType::FnDef; + return sym; +} + QList QAsCodeParser::parseStatementBlock(const QByteArrayList &ns, const QByteArray &code, qsizetype end) { @@ -744,10 +792,7 @@ sToken QAsCodeParser::superficiallyParseStatementBlock() { level--; else if (t1.type == ttStartStatementBlock) level++; - else if (t1.type == ttNonTerminatedStringConstant) { - rewindErrorTo(&t1); - return t1; - } else if (t1.type == ttEnd) { + else if (t1.type == ttEnd) { rewindErrorTo(&start); return start; } @@ -928,12 +973,13 @@ QAsCodeParser::CodeSegment QAsCodeParser::parseInterface() { auto id = parseIdentifier(); seg.name = getSymbolString(id); - seg.offset = id.pos; + seg.scope = currentNs; - seg.type = SymbolType::Class; + seg.type = SymbolType::Interface; // External shared declarations are ended with ';' getToken(&t); + seg.offset = t.pos; if (t.type == ttEndStatement) { rewindTo(&t); parseToken(ttEndStatement); @@ -1005,17 +1051,6 @@ void QAsCodeParser::superficiallyParseVarInit() { } } -void QAsCodeParser::parseStatement() { - sToken t1; - - getToken(&t1); - rewindTo(&t1); - - // TODO - if (isVarDecl()) { - } -} - QList QAsCodeParser::parseDeclaration(const QByteArrayList &ns, bool isClassProp, bool isGlobalVar) { @@ -1107,10 +1142,14 @@ QList QAsCodeParser::parseScript(bool inBlock) { rewindTo(&tStart); if (t1.type == ttImport) { + CodeSegment seg; + seg.offset = t1.pos; + + seg.type = SymbolType::Import; + segs << seg; + // import we don't support just skip skipCodeBlock(); - // add empty invalid segment - segs << CodeSegment(); } else if (t1.type == ttEnum) // Handle enumerations segs << parseEnumeration(); else if (t1.type == ttTypedef) // Handle primitive typedefs @@ -1235,12 +1274,13 @@ QAsCodeParser::CodeSegment QAsCodeParser::parseClass() { auto cls = parseIdentifier(); seg.name = getSymbolString(cls); - seg.offset = cls.pos; + seg.scope = currentNs; seg.type = SymbolType::Class; // External shared declarations are ended with ';' getToken(&t); + seg.offset = t.pos; if (t.type == ttEndStatement) { rewindTo(&t); parseToken(ttEndStatement); @@ -1383,112 +1423,103 @@ QAsCodeParser::parseGlobalVarDecls(const QByteArrayList &ns, return parseDeclaration(ns, false, true); } -QAsCodeParser::CodeSegment QAsCodeParser::parseFunction(bool isMethod) { - CodeSegment fn; +QAsCodeParser::CodeSegment QAsCodeParser::parseFunctionMethod() { + CodeSegment seg; sToken t1; getToken(&t1); - if (!isMethod) { - // A global function can be marked as shared and external - while (t1.type == ttIdentifier) { - if (identifierIs(t1, SHARED_TOKEN) || - identifierIs(t1, EXTERNAL_TOKEN)) { - rewindTo(&t1); - parseIdentifier(); - if (_isSyntaxError) - return fn; - } else - break; - - getToken(&t1); - } - } - // A class method can start with 'private' or 'protected' - if (isMethod && t1.type == ttPrivate) { + if (t1.type == ttPrivate) { rewindTo(&t1); parseToken(ttPrivate); getToken(&t1); - } else if (isMethod && t1.type == ttProtected) { + } else if (t1.type == ttProtected) { rewindTo(&t1); parseToken(ttProtected); getToken(&t1); } if (_isSyntaxError) - return fn; + return seg; // If it is a global function, or a method, except constructor and // destructor, then the return type is parsed sToken t2; getToken(&t2); rewindTo(&t1); - if (!isMethod || (t1.type != ttBitNot && t2.type != ttOpenParenthesis)) { + if (t1.type != ttBitNot && t2.type != ttOpenParenthesis) { auto id = parseType(true); if (_isSyntaxError) - return fn; - fn.additonalInfos.append(id); + return seg; + seg.additonalInfos.append(id); parseTypeMod(false); if (_isSyntaxError) - return fn; + return seg; } // If this is a class destructor then it starts with ~, and no return type // is declared - if (isMethod && t1.type == ttBitNot) { + if (t1.type == ttBitNot) { parseToken(ttBitNot); if (_isSyntaxError) - return fn; + return seg; } auto iden = parseIdentifier(); if (_isSyntaxError) - return fn; - fn.name = getSymbolString(iden); - fn.offset = iden.pos; + return seg; + seg.name = getSymbolString(iden); + seg.offset = iden.pos; auto params = parseParameterListContent(); if (_isSyntaxError) - return fn; - if (!params.isEmpty()) { - if (fn.additonalInfos.isEmpty()) { - fn.additonalInfos.append(QByteArray()); + return seg; + + if (seg.additonalInfos.isEmpty()) { + seg.additonalInfos.append(QByteArray()); + } + if (params.isEmpty()) { + seg.additonalInfos.append(QByteArray()); + } else { + QByteArrayList args; + for (auto &p : params) { + args.append(p.type + ' ' + p.name); } - // fn.additonalInfos; + seg.additonalInfos.append(args.join(", ")); } - if (isMethod) { - getToken(&t1); - rewindTo(&t1); + getToken(&t1); + rewindTo(&t1); - // Is the method a const? - if (t1.type == ttConst) - parseToken(ttConst); - } + // Is the method a const? + if (t1.type == ttConst) + parseToken(ttConst); // TODO: Should support abstract methods, in which case no statement block // should be provided parseMethodAttributes(); if (_isSyntaxError) - return fn; + return seg; // External shared functions must be ended with ';' getToken(&t1); rewindTo(&t1); + + seg.scope = currentNs; + if (t1.type == ttEndStatement) { parseToken(ttEndStatement); - // fn.ns = _curns; - return fn; + return seg; } - // We should just find the end of the statement block here. The statements - // will be parsed on request by the compiler once it starts the compilation. - superficiallyParseStatementBlock(); - // fn.ns = _curns; - // fn.code = code.mid(t1.pos, sourcePos - t1.pos); - // fn.valid = true; - return fn; + auto begin = t1.pos; + + // We should just find the end of the statement block here. + t1 = superficiallyParseStatementBlock(); + auto end = t1.pos; + seg.codes = _code.sliced(begin, end - begin + 1); + return seg; } QAsCodeParser::Symbol @@ -1496,50 +1527,11 @@ QAsCodeParser::parseFuncDefContent(const QByteArrayList &ns, const QByteArray &code) { reset(); _code = code; - - Symbol sym; - - sToken t1; - getToken(&t1); - - if (t1.type != ttFuncDef) { - rewindErrorTo(&t1); - return sym; - } - - sym.type = parseType(true); - if (_isSyntaxError) - return sym; - - parseTypeMod(false); - if (_isSyntaxError) - return sym; - - auto iden = parseIdentifier(); - if (_isSyntaxError) - return sym; - - sym.scope = ns; - sym.name = getSymbolString(iden); - sym.offset = iden.pos; - - auto args = parseParameterListContent(); - if (_isSyntaxError) - return sym; - sym.children = args; - - getToken(&t1); - if (t1.type != ttEndStatement) { - rewindErrorTo(&t1); - return sym; - } - - sym.symtype = SymbolType::FnDef; - return sym; + return parseFuncDefContent(ns); } QList -QAsCodeParser::parseClassContent(const QByteArrayList &ns, +QAsCodeParser::parseClassContent(qsizetype offset, const QByteArrayList &ns, const QByteArray &code) { reset(); _code = code; @@ -1552,15 +1544,27 @@ QAsCodeParser::parseClassContent(const QByteArrayList &ns, // Optional list of interfaces that are being implemented and classes that // are being inherited if (t.type == ttColon) { - // TODO: dont support temperily, later - parseOptionalScope(); - parseIdentifier(); + Symbol inhertSym; + + // assuming it as an interface + inhertSym.symtype = SymbolType::Interface; + + Symbol isym; + isym.scope = parseOptionalScope(); + isym.name = getSymbolString(parseIdentifier()); + isym.symtype = SymbolType::Class; // assuming it as a class + + inhertSym.children.append(isym); + getToken(&t); while (t.type == ttListSeparator) { - parseOptionalScope(); - parseIdentifier(); + isym.scope = parseOptionalScope(); + isym.name = getSymbolString(parseIdentifier()); + inhertSym.children.append(isym); getToken(&t); } + + syms.append(inhertSym); } if (t.type != ttStartStatementBlock) { @@ -1574,23 +1578,32 @@ QAsCodeParser::parseClassContent(const QByteArrayList &ns, while (t.type != ttEndStatementBlock && t.type != ttEnd) { // Is it a property or a method? if (t.type == ttFuncDef) { - // auto fndef = parseFuncDefContent(); - // if (fndef.isValid()) { - // clssym.content.append(fndef); - // } + auto fndef = parseFuncDefContent(ns); + syms.append(fndef); } else if (isFuncDecl(true)) { - auto fn = parseFunction(true); - // if (fn.isValid()) { - // clssym.codesegs.insert(fn.nameInSrc, fn); - // } + auto fn = parseFunctionMethod(); + + // add function symbols + Symbol sym; + sym.symtype = fn.type; + sym.scope = fn.scope; + sym.offset = fn.offset; + sym.name = fn.name; + sym.type = fn.additonalInfos.at(0); + sym.additonalInfo = fn.additonalInfos.at(1); + syms.append(sym); + + // deep parsing + if (offset >= fn.offset && offset < fn.end()) { + auto ss = parseStatementBlock(fn.scope, fn.codes, offset); + syms.append(ss); + } } else if (isVirtualPropertyDecl()) { auto vp = parseVirtualPropertyDecl(true, false); - // if (vp.isValid()) { - // clssym.content.append(vp); - // } + syms.append(vp); } else if (isVarDecl()) { - // auto decl = parseDeclaration(true); - // clssym.content.append(decl); + auto decl = parseDeclaration(ns, true); + syms.append(decl); } else if (t.type == ttEndStatement) // Skip empty declarations getToken(&t); @@ -1615,53 +1628,14 @@ QAsCodeParser::parseClassContent(const QByteArrayList &ns, return syms; } -void QAsCodeParser::parseMixinContent() { - sToken t; - getToken(&t); - - if (t.type != ttMixin) { - rewindErrorTo(&t); - return; - } - - // A mixin token must be followed by a class declaration - // parseClassContent(); -} - -void QAsCodeParser::parseInterfaceContent() { - Symbol sym; +QList +QAsCodeParser::parseInterfaceContent(qsizetype offset, const QByteArrayList &ns, + const QByteArray &code) { + QList syms; sToken t; - // Allow keywords 'external' and 'shared' before 'interface' getToken(&t); - while (identifierIs(t, SHARED_TOKEN) || identifierIs(t, EXTERNAL_TOKEN)) { - rewindTo(&t); - parseIdentifier(); - - if (_isSyntaxError) - return; - - getToken(&t); - } - - if (t.type != ttInterface) { - rewindErrorTo(&t); - return; - } - - auto id = parseIdentifier(); - sym.name = getSymbolString(id); - // sym.nameInSrc = id.pos; - - // External shared declarations are ended with ';' - getToken(&t); - if (t.type == ttEndStatement) { - rewindTo(&t); - parseToken(ttEndStatement); - return; - } - // Can optionally have a list of interfaces that are inherited if (t.type == ttColon) { parseOptionalScope(); @@ -1676,7 +1650,7 @@ void QAsCodeParser::parseInterfaceContent() { if (t.type != ttStartStatementBlock) { rewindErrorTo(&t); - return; + return {}; } // Parse interface methods @@ -1685,22 +1659,18 @@ void QAsCodeParser::parseInterfaceContent() { while (t.type != ttEndStatementBlock && t.type != ttEnd) { if (isVirtualPropertyDecl()) { auto vp = parseVirtualPropertyDecl(true, true); - // if (vp.isValid()) { - // sym.content.append(vp); - // } + syms.append(vp); } else if (t.type == ttEndStatement) { // Skip empty declarations getToken(&t); } else { // Parse the method signature auto im = parseInterfaceMethod(); - // if (im.isValid()) { - // sym.content.append(im); - // } + syms.append(im); } if (_isSyntaxError) - return; + return syms; getToken(&t); rewindTo(&t); @@ -1709,8 +1679,10 @@ void QAsCodeParser::parseInterfaceContent() { getToken(&t); if (t.type != ttEndStatementBlock) { rewindErrorTo(&t); - return; + return syms; } + + return syms; } QAsCodeParser::Symbol QAsCodeParser::parseInterfaceMethod() { @@ -1719,7 +1691,7 @@ QAsCodeParser::Symbol QAsCodeParser::parseInterfaceMethod() { auto ret = parseType(true); if (_isSyntaxError) return sym; - // sym.typeStr = getSymbolString(ret); + sym.type = ret; parseTypeMod(false); if (_isSyntaxError) @@ -1729,12 +1701,13 @@ QAsCodeParser::Symbol QAsCodeParser::parseInterfaceMethod() { if (_isSyntaxError) return sym; sym.name = getSymbolString(id); - // sym.nameInSrc = id.pos; + sym.offset = id.pos; auto args = parseParameterListContent(); if (_isSyntaxError) return sym; - // sym.content = args; + + sym.children = args; // Parse an optional const after the method definition sToken t1; @@ -1749,7 +1722,7 @@ QAsCodeParser::Symbol QAsCodeParser::parseInterfaceMethod() { return sym; } - // sym.type = SymbolType::FnDecl; + sym.symtype = SymbolType::Function; return sym; } @@ -1777,7 +1750,7 @@ QAsCodeParser::parseVirtualPropertyDecl(bool isMethod, bool isInterface) { auto id = parseType(true); if (_isSyntaxError) return sym; - // sym.typeStr = getSymbolString(id); + sym.type = id; parseTypeMod(false); if (_isSyntaxError) @@ -1787,7 +1760,7 @@ QAsCodeParser::parseVirtualPropertyDecl(bool isMethod, bool isInterface) { if (_isSyntaxError) return sym; sym.name = getSymbolString(iden); - // sym.nameInSrc = iden.pos; + sym.offset = iden.pos; getToken(&t1); if (t1.type != ttStartStatementBlock) { @@ -1825,11 +1798,7 @@ QAsCodeParser::parseVirtualPropertyDecl(bool isMethod, bool isInterface) { if (t1.type == ttStartStatementBlock) { rewindTo(&t1); superficiallyParseStatementBlock(); - - // seg.valid = true; - // seg.code = code.mid(t1.pos, sourcePos - t1.pos); - // sym.codesegs.insert(t1.pos, seg); - + // TODO: support deep parsing ? if (_isSyntaxError) return sym; } else if (t1.type == ttEndStatement) { @@ -1861,7 +1830,7 @@ QAsCodeParser::parseVirtualPropertyDecl(bool isMethod, bool isInterface) { } } - // sym.type = SymbolType::Property; + sym.symtype = SymbolType::Variable; return sym; } @@ -2206,75 +2175,6 @@ bool QAsCodeParser::isFuncDecl( return false; } -bool QAsCodeParser::isLambda() { - bool isLambda = false; - sToken t; - getToken(&t); - if (t.type == ttIdentifier && identifierIs(t, FUNCTION_TOKEN)) { - sToken t2; - getToken(&t2); - if (t2.type == ttOpenParenthesis) { - // Skip until ) - while (t2.type != ttCloseParenthesis && t2.type != ttEnd) - getToken(&t2); - - // The next token must be a { - getToken(&t2); - if (t2.type == ttStartStatementBlock) - isLambda = true; - } - } - - rewindTo(&t); - return isLambda; -} - -bool QAsCodeParser::isFunctionCall() { - sToken s; - sToken t1, t2; - - getToken(&s); - t1 = s; - - // A function call may be prefixed with scope resolution - if (t1.type == ttScope) - getToken(&t1); - getToken(&t2); - - while (t1.type == ttIdentifier && t2.type == ttScope) { - getToken(&t1); - getToken(&t2); - } - - // A function call starts with an identifier followed by an argument list - // The parser doesn't have enough RewindErrorTormation about scope to - // determine if the identifier is a datatype, so even if it happens to be - // the parser will identify the expression as a function call rather than a - // construct call. The compiler will sort this out later - if (t1.type != ttIdentifier) { - rewindTo(&s); - return false; - } - - if (t2.type == ttOpenParenthesis) { - rewindTo(&s); - return true; - } - - rewindTo(&s); - return false; -} - -void QAsCodeParser::ParseStringConstant() { - sToken t; - getToken(&t); - if (t.type != ttStringConstant && t.type != ttMultilineStringConstant && - t.type != ttHeredocStringConstant) { - rewindErrorTo(&t); - return; - } -} - bool QAsCodeParser::findTokenAfterType( sToken &nextToken) { // Set a rewind point sToken t, t1; @@ -2369,43 +2269,6 @@ bool QAsCodeParser::findTokenAfterType( return true; } -bool QAsCodeParser::findIdentifierAfterScope(sToken &nextToken) { - sToken t1, t2, t3; - - // Determine the last identifier after scope in order to check if it is a - // type - getToken(&t1); - getToken(&t2); - rewindTo(&t1); - - if (t1.type != ttScope && t2.type != ttScope) { - if (t1.type == ttIdentifier) { - nextToken = t1; - return true; - } - return false; - } - - if (t1.type == ttScope) - t3 = t2; - else - t3 = t1; - rewindTo(&t3); - getToken(&t2); - while (t3.type == ttIdentifier) { - t2 = t3; - getToken(&t3); - if (t3.type == ttScope) - getToken(&t3); - else - break; - } - rewindTo(&t1); - nextToken = t2; - - return true; -} - bool QAsCodeParser::isConstant(int tokenType) { if (tokenType == ttIntConstant || tokenType == ttFloatConstant || tokenType == ttDoubleConstant || tokenType == ttStringConstant || diff --git a/src/class/qascodeparser.h b/src/class/qascodeparser.h index b655960..bfd6742 100644 --- a/src/class/qascodeparser.h +++ b/src/class/qascodeparser.h @@ -24,7 +24,7 @@ #include #include -// This class is the modification of as_parser. +// This class comes from as_parser.h . // You can modified it to support more features. /** It's a complex thing to fully support AngelScript code intellisense. ** I just support basic code completion. @@ -41,16 +41,17 @@ public: public: enum class SymbolType { Invalid, - Variable, // variable or property in class - Enum, // an enum - Class, // a class type - Function, // a function - TypeDef, // a typedef - FnDef, // a funcdef + Variable, // variable or property in class + Enum, // an enum + Class, // a class type + Function, // a function + TypeDef, // a typedef + FnDef, // a funcdef + Interface, // an interface + Import, // import but not supported }; enum class Visiblity { Public, Private, Protected }; - /** * @brief The CodeSegment class */ @@ -133,6 +134,7 @@ private: CodeSegment parseInterface(); CodeSegment parseFuncDef(); CodeSegment parseFunction(); + CodeSegment parseFunctionMethod(); private: // parse tokens @@ -168,18 +170,19 @@ private: Symbol parseFuncDefContent(const QByteArrayList &ns, const QByteArray &code); - QList parseClassContent(const QByteArrayList &ns, + Symbol parseFuncDefContent(const QByteArrayList &ns); + + QList parseClassContent(qsizetype offset, const QByteArrayList &ns, const QByteArray &code); + QList parseInterfaceContent(qsizetype offset, + const QByteArrayList &ns, + const QByteArray &code); + QList parseStatementBlock(const QByteArrayList &ns, const QByteArray &code, qsizetype end); private: - void parseStatement(); - void parseMixinContent(); - void parseInterfaceContent(); - CodeSegment parseFunction(bool isMethod); - Symbol parseVirtualPropertyDecl(bool isMethod, bool isInterface); QList parseParameterListContent(); @@ -192,8 +195,6 @@ private: bool isVarDecl(); bool isVirtualPropertyDecl(); bool isFuncDecl(bool isMethod); - bool isLambda(); - bool isFunctionCall(); void getToken(sToken *token); void rewindTo(const sToken *token); @@ -209,14 +210,11 @@ private: private: bool findTokenAfterType(sToken &nextToken); - bool findIdentifierAfterScope(sToken &nextToken); bool typeExist(const QString &t); Symbol parseInterfaceMethod(); - void ParseStringConstant(); - private: bool _errorWhileParsing; bool _isSyntaxError; diff --git a/src/class/scriptmachine.cpp b/src/class/scriptmachine.cpp index 27ef928..15e3e38 100644 --- a/src/class/scriptmachine.cpp +++ b/src/class/scriptmachine.cpp @@ -1825,7 +1825,10 @@ bool ScriptMachine::executeCode(ConsoleMode mode, const QString &code) { auto ret = parser.parse(ccode); // check whether there is any enum/class - if (ret.isEmpty()) { + if (std::find_if(ret.begin(), ret.end(), + [](const QAsCodeParser::CodeSegment &seg) { + return seg.isValid(); + }) == ret.end()) { // ok, wrap the codes ccode.prepend("void main(){").append("}"); // start to compile diff --git a/src/dialog/mainwindow.cpp b/src/dialog/mainwindow.cpp index 136334d..08df9d5 100644 --- a/src/dialog/mainwindow.cpp +++ b/src/dialog/mainwindow.cpp @@ -3786,13 +3786,15 @@ void MainWindow::closeEvent(QCloseEvent *event) { } // then checking the scripting dialog - if (!m_scriptDialog->about2Close()) { - event->ignore(); - return; - } + if (m_scriptDialog) { + if (!m_scriptDialog->about2Close()) { + event->ignore(); + return; + } - // then abort all script running - ScriptMachine::instance().abortScript(); + // then abort all script running + ScriptMachine::instance().abortScript(); + } // then checking itself if (!m_views.isEmpty()) { @@ -3843,9 +3845,11 @@ void MainWindow::closeEvent(QCloseEvent *event) { auto &set = SettingManager::instance(); set.setDockLayout(m_dock->saveState()); - m_scriptDialog->saveDockLayout(); - set.setRecentFiles(m_recentmanager->saveRecent()); - set.save(); + if (m_scriptDialog) { + m_scriptDialog->saveDockLayout(); + set.setRecentFiles(m_recentmanager->saveRecent()); + set.save(); + } PluginSystem::instance().destory();