Implement """triple-quoted""" multi-line strings

Fixes #589
This commit is contained in:
Rangi
2020-12-16 12:31:44 -05:00
committed by Eldred Habert
parent ad6f17cd93
commit 255b8bf9ba
5 changed files with 90 additions and 7 deletions

View File

@@ -1448,30 +1448,57 @@ static void readString(void)
dbgPrint("Reading string\n"); dbgPrint("Reading string\n");
lexerState->disableMacroArgs = true; lexerState->disableMacroArgs = true;
lexerState->disableInterpolation = 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 (;;) { for (;;) {
int c = peek(0); 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) { switch (c) {
case '"': 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)) { if (i == sizeof(yylval.tzString)) {
i--; i--;
warning(WARNING_LONG_STR, "String constant too long\n"); warning(WARNING_LONG_STR, "String constant too long\n");
} }
yylval.tzString[i] = '\0';
dbgPrint("Read string \"%s\"\n", yylval.tzString);
goto finish; goto finish;
case '\r':
case '\n': /* Do not shift these! */
case EOF: case EOF:
if (i == sizeof(yylval.tzString)) { if (i == sizeof(yylval.tzString)) {
i--; i--;
warning(WARNING_LONG_STR, "String constant too long\n"); warning(WARNING_LONG_STR, "String constant too long\n");
} }
yylval.tzString[i] = '\0';
error("Unterminated string\n"); error("Unterminated string\n");
dbgPrint("Read string \"%s\"\n", yylval.tzString);
goto finish; goto finish;
case '\\': /* Character escape or macro arg */ case '\\': /* Character escape or macro arg */
@@ -1553,6 +1580,9 @@ static void readString(void)
} }
finish: finish:
yylval.tzString[i] = '\0';
dbgPrint("Read string \"%s\"\n", yylval.tzString);
lexerState->disableMacroArgs = false; lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false; lexerState->disableInterpolation = false;
} }

View File

@@ -240,6 +240,14 @@ There are a number of escape sequences you can use within a string:
.El .El
(Note that some of those can be used outside of strings, when noted further in this document.) (Note that some of those can be used outside of strings, when noted further in this document.)
.Pp .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 A funky feature is
.Ql {symbol} .Ql {symbol}
within a string, called within a string, called

View File

@@ -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")

View File

View File

@@ -0,0 +1,13 @@
"""
Hello
world
The multi-line string can contain:
- "single quotes"
- ""double quotes""
- even escaped """triple""" """quotes"""
!
arg <">
arg (")
arg <""">
arg (""")
()