mirror of
https://github.com/godotengine/godot.git
synced 2025-10-28 04:04:24 +00:00
GDScript: Add lambdas to the type analyzer
- Lambdas are always callables (no specific signature match). - Captures from the current context are evaluated.
This commit is contained in:
parent
c6e66a43b0
commit
3155368093
5 changed files with 122 additions and 24 deletions
|
|
@ -856,6 +856,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
|
|||
case GDScriptParser::Node::DICTIONARY:
|
||||
case GDScriptParser::Node::GET_NODE:
|
||||
case GDScriptParser::Node::IDENTIFIER:
|
||||
case GDScriptParser::Node::LAMBDA:
|
||||
case GDScriptParser::Node::LITERAL:
|
||||
case GDScriptParser::Node::PRELOAD:
|
||||
case GDScriptParser::Node::SELF:
|
||||
|
|
@ -872,9 +873,6 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
|
|||
case GDScriptParser::Node::SIGNAL:
|
||||
// Nothing to do.
|
||||
break;
|
||||
case GDScriptParser::Node::LAMBDA:
|
||||
// FIXME: Recurse into lambda.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1461,6 +1459,9 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
|
|||
case GDScriptParser::Node::IDENTIFIER:
|
||||
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_expression));
|
||||
break;
|
||||
case GDScriptParser::Node::LAMBDA:
|
||||
reduce_lambda(static_cast<GDScriptParser::LambdaNode *>(p_expression));
|
||||
break;
|
||||
case GDScriptParser::Node::LITERAL:
|
||||
reduce_literal(static_cast<GDScriptParser::LiteralNode *>(p_expression));
|
||||
break;
|
||||
|
|
@ -1492,7 +1493,6 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
|
|||
case GDScriptParser::Node::FOR:
|
||||
case GDScriptParser::Node::FUNCTION:
|
||||
case GDScriptParser::Node::IF:
|
||||
case GDScriptParser::Node::LAMBDA:
|
||||
case GDScriptParser::Node::MATCH:
|
||||
case GDScriptParser::Node::MATCH_BRANCH:
|
||||
case GDScriptParser::Node::PARAMETER:
|
||||
|
|
@ -2350,6 +2350,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|||
case GDScriptParser::ClassNode::Member::ENUM_VALUE:
|
||||
p_identifier->is_constant = true;
|
||||
p_identifier->reduced_value = member.enum_value.value;
|
||||
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
|
||||
break;
|
||||
case GDScriptParser::ClassNode::Member::VARIABLE:
|
||||
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
|
||||
|
|
@ -2450,42 +2451,65 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|||
}
|
||||
}
|
||||
|
||||
bool found_source = false;
|
||||
// Check if identifier is local.
|
||||
// If that's the case, the declaration already was solved before.
|
||||
switch (p_identifier->source) {
|
||||
case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
|
||||
p_identifier->set_datatype(p_identifier->parameter_source->get_datatype());
|
||||
return;
|
||||
found_source = true;
|
||||
break;
|
||||
case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
|
||||
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
|
||||
p_identifier->set_datatype(p_identifier->constant_source->get_datatype());
|
||||
p_identifier->is_constant = true;
|
||||
// TODO: Constant should have a value on the node itself.
|
||||
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
|
||||
return;
|
||||
found_source = true;
|
||||
break;
|
||||
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
|
||||
p_identifier->variable_source->usages++;
|
||||
[[fallthrough]];
|
||||
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
|
||||
p_identifier->set_datatype(p_identifier->variable_source->get_datatype());
|
||||
return;
|
||||
found_source = true;
|
||||
break;
|
||||
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
|
||||
p_identifier->set_datatype(p_identifier->bind_source->get_datatype());
|
||||
return;
|
||||
found_source = true;
|
||||
break;
|
||||
case GDScriptParser::IdentifierNode::LOCAL_BIND: {
|
||||
GDScriptParser::DataType result = p_identifier->bind_source->get_datatype();
|
||||
result.is_constant = true;
|
||||
p_identifier->set_datatype(result);
|
||||
return;
|
||||
}
|
||||
found_source = true;
|
||||
} break;
|
||||
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
|
||||
break;
|
||||
}
|
||||
|
||||
// Not a local, so check members.
|
||||
reduce_identifier_from_base(p_identifier);
|
||||
if (p_identifier->get_datatype().is_set()) {
|
||||
// Found.
|
||||
if (!found_source) {
|
||||
reduce_identifier_from_base(p_identifier);
|
||||
if (p_identifier->source != GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->get_datatype().is_set()) {
|
||||
// Found.
|
||||
found_source = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_source) {
|
||||
// If the identifier is local, check if it's any kind of capture by comparing their source function.
|
||||
// Only capture locals and members and enum values. Constants are still accessible from the lambda using the script reference.
|
||||
if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT || lambda_stack.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function;
|
||||
while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) {
|
||||
function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size();
|
||||
function_test->source_lambda->captures.push_back(p_identifier);
|
||||
function_test = function_test->source_lambda->parent_function;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2567,6 +2591,33 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|||
p_identifier->set_datatype(dummy); // Just so type is set to something.
|
||||
}
|
||||
|
||||
void GDScriptAnalyzer::reduce_lambda(GDScriptParser::LambdaNode *p_lambda) {
|
||||
// Lambda is always a Callable.
|
||||
GDScriptParser::DataType lambda_type;
|
||||
lambda_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
|
||||
lambda_type.kind = GDScriptParser::DataType::BUILTIN;
|
||||
lambda_type.builtin_type = Variant::CALLABLE;
|
||||
p_lambda->set_datatype(lambda_type);
|
||||
|
||||
if (p_lambda->function == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
GDScriptParser::FunctionNode *previous_function = parser->current_function;
|
||||
parser->current_function = p_lambda->function;
|
||||
|
||||
lambda_stack.push_back(p_lambda);
|
||||
|
||||
for (int i = 0; i < p_lambda->function->parameters.size(); i++) {
|
||||
resolve_parameter(p_lambda->function->parameters[i]);
|
||||
}
|
||||
|
||||
resolve_suite(p_lambda->function->body);
|
||||
|
||||
lambda_stack.pop_back();
|
||||
parser->current_function = previous_function;
|
||||
}
|
||||
|
||||
void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {
|
||||
p_literal->reduced_value = p_literal->value;
|
||||
p_literal->is_constant = true;
|
||||
|
|
@ -3526,6 +3577,13 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) {
|
|||
return ref;
|
||||
}
|
||||
|
||||
const GDScriptParser::LambdaNode *GDScriptAnalyzer::get_current_lambda() const {
|
||||
if (lambda_stack.size()) {
|
||||
return lambda_stack.back()->get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Error GDScriptAnalyzer::resolve_inheritance() {
|
||||
return resolve_inheritance(parser->head);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue