diff options
author | George Marques <george@gmarqu.es> | 2018-05-29 23:16:52 -0300 |
---|---|---|
committer | George Marques <george@gmarqu.es> | 2018-07-20 21:55:16 -0300 |
commit | b7a00aead039a988f9e224ef5ad19688a17c971c (patch) | |
tree | 7adf2665a56e241cf1fdb84693145f5102f4ed0d | |
parent | 8aab9a06d4db1106dc733022f951db979e39f97b (diff) |
Move inheritance resolution to the parser
-rw-r--r-- | modules/gdscript/gdscript.cpp | 36 | ||||
-rw-r--r-- | modules/gdscript/gdscript_compiler.cpp | 340 | ||||
-rw-r--r-- | modules/gdscript/gdscript_compiler.h | 8 | ||||
-rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 241 | ||||
-rw-r--r-- | modules/gdscript/gdscript_parser.h | 4 |
5 files changed, 395 insertions, 234 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index f221aac5fe..ae8dcfc370 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1826,8 +1826,40 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) { const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree()); - if (r_base_type && c->extends_used && c->extends_class.size() == 1) { - *r_base_type = c->extends_class[0]; //todo, should work much better + if (r_base_type) { + GDScriptParser::DataType base_type; + if (c->base_type.has_type) { + base_type = c->base_type; + while (base_type.has_type && base_type.kind != GDScriptParser::DataType::NATIVE) { + switch (base_type.kind) { + case GDScriptParser::DataType::CLASS: { + base_type = base_type.class_type->base_type; + } break; + case GDScriptParser::DataType::GDSCRIPT: { + Ref<GDScript> gds = base_type.script_type; + if (gds.is_valid()) { + base_type.kind = GDScriptParser::DataType::NATIVE; + base_type.native_type = gds->get_instance_base_type(); + } else { + base_type = GDScriptParser::DataType(); + } + } break; + default: { + base_type = GDScriptParser::DataType(); + } break; + } + } + } + if (base_type.has_type) { + *r_base_type = base_type.native_type; + } else { + // Fallback + if (c->extends_used && c->extends_class.size() == 1) { + *r_base_type = c->extends_class[0]; + } else if (!c->extends_used) { + *r_base_type = "Reference"; + } + } } return c->name; } diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 69a0575efe..f15b4b83b9 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -263,15 +263,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: owner = owner->_owner; } - /* - handled in constants now - if (codegen.script->subclasses.has(identifier)) { - //same with a subclass, make it a local constant. - int idx = codegen.get_constant_pos(codegen.script->subclasses[identifier]); - return idx|(GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT<<GDScriptFunction::ADDR_BITS); //make it a local constant (faster access) - - }*/ - if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) { int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier]; @@ -1657,12 +1648,23 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser return OK; } -Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { +Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { - Map<StringName, Ref<GDScript> > old_subclasses; - - if (p_keep_state) { - old_subclasses = p_script->subclasses; + if (p_class->owner && p_class->owner->owner) { + // Owner is not root + StringName owner_name = p_class->owner->name; + if (!parsed_classes.has(owner_name)) { + if (parsing_classes.has(owner_name)) { + _set_error("Cyclic class reference for '" + String(owner_name) + "'.", p_class); + return ERR_PARSE_ERROR; + } + parsing_classes.insert(owner_name); + Error err = _parse_class_level(class_map[owner_name].ptr(), class_map[owner_name]->_owner, p_class->owner, p_keep_state); + if (err) { + return err; + } + parsing_classes.erase(owner_name); + } } p_script->native = Ref<GDScriptNativeClass>(); @@ -1686,177 +1688,46 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons Ref<GDScriptNativeClass> native; - if (p_class->extends_used) { - //do inheritance - String path = p_class->extends_file; - - Ref<GDScript> script; - - if (path != "") { - //path (and optionally subclasses) - - if (path.is_rel_path()) { - - String base; - - if (p_owner) { - GDScript *current_class = p_owner; - while (current_class != NULL) { - base = current_class->get_path(); - if (base == "") - current_class = current_class->_owner; - else - break; - } - } else { - base = p_script->get_path(); - } - - if (base == "" || base.is_rel_path()) { - _set_error("Could not resolve relative path for parent class: " + path, p_class); - return ERR_FILE_NOT_FOUND; - } - path = base.get_base_dir().plus_file(path).simplify_path(); - } - script = ResourceLoader::load(path); - if (script.is_null()) { - _set_error("Could not load base class: " + path, p_class); - return ERR_FILE_NOT_FOUND; - } - if (!script->valid) { - - _set_error("Script not fully loaded (cyclic preload?): " + path, p_class); - return ERR_BUSY; - } - //print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid)); - - if (p_class->extends_class.size()) { - - for (int i = 0; i < p_class->extends_class.size(); i++) { - - String sub = p_class->extends_class[i]; - if (script->subclasses.has(sub)) { - - Ref<Script> subclass = script->subclasses[sub]; //avoid reference from disappearing - script = subclass; - } else { - - _set_error("Could not find subclass: " + sub, p_class); - return ERR_FILE_NOT_FOUND; - } - } - } - - } else { - - ERR_FAIL_COND_V(p_class->extends_class.size() == 0, ERR_BUG); - //look around for the subclasses - - String base = p_class->extends_class[0]; - GDScript *p = p_owner; - Ref<GDScript> base_class; - - while (p) { - - if (p->subclasses.has(base)) { - - base_class = p->subclasses[base]; - break; - } - - if (p->constants.has(base)) { - - base_class = p->constants[base]; - if (base_class.is_null()) { - _set_error("Constant is not a class: " + base, p_class); - return ERR_SCRIPT_FAILED; - } - break; - } - - p = p->_owner; - } - - if (base_class.is_valid()) { - - String ident = base; - - for (int i = 1; i < p_class->extends_class.size(); i++) { - - String subclass = p_class->extends_class[i]; - - ident += ("." + subclass); - - if (base_class->subclasses.has(subclass)) { - - base_class = base_class->subclasses[subclass]; - } else if (base_class->constants.has(subclass)) { - - Ref<GDScript> new_base_class = base_class->constants[subclass]; - if (new_base_class.is_null()) { - _set_error("Constant is not a class: " + ident, p_class); - return ERR_SCRIPT_FAILED; - } - base_class = new_base_class; - } else { - - _set_error("Could not find subclass: " + ident, p_class); - return ERR_FILE_NOT_FOUND; - } - } - - script = base_class; - - } else { - - if (p_class->extends_class.size() > 1) { - - _set_error("Invalid inheritance (unknown class+subclasses)", p_class); - return ERR_FILE_NOT_FOUND; - } - //if not found, try engine classes - if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) { - - _set_error("Unknown class: '" + base + "'", p_class); - return ERR_FILE_NOT_FOUND; + // Inheritance + switch (p_class->base_type.kind) { + case GDScriptParser::DataType::CLASS: { + StringName base_name = p_class->base_type.class_type->name; + // Make sure dependency is parsed first + if (!parsed_classes.has(base_name)) { + if (parsing_classes.has(base_name)) { + _set_error("Cyclic class reference for '" + String(base_name) + "'.", p_class); + return ERR_PARSE_ERROR; } - - int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base]; - native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx]; - if (!native.is_valid()) { - - _set_error("Global not a class: '" + base + "'", p_class); - - return ERR_FILE_NOT_FOUND; + parsing_classes.insert(base_name); + Error err = _parse_class_level(class_map[base_name].ptr(), class_map[base_name]->_owner, p_class->base_type.class_type, p_keep_state); + if (err) { + return err; } + parsing_classes.erase(base_name); } - } - - if (script.is_valid()) { - - p_script->base = script; + Ref<GDScript> base = class_map[base_name]; + p_script->base = base; p_script->_base = p_script->base.ptr(); - p_script->member_indices = script->member_indices; - - } else if (native.is_valid()) { - + p_script->member_indices = base->member_indices; + } break; + case GDScriptParser::DataType::GDSCRIPT: { + Ref<GDScript> base = p_class->base_type.script_type; + p_script->base = base; + p_script->_base = p_script->base.ptr(); + p_script->member_indices = base->member_indices; + } break; + case GDScriptParser::DataType::NATIVE: { + int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_class->base_type.native_type]; + native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx]; + ERR_FAIL_COND_V(native.is_null(), ERR_BUG); p_script->native = native; - } else { - - _set_error("Could not determine inheritance", p_class); - return ERR_FILE_NOT_FOUND; - } - - } else { - // without extends, implicitly extend Reference - int native_idx = GDScriptLanguage::get_singleton()->get_global_map()["Reference"]; - native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx]; - ERR_FAIL_COND_V(native.is_null(), ERR_BUG); - p_script->native = native; + } break; + default: { + _set_error("Parser bug: invalid inheritance.", p_class); + return ERR_BUG; + } break; } - //print_line("Script: "+p_script->get_path()+" indices: "+itos(p_script->member_indices.size())); - for (int i = 0; i < p_class->variables.size(); i++) { StringName name = p_class->variables[i].identifier; @@ -1902,6 +1773,7 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons for (int i = 0; i < p_class->constant_expressions.size(); i++) { StringName name = p_class->constant_expressions[i].identifier; + ERR_CONTINUE(p_class->constant_expressions[i].expression->type != GDScriptParser::Node::TYPE_CONSTANT); if (_is_class_member_property(p_script, name)) { @@ -1948,23 +1820,27 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons p_script->_signals[name] = p_class->_signals[i].arguments; } + + if (p_class->name != StringName()) { + parsed_classes.insert(p_class->name); + } + //parse sub-classes for (int i = 0; i < p_class->subclasses.size(); i++) { StringName name = p_class->subclasses[i]->name; - Ref<GDScript> subclass; + Ref<GDScript> subclass = class_map[name]; - if (old_subclasses.has(name)) { - subclass = old_subclasses[name]; - } else { - subclass.instance(); + // Subclass might still be parsing, just skip it + if (!parsed_classes.has(name) && !parsing_classes.has(name)) { + parsing_classes.insert(name); + Error err = _parse_class_level(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state); + if (err) + return err; + parsing_classes.erase(name); } - Error err = _parse_class(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state); - if (err) - return err; - #ifdef TOOLS_ENABLED p_script->member_lines[name] = p_class->subclasses[i]->line; @@ -1974,6 +1850,11 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons p_script->subclasses.insert(name, subclass); } + p_script->valid = true; + return OK; +} + +Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { //parse methods bool has_initializer = false; @@ -2014,44 +1895,6 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons } #ifdef DEBUG_ENABLED - //validate setters/getters if debug is enabled - for (int i = 0; i < p_class->variables.size(); i++) { - - if (p_class->variables[i].setter) { - const Map<StringName, GDScriptFunction *>::Element *E = p_script->get_member_functions().find(p_class->variables[i].setter); - if (!E) { - _set_error("Setter function '" + String(p_class->variables[i].setter) + "' not found in class.", NULL); - err_line = p_class->variables[i].line; - err_column = 0; - return ERR_PARSE_ERROR; - } - - if (E->get()->is_static()) { - - _set_error("Setter function '" + String(p_class->variables[i].setter) + "' is static.", NULL); - err_line = p_class->variables[i].line; - err_column = 0; - return ERR_PARSE_ERROR; - } - } - if (p_class->variables[i].getter) { - const Map<StringName, GDScriptFunction *>::Element *E = p_script->get_member_functions().find(p_class->variables[i].getter); - if (!E) { - _set_error("Getter function '" + String(p_class->variables[i].getter) + "' not found in class.", NULL); - err_line = p_class->variables[i].line; - err_column = 0; - return ERR_PARSE_ERROR; - } - - if (E->get()->is_static()) { - - _set_error("Getter function '" + String(p_class->variables[i].getter) + "' is static.", NULL); - err_line = p_class->variables[i].line; - err_column = 0; - return ERR_PARSE_ERROR; - } - } - } //validate instances if keeping state @@ -2104,10 +1947,45 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons } #endif - p_script->valid = true; + for (int i = 0; i < p_class->subclasses.size(); i++) { + StringName name = p_class->subclasses[i]->name; + Ref<GDScript> subclass = class_map[name]; + + Error err = _parse_class_blocks(subclass.ptr(), p_class->subclasses[i], p_keep_state); + if (err) { + return err; + } + } + return OK; } +void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { + + Map<StringName, Ref<GDScript> > old_subclasses; + + if (p_keep_state) { + old_subclasses = p_script->subclasses; + } + + for (int i = 0; i < p_class->subclasses.size(); i++) { + StringName name = p_class->subclasses[i]->name; + + Ref<GDScript> subclass; + + if (old_subclasses.has(name)) { + subclass = old_subclasses[name]; + } else { + subclass.instance(); + } + + subclass->_owner = const_cast<GDScript *>(p_script); + class_map.insert(name, subclass); + + _make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state); + } +} + Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) { err_line = -1; @@ -2119,7 +1997,15 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri source = p_script->get_path(); - Error err = _parse_class(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state); + // Create scripts for subclasses beforehand so they can be referenced + _make_scripts(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state); + + Error err = _parse_class_level(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state); + + if (err) + return err; + + err = _parse_class_blocks(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state); if (err) return err; diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 237b0de9e7..19df9bd722 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -31,12 +31,16 @@ #ifndef GDSCRIPT_COMPILER_H #define GDSCRIPT_COMPILER_H +#include "core/set.h" #include "gdscript.h" #include "gdscript_parser.h" class GDScriptCompiler { const GDScriptParser *parser; + Map<StringName, Ref<GDScript> > class_map; + Set<StringName> parsed_classes; + Set<StringName> parsing_classes; struct CodeGen { GDScript *script; @@ -142,7 +146,9 @@ class GDScriptCompiler { int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false); Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1); Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false); - Error _parse_class(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + Error _parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + void _make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); int err_line; int err_column; StringName source; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 37781cb934..69f71c6759 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3188,10 +3188,25 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { name = tokenizer->get_token_identifier(1); tokenizer->advance(2); + // Check if name is shadowing something else + if (ClassDB::class_exists(name)) { + _set_error("Class '" + String(name) + "' shadows a native class."); + return; + } if (ScriptServer::is_global_class(name)) { _set_error("Can't override name of unique global class '" + name + "' already exists at path: " + ScriptServer::get_global_class_path(p_class->name)); return; } + if (class_map.has(name)) { + _set_error("Class '" + String(name) + "' shadows another class in the file."); + return; + } + for (int i = 0; i < p_class->constant_expressions.size(); i++) { + if (p_class->constant_expressions[i].identifier == name) { + _set_error("Class '" + String(name) + "' shadows a constant of the outer class."); + return; + } + } ClassNode *newclass = alloc_node<ClassNode>(); newclass->initializer = alloc_node<BlockNode>(); @@ -3202,6 +3217,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { newclass->owner = p_class; p_class->subclasses.push_back(newclass); + class_map.insert(name, newclass); if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_EXTENDS) { @@ -4337,6 +4353,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } + int line = tokenizer->get_token_line(); + tokenizer->advance(); Node *subexpr = _parse_and_reduce_expression(p_class, true, true); @@ -4348,14 +4366,15 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } if (subexpr->type != Node::TYPE_CONSTANT) { - _set_error("Expected constant expression"); + _set_error("Expected constant expression", line); + return; } constant.expression = subexpr; p_class->constant_expressions.push_back(constant); if (!_end_statement()) { - _set_error("Expected end of statement (constant)"); + _set_error("Expected end of statement (constant)", line); return; } @@ -4415,17 +4434,19 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (subexpr->type != Node::TYPE_CONSTANT) { _set_error("Expected constant expression"); + return; } - const ConstantNode *subexpr_const = static_cast<const ConstantNode *>(subexpr); + ConstantNode *subexpr_const = static_cast<ConstantNode *>(subexpr); if (subexpr_const->value.get_type() != Variant::INT) { _set_error("Expected an int value for enum"); + return; } last_assign = subexpr_const->value; - constant.expression = subexpr; + constant.expression = subexpr_const; } else { last_assign = last_assign + 1; @@ -4483,6 +4504,212 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } +void GDScriptParser::_determine_inheritance(ClassNode *p_class) { + + if (p_class->extends_used) { + //do inheritance + String path = p_class->extends_file; + + Ref<GDScript> script; + StringName native; + ClassNode *base_class = NULL; + + if (path != "") { + //path (and optionally subclasses) + + if (path.is_rel_path()) { + + String base = self_path; + + if (base == "" || base.is_rel_path()) { + _set_error("Could not resolve relative path for parent class: " + path, p_class->line); + return; + } + path = base.get_base_dir().plus_file(path).simplify_path(); + } + script = ResourceLoader::load(path); + if (script.is_null()) { + _set_error("Could not load base class: " + path, p_class->line); + return; + } + if (!script->is_valid()) { + + _set_error("Script not fully loaded (cyclic preload?): " + path, p_class->line); + return; + } + + if (p_class->extends_class.size()) { + + for (int i = 0; i < p_class->extends_class.size(); i++) { + + String sub = p_class->extends_class[i]; + if (script->get_subclasses().has(sub)) { + + Ref<Script> subclass = script->get_subclasses()[sub]; //avoid reference from disappearing + script = subclass; + } else { + + _set_error("Could not find subclass: " + sub, p_class->line); + return; + } + } + } + + } else { + + if (p_class->extends_class.size() == 0) { + _set_error("Parser bug: undecidable inheritance.", p_class->line); + ERR_FAIL(); + } + //look around for the subclasses + + int extend_iter = 1; + String base = p_class->extends_class[0]; + ClassNode *p = p_class->owner; + Ref<GDScript> base_script; + + if (ScriptServer::is_global_class(base)) { + base_script = ResourceLoader::load(ScriptServer::get_global_class_path(base)); + if (!base_script.is_valid()) { + _set_error("Class '" + base + "' could not be fully loaded (script error or cyclic inheritance).", p_class->line); + return; + } + p = NULL; + } + + while (p) { + + bool found = false; + + for (int i = 0; i < p->subclasses.size(); i++) { + if (p->subclasses[i]->name == base) { + ClassNode *test = p->subclasses[i]; + while (test) { + if (test == p_class) { + _set_error("Cyclic inheritance.", test->line); + return; + } + if (test->base_type.kind == DataType::CLASS) { + test = test->base_type.class_type; + } else { + break; + } + } + found = true; + if (extend_iter < p_class->extends_class.size()) { + // Keep looking at current classes if possible + base = p_class->extends_class[extend_iter++]; + p = p->subclasses[i]; + } else { + base_class = p->subclasses[i]; + } + break; + } + } + + if (base_class) break; + if (found) continue; + + for (int i = 0; i < p->constant_expressions.size(); i++) { + if (p->constant_expressions[i].identifier == base) { + if (!p->constant_expressions[i].expression->type == Node::TYPE_CONSTANT) { + _set_error("Could not resolve constant '" + base + "'.", p_class->line); + return; + } + const ConstantNode *cn = static_cast<const ConstantNode *>(p->constant_expressions[i].expression); + base_script = cn->value; + if (base_script.is_null()) { + _set_error("Constant is not a class: " + base, p_class->line); + return; + } + found = true; + } + } + + if (found) break; + + p = p->owner; + } + + if (base_script.is_valid()) { + + String ident = base; + + for (int i = extend_iter; i < p_class->extends_class.size(); i++) { + + String subclass = p_class->extends_class[i]; + + ident += ("." + subclass); + + if (base_script->get_subclasses().has(subclass)) { + + base_script = base_script->get_subclasses()[subclass]; + } else if (base_script->get_constants().has(subclass)) { + + Ref<GDScript> new_base_class = base_script->get_constants()[subclass]; + if (new_base_class.is_null()) { + _set_error("Constant is not a class: " + ident, p_class->line); + return; + } + base_script = new_base_class; + } else { + + _set_error("Could not find subclass: " + ident, p_class->line); + return; + } + } + + script = base_script; + + } else if (!base_class) { + + if (p_class->extends_class.size() > 1) { + + _set_error("Invalid inheritance (unknown class + subclasses)", p_class->line); + return; + } + //if not found, try engine classes + if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) { + + _set_error("Unknown class: '" + base + "'", p_class->line); + return; + } + + native = base; + } + } + + if (base_class) { + p_class->base_type.has_type = true; + p_class->base_type.kind = DataType::CLASS; + p_class->base_type.class_type = base_class; + } else if (script.is_valid()) { + p_class->base_type.has_type = true; + p_class->base_type.kind = DataType::GDSCRIPT; + p_class->base_type.script_type = script; + p_class->base_type.native_type = script->get_instance_base_type(); + } else if (native != StringName()) { + p_class->base_type.has_type = true; + p_class->base_type.kind = DataType::NATIVE; + p_class->base_type.native_type = native; + } else { + _set_error("Could not determine inheritance", p_class->line); + return; + } + + } else { + // without extends, implicitly extend Reference + p_class->base_type.has_type = true; + p_class->base_type.kind = DataType::NATIVE; + p_class->base_type.native_type = "Reference"; + } + + // Recursively determine subclasses + for (int i = 0; i < p_class->subclasses.size(); i++) { + _determine_inheritance(p_class->subclasses[i]); + } +} + bool GDScriptParser::_parse_type(DataType &r_type, bool p_can_be_void) { tokenizer->advance(); @@ -4553,9 +4780,15 @@ Error GDScriptParser::_parse(const String &p_base_path) { } if (error_set) { + return ERR_PARSE_ERROR; + } + _determine_inheritance(main_class); + + if (error_set) { return ERR_PARSE_ERROR; } + return OK; } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index a71bd96ded..5330d82133 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -107,6 +107,7 @@ public: struct FunctionNode; struct BlockNode; + struct ConstantNode; struct ClassNode : public Node { @@ -115,6 +116,7 @@ public: bool extends_used; StringName extends_file; Vector<StringName> extends_class; + DataType base_type; struct Member { PropertyInfo _export; @@ -446,6 +448,7 @@ private: ClassNode *current_class; FunctionNode *current_function; BlockNode *current_block; + Map<StringName, ClassNode *> class_map; bool _get_completable_identifier(CompletionType p_type, StringName &identifier); void _make_completable_call(int p_arg); @@ -487,6 +490,7 @@ private: void _parse_class(ClassNode *p_class); bool _end_statement(); + void _determine_inheritance(ClassNode *p_class); bool _parse_type(DataType &r_type, bool p_can_be_void = false); Error _parse(const String &p_base_path); |