Stop unnecessary evaluation when parsing constant names

This commit is contained in:
GriffinR 2023-12-11 16:40:40 -05:00
parent 06ff213691
commit c4adcc2963
7 changed files with 69 additions and 40 deletions

View file

@ -44,7 +44,6 @@ The filepath that Porymap expects for each file can be overridden under the ``Pr
include/constants/global.h, yes, no, ``constants_global``, reads ``OBJECT_EVENT_TEMPLATES_COUNT`` include/constants/global.h, yes, no, ``constants_global``, reads ``OBJECT_EVENT_TEMPLATES_COUNT``
include/constants/map_groups.h, no, yes, ``constants_map_groups``, include/constants/map_groups.h, no, yes, ``constants_map_groups``,
include/constants/items.h, yes, no, ``constants_items``, include/constants/items.h, yes, no, ``constants_items``,
include/constants/opponents.h, yes, no, ``constants_opponents``, reads max trainers constant
include/constants/flags.h, yes, no, ``constants_flags``, include/constants/flags.h, yes, no, ``constants_flags``,
include/constants/vars.h, yes, no, ``constants_vars``, include/constants/vars.h, yes, no, ``constants_vars``,
include/constants/weather.h, yes, no, ``constants_weather``, include/constants/weather.h, yes, no, ``constants_weather``,

View file

@ -196,7 +196,6 @@ enum ProjectFilePath {
constants_global, constants_global,
constants_map_groups, constants_map_groups,
constants_items, constants_items,
constants_opponents,
constants_flags, constants_flags,
constants_vars, constants_vars,
constants_weather, constants_weather,

View file

@ -56,7 +56,8 @@ public:
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> readCDefines(const QString &filename, const QStringList &prefixes, QMap<QString, int> = { }); QMap<QString, int> readCDefines(const QString &filename, const QStringList &prefixes, QMap<QString, int> = { });
QStringList readCDefinesSorted(const QString&, const QStringList&, const QMap<QString, int>& = { }); QStringList readCDefineNames(const QString&, const QStringList&);
QStringList readCDefineNamesByValue(const QString&, const QStringList&, const QMap<QString, int>& = { });
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&);
@ -89,6 +90,7 @@ private:
QString file; QString file;
QString curDefine; QString curDefine;
QHash<QString, QStringList> errorMap; QHash<QString, QStringList> errorMap;
QString readCDefinesFile(const QString &filename);
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);

View file

@ -42,7 +42,6 @@ const QMap<ProjectFilePath, std::pair<QString, QString>> ProjectConfig::defaultP
{ProjectFilePath::constants_global, { "constants_global", "include/constants/global.h"}}, {ProjectFilePath::constants_global, { "constants_global", "include/constants/global.h"}},
{ProjectFilePath::constants_map_groups, { "constants_map_groups", "include/constants/map_groups.h"}}, {ProjectFilePath::constants_map_groups, { "constants_map_groups", "include/constants/map_groups.h"}},
{ProjectFilePath::constants_items, { "constants_items", "include/constants/items.h"}}, {ProjectFilePath::constants_items, { "constants_items", "include/constants/items.h"}},
{ProjectFilePath::constants_opponents, { "constants_opponents", "include/constants/opponents.h"}},
{ProjectFilePath::constants_flags, { "constants_flags", "include/constants/flags.h"}}, {ProjectFilePath::constants_flags, { "constants_flags", "include/constants/flags.h"}},
{ProjectFilePath::constants_vars, { "constants_vars", "include/constants/vars.h"}}, {ProjectFilePath::constants_vars, { "constants_vars", "include/constants/vars.h"}},
{ProjectFilePath::constants_weather, { "constants_weather", "include/constants/weather.h"}}, {ProjectFilePath::constants_weather, { "constants_weather", "include/constants/weather.h"}},

View file

