Fixed pattern matching with expressions

This commit is contained in:
ThreeRhinosInAnElephantCostume
2021-08-24 21:27:17 +02:00
parent ca7f53dd25
commit 6c258a89de

View File

@ -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,8 @@ 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 +1780,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 +1842,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 +1888,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;