diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 4b8d52788145..0b33be44ef96 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1486,6 +1486,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root case GDScriptParser::Node::TERNARY_OPERATOR: case GDScriptParser::Node::TYPE_TEST: case GDScriptParser::Node::UNARY_OPERATOR: + case GDScriptParser::Node::COMPREHENSION: reduce_expression(static_cast(p_node), p_is_root); break; case GDScriptParser::Node::BREAK: @@ -1990,13 +1991,16 @@ void GDScriptAnalyzer::resolve_if(GDScriptParser::IfNode *p_if) { } } -void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { +void GDScriptAnalyzer::resolve_iteration(GDScriptParser::ExpressionNode *list, + GDScriptParser::IdentifierNode *variable, + bool *use_conversion_assign, + GDScriptParser::TypeNode *datatype_specifier) { bool list_resolved = false; // Optimize constant range() call to not allocate an array. // Use int, Vector2i, Vector3i instead, which also can be used as range iterators. - if (p_for->list && p_for->list->type == GDScriptParser::Node::CALL) { - GDScriptParser::CallNode *call = static_cast(p_for->list); + if (list && list->type == GDScriptParser::Node::CALL) { + GDScriptParser::CallNode *call = static_cast(list); GDScriptParser::Node::Type callee_type = call->get_callee_type(); if (callee_type == GDScriptParser::Node::IDENTIFIER) { GDScriptParser::IdentifierNode *callee = static_cast(call->callee); @@ -2053,19 +2057,19 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { reduced = Vector3i(args[0], args[1], args[2]); break; } - p_for->list->is_constant = true; - p_for->list->reduced_value = reduced; + list->is_constant = true; + list->reduced_value = reduced; } } - if (p_for->list->is_constant) { - p_for->list->set_datatype(type_from_variant(p_for->list->reduced_value, p_for->list)); + if (list->is_constant) { + list->set_datatype(type_from_variant(list->reduced_value, list)); } else { GDScriptParser::DataType list_type; list_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; list_type.kind = GDScriptParser::DataType::BUILTIN; list_type.builtin_type = Variant::ARRAY; - p_for->list->set_datatype(list_type); + list->set_datatype(list_type); } } } @@ -2078,16 +2082,16 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { variable_type.kind = GDScriptParser::DataType::BUILTIN; variable_type.builtin_type = Variant::INT; list_visible_type = "Array[int]"; // NOTE: `range()` has `Array` return type. - } else if (p_for->list) { - resolve_node(p_for->list, false); - GDScriptParser::DataType list_type = p_for->list->get_datatype(); + } else if (list) { + resolve_node(list, false); + GDScriptParser::DataType list_type = list->get_datatype(); list_visible_type = list_type.to_string(); if (!list_type.is_hard_type()) { - mark_node_unsafe(p_for->list); + mark_node_unsafe(list); } if (list_type.is_variant()) { variable_type.kind = GDScriptParser::DataType::VARIANT; - mark_node_unsafe(p_for->list); + mark_node_unsafe(list); } else if (list_type.has_container_element_type()) { variable_type = list_type.get_container_element_type(); variable_type.type_source = list_type.type_source; @@ -2111,49 +2115,53 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { List par_types; int default_arg_count = 0; BitField method_flags; - if (get_function_signature(p_for->list, false, list_type, CoreStringNames::get_singleton()->_iter_get, return_type, par_types, default_arg_count, method_flags)) { + if (get_function_signature(list, false, list_type, CoreStringNames::get_singleton()->_iter_get, return_type, par_types, default_arg_count, method_flags)) { variable_type = return_type; variable_type.type_source = list_type.type_source; } else if (!list_type.is_hard_type()) { variable_type.kind = GDScriptParser::DataType::VARIANT; } else { - push_error(vformat(R"(Unable to iterate on object of type "%s".)", list_type.to_string()), p_for->list); + push_error(vformat(R"(Unable to iterate on object of type "%s".)", list_type.to_string()), list); } } else if (list_type.builtin_type == Variant::ARRAY || list_type.builtin_type == Variant::DICTIONARY || !list_type.is_hard_type()) { variable_type.kind = GDScriptParser::DataType::VARIANT; } else { - push_error(vformat(R"(Unable to iterate on value of type "%s".)", list_type.to_string()), p_for->list); + push_error(vformat(R"(Unable to iterate on value of type "%s".)", list_type.to_string()), list); } } - if (p_for->variable) { - if (p_for->datatype_specifier) { - GDScriptParser::DataType specified_type = type_from_metatype(resolve_datatype(p_for->datatype_specifier)); + if (variable) { + if (datatype_specifier) { + GDScriptParser::DataType specified_type = type_from_metatype(resolve_datatype(datatype_specifier)); if (!specified_type.is_variant()) { if (variable_type.is_variant() || !variable_type.is_hard_type()) { - mark_node_unsafe(p_for->variable); - p_for->use_conversion_assign = true; - } else if (!is_type_compatible(specified_type, variable_type, true, p_for->variable)) { + mark_node_unsafe(variable); + *use_conversion_assign = true; + } else if (!is_type_compatible(specified_type, variable_type, true, variable)) { if (is_type_compatible(variable_type, specified_type)) { - mark_node_unsafe(p_for->variable); - p_for->use_conversion_assign = true; + mark_node_unsafe(variable); + *use_conversion_assign = true; } else { - push_error(vformat(R"(Unable to iterate on value of type "%s" with variable of type "%s".)", list_visible_type, specified_type.to_string()), p_for->datatype_specifier); + push_error(vformat(R"(Unable to iterate on value of type "%s" with variable of type "%s".)", list_visible_type, specified_type.to_string()), datatype_specifier); } } else if (!is_type_compatible(specified_type, variable_type)) { - p_for->use_conversion_assign = true; + *use_conversion_assign = true; } } - p_for->variable->set_datatype(specified_type); + variable->set_datatype(specified_type); } else { - p_for->variable->set_datatype(variable_type); + variable->set_datatype(variable_type); #ifdef DEBUG_ENABLED if (!variable_type.is_hard_type()) { - parser->push_warning(p_for->variable, GDScriptWarning::UNTYPED_DECLARATION, R"("for" iterator variable)", p_for->variable->name); + parser->push_warning(variable, GDScriptWarning::UNTYPED_DECLARATION, R"("for" iterator variable)", variable->name); } #endif } } +} + +void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { + resolve_iteration(p_for->list, p_for->variable, &p_for->use_conversion_assign, p_for->datatype_specifier); resolve_suite(p_for->loop); p_for->set_datatype(p_for->loop->get_datatype()); @@ -2399,6 +2407,9 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre case GDScriptParser::Node::CAST: reduce_cast(static_cast(p_expression)); break; + case GDScriptParser::Node::COMPREHENSION: + reduce_comprehension(static_cast(p_expression)); + break; case GDScriptParser::Node::DICTIONARY: reduce_dictionary(static_cast(p_expression)); break; @@ -2476,6 +2487,41 @@ void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) { p_array->set_datatype(arr_type); } +void GDScriptAnalyzer::reduce_comprehension(GDScriptParser::ComprehensionNode *p_comprehension) { + for (int i = 0; i < p_comprehension->for_ifs.size(); i++) { + comprehension_identifier_stack.push_back(p_comprehension->for_ifs[i].variable); + } + + for (int i = p_comprehension->for_ifs.size() - 1; i >= 0; i--) { + GDScriptParser::ComprehensionNode::ForIf for_if = p_comprehension->for_ifs[i]; + comprehension_identifier_stack.pop_back(); + + reduce_expression(for_if.list); + resolve_iteration(for_if.list, for_if.variable, &for_if.use_conversion_assign); + } + + for (int i = p_comprehension->for_ifs.size() - 1; i >= 0; i--) { + comprehension_identifier_stack.push_back(p_comprehension->for_ifs[i].variable); + } + + reduce_expression(p_comprehension->expression); + + for (int i = p_comprehension->for_ifs.size() - 1; i >= 0; i--) { + GDScriptParser::ComprehensionNode::ForIf for_if = p_comprehension->for_ifs[i]; + + reduce_expression(for_if.condition); + + comprehension_identifier_stack.pop_back(); + } + + GDScriptParser::DataType arr_type; + arr_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + arr_type.kind = GDScriptParser::DataType::BUILTIN; + arr_type.builtin_type = Variant::ARRAY; + arr_type.is_constant = true; + p_comprehension->set_datatype(arr_type); +} + #ifdef DEBUG_ENABLED static bool enum_has_value(const GDScriptParser::DataType p_type, int64_t p_value) { for (const KeyValue &E : p_type.enum_values) { @@ -3745,6 +3791,20 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident } } + // Check if we are inside a comprehension. This allows the comprehension to access the variable it is iterating over. + if (comprehension_identifier_stack.size() > 0) { + for (int i = comprehension_identifier_stack.size() - 1; i >= 0; i--) { + GDScriptParser::IdentifierNode *current_identifier = comprehension_identifier_stack[i]; + + if (current_identifier->name == p_identifier->name) { + GDScriptParser::DataType variable_data_type = current_identifier->get_datatype(); + p_identifier->set_datatype(variable_data_type); + p_identifier->source = GDScriptParser::IdentifierNode::LOCAL_ITERATOR; + return; + } + } + } + bool found_source = false; // Check if identifier is local. // If that's the case, the declaration already was solved before. diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index ec155706df0d..9816f79f1da3 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -45,6 +45,7 @@ class GDScriptAnalyzer { const GDScriptParser::EnumNode *current_enum = nullptr; GDScriptParser::LambdaNode *current_lambda = nullptr; List pending_body_resolution_lambdas; + List comprehension_identifier_stack; //?: could this be a Vector instead? bool static_context = false; // Tests for detecting invalid overloading of script members @@ -61,6 +62,8 @@ class GDScriptAnalyzer { void decide_suite_type(GDScriptParser::Node *p_suite, GDScriptParser::Node *p_statement); + void resolve_iteration(GDScriptParser::ExpressionNode *list, GDScriptParser::IdentifierNode *variable, bool *use_conversion_assign, GDScriptParser::TypeNode *datatype_specifier = nullptr); + void resolve_annotation(GDScriptParser::AnnotationNode *p_annotation); void resolve_class_member(GDScriptParser::ClassNode *p_class, StringName p_name, const GDScriptParser::Node *p_source = nullptr); void resolve_class_member(GDScriptParser::ClassNode *p_class, int p_index, const GDScriptParser::Node *p_source = nullptr); @@ -93,6 +96,7 @@ class GDScriptAnalyzer { void reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_op); void reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await = false, bool p_is_root = false); void reduce_cast(GDScriptParser::CastNode *p_cast); + void reduce_comprehension(GDScriptParser::ComprehensionNode *p_comprehension); void reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary); void reduce_get_node(GDScriptParser::GetNodeNode *p_get_node); void reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin = false); diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 8394fce9b353..8532ef6ac86f 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -1783,8 +1783,8 @@ void GDScriptByteCodeGenerator::start_block() { push_stack_identifiers(); } -void GDScriptByteCodeGenerator::end_block() { - pop_stack_identifiers(); +void GDScriptByteCodeGenerator::end_block(bool expect_no_locals) { + pop_stack_identifiers(expect_no_locals); } void GDScriptByteCodeGenerator::clean_temporaries() { diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index 671dea5d6d67..6f4930d5cea6 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -86,7 +86,9 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { Vector locals; Vector temporaries; + List used_temporaries; + List temporaries_pending_clear; RBMap> temporaries_pool; @@ -184,13 +186,13 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { } } - void pop_stack_identifiers() { + void pop_stack_identifiers(bool expect_no_locals = true) { int current_locals = stack_identifiers_counts.back()->get(); stack_identifiers_counts.pop_back(); stack_identifiers = stack_id_stack.back()->get(); stack_id_stack.pop_back(); #ifdef DEBUG_ENABLED - if (!used_temporaries.is_empty()) { + if (!used_temporaries.is_empty() && expect_no_locals) { ERR_PRINT("Leaving block with non-zero temporary variables: " + itos(used_temporaries.size())); } #endif @@ -468,7 +470,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { virtual void end_parameters() override; virtual void start_block() override; - virtual void end_block() override; + virtual void end_block(bool expect_no_locals) override; virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Variant p_rpc_config, const GDScriptDataType &p_return_type) override; virtual GDScriptFunction *write_end() override; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index cf17353dec19..48f897607031 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -79,7 +79,7 @@ class GDScriptCodeGenerator { virtual void end_parameters() = 0; virtual void start_block() = 0; - virtual void end_block() = 0; + virtual void end_block(bool expect_no_locals) = 0; virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Variant p_rpc_config, const GDScriptDataType &p_return_type) = 0; virtual GDScriptFunction *write_end() = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 7f2c401afc51..59110556b304 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1373,6 +1373,69 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code return result; } break; + case GDScriptParser::Node::COMPREHENSION: { + const GDScriptParser::ComprehensionNode *comprehension = static_cast(p_expression); + GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(comprehension->get_datatype(), codegen.script)); + + Vector values; + + gen->write_construct_array(result, values); + + for (const GDScriptParser::ComprehensionNode::ForIf &for_if : comprehension->for_ifs) { + codegen.start_block(); + + GDScriptCodeGenerator::Address list = _parse_expression(codegen, r_error, for_if.list); + if (r_error) { + return GDScriptCodeGenerator::Address(); + } + + GDScriptCodeGenerator::Address iterator = codegen.add_local(for_if.variable->name, _gdtype_from_datatype(for_if.variable->get_datatype(), codegen.script)); + + gen->start_for(iterator.type, _gdtype_from_datatype(for_if.list->get_datatype(), codegen.script)); + + gen->write_for_assignment(list); + if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + codegen.generator->pop_temporary(); + } + + gen->write_for(iterator, for_if.use_conversion_assign); + + if (for_if.condition != nullptr) { + GDScriptCodeGenerator::Address condition = _parse_expression(codegen, r_error, for_if.condition); + if (r_error) { + return GDScriptCodeGenerator::Address(); + } + + gen->write_if(condition); + + if (condition.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + codegen.generator->pop_temporary(); + } + } + } + + GDScriptCodeGenerator::Address expression = _parse_expression(codegen, r_error, comprehension->expression); + if (r_error) { + return GDScriptCodeGenerator::Address(); + } + values.push_back(expression); + if (expression.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + codegen.generator->pop_temporary(); + } + + gen->write_call_builtin_type(GDScriptCodeGenerator::Address(), result, Variant::Type::ARRAY, "push_back", values); + + for (int i = comprehension->for_ifs.size() - 1; i >= 0; i--) { + if (comprehension->for_ifs[i].condition != nullptr) { + gen->write_endif(); + } + gen->write_endfor(); + + codegen.end_block(false); + } + + return result; + } break; default: { ERR_FAIL_V_MSG(GDScriptCodeGenerator::Address(), "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); // Unreachable code. } break; diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 099bd00a2ea1..d189347e6a3e 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -108,10 +108,10 @@ class GDScriptCompiler { generator->start_block(); } - void end_block() { + void end_block(bool expect_no_locals = true) { locals = locals_stack.back()->get(); locals_stack.pop_back(); - generator->end_block(); + generator->end_block(expect_no_locals); } }; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 52c1a5b141e2..06d6d9b45ff6 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -2227,7 +2227,7 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() { return n_while; } -GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_precedence, bool p_can_assign, bool p_stop_on_assign) { +GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_precedence, bool p_can_assign, bool p_stop_on_assign, bool p_disallow_ternary) { // Switch multiline mode on for grouping tokens. // Do this early to avoid the tokenizer generating whitespace tokens. switch (current.type) { @@ -2249,6 +2249,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr // Allow keywords that can be treated as identifiers. token_type = GDScriptTokenizer::Token::IDENTIFIER; } + ParseFunction prefix_rule = get_rule(token_type)->prefix; if (prefix_rule == nullptr) { @@ -2264,6 +2265,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) || lambda_ended) { return previous_operand; } + + if (current.type == GDScriptTokenizer::Token::IF && p_disallow_ternary) { + return previous_operand; + } + // Also switch multiline mode on here for infix operators. switch (current.type) { // case GDScriptTokenizer::Token::BRACE_OPEN: // Not an infix operator. @@ -2282,8 +2288,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr return previous_operand; } -GDScriptParser::ExpressionNode *GDScriptParser::parse_expression(bool p_can_assign, bool p_stop_on_assign) { - return parse_precedence(PREC_ASSIGNMENT, p_can_assign, p_stop_on_assign); +GDScriptParser::ExpressionNode *GDScriptParser::parse_expression(bool p_can_assign, bool p_stop_on_assign, bool p_disallow_ternary) { + return parse_precedence(PREC_ASSIGNMENT, p_can_assign, p_stop_on_assign, p_disallow_ternary); } GDScriptParser::IdentifierNode *GDScriptParser::parse_identifier() { @@ -2734,8 +2740,40 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_await(ExpressionNode *p_pr return await; } +GDScriptParser::ComprehensionNode *GDScriptParser::parse_comprehension(ExpressionNode *expression) { + ComprehensionNode *comprehension = alloc_node(); + comprehension->expression = expression; + + do { + ComprehensionNode::ForIf for_if; + + if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected loop variable name in list comprehension)")) { + for_if.variable = parse_identifier(); + } + consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after list comprehension variable name.)"); + + for_if.list = parse_expression(false, false, true); // disallow ternary at this point + + if (match(GDScriptTokenizer::Token::IF)) { + for_if.condition = parse_expression(false); + + if (for_if.condition == nullptr) { + push_error(R"(Expected conditional expression after "if" in list comprehension.)"); + } + } + + comprehension->for_ifs.push_back(for_if); + } while (match(GDScriptTokenizer::Token::FOR) && !is_at_end()); + + pop_multiline(); + consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after list comprehension.)"); + complete_extents(comprehension); + + return comprehension; +} + GDScriptParser::ExpressionNode *GDScriptParser::parse_array(ExpressionNode *p_previous_operand, bool p_can_assign) { - ArrayNode *array = alloc_node(); + ArrayNode *array = nullptr; if (!check(GDScriptTokenizer::Token::BRACKET_CLOSE)) { do { @@ -2745,13 +2783,25 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_array(ExpressionNode *p_pr } ExpressionNode *element = parse_expression(false); + + if (array == nullptr && match(GDScriptTokenizer::Token::FOR)) { + return parse_comprehension(element); + } + + // Since it's not a comprehension, we can now alloc the array. + if (array == nullptr) + array = alloc_node(); + if (element == nullptr) { push_error(R"(Expected expression as array element.)"); } else { array->elements.push_back(element); } } while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end()); + } else { + array = alloc_node(); } + pop_multiline(); consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after array elements.)"); complete_extents(array); @@ -4837,6 +4887,22 @@ void GDScriptParser::TreePrinter::print_class(ClassNode *p_class) { decrease_indent(); } +void GDScriptParser::TreePrinter::print_comprehension(ComprehensionNode *p_comprehension) { + push_text("Comprehension "); + print_expression(p_comprehension->expression); + + for (GDScriptParser::ComprehensionNode::ForIf &c : p_comprehension->for_ifs) { + push_text(" for "); + print_expression(c.variable); + push_text(" in "); + print_expression(c.list); + if (c.condition != nullptr) { + push_text(" if "); + print_expression(c.condition); + } + } +} + void GDScriptParser::TreePrinter::print_constant(ConstantNode *p_constant) { push_text("Constant "); print_identifier(p_constant->identifier); @@ -4895,6 +4961,9 @@ void GDScriptParser::TreePrinter::print_expression(ExpressionNode *p_expression) case Node::CAST: print_cast(static_cast(p_expression)); break; + case Node::COMPREHENSION: + print_comprehension(static_cast(p_expression)); + break; case Node::DICTIONARY: print_dictionary(static_cast(p_expression)); break; diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 27d4d0fb47c6..e29e484c3710 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -70,6 +70,7 @@ class GDScriptParser { struct CallNode; struct CastNode; struct ClassNode; + struct ComprehensionNode; struct ConstantNode; struct ContinueNode; struct DictionaryNode; @@ -289,6 +290,7 @@ class GDScriptParser { CALL, CAST, CLASS, + COMPREHENSION, CONSTANT, CONTINUE, DICTIONARY, @@ -785,6 +787,23 @@ class GDScriptParser { } }; + struct ComprehensionNode : public ExpressionNode { + ExpressionNode *expression = nullptr; + + struct ForIf { + ExpressionNode *list = nullptr; + IdentifierNode *variable = nullptr; + ExpressionNode *condition = nullptr; + bool use_conversion_assign = false; + }; + + List for_ifs; + + ComprehensionNode() { + type = COMPREHENSION; + } + }; + struct ConstantNode : public AssignableNode { #ifdef TOOLS_ENABLED MemberDocData doc_data; @@ -1487,8 +1506,8 @@ class GDScriptParser { PatternNode *parse_match_pattern(PatternNode *p_root_pattern = nullptr); WhileNode *parse_while(); // Expressions. - ExpressionNode *parse_expression(bool p_can_assign, bool p_stop_on_assign = false); - ExpressionNode *parse_precedence(Precedence p_precedence, bool p_can_assign, bool p_stop_on_assign = false); + ExpressionNode *parse_expression(bool p_can_assign, bool p_stop_on_assign = false, bool p_disallow_ternary = false); + ExpressionNode *parse_precedence(Precedence p_precedence, bool p_can_assign, bool p_stop_on_assign = false, bool p_disallow_ternary = false); ExpressionNode *parse_literal(ExpressionNode *p_previous_operand, bool p_can_assign); LiteralNode *parse_literal(); ExpressionNode *parse_self(ExpressionNode *p_previous_operand, bool p_can_assign); @@ -1501,6 +1520,7 @@ class GDScriptParser { ExpressionNode *parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign); ExpressionNode *parse_assignment(ExpressionNode *p_previous_operand, bool p_can_assign); ExpressionNode *parse_array(ExpressionNode *p_previous_operand, bool p_can_assign); + ComprehensionNode *parse_comprehension(ExpressionNode *expression); ExpressionNode *parse_dictionary(ExpressionNode *p_previous_operand, bool p_can_assign); ExpressionNode *parse_call(ExpressionNode *p_previous_operand, bool p_can_assign); ExpressionNode *parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign); @@ -1575,6 +1595,7 @@ class GDScriptParser { void print_call(CallNode *p_call); void print_cast(CastNode *p_cast); void print_class(ClassNode *p_class); + void print_comprehension(ComprehensionNode *p_comprehension); void print_constant(ConstantNode *p_constant); void print_dictionary(DictionaryNode *p_dictionary); void print_expression(ExpressionNode *p_expression);