diff --git a/include/core/parseutil.h b/include/core/parseutil.h index d1ec1623..2d6a159c 100644 --- a/include/core/parseutil.h +++ b/include/core/parseutil.h @@ -74,10 +74,15 @@ private: QString root; QString text; QString file; + QString curDefine; + QMap errorMap; QList tokenizeExpression(QString expression, const QMap &knownIdentifiers); QList generatePostfix(const QList &tokens); int evaluatePostfix(const QList &postfix); - void error(const QString &message, const QString &expression); + void recordError(const QString &message); + void recordErrors(const QStringList &errors); + void logRecordedErrors(); + QString createErrorMessage(const QString &message, const QString &expression); static const QRegularExpression re_incScriptLabel; static const QRegularExpression re_globalIncScriptLabel; diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index f81547ec..a7533f5c 100644 --- a/src/core/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -16,15 +16,33 @@ void ParseUtil::set_root(const QString &dir) { this->root = dir; } -void ParseUtil::error(const QString &message, const QString &expression) { - QStringList lines = text.split(QRegularExpression("[\r\n]")); +void ParseUtil::recordError(const QString &message) { + this->errorMap[this->curDefine].append(message); +} + +void ParseUtil::recordErrors(const QStringList &errors) { + if (errors.isEmpty()) return; + this->errorMap[this->curDefine].append(errors); +} + +void ParseUtil::logRecordedErrors() { + QStringList errors = this->errorMap.value(this->curDefine); + if (errors.isEmpty()) return; + QString message = QString("Failed to parse '%1':").arg(this->curDefine); + for (const auto error : errors) + message.append(QString("\n%1").arg(error)); + logError(message); +} + +QString ParseUtil::createErrorMessage(const QString &message, const QString &expression) { + QStringList lines = this->text.split(QRegularExpression("[\r\n]")); int lineNum = 0, colNum = 0; for (QString line : lines) { lineNum++; colNum = line.indexOf(expression) + 1; if (colNum) break; } - logError(QString("%1:%2:%3: %4").arg(file).arg(lineNum).arg(colNum).arg(message)); + return QString("%1:%2:%3: %4").arg(this->file).arg(lineNum).arg(colNum).arg(message); } QString ParseUtil::readTextFile(const QString &path) { @@ -52,8 +70,8 @@ int ParseUtil::textFileLineCount(const QString &path) { QList ParseUtil::parseAsm(const QString &filename) { QList parsed; - text = readTextFile(root + '/' + filename); - const QStringList lines = removeLineComments(text, "@").split('\n'); + this->text = readTextFile(this->root + '/' + filename); + const QStringList lines = removeLineComments(this->text, "@").split('\n'); for (const auto &line : lines) { const QString trimmedLine = line.trimmed(); if (trimmedLine.isEmpty()) { @@ -101,6 +119,8 @@ QList ParseUtil::tokenizeExpression(QString expression, const QMaperrorMap.value(token)); QString actualToken = QString("%1").arg(knownIdentifiers.value(token)); expression = expression.replace(0, token.length(), actualToken); token = actualToken; @@ -109,14 +129,14 @@ QList ParseUtil::tokenizeExpression(QString expression, const QMap ParseUtil::generatePostfix(const QList &tokens) { // pop the left parenthesis token operatorStack.pop(); } else { - logError("Mismatched parentheses detected in expression!"); + recordError("Mismatched parentheses detected in expression!"); } } else { // token is an operator @@ -176,7 +196,7 @@ QList ParseUtil::generatePostfix(const QList &tokens) { while (!operatorStack.isEmpty()) { if (operatorStack.top().value == "(" || operatorStack.top().value == ")") { - logError("Mismatched parentheses detected in expression!"); + recordError("Mismatched parentheses detected in expression!"); } else { output.append(operatorStack.pop()); } @@ -230,7 +250,7 @@ QString ParseUtil::readCIncbin(const QString &filename, const QString &label) { return path; } - text = readTextFile(root + "/" + filename); + this->text = readTextFile(this->root + "/" + filename); QRegularExpression re(QString( "\\b%1\\b" @@ -239,7 +259,7 @@ QString ParseUtil::readCIncbin(const QString &filename, const QString &label) { "\\(\\s*\"([^\"]*)\"\\s*\\)").arg(label)); QRegularExpressionMatch match; - qsizetype pos = text.indexOf(re, 0, &match); + qsizetype pos = this->text.indexOf(re, 0, &match); if (pos != -1) { path = match.captured(1); } @@ -253,36 +273,40 @@ QMap ParseUtil::readCDefines(const QString &filename, { QMap filteredDefines; - file = filename; + this->file = filename; - if (file.isEmpty()) { + if (this->file.isEmpty()) { return filteredDefines; } - QString filepath = root + "/" + file; - text = readTextFile(filepath); + QString filepath = this->root + "/" + this->file; + this->text = readTextFile(filepath); - if (text.isNull()) { + if (this->text.isNull()) { logError(QString("Failed to read C defines file: '%1'").arg(filepath)); return filteredDefines; } - text.replace(QRegularExpression("(//.*)|(\\/+\\*+[^*]*\\*+\\/+)"), ""); - text.replace(QRegularExpression("(\\\\\\s+)"), ""); + this->text.replace(QRegularExpression("(//.*)|(\\/+\\*+[^*]*\\*+\\/+)"), ""); + this->text.replace(QRegularExpression("(\\\\\\s+)"), ""); allDefines.insert("FALSE", 0); allDefines.insert("TRUE", 1); QRegularExpression re("#define\\s+(?\\w+)[^\\S\\n]+(?.+)"); - QRegularExpressionMatchIterator iter = re.globalMatch(text); + QRegularExpressionMatchIterator iter = re.globalMatch(this->text); + this->errorMap.clear(); while (iter.hasNext()) { QRegularExpressionMatch match = iter.next(); QString name = match.captured("defineName"); QString expression = match.captured("defineValue"); if (expression == " ") continue; + this->curDefine = name; int value = evaluateDefine(expression, allDefines); allDefines.insert(name, value); for (QString prefix : prefixes) { if (name.startsWith(prefix) || QRegularExpression(prefix).match(name).hasMatch()) { + // Only log errors for defines that Porymap is looking for + logRecordedErrors(); filteredDefines.insert(name, value); } } @@ -296,7 +320,7 @@ QStringList ParseUtil::readCDefinesSorted(const QString &filename, { QMap defines = readCDefines(filename, prefixes, knownDefines); - // The defines should to be sorted by their underlying value, not alphabetically. + // The defines should be sorted by their underlying value, not alphabetically. // Reverse the map and read out the resulting keys in order. QMultiMap definesInverse; for (QString defineName : defines.keys()) { @@ -312,11 +336,11 @@ QStringList ParseUtil::readCArray(const QString &filename, const QString &label) return list; } - file = filename; - text = readTextFile(root + "/" + filename); + this->file = filename; + this->text = readTextFile(this->root + "/" + filename); QRegularExpression re(QString(R"(\b%1\b\s*(\[?[^\]]*\])?\s*=\s*\{([^\}]*)\})").arg(label)); - QRegularExpressionMatch match = re.match(text); + QRegularExpressionMatch match = re.match(this->text); if (match.hasMatch()) { QString body = match.captured(2); @@ -332,11 +356,11 @@ QStringList ParseUtil::readCArray(const QString &filename, const QString &label) } QMap ParseUtil::readNamedIndexCArray(const QString &filename, const QString &label) { - text = readTextFile(root + "/" + filename); + this->text = readTextFile(this->root + "/" + filename); QMap map; QRegularExpression re_text(QString(R"(\b%1\b\s*(\[?[^\]]*\])?\s*=\s*\{([^\}]*)\})").arg(label)); - QString body = re_text.match(text).captured(2).replace(QRegularExpression("\\s*"), ""); + QString body = re_text.match(this->text).captured(2).replace(QRegularExpression("\\s*"), ""); QRegularExpression re("\\[(?[A-Za-z0-9_]*)\\]=(?&?[A-Za-z0-9_]*)"); QRegularExpressionMatchIterator iter = re.globalMatch(body); diff --git a/src/project.cpp b/src/project.cpp index e9857954..fb55cead 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -2221,15 +2221,10 @@ bool Project::readSecretBaseIds() { if (!projectConfig.getEventSecretBaseEnabled()) return true; - // SECRET_BASE_GROUP is a function-like macro, which Porymap can't handle. - // Redefine it so Porymap won't produce spurious errors about failing to parse it. - QMap knownDefines = QMap(); - knownDefines.insert("SECRET_BASE_GROUP", 0); - QStringList prefixes("\\bSECRET_BASE_[A-Za-z0-9_]*_[0-9]+"); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_secret_bases); fileWatcher.addPath(root + "/" + filename); - secretBaseIds = parser.readCDefinesSorted(filename, prefixes, knownDefines); + secretBaseIds = parser.readCDefinesSorted(filename, prefixes); if (secretBaseIds.isEmpty()) { logError(QString("Failed to read secret base id constants from %1").arg(filename)); return false;