diff options
Diffstat (limited to 'modules/gdscript/gdscript_parser.cpp')
-rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 208 |
1 files changed, 148 insertions, 60 deletions
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index bb5d6770c6..aaa09134f0 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -101,7 +101,6 @@ GDScriptParser::GDScriptParser() { register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation); // Export annotations. register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>); - register_annotation(MethodInfo("@export_storage"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>); register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true); register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true); register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>); @@ -121,6 +120,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_storage"), AnnotationInfo::VARIABLE, &GDScriptParser::export_storage_annotation); 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>); @@ -137,33 +137,38 @@ GDScriptParser::GDScriptParser() { #endif #ifdef TOOLS_ENABLED - if (theme_color_names.is_empty()) { + if (unlikely(theme_color_names.is_empty())) { + // Vectors. theme_color_names.insert("x", "axis_x_color"); theme_color_names.insert("y", "axis_y_color"); theme_color_names.insert("z", "axis_z_color"); theme_color_names.insert("w", "axis_w_color"); + + // Color. + theme_color_names.insert("r", "axis_x_color"); + theme_color_names.insert("r8", "axis_x_color"); + theme_color_names.insert("g", "axis_y_color"); + theme_color_names.insert("g8", "axis_y_color"); + theme_color_names.insert("b", "axis_z_color"); + theme_color_names.insert("b8", "axis_z_color"); + theme_color_names.insert("a", "axis_w_color"); + theme_color_names.insert("a8", "axis_w_color"); } #endif } GDScriptParser::~GDScriptParser() { - clear(); -} - -void GDScriptParser::clear() { while (list != nullptr) { Node *element = list; list = list->next; memdelete(element); } +} - head = nullptr; - list = nullptr; - _is_tool = false; - for_completion = false; - errors.clear(); - multiline_stack.clear(); - nodes_in_progress.clear(); +void GDScriptParser::clear() { + GDScriptParser tmp; + tmp = *this; + *this = GDScriptParser(); } void GDScriptParser::push_error(const String &p_message, const Node *p_origin) { @@ -172,7 +177,7 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) { panic_mode = true; // TODO: Improve positional information. if (p_origin == nullptr) { - errors.push_back({ p_message, current.start_line, current.start_column }); + errors.push_back({ p_message, previous.start_line, previous.start_column }); } else { errors.push_back({ p_message, p_origin->start_line, p_origin->leftmost_column }); } @@ -709,6 +714,25 @@ void GDScriptParser::parse_program() { clear_unused_annotations(); } +Ref<GDScriptParserRef> GDScriptParser::get_depended_parser_for(const String &p_path) { + Ref<GDScriptParserRef> ref; + if (depended_parsers.has(p_path)) { + ref = depended_parsers[p_path]; + } else { + Error err = OK; + ref = GDScriptCache::get_parser(p_path, GDScriptParserRef::EMPTY, err, script_path); + if (ref.is_valid()) { + depended_parsers[p_path] = ref; + } + } + + return ref; +} + +const HashMap<String, Ref<GDScriptParserRef>> &GDScriptParser::get_depended_parsers() { + return depended_parsers; +} + GDScriptParser::ClassNode *GDScriptParser::find_class(const String &p_qualified_name) const { String first = p_qualified_name.get_slice("::", 0); @@ -1864,6 +1888,10 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { case Node::CALL: // Fine. break; + case Node::PRELOAD: + // `preload` is a function-like keyword. + push_warning(expression, GDScriptWarning::RETURN_VALUE_DISCARDED, "preload"); + break; case Node::LAMBDA: // Standalone lambdas can't be used, so make this an error. push_error("Standalone lambdas cannot be accessed. Consider assigning it to a variable.", expression); @@ -3186,6 +3214,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre } GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) { + // We want code completion after a DOLLAR even if the current code is invalid. + make_completion_context(COMPLETION_GET_NODE, nullptr, -1, true); + if (!current.is_node_name() && !check(GDScriptTokenizer::Token::LITERAL) && !check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) { push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name())); return nullptr; @@ -3241,7 +3272,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p path_state = PATH_STATE_SLASH; } - make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++); + make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++, true); if (match(GDScriptTokenizer::Token::LITERAL)) { if (previous.literal.get_type() != Variant::STRING) { @@ -4071,6 +4102,7 @@ bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, Node if (current_class && !ClassDB::is_parent_class(current_class->get_datatype().native_type, SNAME("Node"))) { push_error(R"("@onready" can only be used in classes that inherit "Node".)", p_annotation); + return false; } VariableNode *variable = static_cast<VariableNode *>(p_target); @@ -4115,6 +4147,9 @@ static String _get_annotation_error_string(const StringName &p_annotation_name, case Variant::COLOR: types.push_back("PackedColorArray"); break; + case Variant::VECTOR4: + types.push_back("PackedVector4Array"); + break; default: break; } @@ -4136,6 +4171,64 @@ static String _get_annotation_error_string(const StringName &p_annotation_name, 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()); } +static StringName _find_narrowest_native_or_global_class(const GDScriptParser::DataType &p_type) { + switch (p_type.kind) { + case GDScriptParser::DataType::NATIVE: { + if (p_type.is_meta_type) { + return Object::get_class_static(); // `GDScriptNativeClass` is not an exposed class. + } + return p_type.native_type; + } break; + case GDScriptParser::DataType::SCRIPT: { + Ref<Script> script; + if (p_type.script_type.is_valid()) { + script = p_type.script_type; + } else { + script = ResourceLoader::load(p_type.script_path, SNAME("Script")); + } + + if (p_type.is_meta_type) { + return script.is_valid() ? script->get_class() : Script::get_class_static(); + } + if (script.is_null()) { + return p_type.native_type; + } + if (script->get_global_name() != StringName()) { + return script->get_global_name(); + } + + Ref<Script> base_script = script->get_base_script(); + if (base_script.is_null()) { + return script->get_instance_base_type(); + } + + GDScriptParser::DataType base_type; + base_type.kind = GDScriptParser::DataType::SCRIPT; + base_type.builtin_type = Variant::OBJECT; + base_type.native_type = base_script->get_instance_base_type(); + base_type.script_type = base_script; + base_type.script_path = base_script->get_path(); + + return _find_narrowest_native_or_global_class(base_type); + } break; + case GDScriptParser::DataType::CLASS: { + if (p_type.is_meta_type) { + return GDScript::get_class_static(); + } + if (p_type.class_type == nullptr) { + return p_type.native_type; + } + if (p_type.class_type->get_global_name() != StringName()) { + return p_type.class_type->get_global_name(); + } + return _find_narrowest_native_or_global_class(p_type.class_type->base_type); + } break; + default: { + ERR_FAIL_V(StringName()); + } break; + } +} + 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)); @@ -4275,56 +4368,24 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node case GDScriptParser::DataType::BUILTIN: variable->export_info.type = export_type.builtin_type; variable->export_info.hint = PROPERTY_HINT_NONE; - variable->export_info.hint_string = Variant::get_type_name(export_type.builtin_type); + variable->export_info.hint_string = String(); break; case GDScriptParser::DataType::NATIVE: + case GDScriptParser::DataType::SCRIPT: + case GDScriptParser::DataType::CLASS: { + const StringName class_name = _find_narrowest_native_or_global_class(export_type); if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) { variable->export_info.type = Variant::OBJECT; variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE; - variable->export_info.hint_string = export_type.native_type; - } else if (ClassDB::is_parent_class(export_type.native_type, SNAME("Node"))) { - variable->export_info.type = Variant::OBJECT; - 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.)", p_annotation); - return false; - } - break; - case GDScriptParser::DataType::CLASS: - if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) { - variable->export_info.type = Variant::OBJECT; - variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE; - variable->export_info.hint_string = export_type.to_string(); + variable->export_info.hint_string = class_name; } else if (ClassDB::is_parent_class(export_type.native_type, SNAME("Node"))) { variable->export_info.type = Variant::OBJECT; variable->export_info.hint = PROPERTY_HINT_NODE_TYPE; - variable->export_info.hint_string = export_type.to_string(); + variable->export_info.hint_string = class_name; } else { push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", p_annotation); return false; } - - break; - case GDScriptParser::DataType::SCRIPT: { - StringName class_name; - StringName native_base; - if (export_type.script_type.is_valid()) { - class_name = export_type.script_type->get_language()->get_global_class_name(export_type.script_type->get_path()); - native_base = export_type.script_type->get_instance_base_type(); - } - if (class_name == StringName()) { - Ref<Script> script = ResourceLoader::load(export_type.script_path, SNAME("Script")); - if (script.is_valid()) { - class_name = script->get_language()->get_global_class_name(export_type.script_path); - native_base = script->get_instance_base_type(); - } - } - if (class_name != StringName() && native_base != StringName() && ClassDB::is_parent_class(native_base, SNAME("Resource"))) { - variable->export_info.type = Variant::OBJECT; - variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE; - variable->export_info.hint_string = class_name; - } } break; case GDScriptParser::DataType::ENUM: { if (export_type.is_meta_type) { @@ -4376,12 +4437,6 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node 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")) { - use_default_variable_type_check = false; // Can be applied to a variable of any type. - - // Save the info because the compiler uses export info for overwriting member info. - variable->export_info = export_type.to_property_info(variable->identifier->name); - variable->export_info.usage |= PROPERTY_USAGE_STORAGE; } if (use_default_variable_type_check) { @@ -4401,10 +4456,37 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node if (variable->export_info.hint) { hint_prefix += "/" + itos(variable->export_info.hint); } + variable->export_info.type = original_export_type_builtin; 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; + variable->export_info.usage = PROPERTY_USAGE_DEFAULT; + variable->export_info.class_name = StringName(); + } + + return true; +} + +// For `@export_storage` and `@export_custom`, there is no need to check the variable type, argument values, +// or handle array exports in a special way, so they are implemented as separate methods. + +bool GDScriptParser::export_storage_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)); + + VariableNode *variable = static_cast<VariableNode *>(p_node); + if (variable->is_static) { + push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation); + return false; } + 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; + + // Save the info because the compiler uses export info for overwriting member info. + variable->export_info = variable->get_datatype().to_property_info(variable->identifier->name); + variable->export_info.usage |= PROPERTY_USAGE_STORAGE; return true; } @@ -4414,6 +4496,10 @@ bool GDScriptParser::export_custom_annotation(const AnnotationNode *p_annotation 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->is_static) { + push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation); + return false; + } if (variable->exported) { push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation); return false; @@ -4813,6 +4899,8 @@ static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_t return Variant::VECTOR3; case Variant::PACKED_COLOR_ARRAY: return Variant::COLOR; + case Variant::PACKED_VECTOR4_ARRAY: + return Variant::VECTOR4; default: return Variant::NIL; } |