@ -334,16 +334,12 @@ QStringList ParseUtil::readCIncbinArray(const QString &filename, const QString &
return paths; return paths;
} }
QMap<QString, int> ParseUtil::readCDefines(const QString &filename, QString ParseUtil::readCDefinesFile(const QString &filename)
const QStringList &prefixes,
QMap<QString, int> allDefines)
{ {
QMap<QString, int> filteredDefines;
this->file = filename; this->file = filename;
if (this->file.isEmpty()) { if (this->file.isEmpty()) {
return filteredDefines; return QString();
} }
QString filepath = this->root + "/" + this->file; QString filepath = this->root + "/" + this->file;
@ -351,13 +347,27 @@ QMap<QString, int> ParseUtil::readCDefines(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 filteredDefines; return QString();
} }
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;
}
QMap<QString, int> ParseUtil::readCDefines(const QString &filename,
const QStringList &prefixes,
QMap<QString, int> allDefines)
{
QMap<QString, int> filteredDefines;
this->text = this->readCDefinesFile(filename);
if (this->text.isEmpty()) {
return filteredDefines;
}
allDefines.insert("FALSE", 0); allDefines.insert("FALSE", 0);
allDefines.insert("TRUE", 1); allDefines.insert("TRUE", 1);
@ -383,13 +393,37 @@ QMap<QString, int> ParseUtil::readCDefines(const QString &filename,
return filteredDefines; return filteredDefines;
} }
QStringList ParseUtil::readCDefinesSorted(const QString &filename, // Similar to readCDefines, but for cases where we only need to show a list of define names.
const QStringList &prefixes, // We can skip evaluating each define (and by extension skip reporting any errors from this process).
const QMap<QString, int> &knownDefines) QStringList ParseUtil::readCDefineNames(const QString &filename, const QStringList &prefixes) {
QStringList filteredDefines;
this->text = this->readCDefinesFile(filename);
if (this->text.isEmpty()) {
return filteredDefines;
}
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()) {
filteredDefines.append(name);
}
}
}
return filteredDefines;
}
QStringList ParseUtil::readCDefineNamesByValue(const QString &filename,
const QStringList &prefixes,
const QMap<QString, int> &knownDefines)
{ {
QMap<QString, int> defines = readCDefines(filename, prefixes, knownDefines); QMap<QString, int> defines = readCDefines(filename, prefixes, knownDefines);
// The defines should be sorted by their underlying value, not alphabetically. // The defines should be sorted by their underlying value, not alphabetically or in parse order.
// 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()) {

View file

@ -2069,7 +2069,7 @@ bool Project::readItemNames() {
QStringList prefixes("\\bITEM_(?!(B_)?USE_)"); // Exclude ITEM_USE_ and ITEM_B_USE_ constants QStringList prefixes("\\bITEM_(?!(B_)?USE_)"); // Exclude ITEM_USE_ and ITEM_B_USE_ constants
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_items); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_items);
fileWatcher.addPath(root + "/" + filename); fileWatcher.addPath(root + "/" + filename);
itemNames = parser.readCDefinesSorted(filename, prefixes); itemNames = parser.readCDefineNames(filename, prefixes);
if (itemNames.isEmpty()) { if (itemNames.isEmpty()) {
logError(QString("Failed to read item constants from %1").arg(filename)); logError(QString("Failed to read item constants from %1").arg(filename));
return false; return false;
@ -2078,18 +2078,12 @@ bool Project::readItemNames() {
} }
bool Project::readFlagNames() { bool Project::readFlagNames() {
// First read MAX_TRAINERS_COUNT, used to skip over trainer flags
// If this fails flags may simply be out of order, no need to check for success
QString opponentsFilename = projectConfig.getFilePath(ProjectFilePath::constants_opponents);
fileWatcher.addPath(root + "/" + opponentsFilename);
QMap<QString, int> maxTrainers = parser.readCDefines(opponentsFilename, QStringList() << "\\bMAX_");
// Parse flags
QStringList prefixes("\\bFLAG_"); QStringList prefixes("\\bFLAG_");
QString flagsFilename = projectConfig.getFilePath(ProjectFilePath::constants_flags); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_flags);
fileWatcher.addPath(root + "/" + flagsFilename); fileWatcher.addPath(root + "/" + filename);
flagNames = parser.readCDefinesSorted(flagsFilename, prefixes, maxTrainers); flagNames = parser.readCDefineNames(filename, prefixes);
if (flagNames.isEmpty()) { if (flagNames.isEmpty()) {
logError(QString("Failed to read flag constants from %1").arg(flagsFilename)); logError(QString("Failed to read flag constants from %1").arg(filename));
return false; return false;
} }
return true; return true;
@ -2099,7 +2093,7 @@ bool Project::readVarNames() {
QStringList prefixes("\\bVAR_"); QStringList prefixes("\\bVAR_");
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_vars); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_vars);
fileWatcher.addPath(root + "/" + filename); fileWatcher.addPath(root + "/" + filename);
varNames = parser.readCDefinesSorted(filename, prefixes); varNames = parser.readCDefineNames(filename, prefixes);
if (varNames.isEmpty()) { if (varNames.isEmpty()) {
logError(QString("Failed to read var constants from %1").arg(filename)); logError(QString("Failed to read var constants from %1").arg(filename));
return false; return false;
@ -2111,7 +2105,7 @@ bool Project::readMovementTypes() {
QStringList prefixes("\\bMOVEMENT_TYPE_"); QStringList prefixes("\\bMOVEMENT_TYPE_");
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_obj_event_movement); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_obj_event_movement);
fileWatcher.addPath(root + "/" + filename); fileWatcher.addPath(root + "/" + filename);
movementTypes = parser.readCDefinesSorted(filename, prefixes); movementTypes = parser.readCDefineNames(filename, prefixes);
if (movementTypes.isEmpty()) { if (movementTypes.isEmpty()) {
logError(QString("Failed to read movement type constants from %1").arg(filename)); logError(QString("Failed to read movement type constants from %1").arg(filename));
return false; return false;
@ -2134,7 +2128,7 @@ bool Project::readMapTypes() {
QStringList prefixes("\\bMAP_TYPE_"); QStringList prefixes("\\bMAP_TYPE_");
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_map_types); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_map_types);
fileWatcher.addPath(root + "/" + filename); fileWatcher.addPath(root + "/" + filename);
mapTypes = parser.readCDefinesSorted(filename, prefixes); mapTypes = parser.readCDefineNames(filename, prefixes);
if (mapTypes.isEmpty()) { if (mapTypes.isEmpty()) {
logError(QString("Failed to read map type constants from %1").arg(filename)); logError(QString("Failed to read map type constants from %1").arg(filename));
return false; return false;
@ -2146,7 +2140,7 @@ bool Project::readMapBattleScenes() {
QStringList prefixes("\\bMAP_BATTLE_SCENE_"); QStringList prefixes("\\bMAP_BATTLE_SCENE_");
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_map_types); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_map_types);
fileWatcher.addPath(root + "/" + filename); fileWatcher.addPath(root + "/" + filename);
mapBattleScenes = parser.readCDefinesSorted(filename, prefixes); mapBattleScenes = parser.readCDefineNames(filename, prefixes);
if (mapBattleScenes.isEmpty()) { if (mapBattleScenes.isEmpty()) {
logError(QString("Failed to read map battle scene constants from %1").arg(filename)); logError(QString("Failed to read map battle scene constants from %1").arg(filename));
return false; return false;
@ -2158,7 +2152,7 @@ bool Project::readWeatherNames() {
QStringList prefixes("\\bWEATHER_"); QStringList prefixes("\\bWEATHER_");
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_weather); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_weather);
fileWatcher.addPath(root + "/" + filename); fileWatcher.addPath(root + "/" + filename);
weatherNames = parser.readCDefinesSorted(filename, prefixes); weatherNames = parser.readCDefineNames(filename, prefixes);
if (weatherNames.isEmpty()) { if (weatherNames.isEmpty()) {
logError(QString("Failed to read weather constants from %1").arg(filename)); logError(QString("Failed to read weather constants from %1").arg(filename));
return false; return false;
@ -2173,7 +2167,7 @@ bool Project::readCoordEventWeatherNames() {
QStringList prefixes("\\bCOORD_EVENT_WEATHER_"); QStringList prefixes("\\bCOORD_EVENT_WEATHER_");
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_weather); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_weather);
fileWatcher.addPath(root + "/" + filename); fileWatcher.addPath(root + "/" + filename);
coordEventWeatherNames = parser.readCDefinesSorted(filename, prefixes); coordEventWeatherNames = parser.readCDefineNames(filename, prefixes);
if (coordEventWeatherNames.isEmpty()) { if (coordEventWeatherNames.isEmpty()) {
logWarn(QString("Failed to read coord event weather constants from %1. Disabling Weather Trigger events.").arg(filename)); logWarn(QString("Failed to read coord event weather constants from %1. Disabling Weather Trigger events.").arg(filename));
projectConfig.setEventWeatherTriggerEnabled(false); projectConfig.setEventWeatherTriggerEnabled(false);
@ -2188,7 +2182,7 @@ bool Project::readSecretBaseIds() {
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); secretBaseIds = parser.readCDefineNames(filename, prefixes);
if (secretBaseIds.isEmpty()) { if (secretBaseIds.isEmpty()) {
logWarn(QString("Failed to read secret base id constants from '%1'. Disabling Secret Base events.").arg(filename)); logWarn(QString("Failed to read secret base id constants from '%1'. Disabling Secret Base events.").arg(filename));
projectConfig.setEventSecretBaseEnabled(false); projectConfig.setEventSecretBaseEnabled(false);
@ -2200,7 +2194,7 @@ bool Project::readBgEventFacingDirections() {
QStringList prefixes("\\bBG_EVENT_PLAYER_FACING_"); QStringList prefixes("\\bBG_EVENT_PLAYER_FACING_");
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_event_bg); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_event_bg);
fileWatcher.addPath(root + "/" + filename); fileWatcher.addPath(root + "/" + filename);
bgEventFacingDirections = parser.readCDefinesSorted(filename, prefixes); bgEventFacingDirections = parser.readCDefineNames(filename, prefixes);
if (bgEventFacingDirections.isEmpty()) { if (bgEventFacingDirections.isEmpty()) {
logError(QString("Failed to read bg event facing direction constants from %1").arg(filename)); logError(QString("Failed to read bg event facing direction constants from %1").arg(filename));
return false; return false;
@ -2212,7 +2206,7 @@ bool Project::readTrainerTypes() {
QStringList prefixes("\\bTRAINER_TYPE_"); QStringList prefixes("\\bTRAINER_TYPE_");
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_trainer_types); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_trainer_types);
fileWatcher.addPath(root + "/" + filename); fileWatcher.addPath(root + "/" + filename);
trainerTypes = parser.readCDefinesSorted(filename, prefixes); trainerTypes = parser.readCDefineNames(filename, prefixes);
if (trainerTypes.isEmpty()) { if (trainerTypes.isEmpty()) {
logError(QString("Failed to read trainer type constants from %1").arg(filename)); logError(QString("Failed to read trainer type constants from %1").arg(filename));
return false; return false;
@ -2243,13 +2237,14 @@ bool Project::readSongNames() {
QStringList songDefinePrefixes{ "\\bSE_", "\\bMUS_" }; QStringList songDefinePrefixes{ "\\bSE_", "\\bMUS_" };
QString filename = projectConfig.getFilePath(ProjectFilePath::constants_songs); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_songs);
fileWatcher.addPath(root + "/" + filename); fileWatcher.addPath(root + "/" + filename);
QMap<QString, int> songDefines = parser.readCDefines(filename, songDefinePrefixes); this->songNames = parser.readCDefineNames(filename, songDefinePrefixes);
this->songNames = songDefines.keys();
this->defaultSong = this->songNames.value(0, "MUS_DUMMY"); this->defaultSong = this->songNames.value(0, "MUS_DUMMY");
if (this->songNames.isEmpty()) { if (this->songNames.isEmpty()) {
logError(QString("Failed to read song names from %1.").arg(filename)); logError(QString("Failed to read song names from %1.").arg(filename));
return false; return false;
} }
// Song names don't have a very useful order (esp. if we include SE_* values), so sort them alphabetically.
this->songNames.sort();
return true; return true;
} }
@ -2484,8 +2479,9 @@ bool Project::readSpeciesIconPaths() {
static const QStringList prefixes("\\bSPECIES_"); static const QStringList prefixes("\\bSPECIES_");
const QString constantsFilename = projectConfig.getFilePath(ProjectFilePath::constants_species); const QString constantsFilename = projectConfig.getFilePath(ProjectFilePath::constants_species);
fileWatcher.addPath(root + "/" + constantsFilename); fileWatcher.addPath(root + "/" + constantsFilename);
const QMap<QString, int> defines = parser.readCDefines(constantsFilename, prefixes); // TODO: Suppress errors QStringList speciesNames = parser.readCDefineNames(constantsFilename, prefixes);
const QStringList speciesNames = defines.isEmpty() ? monIconNames.keys() : defines.keys(); if (speciesNames.isEmpty())
speciesNames = monIconNames.keys();
bool missingIcons = false; bool missingIcons = false;
for (auto species : speciesNames) { for (auto species : speciesNames) {

View file

@ -175,7 +175,7 @@ void NewMapPopup::setDefaultSettings(Project *project) {
settings.secondaryTilesetLabel = project->getDefaultSecondaryTilesetLabel(); settings.secondaryTilesetLabel = project->getDefaultSecondaryTilesetLabel();
settings.type = project->mapTypes.at(0); settings.type = project->mapTypes.at(0);
settings.location = project->mapSectionValueToName.values().at(0); settings.location = project->mapSectionValueToName.values().at(0);
settings.song = project->songNames.at(0); settings.song = project->defaultSong;
settings.canFlyTo = false; settings.canFlyTo = false;
settings.showLocationName = true; settings.showLocationName = true;
settings.allowRunning = false; settings.allowRunning = false;