mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Don't count single quote ' as garbage (#1801)
Also copy the "blank space" (space or tab) vs "whitespace" (space, tab, or newline) convention from `<ctype.h>`
This commit is contained in:
3
Makefile
3
Makefile
@@ -109,7 +109,8 @@ rgbfix_obj := \
|
|||||||
${common_obj} \
|
${common_obj} \
|
||||||
src/fix/main.o \
|
src/fix/main.o \
|
||||||
src/fix/mbc.o \
|
src/fix/mbc.o \
|
||||||
src/fix/warning.o
|
src/fix/warning.o \
|
||||||
|
src/util.o
|
||||||
|
|
||||||
rgbgfx_obj := \
|
rgbgfx_obj := \
|
||||||
${common_obj} \
|
${common_obj} \
|
||||||
|
|||||||
@@ -12,8 +12,9 @@
|
|||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
|
||||||
bool isWhitespace(int c);
|
|
||||||
bool isNewline(int c);
|
bool isNewline(int c);
|
||||||
|
bool isBlankSpace(int c);
|
||||||
|
bool isWhitespace(int c);
|
||||||
bool isPrintable(int c);
|
bool isPrintable(int c);
|
||||||
|
|
||||||
bool startsIdentifier(int c);
|
bool startsIdentifier(int c);
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ set(rgbfix_src
|
|||||||
"fix/main.cpp"
|
"fix/main.cpp"
|
||||||
"fix/mbc.cpp"
|
"fix/mbc.cpp"
|
||||||
"fix/warning.cpp"
|
"fix/warning.cpp"
|
||||||
|
"util.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(rgbgfx_src
|
set(rgbgfx_src
|
||||||
|
|||||||
@@ -898,7 +898,7 @@ static void discardComment() {
|
|||||||
|
|
||||||
static void discardLineContinuation() {
|
static void discardLineContinuation() {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (int c = peek(); isWhitespace(c)) {
|
if (int c = peek(); isBlankSpace(c)) {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
} else if (isNewline(c)) {
|
} else if (isNewline(c)) {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
@@ -1561,8 +1561,18 @@ static Token yylex_SKIP_TO_ENDC(); // Forward declaration for `yylex_NORMAL`
|
|||||||
|
|
||||||
// Must stay in sync with the `switch` in `yylex_NORMAL`!
|
// Must stay in sync with the `switch` in `yylex_NORMAL`!
|
||||||
static bool isGarbageCharacter(int c) {
|
static bool isGarbageCharacter(int c) {
|
||||||
return c != EOF && !continuesIdentifier(c)
|
// Whitespace characters are not garbage, even the non-"printable" ones
|
||||||
&& (c == '\0' || !strchr("; \t~[](),+-*/|^=!<>:&%`\"\r\n\\", c));
|
if (isWhitespace(c)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Printable characters which are nevertheless garbage: braces should have been interpolated,
|
||||||
|
// and question mark is unused
|
||||||
|
if (c == '{' || c == '}' || c == '?') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// All other printable characters are not garbage (i.e. `yylex_NORMAL` handles them), and
|
||||||
|
// all other nonprintable characters are garbage (including '\0' and EOF)
|
||||||
|
return !isPrintable(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reportGarbageCharacters(int c) {
|
static void reportGarbageCharacters(int c) {
|
||||||
@@ -1594,7 +1604,7 @@ static Token yylex_NORMAL() {
|
|||||||
int c = bumpChar();
|
int c = bumpChar();
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
// Ignore whitespace and comments
|
// Ignore blank space and comments
|
||||||
|
|
||||||
case ';':
|
case ';':
|
||||||
discardComment();
|
discardComment();
|
||||||
@@ -1949,18 +1959,18 @@ static Token yylex_RAW() {
|
|||||||
size_t parenDepth = 0;
|
size_t parenDepth = 0;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
// Trim left whitespace (stops at a block comment)
|
// Trim left spaces (stops at a block comment)
|
||||||
for (;;) {
|
for (;;) {
|
||||||
c = peek();
|
c = peek();
|
||||||
if (isWhitespace(c)) {
|
if (isBlankSpace(c)) {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
} else if (c == '\\') {
|
} else if (c == '\\') {
|
||||||
c = nextChar();
|
c = nextChar();
|
||||||
// If not a line continuation, handle as a normal char
|
// If not a line continuation, handle as a normal char
|
||||||
if (!isWhitespace(c) && !isNewline(c)) {
|
if (!isWhitespace(c)) {
|
||||||
goto backslash;
|
goto backslash;
|
||||||
}
|
}
|
||||||
// Line continuations count as "whitespace"
|
// Line continuations count as "space"
|
||||||
discardLineContinuation();
|
discardLineContinuation();
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@@ -2083,8 +2093,8 @@ append:
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish: // Can't `break` out of a nested `for`-`switch`
|
finish: // Can't `break` out of a nested `for`-`switch`
|
||||||
// Trim right whitespace
|
// Trim right blank space
|
||||||
auto rightPos = std::find_if_not(str.rbegin(), str.rend(), isWhitespace);
|
auto rightPos = std::find_if_not(str.rbegin(), str.rend(), isBlankSpace);
|
||||||
str.resize(rightPos.base() - str.begin());
|
str.resize(rightPos.base() - str.begin());
|
||||||
|
|
||||||
// Returning COMMAs to the parser would mean that two consecutive commas
|
// Returning COMMAs to the parser would mean that two consecutive commas
|
||||||
@@ -2120,7 +2130,7 @@ finish: // Can't `break` out of a nested `for`-`switch`
|
|||||||
static int skipPastEOL() {
|
static int skipPastEOL() {
|
||||||
if (lexerState->atLineStart) {
|
if (lexerState->atLineStart) {
|
||||||
lexerState->atLineStart = false;
|
lexerState->atLineStart = false;
|
||||||
return skipChars(isWhitespace);
|
return skipChars(isBlankSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -2129,7 +2139,7 @@ static int skipPastEOL() {
|
|||||||
} else if (isNewline(c)) {
|
} else if (isNewline(c)) {
|
||||||
handleCRLF(c);
|
handleCRLF(c);
|
||||||
nextLine();
|
nextLine();
|
||||||
return skipChars(isWhitespace);
|
return skipChars(isBlankSpace);
|
||||||
} else if (c == '\\') {
|
} else if (c == '\\') {
|
||||||
// Unconditionally skip the next char, including line continuations
|
// Unconditionally skip the next char, including line continuations
|
||||||
c = bumpChar();
|
c = bumpChar();
|
||||||
@@ -2323,7 +2333,7 @@ Capture lexer_CaptureRept() {
|
|||||||
nextLine();
|
nextLine();
|
||||||
|
|
||||||
// We're at line start, so attempt to match a `REPT`, `FOR`, or `ENDR` token
|
// We're at line start, so attempt to match a `REPT`, `FOR`, or `ENDR` token
|
||||||
if (int c = skipChars(isWhitespace); startsIdentifier(c)) {
|
if (int c = skipChars(isBlankSpace); startsIdentifier(c)) {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
switch (readIdentifier(c, false).type) {
|
switch (readIdentifier(c, false).type) {
|
||||||
case T_(POP_REPT):
|
case T_(POP_REPT):
|
||||||
@@ -2365,7 +2375,7 @@ Capture lexer_CaptureMacro() {
|
|||||||
nextLine();
|
nextLine();
|
||||||
|
|
||||||
// We're at line start, so attempt to match an `ENDM` token
|
// We're at line start, so attempt to match an `ENDM` token
|
||||||
if (int c = skipChars(isWhitespace); startsIdentifier(c)) {
|
if (int c = skipChars(isBlankSpace); startsIdentifier(c)) {
|
||||||
shiftChar();
|
shiftChar();
|
||||||
if (readIdentifier(c, false).type == T_(POP_ENDM)) {
|
if (readIdentifier(c, false).type == T_(POP_ENDM)) {
|
||||||
endCapture(capture);
|
endCapture(capture);
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ static std::vector<StateFeature> parseStateFeatures(char *str) {
|
|||||||
if (next) {
|
if (next) {
|
||||||
*next++ = '\0';
|
*next++ = '\0';
|
||||||
}
|
}
|
||||||
// Trim whitespace from the beginning of `feature`...
|
// Trim blank spaces from the beginning of `feature`...
|
||||||
feature += strspn(feature, " \t");
|
feature += strspn(feature, " \t");
|
||||||
// ...and from the end
|
// ...and from the end
|
||||||
if (char *end = strpbrk(feature, " \t"); end) {
|
if (char *end = strpbrk(feature, " \t"); end) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -8,6 +7,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "helpers.hpp" // assume
|
#include "helpers.hpp" // assume
|
||||||
|
#include "util.hpp" // isBlankSpace
|
||||||
|
|
||||||
#include "asm/fixpoint.hpp"
|
#include "asm/fixpoint.hpp"
|
||||||
#include "asm/fstack.hpp"
|
#include "asm/fstack.hpp"
|
||||||
@@ -116,8 +116,8 @@ void opt_Parse(char const *s) {
|
|||||||
|
|
||||||
case 'r': {
|
case 'r': {
|
||||||
++s; // Skip 'r'
|
++s; // Skip 'r'
|
||||||
while (isblank(*s)) {
|
while (isBlankSpace(*s)) {
|
||||||
++s; // Skip leading whitespace
|
++s; // Skip leading blank spaces
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s[0] == '\0') {
|
if (s[0] == '\0') {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ std::pair<WarningState, std::optional<uint32_t>> getInitialWarningState(std::str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Is the rest of the string a decimal number?
|
// Is the rest of the string a decimal number?
|
||||||
// We want to avoid `strtoul`'s whitespace and sign, so we parse manually
|
// We want to avoid `strtoul`'s whitespace and sign handling, so we parse manually
|
||||||
char const *ptr = flag.c_str() + equals + 1;
|
char const *ptr = flag.c_str() + equals + 1;
|
||||||
uint32_t param = 0;
|
uint32_t param = 0;
|
||||||
bool overflowed = false;
|
bool overflowed = false;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "helpers.hpp" // unreachable_
|
#include "helpers.hpp" // unreachable_
|
||||||
#include "platform.hpp" // strcasecmp
|
#include "platform.hpp" // strcasecmp
|
||||||
|
#include "util.hpp" // isBlankSpace
|
||||||
|
|
||||||
#include "fix/warning.hpp"
|
#include "fix/warning.hpp"
|
||||||
|
|
||||||
@@ -93,14 +94,14 @@ bool mbc_HasRAM(MbcType type) {
|
|||||||
return search != mbcData.end() && search->second.second;
|
return search != mbcData.end() && search->second.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void skipWhitespace(char const *&ptr) {
|
static void skipBlankSpace(char const *&ptr) {
|
||||||
while (*ptr == ' ' || *ptr == '\t') {
|
while (isBlankSpace(*ptr)) {
|
||||||
++ptr;
|
++ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void skipMBCSpace(char const *&ptr) {
|
static void skipMBCSpace(char const *&ptr) {
|
||||||
while (*ptr == ' ' || *ptr == '\t' || *ptr == '_') {
|
while (isBlankSpace(*ptr) || *ptr == '_') {
|
||||||
++ptr;
|
++ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,7 +167,7 @@ MbcType mbc_ParseName(char const *name, uint8_t &tpp1Major, uint8_t &tpp1Minor)
|
|||||||
uint16_t mbc;
|
uint16_t mbc;
|
||||||
char const *ptr = name;
|
char const *ptr = name;
|
||||||
|
|
||||||
skipWhitespace(ptr); // Trim off leading whitespace
|
skipBlankSpace(ptr); // Trim off leading blank space
|
||||||
|
|
||||||
#define tryReadSlice(expected) \
|
#define tryReadSlice(expected) \
|
||||||
do { \
|
do { \
|
||||||
@@ -318,7 +319,7 @@ MbcType mbc_ParseName(char const *name, uint8_t &tpp1Major, uint8_t &tpp1Minor)
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
skipWhitespace(ptr); // Trim off trailing whitespace
|
skipBlankSpace(ptr); // Trim off trailing blank space
|
||||||
|
|
||||||
// If done, start processing "features"
|
// If done, start processing "features"
|
||||||
if (!*ptr) {
|
if (!*ptr) {
|
||||||
@@ -501,9 +502,9 @@ MbcType mbc_ParseName(char const *name, uint8_t &tpp1Major, uint8_t &tpp1Minor)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
skipWhitespace(ptr); // Trim off trailing whitespace
|
skipBlankSpace(ptr); // Trim off trailing blank space
|
||||||
|
|
||||||
// If there is still something past the whitespace, error out
|
// If there is still something left, error out
|
||||||
if (*ptr) {
|
if (*ptr) {
|
||||||
fatalUnknownMBC(fullName);
|
fatalUnknownMBC(fullName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ static uint16_t parseNumber(char *&string, char const *errPrefix, uint16_t errVa
|
|||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void skipWhitespace(char *&arg) {
|
static void skipBlankSpace(char *&arg) {
|
||||||
arg += strspn(arg, " \t");
|
arg += strspn(arg, " \t");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,8 +214,8 @@ static std::vector<size_t> readAtFile(std::string const &path, std::vector<char>
|
|||||||
for (std::vector<size_t> argvOfs;;) {
|
for (std::vector<size_t> argvOfs;;) {
|
||||||
int c = file->sbumpc();
|
int c = file->sbumpc();
|
||||||
|
|
||||||
// First, discard any leading whitespace
|
// First, discard any leading blank space
|
||||||
while (isWhitespace(c)) {
|
while (isBlankSpace(c)) {
|
||||||
c = file->sbumpc();
|
c = file->sbumpc();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,13 +239,13 @@ static std::vector<size_t> readAtFile(std::string const &path, std::vector<char>
|
|||||||
|
|
||||||
// Read one argument (until the next whitespace char).
|
// Read one argument (until the next whitespace char).
|
||||||
// We know there is one because we already have its first character in `c`.
|
// We know there is one because we already have its first character in `c`.
|
||||||
for (; c != EOF && !isNewline(c) && !isWhitespace(c); c = file->sbumpc()) {
|
for (; c != EOF && !isWhitespace(c); c = file->sbumpc()) {
|
||||||
argPool.push_back(c);
|
argPool.push_back(c);
|
||||||
}
|
}
|
||||||
argPool.push_back('\0');
|
argPool.push_back('\0');
|
||||||
|
|
||||||
// Discard whitespace until the next argument (candidate)
|
// Discard blank space until the next argument (candidate)
|
||||||
while (isWhitespace(c)) {
|
while (isBlankSpace(c)) {
|
||||||
c = file->sbumpc();
|
c = file->sbumpc();
|
||||||
}
|
}
|
||||||
} while (c != EOF && !isNewline(c)); // End if we reached EOL
|
} while (c != EOF && !isNewline(c)); // End if we reached EOL
|
||||||
@@ -285,7 +285,7 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
options.baseTileIDs[1] = 0;
|
options.baseTileIDs[1] = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
skipWhitespace(arg);
|
skipBlankSpace(arg);
|
||||||
if (*arg != ',') {
|
if (*arg != ',') {
|
||||||
error(
|
error(
|
||||||
"Base tile IDs must be one or two comma-separated numbers, not \"%s\"",
|
"Base tile IDs must be one or two comma-separated numbers, not \"%s\"",
|
||||||
@@ -294,7 +294,7 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++arg; // Skip comma
|
++arg; // Skip comma
|
||||||
skipWhitespace(arg);
|
skipBlankSpace(arg);
|
||||||
number = parseNumber(arg, "Bank 1 base tile ID", 0);
|
number = parseNumber(arg, "Bank 1 base tile ID", 0);
|
||||||
if (number >= 256) {
|
if (number >= 256) {
|
||||||
error("Bank 1 base tile ID must be below 256");
|
error("Bank 1 base tile ID must be below 256");
|
||||||
@@ -356,23 +356,23 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
error("Input slice left coordinate is out of range!");
|
error("Input slice left coordinate is out of range!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
skipWhitespace(arg);
|
skipBlankSpace(arg);
|
||||||
if (*arg != ',') {
|
if (*arg != ',') {
|
||||||
error("Missing comma after left coordinate in \"%s\"", musl_optarg);
|
error("Missing comma after left coordinate in \"%s\"", musl_optarg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++arg;
|
++arg;
|
||||||
skipWhitespace(arg);
|
skipBlankSpace(arg);
|
||||||
options.inputSlice.top = parseNumber(arg, "Input slice upper coordinate");
|
options.inputSlice.top = parseNumber(arg, "Input slice upper coordinate");
|
||||||
skipWhitespace(arg);
|
skipBlankSpace(arg);
|
||||||
if (*arg != ':') {
|
if (*arg != ':') {
|
||||||
error("Missing colon after upper coordinate in \"%s\"", musl_optarg);
|
error("Missing colon after upper coordinate in \"%s\"", musl_optarg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++arg;
|
++arg;
|
||||||
skipWhitespace(arg);
|
skipBlankSpace(arg);
|
||||||
options.inputSlice.width = parseNumber(arg, "Input slice width");
|
options.inputSlice.width = parseNumber(arg, "Input slice width");
|
||||||
skipWhitespace(arg);
|
skipBlankSpace(arg);
|
||||||
if (options.inputSlice.width == 0) {
|
if (options.inputSlice.width == 0) {
|
||||||
error("Input slice width may not be 0!");
|
error("Input slice width may not be 0!");
|
||||||
}
|
}
|
||||||
@@ -381,7 +381,7 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++arg;
|
++arg;
|
||||||
skipWhitespace(arg);
|
skipBlankSpace(arg);
|
||||||
options.inputSlice.height = parseNumber(arg, "Input slice height");
|
options.inputSlice.height = parseNumber(arg, "Input slice height");
|
||||||
if (options.inputSlice.height == 0) {
|
if (options.inputSlice.height == 0) {
|
||||||
error("Input slice height may not be 0!");
|
error("Input slice height may not be 0!");
|
||||||
@@ -416,7 +416,7 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
options.maxNbTiles[1] = 0;
|
options.maxNbTiles[1] = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
skipWhitespace(arg);
|
skipBlankSpace(arg);
|
||||||
if (*arg != ',') {
|
if (*arg != ',') {
|
||||||
error(
|
error(
|
||||||
"Bank capacity must be one or two comma-separated numbers, not \"%s\"",
|
"Bank capacity must be one or two comma-separated numbers, not \"%s\"",
|
||||||
@@ -425,7 +425,7 @@ static char *parseArgv(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++arg; // Skip comma
|
++arg; // Skip comma
|
||||||
skipWhitespace(arg);
|
skipBlankSpace(arg);
|
||||||
options.maxNbTiles[1] = parseNumber(arg, "Number of tiles in bank 1", 256);
|
options.maxNbTiles[1] = parseNumber(arg, "Number of tiles in bank 1", 256);
|
||||||
if (options.maxNbTiles[1] > 256) {
|
if (options.maxNbTiles[1] > 256) {
|
||||||
error("Bank 1 cannot contain more than 256 tiles");
|
error("Bank 1 cannot contain more than 256 tiles");
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ using namespace std::string_view_literals;
|
|||||||
static char const *hexDigits = "0123456789ABCDEFabcdef";
|
static char const *hexDigits = "0123456789ABCDEFabcdef";
|
||||||
|
|
||||||
template<typename Str> // Should be std::string or std::string_view
|
template<typename Str> // Should be std::string or std::string_view
|
||||||
static void skipWhitespace(Str const &str, size_t &pos) {
|
static void skipBlankSpace(Str const &str, size_t &pos) {
|
||||||
pos = std::min(str.find_first_not_of(" \t"sv, pos), str.length());
|
pos = std::min(str.find_first_not_of(" \t"sv, pos), str.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,8 +120,8 @@ void parseInlinePalSpec(char const * const rawArg) {
|
|||||||
n = pos;
|
n = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip whitespace, if any
|
// Skip trailing space, if any
|
||||||
skipWhitespace(arg, n);
|
skipBlankSpace(arg, n);
|
||||||
|
|
||||||
// Skip comma/semicolon, or end
|
// Skip comma/semicolon, or end
|
||||||
if (n == arg.length()) {
|
if (n == arg.length()) {
|
||||||
@@ -134,7 +134,7 @@ void parseInlinePalSpec(char const * const rawArg) {
|
|||||||
++nbColors;
|
++nbColors;
|
||||||
|
|
||||||
// A trailing comma may be followed by a semicolon
|
// A trailing comma may be followed by a semicolon
|
||||||
skipWhitespace(arg, n);
|
skipBlankSpace(arg, n);
|
||||||
if (n == arg.length()) {
|
if (n == arg.length()) {
|
||||||
break;
|
break;
|
||||||
} else if (arg[n] != ';' && arg[n] != ':') {
|
} else if (arg[n] != ';' && arg[n] != ':') {
|
||||||
@@ -149,7 +149,7 @@ void parseInlinePalSpec(char const * const rawArg) {
|
|||||||
case ':':
|
case ':':
|
||||||
case ';':
|
case ';':
|
||||||
++n;
|
++n;
|
||||||
skipWhitespace(arg, n);
|
skipBlankSpace(arg, n);
|
||||||
|
|
||||||
nbColors = 0; // Start a new palette
|
nbColors = 0; // Start a new palette
|
||||||
// Avoid creating a spurious empty palette
|
// Avoid creating a spurious empty palette
|
||||||
@@ -253,7 +253,7 @@ static std::optional<Rgba> parseColor(std::string const &str, size_t &n, uint16_
|
|||||||
error("Failed to parse color #%d (\"%s\"): invalid red component", i + 1, str.c_str());
|
error("Failed to parse color #%d (\"%s\"): invalid red component", i + 1, str.c_str());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
skipWhitespace(str, n);
|
skipBlankSpace(str, n);
|
||||||
if (n == str.length()) {
|
if (n == str.length()) {
|
||||||
error("Failed to parse color #%d (\"%s\"): missing green component", i + 1, str.c_str());
|
error("Failed to parse color #%d (\"%s\"): missing green component", i + 1, str.c_str());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -263,7 +263,7 @@ static std::optional<Rgba> parseColor(std::string const &str, size_t &n, uint16_
|
|||||||
error("Failed to parse color #%d (\"%s\"): invalid green component", i + 1, str.c_str());
|
error("Failed to parse color #%d (\"%s\"): invalid green component", i + 1, str.c_str());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
skipWhitespace(str, n);
|
skipBlankSpace(str, n);
|
||||||
if (n == str.length()) {
|
if (n == str.length()) {
|
||||||
error("Failed to parse color #%d (\"%s\"): missing blue component", i + 1, str.c_str());
|
error("Failed to parse color #%d (\"%s\"): missing blue component", i + 1, str.c_str());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -356,7 +356,7 @@ static void parseGPLFile(char const *filename, std::filebuf &file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
skipWhitespace(line, n);
|
skipBlankSpace(line, n);
|
||||||
// Skip empty lines, or lines that contain just a comment.
|
// Skip empty lines, or lines that contain just a comment.
|
||||||
if (line.length() == n || line[n] == '#') {
|
if (line.length() == n || line[n] == '#') {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -260,8 +260,8 @@ yy::parser::symbol_type yylex() {
|
|||||||
LexerStackEntry &context = lexerStack.back();
|
LexerStackEntry &context = lexerStack.back();
|
||||||
int c = context.file.sbumpc();
|
int c = context.file.sbumpc();
|
||||||
|
|
||||||
// First, skip leading whitespace.
|
// First, skip leading blank space.
|
||||||
while (isWhitespace(c)) {
|
while (isBlankSpace(c)) {
|
||||||
c = context.file.sbumpc();
|
c = context.file.sbumpc();
|
||||||
}
|
}
|
||||||
// Then, skip a comment if applicable.
|
// Then, skip a comment if applicable.
|
||||||
|
|||||||
@@ -182,6 +182,10 @@ static void verboseOutputConfig(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
|
static size_t skipBlankSpace(char const *str) {
|
||||||
|
return strspn(str, " \t");
|
||||||
|
}
|
||||||
|
|
||||||
static void parseScrambleSpec(char *spec) {
|
static void parseScrambleSpec(char *spec) {
|
||||||
// clang-format off: vertically align nested initializers
|
// clang-format off: vertically align nested initializers
|
||||||
static UpperMap<std::pair<uint16_t *, uint16_t>> scrambleSpecs{
|
static UpperMap<std::pair<uint16_t *, uint16_t>> scrambleSpecs{
|
||||||
@@ -191,19 +195,19 @@ static void parseScrambleSpec(char *spec) {
|
|||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
// Skip leading whitespace before the regions.
|
// Skip leading blank space before the regions.
|
||||||
spec += strspn(spec, " \t");
|
spec += skipBlankSpace(spec);
|
||||||
|
|
||||||
// The argument to `-S` should be a comma-separated list of regions, allowing a trailing comma.
|
// The argument to `-S` should be a comma-separated list of regions, allowing a trailing comma.
|
||||||
// Each region name is optionally followed by an '=' and a region size.
|
// Each region name is optionally followed by an '=' and a region size.
|
||||||
while (*spec) {
|
while (*spec) {
|
||||||
char *regionName = spec;
|
char *regionName = spec;
|
||||||
|
|
||||||
// The region name continues (skipping any whitespace) until a ',' (next region),
|
// The region name continues (skipping any blank space) until a ',' (next region),
|
||||||
// '=' (region size), or the end of the string.
|
// '=' (region size), or the end of the string.
|
||||||
size_t regionNameLen = strcspn(regionName, "=, \t");
|
size_t regionNameLen = strcspn(regionName, "=, \t");
|
||||||
// Skip trailing whitespace after the region name.
|
// Skip trailing blank space after the region name.
|
||||||
size_t regionNameSkipLen = regionNameLen + strspn(regionName + regionNameLen, " \t");
|
size_t regionNameSkipLen = regionNameLen + skipBlankSpace(regionName + regionNameLen);
|
||||||
spec = regionName + regionNameSkipLen;
|
spec = regionName + regionNameSkipLen;
|
||||||
|
|
||||||
if (*spec != '=' && *spec != ',' && *spec != '\0') {
|
if (*spec != '=' && *spec != ',' && *spec != '\0') {
|
||||||
@@ -215,14 +219,14 @@ static void parseScrambleSpec(char *spec) {
|
|||||||
// The '=' region size limit is optional.
|
// The '=' region size limit is optional.
|
||||||
if (*spec == '=') {
|
if (*spec == '=') {
|
||||||
regionSize = spec + 1; // Skip the '='
|
regionSize = spec + 1; // Skip the '='
|
||||||
// Skip leading whitespace before the region size.
|
// Skip leading blank space before the region size.
|
||||||
regionSize += strspn(regionSize, " \t");
|
regionSize += skipBlankSpace(regionSize);
|
||||||
|
|
||||||
// The region size continues (skipping any whitespace) until a ',' (next region)
|
// The region size continues (skipping any blank space) until a ',' (next region)
|
||||||
// or the end of the string.
|
// or the end of the string.
|
||||||
regionSizeLen = strcspn(regionSize, ", \t");
|
regionSizeLen = strcspn(regionSize, ", \t");
|
||||||
// Skip trailing whitespace after the region size.
|
// Skip trailing blank space after the region size.
|
||||||
size_t regionSizeSkipLen = regionSizeLen + strspn(regionSize + regionSizeLen, " \t");
|
size_t regionSizeSkipLen = regionSizeLen + skipBlankSpace(regionSize + regionSizeLen);
|
||||||
spec = regionSize + regionSizeSkipLen;
|
spec = regionSize + regionSizeSkipLen;
|
||||||
|
|
||||||
if (*spec != ',' && *spec != '\0') {
|
if (*spec != ',' && *spec != '\0') {
|
||||||
@@ -234,9 +238,9 @@ static void parseScrambleSpec(char *spec) {
|
|||||||
if (*spec == ',') {
|
if (*spec == ',') {
|
||||||
++spec;
|
++spec;
|
||||||
}
|
}
|
||||||
// Skip trailing whitespace after the region.
|
// Skip trailing blank space after the region.
|
||||||
// `spec` will be the next region name, or the end of the string.
|
// `spec` will be the next region name, or the end of the string.
|
||||||
spec += strspn(spec, " \t");
|
spec += skipBlankSpace(spec);
|
||||||
|
|
||||||
// Terminate the `regionName` and `regionSize` strings.
|
// Terminate the `regionName` and `regionSize` strings.
|
||||||
regionName[regionNameLen] = '\0';
|
regionName[regionNameLen] = '\0';
|
||||||
@@ -245,8 +249,8 @@ static void parseScrambleSpec(char *spec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for an empty region name or limit.
|
// Check for an empty region name or limit.
|
||||||
// Note that by skipping leading whitespace before the loop, and skipping a trailing comma
|
// Note that by skipping leading blank space before the loop, and skipping a trailing comma
|
||||||
// and whitespace before the next iteration, we guarantee that the region name will not be
|
// and blank space before the next iteration, we guarantee that the region name will not be
|
||||||
// empty if it is present at all.
|
// empty if it is present at all.
|
||||||
if (*regionName == '\0') {
|
if (*regionName == '\0') {
|
||||||
fatal("Empty region name in spec for option '-S'");
|
fatal("Empty region name in spec for option '-S'");
|
||||||
|
|||||||
10
src/util.cpp
10
src/util.cpp
@@ -5,12 +5,16 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
bool isWhitespace(int c) {
|
bool isNewline(int c) {
|
||||||
|
return c == '\r' || c == '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isBlankSpace(int c) {
|
||||||
return c == ' ' || c == '\t';
|
return c == ' ' || c == '\t';
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isNewline(int c) {
|
bool isWhitespace(int c) {
|
||||||
return c == '\r' || c == '\n';
|
return isBlankSpace(c) || isNewline(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isPrintable(int c) {
|
bool isPrintable(int c) {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
assert 1 +# 1 == 2
|
assert 1 +# 1 == 2
|
||||||
assert 2 ?<EFBFBD>* 2 == 4
|
assert 2 ?<EFBFBD>* 2 == 4
|
||||||
assert 3 **?<EFBFBD>?##?? 3 == 27
|
assert 3 **?<EFBFBD>?##?? 3 == 27
|
||||||
|
charmap "x", 4
|
||||||
|
assert 4 <<#<EFBFBD>'x' == 64
|
||||||
|
|||||||
@@ -8,4 +8,6 @@ error: Invalid character '#'
|
|||||||
at garbage_sequence.asm(3)
|
at garbage_sequence.asm(3)
|
||||||
error: Invalid characters '#', '?', '?'
|
error: Invalid characters '#', '?', '?'
|
||||||
at garbage_sequence.asm(3)
|
at garbage_sequence.asm(3)
|
||||||
Assembly aborted with 5 errors!
|
error: Invalid characters '#', 0xFF (is the file UTF-8?)
|
||||||
|
at garbage_sequence.asm(5)
|
||||||
|
Assembly aborted with 6 errors!
|
||||||
|
|||||||
Reference in New Issue
Block a user