diff options
Diffstat (limited to 'modules/gdscript/gdscript_parser.cpp')
-rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 192 |
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; } |