Factor out an UpperMap for case-insensitive matching

This commit is contained in:
Rangi42
2025-07-27 23:14:52 -04:00
parent d16751f56a
commit 75aed1afd5
8 changed files with 87 additions and 84 deletions

View File

@@ -116,7 +116,8 @@ rgbgfx_obj := \
src/gfx/process.o \ src/gfx/process.o \
src/gfx/reverse.o \ src/gfx/reverse.o \
src/gfx/rgba.o \ src/gfx/rgba.o \
src/gfx/warning.o src/gfx/warning.o \
src/util.o
rgbasm: ${rgbasm_obj} rgbasm: ${rgbasm_obj}
$Q${CXX} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCXXFLAGS} src/version.cpp $Q${CXX} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCXXFLAGS} src/version.cpp

View File

@@ -3,9 +3,23 @@
#ifndef RGBDS_UTIL_HPP #ifndef RGBDS_UTIL_HPP
#define RGBDS_UTIL_HPP #define RGBDS_UTIL_HPP
#include <stddef.h>
#include <string>
#include <unordered_map>
#include "helpers.hpp"
bool startsIdentifier(int c); bool startsIdentifier(int c);
bool continuesIdentifier(int c); bool continuesIdentifier(int c);
char const *printChar(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<typename T>
using UpperMap = std::unordered_map<std::string, T, Uppercase, Uppercase>;
#endif // RGBDS_UTIL_HPP #endif // RGBDS_UTIL_HPP

View File

@@ -91,6 +91,7 @@ set(rgbgfx_src
"gfx/reverse.cpp" "gfx/reverse.cpp"
"gfx/rgba.cpp" "gfx/rgba.cpp"
"gfx/warning.cpp" "gfx/warning.cpp"
"util.cpp"
) )
foreach(PROG "asm" "fix" "gfx" "link") foreach(PROG "asm" "fix" "gfx" "link")

View File

@@ -5,7 +5,6 @@
#include <sys/types.h> #include <sys/types.h>
#include <algorithm> #include <algorithm>
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <inttypes.h> #include <inttypes.h>
@@ -115,28 +114,9 @@ struct Token {
Token(int type_, std::string &&value_) : type(type_), value(value_) {} 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. // This map lists all RGBASM keywords which `yylex_NORMAL` lexes as identifiers.
// All non-identifier tokens are lexed separately. // All non-identifier tokens are lexed separately.
static std::unordered_map<std::string, int, CaseInsensitive, CaseInsensitive> keywordDict = { static UpperMap<int> const keywordDict{
{"ADC", T_(SM83_ADC) }, {"ADC", T_(SM83_ADC) },
{"ADD", T_(SM83_ADD) }, {"ADD", T_(SM83_ADD) },
{"AND", T_(SM83_AND) }, {"AND", T_(SM83_AND) },

View File

@@ -16,6 +16,7 @@
#include "helpers.hpp" #include "helpers.hpp"
#include "parser.hpp" // Generated from parser.y #include "parser.hpp" // Generated from parser.y
#include "usage.hpp" #include "usage.hpp"
#include "util.hpp" // UpperMap
#include "version.hpp" #include "version.hpp"
#include "asm/charmap.hpp" #include "asm/charmap.hpp"
@@ -124,25 +125,25 @@ static std::vector<StateFeature> parseStateFeatures(char *str) {
fatal("Empty feature for option 's'"); fatal("Empty feature for option 's'");
} }
// Parse the `feature` and update the `features` list // Parse the `feature` and update the `features` list
static UpperMap<StateFeature> const featureNames{
{"EQU", STATE_EQU },
{"VAR", STATE_VAR },
{"EQUS", STATE_EQUS },
{"CHAR", STATE_CHAR },
{"MACRO", STATE_MACRO},
};
if (!strcasecmp(feature, "all")) { if (!strcasecmp(feature, "all")) {
if (!features.empty()) { if (!features.empty()) {
warnx("Redundant feature before \"%s\" for option 's'", feature); warnx("Redundant feature before \"%s\" for option 's'", feature);
} }
features.assign({STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO}); 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 { } else {
StateFeature value = !strcasecmp(feature, "equ") ? STATE_EQU features.push_back(value);
: !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);
}
} }
feature = next; feature = next;
} }

View File

