From 255b8bf9ba04a4621cd41daadf498156c4bc3d80 Mon Sep 17 00:00:00 2001 From: Rangi Date: Wed, 16 Dec 2020 12:31:44 -0500 Subject: [PATCH] Implement """triple-quoted""" multi-line strings Fixes #589 --- src/asm/lexer.c | 44 +++++++++++++++++++++++++++------ src/asm/rgbasm.5 | 8 ++++++ test/asm/multi-line-strings.asm | 32 ++++++++++++++++++++++++ test/asm/multi-line-strings.err | 0 test/asm/multi-line-strings.out | 13 ++++++++++ 5 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 test/asm/multi-line-strings.asm create mode 100644 test/asm/multi-line-strings.err create mode 100644 test/asm/multi-line-strings.out diff --git a/src/asm/lexer.c b/src/asm/lexer.c index 1318cba7..ecf74625 100644 --- a/src/asm/lexer.c +++ b/src/asm/lexer.c @@ -1448,30 +1448,57 @@ static void readString(void) dbgPrint("Reading string\n"); lexerState->disableMacroArgs = true; lexerState->disableInterpolation = true; + + bool multiline = false; + + if (peek(0) == '"') { + shiftChars(1); + if (peek(0) == '"') { + /* """ begins a multi-line string */ + shiftChars(1); + multiline = true; + } else { + /* "" is an empty string */ + goto finish; + } + } + for (;;) { int c = peek(0); + if (c == '\r' || c == '\n') { + if (!multiline) { + /* '\r' or '\n' ends a single-line string early */ + c = EOF; + } else if (c == '\r' && peek(1) == '\n') { + /* '\r\n' becomes '\n' in multi-line strings */ + shiftChars(1); + c = '\n'; + } + } + switch (c) { case '"': - shiftChars(1); + if (multiline) { + /* """ ends a multi-line string */ + if (peek(1) != '"' || peek(2) != '"') + break; + shiftChars(3); + } else { + shiftChars(1); + } if (i == sizeof(yylval.tzString)) { i--; warning(WARNING_LONG_STR, "String constant too long\n"); } - yylval.tzString[i] = '\0'; - dbgPrint("Read string \"%s\"\n", yylval.tzString); goto finish; - case '\r': - case '\n': /* Do not shift these! */ case EOF: if (i == sizeof(yylval.tzString)) { i--; warning(WARNING_LONG_STR, "String constant too long\n"); } - yylval.tzString[i] = '\0'; error("Unterminated string\n"); - dbgPrint("Read string \"%s\"\n", yylval.tzString); goto finish; case '\\': /* Character escape or macro arg */ @@ -1553,6 +1580,9 @@ static void readString(void) } finish: + yylval.tzString[i] = '\0'; + + dbgPrint("Read string \"%s\"\n", yylval.tzString); lexerState->disableMacroArgs = false; lexerState->disableInterpolation = false; } diff --git a/src/asm/rgbasm.5 b/src/asm/rgbasm.5 index d787d4fb..fddb12c2 100644 --- a/src/asm/rgbasm.5 +++ b/src/asm/rgbasm.5 @@ -240,6 +240,14 @@ There are a number of escape sequences you can use within a string: .El (Note that some of those can be used outside of strings, when noted further in this document.) .Pp +Multi-line strings are contained in triple quotes +.Pq Ql \&"\&"\&"for instance\&"\&"\&" . +Escape sequences work the same way in multi-line strings; however, literal newline +characters will be included as-is, without needing to escape them with +.Ql \[rs]r +or +.Ql \[rs]n . +.Pp A funky feature is .Ql {symbol} within a string, called diff --git a/test/asm/multi-line-strings.asm b/test/asm/multi-line-strings.asm new file mode 100644 index 00000000..b7e8b99e --- /dev/null +++ b/test/asm/multi-line-strings.asm @@ -0,0 +1,32 @@ +S EQUS "Hello" + +PRINTT "\"\"\"\n" + +PRINTT """{S} +world +""" + +PRINTT """The multi-line string \ ; line continuations work +can contain: +- "single quotes" +- ""double quotes"" +- even escaped \"""triple"\"" ""\"quotes\"\"\" +!""" + +PRINTT """\n""" + +printarg: MACRO + PRINTT "arg <\1>\n" + PRINTT """arg (\1)\n""" +ENDM + + printarg " + printarg """ + +EMPTY1 EQUS "" +EMPTY2 EQUS "\ ; comment +" +EMPTY3 EQUS """""" +EMPTY4 EQUS """\ ; comment +""" + PRINTT STRCAT("(", "{EMPTY1}", "{EMPTY2}", "{EMPTY3}", "{EMPTY4}", ")\n") diff --git a/test/asm/multi-line-strings.err b/test/asm/multi-line-strings.err new file mode 100644 index 00000000..e69de29b diff --git a/test/asm/multi-line-strings.out b/test/asm/multi-line-strings.out new file mode 100644 index 00000000..ef7d1180 --- /dev/null +++ b/test/asm/multi-line-strings.out @@ -0,0 +1,13 @@ +""" +Hello +world +The multi-line string can contain: +- "single quotes" +- ""double quotes"" +- even escaped """triple""" """quotes""" +! +arg <"> +arg (") +arg <"""> +arg (""") +()