diff options
author | George Marques <george@gmarqu.es> | 2020-06-10 18:18:10 -0300 |
---|---|---|
committer | George Marques <george@gmarqu.es> | 2020-07-20 11:38:39 -0300 |
commit | 17cd6347ba96d4e64cba475854e842e0a490a465 (patch) | |
tree | a2c9120433ae80fa09425089b8bdd242e1e54846 | |
parent | 7adb0d77cc5dbe0beaa05c9aac0f5c16f2b79775 (diff) |
Add better local variable detection in GDScript parser
Also store Variant operator to avoid needing to do it repeatedly in
later compiling stages.
-rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 267 | ||||
-rw-r--r-- | modules/gdscript/gdscript_parser.h | 144 |
2 files changed, 361 insertions, 50 deletions
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 8d07099fc3..fe056c26ad 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -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(); + IdentifierNode *type_element = 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; + 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]); + } } } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 0d775915cf..ec5e78bd4f 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -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); |