Move inheritance resolution to the parser
This commit is contained in:
parent
8aab9a06d4
commit
b7a00aead0
|
@ -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) {
|
if (r_base_type) {
|
||||||
*r_base_type = c->extends_class[0]; //todo, should work much better
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_class->owner && p_class->owner->owner) {
|
||||||
|
// Owner is not root
|
||||||
if (p_keep_state) {
|
StringName owner_name = p_class->owner->name;
|
||||||
old_subclasses = p_script->subclasses;
|
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) {
|
// Inheritance
|
||||||
//do inheritance
|
switch (p_class->base_type.kind) {
|
||||||
String path = p_class->extends_file;
|
case GDScriptParser::DataType::CLASS: {
|
||||||
|
StringName base_name = p_class->base_type.class_type->name;
|
||||||
Ref<GDScript> script;
|
// Make sure dependency is parsed first
|
||||||
|
if (!parsed_classes.has(base_name)) {
|
||||||
if (path != "") {
|
if (parsing_classes.has(base_name)) {
|
||||||
//path (and optionally subclasses)
|
_set_error("Cyclic class reference for '" + String(base_name) + "'.", p_class);
|
||||||
|
return ERR_PARSE_ERROR;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
parsing_classes.insert(base_name);
|
||||||
if (base == "" || base.is_rel_path()) {
|
Error err = _parse_class_level(class_map[base_name].ptr(), class_map[base_name]->_owner, p_class->base_type.class_type, p_keep_state);
|
||||||
_set_error("Could not resolve relative path for parent class: " + path, p_class);
|
if (err) {
|
||||||
return ERR_FILE_NOT_FOUND;
|
return err;
|
||||||
}
|
}
|
||||||
path = base.get_base_dir().plus_file(path).simplify_path();
|
parsing_classes.erase(base_name);
|
||||||
}
|
}
|
||||||
script = ResourceLoader::load(path);
|
Ref<GDScript> base = class_map[base_name];
|
||||||
if (script.is_null()) {
|
p_script->base = base;
|
||||||
_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (script.is_valid()) {
|
|
||||||
|
|
||||||
p_script->base = script;
|
|
||||||
p_script->_base = p_script->base.ptr();
|
p_script->_base = p_script->base.ptr();
|
||||||
p_script->member_indices = script->member_indices;
|
p_script->member_indices = base->member_indices;
|
||||||
|
} break;
|
||||||
} else if (native.is_valid()) {
|
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 {
|
} break;
|
||||||
|
default: {
|
||||||
_set_error("Could not determine inheritance", p_class);
|
_set_error("Parser bug: invalid inheritance.", p_class);
|
||||||
return ERR_FILE_NOT_FOUND;
|
return ERR_BUG;
|
||||||
}
|
} break;
|
||||||
|
|
||||||
} 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//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 might still be parsing, just skip it
|
||||||
subclass = old_subclasses[name];
|
if (!parsed_classes.has(name) && !parsing_classes.has(name)) {
|
||||||
} else {
|
parsing_classes.insert(name);
|
||||||
subclass.instance();
|
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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_determine_inheritance(main_class);
|
||||||
|
|
||||||
|
if (error_set) {
|
||||||
|
return ERR_PARSE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue