diff --git a/include/asm/charmap.h b/include/asm/charmap.h index 8d3c7d80..91d6268c 100644 --- a/include/asm/charmap.h +++ b/include/asm/charmap.h @@ -9,6 +9,7 @@ #ifndef RGBDS_ASM_CHARMAP_H #define RGBDS_ASM_CHARMAP_H +#include #include #define DEFAULT_CHARMAP_NAME "main" @@ -19,6 +20,7 @@ void charmap_Set(char const *name); void charmap_Push(void); void charmap_Pop(void); void charmap_Add(char *mapping, uint8_t value); +bool charmap_HasChar(char const *input); size_t charmap_Convert(char const *input, uint8_t *output); size_t charmap_ConvertNext(char const **input, uint8_t **output); diff --git a/man/rgbasm.5 b/man/rgbasm.5 index 7f71ccf4..ba02e1a0 100644 --- a/man/rgbasm.5 +++ b/man/rgbasm.5 @@ -425,6 +425,7 @@ pattern replaced by interpolating the format with its corresponding argument in .Ar args .Pq So %% Sc is replaced by the So % Sc character . +.It Fn INCHARMAP str Ta Returns 1 if Ar str No has an entry in the current charmap, and 0 otherwise. .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, last is position -1) with the current charmap. .El diff --git a/src/asm/charmap.c b/src/asm/charmap.c index bbf428c7..fc8bb2f2 100644 --- a/src/asm/charmap.c +++ b/src/asm/charmap.c @@ -187,6 +187,22 @@ void charmap_Add(char *mapping, uint8_t value) node->value = value; } +bool charmap_HasChar(char const *input) +{ + struct Charmap const *charmap = *currentCharmap; + struct Charnode const *node = &charmap->nodes[0]; + + for (; *input; input++) { + size_t next = node->next[*input - 1]; + + if (!next) + return false; + node = &charmap->nodes[next]; + } + + return node->isTerminal; +} + size_t charmap_Convert(char const *input, uint8_t *output) { uint8_t *start = output; diff --git a/src/asm/lexer.c b/src/asm/lexer.c index 443caf72..649e045a 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -206,6 +206,7 @@ static struct KeywordMapping { {"CHARLEN", T_OP_CHARLEN}, {"CHARSUB", T_OP_CHARSUB}, + {"INCHARMAP", T_OP_INCHARMAP}, {"INCLUDE", T_POP_INCLUDE}, {"PRINT", T_POP_PRINT}, @@ -571,7 +572,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[365] = {0}; // Make sure to keep this correct when adding keywords! +} keywordDict[370] = {0}; // Make sure to keep this correct when adding keywords! // Convert a char into its index into the dict static uint8_t dictIndex(char c) diff --git a/src/asm/parser.y b/src/asm/parser.y index 8fdcae66..08286a6c 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -606,6 +606,7 @@ enum { %token T_OP_CHARLEN "CHARLEN" %token T_OP_CHARSUB "CHARSUB" +%token T_OP_INCHARMAP "INCHARMAP" %token T_LABEL "label" %token T_ID "identifier" @@ -1608,6 +1609,9 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); } | T_OP_CHARLEN T_LPAREN string T_RPAREN { rpn_Number(&$$, charlenUTF8($3)); } + | T_OP_INCHARMAP T_LPAREN string T_RPAREN { + rpn_Number(&$$, charmap_HasChar($3)); + } | T_LPAREN relocexpr T_RPAREN { $$ = $2; } ; diff --git a/test/asm/incharmap.asm b/test/asm/incharmap.asm new file mode 100644 index 00000000..dfcdc97b --- /dev/null +++ b/test/asm/incharmap.asm @@ -0,0 +1,28 @@ +; default charmap 'main' +charmap "a", 1 +charmap "ab", 2 +charmap "abc", 3 + +newcharmap second +charmap "d", 4 +charmap "e", 5 +charmap {__ISO_8601_UTC__}, 6 ; expands with quotes + +setcharmap main + +assert incharmap("a") +assert incharmap("ab") +assert incharmap(strcat("ab", "c")) + +assert !incharmap("") ; empty +assert !incharmap("A") ; case sensitive +assert !incharmap("aa") ; multiple chars "a" "a" +assert !incharmap("d") ; unmapped char "d" +assert !incharmap("bc") ; unmapped chars "b" "c" + +setcharmap second + +assert incharmap("d") ; now "d" is mapped +assert !incharmap("a") ; only in 'main' +assert !incharmap("bc") ; still unmapped chars +assert incharmap({__ISO_8601_UTC__}) diff --git a/test/asm/incharmap.err b/test/asm/incharmap.err new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/incharmap.out b/test/asm/incharmap.out new file mode 100644 index 00000000..e69de29b