You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-06 12:20:30 +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:
@@ -856,6 +856,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
|
|||||||
case GDScriptParser::Node::DICTIONARY:
|
case GDScriptParser::Node::DICTIONARY:
|
||||||
case GDScriptParser::Node::GET_NODE:
|
case GDScriptParser::Node::GET_NODE:
|
||||||
case GDScriptParser::Node::IDENTIFIER:
|
case GDScriptParser::Node::IDENTIFIER:
|
||||||
|
case GDScriptParser::Node::LAMBDA:
|
||||||
case GDScriptParser::Node::LITERAL:
|
case GDScriptParser::Node::LITERAL:
|
||||||
case GDScriptParser::Node::PRELOAD:
|
case GDScriptParser::Node::PRELOAD:
|
||||||
case GDScriptParser::Node::SELF:
|
case GDScriptParser::Node::SELF:
|
||||||
@@ -872,9 +873,6 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
|
|||||||
case GDScriptParser::Node::SIGNAL:
|
case GDScriptParser::Node::SIGNAL:
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
break;
|
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:
|
case GDScriptParser::Node::IDENTIFIER:
|
||||||
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_expression));
|
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_expression));
|
||||||
break;
|
break;
|
||||||
|
case GDScriptParser::Node::LAMBDA:
|
||||||
|
reduce_lambda(static_cast<GDScriptParser::LambdaNode *>(p_expression));
|
||||||
|
break;
|
||||||
case GDScriptParser::Node::LITERAL:
|
case GDScriptParser::Node::LITERAL:
|
||||||
reduce_literal(static_cast<GDScriptParser::LiteralNode *>(p_expression));
|
reduce_literal(static_cast<GDScriptParser::LiteralNode *>(p_expression));
|
||||||
break;
|
break;
|
||||||
@@ -1492,7 +1493,6 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
|
|||||||
case GDScriptParser::Node::FOR:
|
case GDScriptParser::Node::FOR:
|
||||||
case GDScriptParser::Node::FUNCTION:
|
case GDScriptParser::Node::FUNCTION:
|
||||||
case GDScriptParser::Node::IF:
|
case GDScriptParser::Node::IF:
|
||||||
case GDScriptParser::Node::LAMBDA:
|
|
||||||
case GDScriptParser::Node::MATCH:
|
case GDScriptParser::Node::MATCH:
|
||||||
case GDScriptParser::Node::MATCH_BRANCH:
|
case GDScriptParser::Node::MATCH_BRANCH:
|
||||||
case GDScriptParser::Node::PARAMETER:
|
case GDScriptParser::Node::PARAMETER:
|
||||||
@@ -2350,6 +2350,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|||||||
case GDScriptParser::ClassNode::Member::ENUM_VALUE:
|
case GDScriptParser::ClassNode::Member::ENUM_VALUE:
|
||||||
p_identifier->is_constant = true;
|
p_identifier->is_constant = true;
|
||||||
p_identifier->reduced_value = member.enum_value.value;
|
p_identifier->reduced_value = member.enum_value.value;
|
||||||
|
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
|
||||||
break;
|
break;
|
||||||
case GDScriptParser::ClassNode::Member::VARIABLE:
|
case GDScriptParser::ClassNode::Member::VARIABLE:
|
||||||
p_identifier->source = GDScriptParser::IdentifierNode::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.
|
// Check if identifier is local.
|
||||||
// If that's the case, the declaration already was solved before.
|
// If that's the case, the declaration already was solved before.
|
||||||
switch (p_identifier->source) {
|
switch (p_identifier->source) {
|
||||||
case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
|
case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
|
||||||
p_identifier->set_datatype(p_identifier->parameter_source->get_datatype());
|
p_identifier->set_datatype(p_identifier->parameter_source->get_datatype());
|
||||||
return;
|
found_source = true;
|
||||||
|
break;
|
||||||
case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
|
case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
|
||||||
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
|
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
|
||||||
p_identifier->set_datatype(p_identifier->constant_source->get_datatype());
|
p_identifier->set_datatype(p_identifier->constant_source->get_datatype());
|
||||||
p_identifier->is_constant = true;
|
p_identifier->is_constant = true;
|
||||||
// TODO: Constant should have a value on the node itself.
|
// TODO: Constant should have a value on the node itself.
|
||||||
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
|
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
|
||||||
return;
|
found_source = true;
|
||||||
|
break;
|
||||||
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
|
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
|
||||||
p_identifier->variable_source->usages++;
|
p_identifier->variable_source->usages++;
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
|
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
|
||||||
p_identifier->set_datatype(p_identifier->variable_source->get_datatype());
|
p_identifier->set_datatype(p_identifier->variable_source->get_datatype());
|
||||||
return;
|
found_source = true;
|
||||||
|
break;
|
||||||
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
|
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
|
||||||
p_identifier->set_datatype(p_identifier->bind_source->get_datatype());
|
p_identifier->set_datatype(p_identifier->bind_source->get_datatype());
|
||||||
return;
|
found_source = true;
|
||||||
|
break;
|
||||||
case GDScriptParser::IdentifierNode::LOCAL_BIND: {
|
case GDScriptParser::IdentifierNode::LOCAL_BIND: {
|
||||||
GDScriptParser::DataType result = p_identifier->bind_source->get_datatype();
|
GDScriptParser::DataType result = p_identifier->bind_source->get_datatype();
|
||||||
result.is_constant = true;
|
result.is_constant = true;
|
||||||
p_identifier->set_datatype(result);
|
p_identifier->set_datatype(result);
|
||||||
return;
|
found_source = true;
|
||||||
}
|
} break;
|
||||||
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
|
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not a local, so check members.
|
// Not a local, so check members.
|
||||||
|
if (!found_source) {
|
||||||
reduce_identifier_from_base(p_identifier);
|
reduce_identifier_from_base(p_identifier);
|
||||||
if (p_identifier->get_datatype().is_set()) {
|
if (p_identifier->source != GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->get_datatype().is_set()) {
|
||||||
// Found.
|
// 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;
|
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.
|
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) {
|
void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {
|
||||||
p_literal->reduced_value = p_literal->value;
|
p_literal->reduced_value = p_literal->value;
|
||||||
p_literal->is_constant = true;
|
p_literal->is_constant = true;
|
||||||
@@ -3526,6 +3577,13 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) {
|
|||||||
return ref;
|
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() {
|
Error GDScriptAnalyzer::resolve_inheritance() {
|
||||||
return resolve_inheritance(parser->head);
|
return resolve_inheritance(parser->head);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class GDScriptAnalyzer {
|
|||||||
HashMap<String, Ref<GDScriptParserRef>> depended_parsers;
|
HashMap<String, Ref<GDScriptParserRef>> depended_parsers;
|
||||||
|
|
||||||
const GDScriptParser::EnumNode *current_enum = nullptr;
|
const GDScriptParser::EnumNode *current_enum = nullptr;
|
||||||
|
List<const GDScriptParser::LambdaNode *> lambda_stack;
|
||||||
|
|
||||||
Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true);
|
Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true);
|
||||||
GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type);
|
GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type);
|
||||||
@@ -82,6 +83,7 @@ class GDScriptAnalyzer {
|
|||||||
void reduce_get_node(GDScriptParser::GetNodeNode *p_get_node);
|
void reduce_get_node(GDScriptParser::GetNodeNode *p_get_node);
|
||||||
void reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin = false);
|
void reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin = false);
|
||||||
void reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base = nullptr);
|
void reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base = nullptr);
|
||||||
|
void reduce_lambda(GDScriptParser::LambdaNode *p_lambda);
|
||||||
void reduce_literal(GDScriptParser::LiteralNode *p_literal);
|
void reduce_literal(GDScriptParser::LiteralNode *p_literal);
|
||||||
void reduce_preload(GDScriptParser::PreloadNode *p_preload);
|
void reduce_preload(GDScriptParser::PreloadNode *p_preload);
|
||||||
void reduce_self(GDScriptParser::SelfNode *p_self);
|
void reduce_self(GDScriptParser::SelfNode *p_self);
|
||||||
@@ -109,6 +111,7 @@ class GDScriptAnalyzer {
|
|||||||
void mark_node_unsafe(const GDScriptParser::Node *p_node);
|
void mark_node_unsafe(const GDScriptParser::Node *p_node);
|
||||||
bool class_exists(const StringName &p_class) const;
|
bool class_exists(const StringName &p_class) const;
|
||||||
Ref<GDScriptParserRef> get_parser_for(const String &p_path);
|
Ref<GDScriptParserRef> get_parser_for(const String &p_path);
|
||||||
|
const GDScriptParser::LambdaNode *get_current_lambda() const;
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
|
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1227,7 +1227,7 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
|
|||||||
} else {
|
} else {
|
||||||
p_function->parameters_indices[parameter->identifier->name] = p_function->parameters.size();
|
p_function->parameters_indices[parameter->identifier->name] = p_function->parameters.size();
|
||||||
p_function->parameters.push_back(parameter);
|
p_function->parameters.push_back(parameter);
|
||||||
p_body->add_local(parameter);
|
p_body->add_local(parameter, current_function);
|
||||||
}
|
}
|
||||||
} while (match(GDScriptTokenizer::Token::COMMA));
|
} while (match(GDScriptTokenizer::Token::COMMA));
|
||||||
}
|
}
|
||||||
@@ -1365,6 +1365,7 @@ bool GDScriptParser::register_annotation(const MethodInfo &p_info, uint32_t p_ta
|
|||||||
GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, SuiteNode *p_suite, bool p_for_lambda) {
|
GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, SuiteNode *p_suite, bool p_for_lambda) {
|
||||||
SuiteNode *suite = p_suite != nullptr ? p_suite : alloc_node<SuiteNode>();
|
SuiteNode *suite = p_suite != nullptr ? p_suite : alloc_node<SuiteNode>();
|
||||||
suite->parent_block = current_suite;
|
suite->parent_block = current_suite;
|
||||||
|
suite->parent_function = current_function;
|
||||||
current_suite = suite;
|
current_suite = suite;
|
||||||
|
|
||||||
bool multiline = false;
|
bool multiline = false;
|
||||||
@@ -1401,7 +1402,7 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
|
|||||||
if (local.type != SuiteNode::Local::UNDEFINED) {
|
if (local.type != SuiteNode::Local::UNDEFINED) {
|
||||||
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), variable->identifier->name));
|
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), variable->identifier->name));
|
||||||
}
|
}
|
||||||
current_suite->add_local(variable);
|
current_suite->add_local(variable, current_function);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Node::CONSTANT: {
|
case Node::CONSTANT: {
|
||||||
@@ -1416,7 +1417,7 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
|
|||||||
}
|
}
|
||||||
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", name, constant->identifier->name));
|
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", name, constant->identifier->name));
|
||||||
}
|
}
|
||||||
current_suite->add_local(constant);
|
current_suite->add_local(constant, current_function);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -1647,7 +1648,7 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
|
|||||||
|
|
||||||
SuiteNode *suite = alloc_node<SuiteNode>();
|
SuiteNode *suite = alloc_node<SuiteNode>();
|
||||||
if (n_for->variable) {
|
if (n_for->variable) {
|
||||||
suite->add_local(SuiteNode::Local(n_for->variable));
|
suite->add_local(SuiteNode::Local(n_for->variable, current_function));
|
||||||
}
|
}
|
||||||
suite->parent_for = n_for;
|
suite->parent_for = n_for;
|
||||||
|
|
||||||
@@ -1802,7 +1803,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
|
|||||||
branch->patterns[0]->binds.get_key_list(&binds);
|
branch->patterns[0]->binds.get_key_list(&binds);
|
||||||
|
|
||||||
for (List<StringName>::Element *E = binds.front(); E != nullptr; E = E->next()) {
|
for (List<StringName>::Element *E = binds.front(); E != nullptr; E = E->next()) {
|
||||||
SuiteNode::Local local(branch->patterns[0]->binds[E->get()]);
|
SuiteNode::Local local(branch->patterns[0]->binds[E->get()], current_function);
|
||||||
suite->add_local(local);
|
suite->add_local(local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2053,6 +2054,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode
|
|||||||
|
|
||||||
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
|
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
|
||||||
const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
|
const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
|
||||||
|
|
||||||
|
identifier->source_function = declaration.source_function;
|
||||||
switch (declaration.type) {
|
switch (declaration.type) {
|
||||||
case SuiteNode::Local::CONSTANT:
|
case SuiteNode::Local::CONSTANT:
|
||||||
identifier->source = IdentifierNode::LOCAL_CONSTANT;
|
identifier->source = IdentifierNode::LOCAL_CONSTANT;
|
||||||
@@ -2731,7 +2734,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_
|
|||||||
|
|
||||||
GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_previous_operand, bool p_can_assign) {
|
GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_previous_operand, bool p_can_assign) {
|
||||||
LambdaNode *lambda = alloc_node<LambdaNode>();
|
LambdaNode *lambda = alloc_node<LambdaNode>();
|
||||||
|
lambda->parent_function = current_function;
|
||||||
FunctionNode *function = alloc_node<FunctionNode>();
|
FunctionNode *function = alloc_node<FunctionNode>();
|
||||||
|
function->source_lambda = lambda;
|
||||||
|
|
||||||
|
function->is_static = current_function != nullptr ? current_function->is_static : false;
|
||||||
|
|
||||||
if (match(GDScriptTokenizer::Token::IDENTIFIER)) {
|
if (match(GDScriptTokenizer::Token::IDENTIFIER)) {
|
||||||
function->identifier = parse_identifier();
|
function->identifier = parse_identifier();
|
||||||
@@ -4024,6 +4031,14 @@ void GDScriptParser::TreePrinter::print_if(IfNode *p_if, bool p_is_elif) {
|
|||||||
|
|
||||||
void GDScriptParser::TreePrinter::print_lambda(LambdaNode *p_lambda) {
|
void GDScriptParser::TreePrinter::print_lambda(LambdaNode *p_lambda) {
|
||||||
print_function(p_lambda->function, "Lambda");
|
print_function(p_lambda->function, "Lambda");
|
||||||
|
push_text("| captures [ ");
|
||||||
|
for (const Map<StringName, IdentifierNode *>::Element *E = p_lambda->captures.front(); E; E = E->next()) {
|
||||||
|
push_text(E->key().operator String());
|
||||||
|
if (E->next()) {
|
||||||
|
push_text(" , ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
push_line(" ]");
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDScriptParser::TreePrinter::print_literal(LiteralNode *p_literal) {
|
void GDScriptParser::TreePrinter::print_literal(LiteralNode *p_literal) {
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ public:
|
|||||||
struct GetNodeNode;
|
struct GetNodeNode;
|
||||||
struct IdentifierNode;
|
struct IdentifierNode;
|
||||||
struct IfNode;
|
struct IfNode;
|
||||||
|
struct LambdaNode;
|
||||||
struct LiteralNode;
|
struct LiteralNode;
|
||||||
struct MatchNode;
|
struct MatchNode;
|
||||||
struct MatchBranchNode;
|
struct MatchBranchNode;
|
||||||
@@ -729,6 +730,7 @@ public:
|
|||||||
bool is_coroutine = false;
|
bool is_coroutine = false;
|
||||||
MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
|
MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
|
||||||
MethodInfo info;
|
MethodInfo info;
|
||||||
|
LambdaNode *source_lambda = nullptr;
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
Vector<Variant> default_arg_values;
|
Vector<Variant> default_arg_values;
|
||||||
String doc_description;
|
String doc_description;
|
||||||
@@ -772,6 +774,7 @@ public:
|
|||||||
VariableNode *variable_source;
|
VariableNode *variable_source;
|
||||||
IdentifierNode *bind_source;
|
IdentifierNode *bind_source;
|
||||||
};
|
};
|
||||||
|
FunctionNode *source_function = nullptr;
|
||||||
|
|
||||||
int usages = 0; // Useful for binds/iterator variable.
|
int usages = 0; // Useful for binds/iterator variable.
|
||||||
|
|
||||||
@@ -792,6 +795,8 @@ public:
|
|||||||
|
|
||||||
struct LambdaNode : public ExpressionNode {
|
struct LambdaNode : public ExpressionNode {
|
||||||
FunctionNode *function = nullptr;
|
FunctionNode *function = nullptr;
|
||||||
|
FunctionNode *parent_function = nullptr;
|
||||||
|
Map<StringName, IdentifierNode *> captures;
|
||||||
|
|
||||||
bool has_name() const {
|
bool has_name() const {
|
||||||
return function && function->identifier;
|
return function && function->identifier;
|
||||||
@@ -955,6 +960,7 @@ public:
|
|||||||
IdentifierNode *bind;
|
IdentifierNode *bind;
|
||||||
};
|
};
|
||||||
StringName name;
|
StringName name;
|
||||||
|
FunctionNode *source_function = nullptr;
|
||||||
|
|
||||||
int start_line = 0, end_line = 0;
|
int start_line = 0, end_line = 0;
|
||||||
int start_column = 0, end_column = 0;
|
int start_column = 0, end_column = 0;
|
||||||
@@ -964,10 +970,11 @@ public:
|
|||||||
String get_name() const;
|
String get_name() const;
|
||||||
|
|
||||||
Local() {}
|
Local() {}
|
||||||
Local(ConstantNode *p_constant) {
|
Local(ConstantNode *p_constant, FunctionNode *p_source_function) {
|
||||||
type = CONSTANT;
|
type = CONSTANT;
|
||||||
constant = p_constant;
|
constant = p_constant;
|
||||||
name = p_constant->identifier->name;
|
name = p_constant->identifier->name;
|
||||||
|
source_function = p_source_function;
|
||||||
|
|
||||||
start_line = p_constant->start_line;
|
start_line = p_constant->start_line;
|
||||||
end_line = p_constant->end_line;
|
end_line = p_constant->end_line;
|
||||||
@@ -976,10 +983,11 @@ public:
|
|||||||
leftmost_column = p_constant->leftmost_column;
|
leftmost_column = p_constant->leftmost_column;
|
||||||
rightmost_column = p_constant->rightmost_column;
|
rightmost_column = p_constant->rightmost_column;
|
||||||
}
|
}
|
||||||
Local(VariableNode *p_variable) {
|
Local(VariableNode *p_variable, FunctionNode *p_source_function) {
|
||||||
type = VARIABLE;
|
type = VARIABLE;
|
||||||
variable = p_variable;
|
variable = p_variable;
|
||||||
name = p_variable->identifier->name;
|
name = p_variable->identifier->name;
|
||||||
|
source_function = p_source_function;
|
||||||
|
|
||||||
start_line = p_variable->start_line;
|
start_line = p_variable->start_line;
|
||||||
end_line = p_variable->end_line;
|
end_line = p_variable->end_line;
|
||||||
@@ -988,10 +996,11 @@ public:
|
|||||||
leftmost_column = p_variable->leftmost_column;
|
leftmost_column = p_variable->leftmost_column;
|
||||||
rightmost_column = p_variable->rightmost_column;
|
rightmost_column = p_variable->rightmost_column;
|
||||||
}
|
}
|
||||||
Local(ParameterNode *p_parameter) {
|
Local(ParameterNode *p_parameter, FunctionNode *p_source_function) {
|
||||||
type = PARAMETER;
|
type = PARAMETER;
|
||||||
parameter = p_parameter;
|
parameter = p_parameter;
|
||||||
name = p_parameter->identifier->name;
|
name = p_parameter->identifier->name;
|
||||||
|
source_function = p_source_function;
|
||||||
|
|
||||||
start_line = p_parameter->start_line;
|
start_line = p_parameter->start_line;
|
||||||
end_line = p_parameter->end_line;
|
end_line = p_parameter->end_line;
|
||||||
@@ -1000,10 +1009,11 @@ public:
|
|||||||
leftmost_column = p_parameter->leftmost_column;
|
leftmost_column = p_parameter->leftmost_column;
|
||||||
rightmost_column = p_parameter->rightmost_column;
|
rightmost_column = p_parameter->rightmost_column;
|
||||||
}
|
}
|
||||||
Local(IdentifierNode *p_identifier) {
|
Local(IdentifierNode *p_identifier, FunctionNode *p_source_function) {
|
||||||
type = FOR_VARIABLE;
|
type = FOR_VARIABLE;
|
||||||
bind = p_identifier;
|
bind = p_identifier;
|
||||||
name = p_identifier->name;
|
name = p_identifier->name;
|
||||||
|
source_function = p_source_function;
|
||||||
|
|
||||||
start_line = p_identifier->start_line;
|
start_line = p_identifier->start_line;
|
||||||
end_line = p_identifier->end_line;
|
end_line = p_identifier->end_line;
|
||||||
@@ -1028,9 +1038,9 @@ public:
|
|||||||
bool has_local(const StringName &p_name) const;
|
bool has_local(const StringName &p_name) const;
|
||||||
const Local &get_local(const StringName &p_name) const;
|
const Local &get_local(const StringName &p_name) const;
|
||||||
template <class T>
|
template <class T>
|
||||||
void add_local(T *p_local) {
|
void add_local(T *p_local, FunctionNode *p_source_function) {
|
||||||
locals_indices[p_local->identifier->name] = locals.size();
|
locals_indices[p_local->identifier->name] = locals.size();
|
||||||
locals.push_back(Local(p_local));
|
locals.push_back(Local(p_local, p_source_function));
|
||||||
}
|
}
|
||||||
void add_local(const Local &p_local) {
|
void add_local(const Local &p_local) {
|
||||||
locals_indices[p_local.name] = locals.size();
|
locals_indices[p_local.name] = locals.size();
|
||||||
|
|||||||
@@ -118,6 +118,18 @@ static void test_parser(const String &p_code, const String &p_script_path, const
|
|||||||
print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
|
print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GDScriptAnalyzer analyzer(&parser);
|
||||||
|
analyzer.analyze();
|
||||||
|
|
||||||
|
if (err != OK) {
|
||||||
|
const List<GDScriptParser::ParserError> &errors = parser.get_errors();
|
||||||
|
for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
|
||||||
|
const GDScriptParser::ParserError &error = E->get();
|
||||||
|
print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
GDScriptParser::TreePrinter printer;
|
GDScriptParser::TreePrinter printer;
|
||||||
printer.print_tree(parser);
|
printer.print_tree(parser);
|
||||||
|
|||||||
Reference in New Issue
Block a user