From d45e1befe3be6054f2ad19c2130c66d5c3a84307 Mon Sep 17 00:00:00 2001 From: George Marques Date: Mon, 17 Aug 2020 19:49:54 -0300 Subject: GDScript: Fix wrong argument check for formatting operator --- modules/gdscript/gdscript_analyzer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules/gdscript') diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 561cdbbda4..c1c1cc80fc 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2883,7 +2883,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator } // Avoid error in formatting operator (%) where it doesn't find a placeholder. - if (a_type == Variant::STRING) { + if (a_type == Variant::STRING && b_type != Variant::ARRAY) { a = String("%s"); } -- cgit v1.2.3 From 9ecd042e785198fcad3847463aff345bf996a640 Mon Sep 17 00:00:00 2001 From: George Marques Date: Mon, 17 Aug 2020 20:14:46 -0300 Subject: GDScript: Allow "match" to be used as an identifier This is needed to call the String.match() function. --- modules/gdscript/gdscript_tokenizer.cpp | 12 ++++++++++++ modules/gdscript/gdscript_tokenizer.h | 5 ++--- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'modules/gdscript') diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp index 7a4bdd88ba..8ced06e2fd 100644 --- a/modules/gdscript/gdscript_tokenizer.cpp +++ b/modules/gdscript/gdscript_tokenizer.cpp @@ -156,6 +156,18 @@ const char *GDScriptTokenizer::Token::get_name() const { return token_names[type]; } +bool GDScriptTokenizer::Token::is_identifier() const { + // Note: Most keywords should not be recognized as identifiers. + // These are only exceptions for stuff that already is on the engine's API. + switch (type) { + case IDENTIFIER: + case MATCH: // Used in String.match(). + return true; + default: + return false; + } +} + String GDScriptTokenizer::get_token_name(Token::Type p_token_type) { ERR_FAIL_INDEX_V_MSG(p_token_type, Token::TK_MAX, "", "Using token type out of the enum."); return token_names[p_token_type]; diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h index 059a226924..956530341c 100644 --- a/modules/gdscript/gdscript_tokenizer.h +++ b/modules/gdscript/gdscript_tokenizer.h @@ -168,9 +168,8 @@ public: String source; const char *get_name() const; - // TODO: Allow some keywords as identifiers? - bool is_identifier() const { return type == IDENTIFIER; } - StringName get_identifier() const { return literal; } + bool is_identifier() const; + StringName get_identifier() const { return source; } Token(Type p_type) { type = p_type; -- cgit v1.2.3 From f374021d52990189d41ca1c3f9353e381e4c5c4e Mon Sep 17 00:00:00 2001 From: George Marques Date: Mon, 17 Aug 2020 20:49:04 -0300 Subject: GDSript: Prevent crash when completing unary operators --- modules/gdscript/gdscript_analyzer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'modules/gdscript') diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index c1c1cc80fc..0768dd05e5 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2509,6 +2509,12 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) GDScriptParser::DataType result; + if (p_unary_op->operand == nullptr) { + result.kind = GDScriptParser::DataType::VARIANT; + p_unary_op->set_datatype(result); + return; + } + if (p_unary_op->operand->is_constant) { p_unary_op->is_constant = true; p_unary_op->reduced_value = Variant::evaluate(p_unary_op->variant_op, p_unary_op->operand->reduced_value, Variant()); -- cgit v1.2.3 From d06ce2f11ec109f6568329438416a63fc09b96f2 Mon Sep 17 00:00:00 2001 From: George Marques Date: Mon, 17 Aug 2020 21:02:49 -0300 Subject: GDScript: Fix editor crash when writing @tool annotation --- modules/gdscript/gdscript_parser.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'modules/gdscript') diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 0967f74285..d1ba8cd84d 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -494,16 +494,18 @@ void GDScriptParser::parse_program() { if (match(GDScriptTokenizer::Token::ANNOTATION)) { // Check for @tool annotation. AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL); - if (annotation->name == "@tool") { - // TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?). - _is_tool = true; - if (previous.type != GDScriptTokenizer::Token::NEWLINE) { - push_error(R"(Expected newline after "@tool" annotation.)"); - } - // @tool annotation has no specific target. - annotation->apply(this, nullptr); - } else { - annotation_stack.push_back(annotation); + if (annotation != nullptr) { + if (annotation->name == "@tool") { + // TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?). + _is_tool = true; + if (previous.type != GDScriptTokenizer::Token::NEWLINE) { + push_error(R"(Expected newline after "@tool" annotation.)"); + } + // @tool annotation has no specific target. + annotation->apply(this, nullptr); + } else { + annotation_stack.push_back(annotation); + } } } -- cgit v1.2.3 From fda6f3b600e124d9fefa3886d2feba1ce32db99f Mon Sep 17 00:00:00 2001 From: George Marques Date: Mon, 17 Aug 2020 21:10:30 -0300 Subject: GDScript: Fix LSP getting wrong union value on unnamed enums --- modules/gdscript/language_server/gdscript_extend_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules/gdscript') diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index ae7898fdf2..4d79d9d395 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -237,7 +237,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p case ClassNode::Member::ENUM_VALUE: { lsp::DocumentSymbol symbol; - symbol.name = m.constant->identifier->name; + symbol.name = m.enum_value.identifier->name; symbol.kind = lsp::SymbolKind::EnumMember; symbol.deprecated = false; symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.enum_value.line); -- cgit v1.2.3 From 3abb3c0d6e9e81d3ed171162cc5ce2bbe58296e0 Mon Sep 17 00:00:00 2001 From: George Marques Date: Mon, 17 Aug 2020 21:30:39 -0300 Subject: GDScript: Fix crash when superclass file is non-existent Incidentally, allow EOF to be an end of statement. --- modules/gdscript/gdscript_cache.cpp | 15 +++++++++++++++ modules/gdscript/gdscript_parser.cpp | 6 +++--- 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'modules/gdscript') diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 583283ff46..cdb14d6281 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -85,6 +85,17 @@ Error GDScriptParserRef::raise_status(Status p_new_status) { return result; } } + if (result != OK) { + if (parser != nullptr) { + memdelete(parser); + parser = nullptr; + } + if (analyzer != nullptr) { + memdelete(analyzer); + analyzer = nullptr; + } + return result; + } } return result; @@ -118,6 +129,10 @@ Ref GDScriptCache::get_parser(const String &p_path, GDScriptP if (singleton->parser_map.has(p_path)) { ref = singleton->parser_map[p_path]; } else { + if (!FileAccess::exists(p_path)) { + r_error = ERR_FILE_NOT_FOUND; + return ref; + } GDScriptParser *parser = memnew(GDScriptParser); ref.instance(); ref->parser = parser; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index d1ba8cd84d..c9868cd80b 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -466,17 +466,17 @@ void GDScriptParser::pop_multiline() { } bool GDScriptParser::is_statement_end() { - return check(GDScriptTokenizer::Token::NEWLINE) || check(GDScriptTokenizer::Token::SEMICOLON); + return check(GDScriptTokenizer::Token::NEWLINE) || check(GDScriptTokenizer::Token::SEMICOLON) || check(GDScriptTokenizer::Token::TK_EOF); } void GDScriptParser::end_statement(const String &p_context) { bool found = false; - while (is_statement_end()) { + while (is_statement_end() && !is_at_end()) { // Remove sequential newlines/semicolons. found = true; advance(); } - if (!found) { + if (!found && !is_at_end()) { push_error(vformat(R"(Expected end of statement after %s, found "%s" instead.)", p_context, current.get_name())); } } -- cgit v1.2.3 From 0f9923e67f477ebab1fe903de53bf117ec3a886f Mon Sep 17 00:00:00 2001 From: George Marques Date: Mon, 17 Aug 2020 21:32:49 -0300 Subject: GDScript: Allow empty files to be valid scripts --- modules/gdscript/gdscript_parser.cpp | 6 ------ 1 file changed, 6 deletions(-) (limited to 'modules/gdscript') diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index c9868cd80b..a0f37fcf23 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -482,12 +482,6 @@ void GDScriptParser::end_statement(const String &p_context) { } void GDScriptParser::parse_program() { - if (current.type == GDScriptTokenizer::Token::TK_EOF) { - // Empty file. - push_error("Source file is empty."); - return; - } - head = alloc_node(); current_class = head; -- cgit v1.2.3 From 8088e9e6acb3d8cec00610e9345596bbabed6923 Mon Sep 17 00:00:00 2001 From: George Marques Date: Tue, 18 Aug 2020 10:36:01 -0300 Subject: GDScript: Add script to cache on reload This ensures that scripts created without a resource loader are properly included in the cache (such as builtin scripts) and are not tried to be loaded from the disk. --- modules/gdscript/gdscript.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'modules/gdscript') diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 9170255c02..541d46a2af 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -596,6 +596,21 @@ Error GDScript::reload(bool p_keep_state) { return OK; } + { + String source_path = path; + if (source_path.empty()) { + source_path = get_path(); + } + if (!source_path.empty()) { + MutexLock lock(GDScriptCache::singleton->lock); + Ref self(this); + if (!GDScriptCache::singleton->shallow_gdscript_cache.has(source_path)) { + GDScriptCache::singleton->shallow_gdscript_cache[source_path] = self; + self->unreference(); + } + } + } + valid = false; GDScriptParser parser; Error err = parser.parse(source, path, false); -- cgit v1.2.3 From 8bc9b3a2ae7e15828825c81960c9c422aaddcf36 Mon Sep 17 00:00:00 2001 From: George Marques Date: Tue, 18 Aug 2020 13:12:18 -0300 Subject: GDScript: Allow implicit type conversion when constructing variants Incidentally fix error message when no valid constructor is found which was missing an end parenthesis. --- modules/gdscript/gdscript_analyzer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'modules/gdscript') diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 0768dd05e5..881c40a4c6 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1634,6 +1634,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa } signature += p_call->arguments[i]->get_datatype().to_string(); } + signature += ")"; push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call->callee); } break; case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: @@ -1684,7 +1685,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa for (int i = 0; i < p_call->arguments.size(); i++) { GDScriptParser::DataType par_type = type_from_property(info.arguments[i]); - if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype())) { + if (!is_type_compatible(par_type, p_call->arguments[i]->get_datatype(), true)) { types_match = false; break; #ifdef DEBUG_ENABLED @@ -1711,6 +1712,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa } signature += p_call->arguments[i]->get_datatype().to_string(); } + signature += ")"; push_error(vformat(R"(No constructor of "%s" matches the signature "%s".)", Variant::get_type_name(builtin_type), signature), p_call); } } -- cgit v1.2.3 From 99d4ea8c792833b5bebc03cf19eea3330c79537d Mon Sep 17 00:00:00 2001 From: George Marques Date: Tue, 18 Aug 2020 15:12:51 -0300 Subject: GDScript: Allow supertype objects to be assigned to a subtype variable It allows `get_node()` to be used with typed variables This is marked as unsafe to warn the user. --- modules/gdscript/gdscript_analyzer.cpp | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'modules/gdscript') diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 881c40a4c6..3a04777bd5 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -498,7 +498,13 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas if (member.variable->initializer != nullptr) { if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true)) { - push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer); + // Try reverse test since it can be a masked subtype. + if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true)) { + push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer); + } else { + // TODO: Add warning. + mark_node_unsafe(member.variable->initializer); + } } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) { #ifdef DEBUG_ENABLED parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION); @@ -994,7 +1000,13 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable if (p_variable->initializer != nullptr) { if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true)) { - push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", p_variable->initializer->get_datatype().to_string(), type.to_string()), p_variable->initializer); + // Try reverse test since it can be a masked subtype. + if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true)) { + push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", p_variable->initializer->get_datatype().to_string(), type.to_string()), p_variable->initializer); + } else { + // TODO: Add warning. + mark_node_unsafe(p_variable->initializer); + } #ifdef DEBUG_ENABLED } else if (type.builtin_type == Variant::INT && p_variable->initializer->get_datatype().builtin_type == Variant::FLOAT) { parser->push_warning(p_variable->initializer, GDScriptWarning::NARROWING_CONVERSION); @@ -1385,9 +1397,16 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig compatible = is_type_compatible(p_assignment->assignee->get_datatype(), op_type, true); if (!compatible) { if (p_assignment->assignee->get_datatype().is_hard_type()) { - push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", p_assignment->assigned_value->get_datatype().to_string(), p_assignment->assignee->get_datatype().to_string()), p_assignment->assigned_value); + // Try reverse test since it can be a masked subtype. + if (!is_type_compatible(op_type, p_assignment->assignee->get_datatype(), true)) { + push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", p_assignment->assigned_value->get_datatype().to_string(), p_assignment->assignee->get_datatype().to_string()), p_assignment->assigned_value); + } else { + // TODO: Add warning. + mark_node_unsafe(p_assignment); + } } else { // TODO: Warning in this case. + mark_node_unsafe(p_assignment); } } } else { -- cgit v1.2.3 From 35176247af80626684ff6bb1a1eb3bc031857b1c Mon Sep 17 00:00:00 2001 From: George Marques Date: Tue, 18 Aug 2020 17:44:20 -0300 Subject: GDScript: Allow enum values to be set to constant expressions Also allow them to access previous values wihout referencing the enum. --- modules/gdscript/gdscript_analyzer.cpp | 83 ++++++++++++++++++++++++++++++++-- modules/gdscript/gdscript_analyzer.h | 2 + modules/gdscript/gdscript_parser.cpp | 22 ++++----- modules/gdscript/gdscript_parser.h | 5 +- 4 files changed, 96 insertions(+), 16 deletions(-) (limited to 'modules/gdscript') diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 3a04777bd5..2082339ba7 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -602,17 +602,67 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas enum_type.is_meta_type = true; enum_type.is_constant = true; + // Enums can't be nested, so we can safely override this. + current_enum = member.m_enum; + for (int j = 0; j < member.m_enum->values.size(); j++) { - enum_type.enum_values[member.m_enum->values[j].identifier->name] = member.m_enum->values[j].value; + GDScriptParser::EnumNode::Value &element = member.m_enum->values.write[j]; + + if (element.custom_value) { + reduce_expression(element.custom_value); + if (!element.custom_value->is_constant) { + push_error(R"(Enum values must be constant.)", element.custom_value); + } else if (element.custom_value->reduced_value.get_type() != Variant::INT) { + push_error(R"(Enum values must be integers.)", element.custom_value); + } else { + element.value = element.custom_value->reduced_value; + element.resolved = true; + } + } else { + if (element.index > 0) { + element.value = element.parent_enum->values[element.index - 1].value + 1; + } else { + element.value = 0; + } + element.resolved = true; + } + + enum_type.enum_values[element.identifier->name] = element.value; } + current_enum = nullptr; + member.m_enum->set_datatype(enum_type); } break; case GDScriptParser::ClassNode::Member::FUNCTION: resolve_function_signature(member.function); break; - case GDScriptParser::ClassNode::Member::ENUM_VALUE: - break; // Nothing to do, type and value set in parser. + case GDScriptParser::ClassNode::Member::ENUM_VALUE: { + if (member.enum_value.custom_value) { + current_enum = member.enum_value.parent_enum; + reduce_expression(member.enum_value.custom_value); + current_enum = nullptr; + + if (!member.enum_value.custom_value->is_constant) { + push_error(R"(Enum values must be constant.)", member.enum_value.custom_value); + } else if (member.enum_value.custom_value->reduced_value.get_type() != Variant::INT) { + push_error(R"(Enum values must be integers.)", member.enum_value.custom_value); + } else { + member.enum_value.value = member.enum_value.custom_value->reduced_value; + member.enum_value.resolved = true; + } + } else { + if (member.enum_value.index > 0) { + member.enum_value.value = member.enum_value.parent_enum->values[member.enum_value.index - 1].value + 1; + } else { + member.enum_value.value = 0; + } + member.enum_value.resolved = true; + } + // Also update the original references. + member.enum_value.parent_enum->values.write[member.enum_value.index] = member.enum_value; + p_class->members.write[i].enum_value = member.enum_value; + } break; case GDScriptParser::ClassNode::Member::CLASS: break; // Done later. case GDScriptParser::ClassNode::Member::UNDEFINED: @@ -2125,6 +2175,33 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin) { // TODO: This is opportunity to further infer types. + + // Check if we are inside and enum. This allows enum values to access other elements of the same enum. + if (current_enum) { + for (int i = 0; i < current_enum->values.size(); i++) { + const GDScriptParser::EnumNode::Value &element = current_enum->values[i]; + if (element.identifier->name == p_identifier->name) { + GDScriptParser::DataType type; + type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM_VALUE : GDScriptParser::DataType::BUILTIN; + type.builtin_type = Variant::INT; + type.is_constant = true; + if (element.parent_enum->identifier) { + type.enum_type = element.parent_enum->identifier->name; + } + p_identifier->set_datatype(type); + + if (element.resolved) { + p_identifier->is_constant = true; + p_identifier->reduced_value = element.value; + } else { + push_error(R"(Cannot use another enum element before it was declared.)", p_identifier); + } + return; // Found anyway. + } + } + } + // Check if identifier is local. // If that's the case, the declaration already was solved before. switch (p_identifier->source) { diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 06d3530cb6..da767522ad 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -41,6 +41,8 @@ class GDScriptAnalyzer { GDScriptParser *parser = nullptr; HashMap> depended_parsers; + const GDScriptParser::EnumNode *current_enum = nullptr; + Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true); GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index a0f37fcf23..e53a1090fe 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -1022,8 +1022,6 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() { push_multiline(true); consume(GDScriptTokenizer::Token::BRACE_OPEN, vformat(R"(Expected "{" after %s.)", named ? "enum name" : R"("enum")")); - int current_value = 0; - do { if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) { break; // Allow trailing comma. @@ -1031,6 +1029,9 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() { if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifer for enum key.)")) { EnumNode::Value item; item.identifier = parse_identifier(); + item.parent_enum = enum_node; + item.line = previous.start_line; + item.leftmost_column = previous.leftmost_column; if (!named) { // TODO: Abstract this recursive member check. @@ -1045,18 +1046,15 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() { } if (match(GDScriptTokenizer::Token::EQUAL)) { - if (consume(GDScriptTokenizer::Token::LITERAL, R"(Expected integer value after "=".)")) { - item.custom_value = parse_literal(); - - if (item.custom_value->value.get_type() != Variant::INT) { - push_error(R"(Expected integer value after "=".)"); - item.custom_value = nullptr; - } else { - current_value = item.custom_value->value; - } + ExpressionNode *value = parse_expression(false); + if (value == nullptr) { + push_error(R"(Expected expression value after "=".)"); } + item.custom_value = value; } - item.value = current_value++; + item.rightmost_column = previous.rightmost_column; + + item.index = enum_node->values.size(); enum_node->values.push_back(item); if (!named) { // Add as member of current class. diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index edfe330c0c..7d8ae7fc55 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -405,7 +405,10 @@ public: struct EnumNode : public Node { struct Value { IdentifierNode *identifier = nullptr; - LiteralNode *custom_value = nullptr; + ExpressionNode *custom_value = nullptr; + EnumNode *parent_enum = nullptr; + int index = -1; + bool resolved = false; int value = 0; int line = 0; int leftmost_column = 0; -- cgit v1.2.3