Implement ++ operator for string concatenation (#1698)

This commit is contained in:
Rangi
2025-06-12 16:52:00 -04:00
committed by GitHub
parent fa3d83a3d1
commit fa9e29e4ce
7 changed files with 58 additions and 17 deletions

View File

@@ -548,7 +548,7 @@ There are a number of escape sequences you can use within a string:
.El
.Pp
Multi-line strings are contained in triple quotes
.Pq Ql \&"\&"\&"for instance\&"\&"\&" .
.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 \er
or
@@ -560,10 +560,19 @@ Inside them, backslashes and braces are treated like regular characters, so they
For example, the raw string
.Ql #"\et\e1{s}\e"
is equivalent to the regular string
.Ql "\e\et\e\e1\e{s}\e\e" .
.Ql \&"\e\et\e\e1\e{s}\e\e" .
(Note that this prevents raw strings from including the double quote character.)
Raw strings also may be contained in triple quotes for them to be multi-line, so they can include literal newline or quote characters (although still not three quotes in a row).
.Pp
You can use the
.Sq ++
operator to concatenate two strings.
.Ql \&"str" ++ \&"ing"
is equivalent to
.Ql \&"string" ,
or to
.Ql STRCAT("str", \&"ing") .
.Pp
The following functions operate on string expressions, and return strings themselves.
.Bl -column "STRSLICE(str, start, stop)"
.It Sy Name Ta Sy Operation

View File

@@ -1774,12 +1774,17 @@ static Token yylex_NORMAL() {
// Handle ambiguous 1- or 2-char tokens
case '+': // Either += or ADD
if (peek() == '=') {
case '+': // Either +=, ADD, or CAT
switch (peek()) {
case '=':
shiftChar();
return Token(T_(POP_ADDEQ));
}
case '+':
shiftChar();
return Token(T_(OP_CAT));
default:
return Token(T_(OP_ADD));
}
case '-': // Either -= or SUB
if (peek() == '=') {

View File

@@ -125,6 +125,9 @@
%token OP_MUL "*" OP_DIV "/" OP_MOD "%"
%token OP_EXP "**"
// String operators
%token OP_CAT "++"
// Comparison operators
%token OP_LOGICEQU "==" OP_LOGICNE "!="
%token OP_LOGICLT "<" OP_LOGICGT ">"
@@ -147,6 +150,7 @@
%left OP_AND OP_OR OP_XOR
%left OP_SHL OP_SHR OP_USHR
%left OP_MUL OP_DIV OP_MOD
%left OP_CAT
%precedence NEG // applies to unary OP_LOGICNOT, OP_ADD, OP_SUB, OP_NOT
%right OP_EXP
@@ -1613,6 +1617,10 @@ string_literal:
STRING {
$$ = std::move($1);
}
| string OP_CAT string {
$$ = std::move($1);
$$.append($3);
}
| OP_STRSLICE LPAREN string COMMA iconst COMMA iconst RPAREN {
size_t len = strlenUTF8($3, false);
uint32_t start = adjustNegativeIndex($5, len, "STRSLICE");

View File

@@ -3,6 +3,5 @@ warning: equs-newline.asm(3): [-Wuser]
while expanding symbol "ACT"
warning: equs-newline.asm(3): [-Wuser]
Second
while expanding symbol "ACT"
warning: equs-newline.asm(4): [-Wuser]
Third

View File

@@ -0,0 +1,26 @@
SECTION "test", ROM0
MACRO test
assert !strcmp(\1, \2)
ENDM
test "a"++"b", "ab"
test "a"++""++"b", "ab"
test "a"++"b", strcat("a", "b")
test "a"++"b"++"c", strcat("a","b","c")
test "" ++ "", ""
test strupr("a") ++ strlwr("B"), "Ab"
def str equs "hi"
test #str ++ strupr(#str), "hiHI"
test "a" ++ """b""" ++ strupr("c") ++ strslice(#str, 0, 0), "abC"
charmap "a", 1
charmap "b", 2
charmap "ab", 12
assert "a" + "b" == 3
assert "a" ++ "b" == 12
; errors
assert 2 ++ 2 == 4
ld a, [hl++]

View File

@@ -0,0 +1,5 @@
error: string-concat.asm(25):
syntax error, unexpected ++
error: string-concat.asm(26):
syntax error, unexpected ++, expecting ] or + or -
error: Assembly aborted (2 errors)!

View File

@@ -3,41 +3,30 @@ error: unique-id.asm(11):
while expanding symbol "warn_unique"
warning: unique-id.asm(11): [-Wuser]
!
while expanding symbol "warn_unique"
warning: unique-id.asm(12) -> unique-id.asm::m(4): [-Wuser]
_u1!
while expanding symbol "warn_unique"
warning: unique-id.asm(12) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~1(6): [-Wuser]
_u2!
while expanding symbol "warn_unique"
warning: unique-id.asm(12) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~2(6): [-Wuser]
_u3!
while expanding symbol "warn_unique"
warning: unique-id.asm(12) -> unique-id.asm::m(8): [-Wuser]
_u1!
while expanding symbol "warn_unique"
error: unique-id.asm(13):
'\@' cannot be used outside of a macro or REPT/FOR block
while expanding symbol "warn_unique"
warning: unique-id.asm(13): [-Wuser]
!
while expanding symbol "warn_unique"
warning: unique-id.asm(14) -> unique-id.asm::m(4): [-Wuser]
_u4!
while expanding symbol "warn_unique"
warning: unique-id.asm(14) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~1(6): [-Wuser]
_u5!
while expanding symbol "warn_unique"
warning: unique-id.asm(14) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~2(6): [-Wuser]
_u6!
while expanding symbol "warn_unique"
warning: unique-id.asm(14) -> unique-id.asm::m(8): [-Wuser]
_u4!
while expanding symbol "warn_unique"
error: unique-id.asm(15):
'\@' cannot be used outside of a macro or REPT/FOR block
while expanding symbol "warn_unique"
warning: unique-id.asm(15): [-Wuser]
!
while expanding symbol "warn_unique"
error: Assembly aborted (3 errors)!