From 75aed1afd51b048cd5bd92109b6f52fa0bdaf5a0 Mon Sep 17 00:00:00 2001 From: Rangi42 Date: Sun, 27 Jul 2025 23:14:52 -0400 Subject: [PATCH] Factor out an `UpperMap` for case-insensitive matching --- Makefile | 3 ++- include/util.hpp | 14 +++++++++++++ src/CMakeLists.txt | 1 + src/asm/lexer.cpp | 22 +------------------ src/asm/main.cpp | 27 ++++++++++++------------ src/gfx/pal_spec.cpp | 37 ++++++++++++++------------------ src/link/lexer.cpp | 50 +++++++++++++++++++------------------------- src/util.cpp | 17 +++++++++++++++ 8 files changed, 87 insertions(+), 84 deletions(-) diff --git a/Makefile b/Makefile index 5d48f209..6a0da441 100644 --- a/Makefile +++ b/Makefile @@ -116,7 +116,8 @@ rgbgfx_obj := \ src/gfx/process.o \ src/gfx/reverse.o \ src/gfx/rgba.o \ - src/gfx/warning.o + src/gfx/warning.o \ + src/util.o rgbasm: ${rgbasm_obj} $Q${CXX} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCXXFLAGS} src/version.cpp diff --git a/include/util.hpp b/include/util.hpp index 69abdc5b..f84babae 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -3,9 +3,23 @@ #ifndef RGBDS_UTIL_HPP #define RGBDS_UTIL_HPP +#include +#include +#include + +#include "helpers.hpp" + bool startsIdentifier(int c); bool continuesIdentifier(int c); char const *printChar(int c); +struct Uppercase { + size_t operator()(std::string const &str) const; + bool operator()(std::string const &str1, std::string const &str2) const; +}; + +template +using UpperMap = std::unordered_map; + #endif // RGBDS_UTIL_HPP diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f163cf5..5d69c7ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -91,6 +91,7 @@ set(rgbgfx_src "gfx/reverse.cpp" "gfx/rgba.cpp" "gfx/warning.cpp" + "util.cpp" ) foreach(PROG "asm" "fix" "gfx" "link") diff --git a/src/asm/lexer.cpp b/src/asm/lexer.cpp index f285d38c..9224031b 100644 --- a/src/asm/lexer.cpp +++ b/src/asm/lexer.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -115,28 +114,9 @@ struct Token { Token(int type_, std::string &&value_) : type(type_), value(value_) {} }; -struct CaseInsensitive { - // FNV-1a hash of an uppercased string - size_t operator()(std::string const &str) const { - size_t hash = 0x811C9DC5; - - for (char const &c : str) { - hash = (hash ^ toupper(c)) * 16777619; - } - return hash; - } - - // Compare two strings without case-sensitivity (by converting to uppercase) - bool operator()(std::string const &str1, std::string const &str2) const { - return std::equal(RANGE(str1), RANGE(str2), [](char c1, char c2) { - return toupper(c1) == toupper(c2); - }); - } -}; - // This map lists all RGBASM keywords which `yylex_NORMAL` lexes as identifiers. // All non-identifier tokens are lexed separately. -static std::unordered_map keywordDict = { +static UpperMap const keywordDict{ {"ADC", T_(SM83_ADC) }, {"ADD", T_(SM83_ADD) }, {"AND", T_(SM83_AND) }, diff --git a/src/asm/main.cpp b/src/asm/main.cpp index b9209160..a4c717fd 100644 --- a/src/asm/main.cpp +++ b/src/asm/main.cpp @@ -16,6 +16,7 @@ #include "helpers.hpp" #include "parser.hpp" // Generated from parser.y #include "usage.hpp" +#include "util.hpp" // UpperMap #include "version.hpp" #include "asm/charmap.hpp" @@ -124,25 +125,25 @@ static std::vector parseStateFeatures(char *str) { fatal("Empty feature for option 's'"); } // Parse the `feature` and update the `features` list + static UpperMap const featureNames{ + {"EQU", STATE_EQU }, + {"VAR", STATE_VAR }, + {"EQUS", STATE_EQUS }, + {"CHAR", STATE_CHAR }, + {"MACRO", STATE_MACRO}, + }; if (!strcasecmp(feature, "all")) { if (!features.empty()) { warnx("Redundant feature before \"%s\" for option 's'", feature); } features.assign({STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO}); + } else if (auto search = featureNames.find(feature); search == featureNames.end()) { + fatal("Invalid feature for option 's': \"%s\"", feature); + } else if (StateFeature value = search->second; + std::find(RANGE(features), value) != features.end()) { + warnx("Ignoring duplicate feature for option 's': \"%s\"", feature); } else { - StateFeature value = !strcasecmp(feature, "equ") ? STATE_EQU - : !strcasecmp(feature, "var") ? STATE_VAR - : !strcasecmp(feature, "equs") ? STATE_EQUS - : !strcasecmp(feature, "char") ? STATE_CHAR - : !strcasecmp(feature, "macro") ? STATE_MACRO - : NB_STATE_FEATURES; - if (value == NB_STATE_FEATURES) { - fatal("Invalid feature for option 's': \"%s\"", feature); - } else if (std::find(RANGE(features), value) != features.end()) { - warnx("Ignoring duplicate feature for option 's': \"%s\"", feature); - } else { - features.push_back(value); - } + features.push_back(value); } feature = next; } diff --git a/src/gfx/pal_spec.cpp b/src/gfx/pal_spec.cpp index 3a1ac79b..a8859dbb 100644 --- a/src/gfx/pal_spec.cpp +++ b/src/gfx/pal_spec.cpp @@ -15,11 +15,12 @@ #include #include #include -#include +#include #include "diagnostics.hpp" #include "helpers.hpp" #include "platform.hpp" +#include "util.hpp" // UpperMap #include "gfx/main.hpp" #include "gfx/png.hpp" @@ -668,36 +669,30 @@ void parseExternalPalSpec(char const *arg) { } char const *path = ptr + 1; - static std::array parsers{ - std::tuple{"PSP", &parsePSPFile, std::ios::in }, - std::tuple{"GPL", &parseGPLFile, std::ios::in }, - std::tuple{"HEX", &parseHEXFile, std::ios::in }, - std::tuple{"ACT", &parseACTFile, std::ios::binary}, - std::tuple{"ACO", &parseACOFile, std::ios::binary}, - std::tuple{"GBC", &parseGBCFile, std::ios::binary}, - std::tuple{"PNG", &parsePNGFile, std::ios::binary}, + static UpperMap> const parsers{ + {"PSP", std::pair{&parsePSPFile, false}}, + {"GPL", std::pair{&parseGPLFile, false}}, + {"HEX", std::pair{&parseHEXFile, false}}, + {"ACT", std::pair{&parseACTFile, true} }, + {"ACO", std::pair{&parseACOFile, true} }, + {"GBC", std::pair{&parseGBCFile, true} }, + {"PNG", std::pair{&parsePNGFile, true} }, }; - - auto iter = std::find_if(RANGE(parsers), [&arg, &ptr](auto const &parser) { - return strncasecmp(arg, std::get<0>(parser), ptr - arg) == 0; - }); - if (iter == parsers.end()) { - error( - "Unknown external palette format \"%.*s\"", - static_cast(std::min(ptr - arg, static_cast(INT_MAX))), - arg - ); + std::string format{arg, ptr}; + auto search = parsers.find(format); + if (search == parsers.end()) { + error("Unknown external palette format \"%s\"", format.c_str()); return; } std::filebuf file; // Some parsers read the file in text mode, others in binary mode - if (!file.open(path, std::ios::in | std::get<2>(*iter))) { + if (!file.open(path, search->second.second ? std::ios::in | std::ios::binary : std::ios::in)) { fatal("Failed to open palette file \"%s\": %s", path, strerror(errno)); return; } - std::get<1> (*iter)(path, file); + search->second.first(path, file); } void parseDmgPalSpec(char const * const rawArg) { diff --git a/src/link/lexer.cpp b/src/link/lexer.cpp index 382c0541..9cfeb851 100644 --- a/src/link/lexer.cpp +++ b/src/link/lexer.cpp @@ -3,12 +3,10 @@ #include "link/lexer.hpp" #include -#include #include #include #include #include -#include #include #include "helpers.hpp" @@ -265,22 +263,6 @@ static yy::parser::symbol_type parseString() { return yy::parser::make_string(std::move(str)); } -struct Keyword { - std::string_view name; - yy::parser::symbol_type (*tokenGen)(); -}; - -using namespace std::literals; - -static std::array keywords{ - Keyword{"ORG"sv, yy::parser::make_ORG }, - Keyword{"FLOATING"sv, yy::parser::make_FLOATING}, - Keyword{"INCLUDE"sv, yy::parser::make_INCLUDE }, - Keyword{"ALIGN"sv, yy::parser::make_ALIGN }, - Keyword{"DS"sv, yy::parser::make_DS }, - Keyword{"OPTIONAL"sv, yy::parser::make_OPTIONAL}, -}; - yy::parser::symbol_type yylex() { LexerStackEntry &context = lexerStack.back(); int c = context.file.sbumpc(); @@ -320,18 +302,30 @@ yy::parser::symbol_type yylex() { } else if (isIdentChar(c)) { // Note that we match these *after* digit characters! std::string ident = readIdent(c); - auto strUpperCmp = [](char cmp, char ref) { return toupper(cmp) == ref; }; - - for (SectionType type : EnumSeq(SECTTYPE_INVALID)) { - if (std::equal(RANGE(ident), RANGE(sectionTypeInfo[type].name), strUpperCmp)) { - return yy::parser::make_sect_type(type); - } + static UpperMap const sectTypes{ + {"WRAM0", SECTTYPE_WRAM0}, + {"VRAM", SECTTYPE_VRAM }, + {"ROMX", SECTTYPE_ROMX }, + {"ROM0", SECTTYPE_ROM0 }, + {"HRAM", SECTTYPE_HRAM }, + {"WRAMX", SECTTYPE_WRAMX}, + {"SRAM", SECTTYPE_SRAM }, + {"OAM", SECTTYPE_OAM }, + }; + if (auto search = sectTypes.find(ident); search != sectTypes.end()) { + return yy::parser::make_sect_type(search->second); } - for (Keyword const &keyword : keywords) { - if (std::equal(RANGE(ident), RANGE(keyword.name), strUpperCmp)) { - return keyword.tokenGen(); - } + static UpperMap const keywords{ + {"ORG", yy::parser::make_ORG }, + {"FLOATING", yy::parser::make_FLOATING}, + {"INCLUDE", yy::parser::make_INCLUDE }, + {"ALIGN", yy::parser::make_ALIGN }, + {"DS", yy::parser::make_DS }, + {"OPTIONAL", yy::parser::make_OPTIONAL}, + }; + if (auto search = keywords.find(ident); search != keywords.end()) { + return search->second(); } lexer_Error("Unknown keyword \"%s\"", ident.c_str()); diff --git a/src/util.cpp b/src/util.cpp index c4127592..ad730203 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -2,6 +2,7 @@ #include "util.hpp" +#include #include #include #include @@ -60,3 +61,19 @@ char const *printChar(int c) { buf[4] = '\0'; return buf; } + +// FNV-1a hash of an uppercased string +size_t Uppercase::operator()(std::string const &str) const { + size_t hash = 0x811C9DC5; + for (char const &c : str) { + hash = (hash ^ toupper(c)) * 16777619; + } + return hash; +} + +// Compare two strings without case-sensitivity (by converting to uppercase) +bool Uppercase::operator()(std::string const &str1, std::string const &str2) const { + return std::equal(RANGE(str1), RANGE(str2), [](char c1, char c2) { + return toupper(c1) == toupper(c2); + }); +}