Browse Source

Move inheritance resolution to the parser

George Marques 7 years ago
parent
commit
b7a00aead0

+ 34 - 2
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) {
 	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());
 		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;
 		return c->name;
 	}
 	}

+ 113 - 227
modules/gdscript/gdscript_compiler.cpp

@@ -263,15 +263,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
 				owner = owner->_owner;
 				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)) {
 			if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
 
 
 				int idx = GDScriptLanguage::get_singleton()->get_global_map()[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;
 	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>();
 	p_script->native = Ref<GDScriptNativeClass>();
@@ -1686,177 +1688,46 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
 
 
 	Ref<GDScriptNativeClass> native;
 	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->_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;
 			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++) {
 	for (int i = 0; i < p_class->variables.size(); i++) {
 
 
 		StringName name = p_class->variables[i].identifier;
 		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++) {
 	for (int i = 0; i < p_class->constant_expressions.size(); i++) {
 
 
 		StringName name = p_class->constant_expressions[i].identifier;
 		StringName name = p_class->constant_expressions[i].identifier;
+
 		ERR_CONTINUE(p_class->constant_expressions[i].expression->type != GDScriptParser::Node::TYPE_CONSTANT);
 		ERR_CONTINUE(p_class->constant_expressions[i].expression->type != GDScriptParser::Node::TYPE_CONSTANT);
 
 
 		if (_is_class_member_property(p_script, name)) {
 		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;
 		p_script->_signals[name] = p_class->_signals[i].arguments;
 	}
 	}
+
+	if (p_class->name != StringName()) {
+		parsed_classes.insert(p_class->name);
+	}
+
 	//parse sub-classes
 	//parse sub-classes
 
 
 	for (int i = 0; i < p_class->subclasses.size(); i++) {
 	for (int i = 0; i < p_class->subclasses.size(); i++) {
 		StringName name = p_class->subclasses[i]->name;
 		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
 #ifdef TOOLS_ENABLED
 
 
 		p_script->member_lines[name] = p_class->subclasses[i]->line;
 		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->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
 	//parse methods
 
 
 	bool has_initializer = false;
 	bool has_initializer = false;
@@ -2014,44 +1895,6 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
 	}
 	}
 
 
 #ifdef DEBUG_ENABLED
 #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
 	//validate instances if keeping state
 
 
@@ -2104,10 +1947,45 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
 	}
 	}
 #endif
 #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;
 	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) {
 Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) {
 
 
 	err_line = -1;
 	err_line = -1;
@@ -2119,7 +1997,15 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
 
 
 	source = p_script->get_path();
 	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)
 	if (err)
 		return err;
 		return err;

+ 7 - 1
modules/gdscript/gdscript_compiler.h

@@ -31,12 +31,16 @@
 #ifndef GDSCRIPT_COMPILER_H
 #ifndef GDSCRIPT_COMPILER_H
 #define GDSCRIPT_COMPILER_H
 #define GDSCRIPT_COMPILER_H
 
 
+#include "core/set.h"
 #include "gdscript.h"
 #include "gdscript.h"
 #include "gdscript_parser.h"
 #include "gdscript_parser.h"
 
 
 class GDScriptCompiler {
 class GDScriptCompiler {
 
 
 	const GDScriptParser *parser;
 	const GDScriptParser *parser;
+	Map<StringName, Ref<GDScript> > class_map;
+	Set<StringName> parsed_classes;
+	Set<StringName> parsing_classes;
 	struct CodeGen {
 	struct CodeGen {
 
 
 		GDScript *script;
 		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);
 	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_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_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_line;
 	int err_column;
 	int err_column;
 	StringName source;
 	StringName source;

+ 237 - 4
modules/gdscript/gdscript_parser.cpp

@@ -3188,10 +3188,25 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
 				name = tokenizer->get_token_identifier(1);
 				name = tokenizer->get_token_identifier(1);
 				tokenizer->advance(2);
 				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)) {
 				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));
 					_set_error("Can't override name of unique global class '" + name + "' already exists at path: " + ScriptServer::get_global_class_path(p_class->name));
 					return;
 					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>();
 				ClassNode *newclass = alloc_node<ClassNode>();
 				newclass->initializer = alloc_node<BlockNode>();
 				newclass->initializer = alloc_node<BlockNode>();
