summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2014-09-15 11:33:30 -0300
committerJuan Linietsky <reduzio@gmail.com>2014-09-15 11:33:30 -0300
commit8cab401d08f8e25aa9b2dc710204785858ff3dbb (patch)
tree1a4cec868f937fb24d340ee33fbeba2f1c6fa9f2 /modules/gdscript
parent1a2cb755e2d8b9d59178f36702f6dff7235b9088 (diff)
3D Physics Rework, Other Stuff
-=-=-=-=-=-=-=-=-=-=-=-=-=- 3D Physics: -Fixed "Bounce" parameter in 3D -Fixed bug affecting Area (sometims it would not detect properly) -Vehicle Body has seen heavy work -Added Query API for doing space queries in 3D. Needs some docs though. -Added JOINTS! Adapted Bullet Joints: and created easy gizmos for setting them up: -PinJoint -HingeJoint (with motor) -SliderJoint -ConeTwistJoint -Generic6DOFJoint -Added OBJECT PICKING! based on the new query API. Any physics object now (Area or Body) has the following signals and virtual functions: -input_event (mouse or multitouch input over the body) -mouse_enter (mouse entered the body area) -mouse_exit (mouse exited body area) For Area it needs to be activated manually, as it isn't by default (ray goes thru). Other: -Begun working on Windows 8 (RT) port. Compiles but does not work yet. -Added TheoraPlayer library for improved to-texture and portable video support. -Fixed a few bugs in the renderer, collada importer, collada exporter, etc.
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/gd_compiler.cpp35
-rw-r--r--modules/gdscript/gd_parser.cpp53
-rw-r--r--modules/gdscript/gd_parser.h4
-rw-r--r--modules/gdscript/gd_script.cpp288
-rw-r--r--modules/gdscript/gd_script.h39
-rw-r--r--modules/gdscript/gd_tokenizer.cpp8
-rw-r--r--modules/gdscript/gd_tokenizer.h1
-rw-r--r--modules/gdscript/register_types.cpp1
8 files changed, 374 insertions, 55 deletions
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp
index fc9040da18..e6dd9d9ae1 100644
--- a/modules/gdscript/gd_compiler.cpp
+++ b/modules/gdscript/gd_compiler.cpp
@@ -60,7 +60,8 @@ bool GDCompiler::_create_unary_operator(CodeGen& codegen,const GDParser::Operato
codegen.opcodes.push_back(GDFunction::OPCODE_OPERATOR); // perform operator
codegen.opcodes.push_back(op); //which operator
codegen.opcodes.push_back(src_address_a); // argument 1
- codegen.opcodes.push_back(GDFunction::ADDR_TYPE_NIL); // argument 2 (unary only takes one parameter)
+ codegen.opcodes.push_back(src_address_a); // argument 2 (repeated)
+ //codegen.opcodes.push_back(GDFunction::ADDR_TYPE_NIL); // argument 2 (unary only takes one parameter)
return true;
}
@@ -507,6 +508,34 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
codegen.opcodes.push_back(arguments[i]);
}
} break;
+ case GDParser::OperatorNode::OP_YIELD: {
+
+
+ ERR_FAIL_COND_V(on->arguments.size() && on->arguments.size()!=2,-1);
+
+ Vector<int> arguments;
+ int slevel = p_stack_level;
+ for(int i=0;i<on->arguments.size();i++) {
+
+ int ret = _parse_expression(codegen,on->arguments[i],slevel);
+ if (ret<0)
+ return ret;
+ if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+ arguments.push_back(ret);
+ }
+
+ //push call bytecode
+ codegen.opcodes.push_back(arguments.size()==0?GDFunction::OPCODE_YIELD:GDFunction::OPCODE_YIELD_SIGNAL); // basic type constructor
+ for(int i=0;i<arguments.size();i++)
+ codegen.opcodes.push_back(arguments[i]); //arguments
+ codegen.opcodes.push_back(GDFunction::OPCODE_YIELD_RESUME);
+ //next will be where to place the result :)
+
+ } break;
+
//indexing operator
case GDParser::OperatorNode::OP_INDEX:
case GDParser::OperatorNode::OP_INDEX_NAMED: {
@@ -644,8 +673,8 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
case GDParser::OperatorNode::OP_BIT_OR: { if (!_create_binary_operator(codegen,on,Variant::OP_BIT_OR,p_stack_level)) return -1;} break;
case GDParser::OperatorNode::OP_BIT_XOR: { if (!_create_binary_operator(codegen,on,Variant::OP_BIT_XOR,p_stack_level)) return -1;} break;
//shift
- case GDParser::OperatorNode::OP_SHIFT_LEFT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_LEFT,p_stack_level)) return -1;} break;
- case GDParser::OperatorNode::OP_SHIFT_RIGHT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_RIGHT,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_SHIFT_LEFT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_LEFT,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_SHIFT_RIGHT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_RIGHT,p_stack_level)) return -1;} break;
//assignment operators
case GDParser::OperatorNode::OP_ASSIGN_ADD:
case GDParser::OperatorNode::OP_ASSIGN_SUB:
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 40c262c503..ef9e85a8c2 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -256,6 +256,55 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
tokenizer->advance();
expr=constant;
+ } else if (tokenizer->get_token()==GDTokenizer::TK_PR_YIELD) {
+
+ //constant defined by tokenizer
+
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_OPEN) {
+ _set_error("Expected '(' after 'yield'");
+ return NULL;
+ }
+
+ tokenizer->advance();
+
+ OperatorNode *yield = alloc_node<OperatorNode>();
+ yield->op=OperatorNode::OP_YIELD;
+
+ if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ expr=yield;
+ tokenizer->advance();
+ } else {
+
+ Node *object = _parse_and_reduce_expression(p_parent,p_static);
+ if (!object)
+ return NULL;
+ yield->arguments.push_back(object);
+
+ if (tokenizer->get_token()!=GDTokenizer::TK_COMMA) {
+
+ _set_error("Expected ',' after first argument of 'yield'");
+ return NULL;
+ }
+
+ tokenizer->advance();
+
+ Node *signal = _parse_and_reduce_expression(p_parent,p_static);
+ if (!signal)
+ return NULL;
+ yield->arguments.push_back(signal);
+
+ if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+
+ _set_error("Expected ')' after second argument of 'yield'");
+ return NULL;
+ }
+
+ tokenizer->advance();
+
+ expr=yield;
+ }
+
} else if (tokenizer->get_token()==GDTokenizer::TK_SELF) {
@@ -1067,6 +1116,10 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
}
return op; //don't reduce yet
+
+ } else if (op->op==OperatorNode::OP_YIELD) {
+ return op;
+
} else if (op->op==OperatorNode::OP_INDEX) {
//can reduce indices into constant arrays or dictionaries
diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h
index ae43a26f5a..50b84d389a 100644
--- a/modules/gdscript/gd_parser.h
+++ b/modules/gdscript/gd_parser.h
@@ -183,6 +183,7 @@ public:
//call/constructor operator
OP_CALL,
OP_PARENT_CALL,
+ OP_YIELD,
OP_EXTENDS,
//indexing operator
OP_INDEX,
@@ -225,7 +226,7 @@ public:
OP_ASSIGN_BIT_XOR,
OP_BIT_AND,
OP_BIT_OR,
- OP_BIT_XOR
+ OP_BIT_XOR,
};
Operator op;
@@ -258,6 +259,7 @@ public:
Node* condition;
AssertNode() { type=TYPE_ASSERT; }
};
+
struct NewLineNode : public Node {
int line;
NewLineNode() { type=TYPE_NEWLINE; }
diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp
index cc7aa70234..92962fa3f7 100644
--- a/modules/gdscript/gd_script.cpp
+++ b/modules/gdscript/gd_script.cpp
@@ -183,7 +183,7 @@ static String _get_var_type(const Variant* p_type) {
}
-Variant GDFunction::call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err) {
+Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_argcount, Variant::CallError& r_err, CallState *p_state) {
if (!_code_ptr) {
@@ -205,77 +205,91 @@ Variant GDFunction::call(GDInstance *p_instance,const Variant **p_args, int p_ar
#endif
- if (p_argcount!=_argument_count) {
+ uint32_t alloca_size=0;
+ GDScript *_class;
+ int ip=0;
+ int line=_initial_line;
- if (p_argcount>_argument_count) {
+ if (p_state) {
+ //use existing (supplied) state (yielded)
+ stack=(Variant*)p_state->stack.ptr();
+ call_args=(Variant**)&p_state->stack[sizeof(Variant)*p_state->stack_size];
+ line=p_state->line;
+ ip=p_state->ip;
+ alloca_size=p_state->stack.size();
+ _class=p_state->_class;
+ p_instance=p_state->instance;
+ defarg=p_state->defarg;
+ self=p_state->self;
+ //stack[p_state->result_pos]=p_state->result; //assign stack with result
- r_err.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_err.argument=_argument_count;
+ } else {
- return Variant();
- } else if (p_argcount < _argument_count - _default_arg_count) {
+ if (p_argcount!=_argument_count) {
- r_err.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_err.argument=_argument_count - _default_arg_count;
- return Variant();
- } else {
+ if (p_argcount>_argument_count) {
- defarg=_argument_count-p_argcount;
- }
- }
+ r_err.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_err.argument=_argument_count;
- uint32_t alloca_size = sizeof(Variant*)*_call_size + sizeof(Variant)*_stack_size;
+ return Variant();
+ } else if (p_argcount < _argument_count - _default_arg_count) {
- if (alloca_size) {
+ r_err.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_err.argument=_argument_count - _default_arg_count;
+ return Variant();
+ } else {
- uint8_t *aptr = (uint8_t*)alloca(alloca_size);
+ defarg=_argument_count-p_argcount;
+ }
+ }
- if (_stack_size) {
+ alloca_size = sizeof(Variant*)*_call_size + sizeof(Variant)*_stack_size;
- stack=(Variant*)aptr;
- for(int i=0;i<p_argcount;i++)
- memnew_placement(&stack[i],Variant(*p_args[i]));
- for(int i=p_argcount;i<_stack_size;i++)
- memnew_placement(&stack[i],Variant);
- } else {
- stack=NULL;
- }
+ if (alloca_size) {
- if (_call_size) {
+ uint8_t *aptr = (uint8_t*)alloca(alloca_size);
- call_args = (Variant**)&aptr[sizeof(Variant)*_stack_size];
- } else {
+ if (_stack_size) {
- call_args=NULL;
- }
+ stack=(Variant*)aptr;
+ for(int i=0;i<p_argcount;i++)
+ memnew_placement(&stack[i],Variant(*p_args[i]));
+ for(int i=p_argcount;i<_stack_size;i++)
+ memnew_placement(&stack[i],Variant);
+ } else {
+ stack=NULL;
+ }
+ if (_call_size) {
- } else {
- stack=NULL;
- call_args=NULL;
- }
+ call_args = (Variant**)&aptr[sizeof(Variant)*_stack_size];
+ } else {
+
+ call_args=NULL;
+ }
- GDScript *_class;
+ } else {
+ stack=NULL;
+ call_args=NULL;
+ }
- if (p_instance) {
- if (p_instance->base_ref && static_cast<Reference*>(p_instance->owner)->is_referenced()) {
+ if (p_instance) {
+ if (p_instance->base_ref && static_cast<Reference*>(p_instance->owner)->is_referenced()) {
- self=REF(static_cast<Reference*>(p_instance->owner));
+ self=REF(static_cast<Reference*>(p_instance->owner));
+ } else {
+ self=p_instance->owner;
+ }
+ _class=p_instance->script.ptr();
} else {
- self=p_instance->owner;
+ _class=_script;
}
- _class=p_instance->script.ptr();
- } else {
- _class=_script;
}
- int ip=0;
- int line=_initial_line;
String err_text;
-
-
#ifdef DEBUG_ENABLED
if (ScriptDebugger::get_singleton())
@@ -784,6 +798,97 @@ Variant GDFunction::call(GDInstance *p_instance,const Variant **p_args, int p_ar
ip+=4+argc;
} continue;
+ case OPCODE_YIELD:
+ case OPCODE_YIELD_SIGNAL: {
+
+ int ipofs=1;
+ if (_code_ptr[ip]==OPCODE_YIELD_SIGNAL) {
+ CHECK_SPACE(4);
+ ipofs+=2;
+ } else {
+ CHECK_SPACE(2);
+
+ }
+
+ Ref<GDFunctionState> gdfs = memnew( GDFunctionState );
+ gdfs->function=this;
+
+ gdfs->state.stack.resize(alloca_size);
+ //copy variant stack
+ for(int i=0;i<_stack_size;i++) {
+ memnew_placement(&stack[sizeof(Variant)*i],Variant(stack[i]));
+ }
+ gdfs->state.stack_size=_stack_size;
+ gdfs->state.self=self;
+ gdfs->state.alloca_size=alloca_size;
+ gdfs->state._class=_class;
+ gdfs->state.ip=ip+ipofs;
+ gdfs->state.line=line;
+ //gdfs->state.result_pos=ip+ipofs-1;
+ gdfs->state.defarg=defarg;
+ gdfs->state.instance=p_instance;
+ gdfs->function=this;
+
+ retvalue=gdfs;
+
+ if (_code_ptr[ip]==OPCODE_YIELD_SIGNAL) {
+ GET_VARIANT_PTR(argobj,1);
+ GET_VARIANT_PTR(argname,2);
+ //do the oneshot connect
+
+ if (argobj->get_type()!=Variant::OBJECT) {
+ err_text="First argument of yield() not of type object.";
+ break;
+ }
+ if (argname->get_type()!=Variant::STRING) {
+ err_text="Second argument of yield() not a string (for signal name).";
+ break;
+ }
+ Object *obj=argobj->operator Object *();
+ String signal = argname->operator String();
+#ifdef DEBUG_ENABLED
+
+ if (!obj) {
+ err_text="First argument of yield() is null.";
+ break;
+ }
+ if (ScriptDebugger::get_singleton()) {
+ if (!ObjectDB::instance_validate(obj)) {
+ err_text="First argument of yield() is a previously freed instance.";
+ break;
+ }
+ }
+ if (signal.length()==0) {
+
+ err_text="Second argument of yield() is an empty string (for signal name).";
+ break;
+ }
+
+#endif
+ Error err = obj->connect(signal,gdfs.ptr(),"_signal_callback",varray(gdfs),Object::CONNECT_ONESHOT);
+ if (err!=OK) {
+ err_text="Error connecting to signal: "+signal+" during yield().";
+ break;
+ }
+
+
+ }
+
+ exit_ok=true;
+
+ } break;
+ case OPCODE_YIELD_RESUME: {
+
+ CHECK_SPACE(2);
+ if (!p_state) {
+ err_text=("Invalid Resume (bug?)");
+ break;
+ }
+ GET_VARIANT_PTR(result,1);
+ *result=p_state->result;
+ ip+=2;
+
+ } continue;
case OPCODE_JUMP: {
CHECK_SPACE(2);
@@ -1168,6 +1273,93 @@ GDFunction::GDFunction() {
}
+/////////////////////
+
+
+Variant GDFunctionState::_signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error) {
+
+ Variant arg;
+ r_error.error=Variant::CallError::CALL_OK;
+
+ ERR_FAIL_COND_V(!function,Variant());
+
+ if (p_argcount==0) {
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument=1;
+ return Variant();
+ } else if (p_argcount==1) {
+ //noooneee
+ } else if (p_argcount==2) {
+ arg=*p_args[0];
+ } else {
+ Array extra_args;
+ for(int i=0;i<p_argcount-1;i++) {
+ extra_args.push_back(*p_args[i]);
+ }
+ arg=extra_args;
+ }
+
+ Ref<GDFunctionState> self = *p_args[p_argcount-1];
+
+ if (self.is_null()) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=p_argcount-1;
+ r_error.expected=Variant::OBJECT;
+ return Variant();
+ }
+
+ state.result=arg;
+ Variant ret = function->call(NULL,NULL,0,r_error,&state);
+ function=NULL; //cleaned up;
+ state.result=Variant();
+ return ret;
+}
+
+
+bool GDFunctionState::is_valid() const {
+
+ return function!=NULL;
+}
+
+Variant GDFunctionState::resume(const Variant& p_arg) {
+
+ ERR_FAIL_COND_V(!function,Variant());
+
+ state.result=p_arg;
+ Variant::CallError err;
+ Variant ret = function->call(NULL,NULL,0,err,&state);
+ function=NULL; //cleaned up;
+ state.result=Variant();
+ return ret;
+}
+
+
+void GDFunctionState::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("resume:var","arg"),&GDFunctionState::resume,DEFVAL(Variant()));
+ ObjectTypeDB::bind_method(_MD("is_valid"),&GDFunctionState::is_valid);
+ ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&GDFunctionState::_signal_callback,MethodInfo("_signal_callback"));
+
+}
+
+GDFunctionState::GDFunctionState() {
+
+ function=NULL;
+}
+
+GDFunctionState::~GDFunctionState() {
+
+ if (function!=NULL) {
+ //never called, deinitialize stack
+ for(int i=0;i<state.stack_size;i++) {
+ Variant *v=(Variant*)&state.stack[sizeof(Variant)*i];
+ v->~Variant();
+ }
+ }
+}
+
+///////////////////////////
+
GDNativeClass::GDNativeClass(const StringName& p_name) {
name=p_name;
@@ -2183,6 +2375,8 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"or",
"export",
"assert",
+ "yield",
+ "static",
0};
diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h
index 3300ee77c8..62787cf6f8 100644
--- a/modules/gdscript/gd_script.h
+++ b/modules/gdscript/gd_script.h
@@ -37,6 +37,8 @@
class GDInstance;
class GDScript;
+
+
class GDFunction {
public:
@@ -58,6 +60,9 @@ public:
OPCODE_CALL_BUILT_IN,
OPCODE_CALL_SELF,
OPCODE_CALL_SELF_BASE,
+ OPCODE_YIELD,
+ OPCODE_YIELD_SIGNAL,
+ OPCODE_YIELD_RESUME,
OPCODE_JUMP,
OPCODE_JUMP_IF,
OPCODE_JUMP_IF_NOT,
@@ -132,6 +137,20 @@ friend class GDCompiler;
public:
+ struct CallState {
+
+ GDInstance *instance;
+ Vector<uint8_t> stack;
+ int stack_size;
+ Variant self;
+ uint32_t alloca_size;
+ GDScript *_class;
+ int ip;
+ int line;
+ int defarg;
+ Variant result;
+
+ };
_FORCE_INLINE_ bool is_static() const { return _static; }
@@ -150,12 +169,30 @@ public:
_FORCE_INLINE_ bool is_empty() const { return _code_size==0; }
int get_argument_count() const { return _argument_count; }
- Variant call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err);
+ Variant call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err,CallState *p_state=NULL);
GDFunction();
};
+class GDFunctionState : public Reference {
+
+ OBJ_TYPE(GDFunctionState,Reference);
+friend class GDFunction;
+ GDFunction *function;
+ GDFunction::CallState state;
+ Variant _signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error);
+protected:
+ static void _bind_methods();
+public:
+
+ bool is_valid() const;
+ Variant resume(const Variant& p_arg=Variant());
+ GDFunctionState();
+ ~GDFunctionState();
+};
+
+
class GDNativeClass : public Reference {
OBJ_TYPE(GDNativeClass,Reference);
diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp
index a92e2f22ea..f73c895d74 100644
--- a/modules/gdscript/gd_tokenizer.cpp
+++ b/modules/gdscript/gd_tokenizer.cpp
@@ -95,6 +95,7 @@ const char* GDTokenizer::token_names[TK_MAX]={
"var",
"preload",
"assert",
+"yield",
"'['",
"']'",
"'{'",
@@ -826,6 +827,7 @@ void GDTokenizerText::_advance() {
{TK_PR_VAR,"var"},
{TK_PR_PRELOAD,"preload"},
{TK_PR_ASSERT,"assert"},
+ {TK_PR_YIELD,"yield"},
{TK_PR_CONST,"const"},
//controlflow
{TK_CF_IF,"if"},
@@ -1006,7 +1008,7 @@ void GDTokenizerText::advance(int p_amount) {
//////////////////////////////////////////////////////////////////////////////////////////////////////
-#define BYTECODE_VERSION 1
+#define BYTECODE_VERSION 2
Error GDTokenizerBuffer::set_code_buffer(const Vector<uint8_t> & p_buffer) {
@@ -1016,8 +1018,8 @@ Error GDTokenizerBuffer::set_code_buffer(const Vector<uint8_t> & p_buffer) {
ERR_FAIL_COND_V( p_buffer.size()<24 || p_buffer[0]!='G' || p_buffer[1]!='D' || p_buffer[2]!='S' || p_buffer[3]!='C',ERR_INVALID_DATA);
int version = decode_uint32(&buf[4]);
- if (version>1) {
- ERR_EXPLAIN("Bytecode is too New!");
+ if (version>BYTECODE_VERSION) {
+ ERR_EXPLAIN("Bytecode is too New! Please use a newer engine version.");
ERR_FAIL_COND_V(version>BYTECODE_VERSION,ERR_INVALID_DATA);
}
int identifier_count = decode_uint32(&buf[8]);
diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h
index c517e07b89..1dd538867e 100644
--- a/modules/gdscript/gd_tokenizer.h
+++ b/modules/gdscript/gd_tokenizer.h
@@ -102,6 +102,7 @@ public:
TK_PR_VAR,
TK_PR_PRELOAD,
TK_PR_ASSERT,
+ TK_PR_YIELD,
TK_BRACKET_OPEN,
TK_BRACKET_CLOSE,
TK_CURLY_BRACKET_OPEN,
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index abb3d5a946..8b46773502 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -130,6 +130,7 @@ void register_gdscript_types() {
ResourceLoader::add_resource_format_loader(resource_loader_gd);
resource_saver_gd=memnew( ResourceFormatSaverGDScript );
ResourceSaver::add_resource_format_saver(resource_saver_gd);
+ ObjectTypeDB::register_virtual_type<GDFunctionState>();
#ifdef TOOLS_ENABLED