Browse Source

- added 'onready' keyword to gdscript. Defers initialization of member variables until _ready() is run.

reduz 9 years ago
parent
commit
30c12297dc

+ 29 - 4
modules/gdscript/gd_compiler.cpp

@@ -1181,7 +1181,7 @@ Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_blo
 }
 
 
-Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *p_class,const GDParser::FunctionNode *p_func) {
+Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *p_class,const GDParser::FunctionNode *p_func,bool p_for_ready) {
 
 	Vector<int> bytecode;
 	CodeGen codegen;
@@ -1212,9 +1212,9 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *
 
 	/* Parse initializer -if applies- */
 
-	bool is_initializer=false || !p_func;
+	bool is_initializer=!p_for_ready && !p_func;
 
-	if (!p_func || String(p_func->name)=="_init") {
+	if (is_initializer || String(p_func->name)=="_init") {
 		//parse initializer for class members
 		if (!p_func && p_class->extends_used && p_script->native.is_null()){
 
@@ -1232,6 +1232,17 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *
 
 	}
 
+	if (p_for_ready || (p_func && String(p_func->name)=="_ready")) {
+		//parse initializer for class members
+		if (p_class->ready->statements.size()) {
+			Error err = _parse_block(codegen,p_class->ready,stack_level);
+			if (err)
+				return err;
+		}
+
+	}
+
+
 	/* Parse default argument code -if applies- */
 
 	Vector<int> defarg_addr;
@@ -1260,7 +1271,10 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *
 
 		func_name=p_func->name;
 	} else {
-		func_name="_init";
+		if (p_for_ready)
+			func_name="_ready";
+		else
+			func_name="_init";
 	}
 
 	codegen.opcodes.push_back(GDFunction::OPCODE_END);
@@ -1614,10 +1628,14 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
 	//parse methods
 
 	bool has_initializer=false;
+	bool has_ready=false;
+
 	for(int i=0;i<p_class->functions.size();i++) {
 
 		if (!has_initializer && p_class->functions[i]->name=="_init")
 			has_initializer=true;
+		if (!has_ready && p_class->functions[i]->name=="_ready")
+			has_ready=true;
 		Error err = _parse_function(p_script,p_class,p_class->functions[i]);
 		if (err)
 			return err;
@@ -1640,6 +1658,13 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars
 			return err;
 	}
 
+	if (!has_ready && p_class->ready->statements.size()) {
+		//create a constructor
+		Error err = _parse_function(p_script,p_class,NULL,true);
+		if (err)
+			return err;
+	}
+
 #ifdef DEBUG_ENABLED
 	//validate setters/getters if debug is enabled
 	for(int i=0;i<p_class->variables.size();i++) {

+ 1 - 1
modules/gdscript/gd_compiler.h

@@ -143,7 +143,7 @@ class GDCompiler {
 	int _parse_assign_right_expression(CodeGen& codegen,const GDParser::OperatorNode *p_expression, int p_stack_level);
 	int _parse_expression(CodeGen& codegen,const GDParser::Node *p_expression, int p_stack_level,bool p_root=false,bool p_initializer=false);
 	Error _parse_block(CodeGen& codegen,const GDParser::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 GDParser::ClassNode *p_class,const GDParser::FunctionNode *p_func);
+	Error _parse_function(GDScript *p_script,const GDParser::ClassNode *p_class,const GDParser::FunctionNode *p_func,bool p_for_ready=false);
 	Error _parse_class(GDScript *p_script,GDScript *p_owner,const GDParser::ClassNode *p_class);
 	int err_line;
 	int err_column;

+ 43 - 2
modules/gdscript/gd_parser.cpp

@@ -2767,6 +2767,21 @@ void GDParser::_parse_class(ClassNode *p_class) {
 				}
 
 			}; //fallthrough to var
+			case GDTokenizer::TK_PR_ONREADY: {
+
+				if (tokenizer->get_token(-1)==GDTokenizer::TK_PR_EXPORT) {
+					current_export=PropertyInfo();
+					_set_error("Expected 'var' (can't combine with 'onready').");
+					return;
+				} else {
+
+					tokenizer->advance();
+					if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+						_set_error("Expected 'var'.");
+						return;
+					}
+				}
+			}; //fallthrough to var
 			case GDTokenizer::TK_PR_VAR: {
 				//variale declaration and (eventual) initialization
 
@@ -2777,6 +2792,8 @@ void GDParser::_parse_class(ClassNode *p_class) {
 					current_export=PropertyInfo();
 				}
 
+				bool onready = tokenizer->get_token(-1)==GDTokenizer::TK_PR_ONREADY;
+
 				tokenizer->advance();
 				if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
 
@@ -2807,6 +2824,21 @@ void GDParser::_parse_class(ClassNode *p_class) {
 						return;
 					}
 
+					//discourage common error
+					if (!onready && subexpr->type==Node::TYPE_OPERATOR) {
+
+						OperatorNode *op=static_cast<OperatorNode*>(subexpr);
+						if (op->op==OperatorNode::OP_CALL && op->arguments[0]->type==Node::TYPE_SELF && op->arguments[1]->type==Node::TYPE_IDENTIFIER) {
+							IdentifierNode *id=static_cast<IdentifierNode*>(op->arguments[1]);
+							if (id->name=="get_node") {
+
+								_set_error("Use 'onready var "+String(member.identifier)+" = get_node(..)' instead");
+								return;
+
+							}
+						}
+					}
+
 					member.expression=subexpr;
 
 					if (autoexport) {
@@ -2853,12 +2885,19 @@ void GDParser::_parse_class(ClassNode *p_class) {
 					op->arguments.push_back(id);
 					op->arguments.push_back(subexpr);
 
+
 #ifdef DEBUG_ENABLED
 					NewLineNode *nl = alloc_node<NewLineNode>();
 					nl->line=line;
-					p_class->initializer->statements.push_back(nl);
+					if (onready)
+						p_class->ready->statements.push_back(nl);
+					else
+						p_class->initializer->statements.push_back(nl);
 #endif
-					p_class->initializer->statements.push_back(op);
+					if (onready)
+						p_class->ready->statements.push_back(op);
+					else
+						p_class->initializer->statements.push_back(op);
 
 
 
@@ -3009,6 +3048,8 @@ Error GDParser::_parse(const String& p_base_path) {
 	ClassNode *main_class = alloc_node<ClassNode>();
 	main_class->initializer = alloc_node<BlockNode>();
 	main_class->initializer->parent_class=main_class;
+	main_class->ready = alloc_node<BlockNode>();
+	main_class->ready->parent_class=main_class;
 	current_class=main_class;
 
 	_parse_class(main_class);

+ 1 - 0
modules/gdscript/gd_parser.h

@@ -105,6 +105,7 @@ public:
 		Vector<FunctionNode*> static_functions;
 		Vector<Signal> _signals;
 		BlockNode *initializer;
+		BlockNode *ready;
 		ClassNode *owner;
 		//Vector<Node*> initializers;
 		int end_line;

+ 1 - 0
modules/gdscript/gd_script.cpp

@@ -2652,6 +2652,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const  {
 		"elif",
 		"enum",
 		"extends"	,
+		"onready",
 		"for"	,
 		"func"	,
 		"if"	,

+ 2 - 1
modules/gdscript/gd_tokenizer.cpp

@@ -851,6 +851,7 @@ void GDTokenizerText::_advance() {
 								{TK_PR_FUNCTION,"function"},
 								{TK_PR_CLASS,"class"},
 								{TK_PR_EXTENDS,"extends"},
+								{TK_PR_ONREADY,"onready"},
 								{TK_PR_TOOL,"tool"},
 								{TK_PR_STATIC,"static"},
 								{TK_PR_EXPORT,"export"},
@@ -1040,7 +1041,7 @@ void GDTokenizerText::advance(int p_amount) {
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////
 
-#define BYTECODE_VERSION 5
+#define BYTECODE_VERSION 6
 
 Error GDTokenizerBuffer::set_code_buffer(const Vector<uint8_t> & p_buffer) {
 

+ 1 - 0
modules/gdscript/gd_tokenizer.h

@@ -95,6 +95,7 @@ public:
 		TK_PR_FUNCTION,
 		TK_PR_CLASS,
 		TK_PR_EXTENDS,
+		TK_PR_ONREADY,
 		TK_PR_TOOL,
 		TK_PR_STATIC,
 		TK_PR_EXPORT,