Просмотр исходного кода

Merge pull request #52081 from ThreeRhinosInAnElephantCostume/fixmatchfreeze

Fix gdscript pattern matching expressions
George Marques 4 лет назад
Родитель
Сommit
1234c2bdd9

+ 42 - 43
modules/gdscript/gdscript_parser.cpp

@@ -1682,6 +1682,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
 	while (!check(GDScriptTokenizer::Token::DEDENT) && !is_at_end()) {
 		MatchBranchNode *branch = parse_match_branch();
 		if (branch == nullptr) {
+			advance();
 			continue;
 		}
 
@@ -1745,7 +1746,9 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_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.
 	bool could_continue = can_continue;
@@ -1778,15 +1781,6 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
 	PatternNode *pattern = alloc_node<PatternNode>();
 
 	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: {
 			// Bind.
 			advance();
@@ -1849,44 +1843,44 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
 			// Dictionary.
 			advance();
 			pattern->pattern_type = PatternNode::PT_DICTIONARY;
-
-			if (!check(GDScriptTokenizer::Token::BRACE_CLOSE) && !is_at_end()) {
-				do {
-					if (match(GDScriptTokenizer::Token::PERIOD_PERIOD)) {
-						// Rest.
+			do {
+				if (check(GDScriptTokenizer::Token::BRACE_CLOSE) || is_at_end()) {
+					break;
+				}
+				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) {
 							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 {
-							PatternNode *sub_pattern = alloc_node<PatternNode>();
-							sub_pattern->pattern_type = PatternNode::PT_REST;
-							pattern->dictionary.push_back({ nullptr, sub_pattern });
-							pattern->rest_used = true;
+							pattern->dictionary.push_back({ key, sub_pattern });
 						}
 					} 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) {
-								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 });
-						}
+						// 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.)");
 			break;
 		}
@@ -1895,8 +1889,13 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
 			ExpressionNode *expression = parse_expression(false);
 			if (expression == nullptr) {
 				push_error(R"(Expected expression for match pattern.)");
+				return nullptr;
 			} 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;
 			}
 			break;

+ 34 - 0
modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd

@@ -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}]])

+ 14 - 0
modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out

@@ -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

+ 27 - 0
modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd

@@ -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})

+ 10 - 0
modules/gdscript/tests/scripts/parser/features/basic_expression_matching.out

@@ -0,0 +1,10 @@
+GDTEST_OK
+wildcard
+1
+2
+[1, 2]
+wildcard
+4
+wildcard
+{1 : 2, 2 : 3}
+wildcard