GDScript: Implement pattern guards for match statement

Within a match statement, it is now possible to add guards in each
branch:

	var a = 0
	match a:
		0 when false: print("does not run")
		0 when true: print("but this does")

This allows more complex logic for deciding which branch to take.
This commit is contained in:
George Marques 2023-07-31 07:47:26 -03:00
parent ec62b8a3ee
commit 54a1414500
No known key found for this signature in database
GPG key ID: 046BD46A3201E43D
15 changed files with 163 additions and 1 deletions

View file

@ -2035,7 +2035,37 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
push_error(R"(No pattern found for "match" branch.)");
}
if (!consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)")) {
bool has_guard = false;
if (match(GDScriptTokenizer::Token::WHEN)) {
// Pattern guard.
// Create block for guard because it also needs to access the bound variables from patterns, and we don't want to add them to the outer scope.
branch->guard_body = alloc_node<SuiteNode>();
if (branch->patterns.size() > 0) {
for (const KeyValue<StringName, IdentifierNode *> &E : branch->patterns[0]->binds) {
SuiteNode::Local local(E.value, current_function);
local.type = SuiteNode::Local::PATTERN_BIND;
branch->guard_body->add_local(local);
}
}
SuiteNode *parent_block = current_suite;
branch->guard_body->parent_block = parent_block;
current_suite = branch->guard_body;
ExpressionNode *guard = parse_expression(false);
if (guard == nullptr) {
push_error(R"(Expected expression for pattern guard after "when".)");
} else {
branch->guard_body->statements.append(guard);
}
current_suite = parent_block;
complete_extents(branch->guard_body);
has_guard = true;
branch->has_wildcard = false; // If it has a guard, the wildcard might still not match.
}
if (!consume(GDScriptTokenizer::Token::COLON, vformat(R"(Expected ":"%s after "match" %s.)", has_guard ? "" : R"( or "when")", has_guard ? "pattern guard" : "patterns"))) {
complete_extents(branch);
return nullptr;
}
@ -3674,6 +3704,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ nullptr, nullptr, PREC_NONE }, // PASS,
{ nullptr, nullptr, PREC_NONE }, // RETURN,
{ nullptr, nullptr, PREC_NONE }, // MATCH,
{ nullptr, nullptr, PREC_NONE }, // WHEN,
// Keywords
{ nullptr, &GDScriptParser::parse_cast, PREC_CAST }, // AS,
{ nullptr, nullptr, PREC_NONE }, // ASSERT,