mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Factor out an UpperMap for case-insensitive matching
This commit is contained in:
3
Makefile
3
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
|
||||
|
||||
@@ -3,9 +3,23 @@
|
||||
#ifndef RGBDS_UTIL_HPP
|
||||
#define RGBDS_UTIL_HPP
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#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<typename T>
|
||||
using UpperMap = std::unordered_map<std::string, T, Uppercase, Uppercase>;
|
||||
|
||||
#endif // RGBDS_UTIL_HPP
|
||||
|
||||
@@ -91,6 +91,7 @@ set(rgbgfx_src
|
||||
"gfx/reverse.cpp"
|
||||
"gfx/rgba.cpp"
|
||||
"gfx/warning.cpp"
|
||||
"util.cpp"
|
||||
)
|
||||
|
||||
foreach(PROG "asm" "fix" "gfx" "link")
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
@@ -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<std::string, int, CaseInsensitive, CaseInsensitive> keywordDict = {
|
||||
static UpperMap<int> const keywordDict{
|
||||
{"ADC", T_(SM83_ADC) },
|
||||
{"ADD", T_(SM83_ADD) },
|
||||
{"AND", T_(SM83_AND) },
|
||||
|
||||
@@ -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<StateFeature> parseStateFeatures(char *str) {
|
||||
fatal("Empty feature for option 's'");
|
||||
}
|
||||
// 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 (!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;
|
||||
}
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#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<std::pair<void (*)(char const *, std::filebuf &), bool>> 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<int>(std::min(ptr - arg, static_cast<decltype(ptr - arg)>(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) {
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
#include "link/lexer.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fstream>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#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<SectionType> 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<yy::parser::symbol_type (*)()> 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());
|
||||
|
||||
17
src/util.cpp
17
src/util.cpp
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user