Initial enum support

This commit is contained in:
GriffinR 2024-08-28 14:42:39 -04:00
parent dc79d5d258
commit 4b0d30fbc4
2 changed files with 90 additions and 58 deletions

View file

@ -54,9 +54,9 @@ public:
QString readCIncbin(const QString &text, const QString &label); QString readCIncbin(const QString &text, const QString &label);
QMap<QString, QString> readCIncbinMulti(const QString &filepath); QMap<QString, QString> readCIncbinMulti(const QString &filepath);
QStringList readCIncbinArray(const QString &filename, const QString &label); QStringList readCIncbinArray(const QString &filename, const QString &label);
QMap<QString, int> readCDefinesByPrefix(const QString &filename, QStringList prefixes); QMap<QString, int> readCDefinesByPrefix(const QString &filename, QStringList searchText);
QMap<QString, int> readCDefinesByName(const QString &filename, QStringList names); QMap<QString, int> readCDefinesByName(const QString &filename, QStringList names);
QStringList readCDefineNames(const QString&, const QStringList&); QStringList readCDefineNames(const QString&, QStringList searchText);
QMap<QString, QHash<QString, QString>> readCStructs(const QString &, const QString & = "", const QHash<int, QString> = { }); QMap<QString, QHash<QString, QString>> readCStructs(const QString &, const QString & = "", const QHash<int, QString> = { });
QList<QStringList> getLabelMacros(const QList<QStringList>&, const QString&); QList<QStringList> getLabelMacros(const QList<QStringList>&, const QString&);
QStringList getLabelValues(const QList<QStringList>&, const QString&); QStringList getLabelValues(const QList<QStringList>&, const QString&);
@ -97,8 +97,9 @@ private:
void recordErrors(const QStringList &errors); void recordErrors(const QStringList &errors);
void logRecordedErrors(); void logRecordedErrors();
QString createErrorMessage(const QString &message, const QString &expression); QString createErrorMessage(const QString &message, const QString &expression);
QString readCDefinesFile(const QString &filename); QMap<QString, QString> readCDefineExpressions(const QString &filename);
QMap<QString, int> readCDefines(const QString &filename, const QStringList &searchText, bool fullMatch); QMap<QString,QString> filterCDefineExpressions(const QMap<QString,QString> &allExpressions, QStringList searchText, bool fullMatch);
QMap<QString, int> evaluateCDefines(const QString &filename, const QStringList &searchText, bool fullMatch);
static const QRegularExpression re_incScriptLabel; static const QRegularExpression re_incScriptLabel;
static const QRegularExpression re_globalIncScriptLabel; static const QRegularExpression re_globalIncScriptLabel;

View file

