You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-23 15:16:17 +00:00
Merge pull request #52081 from ThreeRhinosInAnElephantCostume/fixmatchfreeze
Fix gdscript pattern matching expressions
This commit is contained in:
@@ -1682,6 +1682,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
|
|||||||
while (!check(GDScriptTokenizer::Token::DEDENT) && !is_at_end()) {
|
while (!check(GDScriptTokenizer::Token::DEDENT) && !is_at_end()) {
|
||||||
MatchBranchNode *branch = parse_match_branch();
|
MatchBranchNode *branch = parse_match_branch();
|
||||||
if (branch == nullptr) {
|
if (branch == nullptr) {
|
||||||
|
advance();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1745,7 +1746,9 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
|
|||||||
push_error(R"(No pattern found for "match" branch.)");
|
push_error(R"(No pattern found for "match" branch.)");
|
||||||
}
|
}
|
||||||
|
|
||||||
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)");
|
if (!consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)")) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Save continue state.
|
// Save continue state.
|
||||||
bool could_continue = can_continue;
|
bool could_continue = can_continue;
|
||||||
@@ -1778,15 +1781,6 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
|
|||||||
PatternNode *pattern = alloc_node<PatternNode>();
|
PatternNode *pattern = alloc_node<PatternNode>();
|
||||||
|
|
||||||
switch (current.type) {
|
switch (current.type) {
|
||||||
case GDScriptTokenizer::Token::LITERAL:
|
|
||||||
advance();
|
|
||||||
pattern->pattern_type = PatternNode::PT_LITERAL;
|
|
||||||
pattern->literal = parse_literal();
|
|
||||||
if (pattern->literal == nullptr) {
|
|
||||||
// Error happened.
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GDScriptTokenizer::Token::VAR: {
|
case GDScriptTokenizer::Token::VAR: {
|
||||||
// Bind.
|
// Bind.
|
||||||
advance();
|
advance();
|
||||||
@@ -1849,44 +1843,44 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
|
|||||||
// Dictionary.
|
// Dictionary.
|
||||||
advance();
|
advance();
|
||||||
pattern->pattern_type = PatternNode::PT_DICTIONARY;
|
pattern->pattern_type = PatternNode::PT_DICTIONARY;
|
||||||
|
do {
|
||||||
if (!check(GDScriptTokenizer::Token::BRACE_CLOSE) && !is_at_end()) {
|
if (check(GDScriptTokenizer::Token::BRACE_CLOSE) || is_at_end()) {
|
||||||
do {
|
break;
|
||||||
if (match(GDScriptTokenizer::Token::PERIOD_PERIOD)) {
|
}
|
||||||
// Rest.
|
if (match(GDScriptTokenizer::Token::PERIOD_PERIOD)) {
|
||||||
|
// Rest.
|
||||||
|
if (pattern->rest_used) {
|
||||||
|
push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
|
||||||
|
} else {
|
||||||
|
PatternNode *sub_pattern = alloc_node<PatternNode>();
|
||||||
|
sub_pattern->pattern_type = PatternNode::PT_REST;
|
||||||
|
pattern->dictionary.push_back({ nullptr, sub_pattern });
|
||||||
|
pattern->rest_used = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ExpressionNode *key = parse_expression(false);
|
||||||
|
if (key == nullptr) {
|
||||||
|
push_error(R"(Expected expression as key for dictionary pattern.)");
|
||||||
|
}
|
||||||
|
if (match(GDScriptTokenizer::Token::COLON)) {
|
||||||
|
// Value pattern.
|
||||||
|
PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
|
||||||
|
if (sub_pattern == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (pattern->rest_used) {
|
if (pattern->rest_used) {
|
||||||
push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
|
push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
|
||||||
|
} else if (sub_pattern->pattern_type == PatternNode::PT_REST) {
|
||||||
|
push_error(R"(The ".." pattern cannot be used as a value.)");
|
||||||
} else {
|
} else {
|
||||||
PatternNode *sub_pattern = alloc_node<PatternNode>();
|
pattern->dictionary.push_back({ key, sub_pattern });
|
||||||
sub_pattern->pattern_type = PatternNode::PT_REST;
|
|
||||||
pattern->dictionary.push_back({ nullptr, sub_pattern });
|
|
||||||
pattern->rest_used = true;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ExpressionNode *key = parse_expression(false);
|
// Key match only.
|
||||||
if (key == nullptr) {
|
pattern->dictionary.push_back({ key, nullptr });
|
||||||
push_error(R"(Expected expression as key for dictionary pattern.)");
|
|
||||||
}
|
|
||||||
if (match(GDScriptTokenizer::Token::COLON)) {
|
|
||||||
// Value pattern.
|
|
||||||
PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
|
|
||||||
if (sub_pattern == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (pattern->rest_used) {
|
|
||||||
push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
|
|
||||||
} else if (sub_pattern->pattern_type == PatternNode::PT_REST) {
|
|
||||||
push_error(R"(The ".." pattern cannot be used as a value.)");
|
|
||||||
} else {
|
|
||||||
pattern->dictionary.push_back({ key, sub_pattern });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Key match only.
|
|
||||||
pattern->dictionary.push_back({ key, nullptr });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} while (match(GDScriptTokenizer::Token::COMMA));
|
}
|
||||||
}
|
} while (match(GDScriptTokenizer::Token::COMMA));
|
||||||
consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected "}" to close the dictionary pattern.)");
|
consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected "}" to close the dictionary pattern.)");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1895,8 +1889,13 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
|
|||||||
ExpressionNode *expression = parse_expression(false);
|
ExpressionNode *expression = parse_expression(false);
|
||||||
if (expression == nullptr) {
|
if (expression == nullptr) {
|
||||||
push_error(R"(Expected expression for match pattern.)");
|
push_error(R"(Expected expression for match pattern.)");
|
||||||
|
return nullptr;
|
||||||
} else {
|
} else {
|
||||||
pattern->pattern_type = PatternNode::PT_EXPRESSION;
|
if (expression->type == GDScriptParser::Node::LITERAL) {
|
||||||
|
pattern->pattern_type = PatternNode::PT_LITERAL;
|
||||||
|
} else {
|
||||||
|
pattern->pattern_type = PatternNode::PT_EXPRESSION;
|
||||||
|
}
|
||||||
pattern->expression = expression;
|
pattern->expression = expression;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
func foo(x):
|
||||||
|
match x:
|
||||||
|
1 + 1:
|
||||||
|
print("1+1")
|
||||||
|
[1,2,[1,{1:2,2:var z,..}]]:
|
||||||
|
print("[1,2,[1,{1:2,2:var z,..}]]")
|
||||||
|
print(z)
|
||||||
|
1 if true else 2:
|
||||||
|
print("1 if true else 2")
|
||||||
|
1 < 2:
|
||||||
|
print("1 < 2")
|
||||||
|
1 or 2 and 1:
|
||||||
|
print("1 or 2 and 1")
|
||||||
|
6 | 1:
|
||||||
|
print("1 | 1")
|
||||||
|
1 >> 1:
|
||||||
|
print("1 >> 1")
|
||||||
|
1, 2 or 3, 4:
|
||||||
|
print("1, 2 or 3, 4")
|
||||||
|
_:
|
||||||
|
print("wildcard")
|
||||||
|
|
||||||
|
func test():
|
||||||
|
foo(6 | 1)
|
||||||
|
foo(1 >> 1)
|
||||||
|
foo(2)
|
||||||
|
foo(1)
|
||||||
|
foo(1+1)
|
||||||
|
foo(1 < 2)
|
||||||
|
foo([2, 1])
|
||||||
|
foo(4)
|
||||||
|
foo([1, 2, [1, {1 : 2, 2:3}]])
|
||||||
|
foo([1, 2, [1, {1 : 2, 2:[1,3,5, "123"], 4:2}]])
|
||||||
|
foo([1, 2, [1, {1 : 2}]])
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
1 | 1
|
||||||
|
1 >> 1
|
||||||
|
1+1
|
||||||
|
1 if true else 2
|
||||||
|
1+1
|
||||||
|
1 < 2
|
||||||
|
wildcard
|
||||||
|
1, 2 or 3, 4
|
||||||
|
[1,2,[1,{1:2,2:var z,..}]]
|
||||||
|
3
|
||||||
|
[1,2,[1,{1:2,2:var z,..}]]
|
||||||
|
[1, 3, 5, 123]
|
||||||
|
wildcard
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
func foo(x):
|
||||||
|
match x:
|
||||||
|
1:
|
||||||
|
print("1")
|
||||||
|
2:
|
||||||
|
print("2")
|
||||||
|
[1, 2]:
|
||||||
|
print("[1, 2]")
|
||||||
|
3 or 4:
|
||||||
|
print("3 or 4")
|
||||||
|
4:
|
||||||
|
print("4")
|
||||||
|
{1 : 2, 2 : 3}:
|
||||||
|
print("{1 : 2, 2 : 3}")
|
||||||
|
_:
|
||||||
|
print("wildcard")
|
||||||
|
|
||||||
|
func test():
|
||||||
|
foo(0)
|
||||||
|
foo(1)
|
||||||
|
foo(2)
|
||||||
|
foo([1, 2])
|
||||||
|
foo(3)
|
||||||
|
foo(4)
|
||||||
|
foo([4,4])
|
||||||
|
foo({1 : 2, 2 : 3})
|
||||||
|
foo({1 : 2, 4 : 3})
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
wildcard
|
||||||
|
1
|
||||||
|
2
|
||||||
|
[1, 2]
|
||||||
|
wildcard
|
||||||
|
4
|
||||||
|
wildcard
|
||||||
|
{1 : 2, 2 : 3}
|
||||||
|
wildcard
|
||||||
Reference in New Issue
Block a user