Add better local variable detection in GDScript parser

Also store Variant operator to avoid needing to do it repeatedly in
later compiling stages.
This commit is contained in:
George Marques 2020-06-10 18:18:10 -03:00
parent 7adb0d77cc
commit 17cd6347ba
No known key found for this signature in database
GPG key ID: 046BD46A3201E43D
2 changed files with 362 additions and 51 deletions

View file

@ -33,6 +33,7 @@
#include "core/io/resource_loader.h"
#include "core/math/math_defs.h"
#include "core/os/file_access.h"
#include "gdscript.h"
#ifdef DEBUG_ENABLED
#include "core/os/os.h"
@ -917,6 +918,10 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
push_multiline(true);
consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after function name.)");
SuiteNode *body = alloc_node<SuiteNode>();
SuiteNode *previous_suite = current_suite;
current_suite = body;
if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE) && !is_at_end()) {
bool default_used = false;
do {
@ -941,6 +946,7 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
} else {
function->parameters_indices[parameter->identifier->name] = function->parameters.size();
function->parameters.push_back(parameter);
body->add_local(parameter);
}
} while (match(GDScriptTokenizer::Token::COMMA));
}
@ -955,7 +961,8 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
// TODO: Improve token consumption so it synchronizes to a statement boundary. This way we can get into the function body with unrecognized tokens.
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after function declaration.)");
function->body = parse_suite("function declaration");
current_suite = previous_suite;
function->body = parse_suite("function declaration", body);
current_function = previous_function;
return function;
@ -1030,8 +1037,8 @@ bool GDScriptParser::register_annotation(const MethodInfo &p_info, uint32_t p_ta
return true;
}
GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context) {
SuiteNode *suite = alloc_node<SuiteNode>();
GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, SuiteNode *p_suite) {
SuiteNode *suite = p_suite != nullptr ? p_suite : alloc_node<SuiteNode>();
suite->parent_block = current_suite;
current_suite = suite;
@ -1063,13 +1070,7 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context)
VariableNode *variable = static_cast<VariableNode *>(statement);
const SuiteNode::Local &local = current_suite->get_local(variable->identifier->name);
if (local.type != SuiteNode::Local::UNDEFINED) {
String name;
if (local.type == SuiteNode::Local::CONSTANT) {
name = "constant";
} else {
name = "variable";
}
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", 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);
break;
@ -1256,7 +1257,10 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
can_break = true;
can_continue = true;
n_for->loop = parse_suite(R"("for" block)");
SuiteNode *suite = alloc_node<SuiteNode>();
suite->add_local(SuiteNode::Local(n_for->variable));
n_for->loop = parse_suite(R"("for" block)", suite);
// Reset break/continue state.
can_break = could_break;
@ -1352,7 +1356,18 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
// Allow continue for match.
can_continue = true;
branch->block = parse_suite("match pattern block");
SuiteNode *suite = alloc_node<SuiteNode>();
if (branch->patterns.size() > 0) {
List<StringName> binds;
branch->patterns[0]->binds.get_key_list(&binds);
for (List<StringName>::Element *E = binds.front(); E != nullptr; E = E->next()) {
SuiteNode::Local local(branch->patterns[0]->binds[E->get()]);
suite->add_local(local);
}
}
branch->block = parse_suite("match pattern block", suite);
// Restore continue state.
can_continue = could_continue;
@ -1360,7 +1375,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
return branch;
}
GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern() {
GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_root_pattern) {
PatternNode *pattern = alloc_node<PatternNode>();
switch (current.type) {
@ -1373,7 +1388,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern() {
return nullptr;
}
break;
case GDScriptTokenizer::Token::VAR:
case GDScriptTokenizer::Token::VAR: {
// Bind.
advance();
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected bind name after "var".)")) {
@ -1381,7 +1396,24 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern() {
}
pattern->pattern_type = PatternNode::PT_BIND;
pattern->bind = parse_identifier();
break;
PatternNode *root_pattern = p_root_pattern == nullptr ? pattern : p_root_pattern;
if (p_root_pattern != nullptr) {
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));
return nullptr;
}
}
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));
return nullptr;
}
root_pattern->binds[pattern->bind->name] = pattern->bind;
} break;
case GDScriptTokenizer::Token::UNDERSCORE:
// Wildcard.
advance();
@ -1399,7 +1431,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern() {
if (!check(GDScriptTokenizer::Token::BRACKET_CLOSE)) {
do {
PatternNode *sub_pattern = parse_match_pattern();
PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
if (sub_pattern == nullptr) {
continue;
}
@ -1438,7 +1470,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern() {
}
if (consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after dictionary pattern key)")) {
// Value pattern.
PatternNode *sub_pattern = parse_match_pattern();
PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
if (sub_pattern == nullptr) {
continue;
}
@ -1472,6 +1504,14 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern() {
return pattern;
}
bool GDScriptParser::PatternNode::has_bind(const StringName &p_name) {
return binds.has(p_name);
}
GDScriptParser::IdentifierNode *GDScriptParser::PatternNode::get_bind(const StringName &p_name) {
return binds[p_name];
}
GDScriptParser::WhileNode *GDScriptParser::parse_while() {
WhileNode *n_while = alloc_node<WhileNode>();
@ -1558,6 +1598,35 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode
}
IdentifierNode *identifier = alloc_node<IdentifierNode>();
identifier->name = previous.get_identifier();
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
switch (declaration.type) {
case SuiteNode::Local::CONSTANT:
identifier->source = IdentifierNode::LOCAL_CONSTANT;
identifier->constant_source = declaration.constant;
break;
case SuiteNode::Local::VARIABLE:
identifier->source = IdentifierNode::LOCAL_VARIABLE;
identifier->variable_source = declaration.variable;
break;
case SuiteNode::Local::PARAMETER:
identifier->source = IdentifierNode::FUNCTION_PARAMETER;
identifier->parameter_source = declaration.parameter;
break;
case SuiteNode::Local::FOR_VARIABLE:
identifier->source = IdentifierNode::LOCAL_ITERATOR;
identifier->bind_source = declaration.bind;
break;
case SuiteNode::Local::PATTERN_BIND:
identifier->source = IdentifierNode::LOCAL_BIND;
identifier->bind_source = declaration.bind;
break;
case SuiteNode::Local::UNDEFINED:
ERR_FAIL_V_MSG(nullptr, "Undefined local found.");
}
}
return identifier;
}
@ -1614,19 +1683,23 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_unary_operator(ExpressionN
switch (op_type) {
case GDScriptTokenizer::Token::MINUS:
operation->operation = UnaryOpNode::OP_NEGATIVE;
operation->variant_op = Variant::OP_NEGATE;
operation->operand = parse_precedence(PREC_SIGN, false);
break;
case GDScriptTokenizer::Token::PLUS:
operation->operation = UnaryOpNode::OP_POSITIVE;
operation->variant_op = Variant::OP_POSITIVE;
operation->operand = parse_precedence(PREC_SIGN, false);
break;
case GDScriptTokenizer::Token::TILDE:
operation->operation = UnaryOpNode::OP_COMPLEMENT;
operation->variant_op = Variant::OP_BIT_NEGATE;
operation->operand = parse_precedence(PREC_BIT_NOT, false);
break;
case GDScriptTokenizer::Token::NOT:
case GDScriptTokenizer::Token::BANG:
operation->operation = UnaryOpNode::OP_LOGIC_NOT;
operation->variant_op = Variant::OP_NOT;
operation->operand = parse_precedence(PREC_LOGIC_NOT, false);
break;
default:
@ -1648,68 +1721,89 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression
push_error(vformat(R"(Expected expression after "%s" operator.")", op.get_name()));
}
// TODO: Store the Variant operator here too (in the node).
// TODO: Also for unary, ternary, and assignment.
switch (op.type) {
case GDScriptTokenizer::Token::PLUS:
operation->operation = BinaryOpNode::OP_ADDITION;
operation->variant_op = Variant::OP_ADD;
break;
case GDScriptTokenizer::Token::MINUS:
operation->operation = BinaryOpNode::OP_SUBTRACTION;
operation->variant_op = Variant::OP_SUBTRACT;
break;
case GDScriptTokenizer::Token::STAR:
operation->operation = BinaryOpNode::OP_MULTIPLICATION;
operation->variant_op = Variant::OP_MULTIPLY;
break;
case GDScriptTokenizer::Token::SLASH:
operation->operation = BinaryOpNode::OP_DIVISION;
operation->variant_op = Variant::OP_DIVIDE;
break;
case GDScriptTokenizer::Token::PERCENT:
operation->operation = BinaryOpNode::OP_MODULO;
operation->variant_op = Variant::OP_MODULE;
break;
case GDScriptTokenizer::Token::LESS_LESS:
operation->operation = BinaryOpNode::OP_BIT_LEFT_SHIFT;
operation->variant_op = Variant::OP_SHIFT_LEFT;
break;
case GDScriptTokenizer::Token::GREATER_GREATER:
operation->operation = BinaryOpNode::OP_BIT_RIGHT_SHIFT;
operation->variant_op = Variant::OP_SHIFT_RIGHT;
break;
case GDScriptTokenizer::Token::AMPERSAND:
operation->operation = BinaryOpNode::OP_BIT_AND;
operation->variant_op = Variant::OP_BIT_AND;
break;
case GDScriptTokenizer::Token::PIPE:
operation->operation = BinaryOpNode::OP_BIT_AND;
operation->operation = BinaryOpNode::OP_BIT_OR;
operation->variant_op = Variant::OP_BIT_OR;
break;
case GDScriptTokenizer::Token::CARET:
operation->operation = BinaryOpNode::OP_BIT_XOR;
operation->variant_op = Variant::OP_BIT_XOR;
break;
case GDScriptTokenizer::Token::AND:
case GDScriptTokenizer::Token::AMPERSAND_AMPERSAND:
operation->operation = BinaryOpNode::OP_LOGIC_AND;
operation->variant_op = Variant::OP_AND;
break;
case GDScriptTokenizer::Token::OR:
case GDScriptTokenizer::Token::PIPE_PIPE:
operation->operation = BinaryOpNode::OP_LOGIC_OR;
operation->variant_op = Variant::OP_OR;
break;
case GDScriptTokenizer::Token::IS:
operation->operation = BinaryOpNode::OP_TYPE_TEST;
break;
case GDScriptTokenizer::Token::IN:
operation->operation = BinaryOpNode::OP_CONTENT_TEST;
operation->variant_op = Variant::OP_IN;
break;
case GDScriptTokenizer::Token::EQUAL_EQUAL:
operation->operation = BinaryOpNode::OP_COMP_EQUAL;
operation->variant_op = Variant::OP_EQUAL;
break;
case GDScriptTokenizer::Token::BANG_EQUAL:
operation->operation = BinaryOpNode::OP_COMP_NOT_EQUAL;
operation->variant_op = Variant::OP_NOT_EQUAL;
break;
case GDScriptTokenizer::Token::LESS:
operation->operation = BinaryOpNode::OP_COMP_LESS;
operation->variant_op = Variant::OP_LESS;
break;
case GDScriptTokenizer::Token::LESS_EQUAL:
operation->operation = BinaryOpNode::OP_COMP_LESS_EQUAL;
operation->variant_op = Variant::OP_LESS_EQUAL;
break;
case GDScriptTokenizer::Token::GREATER:
operation->operation = BinaryOpNode::OP_COMP_GREATER;
operation->variant_op = Variant::OP_GREATER;
break;
case GDScriptTokenizer::Token::GREATER_EQUAL:
operation->operation = BinaryOpNode::OP_COMP_GREATER_EQUAL;
operation->variant_op = Variant::OP_GREATER_EQUAL;
break;
default:
return nullptr; // Unreachable.
@ -1964,17 +2058,34 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
pop_multiline();
return nullptr;
}
call->function_name = current_function->identifier->name;
} else {
consume(GDScriptTokenizer::Token::PERIOD, R"(Expected "." or "(" after "super".)");
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after ".".)")) {
pop_multiline();
return nullptr;
}
call->callee = parse_identifier();
IdentifierNode *identifier = parse_identifier();
call->callee = identifier;
call->function_name = identifier->name;
consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after function name.)");
}
} else {
call->callee = p_previous_operand;
if (call->callee->type == Node::IDENTIFIER) {
call->function_name = static_cast<IdentifierNode *>(call->callee)->name;
} else if (call->callee->type == Node::SUBSCRIPT) {
SubscriptNode *attribute = static_cast<SubscriptNode *>(call->callee);
if (attribute->is_attribute) {
call->function_name = attribute->attribute->name;
} else {
// TODO: The analyzer can see if this is actually a Callable and give better error message.
push_error(R"*(Cannot call on an expression. Use ".call()" if it's a Callable.)*");
}
} else {
push_error(R"*(Cannot call on an expression. Use ".call()" if it's a Callable.)*");
}
}
if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
@ -2095,18 +2206,17 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
// Leave error message to the caller who knows the context.
return nullptr;
}
TypeNode *type = alloc_node<TypeNode>();
IdentifierNode *type_base = parse_identifier();
// FIXME: This is likely not working with multiple chained attributes (A.B.C.D...).
// FIXME: I probably should use a list for this, not an attribute (since those aren't chained anymore).
if (match(GDScriptTokenizer::Token::PERIOD)) {
type->type_specifier = static_cast<SubscriptNode *>(parse_attribute(type_base, false));
if (type->type_specifier->index == nullptr) {
return nullptr;
TypeNode *type = alloc_node<TypeNode>();
IdentifierNode *type_element = parse_identifier();
type->type_chain.push_back(type_element);
while (match(GDScriptTokenizer::Token::PERIOD)) {
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected inner type name after ".".)")) {
type_element = parse_identifier();
type->type_chain.push_back(type_element);
}
} else {
type->type_base = type_base;
}
return type;
@ -2117,7 +2227,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
// clang-format destroys the alignment here, so turn off for the table.
/* clang-format off */
static ParseRule rules[] = {
// PREFIX INFIX PRECEDENCE (for binary)
// PREFIX INFIX PRECEDENCE (for infix)
{ nullptr, nullptr, PREC_NONE }, // EMPTY,
// Basic
{ nullptr, nullptr, PREC_NONE }, // ANNOTATION,
@ -2432,6 +2542,92 @@ bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Nod
return true;
}
GDScriptParser::DataType GDScriptParser::SuiteNode::Local::get_datatype() const {
switch (type) {
case CONSTANT:
return constant->get_datatype();
case VARIABLE:
return variable->get_datatype();
case PARAMETER:
return parameter->get_datatype();
case FOR_VARIABLE:
case PATTERN_BIND:
return bind->get_datatype();
case UNDEFINED:
return DataType();
}
return DataType();
}
String GDScriptParser::SuiteNode::Local::get_name() const {
String name;
switch (type) {
case SuiteNode::Local::PARAMETER:
name = "parameter";
break;
case SuiteNode::Local::CONSTANT:
name = "constant";
break;
case SuiteNode::Local::VARIABLE:
name = "variable";
break;
case SuiteNode::Local::FOR_VARIABLE:
name = "for loop iterator";
break;
case SuiteNode::Local::PATTERN_BIND:
name = "pattern_bind";
break;
case SuiteNode::Local::UNDEFINED:
name = "<undefined>";
break;
}
return name;
}
String GDScriptParser::DataType::to_string() const {
switch (kind) {
case VARIANT:
return "Variant";
case BUILTIN:
if (builtin_type == Variant::NIL) {
return "null";
}
return Variant::get_type_name(builtin_type);
case NATIVE:
if (is_meta_type) {
return GDScriptNativeClass::get_class_static();
}
return native_type.operator String();
case CLASS:
if (is_meta_type) {
return GDScript::get_class_static();
}
if (class_type->identifier != nullptr) {
return class_type->identifier->name.operator String();
}
// TODO: GDScript FQCN
return "<unnamed GDScript class>";
case SCRIPT: {
if (is_meta_type) {
return script_type->get_class_name().operator String();
}
String name = script_type->get_name();
if (!name.empty()) {
return name;
}
name = script_type->get_path();
if (!name.empty()) {
return name;
}
return native_type.operator String();
}
case UNRESOLVED:
return "<unresolved type>";
}
ERR_FAIL_V_MSG("<unresolved type", "Kind set outside the enum range.");
}
/*---------- PRETTY PRINT FOR DEBUG ----------*/
#ifdef DEBUG_ENABLED
@ -3132,12 +3328,15 @@ void GDScriptParser::TreePrinter::print_ternary_op(TernaryOpNode *p_ternary_op)
}
void GDScriptParser::TreePrinter::print_type(TypeNode *p_type) {
if (p_type->type_specifier != nullptr) {
print_subscript(p_type->type_specifier);
} else if (p_type->type_base != nullptr) {
print_identifier(p_type->type_base);
} else {
if (p_type->type_chain.empty()) {
push_text("Void");
} else {
for (int i = 0; i < p_type->type_chain.size(); i++) {
if (i > 0) {
push_text(".");
}
print_identifier(p_type->type_chain[i]);
}
}
}

View file

@ -99,7 +99,9 @@ public:
NATIVE,
SCRIPT,
CLASS, // GDScript.
VARIANT, // Can be any type.
UNRESOLVED,
// TODO: Enum, Signal, Callable
};
Kind kind = UNRESOLVED;
@ -113,14 +115,18 @@ public:
bool is_constant = false;
bool is_meta_type = false;
bool infer_type = false;
bool is_coroutine = false; // For function calls.
Variant::Type builtin_type = Variant::NIL;
StringName native_type;
Ref<Script> script_type;
ClassNode *gdscript_type = nullptr;
ClassNode *class_type = nullptr;
_FORCE_INLINE_ bool is_set() const { return type_source != UNDETECTED; }
MethodInfo method_info; // For callable/signals.
_FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; }
_FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; }
_FORCE_INLINE_ bool is_variant() const { return kind == VARIANT; }
String to_string() const;
bool operator==(const DataType &p_other) const {
@ -137,6 +143,8 @@ public:
}
switch (kind) {
case VARIANT:
return true; // All variants are the same.
case BUILTIN:
return builtin_type == p_other.builtin_type;
case NATIVE:
@ -144,13 +152,17 @@ public:
case SCRIPT:
return script_type == p_other.script_type;
case CLASS:
return gdscript_type == p_other.gdscript_type;
return class_type == p_other.class_type;
case UNRESOLVED:
break;
}
return false;
}
bool operator!=(const DataType &p_other) const {
return !(this->operator==(p_other));
}
};
struct ParserError {
@ -215,8 +227,10 @@ public:
Node *next = nullptr;
List<AnnotationNode *> annotations;
virtual DataType get_datatype() const { return DataType(); }
virtual void set_datatype(const DataType &p_datatype) {}
DataType datatype;
virtual DataType get_datatype() const { return datatype; }
virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
virtual bool is_expression() const { return false; }
@ -225,6 +239,10 @@ public:
struct ExpressionNode : public Node {
// Base type for all expression kinds.
bool reduced = false;
bool is_constant = false;
Variant reduced_value;
virtual bool is_expression() const { return true; }
virtual ~ExpressionNode() {}
@ -281,6 +299,7 @@ public:
};
Operation operation = OP_NONE;
Variant::Operator variant_op = Variant::OP_MAX;
ExpressionNode *assignee = nullptr;
ExpressionNode *assigned_value = nullptr;
@ -322,6 +341,7 @@ public:
};
OpType operation;
Variant::Operator variant_op = Variant::OP_MAX;
ExpressionNode *left_operand = nullptr;
ExpressionNode *right_operand = nullptr;
@ -345,6 +365,7 @@ public:
struct CallNode : public ExpressionNode {
ExpressionNode *callee = nullptr;
Vector<ExpressionNode *> arguments;
StringName function_name; // TODO: Set this.
bool is_super = false;
CallNode() {
@ -422,6 +443,34 @@ public:
return "";
}
DataType get_datatype() const {
switch (type) {
case CLASS:
return m_class->get_datatype();
case CONSTANT:
return constant->get_datatype();
case FUNCTION:
return function->get_datatype();
case VARIABLE:
return variable->get_datatype();
case ENUM_VALUE: {
// Always integer.
DataType type;
type.type_source = DataType::ANNOTATED_EXPLICIT;
type.kind = DataType::BUILTIN;
type.builtin_type = Variant::INT;
return type;
}
case ENUM:
case SIGNAL:
// TODO: Use special datatype kinds for these.
return DataType();
case UNDEFINED:
return DataType();
}
ERR_FAIL_V_MSG(DataType(), "Reaching unhandled type.");
}
Member() {}
Member(ClassNode *p_class) {
@ -464,10 +513,17 @@ public:
String extends_path;
Vector<StringName> extends; // List for indexing: extends A.B.C
DataType base_type;
String fqcn; // Fully-qualified class name. Identifies uniquely any class in the project.
bool resolved_interface = false;
bool resolved_body = false;
Member get_member(const StringName &p_name) const {
return members[members_indices[p_name]];
}
bool has_member(const StringName &p_name) const {
return members_indices.has(p_name);
}
template <class T>
void add_member(T *p_member_node) {
members_indices[p_member_node->identifier->name] = members.size();
@ -477,12 +533,6 @@ public:
members_indices[p_enum_value.identifier->name] = members.size();
members.push_back(Member(p_enum_value));
}
virtual DataType get_datatype() const {
return base_type;
}
virtual void set_datatype(const DataType &p_datatype) {
base_type = p_datatype;
}
ClassNode() {
type = CLASS;
@ -543,6 +593,9 @@ public:
bool is_static = false;
MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
bool resolved_signature = false;
bool resolved_body = false;
FunctionNode() {
type = FUNCTION;
}
@ -560,6 +613,24 @@ public:
struct IdentifierNode : public ExpressionNode {
StringName name;
enum Source {
UNDEFINED_SOURCE,
FUNCTION_PARAMETER,
LOCAL_CONSTANT,
LOCAL_VARIABLE,
LOCAL_ITERATOR, // `for` loop iterator.
LOCAL_BIND, // Pattern bind.
// TODO: Add higher sources to help compiling?
};
Source source = UNDEFINED_SOURCE;
union {
ParameterNode *parameter_source = nullptr;
ConstantNode *constant_source;
VariableNode *variable_source;
IdentifierNode *bind_source;
};
IdentifierNode() {
type = IDENTIFIER;
}
@ -644,6 +715,11 @@ public:
};
Vector<Pair> dictionary;
HashMap<StringName, IdentifierNode *> binds;
bool has_bind(const StringName &p_name);
IdentifierNode *get_bind(const StringName &p_name);
PatternNode() {
type = PATTERN;
}
@ -706,27 +782,57 @@ public:
UNDEFINED,
CONSTANT,
VARIABLE,
PARAMETER,
FOR_VARIABLE,
PATTERN_BIND,
};
Type type = UNDEFINED;
union {
ConstantNode *constant = nullptr;
VariableNode *variable;
ParameterNode *parameter;
IdentifierNode *bind;
};
StringName name;
int start_line = 0, end_line = 0;
int start_column = 0, end_column = 0;
int leftmost_column = 0, rightmost_column = 0;
DataType get_datatype() const;
String get_name() const;
Local() {}
Local(ConstantNode *p_constant) {
type = CONSTANT;
constant = p_constant;
name = p_constant->identifier->name;
}
Local(VariableNode *p_variable) {
type = VARIABLE;
variable = p_variable;
name = p_variable->identifier->name;
}
Local(ParameterNode *p_parameter) {
type = PARAMETER;
parameter = p_parameter;
name = p_parameter->identifier->name;
}
Local(IdentifierNode *p_identifier) {
type = FOR_VARIABLE;
bind = p_identifier;
name = p_identifier->name;
}
};
Local empty;
Vector<Local> locals;
HashMap<StringName, int> locals_indices;
FunctionNode *parent_function = nullptr;
ForNode *parent_for = nullptr;
IfNode *parent_if = nullptr;
PatternNode *parent_pattern = nullptr;
bool has_local(const StringName &p_name) const;
const Local &get_local(const StringName &p_name) const;
template <class T>
@ -734,6 +840,10 @@ public:
locals_indices[p_local->identifier->name] = locals.size();
locals.push_back(Local(p_local));
}
void add_local(const Local &p_local) {
locals_indices[p_local.name] = locals.size();
locals.push_back(p_local);
}
SuiteNode() {
type = SUITE;
@ -752,8 +862,7 @@ public:
};
struct TypeNode : public Node {
IdentifierNode *type_base = nullptr;
SubscriptNode *type_specifier = nullptr;
Vector<IdentifierNode *> type_chain;
TypeNode() {
type = TYPE;
@ -769,6 +878,7 @@ public:
};
OpType operation;
Variant::Operator variant_op = Variant::OP_MAX;
ExpressionNode *operand = nullptr;
UnaryOpNode() {
@ -861,6 +971,8 @@ private:
HashMap<StringName, AnnotationInfo> valid_annotations;
List<AnnotationNode *> annotation_stack;
Set<int> unsafe_lines;
typedef ExpressionNode *(GDScriptParser::*ParseFunction)(ExpressionNode *p_previous_operand, bool p_can_assign);
// Higher value means higher precedence (i.e. is evaluated first).
enum Precedence {
@ -924,7 +1036,7 @@ private:
EnumNode *parse_enum();
ParameterNode *parse_parameter();
FunctionNode *parse_function();
SuiteNode *parse_suite(const String &p_context);
SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr);
// Annotations
AnnotationNode *parse_annotation(uint32_t p_valid_targets);
bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, int p_optional_arguments = 0, bool p_is_vararg = false);
@ -953,7 +1065,7 @@ private:
IfNode *parse_if(const String &p_token = "if");
MatchNode *parse_match();
MatchBranchNode *parse_match_branch();
PatternNode *parse_match_pattern();
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);