Suppress errors for unneeded defines

This commit is contained in:
GriffinR 2022-09-26 00:36:42 -04:00 committed by Marcus Huderle
parent ddf0fe4e11
commit d353164244
3 changed files with 56 additions and 32 deletions

View file

@ -74,10 +74,15 @@ private:
QString root; QString root;
QString text; QString text;
QString file; QString file;
QString curDefine;
QMap<QString, QStringList> errorMap;
QList<Token> tokenizeExpression(QString expression, const QMap<QString, int> &knownIdentifiers); QList<Token> tokenizeExpression(QString expression, const QMap<QString, int> &knownIdentifiers);
QList<Token> generatePostfix(const QList<Token> &tokens); QList<Token> generatePostfix(const QList<Token> &tokens);
int evaluatePostfix(const QList<Token> &postfix); int evaluatePostfix(const QList<Token> &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_incScriptLabel;
static const QRegularExpression re_globalIncScriptLabel; static const QRegularExpression re_globalIncScriptLabel;

View file

@ -16,15 +16,33 @@ void ParseUtil::set_root(const QString &dir) {
this->root = dir; this->root = dir;
} }
void ParseUtil::error(const QString &message, const QString &expression) { void ParseUtil::recordError(const QString &message) {
QStringList lines = text.split(QRegularExpression("[\r\n]")); 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; int lineNum = 0, colNum = 0;
for (QString line : lines) { for (QString line : lines) {
lineNum++; lineNum++;
colNum = line.indexOf(expression) + 1; colNum = line.indexOf(expression) + 1;
if (colNum) break; 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) { QString ParseUtil::readTextFile(const QString &path) {
@ -52,8 +70,8 @@ int ParseUtil::textFileLineCount(const QString &path) {
QList<QStringList> ParseUtil::parseAsm(const QString &filename) { QList<QStringList> ParseUtil::parseAsm(const QString &filename) {
QList<QStringList> parsed; QList<QStringList> parsed;
text = readTextFile(root + '/' + filename); this->text = readTextFile(this->root + '/' + filename);
const QStringList lines = removeLineComments(text, "@").split('\n'); const QStringList lines = removeLineComments(this->text, "@").split('\n');
for (const auto &line : lines) { for (const auto &line : lines) {
const QString trimmedLine = line.trimmed(); const QString trimmedLine = line.trimmed();
if (trimmedLine.isEmpty()) { if (trimmedLine.isEmpty()) {
@ -101,6 +119,8 @@ QList<Token> ParseUtil::tokenizeExpression(QString expression, const QMap<QStrin
if (!token.isEmpty()) { if (!token.isEmpty()) {
if (tokenType == "identifier") { if (tokenType == "identifier") {
if (knownIdentifiers.contains(token)) { if (knownIdentifiers.contains(token)) {
// Any errors encountered when this identifier was evaluated should be recorded for this expression as well.
recordErrors(this->errorMap.value(token));
QString actualToken = QString("%1").arg(knownIdentifiers.value(token)); QString actualToken = QString("%1").arg(knownIdentifiers.value(token));
expression = expression.replace(0, token.length(), actualToken); expression = expression.replace(0, token.length(), actualToken);
token = actualToken; token = actualToken;
@ -109,14 +129,14 @@ QList<Token> ParseUtil::tokenizeExpression(QString expression, const QMap<QStrin
tokenType = "error"; tokenType = "error";
QString message = QString("unknown token '%1' found in expression '%2'") QString message = QString("unknown token '%1' found in expression '%2'")
.arg(token).arg(expression); .arg(token).arg(expression);
error(message, expression); recordError(createErrorMessage(message, expression));
} }
} }
else if (tokenType == "operator") { else if (tokenType == "operator") {
if (!Token::precedenceMap.contains(token)) { if (!Token::precedenceMap.contains(token)) {
QString message = QString("unsupported postfix operator: '%1'") QString message = QString("unsupported postfix operator: '%1'")
.arg(token); .arg(token);
error(message, expression); recordError(createErrorMessage(message, expression));
} }
} }
@ -161,7 +181,7 @@ QList<Token> ParseUtil::generatePostfix(const QList<Token> &tokens) {
// pop the left parenthesis token // pop the left parenthesis token
operatorStack.pop(); operatorStack.pop();
} else { } else {
logError("Mismatched parentheses detected in expression!"); recordError("Mismatched parentheses detected in expression!");
} }
} else { } else {
// token is an operator // token is an operator
@ -176,7 +196,7 @@ QList<Token> ParseUtil::generatePostfix(const QList<Token> &tokens) {
while (!operatorStack.isEmpty()) { while (!operatorStack.isEmpty()) {
if (operatorStack.top().value == "(" || operatorStack.top().value == ")") { if (operatorStack.top().value == "(" || operatorStack.top().value == ")") {
logError("Mismatched parentheses detected in expression!"); recordError("Mismatched parentheses detected in expression!");
} else { } else {
output.append(operatorStack.pop()); output.append(operatorStack.pop());
} }
@ -230,7 +250,7 @@ QString ParseUtil::readCIncbin(const QString &filename, const QString &label) {
return path; return path;
} }
text = readTextFile(root + "/" + filename); this->text = readTextFile(this->root + "/" + filename);
QRegularExpression re(QString( QRegularExpression re(QString(
"\\b%1\\b" "\\b%1\\b"
@ -239,7 +259,7 @@ QString ParseUtil::readCIncbin(const QString &filename, const QString &label) {
"\\(\\s*\"([^\"]*)\"\\s*\\)").arg(label)); "\\(\\s*\"([^\"]*)\"\\s*\\)").arg(label));
QRegularExpressionMatch match; QRegularExpressionMatch match;
qsizetype pos = text.indexOf(re, 0, &match); qsizetype pos = this->text.indexOf(re, 0, &match);
if (pos != -1) { if (pos != -1) {
path = match.captured(1); path = match.captured(1);
} }
@ -253,36 +273,40 @@ QMap<QString, int> ParseUtil::readCDefines(const QString &filename,
{ {
QMap<QString, int> filteredDefines; QMap<QString, int> filteredDefines;
file = filename; this->file = filename;
if (file.isEmpty()) { if (this->file.isEmpty()) {
return filteredDefines; return filteredDefines;
} }
QString filepath = root + "/" + file; QString filepath = this->root + "/" + this->file;
text = readTextFile(filepath); this->text = readTextFile(filepath);
if (text.isNull()) { if (this->text.isNull()) {
logError(QString("Failed to read C defines file: '%1'").arg(filepath)); logError(QString("Failed to read C defines file: '%1'").arg(filepath));
return filteredDefines; return filteredDefines;
} }
text.replace(QRegularExpression("(//.*)|(\\/+\\*+[^*]*\\*+\\/+)"), ""); this->text.replace(QRegularExpression("(//.*)|(\\/+\\*+[^*]*\\*+\\/+)"), "");
text.replace(QRegularExpression("(\\\\\\s+)"), ""); this->text.replace(QRegularExpression("(\\\\\\s+)"), "");
allDefines.insert("FALSE", 0); allDefines.insert("FALSE", 0);
allDefines.insert("TRUE", 1); allDefines.insert("TRUE", 1);
QRegularExpression re("#define\\s+(?<defineName>\\w+)[^\\S\\n]+(?<defineValue>.+)"); QRegularExpression re("#define\\s+(?<defineName>\\w+)[^\\S\\n]+(?<defineValue>.+)");
QRegularExpressionMatchIterator iter = re.globalMatch(text); QRegularExpressionMatchIterator iter = re.globalMatch(this->text);
this->errorMap.clear();
while (iter.hasNext()) { while (iter.hasNext()) {
QRegularExpressionMatch match = iter.next(); QRegularExpressionMatch match = iter.next();
QString name = match.captured("defineName"); QString name = match.captured("defineName");
QString expression = match.captured("defineValue"); QString expression = match.captured("defineValue");
if (expression == " ") continue; if (expression == " ") continue;
this->curDefine = name;
int value = evaluateDefine(expression, allDefines); int value = evaluateDefine(expression, allDefines);
allDefines.insert(name, value); allDefines.insert(name, value);
for (QString prefix : prefixes) { for (QString prefix : prefixes) {
if (name.startsWith(prefix) || QRegularExpression(prefix).match(name).hasMatch()) { if (name.startsWith(prefix) || QRegularExpression(prefix).match(name).hasMatch()) {
// Only log errors for defines that Porymap is looking for
logRecordedErrors();
filteredDefines.insert(name, value); filteredDefines.insert(name, value);
} }
} }
@ -296,7 +320,7 @@ QStringList ParseUtil::readCDefinesSorted(const QString &filename,
{ {
QMap<QString, int> defines = readCDefines(filename, prefixes, knownDefines); QMap<QString, int> 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. // Reverse the map and read out the resulting keys in order.
QMultiMap<int, QString> definesInverse; QMultiMap<int, QString> definesInverse;
for (QString defineName : defines.keys()) { for (QString defineName : defines.keys()) {
@ -312,11 +336,11 @@ QStringList ParseUtil::readCArray(const QString &filename, const QString &label)
return list; return list;
} }
file = filename; this->file = filename;
text = readTextFile(root + "/" + filename); this->text = readTextFile(this->root + "/" + filename);
QRegularExpression re(QString(R"(\b%1\b\s*(\[?[^\]]*\])?\s*=\s*\{([^\}]*)\})").arg(label)); 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()) { if (match.hasMatch()) {
QString body = match.captured(2); QString body = match.captured(2);
@ -332,11 +356,11 @@ QStringList ParseUtil::readCArray(const QString &filename, const QString &label)
} }
QMap<QString, QString> ParseUtil::readNamedIndexCArray(const QString &filename, const QString &label) { QMap<QString, QString> ParseUtil::readNamedIndexCArray(const QString &filename, const QString &label) {
text = readTextFile(root + "/" + filename); this->text = readTextFile(this->root + "/" + filename);
QMap<QString, QString> map; QMap<QString, QString> map;
QRegularExpression re_text(QString(R"(\b%1\b\s*(\[?[^\]]*\])?\s*=\s*\{([^\}]*)\})").arg(label)); 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("\\[(?<index>[A-Za-z0-9_]*)\\]=(?<value>&?[A-Za-z0-9_]*)"); QRegularExpression re("\\[(?<index>[A-Za-z0-9_]*)\\]=(?<value>&?[A-Za-z0-9_]*)");
QRegularExpressionMatchIterator iter = re.globalMatch(body); QRegularExpressionMatchIterator iter = re.globalMatch(body);

View file

@ -2221,15 +2221,10 @@ bool Project::readSecretBaseIds() {
if (!projectConfig.getEventSecretBaseEnabled()) if (!projectConfig.getEventSecretBaseEnabled())
return true; 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<QString, int> knownDefines = QMap<QString, int>();
knownDefines.insert("SECRET_BASE_GROUP", 0);
QStringList prefixes("\\bSECRET_BASE_[A-Za-z0-9_]*_[0-9]+"); QStringList prefixes("\\bSECRET_BASE_[A-Za-z0-9_]*_[0-9]+");
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_secret_bases); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_secret_bases);
fileWatcher.addPath(root + "/" + filename); fileWatcher.addPath(root + "/" + filename);
secretBaseIds = parser.readCDefinesSorted(filename, prefixes, knownDefines); secretBaseIds = parser.readCDefinesSorted(filename, prefixes);
if (secretBaseIds.isEmpty()) { if (secretBaseIds.isEmpty()) {
logError(QString("Failed to read secret base id constants from %1").arg(filename)); logError(QString("Failed to read secret base id constants from %1").arg(filename));
return false; return false;