From e31bcabbaa5de6f01bb796d275d807dd8235a19f Mon Sep 17 00:00:00 2001 From: Rangi <35663410+Rangi42@users.noreply.github.com> Date: Fri, 19 Sep 2025 14:06:36 -0400 Subject: [PATCH] Implement `===` and `!==` string comparison operators (#1832) --- man/rgbasm.5 | 13 +++++++++++++ src/asm/lexer.cpp | 20 ++++++++++++++------ src/asm/parser.y | 7 +++++++ test/asm/string-compare.asm | 7 +++++++ 4 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 test/asm/string-compare.asm diff --git a/man/rgbasm.5 b/man/rgbasm.5 index ce519daf..05fe1e17 100644 --- a/man/rgbasm.5 +++ b/man/rgbasm.5 @@ -586,6 +586,19 @@ is equivalent to or to .Ql STRCAT("str", \&"ing") . .Pp +You can use the +.Sq === +and +.Sq !== +operators to compare two strings. +.Ql \&"str" === \&"ing" +is equivalent to +.Ql STRCMP("str", \&"ing") == 0 , +and +.Ql \&"str" !== \&"ing" +is equivalent to +.Ql STRCMP("str", \&"ing") != 0 . +.Pp The following functions operate on string expressions, and return strings themselves. .Bl -column "STRSLICE(str, start, stop)" .It Sy Name Ta Sy Operation diff --git a/src/asm/lexer.cpp b/src/asm/lexer.cpp index b69103bb..4a657f8b 100644 --- a/src/asm/lexer.cpp +++ b/src/asm/lexer.cpp @@ -1750,14 +1750,22 @@ static Token yylex_NORMAL() { case '^': // Either ^= or XOR return oneOrTwo('=', T_(POP_XOREQ), T_(OP_XOR)); - case '=': // Either assignment or EQ - return oneOrTwo('=', T_(OP_LOGICEQU), T_(POP_EQUAL)); - - case '!': // Either a NEQ or negation - return oneOrTwo('=', T_(OP_LOGICNE), T_(OP_LOGICNOT)); - // Handle ambiguous 1-, 2-, or 3-char tokens + case '=': // Either assignment, EQ or string EQ + if (peek() == '=') { + shiftChar(); + return oneOrTwo('=', T_(OP_STREQU), T_(OP_LOGICEQU)); + } + return Token(T_(POP_EQUAL)); + + case '!': // Either negation, NEQ, or string NEQ + if (peek() == '=') { + shiftChar(); + return oneOrTwo('=', T_(OP_STRNE), T_(OP_LOGICNE)); + } + return Token(T_(OP_LOGICNOT)); + case '<': // Either <<=, LT, LTE, or left shift if (peek() == '<') { shiftChar(); diff --git a/src/asm/parser.y b/src/asm/parser.y index ac4bf819..8bf7ceef 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -107,6 +107,7 @@ // String operators %token OP_CAT "++" +%token OP_STREQU "===" OP_STRNE "!==" // Comparison operators %token OP_LOGICEQU "==" OP_LOGICNE "!=" @@ -1287,6 +1288,12 @@ relocexpr_no_str: | CHARACTER { $$.makeNumber(act_CharToNum($1)); } + | string OP_STREQU string { + $$.makeNumber($1.compare($3) == 0); + } + | string OP_STRNE string { + $$.makeNumber($1.compare($3) != 0); + } | OP_LOGICNOT relocexpr %prec NEG { $$.makeUnaryOp(RPN_LOGNOT, std::move($2)); } diff --git a/test/asm/string-compare.asm b/test/asm/string-compare.asm new file mode 100644 index 00000000..930bda81 --- /dev/null +++ b/test/asm/string-compare.asm @@ -0,0 +1,7 @@ +assert "hello" === "hello" +assert "hello" !== "goodbye" +assert "game" ++ "boy" === "gameboy" +assert "fire flower" === "fire" ++ " " ++ "flower" +assert "a" === "b" == 0 +assert 1 == "a" !== "b" +assert 1 + 2 * 3 ** "x" !== "y" == 7