@ -15,6 +15,22 @@ const QRegularExpression ParseUtil::re_poryScriptLabel("\\b(script)(\\((global|l
const QRegularExpression ParseUtil::re_globalPoryScriptLabel("\\b(script)(\\((global)\\))?\\s*\\b(?<label>[\\w_][\\w\\d_]*)"); const QRegularExpression ParseUtil::re_globalPoryScriptLabel("\\b(script)(\\((global)\\))?\\s*\\b(?<label>[\\w_][\\w\\d_]*)");
const QRegularExpression ParseUtil::re_poryRawSection("\\b(raw)\\s*`(?<raw_script>[^`]*)"); const QRegularExpression ParseUtil::re_poryRawSection("\\b(raw)\\s*`(?<raw_script>[^`]*)");
static const QMap<QString, int> defaultDefineValues = {
{"FALSE", 0},
{"TRUE", 1},
{"SCHAR_MIN", SCHAR_MIN},
{"SCHAR_MAX", SCHAR_MAX},
{"CHAR_MIN", CHAR_MIN},
{"CHAR_MAX", CHAR_MAX},
{"UCHAR_MAX", UCHAR_MAX},
{"SHRT_MIN", SHRT_MIN},
{"SHRT_MAX", SHRT_MAX},
{"USHRT_MAX", USHRT_MAX},
{"INT_MIN", INT_MIN},
{"INT_MAX", INT_MAX},
{"UINT_MAX", UINT_MAX},
};
using OrderedJson = poryjson::Json; using OrderedJson = poryjson::Json;
ParseUtil::ParseUtil() { } ParseUtil::ParseUtil() { }
@ -353,12 +369,14 @@ QStringList ParseUtil::readCIncbinArray(const QString &filename, const QString &
return paths; return paths;
} }
QString ParseUtil::readCDefinesFile(const QString &filename) // Open the file and return all the names and expressions defined by #define or enum
QMap<QString,QString> ParseUtil::readCDefineExpressions(const QString &filename)
{ {
QMap<QString, QString> expressions;
this->file = filename; this->file = filename;
if (this->file.isEmpty()) { if (this->file.isEmpty()) {
return QString(); return expressions;
} }
QString filepath = this->root + "/" + this->file; QString filepath = this->root + "/" + this->file;
@ -366,50 +384,82 @@ QString ParseUtil::readCDefinesFile(const QString &filename)
if (this->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 QString(); return expressions;
} }
static const QRegularExpression re_extraChars("(//.*)|(\\/+\\*+[^*]*\\*+\\/+)"); static const QRegularExpression re_extraChars("(//.*)|(\\/+\\*+[^*]*\\*+\\/+)");
this->text.replace(re_extraChars, ""); this->text.replace(re_extraChars, "");
static const QRegularExpression re_extraSpaces("(\\\\\\s+)"); static const QRegularExpression re_extraSpaces("(\\\\\\s+)");
this->text.replace(re_extraSpaces, ""); this->text.replace(re_extraSpaces, "");
return this->text;
}
// Read all the define names and their expressions in the specified file, then evaluate the ones matching the search text (and any they depend on). if (this->text.isEmpty())
// If 'fullMatch' is true, 'searchText' is a list of exact define names to evaluate and return. return expressions;
// If 'fullMatch' is false, 'searchText' is a list of prefixes or regexes for define names to evaluate and return.
QMap<QString, int> ParseUtil::readCDefines(const QString &filename, const QStringList &searchText, bool fullMatch)
{
QMap<QString, int> filteredValues;
this->text = this->readCDefinesFile(filename); // Capture either the name and value of a #define, or everything between the braces of 'enum { }'
if (this->text.isEmpty()) { static const QRegularExpression re("#define\\s+(?<defineName>\\w+)[\\s\\n][^\\S\\n]*(?<defineValue>.+)?"
return filteredValues; "|\\benum\\b[^{]*{(?<enumBody>[^}]*)}");
}
// Extract all the define names and expressions
QMap<QString, QString> allExpressions;
QMap<QString, QString> filteredExpressions;
static const QRegularExpression re("#define\\s+(?<defineName>\\w+)[^\\S\\n]+(?<defineValue>.+)");
QRegularExpressionMatchIterator iter = re.globalMatch(this->text); QRegularExpressionMatchIterator iter = re.globalMatch(this->text);
while (iter.hasNext()) { while (iter.hasNext()) {
QRegularExpressionMatch match = iter.next(); QRegularExpressionMatch match = iter.next();
const QString name = match.captured("defineName"); const QString enumBody = match.captured("enumBody");
const QString expression = match.captured("defineValue"); if (!enumBody.isNull()) {
// If name matches the search text record it for evaluation. // Encountered an enum, extract the elements of the enum and give each an appropriate expression
int baseNum = 0;
QString baseExpression = "0";
static const QRegularExpression re_enumElement("\\b(?<name>\\w+)\\b\\s*=?\\s*(?<expression>.+)?[,\\$]"); // TODO: Ignores terminal element lacking comma
QRegularExpressionMatchIterator elementIter = re_enumElement.globalMatch(enumBody);
while (elementIter.hasNext()) {
QRegularExpressionMatch elementMatch = elementIter.next();
QString expression = elementMatch.captured("expression");
if (expression.isEmpty()) {
// enum values may use tokens that we don't know how to evaluate yet.
// For now we define each element to be 1 + the previous element's expression.
expression = QString("((%1)+%2)").arg(baseExpression).arg(baseNum++);
} else {
// This element was explicitly assigned an expression with '=', reset the bases for any subsequent elements.
baseExpression = expression;
baseNum = 1;
}
expressions.insert(elementMatch.captured("name"), expression);
}
} else {
// Encountered a #define
expressions.insert(match.captured("defineName"), match.captured("defineValue"));
}
}
return expressions;
}
// Given a map of define names->expressions, returns a map of define names->expressions where all the names match searchText.
// If 'fullMatch' is true, 'searchText' is a list of exact define names to match.
// If 'fullMatch' is false, 'searchText' is a list of prefixes or regexes of define names to match.
QMap<QString,QString> ParseUtil::filterCDefineExpressions(const QMap<QString,QString> &allExpressions, QStringList searchText, bool fullMatch) {
QMap<QString,QString> filteredExpressions;
searchText.removeDuplicates();
for (auto i = allExpressions.cbegin(), end = allExpressions.cend(); i != end; i++) {
const QString name = i.key();
for (auto s : searchText) { for (auto s : searchText) {
if ((fullMatch && name == s) || (!fullMatch && (name.startsWith(s) || QRegularExpression(s).match(name).hasMatch()))) { if ((fullMatch && name == s) || (!fullMatch && (name.startsWith(s) || QRegularExpression(s).match(name).hasMatch()))) {
filteredExpressions.insert(name, expression); filteredExpressions.insert(name, i.value());
break; break;
} }
} }
allExpressions.insert(name, expression);
} }
return filteredExpressions;
}
QMap<QString, int> allValues; // Read all the define names and their expressions in the specified file, then evaluate the ones matching the search text (and any they depend on).
allValues.insert("FALSE", 0); QMap<QString, int> ParseUtil::evaluateCDefines(const QString &filename, const QStringList &searchText, bool fullMatch) {
allValues.insert("TRUE", 1); QMap<QString, int> filteredValues;
QMap<QString, int> allValues = defaultDefineValues;
QMap<QString, QString> allExpressions = this->readCDefineExpressions(filename);
if (allExpressions.isEmpty())
return filteredValues;
QMap<QString, QString> filteredExpressions = this->filterCDefineExpressions(allExpressions, searchText, fullMatch);
// Evaluate defines // Evaluate defines
this->errorMap.clear(); this->errorMap.clear();
@ -425,39 +475,20 @@ QMap<QString, int> ParseUtil::readCDefines(const QString &filename, const QStrin
} }
// Find and evaluate an unknown list of defines with a known name prefix. // Find and evaluate an unknown list of defines with a known name prefix.
QMap<QString, int> ParseUtil::readCDefinesByPrefix(const QString &filename, QStringList prefixes) { QMap<QString, int> ParseUtil::readCDefinesByPrefix(const QString &filename, QStringList searchText) {
prefixes.removeDuplicates(); return this->evaluateCDefines(filename, searchText, false);
return this->readCDefines(filename, prefixes, false);
} }
// Find and evaluate a specific set of defines with known names. // Find and evaluate a specific set of defines with known names.
QMap<QString, int> ParseUtil::readCDefinesByName(const QString &filename, QStringList names) { QMap<QString, int> ParseUtil::readCDefinesByName(const QString &filename, QStringList names) {
names.removeDuplicates(); return this->evaluateCDefines(filename, names, true);
return this->readCDefines(filename, names, true);
} }
// Similar to readCDefines, but for cases where we only need to show a list of define names. // Similar to readCDefinesByPrefix, but for cases where we only need to show a list of define names.
// We can skip reading/evaluating any expressions (and by extension skip reporting any errors from this process). // We can skip evaluating any expressions (and by extension skip reporting any errors from this process).
QStringList ParseUtil::readCDefineNames(const QString &filename, const QStringList &prefixes) { QStringList ParseUtil::readCDefineNames(const QString &filename, QStringList searchText) {
QStringList filteredNames; // TODO: These will now be alphabetical. Is that ok?
return this->filterCDefineExpressions(this->readCDefineExpressions(filename), searchText, false).keys();
this->text = this->readCDefinesFile(filename);
if (this->text.isEmpty()) {
return filteredNames;
}
static const QRegularExpression re("#define\\s+(?<defineName>\\w+)[^\\S\\n]+");
QRegularExpressionMatchIterator iter = re.globalMatch(this->text);
while (iter.hasNext()) {
QRegularExpressionMatch match = iter.next();
QString name = match.captured("defineName");
for (QString prefix : prefixes) {
if (name.startsWith(prefix) || QRegularExpression(prefix).match(name).hasMatch()) {
filteredNames.append(name);
}
}
}
return filteredNames;
} }
QStringList ParseUtil::readCArray(const QString &filename, const QString &label) { QStringList ParseUtil::readCArray(const QString &filename, const QString &label) {