Implement STRRPL

Fixes #660

STRRPL(str, "", new) does nothing
(warn about it with -Wempty-strrpl)
This commit is contained in:
Rangi
2020-12-30 11:00:44 -05:00
committed by Eldred Habert
parent 669a392fcd
commit 77279984a5
10 changed files with 88 additions and 13 deletions

View File

@@ -20,6 +20,7 @@ enum WarningID {
WARNING_DIV, /* Division undefined behavior */
WARNING_EMPTY_DATA_DIRECTIVE, /* `db`, `dw` or `dl` with no directive in ROM */
WARNING_EMPTY_ENTRY, /* Empty entry in `db`, `dw` or `dl` */
WARNING_EMPTY_STRRPL, /* Empty second argument in `STRRPL` */
WARNING_LARGE_CONSTANT, /* Constants too large */
WARNING_LONG_STR, /* String too long for internal buffers */
WARNING_NESTED_COMMENT, /* Comment-start delimiter in a block comment */

View File

@@ -230,7 +230,7 @@ size_t charmap_Convert(char const *input, uint8_t *output)
size_t codepointLen = readUTF8Char(output, input);
if (codepointLen == 0) {
error("Input string is not valid UTF-8!");
error("Input string is not valid UTF-8!\n");
break;
}
input += codepointLen; /* OK because UTF-8 has no NUL in multi-byte chars */

View File

@@ -204,6 +204,7 @@ static struct KeywordMapping {
{"STRCAT", T_OP_STRCAT},
{"STRUPR", T_OP_STRUPR},
{"STRLWR", T_OP_STRLWR},
{"STRRPL", T_OP_STRRPL},
{"STRFMT", T_OP_STRFMT},
{"INCLUDE", T_POP_INCLUDE},
@@ -493,7 +494,7 @@ struct KeywordDictNode {
uint16_t children[0x60 - ' '];
struct KeywordMapping const *keyword;
/* Since the keyword structure is invariant, the min number of nodes is known at compile time */
} keywordDict[353] = {0}; /* Make sure to keep this correct when adding keywords! */
} keywordDict[355] = {0}; /* Make sure to keep this correct when adding keywords! */
/* Convert a char into its index into the dict */
static inline uint8_t dictIndex(char c)

View File

@@ -164,7 +164,46 @@ static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len)
dest[destIndex] = 0;
}
static void initStrFmtArgList(struct StrFmtArgList *args) {
static void strrpl(char *dest, size_t destLen, char const *src, char const *old, char const *new)
{
size_t oldLen = strlen(old);
size_t newLen = strlen(new);
size_t i = 0;
if (!oldLen) {
warning(WARNING_EMPTY_STRRPL, "STRRPL: Cannot replace an empty string\n");
strcpy(dest, src);
return;
}
for (char const *next = strstr(src, old); next && *next; next = strstr(src, old)) {
memcpy(dest + i, src, next - src < destLen - i ? next - src : destLen - i);
i += next - src;
if (i >= destLen)
break;
memcpy(dest + i, new, newLen < destLen - i ? newLen : destLen - i);
i += newLen;
if (i >= destLen)
break;
src = next + oldLen;
}
size_t srcLen = strlen(src);
memcpy(dest + i, src, srcLen < destLen - i ? srcLen : destLen - i);
i += srcLen;
if (i >= destLen) {
warning(WARNING_LONG_STR, "STRRPL: String too long, got truncated\n");
i = destLen - 1;
}
dest[i] = '\0';
}
static void initStrFmtArgList(struct StrFmtArgList *args)
{
args->nbArgs = 0;
args->capacity = INITIAL_STRFMT_ARG_SIZE;
args->args = malloc(args->capacity * sizeof(*args->args));
@@ -173,7 +212,8 @@ static void initStrFmtArgList(struct StrFmtArgList *args) {
strerror(errno));
}
static size_t nextStrFmtArgListIndex(struct StrFmtArgList *args) {
static size_t nextStrFmtArgListIndex(struct StrFmtArgList *args)
{
if (args->nbArgs == args->capacity) {
args->capacity = (args->capacity + 1) * 2;
args->args = realloc(args->args, args->capacity * sizeof(*args->args));
@@ -184,7 +224,8 @@ static size_t nextStrFmtArgListIndex(struct StrFmtArgList *args) {
return args->nbArgs++;
}
static void freeStrFmtArgList(struct StrFmtArgList *args) {
static void freeStrFmtArgList(struct StrFmtArgList *args)
{
free(args->format);
for (size_t i = 0; i < args->nbArgs; i++)
if (!args->args[i].isNumeric)
@@ -192,11 +233,12 @@ static void freeStrFmtArgList(struct StrFmtArgList *args) {
free(args->args);
}
static void strfmt(char *dest, size_t destLen, char const *fmt, size_t nbArgs, struct StrFmtArg *args) {
static void strfmt(char *dest, size_t destLen, char const *fmt, size_t nbArgs, struct StrFmtArg *args)
{
size_t a = 0;
size_t i;
size_t i = 0;
for (i = 0; i < destLen;) {
while (i < destLen) {
int c = *fmt++;
if (c == '\0') {
@@ -254,14 +296,14 @@ static void strfmt(char *dest, size_t destLen, char const *fmt, size_t nbArgs, s
i += snprintf(&dest[i], destLen - i, "%s", buf);
}
if (a < nbArgs)
error("STRFMT: %zu unformatted argument(s)\n", nbArgs - a);
if (i > destLen - 1) {
warning(WARNING_LONG_STR, "STRFMT: String too long, got truncated\n");
i = destLen - 1;
}
dest[i] = '\0';
if (a < nbArgs)
error("STRFMT: %zu unformatted argument(s)\n", nbArgs - a);
}
static inline void failAssert(enum AssertionType type)
@@ -387,6 +429,7 @@ static inline void failAssertMsg(enum AssertionType type, char const *msg)
%left T_OP_STRCAT
%left T_OP_STRUPR
%left T_OP_STRLWR
%left T_OP_STRRPL
%left T_OP_STRFMT
%token <tzSym> T_LABEL
@@ -1293,15 +1336,18 @@ string : T_STRING
| T_OP_STRLWR T_LPAREN string T_RPAREN {
lowerstring($$, $3);
}
| T_OP_STRRPL T_LPAREN string T_COMMA string T_COMMA string T_RPAREN {
strrpl($$, sizeof($$), $3, $5, $7);
}
| T_OP_STRFMT T_LPAREN strfmt_args T_RPAREN {
strfmt($$, MAXSTRLEN + 1, $3.format, $3.nbArgs, $3.args);
strfmt($$, sizeof($$), $3.format, $3.nbArgs, $3.args);
freeStrFmtArgList(&$3);
}
;
strcat_args : string
| strcat_args T_COMMA string {
if (snprintf($$, MAXSTRLEN + 1, "%s%s", $1, $3) > MAXSTRLEN)
if (snprintf($$, sizeof($$), "%s%s", $1, $3) > MAXSTRLEN)
warning(WARNING_LONG_STR, "STRCAT: String too long '%s%s'\n",
$1, $3);
}

View File

@@ -215,6 +215,12 @@ Warn when an empty entry is encountered in a
list.
This warning is enabled by
.Fl Wextra .
.It Fl Wempty-strrpl
Warn when
.Fn STRRPL
is called with an empty string as its second argument (the substring to replace).
This warning is enabled by
.Fl Wall .
.It Fl Wlarge-constant
Warn when a constant too large to fit in a signed 32-bit integer is encountered.
This warning is enabled by

View File

@@ -374,6 +374,7 @@ Most of them return a string, however some of these functions actually return an
.It Fn STRSUB str pos len Ta Returns a substring from Ar str No starting at Ar pos Po first character is position 1 Pc and Ar len No characters long.
.It Fn STRUPR str Ta Returns Ar str No with all letters in uppercase.
.It Fn STRLWR str Ta Returns Ar str No with all letters in lowercase.
.It Fn STRRPL str old new Ta Returns Ar str No with each non-overlapping occurrence of the substring Ar old No replaced with Ar new .
.It Fn STRFMT fmt args... Ta Returns the string Ar fmt No with each
.Ql %spec
pattern replaced by interpolating the format

View File

@@ -34,6 +34,7 @@ static enum WarningState const defaultWarnings[NB_WARNINGS] = {
[WARNING_DIV] = WARNING_DISABLED,
[WARNING_EMPTY_DATA_DIRECTIVE] = WARNING_DISABLED,
[WARNING_EMPTY_ENTRY] = WARNING_DISABLED,
[WARNING_EMPTY_STRRPL] = WARNING_DISABLED,
[WARNING_LARGE_CONSTANT] = WARNING_DISABLED,
[WARNING_LONG_STR] = WARNING_DISABLED,
[WARNING_NESTED_COMMENT] = WARNING_ENABLED,
@@ -74,6 +75,7 @@ static char const *warningFlags[NB_WARNINGS_ALL] = {
"div",
"empty-data-directive",
"empty-entry",
"empty-strrpl",
"large-constant",
"long-string",
"nested-comment",
@@ -98,6 +100,7 @@ static uint8_t const _wallCommands[] = {
WARNING_BUILTIN_ARG,
WARNING_CHARMAP_REDEF,
WARNING_EMPTY_DATA_DIRECTIVE,
WARNING_EMPTY_STRRPL,
WARNING_LARGE_CONSTANT,
WARNING_LONG_STR,
META_WARNING_DONE
@@ -116,6 +119,7 @@ static uint8_t const _weverythingCommands[] = {
WARNING_DIV,
WARNING_EMPTY_DATA_DIRECTIVE,
WARNING_EMPTY_ENTRY,
WARNING_EMPTY_STRRPL,
WARNING_LARGE_CONSTANT,
WARNING_LONG_STR,
WARNING_NESTED_COMMENT,

6
test/asm/strrpl.asm Normal file
View File

@@ -0,0 +1,6 @@
println strrpl("\tld [hli], a", "[hli]", "[hl+]")
println strrpl("lolololol", "lol", "hah")
println strrpl("h e ll o", " ", "")
println strrpl("world", "", "x")
println strrpl("", "a", "b")
println strrpl("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "a", "[bbbbbbbb]")

4
test/asm/strrpl.err Normal file
View File

@@ -0,0 +1,4 @@
warning: strrpl.asm(4): [-Wempty-strrpl]
STRRPL: Cannot replace an empty string
warning: strrpl.asm(6): [-Wlong-string]
STRRPL: String too long, got truncated

6
test/asm/strrpl.out Normal file
View File

@@ -0,0 +1,6 @@
ld [hl+], a
hahohahol
hello
world
[bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbbbbbb][bbbb