Browse Source

GDScript: Add constant `Array` and `Dictionary` constructors

Danil Alexeev 4 months ago
parent
commit
7721e13a92

+ 63 - 11
modules/gdscript/gdscript_analyzer.cpp

@@ -5197,24 +5197,29 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
 }
 }
 
 
 Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &is_reduced) {
 Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &is_reduced) {
-	Variant value;
-
 	if (p_expression == nullptr) {
 	if (p_expression == nullptr) {
-		return value;
+		return Variant();
 	}
 	}
 
 
 	if (p_expression->is_constant) {
 	if (p_expression->is_constant) {
 		is_reduced = true;
 		is_reduced = true;
-		value = p_expression->reduced_value;
-	} else if (p_expression->type == GDScriptParser::Node::ARRAY) {
-		value = make_array_reduced_value(static_cast<GDScriptParser::ArrayNode *>(p_expression), is_reduced);
-	} else if (p_expression->type == GDScriptParser::Node::DICTIONARY) {
-		value = make_dictionary_reduced_value(static_cast<GDScriptParser::DictionaryNode *>(p_expression), is_reduced);
-	} else if (p_expression->type == GDScriptParser::Node::SUBSCRIPT) {
-		value = make_subscript_reduced_value(static_cast<GDScriptParser::SubscriptNode *>(p_expression), is_reduced);
+		return p_expression->reduced_value;
 	}
 	}
 
 
-	return value;
+	switch (p_expression->type) {
+		case GDScriptParser::Node::ARRAY:
+			return make_array_reduced_value(static_cast<GDScriptParser::ArrayNode *>(p_expression), is_reduced);
+		case GDScriptParser::Node::DICTIONARY:
+			return make_dictionary_reduced_value(static_cast<GDScriptParser::DictionaryNode *>(p_expression), is_reduced);
+		case GDScriptParser::Node::SUBSCRIPT:
+			return make_subscript_reduced_value(static_cast<GDScriptParser::SubscriptNode *>(p_expression), is_reduced);
+		case GDScriptParser::Node::CALL:
+			return make_call_reduced_value(static_cast<GDScriptParser::CallNode *>(p_expression), is_reduced);
+		default:
+			break;
+	}
+
+	return Variant();
 }
 }
 
 
 Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced) {
 Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced) {
@@ -5306,6 +5311,53 @@ Variant GDScriptAnalyzer::make_subscript_reduced_value(GDScriptParser::Subscript
 	}
 	}
 }
 }
 
 
+Variant GDScriptAnalyzer::make_call_reduced_value(GDScriptParser::CallNode *p_call, bool &is_reduced) {
+	if (p_call->get_callee_type() == GDScriptParser::Node::IDENTIFIER) {
+		Variant::Type type = Variant::NIL;
+		if (p_call->function_name == SNAME("Array")) {
+			type = Variant::ARRAY;
+		} else if (p_call->function_name == SNAME("Dictionary")) {
+			type = Variant::DICTIONARY;
+		} else {
+			return Variant();
+		}
+
+		Vector<Variant> args;
+		args.resize(p_call->arguments.size());
+		const Variant **argptrs = (const Variant **)alloca(sizeof(const Variant **) * args.size());
+		for (int i = 0; i < p_call->arguments.size(); i++) {
+			bool is_arg_value_reduced = false;
+			Variant arg_value = make_expression_reduced_value(p_call->arguments[i], is_arg_value_reduced);
+			if (!is_arg_value_reduced) {
+				return Variant();
+			}
+			args.write[i] = arg_value;
+			argptrs[i] = &args[i];
+		}
+
+		Variant result;
+		Callable::CallError ce;
+		Variant::construct(type, result, argptrs, args.size(), ce);
+		if (ce.error) {
+			push_error(vformat(R"(Failed to construct "%s".)", Variant::get_type_name(type)), p_call);
+			return Variant();
+		}
+
+		if (type == Variant::ARRAY) {
+			Array array = result;
+			array.make_read_only();
+		} else if (type == Variant::DICTIONARY) {
+			Dictionary dictionary = result;
+			dictionary.make_read_only();
+		}
+
+		is_reduced = true;
+		return result;
+	}
+
+	return Variant();
+}
+
 Array GDScriptAnalyzer::make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node) {
 Array GDScriptAnalyzer::make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node) {
 	Array array;
 	Array array;
 
 

+ 1 - 0
modules/gdscript/gdscript_analyzer.h

@@ -120,6 +120,7 @@ class GDScriptAnalyzer {
 	Variant make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced);
 	Variant make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced);
 	Variant make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &is_reduced);
 	Variant make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &is_reduced);
 	Variant make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &is_reduced);
 	Variant make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &is_reduced);