@@ -15,11 +15,12 @@
#include <string.h> #include <string.h>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <tuple> #include <utility>
#include "diagnostics.hpp" #include "diagnostics.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "platform.hpp" #include "platform.hpp"
#include "util.hpp" // UpperMap
#include "gfx/main.hpp" #include "gfx/main.hpp"
#include "gfx/png.hpp" #include "gfx/png.hpp"
@@ -668,36 +669,30 @@ void parseExternalPalSpec(char const *arg) {
} }
char const *path = ptr + 1; char const *path = ptr + 1;
static std::array parsers{ static UpperMap<std::pair<void (*)(char const *, std::filebuf &), bool>> const parsers{
std::tuple{"PSP", &parsePSPFile, std::ios::in }, {"PSP", std::pair{&parsePSPFile, false}},
std::tuple{"GPL", &parseGPLFile, std::ios::in }, {"GPL", std::pair{&parseGPLFile, false}},
std::tuple{"HEX", &parseHEXFile, std::ios::in }, {"HEX", std::pair{&parseHEXFile, false}},
std::tuple{"ACT", &parseACTFile, std::ios::binary}, {"ACT", std::pair{&parseACTFile, true} },
std::tuple{"ACO", &parseACOFile, std::ios::binary}, {"ACO", std::pair{&parseACOFile, true} },
std::tuple{"GBC", &parseGBCFile, std::ios::binary}, {"GBC", std::pair{&parseGBCFile, true} },
std::tuple{"PNG", &parsePNGFile, std::ios::binary}, {"PNG", std::pair{&parsePNGFile, true} },
}; };
std::string format{arg, ptr};
auto iter = std::find_if(RANGE(parsers), [&arg, &ptr](auto const &parser) { auto search = parsers.find(format);
return strncasecmp(arg, std::get<0>(parser), ptr - arg) == 0; if (search == parsers.end()) {
}); error("Unknown external palette format \"%s\"", format.c_str());
if (iter == parsers.end()) {
error(
"Unknown external palette format \"%.*s\"",
static_cast<int>(std::min(ptr - arg, static_cast<decltype(ptr - arg)>(INT_MAX))),
arg
);
return; return;
} }
std::filebuf file; std::filebuf file;
// Some parsers read the file in text mode, others in binary mode // 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)); fatal("Failed to open palette file \"%s\": %s", path, strerror(errno));
return; return;
} }
std::get<1> (*iter)(path, file); search->second.first(path, file);
} }
void parseDmgPalSpec(char const * const rawArg) { void parseDmgPalSpec(char const * const rawArg) {

View File

@@ -3,12 +3,10 @@
#include "link/lexer.hpp" #include "link/lexer.hpp"
#include <array> #include <array>
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fstream> #include <fstream>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <string_view>
#include <vector> #include <vector>
#include "helpers.hpp" #include "helpers.hpp"
@@ -265,22 +263,6 @@ static yy::parser::symbol_type parseString() {
return yy::parser::make_string(std::move(str)); 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() { yy::parser::symbol_type yylex() {
LexerStackEntry &context = lexerStack.back(); LexerStackEntry &context = lexerStack.back();
int c = context.file.sbumpc(); 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! } else if (isIdentChar(c)) { // Note that we match these *after* digit characters!
std::string ident = readIdent(c); std::string ident = readIdent(c);
auto strUpperCmp = [](char cmp, char ref) { return toupper(cmp) == ref; }; static UpperMap<SectionType> const sectTypes{
{"WRAM0", SECTTYPE_WRAM0},
for (SectionType type : EnumSeq(SECTTYPE_INVALID)) { {"VRAM", SECTTYPE_VRAM },
if (std::equal(RANGE(ident), RANGE(sectionTypeInfo[type].name), strUpperCmp)) { {"ROMX", SECTTYPE_ROMX },
return yy::parser::make_sect_type(type); {"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) { static UpperMap<yy::parser::symbol_type (*)()> const keywords{
if (std::equal(RANGE(ident), RANGE(keyword.name), strUpperCmp)) { {"ORG", yy::parser::make_ORG },
return keyword.tokenGen(); {"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()); lexer_Error("Unknown keyword \"%s\"", ident.c_str());

View File

@@ -2,6 +2,7 @@
#include "util.hpp" #include "util.hpp"
#include <algorithm>
#include <ctype.h> #include <ctype.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
@@ -60,3 +61,19 @@ char const *printChar(int c) {
buf[4] = '\0'; buf[4] = '\0';
return buf; 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);
});
}