Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add logical XOR to GDScript #76191

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 17 additions & 47 deletions core/math/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 '~': {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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<ExpressionNode> expression_nodes;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion core/math/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -98,7 +99,6 @@ class Expression : public RefCounted {
TK_MAX
};

static const char *token_name[TK_MAX];
struct Token {
TokenType type;
Variant value;
Expand Down
2 changes: 1 addition & 1 deletion doc/classes/@GlobalScope.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3056,7 +3056,7 @@
Logical OR operator ([code]or[/code] or [code]||[/code]).
</constant>
<constant name="OP_XOR" value="22" enum="Variant.Operator">
Logical XOR operator (not implemented in GDScript).
Logical XOR operator ([code]xor[/code] or [code]^^[/code]).
</constant>
<constant name="OP_NOT" value="23" enum="Variant.Operator">
Logical NOT operator ([code]not[/code] or [code]![/code]).
Expand Down
10 changes: 7 additions & 3 deletions modules/gdscript/editor/gdscript_highlighter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions modules/gdscript/gdscript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2310,6 +2310,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"in",
"not",
"or",
"xor",
// types and values
"false",
"float",
Expand Down
2 changes: 1 addition & 1 deletion modules/gdscript/gdscript_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down
10 changes: 10 additions & 0 deletions modules/gdscript/gdscript_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions modules/gdscript/gdscript_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
10 changes: 9 additions & 1 deletion modules/gdscript/gdscript_tokenizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -222,6 +224,7 @@ bool GDScriptTokenizer::Token::is_node_name() const {
case VAR:
case VOID:
case WHILE:
case XOR:
case YIELD:
return true;
default:
Expand Down Expand Up @@ -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') \
Expand Down Expand Up @@ -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() == '\'') {
Expand Down
2 changes: 2 additions & 0 deletions modules/gdscript/gdscript_tokenizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,11 @@ class GDScriptTokenizer {
// Logical
AND,
OR,
XOR,
NOT,
AMPERSAND_AMPERSAND,
PIPE_PIPE,
CARET_CARET,
BANG,
// Bitwise
AMPERSAND,
Expand Down