+	Variant make_call_reduced_value(GDScriptParser::CallNode *p_call, bool &is_reduced);
 
 
 	// Helpers.
 	// Helpers.
 	Array make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node = nullptr);
 	Array make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node = nullptr);

+ 64 - 0
modules/gdscript/tests/scripts/analyzer/features/const_array_and_dictionary_constructors.gd

@@ -0,0 +1,64 @@
+const A1 = Array()
+const A2 = Array(Array())
+const A3 = Array([])
+const A4 = [Array()]
+const A5 = [[]]
+const A6 = Array([1], TYPE_INT, &"", null)
+
+const D1 = Dictionary()
+const D2 = Dictionary(Dictionary())
+const D3 = Dictionary({})
+const D4 = { Dictionary(): Dictionary() }
+const D5 = { {}: {} }
+const D6 = Dictionary({ 1: 1 }, TYPE_INT, &"", null, TYPE_INT, &"", null)
+
+var a1 = Array()
+var a2 = Array(Array())
+var a3 = Array([])
+var a4 = [Array()]
+var a5 = [[]]
+var a6 = Array([1], TYPE_INT, &"", null)
+
+var d1 = Dictionary()
+var d2 = Dictionary(Dictionary())
+var d3 = Dictionary({})
+var d4 = { Dictionary(): Dictionary() }
+var d5 = { {}: {} }
+var d6 = Dictionary({ 1: 1 }, TYPE_INT, &"", null, TYPE_INT, &"", null)
+
+func test_value(value: Variant) -> void:
+	@warning_ignore("unsafe_method_access")
+	prints(value.is_read_only(), var_to_str(value).replace("\n", " "))
+
+func test():
+	print('---')
+	test_value(A1)
+	test_value(A2)
+	test_value(A3)
+	test_value(A4)
+	test_value(A5)
+	test_value(A6)
+
+	print('---')
+	test_value(D1)
+	test_value(D2)
+	test_value(D3)
+	test_value(D4)
+	test_value(D5)
+	test_value(D6)
+
+	print('---')
+	test_value(a1)
+	test_value(a2)
+	test_value(a3)
+	test_value(a4)
+	test_value(a5)
+	test_value(a6)
+
+	print('---')
+	test_value(d1)
+	test_value(d2)
+	test_value(d3)
+	test_value(d4)
+	test_value(d5)
+	test_value(d6)

+ 29 - 0
modules/gdscript/tests/scripts/analyzer/features/const_array_and_dictionary_constructors.out

@@ -0,0 +1,29 @@
+GDTEST_OK
+---
+true []
+true []
+true []
+true [[]]
+true [[]]
+true Array[int]([1])
+---
+true {}
+true {}
+true {}
+true { {}: {} }
+true { {}: {} }
+true Dictionary[int, int]({ 1: 1 })
+---
+false []
+false []
+false []
+false [[]]
+false [[]]
+false Array[int]([1])
+---
+false {}
+false {}
+false {}
+false { {}: {} }
+false { {}: {} }
+false Dictionary[int, int]({ 1: 1 })