Parser: Properly set node extents

This commit is contained in:
cdemirer 2022-07-11 21:31:15 +03:00
parent d26442e709
commit 12b9572121
2 changed files with 186 additions and 38 deletions

View File

@ -162,6 +162,7 @@ void GDScriptParser::clear() {
for_completion = false; for_completion = false;
errors.clear(); errors.clear();
multiline_stack.clear(); multiline_stack.clear();
nodes_in_progress.clear();
} }
void GDScriptParser::push_error(const String &p_message, const Node *p_origin) { void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
@ -413,6 +414,9 @@ GDScriptTokenizer::Token GDScriptParser::advance() {
push_error(current.literal); push_error(current.literal);
current = tokenizer.scan(); current = tokenizer.scan();
} }
for (Node *n : nodes_in_progress) {
update_extents(n);
}
return previous; return previous;
} }
@ -609,6 +613,7 @@ void GDScriptParser::parse_program() {
} }
parse_class_body(true); parse_class_body(true);
complete_extents(head);
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
for (const KeyValue<int, GDScriptTokenizer::CommentData> &E : tokenizer.get_comments()) { for (const KeyValue<int, GDScriptTokenizer::CommentData> &E : tokenizer.get_comments()) {
@ -649,6 +654,7 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
if (multiline && !consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block after class declaration.)")) { if (multiline && !consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block after class declaration.)")) {
current_class = previous_class; current_class = previous_class;
complete_extents(n_class);
return n_class; return n_class;
} }
@ -661,6 +667,7 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
} }
parse_class_body(multiline); parse_class_body(multiline);
complete_extents(n_class);
if (multiline) { if (multiline) {
consume(GDScriptTokenizer::Token::DEDENT, R"(Missing unindent at the end of the class body.)"); consume(GDScriptTokenizer::Token::DEDENT, R"(Missing unindent at the end of the class body.)");
@ -870,11 +877,13 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable() {
} }
GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_property) { GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_property) {
VariableNode *variable = alloc_node<VariableNode>();
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected variable name after "var".)")) { if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected variable name after "var".)")) {
complete_extents(variable);
return nullptr; return nullptr;
} }
VariableNode *variable = alloc_node<VariableNode>();
variable->identifier = parse_identifier(); variable->identifier = parse_identifier();
variable->export_info.name = variable->identifier->name; variable->export_info.name = variable->identifier->name;
@ -882,10 +891,10 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
if (check(GDScriptTokenizer::Token::NEWLINE)) { if (check(GDScriptTokenizer::Token::NEWLINE)) {
if (p_allow_property) { if (p_allow_property) {
advance(); advance();
return parse_property(variable, true); return parse_property(variable, true);
} else { } else {
push_error(R"(Expected type after ":")"); push_error(R"(Expected type after ":")");
complete_extents(variable);
return nullptr; return nullptr;
} }
} else if (check((GDScriptTokenizer::Token::EQUAL))) { } else if (check((GDScriptTokenizer::Token::EQUAL))) {
@ -924,6 +933,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
} }
} }
complete_extents(variable);
end_statement("variable declaration"); end_statement("variable declaration");
return variable; return variable;
@ -932,6 +942,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_variable, bool p_need_indent) { GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_variable, bool p_need_indent) {
if (p_need_indent) { if (p_need_indent) {
if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block for property after ":".)")) { if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block for property after ":".)")) {
complete_extents(p_variable);
return nullptr; return nullptr;
} }
} }
@ -941,6 +952,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var
make_completion_context(COMPLETION_PROPERTY_DECLARATION, property); make_completion_context(COMPLETION_PROPERTY_DECLARATION, property);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected "get" or "set" for property declaration.)")) { if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected "get" or "set" for property declaration.)")) {
complete_extents(p_variable);
return nullptr; return nullptr;
} }
@ -997,6 +1009,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var
} }
function = parse_identifier(); function = parse_identifier();
} }
complete_extents(p_variable);
if (p_variable->property == VariableNode::PROP_SETGET) { if (p_variable->property == VariableNode::PROP_SETGET) {
end_statement("property declaration"); end_statement("property declaration");
@ -1011,37 +1024,37 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var
void GDScriptParser::parse_property_setter(VariableNode *p_variable) { void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
switch (p_variable->property) { switch (p_variable->property) {
case VariableNode::PROP_INLINE: { case VariableNode::PROP_INLINE: {
FunctionNode *function = alloc_node<FunctionNode>();
IdentifierNode *identifier = alloc_node<IdentifierNode>();
complete_extents(identifier);
identifier->name = "@" + p_variable->identifier->name + "_setter";
function->identifier = identifier;
consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)"); consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)");
ParameterNode *parameter = alloc_node<ParameterNode>();
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) { if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) {
reset_extents(parameter, previous);
p_variable->setter_parameter = parse_identifier(); p_variable->setter_parameter = parse_identifier();
parameter->identifier = p_variable->setter_parameter;
function->parameters_indices[parameter->identifier->name] = 0;
function->parameters.push_back(parameter);
} }
complete_extents(parameter);
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*"); consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*");
consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*"); consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*");
IdentifierNode *identifier = alloc_node<IdentifierNode>();
identifier->name = "@" + p_variable->identifier->name + "_setter";
FunctionNode *function = alloc_node<FunctionNode>();
function->identifier = identifier;
FunctionNode *previous_function = current_function; FunctionNode *previous_function = current_function;
current_function = function; current_function = function;
if (p_variable->setter_parameter != nullptr) {
ParameterNode *parameter = alloc_node<ParameterNode>();
parameter->identifier = p_variable->setter_parameter;
if (parameter->identifier != nullptr) {
function->parameters_indices[parameter->identifier->name] = 0;
function->parameters.push_back(parameter);
SuiteNode *body = alloc_node<SuiteNode>(); SuiteNode *body = alloc_node<SuiteNode>();
body->add_local(parameter, function); body->add_local(parameter, function);
function->body = parse_suite("setter declaration", body); function->body = parse_suite("setter declaration", body);
p_variable->setter = function; p_variable->setter = function;
} }
current_function = previous_function; current_function = previous_function;
complete_extents(function);
break; break;
} }
case VariableNode::PROP_SETGET: case VariableNode::PROP_SETGET:
@ -1059,12 +1072,13 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
void GDScriptParser::parse_property_getter(VariableNode *p_variable) { void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
switch (p_variable->property) { switch (p_variable->property) {
case VariableNode::PROP_INLINE: { case VariableNode::PROP_INLINE: {
FunctionNode *function = alloc_node<FunctionNode>();
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "get".)"); consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "get".)");
IdentifierNode *identifier = alloc_node<IdentifierNode>(); IdentifierNode *identifier = alloc_node<IdentifierNode>();
complete_extents(identifier);
identifier->name = "@" + p_variable->identifier->name + "_getter"; identifier->name = "@" + p_variable->identifier->name + "_getter";
FunctionNode *function = alloc_node<FunctionNode>();
function->identifier = identifier; function->identifier = identifier;
FunctionNode *previous_function = current_function; FunctionNode *previous_function = current_function;
@ -1072,9 +1086,10 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
SuiteNode *body = alloc_node<SuiteNode>(); SuiteNode *body = alloc_node<SuiteNode>();
function->body = parse_suite("getter declaration", body); function->body = parse_suite("getter declaration", body);
p_variable->getter = function; p_variable->getter = function;
current_function = previous_function; current_function = previous_function;
complete_extents(function);
break; break;
} }
case VariableNode::PROP_SETGET: case VariableNode::PROP_SETGET:
@ -1090,11 +1105,12 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
} }
GDScriptParser::ConstantNode *GDScriptParser::parse_constant() { GDScriptParser::ConstantNode *GDScriptParser::parse_constant() {
ConstantNode *constant = alloc_node<ConstantNode>();
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected constant name after "const".)")) { if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected constant name after "const".)")) {
return nullptr; return nullptr;
} }
ConstantNode *constant = alloc_node<ConstantNode>();
constant->identifier = parse_identifier(); constant->identifier = parse_identifier();
if (match(GDScriptTokenizer::Token::COLON)) { if (match(GDScriptTokenizer::Token::COLON)) {
@ -1113,12 +1129,15 @@ GDScriptParser::ConstantNode *GDScriptParser::parse_constant() {
if (constant->initializer == nullptr) { if (constant->initializer == nullptr) {
push_error(R"(Expected initializer expression for constant.)"); push_error(R"(Expected initializer expression for constant.)");
complete_extents(constant);
return nullptr; return nullptr;
} }
} else { } else {
complete_extents(constant);
return nullptr; return nullptr;
} }
complete_extents(constant);
end_statement("constant declaration"); end_statement("constant declaration");
return constant; return constant;
@ -1148,15 +1167,18 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
parameter->default_value = parse_expression(false); parameter->default_value = parse_expression(false);
} }
complete_extents(parameter);
return parameter; return parameter;
} }
GDScriptParser::SignalNode *GDScriptParser::parse_signal() { GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
SignalNode *signal = alloc_node<SignalNode>();
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected signal name after "signal".)")) { if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected signal name after "signal".)")) {
complete_extents(signal);
return nullptr; return nullptr;
} }
SignalNode *signal = alloc_node<SignalNode>();
signal->identifier = parse_identifier(); signal->identifier = parse_identifier();
if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) { if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
@ -1188,6 +1210,7 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after signal parameters.)*"); consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after signal parameters.)*");
} }
complete_extents(signal);
end_statement("signal declaration"); end_statement("signal declaration");
return signal; return signal;
@ -1299,6 +1322,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
} }
#endif // TOOLS_ENABLED #endif // TOOLS_ENABLED
complete_extents(enum_node);
end_statement("enum"); end_statement("enum");
return enum_node; return enum_node;
@ -1350,19 +1374,22 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
} }
GDScriptParser::FunctionNode *GDScriptParser::parse_function() { GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
FunctionNode *function = alloc_node<FunctionNode>();
bool _static = false; bool _static = false;
if (previous.type == GDScriptTokenizer::Token::STATIC) { if (previous.type == GDScriptTokenizer::Token::STATIC) {
// TODO: Improve message if user uses "static" with "var" or "const" // TODO: Improve message if user uses "static" with "var" or "const"
if (!consume(GDScriptTokenizer::Token::FUNC, R"(Expected "func" after "static".)")) { if (!consume(GDScriptTokenizer::Token::FUNC, R"(Expected "func" after "static".)")) {
complete_extents(function);
return nullptr; return nullptr;
} }
_static = true; _static = true;
} }
FunctionNode *function = alloc_node<FunctionNode>();
make_completion_context(COMPLETION_OVERRIDE_METHOD, function); make_completion_context(COMPLETION_OVERRIDE_METHOD, function);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) { if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) {
complete_extents(function);
return nullptr; return nullptr;
} }
@ -1384,6 +1411,7 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
function->body = parse_suite("function declaration", body); function->body = parse_suite("function declaration", body);
current_function = previous_function; current_function = previous_function;
complete_extents(function);
return function; return function;
} }
@ -1431,6 +1459,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
} }
pop_completion_call(); pop_completion_call();
} }
complete_extents(annotation);
match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional. match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional.
@ -1480,9 +1509,11 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
if (multiline) { if (multiline) {
if (!consume(GDScriptTokenizer::Token::INDENT, vformat(R"(Expected indented block after %s.)", p_context))) { if (!consume(GDScriptTokenizer::Token::INDENT, vformat(R"(Expected indented block after %s.)", p_context))) {
current_suite = suite->parent_block; current_suite = suite->parent_block;
complete_extents(suite);
return suite; return suite;
} }
} }
reset_extents(suite, current);
int error_count = 0; int error_count = 0;
@ -1532,6 +1563,8 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
} while ((multiline || previous.type == GDScriptTokenizer::Token::SEMICOLON) && !check(GDScriptTokenizer::Token::DEDENT) && !lambda_ended && !is_at_end()); } while ((multiline || previous.type == GDScriptTokenizer::Token::SEMICOLON) && !check(GDScriptTokenizer::Token::DEDENT) && !lambda_ended && !is_at_end());
complete_extents(suite);
if (multiline) { if (multiline) {
if (!lambda_ended) { if (!lambda_ended) {
consume(GDScriptTokenizer::Token::DEDENT, vformat(R"(Missing unindent at the end of %s.)", p_context)); consume(GDScriptTokenizer::Token::DEDENT, vformat(R"(Missing unindent at the end of %s.)", p_context));
@ -1562,6 +1595,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
case GDScriptTokenizer::Token::PASS: case GDScriptTokenizer::Token::PASS:
advance(); advance();
result = alloc_node<PassNode>(); result = alloc_node<PassNode>();
complete_extents(result);
end_statement(R"("pass")"); end_statement(R"("pass")");
break; break;
case GDScriptTokenizer::Token::VAR: case GDScriptTokenizer::Token::VAR:
@ -1609,6 +1643,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
// If this fails the expression will be nullptr, but that's the same as no return, so it's fine. // If this fails the expression will be nullptr, but that's the same as no return, so it's fine.
n_return->return_value = parse_expression(false); n_return->return_value = parse_expression(false);
} }
complete_extents(n_return);
result = n_return; result = n_return;
current_suite->has_return = true; current_suite->has_return = true;
@ -1619,6 +1654,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
case GDScriptTokenizer::Token::BREAKPOINT: case GDScriptTokenizer::Token::BREAKPOINT:
advance(); advance();
result = alloc_node<BreakpointNode>(); result = alloc_node<BreakpointNode>();
complete_extents(result);
end_statement(R"("breakpoint")"); end_statement(R"("breakpoint")");
break; break;
case GDScriptTokenizer::Token::ASSERT: case GDScriptTokenizer::Token::ASSERT:
@ -1710,6 +1746,7 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() {
assert->condition = parse_expression(false); assert->condition = parse_expression(false);
if (assert->condition == nullptr) { if (assert->condition == nullptr) {
push_error("Expected expression to assert."); push_error("Expected expression to assert.");
complete_extents(assert);
return nullptr; return nullptr;
} }
@ -1718,12 +1755,14 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() {
assert->message = parse_expression(false); assert->message = parse_expression(false);
if (assert->message == nullptr) { if (assert->message == nullptr) {
push_error(R"(Expected error message for assert after ",".)"); push_error(R"(Expected error message for assert after ",".)");
complete_extents(assert);
return nullptr; return nullptr;
} }
} }
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after assert expression.)*"); consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after assert expression.)*");
complete_extents(assert);
end_statement(R"("assert")"); end_statement(R"("assert")");
return assert; return assert;
@ -1733,8 +1772,10 @@ GDScriptParser::BreakNode *GDScriptParser::parse_break() {
if (!can_break) { if (!can_break) {
push_error(R"(Cannot use "break" outside of a loop.)"); push_error(R"(Cannot use "break" outside of a loop.)");
} }
BreakNode *break_node = alloc_node<BreakNode>();
complete_extents(break_node);
end_statement(R"("break")"); end_statement(R"("break")");
return alloc_node<BreakNode>(); return break_node;
} }
GDScriptParser::ContinueNode *GDScriptParser::parse_continue() { GDScriptParser::ContinueNode *GDScriptParser::parse_continue() {
@ -1742,9 +1783,10 @@ GDScriptParser::ContinueNode *GDScriptParser::parse_continue() {
push_error(R"(Cannot use "continue" outside of a loop or pattern matching block.)"); push_error(R"(Cannot use "continue" outside of a loop or pattern matching block.)");
} }
current_suite->has_continue = true; current_suite->has_continue = true;
end_statement(R"("continue")");
ContinueNode *cont = alloc_node<ContinueNode>(); ContinueNode *cont = alloc_node<ContinueNode>();
cont->is_for_match = is_continue_match; cont->is_for_match = is_continue_match;
complete_extents(cont);
end_statement(R"("continue")");
return cont; return cont;
} }
@ -1786,6 +1828,7 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
suite->parent_for = n_for; suite->parent_for = n_for;
n_for->loop = parse_suite(R"("for" block)", suite); n_for->loop = parse_suite(R"("for" block)", suite);
complete_extents(n_for);
// Reset break/continue state. // Reset break/continue state.
can_break = could_break; can_break = could_break;
@ -1813,15 +1856,16 @@ GDScriptParser::IfNode *GDScriptParser::parse_if(const String &p_token) {
} }
if (match(GDScriptTokenizer::Token::ELIF)) { if (match(GDScriptTokenizer::Token::ELIF)) {
IfNode *elif = parse_if("elif");
SuiteNode *else_block = alloc_node<SuiteNode>(); SuiteNode *else_block = alloc_node<SuiteNode>();
IfNode *elif = parse_if("elif");
else_block->statements.push_back(elif); else_block->statements.push_back(elif);
complete_extents(else_block);
n_if->false_block = else_block; n_if->false_block = else_block;
} else if (match(GDScriptTokenizer::Token::ELSE)) { } else if (match(GDScriptTokenizer::Token::ELSE)) {
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "else".)"); consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "else".)");
n_if->false_block = parse_suite(R"("else" block)"); n_if->false_block = parse_suite(R"("else" block)");
} }
complete_extents(n_if);
if (n_if->false_block != nullptr && n_if->false_block->has_return && n_if->true_block->has_return) { if (n_if->false_block != nullptr && n_if->false_block->has_return && n_if->true_block->has_return) {
current_suite->has_return = true; current_suite->has_return = true;
@ -1845,6 +1889,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
consume(GDScriptTokenizer::Token::NEWLINE, R"(Expected a newline after "match" statement.)"); consume(GDScriptTokenizer::Token::NEWLINE, R"(Expected a newline after "match" statement.)");
if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected an indented block after "match" statement.)")) { if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected an indented block after "match" statement.)")) {
complete_extents(match);
return match; return match;
} }
@ -1878,6 +1923,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
#endif #endif
match->branches.push_back(branch); match->branches.push_back(branch);
} }
complete_extents(match);
consume(GDScriptTokenizer::Token::DEDENT, R"(Expected an indented block after "match" statement.)"); consume(GDScriptTokenizer::Token::DEDENT, R"(Expected an indented block after "match" statement.)");
@ -1892,6 +1938,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() { GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
MatchBranchNode *branch = alloc_node<MatchBranchNode>(); MatchBranchNode *branch = alloc_node<MatchBranchNode>();
reset_extents(branch, current);
bool has_bind = false; bool has_bind = false;
@ -1919,6 +1966,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
} }
if (!consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)")) { if (!consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)")) {
complete_extents(branch);
return nullptr; return nullptr;
} }
@ -1939,6 +1987,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
} }
branch->block = parse_suite("match pattern block", suite); branch->block = parse_suite("match pattern block", suite);
complete_extents(branch);
// Restore continue state. // Restore continue state.
can_continue = could_continue; can_continue = could_continue;
@ -1949,12 +1998,14 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_root_pattern) { GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_root_pattern) {
PatternNode *pattern = alloc_node<PatternNode>(); PatternNode *pattern = alloc_node<PatternNode>();
reset_extents(pattern, current);
switch (current.type) { switch (current.type) {
case GDScriptTokenizer::Token::VAR: { case GDScriptTokenizer::Token::VAR: {
// Bind. // Bind.
advance(); advance();
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected bind name after "var".)")) { if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected bind name after "var".)")) {
complete_extents(pattern);
return nullptr; return nullptr;
} }
pattern->pattern_type = PatternNode::PT_BIND; pattern->pattern_type = PatternNode::PT_BIND;
@ -1965,12 +2016,14 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
if (p_root_pattern != nullptr) { if (p_root_pattern != nullptr) {
if (p_root_pattern->has_bind(pattern->bind->name)) { if (p_root_pattern->has_bind(pattern->bind->name)) {
push_error(vformat(R"(Bind variable name "%s" was already used in this pattern.)", pattern->bind->name)); push_error(vformat(R"(Bind variable name "%s" was already used in this pattern.)", pattern->bind->name));
complete_extents(pattern);
return nullptr; return nullptr;
} }
} }
if (current_suite->has_local(pattern->bind->name)) { if (current_suite->has_local(pattern->bind->name)) {
push_error(vformat(R"(There's already a %s named "%s" in this scope.)", current_suite->get_local(pattern->bind->name).get_name(), pattern->bind->name)); push_error(vformat(R"(There's already a %s named "%s" in this scope.)", current_suite->get_local(pattern->bind->name).get_name(), pattern->bind->name));
complete_extents(pattern);
return nullptr; return nullptr;
} }
@ -2023,6 +2076,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)"); push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
} else { } else {
PatternNode *sub_pattern = alloc_node<PatternNode>(); PatternNode *sub_pattern = alloc_node<PatternNode>();
complete_extents(sub_pattern);
sub_pattern->pattern_type = PatternNode::PT_REST; sub_pattern->pattern_type = PatternNode::PT_REST;
pattern->dictionary.push_back({ nullptr, sub_pattern }); pattern->dictionary.push_back({ nullptr, sub_pattern });
pattern->rest_used = true; pattern->rest_used = true;
@ -2071,6 +2125,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
break; break;
} }
} }
complete_extents(pattern);
return pattern; return pattern;
} }
@ -2104,6 +2159,7 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() {
is_continue_match = false; is_continue_match = false;
n_while->loop = parse_suite(R"("while" block)"); n_while->loop = parse_suite(R"("while" block)");
complete_extents(n_while);
// Reset break/continue state. // Reset break/continue state.
can_break = could_break; can_break = could_break;
@ -2176,6 +2232,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode
ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing literal node without literal token."); ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing literal node without literal token.");
} }
IdentifierNode *identifier = alloc_node<IdentifierNode>(); IdentifierNode *identifier = alloc_node<IdentifierNode>();
complete_extents(identifier);
identifier->name = previous.get_identifier(); identifier->name = previous.get_identifier();
if (current_suite != nullptr && current_suite->has_local(identifier->name)) { if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
@ -2227,6 +2284,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_literal(ExpressionNode *p_
} }
LiteralNode *literal = alloc_node<LiteralNode>(); LiteralNode *literal = alloc_node<LiteralNode>();
complete_extents(literal);
literal->value = previous.literal; literal->value = previous.literal;
return literal; return literal;
} }
@ -2236,6 +2294,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre
push_error(R"(Cannot use "self" inside a static function.)"); push_error(R"(Cannot use "self" inside a static function.)");
} }
SelfNode *self = alloc_node<SelfNode>(); SelfNode *self = alloc_node<SelfNode>();
complete_extents(self);
self->current_class = current_class; self->current_class = current_class;
return self; return self;
} }
@ -2243,6 +2302,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre
GDScriptParser::ExpressionNode *GDScriptParser::parse_builtin_constant(ExpressionNode *p_previous_operand, bool p_can_assign) { GDScriptParser::ExpressionNode *GDScriptParser::parse_builtin_constant(ExpressionNode *p_previous_operand, bool p_can_assign) {
GDScriptTokenizer::Token::Type op_type = previous.type; GDScriptTokenizer::Token::Type op_type = previous.type;
LiteralNode *constant = alloc_node<LiteralNode>(); LiteralNode *constant = alloc_node<LiteralNode>();
complete_extents(constant);
switch (op_type) { switch (op_type) {
case GDScriptTokenizer::Token::CONST_PI: case GDScriptTokenizer::Token::CONST_PI:
@ -2303,30 +2363,38 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_unary_operator(ExpressionN
} }
break; break;
default: default:
complete_extents(operation);
return nullptr; // Unreachable. return nullptr; // Unreachable.
} }
complete_extents(operation);
return operation; return operation;
} }
GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_not_in_operator(ExpressionNode *p_previous_operand, bool p_can_assign) { GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_not_in_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
// check that NOT is followed by IN by consuming it before calling parse_binary_operator which will only receive a plain IN // check that NOT is followed by IN by consuming it before calling parse_binary_operator which will only receive a plain IN
UnaryOpNode *operation = alloc_node<UnaryOpNode>();
reset_extents(operation, p_previous_operand);
update_extents(operation);
consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "not" in content-test operator.)"); consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "not" in content-test operator.)");
ExpressionNode *in_operation = parse_binary_operator(p_previous_operand, p_can_assign); ExpressionNode *in_operation = parse_binary_operator(p_previous_operand, p_can_assign);
UnaryOpNode *operation = alloc_node<UnaryOpNode>();
operation->operation = UnaryOpNode::OP_LOGIC_NOT; operation->operation = UnaryOpNode::OP_LOGIC_NOT;
operation->variant_op = Variant::OP_NOT; operation->variant_op = Variant::OP_NOT;
operation->operand = in_operation; operation->operand = in_operation;
complete_extents(operation);
return operation; return operation;
} }
GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) { GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
GDScriptTokenizer::Token op = previous; GDScriptTokenizer::Token op = previous;
BinaryOpNode *operation = alloc_node<BinaryOpNode>(); BinaryOpNode *operation = alloc_node<BinaryOpNode>();
reset_extents(operation, p_previous_operand);
update_extents(operation);
Precedence precedence = (Precedence)(get_rule(op.type)->precedence + 1); Precedence precedence = (Precedence)(get_rule(op.type)->precedence + 1);
operation->left_operand = p_previous_operand; operation->left_operand = p_previous_operand;
operation->right_operand = parse_precedence(precedence, false); operation->right_operand = parse_precedence(precedence, false);
complete_extents(operation);
if (operation->right_operand == nullptr) { if (operation->right_operand == nullptr) {
push_error(vformat(R"(Expected expression after "%s" operator.")", op.get_name())); push_error(vformat(R"(Expected expression after "%s" operator.")", op.get_name()));
@ -2429,8 +2497,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression
GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) { GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
// Only one ternary operation exists, so no abstraction here. // Only one ternary operation exists, so no abstraction here.
TernaryOpNode *operation = alloc_node<TernaryOpNode>(); TernaryOpNode *operation = alloc_node<TernaryOpNode>();
operation->true_expr = p_previous_operand; reset_extents(operation, p_previous_operand);
update_extents(operation);
operation->true_expr = p_previous_operand;
operation->condition = parse_precedence(PREC_TERNARY, false); operation->condition = parse_precedence(PREC_TERNARY, false);
if (operation->condition == nullptr) { if (operation->condition == nullptr) {
@ -2445,6 +2515,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(Expressio
push_error(R"(Expected expression after "else".)"); push_error(R"(Expected expression after "else".)");
} }
complete_extents(operation);
return operation; return operation;
} }
@ -2497,6 +2568,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
} }
AssignmentNode *assignment = alloc_node<AssignmentNode>(); AssignmentNode *assignment = alloc_node<AssignmentNode>();
reset_extents(assignment, p_previous_operand);
update_extents(assignment);
make_completion_context(COMPLETION_ASSIGN, assignment); make_completion_context(COMPLETION_ASSIGN, assignment);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
bool has_operator = true; bool has_operator = true;
@ -2561,6 +2635,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
if (assignment->assigned_value == nullptr) { if (assignment->assigned_value == nullptr) {
push_error(R"(Expected an expression after "=".)"); push_error(R"(Expected an expression after "=".)");
} }
complete_extents(assignment);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (source_variable != nullptr) { if (source_variable != nullptr) {
@ -2582,6 +2657,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_await(ExpressionNode *p_pr
push_error(R"(Expected signal or coroutine after "await".)"); push_error(R"(Expected signal or coroutine after "await".)");
} }
await->to_await = element; await->to_await = element;
complete_extents(await);
if (current_function) { // Might be null in a getter or setter. if (current_function) { // Might be null in a getter or setter.
current_function->is_coroutine = true; current_function->is_coroutine = true;
@ -2610,6 +2686,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_array(ExpressionNode *p_pr
} }
pop_multiline(); pop_multiline();
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after array elements.)"); consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after array elements.)");
complete_extents(array);
return array; return array;
} }
@ -2701,6 +2778,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode
} }
pop_multiline(); pop_multiline();
consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" after dictionary elements.)"); consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" after dictionary elements.)");
complete_extents(dictionary);
return dictionary; return dictionary;
} }
@ -2718,6 +2796,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_grouping(ExpressionNode *p
GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *p_previous_operand, bool p_can_assign) { GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *p_previous_operand, bool p_can_assign) {
SubscriptNode *attribute = alloc_node<SubscriptNode>(); SubscriptNode *attribute = alloc_node<SubscriptNode>();
reset_extents(attribute, p_previous_operand);
update_extents(attribute);
if (for_completion) { if (for_completion) {
bool is_builtin = false; bool is_builtin = false;
@ -2737,17 +2817,21 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *
attribute->base = p_previous_operand; attribute->base = p_previous_operand;
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier after "." for attribute access.)")) { if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier after "." for attribute access.)")) {
complete_extents(attribute);
return attribute; return attribute;
} }
attribute->is_attribute = true; attribute->is_attribute = true;
attribute->attribute = parse_identifier(); attribute->attribute = parse_identifier();
complete_extents(attribute);
return attribute; return attribute;
} }
GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign) { GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign) {
SubscriptNode *subscript = alloc_node<SubscriptNode>(); SubscriptNode *subscript = alloc_node<SubscriptNode>();
reset_extents(subscript, p_previous_operand);
update_extents(subscript);
make_completion_context(COMPLETION_SUBSCRIPT, subscript); make_completion_context(COMPLETION_SUBSCRIPT, subscript);
@ -2760,15 +2844,19 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *
pop_multiline(); pop_multiline();
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" after subscription index.)"); consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" after subscription index.)");
complete_extents(subscript);
return subscript; return subscript;
} }
GDScriptParser::ExpressionNode *GDScriptParser::parse_cast(ExpressionNode *p_previous_operand, bool p_can_assign) { GDScriptParser::ExpressionNode *GDScriptParser::parse_cast(ExpressionNode *p_previous_operand, bool p_can_assign) {
CastNode *cast = alloc_node<CastNode>(); CastNode *cast = alloc_node<CastNode>();
reset_extents(cast, p_previous_operand);
update_extents(cast);
cast->operand = p_previous_operand; cast->operand = p_previous_operand;
cast->cast_type = parse_type(); cast->cast_type = parse_type();
complete_extents(cast);
if (cast->cast_type == nullptr) { if (cast->cast_type == nullptr) {
push_error(R"(Expected type specifier after "as".)"); push_error(R"(Expected type specifier after "as".)");
@ -2780,6 +2868,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_cast(ExpressionNode *p_pre
GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_previous_operand, bool p_can_assign) { GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_previous_operand, bool p_can_assign) {
CallNode *call = alloc_node<CallNode>(); CallNode *call = alloc_node<CallNode>();
reset_extents(call, p_previous_operand);
if (previous.type == GDScriptTokenizer::Token::SUPER) { if (previous.type == GDScriptTokenizer::Token::SUPER) {
// Super call. // Super call.
@ -2790,6 +2879,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
if (current_function == nullptr) { if (current_function == nullptr) {
push_error(R"(Cannot use implicit "super" call outside of a function.)"); push_error(R"(Cannot use implicit "super" call outside of a function.)");
pop_multiline(); pop_multiline();
complete_extents(call);
return nullptr; return nullptr;
} }
if (current_function->identifier) { if (current_function->identifier) {
@ -2802,6 +2892,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
make_completion_context(COMPLETION_SUPER_METHOD, call, true); make_completion_context(COMPLETION_SUPER_METHOD, call, true);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after ".".)")) { if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after ".".)")) {
pop_multiline(); pop_multiline();
complete_extents(call);
return nullptr; return nullptr;
} }
IdentifierNode *identifier = parse_identifier(); IdentifierNode *identifier = parse_identifier();
@ -2858,6 +2949,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
pop_multiline(); pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after call arguments.)*"); consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after call arguments.)*");
complete_extents(call);
return call; return call;
} }
@ -2901,6 +2993,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
if (previous.type == GDScriptTokenizer::Token::PERCENT) { if (previous.type == GDScriptTokenizer::Token::PERCENT) {
if (path_state != PATH_STATE_START && path_state != PATH_STATE_SLASH) { if (path_state != PATH_STATE_START && path_state != PATH_STATE_SLASH) {
push_error(R"("%" is only valid in the beginning of a node name (either after "$" or after "/"))"); push_error(R"("%" is only valid in the beginning of a node name (either after "$" or after "/"))");
complete_extents(get_node);
return nullptr; return nullptr;
} }
get_node->full_path += "%"; get_node->full_path += "%";
@ -2909,6 +3002,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
} else if (previous.type == GDScriptTokenizer::Token::SLASH) { } else if (previous.type == GDScriptTokenizer::Token::SLASH) {
if (path_state != PATH_STATE_START && path_state != PATH_STATE_NODE_NAME) { if (path_state != PATH_STATE_START && path_state != PATH_STATE_NODE_NAME) {
push_error(R"("/" is only valid at the beginning of the path or after a node name.)"); push_error(R"("/" is only valid at the beginning of the path or after a node name.)");
complete_extents(get_node);
return nullptr; return nullptr;
} }
@ -2936,6 +3030,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
break; break;
} }
push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous_token)); push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous_token));
complete_extents(get_node);
return nullptr; return nullptr;
} }
@ -2949,10 +3044,12 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
path_state = PATH_STATE_NODE_NAME; path_state = PATH_STATE_NODE_NAME;
} else if (!check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) { } else if (!check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) {
push_error(vformat(R"(Unexpected "%s" in node path.)", current.get_name())); push_error(vformat(R"(Unexpected "%s" in node path.)", current.get_name()));
complete_extents(get_node);
return nullptr; return nullptr;
} }
} while (match(GDScriptTokenizer::Token::SLASH) || match(GDScriptTokenizer::Token::PERCENT)); } while (match(GDScriptTokenizer::Token::SLASH) || match(GDScriptTokenizer::Token::PERCENT));
complete_extents(get_node);
return get_node; return get_node;
} }
@ -2976,6 +3073,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_
pop_multiline(); pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after preload path.)*"); consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after preload path.)*");
complete_extents(preload);
return preload; return preload;
} }
@ -3025,6 +3123,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p
in_lambda = true; in_lambda = true;
function->body = parse_suite("lambda declaration", body, true); function->body = parse_suite("lambda declaration", body, true);
complete_extents(function);
complete_extents(lambda);
pop_multiline(); pop_multiline();
@ -3069,13 +3169,15 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
if (!match(GDScriptTokenizer::Token::IDENTIFIER)) { if (!match(GDScriptTokenizer::Token::IDENTIFIER)) {
if (match(GDScriptTokenizer::Token::VOID)) { if (match(GDScriptTokenizer::Token::VOID)) {
if (p_allow_void) { if (p_allow_void) {
TypeNode *void_type = alloc_node<TypeNode>(); complete_extents(type);
TypeNode *void_type = type;
return void_type; return void_type;
} else { } else {
push_error(R"("void" is only allowed for a function return type.)"); push_error(R"("void" is only allowed for a function return type.)");
} }
} }
// Leave error message to the caller who knows the context. // Leave error message to the caller who knows the context.
complete_extents(type);
return nullptr; return nullptr;
} }
@ -3088,11 +3190,15 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
type->container_type = parse_type(false); // Don't allow void for array element type. type->container_type = parse_type(false); // Don't allow void for array element type.
if (type->container_type == nullptr) { if (type->container_type == nullptr) {
push_error(R"(Expected type for collection after "[".)"); push_error(R"(Expected type for collection after "[".)");
complete_extents(type);
type = nullptr; type = nullptr;
} else if (type->container_type->container_type != nullptr) { } else if (type->container_type->container_type != nullptr) {
push_error("Nested typed collections are not supported."); push_error("Nested typed collections are not supported.");
} }
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after collection type.)"); consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after collection type.)");
if (type != nullptr) {
complete_extents(type);
}
return type; return type;
} }
@ -3105,6 +3211,7 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
} }
} }
complete_extents(type);
return type; return type;
} }
@ -3922,6 +4029,46 @@ GDScriptParser::DataType GDScriptParser::DataType::get_typed_container_type() co
return type; return type;
} }
void GDScriptParser::complete_extents(Node *p_node) {
while (!nodes_in_progress.is_empty() && nodes_in_progress.back()->get() != p_node) {
ERR_PRINT("Parser bug: Mismatch in extents tracking stack.");
nodes_in_progress.pop_back();
}
if (nodes_in_progress.is_empty()) {
ERR_PRINT("Parser bug: Extents tracking stack is empty.");
} else {
nodes_in_progress.pop_back();
}
}
void GDScriptParser::update_extents(Node *p_node) {
p_node->end_line = previous.end_line;
p_node->end_column = previous.end_column;
p_node->leftmost_column = MIN(p_node->leftmost_column, previous.leftmost_column);
p_node->rightmost_column = MAX(p_node->rightmost_column, previous.rightmost_column);
}
void GDScriptParser::reset_extents(Node *p_node, GDScriptTokenizer::Token p_token) {
p_node->start_line = p_token.start_line;
p_node->end_line = p_token.end_line;
p_node->start_column = p_token.start_column;
p_node->end_column = p_token.end_column;
p_node->leftmost_column = p_token.leftmost_column;
p_node->rightmost_column = p_token.rightmost_column;
}
void GDScriptParser::reset_extents(Node *p_node, Node *p_from) {
if (p_from == nullptr) {
return;
}
p_node->start_line = p_from->start_line;
p_node->end_line = p_from->end_line;
p_node->start_column = p_from->start_column;
p_node->end_column = p_from->end_column;
p_node->leftmost_column = p_from->leftmost_column;
p_node->rightmost_column = p_from->rightmost_column;
}
/*---------- PRETTY PRINT FOR DEBUG ----------*/ /*---------- PRETTY PRINT FOR DEBUG ----------*/
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED

View File

@ -1301,6 +1301,12 @@ private:
}; };
static ParseRule *get_rule(GDScriptTokenizer::Token::Type p_token_type); static ParseRule *get_rule(GDScriptTokenizer::Token::Type p_token_type);
List<Node *> nodes_in_progress;
void complete_extents(Node *p_node);
void update_extents(Node *p_node);
void reset_extents(Node *p_node, GDScriptTokenizer::Token p_token);
void reset_extents(Node *p_node, Node *p_from);
template <class T> template <class T>
T *alloc_node() { T *alloc_node() {
T *node = memnew(T); T *node = memnew(T);
@ -1308,13 +1314,8 @@ private:
node->next = list; node->next = list;
list = node; list = node;
// TODO: Properly set positions for all nodes. reset_extents(node, previous);
node->start_line = previous.start_line; nodes_in_progress.push_back(node);
node->end_line = previous.end_line;
node->start_column = previous.start_column;
node->end_column = previous.end_column;
node->leftmost_column = previous.leftmost_column;
node->rightmost_column = previous.rightmost_column;
return node; return node;
} }