From b1e6c7319792afa6aeda51fd83b891497dcccc4c Mon Sep 17 00:00:00 2001 From: Rangi Date: Sun, 18 Apr 2021 21:40:38 -0400 Subject: [PATCH] STRSUB and CHARSUB allow zero or negative positions These are offsets from the end of the string, as if the STRLEN or CHARLEN respectively were added to the position. Fixes #812 --- src/asm/parser.y | 28 ++++++++++++++++------------ src/asm/rgbasm.5 | 4 ++-- test/asm/charlen-charsub.asm | 2 ++ test/asm/strsub.asm | 5 ++++- test/asm/strsub.err | 16 ++++++++-------- test/asm/strsub.out | 3 +++ 6 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/asm/parser.y b/src/asm/parser.y index d6a09ffd..ad26d988 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -105,7 +105,7 @@ static size_t strlenUTF8(char const *s) return len; } -static void strsubUTF8(char *dest, size_t destLen, char const *src, uint32_t pos, uint32_t len) +static void strsubUTF8(char *dest, size_t destLen, char const *src, int32_t pos, uint32_t len) { size_t srcIndex = 0; size_t destIndex = 0; @@ -114,8 +114,11 @@ static void strsubUTF8(char *dest, size_t destLen, char const *src, uint32_t pos uint32_t curLen = 0; if (pos < 1) { - warning(WARNING_BUILTIN_ARG, "STRSUB: Position starts at 1\n"); - pos = 1; + pos += strlenUTF8(src); + if (pos < 1) { + warning(WARNING_BUILTIN_ARG, "STRSUB: Position starts at 1\n"); + pos = 1; + } } /* Advance to starting position in source string. */ @@ -132,8 +135,7 @@ static void strsubUTF8(char *dest, size_t destLen, char const *src, uint32_t pos if (!src[srcIndex] && len) warning(WARNING_BUILTIN_ARG, - "STRSUB: Position %lu is past the end of the string\n", - (unsigned long)pos); + "STRSUB: Position %" PRId32 " is past the end of the string\n", pos); /* Copy from source to destination. */ while (src[srcIndex] && destIndex < destLen - 1 && curLen < len) { @@ -168,13 +170,16 @@ static size_t charlenUTF8(char const *s) return len; } -static void charsubUTF8(char *dest, char const *src, uint32_t pos) +static void charsubUTF8(char *dest, char const *src, int32_t pos) { size_t charLen = 1; if (pos < 1) { - warning(WARNING_BUILTIN_ARG, "CHARSUB: Position starts at 1\n"); - pos = 1; + pos += charlenUTF8(src); + if (pos < 1) { + warning(WARNING_BUILTIN_ARG, "CHARSUB: Position starts at 1\n"); + pos = 1; + } } /* Advance to starting position in source string. */ @@ -185,8 +190,7 @@ static void charsubUTF8(char *dest, char const *src, uint32_t pos) if (!charmap_ConvertNext(&src, NULL)) warning(WARNING_BUILTIN_ARG, - "CHARSUB: Position %lu is past the end of the string\n", - (unsigned long)pos); + "CHARSUB: Position %" PRId32 " is past the end of the string\n", pos); /* Copy from source to destination. */ memcpy(dest, start, src - start); @@ -1526,10 +1530,10 @@ const_no_str : relocexpr_no_str { ; string : T_STRING - | T_OP_STRSUB T_LPAREN string T_COMMA uconst T_COMMA uconst T_RPAREN { + | T_OP_STRSUB T_LPAREN string T_COMMA const T_COMMA uconst T_RPAREN { strsubUTF8($$, sizeof($$), $3, $5, $7); } - | T_OP_CHARSUB T_LPAREN string T_COMMA uconst T_RPAREN { + | T_OP_CHARSUB T_LPAREN string T_COMMA const T_RPAREN { charsubUTF8($$, $3, $5); } | T_OP_STRCAT T_LPAREN T_RPAREN { diff --git a/src/asm/rgbasm.5 b/src/asm/rgbasm.5 index cdc95989..f37093f1 100644 --- a/src/asm/rgbasm.5 +++ b/src/asm/rgbasm.5 @@ -394,13 +394,13 @@ Most of them return a string, however some of these functions actually return an .It Fn STRCMP str1 str2 Ta Returns -1 if Ar str1 No is alphabetically lower than Ar str2 No , zero if they match, 1 if Ar str1 No is greater than Ar str2 . .It Fn STRIN str1 str2 Ta Returns the first position of Ar str2 No in Ar str1 No or zero if it's not present Pq first character is position 1 . .It Fn STRRIN str1 str2 Ta Returns the last position of Ar str2 No in Ar str1 No or zero if it's not present Pq first character is position 1 . -.It Fn STRSUB str pos len Ta Returns a substring from Ar str No starting at Ar pos No (first character is position 1) and Ar len No characters long. +.It Fn STRSUB str pos len Ta Returns a substring from Ar str No starting at Ar pos No (first character is position 1) and Ar len No characters long. Zero or negative Ar pos No counts from the end, as if Qo STRLEN(str) Qc were added to it. .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 .It Fn CHARLEN str Ta Returns the number of charmap entries in Ar str No with the current charmap. -.It Fn CHARSUB str pos Ta Returns the substring for the charmap entry at Ar pos No in Ar str No (first character is position 1) with the current charmap. +.It Fn CHARSUB str pos Ta Returns the substring for the charmap entry at Ar pos No in Ar str No (first character is position 1) with the current charmap. Zero or negative Ar pos No counts from the end, as if Qo CHARLEN(str) Qc were added to it. .Ql %spec pattern replaced by interpolating the format .Ar spec diff --git a/test/asm/charlen-charsub.asm b/test/asm/charlen-charsub.asm index 06792966..dcab1fb0 100644 --- a/test/asm/charlen-charsub.asm +++ b/test/asm/charlen-charsub.asm @@ -11,6 +11,7 @@ S EQUS "XBoldABC" assert CHARLEN("{S}") == 6 println CHARSUB("{S}", 2) assert !STRCMP(CHARSUB("{S}", 2), "Bold") + assert CHARSUB("{S}", -4) == CHARSUB("{S}", CHARLEN("{S}") - 4) assert CHARSUB("{S}", 2) == "Bold" && "Bold" == $88 assert CHARSUB("{S}", 1) == $58 ; ASCII "X" db "{S}" @@ -20,6 +21,7 @@ S EQUS "XBoldABC" assert CHARLEN("{S}") == 14 println CHARSUB("{S}", 2) assert !STRCMP(CHARSUB("{S}", 2), "B") + assert CHARSUB("{S}", -4) == CHARSUB("{S}", CHARLEN("{S}") - 4) assert CHARSUB("{S}", 2) == "B" && "B" == $42 ; ASCII "B" assert CHARSUB("{S}", 1) == $58 ; ASCII "X" db "{S}" diff --git a/test/asm/strsub.asm b/test/asm/strsub.asm index 924594eb..0f9d0375 100644 --- a/test/asm/strsub.asm +++ b/test/asm/strsub.asm @@ -7,11 +7,14 @@ ENDM xstrsub "ABC", 1, 1 xstrsub "ABC", 2, 1 xstrsub "ABC", 3, 1 + xstrsub "ABC", -2, 1 + xstrsub "ABC", -1, 1 + xstrsub "ABC", 0, 1 xstrsub "ABC", 1, 2 xstrsub "ABC", 2, 2 xstrsub "ABC", 2, 32 xstrsub "ABC", 2, 300 - xstrsub "ABC", 0, 300 + xstrsub "ABC", -3, 300 xstrsub "ABC", 4, 0 xstrsub "ABC", 4, 1 xstrsub "カタカナ", 1, 2 diff --git a/test/asm/strsub.err b/test/asm/strsub.err index ff51a3cb..b302ef50 100644 --- a/test/asm/strsub.err +++ b/test/asm/strsub.err @@ -1,14 +1,14 @@ -warning: strsub.asm(12) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] +warning: strsub.asm(15) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Length too big: 32 -warning: strsub.asm(13) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] +warning: strsub.asm(16) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Length too big: 300 -warning: strsub.asm(14) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] +warning: strsub.asm(17) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Position starts at 1 -warning: strsub.asm(14) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] +warning: strsub.asm(17) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Length too big: 300 -warning: strsub.asm(16) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] - STRSUB: Position 4 is past the end of the string -warning: strsub.asm(16) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] - STRSUB: Length too big: 1 warning: strsub.asm(19) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] + STRSUB: Position 4 is past the end of the string +warning: strsub.asm(19) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] + STRSUB: Length too big: 1 +warning: strsub.asm(22) -> strsub.asm::xstrsub(4): [-Wbuiltin-args] STRSUB: Length too big: 10 diff --git a/test/asm/strsub.out b/test/asm/strsub.out index aecbf596..7c4b4128 100644 --- a/test/asm/strsub.out +++ b/test/asm/strsub.out @@ -1,6 +1,9 @@ A B C +A +B +C AB BC BC