From 74f4e19055da0681a085e1ed936030aaac98b93a Mon Sep 17 00:00:00 2001 From: JeffVenancius Date: Thu, 27 Apr 2023 21:20:23 -0300 Subject: [PATCH] Add logical XOR to gdscript applied requested changes Co-authored-by: Danil Alexeev <47700418+dalexeev@users.noreply.github.com> --- core/math/expression.cpp | 64 +++++-------------- core/math/expression.h | 2 +- doc/classes/@GlobalScope.xml | 2 +- .../gdscript/editor/gdscript_highlighter.cpp | 10 ++- modules/gdscript/gdscript.cpp | 1 + modules/gdscript/gdscript_editor.cpp | 2 +- modules/gdscript/gdscript_parser.cpp | 10 +++ modules/gdscript/gdscript_parser.h | 2 + modules/gdscript/gdscript_tokenizer.cpp | 10 ++- modules/gdscript/gdscript_tokenizer.h | 2 + 10 files changed, 51 insertions(+), 54 deletions(-) diff --git a/core/math/expression.cpp b/core/math/expression.cpp index d1ec987d56c8..12dc82cc896c 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -186,8 +186,12 @@ Error Expression::_get_token(Token &r_token) { return OK; } case '^': { - r_token.type = TK_OP_BIT_XOR; - + if (expression[str_ofs] == '^') { + r_token.type = TK_OP_XOR; + str_ofs++; + } else { + r_token.type = TK_OP_BIT_XOR; + } return OK; } case '~': { @@ -470,10 +474,12 @@ Error Expression::_get_token(Token &r_token) { r_token.value = NAN; } else if (id == "not") { r_token.type = TK_OP_NOT; - } else if (id == "or") { - r_token.type = TK_OP_OR; } else if (id == "and") { r_token.type = TK_OP_AND; + } else if (id == "or") { + r_token.type = TK_OP_OR; + } else if (id == "xor") { + r_token.type = TK_OP_XOR; } else if (id == "self") { r_token.type = TK_SELF; } else { @@ -516,48 +522,6 @@ Error Expression::_get_token(Token &r_token) { return ERR_PARSE_ERROR; } -const char *Expression::token_name[TK_MAX] = { - "CURLY BRACKET OPEN", - "CURLY BRACKET CLOSE", - "BRACKET OPEN", - "BRACKET CLOSE", - "PARENTHESIS OPEN", - "PARENTHESIS CLOSE", - "IDENTIFIER", - "BUILTIN FUNC", - "SELF", - "CONSTANT", - "BASIC TYPE", - "COLON", - "COMMA", - "PERIOD", - "OP IN", - "OP EQUAL", - "OP NOT EQUAL", - "OP LESS", - "OP LESS EQUAL", - "OP GREATER", - "OP GREATER EQUAL", - "OP AND", - "OP OR", - "OP NOT", - "OP ADD", - "OP SUB", - "OP MUL", - "OP DIV", - "OP MOD", - "OP POW", - "OP SHIFT LEFT", - "OP SHIFT RIGHT", - "OP BIT AND", - "OP BIT OR", - "OP BIT XOR", - "OP BIT INVERT", - "OP INPUT", - "EOF", - "ERROR" -}; - Expression::ENode *Expression::_parse_expression() { Vector expression_nodes; @@ -1000,6 +964,9 @@ Expression::ENode *Expression::_parse_expression() { case TK_OP_OR: op = Variant::OP_OR; break; + case TK_OP_XOR: + op = Variant::OP_XOR; + break; case TK_OP_NOT: op = Variant::OP_NOT; break; @@ -1125,9 +1092,12 @@ Expression::ENode *Expression::_parse_expression() { case Variant::OP_AND: priority = 13; break; - case Variant::OP_OR: + case Variant::OP_XOR: priority = 14; break; + case Variant::OP_OR: + priority = 15; + break; default: { _set_error("Parser bug, invalid operator in expression: " + itos(expression_nodes[i].op)); return nullptr; diff --git a/core/math/expression.h b/core/math/expression.h index 175db4e25e19..12b10ad57b36 100644 --- a/core/math/expression.h +++ b/core/math/expression.h @@ -79,6 +79,7 @@ class Expression : public RefCounted { TK_OP_GREATER_EQUAL, TK_OP_AND, TK_OP_OR, + TK_OP_XOR, TK_OP_NOT, TK_OP_ADD, TK_OP_SUB, @@ -98,7 +99,6 @@ class Expression : public RefCounted { TK_MAX }; - static const char *token_name[TK_MAX]; struct Token { TokenType type; Variant value; diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index a0b623936a6b..c2afd88c900c 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -3056,7 +3056,7 @@ Logical OR operator ([code]or[/code] or [code]||[/code]). - Logical XOR operator (not implemented in GDScript). + Logical XOR operator ([code]xor[/code] or [code]^^[/code]). Logical NOT operator ([code]not[/code] or [code]![/code]). diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index bba11363d558..f93f5da7e635 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -466,9 +466,13 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_string_name = false; } - // '^^' has no special meaning, so unlike StringName, when binary, use NodePath color for the last caret. - if (!in_node_path && in_region == -1 && str[j] == '^' && !is_binary_op && (j == 0 || (j > 0 && str[j - 1] != '^') || prev_is_binary_op)) { - in_node_path = true; + // Keep symbol color for binary '^^'. In the case of '^^^' use NodePath color for the last caret. + if (!in_node_path && in_region == -1 && str[j] == '^' && !is_binary_op) { + if (j >= 2 && str[j - 1] == '^' && str[j - 2] != '^' && prev_is_binary_op) { + is_binary_op = true; + } else if (j == 0 || (j > 0 && str[j - 1] != '^') || prev_is_binary_op) { + in_node_path = true; + } } else if (in_region != -1 || is_a_symbol) { in_node_path = false; } diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index d0790aba2554..8f6ae0ca75ad 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2310,6 +2310,7 @@ void GDScriptLanguage::get_reserved_words(List *p_words) const { "in", "not", "or", + "xor", // types and values "false", "float", diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 829567d73477..e81b10a0b513 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1242,7 +1242,7 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context } static const char *_keywords_with_space[] = { - "and", "not", "or", "in", "as", "class", "class_name", "extends", "is", "func", "signal", "await", + "and", "not", "or", "xor", "in", "as", "class", "class_name", "extends", "is", "func", "signal", "await", "const", "enum", "static", "var", "if", "elif", "else", "for", "match", "while", nullptr }; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index d3529154cfc3..a863617a6c92 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -2491,6 +2491,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression operation->operation = BinaryOpNode::OP_LOGIC_OR; operation->variant_op = Variant::OP_OR; break; + case GDScriptTokenizer::Token::XOR: + case GDScriptTokenizer::Token::CARET_CARET: + operation->operation = BinaryOpNode::OP_LOGIC_XOR; + operation->variant_op = Variant::OP_XOR; + break; case GDScriptTokenizer::Token::IN: operation->operation = BinaryOpNode::OP_CONTENT_TEST; operation->variant_op = Variant::OP_IN; @@ -3539,9 +3544,11 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty // Logical { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_AND }, // AND, { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_OR }, // OR, + { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_XOR }, // XOR, { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_not_in_operator, PREC_CONTENT_TEST }, // NOT, { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_AND }, // AMPERSAND_AMPERSAND, { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_OR }, // PIPE_PIPE, + { nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_XOR }, // CARET_CARET, { &GDScriptParser::parse_unary_operator, nullptr, PREC_NONE }, // BANG, // Bitwise { nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_AND }, // AMPERSAND, @@ -4466,6 +4473,9 @@ void GDScriptParser::TreePrinter::print_binary_op(BinaryOpNode *p_binary_op) { case BinaryOpNode::OP_LOGIC_OR: push_text(" OR "); break; + case BinaryOpNode::OP_LOGIC_XOR: + push_text(" XOR "); + break; case BinaryOpNode::OP_CONTENT_TEST: push_text(" IN "); break; diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 8f0265510f72..6c00a213538d 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -430,6 +430,7 @@ class GDScriptParser { OP_BIT_XOR, OP_LOGIC_AND, OP_LOGIC_OR, + OP_LOGIC_XOR, OP_CONTENT_TEST, OP_COMP_EQUAL, OP_COMP_NOT_EQUAL, @@ -1334,6 +1335,7 @@ class GDScriptParser { PREC_CAST, PREC_TERNARY, PREC_LOGIC_OR, + PREC_LOGIC_XOR, PREC_LOGIC_AND, PREC_LOGIC_NOT, PREC_CONTENT_TEST, diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index a45a73a8d51d..84862636bcb9 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -56,9 +56,11 @@ static const char *token_names[] = { // Logical "and", // AND, "or", // OR, + "xor", // XOR, "not", // NOT, "&&", // AMPERSAND_AMPERSAND, "||", // PIPE_PIPE, + "^^", // CARET_CARET, "!", // BANG, // Bitwise "&", // AMPERSAND, @@ -222,6 +224,7 @@ bool GDScriptTokenizer::Token::is_node_name() const { case VAR: case VOID: case WHILE: + case XOR: case YIELD: return true; default: @@ -511,6 +514,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::annotation() { KEYWORD("void", Token::VOID) \ KEYWORD_GROUP('w') \ KEYWORD("while", Token::WHILE) \ + KEYWORD_GROUP('x') \ + KEYWORD("xor", Token::XOR) \ KEYWORD_GROUP('y') \ KEYWORD("yield", Token::YIELD) \ KEYWORD_GROUP('I') \ @@ -1474,7 +1479,10 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() { return make_token(Token::PERCENT); } case '^': - if (_peek() == '=') { + if (_peek() == '^') { + _advance(); + return make_token(Token::CARET_CARET); + } else if (_peek() == '=') { _advance(); return make_token(Token::CARET_EQUAL); } else if (_peek() == '"' || _peek() == '\'') { diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index 608840d3f1d5..2a6449146b77 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -63,9 +63,11 @@ class GDScriptTokenizer { // Logical AND, OR, + XOR, NOT, AMPERSAND_AMPERSAND, PIPE_PIPE, + CARET_CARET, BANG, // Bitwise AMPERSAND,