summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_parser.cpp')
-rw-r--r--modules/gdscript/gdscript_parser.cpp192
1 files changed, 138 insertions, 54 deletions
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 4d4eadf0fa..49341cb670 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -121,6 +121,7 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>);
+ register_annotation(MethodInfo("@export_custom", PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_ENUM, "PropertyHint"), PropertyInfo(Variant::STRING, "hint_string"), PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_BITFIELD, "PropertyUsageFlags")), AnnotationInfo::VARIABLE, &GDScriptParser::export_custom_annotation, varray(PROPERTY_USAGE_DEFAULT));
// Export grouping annotations.
register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray(""));
@@ -3459,6 +3460,7 @@ enum DocLineState {
DOC_LINE_NORMAL,
DOC_LINE_IN_CODE,
DOC_LINE_IN_CODEBLOCK,
+ DOC_LINE_IN_KBD,
};
static String _process_doc_line(const String &p_line, const String &p_text, const String &p_space_prefix, DocLineState &r_state) {
@@ -3504,21 +3506,23 @@ static String _process_doc_line(const String &p_line, const String &p_text, cons
from = rb_pos + 1;
String tag = line.substr(lb_pos + 1, rb_pos - lb_pos - 1);
- if (tag == "code") {
+ if (tag == "code" || tag.begins_with("code ")) {
r_state = DOC_LINE_IN_CODE;
- } else if (tag == "codeblock") {
+ } else if (tag == "codeblock" || tag.begins_with("codeblock ")) {
if (lb_pos == 0) {
line_join = "\n";
} else {
result += line.substr(buffer_start, lb_pos - buffer_start) + '\n';
}
- result += "[codeblock]";
+ result += "[" + tag + "]";
if (from < len) {
result += '\n';
}
r_state = DOC_LINE_IN_CODEBLOCK;
buffer_start = from;
+ } else if (tag == "kbd") {
+ r_state = DOC_LINE_IN_KBD;
}
} break;
case DOC_LINE_IN_CODE: {
@@ -3528,7 +3532,7 @@ static String _process_doc_line(const String &p_line, const String &p_text, cons
break;
}
- from = pos + 7;
+ from = pos + 7; // `len("[/code]")`.
r_state = DOC_LINE_NORMAL;
} break;
@@ -3539,7 +3543,7 @@ static String _process_doc_line(const String &p_line, const String &p_text, cons
break;
}
- from = pos + 12;
+ from = pos + 12; // `len("[/codeblock]")`.
if (pos == 0) {
line_join = "\n";
@@ -3554,6 +3558,17 @@ static String _process_doc_line(const String &p_line, const String &p_text, cons
r_state = DOC_LINE_NORMAL;
buffer_start = from;
} break;
+ case DOC_LINE_IN_KBD: {
+ int pos = line.find("[/kbd]", from);
+ if (pos < 0) {
+ process = false;
+ break;
+ }
+
+ from = pos + 6; // `len("[/kbd]")`.
+
+ r_state = DOC_LINE_NORMAL;
+ } break;
}
}
@@ -4006,6 +4021,55 @@ bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, Node
return true;
}
+static String _get_annotation_error_string(const StringName &p_annotation_name, const Vector<Variant::Type> &p_expected_types, const GDScriptParser::DataType &p_provided_type) {
+ Vector<String> types;
+ for (int i = 0; i < p_expected_types.size(); i++) {
+ const Variant::Type &type = p_expected_types[i];
+ types.push_back(Variant::get_type_name(type));
+ types.push_back("Array[" + Variant::get_type_name(type) + "]");
+ switch (type) {
+ case Variant::INT:
+ types.push_back("PackedByteArray");
+ types.push_back("PackedInt32Array");
+ types.push_back("PackedInt64Array");
+ break;
+ case Variant::FLOAT:
+ types.push_back("PackedFloat32Array");
+ types.push_back("PackedFloat64Array");
+ break;
+ case Variant::STRING:
+ types.push_back("PackedStringArray");
+ break;
+ case Variant::VECTOR2:
+ types.push_back("PackedVector2Array");
+ break;
+ case Variant::VECTOR3:
+ types.push_back("PackedVector3Array");
+ break;
+ case Variant::COLOR:
+ types.push_back("PackedColorArray");
+ break;
+ default:
+ break;
+ }
+ }
+
+ String string;
+ if (types.size() == 1) {
+ string = types[0].quote();
+ } else if (types.size() == 2) {
+ string = types[0].quote() + " or " + types[1].quote();
+ } else if (types.size() >= 3) {
+ string = types[0].quote();
+ for (int i = 1; i < types.size() - 1; i++) {
+ string += ", " + types[i].quote();
+ }
+ string += ", or " + types[types.size() - 1].quote();
+ }
+
+ return vformat(R"("%s" annotation requires a variable of type %s, but type "%s" was given instead.)", p_annotation_name, string, p_provided_type.to_string());
+}
+
template <PropertyHint t_hint, Variant::Type t_type>
bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
ERR_FAIL_COND_V_MSG(p_target->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
@@ -4090,6 +4154,26 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
// This is called after the analyzer is done finding the type, so this should be set here.
DataType export_type = variable->get_datatype();
+
+ // Use initializer type if specified type is `Variant`.
+ if (export_type.is_variant() && variable->initializer != nullptr && variable->initializer->datatype.is_set()) {
+ export_type = variable->initializer->get_datatype();
+ export_type.type_source = DataType::INFERRED;
+ }
+
+ const Variant::Type original_export_type_builtin = export_type.builtin_type;
+
+ // Process array and packed array annotations on the element type.
+ bool is_array = false;
+ if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type(0)) {
+ is_array = true;
+ export_type = export_type.get_container_element_type(0);
+ } else if (export_type.is_typed_container_type()) {
+ is_array = true;
+ export_type = export_type.get_typed_container_type();
+ export_type.type_source = variable->datatype.type_source;
+ }
+
bool use_default_variable_type_check = true;
if (p_annotation->name == SNAME("@export_range")) {
@@ -4097,30 +4181,16 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.type = Variant::INT;
}
} else if (p_annotation->name == SNAME("@export_multiline")) {
- if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type(0)) {
- DataType inner_type = export_type.get_container_element_type(0);
- if (inner_type.builtin_type != Variant::STRING) {
- push_error(vformat(R"("%s" annotation on arrays requires a string type but type "%s" was given instead.)", p_annotation->name.operator String(), inner_type.to_string()), variable);
- return false;
- }
+ use_default_variable_type_check = false;
- String hint_prefix = itos(inner_type.builtin_type) + "/" + itos(variable->export_info.hint);
- variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
- variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
- variable->export_info.type = Variant::ARRAY;
+ if (export_type.builtin_type != Variant::STRING && export_type.builtin_type != Variant::DICTIONARY) {
+ Vector<Variant::Type> expected_types = { Variant::STRING, Variant::DICTIONARY };
+ push_error(_get_annotation_error_string(p_annotation->name, expected_types, variable->get_datatype()), p_annotation);
+ return false;
+ }
- return true;
- } else if (export_type.builtin_type == Variant::DICTIONARY) {
+ if (export_type.builtin_type == Variant::DICTIONARY) {
variable->export_info.type = Variant::DICTIONARY;
-
- return true;
- } else if (export_type.builtin_type == Variant::PACKED_STRING_ARRAY) {
- String hint_prefix = itos(Variant::STRING) + "/" + itos(variable->export_info.hint);
- variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
- variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
- variable->export_info.type = Variant::PACKED_STRING_ARRAY;
-
- return true;
}
} else if (p_annotation->name == SNAME("@export")) {
use_default_variable_type_check = false;
@@ -4130,13 +4200,6 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
return false;
}
- bool is_array = false;
-
- if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type(0)) {
- export_type = export_type.get_container_element_type(0); // Use inner type for.
- is_array = true;
- }
-
if (export_type.is_variant() || export_type.has_no_type()) {
push_error(R"(Cannot use simple "@export" annotation because the type of the initialized value can't be inferred.)", p_annotation);
return false;
@@ -4158,7 +4221,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint = PROPERTY_HINT_NODE_TYPE;
variable->export_info.hint_string = export_type.native_type;
} else {
- push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable);
+ push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", p_annotation);
return false;
}
break;
@@ -4172,7 +4235,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint = PROPERTY_HINT_NODE_TYPE;
variable->export_info.hint_string = export_type.to_string();
} else {
- push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable);
+ push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", p_annotation);
return false;
}
@@ -4223,24 +4286,14 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
}
} break;
default:
- push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable);
+ push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", p_annotation);
return false;
}
if (variable->export_info.hint == PROPERTY_HINT_NODE_TYPE && !ClassDB::is_parent_class(p_class->base_type.native_type, SNAME("Node"))) {
- push_error(vformat(R"(Node export is only supported in Node-derived classes, but the current class inherits "%s".)", p_class->base_type.to_string()), variable);
+ push_error(vformat(R"(Node export is only supported in Node-derived classes, but the current class inherits "%s".)", p_class->base_type.to_string()), p_annotation);
return false;
}
-
- if (is_array) {
- String hint_prefix = itos(variable->export_info.type);
- if (variable->export_info.hint) {
- hint_prefix += "/" + itos(variable->export_info.hint);
- }
- variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
- variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
- variable->export_info.type = Variant::ARRAY;
- }
} else if (p_annotation->name == SNAME("@export_enum")) {
use_default_variable_type_check = false;
@@ -4248,17 +4301,13 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
if (export_type.kind == DataType::BUILTIN && export_type.builtin_type == Variant::STRING) {
enum_type = Variant::STRING;
- } else if (export_type.is_variant() && variable->initializer != nullptr) {
- DataType initializer_type = variable->initializer->get_datatype();
- if (initializer_type.kind == DataType::BUILTIN && initializer_type.builtin_type == Variant::STRING) {
- enum_type = Variant::STRING;
- }
}
variable->export_info.type = enum_type;
if (!export_type.is_variant() && (export_type.kind != DataType::BUILTIN || export_type.builtin_type != enum_type)) {
- push_error(vformat(R"("@export_enum" annotation requires a variable of type "int" or "String" but type "%s" was given instead.)", export_type.to_string()), variable);
+ Vector<Variant::Type> expected_types = { Variant::INT, Variant::STRING };
+ push_error(_get_annotation_error_string(p_annotation->name, expected_types, variable->get_datatype()), p_annotation);
return false;
}
} else if (p_annotation->name == SNAME("@export_storage")) {
@@ -4274,12 +4323,47 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
if (!export_type.is_variant() && (export_type.kind != DataType::BUILTIN || export_type.builtin_type != t_type)) {
// Allow float/int conversion.
if ((t_type != Variant::FLOAT || export_type.builtin_type != Variant::INT) && (t_type != Variant::INT || export_type.builtin_type != Variant::FLOAT)) {
- push_error(vformat(R"("%s" annotation requires a variable of type "%s" but type "%s" was given instead.)", p_annotation->name.operator String(), Variant::get_type_name(t_type), export_type.to_string()), variable);
+ Vector<Variant::Type> expected_types = { t_type };
+ push_error(_get_annotation_error_string(p_annotation->name, expected_types, variable->get_datatype()), p_annotation);
return false;
}
}
}
+ if (is_array) {
+ String hint_prefix = itos(variable->export_info.type);
+ if (variable->export_info.hint) {
+ hint_prefix += "/" + itos(variable->export_info.hint);
+ }
+ variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
+ variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
+ variable->export_info.type = original_export_type_builtin;
+ }
+
+ return true;
+}
+
+bool GDScriptParser::export_custom_annotation(const AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) {
+ ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
+ ERR_FAIL_COND_V_MSG(p_annotation->resolved_arguments.size() < 2, false, R"(Annotation "@export_custom" requires 2 arguments.)");
+
+ VariableNode *variable = static_cast<VariableNode *>(p_node);
+ if (variable->exported) {
+ push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
+ return false;
+ }
+
+ variable->exported = true;
+
+ DataType export_type = variable->get_datatype();
+
+ variable->export_info.type = export_type.builtin_type;
+ variable->export_info.hint = static_cast<PropertyHint>(p_annotation->resolved_arguments[0].operator int64_t());
+ variable->export_info.hint_string = p_annotation->resolved_arguments[1];
+
+ if (p_annotation->resolved_arguments.size() >= 3) {
+ variable->export_info.usage = p_annotation->resolved_arguments[2].operator int64_t();
+ }
return true;
}