@@ -3202,6 +3217,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
 				newclass->owner = p_class;
 				newclass->owner = p_class;
 
 
 				p_class->subclasses.push_back(newclass);
 				p_class->subclasses.push_back(newclass);
+				class_map.insert(name, newclass);
 
 
 				if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_EXTENDS) {
 				if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_EXTENDS) {
 
 
@@ -4337,6 +4353,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
 					return;
 					return;
 				}
 				}
 
 
+				int line = tokenizer->get_token_line();
+
 				tokenizer->advance();
 				tokenizer->advance();
 
 
 				Node *subexpr = _parse_and_reduce_expression(p_class, true, true);
 				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) {
 				if (subexpr->type != Node::TYPE_CONSTANT) {
-					_set_error("Expected constant expression");
+					_set_error("Expected constant expression", line);
+					return;
 				}
 				}
 				constant.expression = subexpr;
 				constant.expression = subexpr;
 
 
 				p_class->constant_expressions.push_back(constant);
 				p_class->constant_expressions.push_back(constant);
 
 
 				if (!_end_statement()) {
 				if (!_end_statement()) {
-					_set_error("Expected end of statement (constant)");
+					_set_error("Expected end of statement (constant)", line);
 					return;
 					return;
 				}
 				}
 
 
@@ -4415,17 +4434,19 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
 
 
 							if (subexpr->type != Node::TYPE_CONSTANT) {
 							if (subexpr->type != Node::TYPE_CONSTANT) {
 								_set_error("Expected constant expression");
 								_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) {
 							if (subexpr_const->value.get_type() != Variant::INT) {
 								_set_error("Expected an int value for enum");
 								_set_error("Expected an int value for enum");
+								return;
 							}
 							}
 
 
 							last_assign = subexpr_const->value;
 							last_assign = subexpr_const->value;
 
 
-							constant.expression = subexpr;
+							constant.expression = subexpr_const;
 
 
 						} else {
 						} else {
 							last_assign = last_assign + 1;
 							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) {
 bool GDScriptParser::_parse_type(DataType &r_type, bool p_can_be_void) {
 	tokenizer->advance();
 	tokenizer->advance();
 
 
@@ -4553,9 +4780,15 @@ Error GDScriptParser::_parse(const String &p_base_path) {
 	}
 	}
 
 
 	if (error_set) {
 	if (error_set) {
+		return ERR_PARSE_ERROR;
+	}
 
 
+	_determine_inheritance(main_class);
+
+	if (error_set) {
 		return ERR_PARSE_ERROR;
 		return ERR_PARSE_ERROR;
 	}
 	}
+
 	return OK;
 	return OK;
 }
 }
 
 

+ 4 - 0
modules/gdscript/gdscript_parser.h

@@ -107,6 +107,7 @@ public:
 
 
 	struct FunctionNode;
 	struct FunctionNode;
 	struct BlockNode;
 	struct BlockNode;
+	struct ConstantNode;
 
 
 	struct ClassNode : public Node {
 	struct ClassNode : public Node {
 
 
@@ -115,6 +116,7 @@ public:
 		bool extends_used;
 		bool extends_used;
 		StringName extends_file;
 		StringName extends_file;
 		Vector<StringName> extends_class;
 		Vector<StringName> extends_class;
+		DataType base_type;
 
 
 		struct Member {
 		struct Member {
 			PropertyInfo _export;
 			PropertyInfo _export;
@@ -446,6 +448,7 @@ private:
 	ClassNode *current_class;
 	ClassNode *current_class;
 	FunctionNode *current_function;
 	FunctionNode *current_function;
 	BlockNode *current_block;
 	BlockNode *current_block;
+	Map<StringName, ClassNode *> class_map;
 
 
 	bool _get_completable_identifier(CompletionType p_type, StringName &identifier);
 	bool _get_completable_identifier(CompletionType p_type, StringName &identifier);
 	void _make_completable_call(int p_arg);
 	void _make_completable_call(int p_arg);
@@ -487,6 +490,7 @@ private:
 	void _parse_class(ClassNode *p_class);
 	void _parse_class(ClassNode *p_class);
 	bool _end_statement();
 	bool _end_statement();
 
 
+	void _determine_inheritance(ClassNode *p_class);
 	bool _parse_type(DataType &r_type, bool p_can_be_void = false);
 	bool _parse_type(DataType &r_type, bool p_can_be_void = false);
 
 
 	Error _parse(const String &p_base_path);
 	Error _parse(const String &p_base_path);