summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2014-02-24 09:53:33 -0300
committerJuan Linietsky <reduzio@gmail.com>2014-02-24 09:53:33 -0300
commit4b07eb8deb03ce8c7870d621cd03d04d45f4caaa (patch)
tree3314811f8a347ae60d951163c19d291b46381511 /modules
parent51609ffc04290f8bd1ecbd9cf1639c0cc6368fac (diff)
-moved script to modules
Diffstat (limited to 'modules')
-rw-r--r--modules/gdscript/SCsub7
-rw-r--r--modules/gdscript/config.py11
-rw-r--r--modules/gdscript/gd_compiler.cpp1531
-rw-r--r--modules/gdscript/gd_compiler.h181
-rw-r--r--modules/gdscript/gd_editor.cpp789
-rw-r--r--modules/gdscript/gd_functions.cpp1218
-rw-r--r--modules/gdscript/gd_functions.h103
-rw-r--r--modules/gdscript/gd_parser.cpp2469
-rw-r--r--modules/gdscript/gd_parser.h397
-rw-r--r--modules/gdscript/gd_pretty_print.cpp34
-rw-r--r--modules/gdscript/gd_pretty_print.h40
-rw-r--r--modules/gdscript/gd_script.cpp2222
-rw-r--r--modules/gdscript/gd_script.h473
-rw-r--r--modules/gdscript/gd_tokenizer.cpp973
-rw-r--r--modules/gdscript/gd_tokenizer.h181
-rw-r--r--modules/gdscript/register_types.cpp46
-rw-r--r--modules/gdscript/register_types.h30
-rw-r--r--modules/multiscript/SCsub7
-rw-r--r--modules/multiscript/config.py11
-rw-r--r--modules/multiscript/multi_script.cpp498
-rw-r--r--modules/multiscript/multi_script.h158
-rw-r--r--modules/multiscript/register_types.cpp32
-rw-r--r--modules/multiscript/register_types.h30
23 files changed, 11441 insertions, 0 deletions
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub
new file mode 100644
index 0000000000..d20da72b72
--- /dev/null
+++ b/modules/gdscript/SCsub
@@ -0,0 +1,7 @@
+Import('env')
+
+env.add_source_files(env.modules_sources,"*.cpp")
+
+Export('env')
+
+
diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py
new file mode 100644
index 0000000000..f9bd7da08d
--- /dev/null
+++ b/modules/gdscript/config.py
@@ -0,0 +1,11 @@
+
+
+def can_build(platform):
+ return True
+
+
+def configure(env):
+ pass
+
+
+
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp
new file mode 100644
index 0000000000..dd2834bf34
--- /dev/null
+++ b/modules/gdscript/gd_compiler.cpp
@@ -0,0 +1,1531 @@
+/*************************************************************************/
+/* gd_compiler.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_compiler.h"
+#include "gd_script.h"
+/* TODO:
+
+ *AND and OR need early abort
+ -Inheritance properly process (done?)
+ *create built in initializer and constructor
+ *assign operators
+ *build arrays and dictionaries
+ *call parent constructor
+ */
+
+
+void GDCompiler::_set_error(const String& p_error,const GDParser::Node *p_node) {
+
+ if (error!="")
+ return;
+
+ error=p_error;
+ err_line=p_node->line;
+ err_column=p_node->column;
+}
+
+bool GDCompiler::_create_unary_operator(CodeGen& codegen,const GDParser::OperatorNode *on,Variant::Operator op, int p_stack_level) {
+
+ ERR_FAIL_COND_V(on->arguments.size()!=1,false);
+
+ int src_address_a = _parse_expression(codegen,on->arguments[0],p_stack_level);
+ if (src_address_a<0)
+ return false;
+
+ 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)
+ return true;
+}
+
+bool GDCompiler::_create_binary_operator(CodeGen& codegen,const GDParser::OperatorNode *on,Variant::Operator op, int p_stack_level) {
+
+ ERR_FAIL_COND_V(on->arguments.size()!=2,false);
+
+
+ int src_address_a = _parse_expression(codegen,on->arguments[0],p_stack_level);
+ if (src_address_a<0)
+ return false;
+ if (src_address_a&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS)
+ p_stack_level++; //uses stack for return, increase stack
+
+ int src_address_b = _parse_expression(codegen,on->arguments[1],p_stack_level);
+ if (src_address_b<0)
+ return false;
+
+
+ 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(src_address_b); // argument 2 (unary only takes one parameter)
+ return true;
+}
+
+
+/*
+int GDCompiler::_parse_subexpression(CodeGen& codegen,const GDParser::Node *p_expression) {
+
+
+ int ret = _parse_expression(codegen,p_expression);
+ if (ret<0)
+ return ret;
+
+ if (ret&(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS)) {
+ codegen.stack_level++;
+ codegen.check_max_stack_level();
+ //stack was used, keep value
+ }
+
+ return ret;
+}
+*/
+
+int GDCompiler::_parse_assign_right_expression(CodeGen& codegen,const GDParser::OperatorNode *p_expression, int p_stack_level) {
+
+ Variant::Operator var_op=Variant::OP_MAX;
+
+ switch(p_expression->op) {
+
+ case GDParser::OperatorNode::OP_ASSIGN_ADD: var_op=Variant::OP_ADD; break;
+ case GDParser::OperatorNode::OP_ASSIGN_SUB: var_op=Variant::OP_SUBSTRACT; break;
+ case GDParser::OperatorNode::OP_ASSIGN_MUL: var_op=Variant::OP_MULTIPLY; break;
+ case GDParser::OperatorNode::OP_ASSIGN_DIV: var_op=Variant::OP_DIVIDE; break;
+ case GDParser::OperatorNode::OP_ASSIGN_MOD: var_op=Variant::OP_MODULE; break;
+ case GDParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT: var_op=Variant::OP_SHIFT_LEFT; break;
+ case GDParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT: var_op=Variant::OP_SHIFT_RIGHT; break;
+ case GDParser::OperatorNode::OP_ASSIGN_BIT_AND: var_op=Variant::OP_BIT_AND; break;
+ case GDParser::OperatorNode::OP_ASSIGN_BIT_OR: var_op=Variant::OP_BIT_OR; break;
+ case GDParser::OperatorNode::OP_ASSIGN_BIT_XOR: var_op=Variant::OP_BIT_XOR; break;
+ case GDParser::OperatorNode::OP_ASSIGN: {
+
+ //none
+ } break;
+ default: {
+
+ ERR_FAIL_V(-1);
+ }
+ }
+
+ if (var_op==Variant::OP_MAX) {
+
+ return _parse_expression(codegen,p_expression->arguments[1],p_stack_level);
+ }
+
+ if (!_create_binary_operator(codegen,p_expression,var_op,p_stack_level))
+ return -1;
+
+ int dst_addr=(p_stack_level)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
+ codegen.alloc_stack(p_stack_level);
+ return dst_addr;
+
+}
+
+int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expression, int p_stack_level,bool p_root) {
+
+
+ switch(p_expression->type) {
+ //should parse variable declaration and adjust stack accordingly...
+ case GDParser::Node::TYPE_IDENTIFIER: {
+ //return identifier
+ //wait, identifier could be a local variable or something else... careful here, must reference properly
+ //as stack may be more interesting to work with
+
+ //This could be made much simpler by just indexing "self", but done this way (with custom self-addressing modes) increases peformance a lot.
+
+ const GDParser::IdentifierNode *in = static_cast<const GDParser::IdentifierNode*>(p_expression);
+
+ StringName identifier = in->name;
+
+ // TRY STACK!
+ if (codegen.stack_identifiers.has(identifier)) {
+
+ int pos = codegen.stack_identifiers[identifier];
+ return pos|(GDFunction::ADDR_TYPE_STACK_VARIABLE<<GDFunction::ADDR_BITS);
+
+ }
+ //TRY ARGUMENTS!
+ if (!codegen.function_node || !codegen.function_node->_static) {
+
+ // TRY MEMBER VARIABLES!
+
+ //static function
+ if (codegen.script->member_indices.has(identifier)) {
+
+ int idx = codegen.script->member_indices[identifier];
+ return idx|(GDFunction::ADDR_TYPE_MEMBER<<GDFunction::ADDR_BITS); //argument (stack root)
+ }
+ }
+
+ //TRY CLASS CONSTANTS
+
+ GDScript *scr = codegen.script;
+ GDNativeClass *nc=NULL;
+ while(scr) {
+
+ if (scr->constants.has(identifier)) {
+
+ //int idx=scr->constants[identifier];
+ int idx = codegen.get_name_map_pos(identifier);
+ return idx|(GDFunction::ADDR_TYPE_CLASS_CONSTANT<<GDFunction::ADDR_BITS); //argument (stack root)
+ }
+ if (scr->native.is_valid())
+ nc=scr->native.ptr();
+ scr=scr->_base;
+ }
+
+ // CLASS C++ Integer Constant
+
+ if (nc) {
+
+ bool success=false;
+ int constant = ObjectTypeDB::get_integer_constant(nc->get_name(),identifier,&success);
+ if (success) {
+ Variant key=constant;
+ int idx;
+
+ if (!codegen.constant_map.has(key)) {
+
+ idx=codegen.constant_map.size();
+ codegen.constant_map[key]=idx;
+
+ } else {
+ idx=codegen.constant_map[key];
+ }
+
+ return idx|(GDFunction::ADDR_TYPE_LOCAL_CONSTANT<<GDFunction::ADDR_BITS); //make it a local constant (faster access)
+ }
+
+ }
+
+ 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|(GDFunction::ADDR_TYPE_LOCAL_CONSTANT<<GDFunction::ADDR_BITS); //make it a local constant (faster access)
+
+ }
+
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
+
+ int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
+ return idx|(GDFunction::ADDR_TYPE_GLOBAL<<GDFunction::ADDR_BITS); //argument (stack root)
+ }
+
+ //not found, error
+
+ _set_error("Identifier not found: "+String(identifier),p_expression);
+
+ return -1;
+
+
+ } break;
+ case GDParser::Node::TYPE_CONSTANT: {
+ //return constant
+ const GDParser::ConstantNode *cn = static_cast<const GDParser::ConstantNode*>(p_expression);
+
+
+ int idx;
+
+ if (!codegen.constant_map.has(cn->value)) {
+
+ idx=codegen.constant_map.size();
+ codegen.constant_map[cn->value]=idx;
+
+ } else {
+ idx=codegen.constant_map[cn->value];
+ }
+
+
+ return idx|(GDFunction::ADDR_TYPE_LOCAL_CONSTANT<<GDFunction::ADDR_BITS); //argument (stack root)
+
+ } break;
+ case GDParser::Node::TYPE_SELF: {
+ //return constant
+ if (codegen.function_node && codegen.function_node->_static) {
+ _set_error("'self' not present in static function!",p_expression);
+ return -1;
+ }
+ return (GDFunction::ADDR_TYPE_SELF<<GDFunction::ADDR_BITS);
+ } break;
+ case GDParser::Node::TYPE_ARRAY: {
+
+ const GDParser::ArrayNode *an = static_cast<const GDParser::ArrayNode*>(p_expression);
+ Vector<int> values;
+
+ int slevel=p_stack_level;
+
+ for(int i=0;i<an->elements.size();i++) {
+
+ int ret = _parse_expression(codegen,an->elements[i],slevel);
+ if (ret<0)
+ return ret;
+ if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+
+ values.push_back(ret);
+ }
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_CONSTRUCT_ARRAY);
+ codegen.opcodes.push_back(values.size());
+ for(int i=0;i<values.size();i++)
+ codegen.opcodes.push_back(values[i]);
+
+ int dst_addr=(p_stack_level)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
+ codegen.alloc_stack(p_stack_level);
+ return dst_addr;
+
+ } break;
+ case GDParser::Node::TYPE_DICTIONARY: {
+
+ const GDParser::DictionaryNode *dn = static_cast<const GDParser::DictionaryNode*>(p_expression);
+ Vector<int> values;
+
+ int slevel=p_stack_level;
+
+ for(int i=0;i<dn->elements.size();i++) {
+
+ int ret = _parse_expression(codegen,dn->elements[i].key,slevel);
+ if (ret<0)
+ return ret;
+ if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+
+ values.push_back(ret);
+
+ ret = _parse_expression(codegen,dn->elements[i].value,slevel);
+ if (ret<0)
+ return ret;
+ if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+
+ values.push_back(ret);
+ }
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_CONSTRUCT_DICTIONARY);
+ codegen.opcodes.push_back(dn->elements.size());
+ for(int i=0;i<values.size();i++)
+ codegen.opcodes.push_back(values[i]);
+
+ int dst_addr=(p_stack_level)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
+ codegen.alloc_stack(p_stack_level);
+ return dst_addr;
+
+ } break;
+ case GDParser::Node::TYPE_OPERATOR: {
+ //hell breaks loose
+
+ const GDParser::OperatorNode *on = static_cast<const GDParser::OperatorNode*>(p_expression);
+ switch(on->op) {
+
+
+ //call/constructor operator
+ case GDParser::OperatorNode::OP_PARENT_CALL: {
+
+
+ ERR_FAIL_COND_V(on->arguments.size()<1,-1);
+
+ const GDParser::IdentifierNode *in = (const GDParser::IdentifierNode *)on->arguments[0];
+
+
+ Vector<int> arguments;
+ int slevel = p_stack_level;
+ for(int i=1;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(GDFunction::OPCODE_CALL_SELF_BASE); // basic type constructor
+
+ codegen.opcodes.push_back(codegen.get_name_map_pos(in->name)); //instance
+ codegen.opcodes.push_back(arguments.size()); //argument count
+ codegen.alloc_call(arguments.size());
+ for(int i=0;i<arguments.size();i++)
+ codegen.opcodes.push_back(arguments[i]); //arguments
+
+ } break;
+ case GDParser::OperatorNode::OP_CALL: {
+
+ if (on->arguments[0]->type==GDParser::Node::TYPE_TYPE) {
+ //construct a basic type
+ ERR_FAIL_COND_V(on->arguments.size()<1,-1);
+
+ const GDParser::TypeNode *tn = (const GDParser::TypeNode *)on->arguments[0];
+ int vtype = tn->vtype;
+
+ Vector<int> arguments;
+ int slevel = p_stack_level;
+ for(int i=1;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(GDFunction::OPCODE_CONSTRUCT); // basic type constructor
+ codegen.opcodes.push_back(vtype); //instance
+ codegen.opcodes.push_back(arguments.size()); //argument count
+ codegen.alloc_call(arguments.size());
+ for(int i=0;i<arguments.size();i++)
+ codegen.opcodes.push_back(arguments[i]); //arguments
+
+ } else if (on->arguments[0]->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) {
+ //built in function
+
+ ERR_FAIL_COND_V(on->arguments.size()<1,-1);
+
+
+ Vector<int> arguments;
+ int slevel = p_stack_level;
+ for(int i=1;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);
+ }
+
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_CALL_BUILT_IN);
+ codegen.opcodes.push_back(static_cast<const GDParser::BuiltInFunctionNode*>(on->arguments[0])->function);
+ codegen.opcodes.push_back(on->arguments.size()-1);
+ codegen.alloc_call(on->arguments.size()-1);
+ for(int i=0;i<arguments.size();i++)
+ codegen.opcodes.push_back(arguments[i]);
+
+ } else {
+ //regular function
+ ERR_FAIL_COND_V(on->arguments.size()<2,-1);
+
+ const GDParser::Node *instance = on->arguments[0];
+
+ if (instance->type==GDParser::Node::TYPE_SELF) {
+ //room for optimization
+
+ }
+
+
+ Vector<int> arguments;
+ int slevel = p_stack_level;
+
+ for(int i=0;i<on->arguments.size();i++) {
+
+ int ret;
+
+ if (i==1) {
+
+ if (on->arguments[i]->type!=GDParser::Node::TYPE_IDENTIFIER) {
+ _set_error("Attempt to call a non-identifier.",on);
+ return -1;
+ }
+ GDParser::IdentifierNode *id = static_cast<GDParser::IdentifierNode*>(on->arguments[i]);
+ ret=codegen.get_name_map_pos(id->name);
+
+ } else {
+ 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);
+
+ }
+
+ codegen.opcodes.push_back(p_root?GDFunction::OPCODE_CALL:GDFunction::OPCODE_CALL_RETURN); // perform operator
+ codegen.opcodes.push_back(on->arguments.size()-2);
+ codegen.alloc_call(on->arguments.size()-2);
+ for(int i=0;i<arguments.size();i++)
+ codegen.opcodes.push_back(arguments[i]);
+ }
+ } break;
+ //indexing operator
+ case GDParser::OperatorNode::OP_INDEX:
+ case GDParser::OperatorNode::OP_INDEX_NAMED: {
+
+ ERR_FAIL_COND_V(on->arguments.size()!=2,-1);
+
+ int slevel = p_stack_level;
+ bool named=(on->op==GDParser::OperatorNode::OP_INDEX_NAMED);
+
+ int from = _parse_expression(codegen,on->arguments[0],slevel);
+ if (from<0)
+ return from;
+
+ int index;
+ if (named) {
+
+ index=codegen.get_name_map_pos(static_cast<GDParser::IdentifierNode*>(on->arguments[1])->name);
+
+ } else {
+
+ if (on->arguments[1]->type==GDParser::Node::TYPE_CONSTANT && static_cast<const GDParser::ConstantNode*>(on->arguments[1])->value.get_type()==Variant::STRING) {
+ //also, somehow, named (speed up anyway)
+ StringName name = static_cast<const GDParser::ConstantNode*>(on->arguments[1])->value;
+ index=codegen.get_name_map_pos(name);
+ named=true;
+
+ } else {
+ //regular indexing
+ if (from&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+
+ index = _parse_expression(codegen,on->arguments[1],slevel);
+ if (index<0)
+ return index;
+ }
+ }
+
+ codegen.opcodes.push_back(named?GDFunction::OPCODE_GET_NAMED:GDFunction::OPCODE_GET); // perform operator
+ codegen.opcodes.push_back(from); // argument 1
+ codegen.opcodes.push_back(index); // argument 2 (unary only takes one parameter)
+
+ } break;
+ case GDParser::OperatorNode::OP_AND: {
+
+ // AND operator with early out on failure
+
+ int res = _parse_expression(codegen,on->arguments[0],p_stack_level);
+ if (res<0)
+ return res;
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(res);
+ int jump_fail_pos=codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+
+ res = _parse_expression(codegen,on->arguments[1],p_stack_level);
+ if (res<0)
+ return res;
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(res);
+ int jump_fail_pos2=codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+
+ codegen.alloc_stack(p_stack_level); //it will be used..
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN_TRUE);
+ codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(codegen.opcodes.size()+3);
+ codegen.opcodes[jump_fail_pos]=codegen.opcodes.size();
+ codegen.opcodes[jump_fail_pos2]=codegen.opcodes.size();
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN_FALSE);
+ codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS;
+
+ } break;
+ case GDParser::OperatorNode::OP_OR: {
+
+ // OR operator with early out on success
+
+ int res = _parse_expression(codegen,on->arguments[0],p_stack_level);
+ if (res<0)
+ return res;
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF);
+ codegen.opcodes.push_back(res);
+ int jump_success_pos=codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+
+ res = _parse_expression(codegen,on->arguments[1],p_stack_level);
+ if (res<0)
+ return res;
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF);
+ codegen.opcodes.push_back(res);
+ int jump_success_pos2=codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+
+ codegen.alloc_stack(p_stack_level); //it will be used..
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN_FALSE);
+ codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(codegen.opcodes.size()+3);
+ codegen.opcodes[jump_success_pos]=codegen.opcodes.size();
+ codegen.opcodes[jump_success_pos2]=codegen.opcodes.size();
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN_TRUE);
+ codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS;
+
+ } break;
+ //unary operators
+ case GDParser::OperatorNode::OP_NEG: { if (!_create_unary_operator(codegen,on,Variant::OP_NEGATE,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_NOT: { if (!_create_unary_operator(codegen,on,Variant::OP_NOT,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_BIT_INVERT: { if (!_create_unary_operator(codegen,on,Variant::OP_BIT_NEGATE,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_PREINC: { } break; //?
+ case GDParser::OperatorNode::OP_PREDEC: { } break;
+ case GDParser::OperatorNode::OP_INC: { } break;
+ case GDParser::OperatorNode::OP_DEC: { } break;
+ //binary operators (in precedence order)
+ case GDParser::OperatorNode::OP_IN: { if (!_create_binary_operator(codegen,on,Variant::OP_IN,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_EQUAL: { if (!_create_binary_operator(codegen,on,Variant::OP_EQUAL,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_NOT_EQUAL: { if (!_create_binary_operator(codegen,on,Variant::OP_NOT_EQUAL,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_LESS: { if (!_create_binary_operator(codegen,on,Variant::OP_LESS,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_LESS_EQUAL: { if (!_create_binary_operator(codegen,on,Variant::OP_LESS_EQUAL,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_GREATER: { if (!_create_binary_operator(codegen,on,Variant::OP_GREATER,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_GREATER_EQUAL: { if (!_create_binary_operator(codegen,on,Variant::OP_GREATER_EQUAL,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_ADD: { if (!_create_binary_operator(codegen,on,Variant::OP_ADD,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_SUB: { if (!_create_binary_operator(codegen,on,Variant::OP_SUBSTRACT,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_MUL: { if (!_create_binary_operator(codegen,on,Variant::OP_MULTIPLY,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_DIV: { if (!_create_binary_operator(codegen,on,Variant::OP_DIVIDE,p_stack_level)) return -1;} break;
+ case GDParser::OperatorNode::OP_MOD: { if (!_create_binary_operator(codegen,on,Variant::OP_MODULE,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;
+ case GDParser::OperatorNode::OP_BIT_AND: { if (!_create_binary_operator(codegen,on,Variant::OP_BIT_AND,p_stack_level)) return -1;} break;
+ 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;
+ //assignment operators
+ case GDParser::OperatorNode::OP_ASSIGN_ADD:
+ case GDParser::OperatorNode::OP_ASSIGN_SUB:
+ case GDParser::OperatorNode::OP_ASSIGN_MUL:
+ case GDParser::OperatorNode::OP_ASSIGN_DIV:
+ case GDParser::OperatorNode::OP_ASSIGN_MOD:
+ case GDParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT:
+ case GDParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
+ case GDParser::OperatorNode::OP_ASSIGN_BIT_AND:
+ case GDParser::OperatorNode::OP_ASSIGN_BIT_OR:
+ case GDParser::OperatorNode::OP_ASSIGN_BIT_XOR:
+ case GDParser::OperatorNode::OP_ASSIGN: {
+
+ ERR_FAIL_COND_V(on->arguments.size()!=2,-1);
+
+
+ if (on->arguments[0]->type==GDParser::Node::TYPE_OPERATOR && (static_cast<GDParser::OperatorNode*>(on->arguments[0])->op==GDParser::OperatorNode::OP_INDEX || static_cast<GDParser::OperatorNode*>(on->arguments[0])->op==GDParser::OperatorNode::OP_INDEX_NAMED)) {
+ //SET (chained) MODE!!
+
+ int slevel=p_stack_level;
+
+ GDParser::OperatorNode* op = static_cast<GDParser::OperatorNode*>(on->arguments[0]);
+
+ /* Find chain of sets */
+
+ List<GDParser::OperatorNode*> chain;
+
+ {
+ //create get/set chain
+ GDParser::OperatorNode* n=op;
+ while(true) {
+
+ chain.push_back(n);
+ if (n->arguments[0]->type!=GDParser::Node::TYPE_OPERATOR)
+ break;
+ n = static_cast<GDParser::OperatorNode*>(n->arguments[0]);
+ if (n->op!=GDParser::OperatorNode::OP_INDEX && n->op!=GDParser::OperatorNode::OP_INDEX_NAMED)
+ break;
+ }
+ }
+
+ /* Chain of gets */
+
+ //get at (potential) root stack pos, so it can be returned
+ int prev_pos = _parse_expression(codegen,chain.back()->get()->arguments[0],slevel);
+ if (prev_pos<0)
+ return prev_pos;
+ int retval=prev_pos;
+
+ if (retval&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+
+
+ Vector<int> setchain;
+
+ for(List<GDParser::OperatorNode*>::Element *E=chain.back();E;E=E->prev()) {
+
+
+ if (E==chain.front()) //ignore first
+ break;
+
+ bool named = E->get()->op==GDParser::OperatorNode::OP_INDEX_NAMED;
+ int key_idx;
+
+ if (named) {
+
+ key_idx = codegen.get_name_map_pos(static_cast<const GDParser::IdentifierNode*>(E->get()->arguments[1])->name);
+ } else {
+
+ GDParser::Node *key = E->get()->arguments[1];
+ key_idx = _parse_expression(codegen,key,slevel);
+ if (retval&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+
+ }
+
+ if (key_idx<0)
+ return key_idx;
+
+ codegen.opcodes.push_back(named ? GDFunction::OPCODE_GET_NAMED : GDFunction::OPCODE_GET);
+ codegen.opcodes.push_back(prev_pos);
+ codegen.opcodes.push_back(key_idx);
+ slevel++;
+ codegen.alloc_stack(slevel);
+ int dst_pos = (GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS)|slevel;
+ codegen.opcodes.push_back(dst_pos);
+
+ //add in reverse order, since it will be reverted
+ setchain.push_back(dst_pos);
+ setchain.push_back(key_idx);
+ setchain.push_back(prev_pos);
+ setchain.push_back(named ? GDFunction::OPCODE_SET_NAMED : GDFunction::OPCODE_SET);
+
+ prev_pos=dst_pos;
+
+ }
+
+ setchain.invert();
+
+
+ int set_index;
+ bool named=false;
+
+
+ if (static_cast<const GDParser::OperatorNode*>(op)->op==GDParser::OperatorNode::OP_INDEX_NAMED) {
+
+
+ set_index=codegen.get_name_map_pos(static_cast<const GDParser::IdentifierNode*>(op->arguments[1])->name);
+ named=true;
+ } else {
+
+ set_index = _parse_expression(codegen,op->arguments[1],slevel+1);
+ named=false;
+ }
+
+
+ if (set_index<0)
+ return set_index;
+
+ if (set_index&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+
+
+ int set_value = _parse_assign_right_expression(codegen,on,slevel+1);
+ if (set_value<0)
+ return set_value;
+
+ codegen.opcodes.push_back(named?GDFunction::OPCODE_SET_NAMED:GDFunction::OPCODE_SET);
+ codegen.opcodes.push_back(prev_pos);
+ codegen.opcodes.push_back(set_index);
+ codegen.opcodes.push_back(set_value);
+
+ for(int i=0;i<setchain.size();i+=4) {
+
+
+ codegen.opcodes.push_back(setchain[i+0]);
+ codegen.opcodes.push_back(setchain[i+1]);
+ codegen.opcodes.push_back(setchain[i+2]);
+ codegen.opcodes.push_back(setchain[i+3]);
+ }
+
+ return retval;
+
+
+ } else {
+ //ASSIGNMENT MODE!!
+
+ int slevel = p_stack_level;
+
+ int dst_address_a = _parse_expression(codegen,on->arguments[0],slevel);
+ if (dst_address_a<0)
+ return -1;
+
+ if (dst_address_a&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) {
+ slevel++;
+ codegen.alloc_stack(slevel);
+ }
+
+ int src_address_b = _parse_assign_right_expression(codegen,on,slevel);
+ if (src_address_b<0)
+ return -1;
+
+
+
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN); // perform operator
+ codegen.opcodes.push_back(dst_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+ return dst_address_a; //if anything, returns wathever was assigned or correct stack position
+
+ }
+
+
+ } break;
+ case GDParser::OperatorNode::OP_EXTENDS: {
+
+ ERR_FAIL_COND_V(on->arguments.size()!=2,false);
+
+
+ int slevel = p_stack_level;
+
+ int src_address_a = _parse_expression(codegen,on->arguments[0],slevel);
+ if (src_address_a<0)
+ return -1;
+
+ if (src_address_a&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS)
+ slevel++; //uses stack for return, increase stack
+
+ int src_address_b = _parse_expression(codegen,on->arguments[1],slevel);
+ if (src_address_b<0)
+ return -1;
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_EXTENDS_TEST); // perform operator
+ codegen.opcodes.push_back(src_address_a); // argument 1
+ codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
+
+ } break;
+ default: {
+
+
+ ERR_EXPLAIN("Bug in bytecode compiler, unexpected operator #"+itos(on->op)+" in parse tree while parsing expression.");
+ ERR_FAIL_V(0); //unreachable code
+
+ } break;
+ }
+
+ int dst_addr=(p_stack_level)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
+ codegen.alloc_stack(p_stack_level);
+ return dst_addr;
+ } break;
+ //TYPE_TYPE,
+ default: {
+
+ ERR_EXPLAIN("Bug in bytecode compiler, unexpected node in parse tree while parsing expression.");
+ ERR_FAIL_V(-1); //unreachable code
+ } break;
+
+
+ }
+
+ ERR_FAIL_V(-1); //unreachable code
+}
+
+
+Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_block,int p_stack_level,int p_break_addr,int p_continue_addr) {
+
+ codegen.push_stack_identifiers();
+ int new_identifiers=0;
+ codegen.current_line=p_block->line;
+
+ for(int i=0;i<p_block->statements.size();i++) {
+
+ const GDParser::Node *s = p_block->statements[i];
+
+
+ switch(s->type) {
+ case GDParser::Node::TYPE_NEWLINE: {
+
+ const GDParser::NewLineNode *nl = static_cast<const GDParser::NewLineNode*>(s);
+ codegen.opcodes.push_back(GDFunction::OPCODE_LINE);
+ codegen.opcodes.push_back(nl->line);
+ codegen.current_line=nl->line;
+
+ } break;
+ case GDParser::Node::TYPE_CONTROL_FLOW: {
+ // try subblocks
+
+ const GDParser::ControlFlowNode *cf = static_cast<const GDParser::ControlFlowNode*>(s);
+
+ switch(cf->cf_type) {
+
+
+ case GDParser::ControlFlowNode::CF_IF: {
+
+#ifdef DEBUG_ENABLED
+ codegen.opcodes.push_back(GDFunction::OPCODE_LINE);
+ codegen.opcodes.push_back(cf->line);
+ codegen.current_line=cf->line;
+#endif
+ int ret = _parse_expression(codegen,cf->arguments[0],p_stack_level,false);
+ if (ret<0)
+ return ERR_PARSE_ERROR;
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(ret);
+ int else_addr=codegen.opcodes.size();
+ codegen.opcodes.push_back(0); //temporary
+
+ Error err = _parse_block(codegen,cf->body,p_stack_level,p_break_addr,p_continue_addr);
+ if (err)
+ return err;
+
+ if (cf->body_else) {
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
+ int end_addr=codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+ codegen.opcodes[else_addr]=codegen.opcodes.size();
+
+ Error err = _parse_block(codegen,cf->body_else,p_stack_level,p_break_addr,p_continue_addr);
+ if (err)
+ return err;
+
+ codegen.opcodes[end_addr]=codegen.opcodes.size();
+ } else {
+ //end without else
+ codegen.opcodes[else_addr]=codegen.opcodes.size();
+
+ }
+
+ } break;
+ case GDParser::ControlFlowNode::CF_FOR: {
+
+
+
+ int slevel=p_stack_level;
+ int iter_stack_pos=slevel;
+ int iterator_pos = (slevel++)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ int counter_pos = (slevel++)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ int container_pos = (slevel++)|(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.alloc_stack(slevel);
+
+ codegen.push_stack_identifiers();
+ codegen.add_stack_identifier(static_cast<const GDParser::IdentifierNode*>(cf->arguments[0])->name,iter_stack_pos);
+
+ int ret = _parse_expression(codegen,cf->arguments[1],slevel,false);
+ if (ret<0)
+ return ERR_COMPILATION_FAILED;
+
+ //assign container
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(container_pos);
+ codegen.opcodes.push_back(ret);
+
+ //begin loop
+ codegen.opcodes.push_back(GDFunction::OPCODE_ITERATE_BEGIN);
+ codegen.opcodes.push_back(counter_pos);
+ codegen.opcodes.push_back(container_pos);
+ codegen.opcodes.push_back(codegen.opcodes.size()+4);
+ codegen.opcodes.push_back(iterator_pos);
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); //skip code for next
+ codegen.opcodes.push_back(codegen.opcodes.size()+8);
+ //break loop
+ int break_pos=codegen.opcodes.size();
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP); //skip code for next
+ codegen.opcodes.push_back(0); //skip code for next
+ //next loop
+ int continue_pos=codegen.opcodes.size();
+ codegen.opcodes.push_back(GDFunction::OPCODE_ITERATE);
+ codegen.opcodes.push_back(counter_pos);
+ codegen.opcodes.push_back(container_pos);
+ codegen.opcodes.push_back(break_pos);
+ codegen.opcodes.push_back(iterator_pos);
+
+
+ Error err = _parse_block(codegen,cf->body,slevel,break_pos,continue_pos);
+ if (err)
+ return err;
+
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(continue_pos);
+ codegen.opcodes[break_pos+1]=codegen.opcodes.size();
+
+
+ codegen.pop_stack_identifiers();
+
+ } break;
+ case GDParser::ControlFlowNode::CF_WHILE: {
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(codegen.opcodes.size()+3);
+ int break_addr=codegen.opcodes.size();
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(0);
+ int continue_addr=codegen.opcodes.size();
+
+ int ret = _parse_expression(codegen,cf->arguments[0],p_stack_level,false);
+ if (ret<0)
+ return ERR_PARSE_ERROR;
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(ret);
+ codegen.opcodes.push_back(break_addr);
+ Error err = _parse_block(codegen,cf->body,p_stack_level,break_addr,continue_addr);
+ if (err)
+ return err;
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(continue_addr);
+
+ codegen.opcodes[break_addr+1]=codegen.opcodes.size();
+
+ } break;
+ case GDParser::ControlFlowNode::CF_SWITCH: {
+
+ } break;
+ case GDParser::ControlFlowNode::CF_BREAK: {
+
+ if (p_break_addr<0) {
+
+ _set_error("'break'' not within loop",cf);
+ return ERR_COMPILATION_FAILED;
+ }
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(p_break_addr);
+
+ } break;
+ case GDParser::ControlFlowNode::CF_CONTINUE: {
+
+ if (p_continue_addr<0) {
+
+ _set_error("'continue' not within loop",cf);
+ return ERR_COMPILATION_FAILED;
+ }
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
+ codegen.opcodes.push_back(p_continue_addr);
+
+ } break;
+ case GDParser::ControlFlowNode::CF_RETURN: {
+
+ int ret;
+
+ if (cf->arguments.size()) {
+
+ ret = _parse_expression(codegen,cf->arguments[0],p_stack_level,false);
+ if (ret<0)
+ return ERR_PARSE_ERROR;
+
+ } else {
+
+ ret=GDFunction::ADDR_TYPE_NIL << GDFunction::ADDR_BITS;
+ }
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_RETURN);
+ codegen.opcodes.push_back(ret);
+
+ } break;
+
+ }
+ } break;
+ case GDParser::Node::TYPE_ASSERT: {
+ // try subblocks
+
+ const GDParser::AssertNode *as = static_cast<const GDParser::AssertNode*>(s);
+
+ int ret = _parse_expression(codegen,as->condition,p_stack_level,false);
+ if (ret<0)
+ return ERR_PARSE_ERROR;
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSERT);
+ codegen.opcodes.push_back(ret);
+ } break;
+ case GDParser::Node::TYPE_LOCAL_VAR: {
+
+
+ const GDParser::LocalVarNode *lv = static_cast<const GDParser::LocalVarNode*>(s);
+
+ codegen.add_stack_identifier(lv->name,p_stack_level++);
+ codegen.alloc_stack(p_stack_level);
+ new_identifiers++;
+
+ } break;
+ default: {
+ //expression
+ int ret = _parse_expression(codegen,s,p_stack_level,true);
+ if (ret<0)
+ return ERR_PARSE_ERROR;
+ } break;
+
+ }
+
+ }
+ codegen.pop_stack_identifiers();
+ return OK;
+}
+
+
+Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *p_class,const GDParser::FunctionNode *p_func) {
+
+ Vector<int> bytecode;
+ CodeGen codegen;
+
+ codegen.class_node=p_class;
+ codegen.script=p_script;
+ codegen.function_node=p_func;
+ codegen.stack_max=0;
+ codegen.current_line=0;
+ codegen.call_max=0;
+ codegen.debug_stack=ScriptDebugger::get_singleton()!=NULL;
+
+ int stack_level=0;
+
+ if (p_func) {
+ for(int i=0;i<p_func->arguments.size();i++) {
+ int idx = i;
+ codegen.add_stack_identifier(p_func->arguments[i],i);
+ }
+ stack_level=p_func->arguments.size();
+ }
+
+ codegen.alloc_stack(stack_level);
+
+ /* Parse initializer -if applies- */
+
+ bool is_initializer=false || !p_func;
+
+ if (!p_func || String(p_func->name)=="_init") {
+ //parse initializer for class members
+ if (!p_func && p_class->extends_used && p_script->native.is_null()){
+
+ //call implicit parent constructor
+ codegen.opcodes.push_back(GDFunction::OPCODE_CALL_SELF_BASE);
+ codegen.opcodes.push_back(codegen.get_name_map_pos("_init"));
+ codegen.opcodes.push_back(0);
+ codegen.opcodes.push_back((GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS)|0);
+
+ }
+ Error err = _parse_block(codegen,p_class->initializer,stack_level);
+ if (err)
+ return err;
+ is_initializer=true;
+
+ }
+
+ /* Parse default argument code -if applies- */
+
+ Vector<int> defarg_addr;
+ StringName func_name;
+
+ if (p_func) {
+ if (p_func->default_values.size()) {
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_TO_DEF_ARGUMENT);
+ defarg_addr.push_back(codegen.opcodes.size());
+ for(int i=0;i<p_func->default_values.size();i++) {
+
+ _parse_expression(codegen,p_func->default_values[i],stack_level,true);
+ defarg_addr.push_back(codegen.opcodes.size());
+ }
+
+
+ defarg_addr.invert();
+ }
+
+
+
+ Error err = _parse_block(codegen,p_func->body,stack_level);
+ if (err)
+ return err;
+
+ func_name=p_func->name;
+ } else {
+ func_name="_init";
+ }
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_END);
+
+ GDFunction *gdfunc=NULL;
+
+ //if (String(p_func->name)=="") { //initializer func
+ // gdfunc = &p_script->initializer;
+
+ //} else { //regular func
+ p_script->member_functions[func_name]=GDFunction();
+ gdfunc = &p_script->member_functions[func_name];
+ //}
+
+ if (p_func)
+ gdfunc->_static=p_func->_static;
+
+ //constants
+ if (codegen.constant_map.size()) {
+ gdfunc->_constant_count=codegen.constant_map.size();
+ gdfunc->constants.resize(codegen.constant_map.size());
+ gdfunc->_constants_ptr=&gdfunc->constants[0];
+ const Variant *K=NULL;
+ while((K=codegen.constant_map.next(K))) {
+ int idx = codegen.constant_map[*K];
+ gdfunc->constants[idx]=*K;
+ }
+ } else {
+
+ gdfunc->_constants_ptr=NULL;
+ gdfunc->_constant_count=0;
+ }
+ //global names
+ if (codegen.name_map.size()) {
+
+ gdfunc->global_names.resize(codegen.name_map.size());
+ gdfunc->_global_names_ptr = &gdfunc->global_names[0];
+ for(Map<StringName,int>::Element *E=codegen.name_map.front();E;E=E->next()) {
+
+ gdfunc->global_names[E->get()]=E->key();
+ }
+ gdfunc->_global_names_count=gdfunc->global_names.size();
+
+ } else {
+ gdfunc->_global_names_ptr = NULL;
+ gdfunc->_global_names_count =0;
+ }
+
+
+ if (codegen.opcodes.size()) {
+
+ gdfunc->code=codegen.opcodes;
+ gdfunc->_code_ptr=&gdfunc->code[0];
+ gdfunc->_code_size=codegen.opcodes.size();
+
+ } else {
+
+ gdfunc->_code_ptr=NULL;
+ gdfunc->_code_size=0;
+ }
+
+ if (defarg_addr.size()) {
+
+ gdfunc->default_arguments=defarg_addr;
+ gdfunc->_default_arg_count=defarg_addr.size();
+ gdfunc->_default_arg_ptr=&gdfunc->default_arguments[0];
+ } else {
+ gdfunc->_default_arg_count=0;
+ gdfunc->_default_arg_ptr=NULL;
+ }
+
+ gdfunc->_argument_count=p_func ? p_func->arguments.size() : 0;
+ gdfunc->_stack_size=codegen.stack_max;
+ gdfunc->_call_size=codegen.call_max;
+ gdfunc->name=func_name;
+ gdfunc->_script=p_script;
+ gdfunc->source=source;
+ if (p_func) {
+ gdfunc->_initial_line=p_func->line;
+ } else {
+ gdfunc->_initial_line=0;
+ }
+
+ if (codegen.debug_stack)
+ gdfunc->stack_debug=codegen.stack_debug;
+
+ if (is_initializer)
+ p_script->initializer=gdfunc;
+
+
+ return OK;
+}
+
+
+
+Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDParser::ClassNode *p_class) {
+
+
+ p_script->native=Ref<GDNativeClass>();
+ p_script->base=Ref<GDScript>();
+ p_script->_base=NULL;
+ p_script->members.clear();
+ p_script->constants.clear();
+ p_script->member_functions.clear();
+ p_script->member_indices.clear();
+ p_script->member_info.clear();
+ p_script->initializer=NULL;
+ p_script->subclasses.clear();
+ p_script->_owner=p_owner;
+ p_script->tool=p_class->tool;
+ p_script->name=p_class->name;
+
+
+ int index_from=0;
+
+ if (p_class->extends_used) {
+ //do inheritance
+ String path = p_class->extends_file;
+
+ Ref<GDScript> script;
+ Ref<GDNativeClass> native;
+
+ if (path!="") {
+ //path (and optionally subclasses)
+
+ script = ResourceLoader::load(path);
+ if (script.is_null()) {
+ _set_error("Could not load base class: "+path,p_class);
+ return ERR_FILE_NOT_FOUND;
+ }
+
+ 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)) {
+
+ script=script->subclasses[sub];
+ } 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;
+ }
+ p=p->_owner;
+ }
+
+ if (base_class.is_valid()) {
+
+ for(int i=1;i<p_class->extends_class.size();i++) {
+
+ String subclass=p_class->extends_class[i];
+
+ if (base_class->subclasses.has(subclass)) {
+
+ base_class=base_class->subclasses[subclass];
+ } else {
+
+ _set_error("Could not find subclass: "+subclass,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->member_indices=script->member_indices;
+
+ } else if (native.is_valid()) {
+
+ p_script->native=native;
+ } else {
+
+ _set_error("Could not determine inheritance",p_class);
+ return ERR_FILE_NOT_FOUND;
+ }
+
+
+ }
+
+
+ for(int i=0;i<p_class->variables.size();i++) {
+
+ StringName name = p_class->variables[i].identifier;
+ if (p_script->member_indices.has(name)) {
+ _set_error("Member '"+name+"' already exists (in current or parent class)",p_class);
+ return ERR_ALREADY_EXISTS;
+ }
+
+ if (p_class->variables[i]._export.type!=Variant::NIL) {
+
+ p_script->member_info[name]=p_class->variables[i]._export;
+#ifdef TOOLS_ENABLED
+ if (p_class->variables[i].default_value.get_type()!=Variant::NIL) {
+
+ p_script->member_default_values[name]=p_class->variables[i].default_value;
+ }
+#endif
+ }
+
+ int new_idx = p_script->member_indices.size();
+ p_script->member_indices[name]=new_idx;
+ p_script->members.insert(name);
+
+ }
+
+ for(int i=0;i<p_class->constant_expressions.size();i++) {
+
+ StringName name = p_class->constant_expressions[i].identifier;
+ ERR_CONTINUE( p_class->constant_expressions[i].expression->type!=GDParser::Node::TYPE_CONSTANT );
+
+ GDParser::ConstantNode *constant = static_cast<GDParser::ConstantNode*>(p_class->constant_expressions[i].expression);
+
+ p_script->constants.insert(name,constant->value);
+ //p_script->constants[constant->value].make_const();
+ }
+
+
+ //parse sub-classes
+
+ for(int i=0;i<p_class->subclasses.size();i++) {
+ StringName name = p_class->subclasses[i]->name;
+
+ Ref<GDScript> subclass = memnew( GDScript );
+
+ Error err = _parse_class(subclass.ptr(),p_script,p_class->subclasses[i]);
+ if (err)
+ return err;
+ p_script->subclasses.insert(name,subclass);
+
+ }
+
+
+ //parse methods
+
+ bool has_initializer=false;
+ for(int i=0;i<p_class->functions.size();i++) {
+
+ if (!has_initializer && p_class->functions[i]->name=="_init")
+ has_initializer=true;
+ Error err = _parse_function(p_script,p_class,p_class->functions[i]);
+ if (err)
+ return err;
+ }
+
+ //parse static methods
+
+ for(int i=0;i<p_class->static_functions.size();i++) {
+
+ Error err = _parse_function(p_script,p_class,p_class->static_functions[i]);
+ if (err)
+ return err;
+ }
+
+
+ if (!has_initializer) {
+ //create a constructor
+ Error err = _parse_function(p_script,p_class,NULL);
+ if (err)
+ return err;
+ }
+
+ return OK;
+}
+
+Error GDCompiler::compile(const GDParser *p_parser,GDScript *p_script) {
+
+ err_line=-1;
+ err_column=-1;
+ error="";
+ parser=p_parser;
+ const GDParser::Node* root = parser->get_parse_tree();
+ ERR_FAIL_COND_V(root->type!=GDParser::Node::TYPE_CLASS,ERR_INVALID_DATA);
+
+ source=p_script->get_path();
+
+
+
+ Error err = _parse_class(p_script,NULL,static_cast<const GDParser::ClassNode*>(root));
+
+ if (err)
+ return err;
+
+ return OK;
+
+}
+
+String GDCompiler::get_error() const {
+
+ return error;
+}
+int GDCompiler::get_error_line() const{
+
+ return err_line;
+}
+int GDCompiler::get_error_column() const{
+
+ return err_column;
+}
+
+GDCompiler::GDCompiler()
+{
+}
+
+
diff --git a/modules/gdscript/gd_compiler.h b/modules/gdscript/gd_compiler.h
new file mode 100644
index 0000000000..cda221dab0
--- /dev/null
+++ b/modules/gdscript/gd_compiler.h
@@ -0,0 +1,181 @@
+/*************************************************************************/
+/* gd_compiler.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_COMPILER_H
+#define GD_COMPILER_H
+
+#include "gd_parser.h"
+#include "gd_script.h"
+
+
+class GDCompiler {
+
+ const GDParser *parser;
+ struct CodeGen {
+
+
+ GDScript *script;
+ const GDParser::ClassNode *class_node;
+ const GDParser::FunctionNode *function_node;
+
+
+ bool debug_stack;
+
+
+ List< Map<StringName,int> > stack_id_stack;
+ Map<StringName,int> stack_identifiers;
+
+ List<GDFunction::StackDebug> stack_debug;
+ List< Map<StringName,int> > block_identifier_stack;
+ Map<StringName,int> block_identifiers;
+
+
+ void add_stack_identifier(const StringName& p_id,int p_stackpos) {
+
+ stack_identifiers[p_id]=p_stackpos;
+ if (debug_stack) {
+
+ block_identifiers[p_id]=p_stackpos;
+ GDFunction::StackDebug sd;
+ sd.added=true;
+ sd.line=current_line;
+ sd.identifier=p_id;
+ sd.pos=p_stackpos;
+ stack_debug.push_back(sd);
+ }
+ }
+
+ void push_stack_identifiers() {
+
+ stack_id_stack.push_back( stack_identifiers );
+ if (debug_stack) {
+
+ block_identifier_stack.push_back(block_identifiers);
+ block_identifiers.clear();
+ }
+ }
+
+ void pop_stack_identifiers() {
+
+ stack_identifiers = stack_id_stack.back()->get();
+ stack_id_stack.pop_back();
+
+ if (debug_stack) {
+ for (Map<StringName,int>::Element *E=block_identifiers.front();E;E=E->next()) {
+
+ GDFunction::StackDebug sd;
+ sd.added=false;
+ sd.identifier=E->key();
+ sd.line=current_line;
+ sd.pos=E->get();
+ stack_debug.push_back(sd);
+ }
+ block_identifiers=block_identifier_stack.back()->get();
+ block_identifier_stack.pop_back();
+ }
+
+ }
+
+
+ // int get_identifier_pos(const StringName& p_dentifier) const;
+ HashMap<Variant,int,VariantHasher> constant_map;
+ Map<StringName,int> name_map;
+
+ int get_name_map_pos(const StringName& p_identifier) {
+
+ int ret;
+ if (!name_map.has(p_identifier)) {
+ ret=name_map.size();
+ name_map[p_identifier]=ret;
+ } else {
+ ret=name_map[p_identifier];
+ }
+ return ret;
+ }
+
+
+
+ int get_constant_pos(const Variant& p_constant) {
+
+
+ if (constant_map.has(p_constant))
+ return constant_map[p_constant];
+ int pos = constant_map.size();
+ constant_map[p_constant]=pos;
+ return pos;
+ }
+
+ Vector<int> opcodes;
+ void alloc_stack(int p_level) { if (p_level >= stack_max) stack_max=p_level+1; }
+ void alloc_call(int p_params) { if (p_params >= call_max) call_max=p_params; }
+
+ int current_line;
+ int stack_max;
+ int call_max;
+ };
+
+#if 0
+ void _create_index(const GDParser::OperatorNode *on);
+ void _create_call(const GDParser::OperatorNode *on);
+
+
+ int _parse_expression(const GDParser::Node *p_expr,CodeGen& codegen);
+ void _parse_block(GDParser::BlockNode *p_block);
+ void _parse_function(GDParser::FunctionNode *p_func);
+ Ref<GDScript> _parse_class(GDParser::ClassNode *p_class);
+#endif
+
+ void _set_error(const String& p_error,const GDParser::Node *p_node);
+
+ bool _create_unary_operator(CodeGen& codegen,const GDParser::OperatorNode *on,Variant::Operator op, int p_stack_level);
+ bool _create_binary_operator(CodeGen& codegen,const GDParser::OperatorNode *on,Variant::Operator op, int p_stack_level);
+
+ //int _parse_subexpression(CodeGen& codegen,const GDParser::BlockNode *p_block,const GDParser::Node *p_expression);
+ 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);
+ 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_class(GDScript *p_script,GDScript *p_owner,const GDParser::ClassNode *p_class);
+ int err_line;
+ int err_column;
+ StringName source;
+ String error;
+
+public:
+
+ Error compile(const GDParser *p_parser,GDScript *p_script);
+
+ String get_error() const;
+ int get_error_line() const;
+ int get_error_column() const;
+
+ GDCompiler();
+};
+
+
+#endif // COMPILER_H
diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp
new file mode 100644
index 0000000000..c10cadf83f
--- /dev/null
+++ b/modules/gdscript/gd_editor.cpp
@@ -0,0 +1,789 @@
+/*************************************************************************/
+/* gd_editor.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_script.h"
+#include "gd_compiler.h"
+
+
+void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
+
+ p_delimiters->push_back("#");
+
+}
+void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
+
+ p_delimiters->push_back("\" \"");
+ p_delimiters->push_back("' '");
+
+
+}
+String GDScriptLanguage::get_template(const String& p_class_name, const String& p_base_class_name) const {
+
+ String _template = String()+
+ "\nextends %BASE%\n\n"+
+ "# member variables here, example:\n"+
+ "# var a=2\n"+
+ "# var b=\"textvar\"\n\n"+
+ "func _ready():\n"+
+ "\t# Initalization here\n"+
+ "\tpass\n"+
+ "\n"+
+ "\n";
+
+ return _template.replace("%BASE%",p_base_class_name);
+}
+
+
+
+
+bool GDScriptLanguage::validate(const String& p_script, int &r_line_error,int &r_col_error,String& r_test_error, const String& p_path,List<String> *r_functions) const {
+
+ GDParser parser;
+
+ Error err = parser.parse(p_script,p_path.get_base_dir());
+ if (err) {
+ r_line_error=parser.get_error_line();
+ r_col_error=parser.get_error_column();
+ r_test_error=parser.get_error();
+ return false;
+ } else {
+
+ const GDParser::Node *root = parser.get_parse_tree();
+ ERR_FAIL_COND_V(root->type!=GDParser::Node::TYPE_CLASS,false);
+
+ const GDParser::ClassNode *cl = static_cast<const GDParser::ClassNode*>(root);
+ Map<int,String> funcs;
+ for(int i=0;i<cl->functions.size();i++) {
+
+ funcs[cl->functions[i]->line]=cl->functions[i]->name;
+ }
+
+ for(int i=0;i<cl->static_functions.size();i++) {
+
+ funcs[cl->static_functions[i]->line]=cl->static_functions[i]->name;
+ }
+
+ for (Map<int,String>::Element *E=funcs.front();E;E=E->next()) {
+
+ r_functions->push_back(E->get()+":"+itos(E->key()));
+ }
+
+
+ }
+
+ return true;
+}
+
+bool GDScriptLanguage::has_named_classes() const {
+
+ return false;
+}
+
+int GDScriptLanguage::find_function(const String& p_function,const String& p_code) const {
+
+ GDTokenizer tokenizer;
+ tokenizer.set_code(p_code);
+ int indent=0;
+ while(tokenizer.get_token()!=GDTokenizer::TK_EOF && tokenizer.get_token()!=GDTokenizer::TK_ERROR) {
+
+ if (tokenizer.get_token()==GDTokenizer::TK_NEWLINE) {
+ indent=tokenizer.get_token_line_indent();
+ }
+ if (indent==0 && tokenizer.get_token()==GDTokenizer::TK_PR_FUNCTION && tokenizer.get_token(1)==GDTokenizer::TK_IDENTIFIER) {
+
+ String identifier = tokenizer.get_token_identifier(1);
+ if (identifier==p_function) {
+ return tokenizer.get_token_line();
+ }
+ }
+ tokenizer.advance();
+ }
+ return -1;
+}
+
+Script *GDScriptLanguage::create_script() const {
+
+ return memnew( GDScript );
+}
+
+/* DEBUGGER FUNCTIONS */
+
+
+bool GDScriptLanguage::debug_break_parse(const String& p_file, int p_line,const String& p_error) {
+ //break because of parse error
+
+ if (ScriptDebugger::get_singleton() && Thread::get_caller_ID()==Thread::get_main_ID()) {
+
+ _debug_parse_err_line=p_line;
+ _debug_parse_err_file=p_file;
+ _debug_error=p_error;
+ ScriptDebugger::get_singleton()->debug(this,false);
+ return true;
+ } else {
+ return false;
+ }
+
+}
+
+bool GDScriptLanguage::debug_break(const String& p_error,bool p_allow_continue) {
+
+ if (ScriptDebugger::get_singleton() && Thread::get_caller_ID()==Thread::get_main_ID()) {
+
+ _debug_parse_err_line=-1;
+ _debug_parse_err_file="";
+ _debug_error=p_error;
+ ScriptDebugger::get_singleton()->debug(this,p_allow_continue);
+ return true;
+ } else {
+ return false;
+ }
+
+}
+
+String GDScriptLanguage::debug_get_error() const {
+
+ return _debug_error;
+}
+
+int GDScriptLanguage::debug_get_stack_level_count() const {
+
+ if (_debug_parse_err_line>=0)
+ return 1;
+
+
+ return _debug_call_stack_pos;
+}
+int GDScriptLanguage::debug_get_stack_level_line(int p_level) const {
+
+ if (_debug_parse_err_line>=0)
+ return _debug_parse_err_line;
+
+ ERR_FAIL_INDEX_V(p_level,_debug_call_stack_pos,-1);
+
+ int l = _debug_call_stack_pos - p_level -1;
+
+ return *(_call_stack[l].line);
+
+}
+String GDScriptLanguage::debug_get_stack_level_function(int p_level) const {
+
+ if (_debug_parse_err_line>=0)
+ return "";
+
+ ERR_FAIL_INDEX_V(p_level,_debug_call_stack_pos,"");
+ int l = _debug_call_stack_pos - p_level -1;
+ return _call_stack[l].function->get_name();
+}
+String GDScriptLanguage::debug_get_stack_level_source(int p_level) const {
+
+ if (_debug_parse_err_line>=0)
+ return _debug_parse_err_file;
+
+ ERR_FAIL_INDEX_V(p_level,_debug_call_stack_pos,"");
+ int l = _debug_call_stack_pos - p_level -1;
+ return _call_stack[l].function->get_script()->get_path();
+
+}
+void GDScriptLanguage::debug_get_stack_level_locals(int p_level,List<String> *p_locals, List<Variant> *p_values, int p_max_subitems,int p_max_depth) {
+
+ if (_debug_parse_err_line>=0)
+ return;
+
+ ERR_FAIL_INDEX(p_level,_debug_call_stack_pos);
+ int l = _debug_call_stack_pos - p_level -1;
+
+ GDFunction *f = _call_stack[l].function;
+
+ List<Pair<StringName,int> > locals;
+
+ f->debug_get_stack_member_state(*_call_stack[l].line,&locals);
+ for( List<Pair<StringName,int> >::Element *E = locals.front();E;E=E->next() ) {
+
+ p_locals->push_back(E->get().first);
+ p_values->push_back(_call_stack[l].stack[E->get().second]);
+ }
+
+}
+void GDScriptLanguage::debug_get_stack_level_members(int p_level,List<String> *p_members, List<Variant> *p_values, int p_max_subitems,int p_max_depth) {
+
+ if (_debug_parse_err_line>=0)
+ return;
+
+ ERR_FAIL_INDEX(p_level,_debug_call_stack_pos);
+ int l = _debug_call_stack_pos - p_level -1;
+
+
+ GDInstance *instance = _call_stack[l].instance;
+
+ if (!instance)
+ return;
+
+ Ref<GDScript> script = instance->get_script();
+ ERR_FAIL_COND( script.is_null() );
+
+
+ const Map<StringName,int>& mi = script->debug_get_member_indices();
+
+ for(const Map<StringName,int>::Element *E=mi.front();E;E=E->next()) {
+
+ p_members->push_back(E->key());
+ p_values->push_back( instance->debug_get_member_by_index(E->get()));
+ }
+
+}
+void GDScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems,int p_max_depth) {
+
+ //no globals are really reachable in gdscript
+}
+String GDScriptLanguage::debug_parse_stack_level_expression(int p_level,const String& p_expression,int p_max_subitems,int p_max_depth) {
+
+ if (_debug_parse_err_line>=0)
+ return "";
+ return "";
+}
+
+void GDScriptLanguage::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("gd");
+}
+
+
+void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {
+
+
+ for(int i=0;i<GDFunctions::FUNC_MAX;i++) {
+
+ p_functions->push_back(GDFunctions::get_info(GDFunctions::Function(i)));
+ }
+}
+
+void GDScriptLanguage::get_public_constants(List<Pair<String,Variant> > *p_constants) const {
+
+ Pair<String,Variant> pi;
+ pi.first="PI";
+ pi.second=Math_PI;
+ p_constants->push_back(pi);
+}
+
+String GDScriptLanguage::make_function(const String& p_class,const String& p_name,const StringArray& p_args) const {
+
+ String s="func "+p_name+"(";
+ if (p_args.size()) {
+ s+=" ";
+ for(int i=0;i<p_args.size();i++) {
+ if (i>0)
+ s+=", ";
+ s+=p_args[i];
+ }
+ s+=" ";
+ }
+ s+="):\n\tpass # replace with function body\n";
+
+ return s;
+
+}
+
+static void _parse_native_symbols(const StringName& p_native,bool p_static,List<String>* r_options) {
+
+ if (!p_static) {
+ List<MethodInfo> methods;
+ ObjectTypeDB::get_method_list(p_native,&methods);
+ for(List<MethodInfo>::Element *E=methods.front();E;E=E->next()) {
+ if (!E->get().name.begins_with("_")) {
+ r_options->push_back(E->get().name);
+ }
+ }
+ }
+
+ List<String> constants;
+ ObjectTypeDB::get_integer_constant_list(p_native,&constants);
+
+ for(List<String>::Element *E=constants.front();E;E=E->next()) {
+ r_options->push_back(E->get());
+ }
+
+}
+
+
+static bool _parse_script_symbols(const Ref<GDScript>& p_script,bool p_static,List<String>* r_options,List<String>::Element *p_indices);
+
+
+static bool _parse_completion_variant(const Variant& p_var,List<String>* r_options,List<String>::Element *p_indices) {
+
+ if (p_indices) {
+
+ bool ok;
+ Variant si = p_var.get(p_indices->get(),&ok);
+ if (!ok)
+ return false;
+ return _parse_completion_variant(si,r_options,p_indices->next());
+ } else {
+
+ switch(p_var.get_type()) {
+
+
+ case Variant::DICTIONARY: {
+
+ Dictionary d=p_var;
+ List<Variant> vl;
+ d.get_key_list(&vl);
+ for (List<Variant>::Element *E=vl.front();E;E=E->next()) {
+
+ if (E->get().get_type()==Variant::STRING)
+ r_options->push_back(E->get());
+ }
+
+
+ List<MethodInfo> ml;
+ p_var.get_method_list(&ml);
+ for(List<MethodInfo>::Element *E=ml.front();E;E=E->next()) {
+ r_options->push_back(E->get().name);
+ }
+
+ } break;
+ case Variant::OBJECT: {
+
+
+ Object *o=p_var;
+ if (o) {
+ print_line("OBJECT: "+o->get_type());
+ if (p_var.is_ref() && o->cast_to<GDScript>()) {
+
+ Ref<GDScript> gds = p_var;
+ _parse_script_symbols(gds,true,r_options,NULL);
+ } else if (o->is_type("GDNativeClass")){
+
+ GDNativeClass *gnc = o->cast_to<GDNativeClass>();
+ _parse_native_symbols(gnc->get_name(),false,r_options);
+ } else {
+
+ print_line("REGULAR BLEND");
+ _parse_native_symbols(o->get_type(),false,r_options);
+ }
+ }
+
+ } break;
+ default: {
+
+ List<PropertyInfo> pi;
+ p_var.get_property_list(&pi);
+ for(List<PropertyInfo>::Element *E=pi.front();E;E=E->next()) {
+ r_options->push_back(E->get().name);
+ }
+ List<StringName> cl;
+
+ p_var.get_numeric_constants_for_type(p_var.get_type(),&cl);
+ for(List<StringName>::Element *E=cl.front();E;E=E->next()) {
+ r_options->push_back(E->get());
+ }
+
+ List<MethodInfo> ml;
+ p_var.get_method_list(&ml);
+ for(List<MethodInfo>::Element *E=ml.front();E;E=E->next()) {
+ r_options->push_back(E->get().name);
+ }
+
+ } break;
+ }
+
+ return true;
+ }
+
+
+}
+
+
+static void _parse_expression_node(const GDParser::Node *p_node,List<String>* r_options,List<String>::Element *p_indices) {
+
+
+
+ if (p_node->type==GDParser::Node::TYPE_CONSTANT) {
+
+ const GDParser::ConstantNode *cn=static_cast<const GDParser::ConstantNode *>(p_node);
+ _parse_completion_variant(cn->value,r_options,p_indices?p_indices->next():NULL);
+ } else if (p_node->type==GDParser::Node::TYPE_DICTIONARY) {
+
+ const GDParser::DictionaryNode *dn=static_cast<const GDParser::DictionaryNode*>(p_node);
+ for(int i=0;i<dn->elements.size();i++) {
+
+ if (dn->elements[i].key->type==GDParser::Node::TYPE_CONSTANT) {
+
+ const GDParser::ConstantNode *cn=static_cast<const GDParser::ConstantNode *>(dn->elements[i].key);
+ if (cn->value.get_type()==Variant::STRING) {
+
+ String str=cn->value;
+ if (p_indices) {
+
+ if (str==p_indices->get()) {
+ _parse_expression_node(dn->elements[i].value,r_options,p_indices->next());
+ return;
+ }
+
+ } else {
+ r_options->push_back(str);
+ }
+ }
+ }
+ }
+ }
+}
+
+static bool _parse_completion_block(const GDParser::BlockNode *p_block,int p_line,List<String>* r_options,List<String>::Element *p_indices) {
+
+ for(int i=0;i<p_block->sub_blocks.size();i++) {
+ //parse inner first
+ if (p_line>=p_block->sub_blocks[i]->line && (p_line<=p_block->sub_blocks[i]->end_line || p_block->sub_blocks[i]->end_line==-1)) {
+ if (_parse_completion_block(p_block->sub_blocks[i],p_line,r_options,p_indices))
+ return true;
+ }
+ }
+
+ if (p_indices) {
+
+ //parse indices in expressions :|
+ for (int i=0;i<p_block->statements.size();i++) {
+
+ if (p_block->statements[i]->line>p_line)
+ break;
+
+ if (p_block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) {
+
+ const GDParser::LocalVarNode *lv=static_cast<const GDParser::LocalVarNode *>(p_block->statements[i]);
+ if (lv->assign && String(lv->name)==p_indices->get()) {
+
+ _parse_expression_node(lv->assign,r_options,p_indices->next());
+ return true;
+ }
+ }
+ }
+
+ } else {
+ for(int i=0;i<p_block->variables.size();i++) {
+ //parse variables second
+ if (p_line>=p_block->variable_lines[i]) {
+ r_options->push_back(p_block->variables[i]);
+ }
+ else break;
+
+ }
+ }
+
+ return false;
+}
+
+
+static bool _parse_script_symbols(const Ref<GDScript>& p_script,bool p_static,List<String>* r_options,List<String>::Element *p_indices) {
+
+ //for (Map<StringName,Ref<GDScript> >::Element ?
+
+ if (!p_static && !p_indices) {
+ for(const Set<StringName>::Element *E=p_script->get_members().front();E;E=E->next()) {
+
+ r_options->push_back(E->get());
+ }
+ }
+
+ for (const Map<StringName,Variant >::Element *E=p_script->get_constants().front();E;E=E->next()) {
+
+ if( p_indices) {
+ if (p_indices->get()==String(E->get())) {
+ _parse_completion_variant(E->get(),r_options,p_indices->next());
+ return true;
+ }
+ } else {
+ r_options->push_back(E->key());
+ }
+ }
+
+ if (!p_indices){
+ for (const Map<StringName,GDFunction>::Element *E=p_script->get_member_functions().front();E;E=E->next()) {
+
+ if (E->get().is_static() || !p_static)
+ r_options->push_back(E->key());
+ }
+ }
+
+ if (p_script->get_base().is_valid()){
+ if (_parse_script_symbols(p_script->get_base(),p_static,r_options,p_indices))
+ return true;
+ } else if (p_script->get_native().is_valid() && !p_indices) {
+ _parse_native_symbols(p_script->get_native()->get_name(),p_static,r_options);
+ }
+
+ return false;
+}
+
+
+static bool _parse_completion_class(const String& p_base_path,const GDParser::ClassNode *p_class,int p_line,List<String>* r_options,List<String>::Element *p_indices) {
+
+
+ static const char*_type_names[Variant::VARIANT_MAX]={
+ "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Trasnform",
+ "Color","Image","NodePath","RID","Object","InputEvent","Dictionary","Array","RawArray","IntArray","FloatArray","StringArray",
+ "Vector2Array","Vector3Array","ColorArray"};
+
+ if (p_indices && !p_indices->next()) {
+ for(int i=0;i<Variant::VARIANT_MAX;i++) {
+
+ if (p_indices->get()==_type_names[i]) {
+
+ List<StringName> ic;
+
+ Variant::get_numeric_constants_for_type(Variant::Type(i),&ic);
+ for(List<StringName>::Element *E=ic.front();E;E=E->next()) {
+ r_options->push_back(E->get());
+ }
+ return true;
+ }
+ }
+ }
+
+
+
+ for(int i=0;i<p_class->subclasses.size();i++) {
+
+ if (p_line>=p_class->subclasses[i]->line && (p_line<=p_class->subclasses[i]->end_line || p_class->subclasses[i]->end_line==-1)) {
+
+ if (_parse_completion_class(p_base_path,p_class->subclasses[i],p_line,r_options,p_indices))
+ return true;
+ }
+ }
+
+ bool in_static_func=false;
+
+ for(int i=0;i<p_class->functions.size();i++) {
+
+ const GDParser::FunctionNode *fu = p_class->functions[i];
+
+ if (p_line>=fu->body->line && (p_line<=fu->body->end_line || fu->body->end_line==-1)) {
+ //if in function, first block stuff from outer to inner
+ if (_parse_completion_block(fu->body,p_line,r_options,p_indices))
+ return true;
+ //then function arguments
+ if (!p_indices) {
+ for(int j=0;j<fu->arguments.size();j++) {
+
+ r_options->push_back(fu->arguments[j]);
+ }
+ }
+ }
+
+ }
+
+ for(int i=0;i<p_class->static_functions.size();i++) {
+
+ const GDParser::FunctionNode *fu = p_class->static_functions[i];
+
+ if (p_line>=fu->body->line && (p_line<=fu->body->end_line || fu->body->end_line==-1)) {
+
+ //if in function, first block stuff from outer to inne
+ if (_parse_completion_block(fu->body,p_line,r_options,p_indices))
+ return true;
+ //then function arguments
+ if (!p_indices) {
+ for(int j=0;j<fu->arguments.size();j++) {
+
+ r_options->push_back(fu->arguments[j]);
+ }
+ }
+
+ in_static_func=true;
+ }
+
+ }
+
+
+ //add all local names
+ if (!p_indices) {
+
+ if (!in_static_func) {
+
+ for(int i=0;i<p_class->variables.size();i++) {
+
+ r_options->push_back(p_class->variables[i].identifier);
+ }
+ }
+
+ for(int i=0;i<p_class->constant_expressions.size();i++) {
+
+ r_options->push_back(p_class->constant_expressions[i].identifier);
+ }
+
+ if (!in_static_func) {
+ for(int i=0;i<p_class->functions.size();i++) {
+
+ r_options->push_back(p_class->functions[i]->name);
+ }
+ }
+
+ for(int i=0;i<p_class->static_functions.size();i++) {
+
+ r_options->push_back(p_class->static_functions[i]->name);
+ }
+ }
+
+
+ if (p_class->extends_used) {
+ //do inheritance
+ String path = p_class->extends_file;
+
+ Ref<GDScript> script;
+ Ref<GDNativeClass> native;
+
+ if (path!="") {
+ //path (and optionally subclasses)
+
+ script = ResourceLoader::load(path);
+ if (script.is_null()) {
+ return false;
+ }
+
+ 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)) {
+
+ script=script->get_subclasses()[sub];
+ } else {
+
+ return false;
+ }
+ }
+ }
+
+ } else {
+
+ ERR_FAIL_COND_V(p_class->extends_class.size()==0,false);
+ //look around for the subclasses
+
+ String base=p_class->extends_class[0];
+ Ref<GDScript> base_class;
+#if 0
+ while(p) {
+
+ if (p->subclasses.has(base)) {
+
+ base_class=p->subclasses[base];
+ break;
+ }
+ p=p->_owner;
+ }
+
+ if (base_class.is_valid()) {
+
+ for(int i=1;i<p_class->extends_class.size();i++) {
+
+ String subclass=p_class->extends_class[i];
+
+ if (base_class->subclasses.has(subclass)) {
+
+ base_class=base_class->subclasses[subclass];
+ } else {
+
+ _set_error("Could not find subclass: "+subclass,p_class);
+ return ERR_FILE_NOT_FOUND;
+ }
+ }
+
+
+ } else {
+#endif
+ if (p_class->extends_class.size()>1) {
+
+ return false;
+
+ }
+ //if not found, try engine classes
+ if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
+ return false;
+ }
+
+ int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base];
+ native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx];
+ if (!native.is_valid()) {
+ return false;
+ }
+#if 0
+ }
+#endif
+
+ }
+
+ if (script.is_valid()) {
+ if (_parse_script_symbols(script,in_static_func,r_options,p_indices))
+ return true;
+
+ } else if (native.is_valid() && !p_indices) {
+
+ _parse_native_symbols(native->get_name(),in_static_func,r_options);
+ }
+ }
+
+ return false;
+
+}
+
+
+Error GDScriptLanguage::complete_keyword(const String& p_code, int p_line, const String& p_base_path, const String& p_base, List<String>* r_options) {
+
+ GDParser p;
+ Error err = p.parse(p_code,p_base_path);
+ // don't care much about error I guess
+ const GDParser::Node* root = p.get_parse_tree();
+ ERR_FAIL_COND_V(root->type!=GDParser::Node::TYPE_CLASS,ERR_INVALID_DATA);
+
+ const GDParser::ClassNode *cl = static_cast<const GDParser::ClassNode*>(root);
+
+ List<String> indices;
+ Vector<String> spl = p_base.split(".");
+
+ for(int i=0;i<spl.size()-1;i++) {
+ indices.push_back(spl[i]);
+ }
+
+ if (_parse_completion_class(p_base,cl,p_line,r_options,indices.front()))
+ return OK;
+ //and the globals x_x?
+ for(Map<StringName,int>::Element *E=globals.front();E;E=E->next()) {
+ if (!indices.empty()) {
+ if (String(E->key())==indices.front()->get()) {
+
+ _parse_completion_variant(global_array[E->get()],r_options,indices.front()->next());
+
+ return OK;
+ }
+ } else {
+ r_options->push_back(E->key());
+ }
+ }
+
+ return OK;
+}
+
diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp
new file mode 100644
index 0000000000..2930d9322c
--- /dev/null
+++ b/modules/gdscript/gd_functions.cpp
@@ -0,0 +1,1218 @@
+/*************************************************************************/
+/* gd_functions.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_functions.h"
+#include "math_funcs.h"
+#include "object_type_db.h"
+#include "reference.h"
+#include "gd_script.h"
+#include "os/os.h"
+
+const char *GDFunctions::get_func_name(Function p_func) {
+
+ ERR_FAIL_INDEX_V(p_func,FUNC_MAX,"");
+
+ static const char *_names[FUNC_MAX]={
+ "sin",
+ "cos",
+ "tan",
+ "sinh",
+ "cosh",
+ "tanh",
+ "asin",
+ "acos",
+ "atan",
+ "atan2",
+ "sqrt",
+ "fmod",
+ "fposmod",
+ "floor",
+ "ceil",
+ "round",
+ "abs",
+ "sign",
+ "pow",
+ "log",
+ "exp",
+ "is_nan",
+ "is_inf",
+ "ease",
+ "decimals",
+ "stepify",
+ "lerp",
+ "dectime",
+ "randomize",
+ "randi",
+ "randf",
+ "rand_range",
+ "rand_seed",
+ "deg2rad",
+ "rad2deg",
+ "linear2db",
+ "db2linear",
+ "max",
+ "min",
+ "clamp",
+ "nearest_po2",
+ "weakref",
+ "convert",
+ "typeof",
+ "str",
+ "print",
+ "printt",
+ "printerr",
+ "printraw",
+ "range",
+ "load",
+ "inst2dict",
+ "dict2inst",
+ "print_stack",
+ };
+
+ return _names[p_func];
+
+}
+
+void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Variant &r_ret,Variant::CallError &r_error) {
+
+ r_error.error=Variant::CallError::CALL_OK;
+#ifdef DEBUG_ENABLED
+
+#define VALIDATE_ARG_COUNT(m_count) \
+ if (p_arg_count<m_count) {\
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;\
+ r_error.argument=m_count;\
+ return;\
+ }\
+ if (p_arg_count>m_count) {\
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;\
+ r_error.argument=m_count;\
+ return;\
+ }
+
+#define VALIDATE_ARG_NUM(m_arg) \
+ if (!p_args[m_arg]->is_num()) {\
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;\
+ r_error.argument=m_arg;\
+ r_error.expected=Variant::REAL;\
+ return;\
+ }
+
+#else
+
+#define VALIDATE_ARG_COUNT(m_count)
+#define VALIDATE_ARG_NUM(m_arg)
+#endif
+
+ //using a switch, so the compiler generates a jumptable
+
+ switch(p_func) {
+
+ case MATH_SIN: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::sin(*p_args[0]);
+ } break;
+ case MATH_COS: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::cos(*p_args[0]);
+ } break;
+ case MATH_TAN: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::tan(*p_args[0]);
+ } break;
+ case MATH_SINH: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::sinh(*p_args[0]);
+ } break;
+ case MATH_COSH: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::cosh(*p_args[0]);
+ } break;
+ case MATH_TANH: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::tanh(*p_args[0]);
+ } break;
+ case MATH_ASIN: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::asin(*p_args[0]);
+ } break;
+ case MATH_ACOS: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::acos(*p_args[0]);
+ } break;
+ case MATH_ATAN: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::atan(*p_args[0]);
+ } break;
+ case MATH_ATAN2: {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ r_ret=Math::atan2(*p_args[0],*p_args[1]);
+ } break;
+ case MATH_SQRT: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::sqrt(*p_args[0]);
+ } break;
+ case MATH_FMOD: {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ r_ret=Math::fmod(*p_args[0],*p_args[1]);
+ } break;
+ case MATH_FPOSMOD: {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ r_ret=Math::fposmod(*p_args[0],*p_args[1]);
+ } break;
+ case MATH_FLOOR: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::floor(*p_args[0]);
+ } break;
+ case MATH_CEIL: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::ceil(*p_args[0]);
+ } break;
+ case MATH_ROUND: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::round(*p_args[0]);
+ } break;
+ case MATH_ABS: {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type()==Variant::INT) {
+
+ int64_t i = *p_args[0];
+ r_ret=ABS(i);
+ } else if (p_args[0]->get_type()==Variant::REAL) {
+
+ real_t r = *p_args[0];
+ r_ret=Math::abs(r);
+ } else {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::REAL;
+ }
+ } break;
+ case MATH_SIGN: {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type()==Variant::INT) {
+
+ int64_t i = *p_args[0];
+ r_ret= i < 0 ? -1 : ( i > 0 ? +1 : 0);
+ } else if (p_args[0]->get_type()==Variant::REAL) {
+
+ real_t r = *p_args[0];
+ r_ret= r < 0.0 ? -1.0 : ( r > 0.0 ? +1.0 : 0.0);
+ } else {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::REAL;
+ }
+ } break;
+ case MATH_POW: {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ r_ret=Math::pow(*p_args[0],*p_args[1]);
+ } break;
+ case MATH_LOG: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::log(*p_args[0]);
+ } break;
+ case MATH_EXP: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::exp(*p_args[0]);
+ } break;
+ case MATH_ISNAN: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::is_nan(*p_args[0]);
+ } break;
+ case MATH_ISINF: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::is_inf(*p_args[0]);
+ } break;
+ case MATH_EASE: {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ r_ret=Math::ease(*p_args[0],*p_args[1]);
+ } break;
+ case MATH_DECIMALS: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::decimals(*p_args[0]);
+ } break;
+ case MATH_STEPIFY: {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ r_ret=Math::stepify(*p_args[0],*p_args[1]);
+ } break;
+ case MATH_LERP: {
+ VALIDATE_ARG_COUNT(3);
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ r_ret=Math::lerp(*p_args[0],*p_args[1],*p_args[2]);
+ } break;
+ case MATH_DECTIME: {
+ VALIDATE_ARG_COUNT(3);
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+ r_ret=Math::dectime(*p_args[0],*p_args[1],*p_args[2]);
+ } break;
+ case MATH_RANDOMIZE: {
+ Math::randomize();
+ r_ret=Variant();
+ } break;
+ case MATH_RAND: {
+ r_ret=Math::rand();
+ } break;
+ case MATH_RANDF: {
+ r_ret=Math::randf();
+ } break;
+ case MATH_RANDOM: {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ r_ret=Math::random(*p_args[0],*p_args[1]);
+ } break;
+ case MATH_RANDSEED: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ uint32_t seed=*p_args[0];
+ int ret = Math::rand_from_seed(&seed);
+ Array reta;
+ reta.push_back(ret);
+ reta.push_back(seed);
+ r_ret=reta;
+
+ } break;
+ case MATH_DEG2RAD: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::deg2rad(*p_args[0]);
+ } break;
+ case MATH_RAD2DEG: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::rad2deg(*p_args[0]);
+ } break;
+ case MATH_LINEAR2DB: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::linear2db(*p_args[0]);
+ } break;
+ case MATH_DB2LINEAR: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ r_ret=Math::db2linear(*p_args[0]);
+ } break;
+ case LOGIC_MAX: {
+ VALIDATE_ARG_COUNT(2);
+ if (p_args[0]->get_type()==Variant::INT && p_args[1]->get_type()==Variant::INT) {
+
+ int64_t a = *p_args[0];
+ int64_t b = *p_args[1];
+ r_ret=MAX(a,b);
+ } else {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+
+ real_t a = *p_args[0];
+ real_t b = *p_args[1];
+
+ r_ret=MAX(a,b);
+ }
+
+ } break;
+ case LOGIC_MIN: {
+ VALIDATE_ARG_COUNT(2);
+ if (p_args[0]->get_type()==Variant::INT && p_args[1]->get_type()==Variant::INT) {
+
+ int64_t a = *p_args[0];
+ int64_t b = *p_args[1];
+ r_ret=MIN(a,b);
+ } else {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+
+ real_t a = *p_args[0];
+ real_t b = *p_args[1];
+
+ r_ret=MIN(a,b);
+ }
+ } break;
+ case LOGIC_CLAMP: {
+ VALIDATE_ARG_COUNT(3);
+ if (p_args[0]->get_type()==Variant::INT && p_args[1]->get_type()==Variant::INT && p_args[2]->get_type()==Variant::INT) {
+
+ int64_t a = *p_args[0];
+ int64_t b = *p_args[1];
+ int64_t c = *p_args[2];
+ r_ret=CLAMP(a,b,c);
+ } else {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+
+ real_t a = *p_args[0];
+ real_t b = *p_args[1];
+ real_t c = *p_args[2];
+
+ r_ret=CLAMP(a,b,c);
+ }
+ } break;
+ case LOGIC_NEAREST_PO2: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ int64_t num = *p_args[0];
+ r_ret = nearest_power_of_2(num);
+ } break;
+ case OBJ_WEAKREF: {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type()!=Variant::OBJECT) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::OBJECT;
+ return;
+
+ }
+
+ if (p_args[0]->is_ref()) {
+
+ REF r = *p_args[0];
+ if (!r.is_valid()) {
+ r_ret=Variant();
+ return;
+ }
+
+ Ref<WeakRef> wref = memnew( WeakRef );
+ wref->set_ref(r);
+ r_ret=wref;
+ } else {
+ Object *obj = *p_args[0];
+ if (!obj) {
+ r_ret=Variant();
+ return;
+ }
+ Ref<WeakRef> wref = memnew( WeakRef );
+ wref->set_obj(obj);
+ r_ret=wref;
+ }
+
+
+
+
+ } break;
+ case TYPE_CONVERT: {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_NUM(1);
+ int type=*p_args[1];
+ if (type<0 || type>=Variant::VARIANT_MAX) {
+
+ ERR_PRINT("Invalid type argument to convert()");
+ r_ret=Variant::NIL;
+
+ } else {
+
+
+ r_ret=Variant::construct(Variant::Type(type),p_args,1,r_error);
+ }
+ } break;
+ case TYPE_OF: {
+
+ VALIDATE_ARG_COUNT(1);
+ r_ret = p_args[0]->get_type();
+
+ } break;
+ case TEXT_STR: {
+
+ String str;
+ for(int i=0;i<p_arg_count;i++) {
+
+ String os = p_args[i]->operator String();;
+ if (i==0)
+ str=os;
+ else
+ str+=os;
+ }
+
+ r_ret=str;
+
+ } break;
+ case TEXT_PRINT: {
+
+ String str;
+ for(int i=0;i<p_arg_count;i++) {
+
+ str+=p_args[i]->operator String();
+ }
+
+ //str+="\n";
+ print_line(str);
+ r_ret=Variant();
+
+
+ } break;
+ case TEXT_PRINT_TABBED: {
+
+ String str;
+ for(int i=0;i<p_arg_count;i++) {
+
+ if (i)
+ str+="\t";
+ str+=p_args[i]->operator String();
+ }
+
+ //str+="\n";
+ print_line(str);
+ r_ret=Variant();
+
+
+ } break;
+
+ case TEXT_PRINTERR: {
+
+ String str;
+ for(int i=0;i<p_arg_count;i++) {
+
+ str+=p_args[i]->operator String();
+ }
+
+ //str+="\n";
+ OS::get_singleton()->printerr("%s\n",str.utf8().get_data());
+ r_ret=Variant();
+
+ } break;
+ case TEXT_PRINTRAW: {
+ String str;
+ for(int i=0;i<p_arg_count;i++) {
+
+ str+=p_args[i]->operator String();
+ }
+
+ //str+="\n";
+ OS::get_singleton()->print("%s\n",str.utf8().get_data());
+ r_ret=Variant();
+
+ } break;
+ case GEN_RANGE: {
+
+
+
+ switch(p_arg_count) {
+
+ case 0: {
+
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument=1;
+
+ } break;
+ case 1: {
+
+ VALIDATE_ARG_NUM(0);
+ int count=*p_args[0];
+ Array arr(true);
+ if (count<=0) {
+ r_ret=arr;
+ return;
+ }
+ Error err = arr.resize(count);
+ if (err!=OK) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_ret=Variant();
+ return;
+ }
+
+ for(int i=0;i<count;i++) {
+ arr[i]=i;
+ }
+
+ r_ret=arr;
+ } break;
+ case 2: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+
+ int from=*p_args[0];
+ int to=*p_args[1];
+
+ Array arr(true);
+ if (from>=to) {
+ r_ret=arr;
+ return;
+ }
+ Error err = arr.resize(to-from);
+ if (err!=OK) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_ret=Variant();
+ return;
+ }
+ for(int i=from;i<to;i++)
+ arr[i-from]=i;
+ r_ret=arr;
+ } break;
+ case 3: {
+
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+
+ int from=*p_args[0];
+ int to=*p_args[1];
+ int incr=*p_args[2];
+ if (incr==0) {
+
+ ERR_EXPLAIN("step argument is zero!");
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ ERR_FAIL();
+ }
+
+ Array arr(true);
+ if (from>=to && incr>0) {
+ r_ret=arr;
+ return;
+ }
+ if (from<=to && incr<0) {
+ r_ret=arr;
+ return;
+ }
+
+ //calculate how many
+ int count=0;
+ if (incr>0) {
+
+ count=((to-from-1)/incr)+1;
+ } else {
+
+ count=((from-to-1)/-incr)+1;
+ }
+
+
+ Error err = arr.resize(count);
+
+ if (err!=OK) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ r_ret=Variant();
+ return;
+ }
+
+ if (incr>0) {
+ int idx=0;
+ for(int i=from;i<to;i+=incr) {
+ arr[idx++]=i;
+ }
+ } else {
+
+ int idx=0;
+ for(int i=from;i>to;i+=incr) {
+ arr[idx++]=i;
+ }
+ }
+
+ r_ret=arr;
+ } break;
+ default: {
+
+ r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument=3;
+ } break;
+ }
+
+ } break;
+ case RESOURCE_LOAD: {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type()!=Variant::STRING) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_ret=Variant();
+ }
+ r_ret=ResourceLoader::load(*p_args[0]);
+
+ }
+ case INST2DICT: {
+
+ VALIDATE_ARG_COUNT(1);
+
+ if (p_args[0]->get_type()==Variant::NIL) {
+ r_ret=Variant();
+ } else if (p_args[0]->get_type()!=Variant::OBJECT) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_ret=Variant();
+ } else {
+
+ Object *obj = *p_args[0];
+ if (!obj) {
+ r_ret=Variant();
+
+ } else if (!obj->get_script_instance() || obj->get_script_instance()->get_language()!=GDScriptLanguage::get_singleton()) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::DICTIONARY;
+ ERR_PRINT("Not a script with an instance");
+
+ } else {
+
+ GDInstance *ins = static_cast<GDInstance*>(obj->get_script_instance());
+ Ref<GDScript> base = ins->get_script();
+ if (base.is_null()) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::DICTIONARY;
+ ERR_PRINT("Not based on a script");
+ return;
+
+ }
+
+
+ GDScript *p = base.ptr();
+ Vector<StringName> sname;
+
+ while(p->_owner) {
+
+ sname.push_back(p->name);
+ p=p->_owner;
+ }
+ sname.invert();
+
+
+ if (!p->path.is_resource_file()) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::DICTIONARY;
+ print_line("PATH: "+p->path);
+ ERR_PRINT("Not based on a resource file");
+
+ return;
+ }
+
+ NodePath cp(sname,Vector<StringName>(),false);
+
+ Dictionary d(true);
+ d["@subpath"]=cp;
+ d["@path"]=p->path;
+
+
+ p = base.ptr();
+
+ while(p) {
+
+ for(Set<StringName>::Element *E=p->members.front();E;E=E->next()) {
+
+ Variant value;
+ if (ins->get(E->get(),value)) {
+
+ String k = E->get();
+ if (!d.has(k)) {
+ d[k]=value;
+ }
+ }
+ }
+
+ p=p->_base;
+ }
+
+ r_ret=d;
+
+ }
+ }
+
+ } break;
+ case DICT2INST: {
+
+ VALIDATE_ARG_COUNT(1);
+
+ if (p_args[0]->get_type()!=Variant::DICTIONARY) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::DICTIONARY;
+ return;
+ }
+
+ Dictionary d = *p_args[0];
+
+ if (!d.has("@path")) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::OBJECT;
+ return;
+ }
+
+ Ref<Script> scr = ResourceLoader::load(d["@path"]);
+ if (!scr.is_valid()) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::OBJECT;
+ return;
+ }
+
+ Ref<GDScript> gdscr = scr;
+
+ if (!gdscr.is_valid()) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::OBJECT;
+ return;
+ }
+
+ NodePath sub;
+ if (d.has("@subpath")) {
+ sub=d["@subpath"];
+ }
+
+ for(int i=0;i<sub.get_name_count();i++) {
+
+ gdscr = gdscr->subclasses[ sub.get_name(i)];
+ if (!gdscr.is_valid()) {
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_error.expected=Variant::OBJECT;
+ return;
+ }
+ }
+
+
+ r_ret = gdscr->_new(NULL,0,r_error);
+
+ } break;
+
+ case PRINT_STACK: {
+
+ ScriptLanguage* script = GDScriptLanguage::get_singleton();
+ for (int i=0; i < script->debug_get_stack_level_count(); i++) {
+
+ print_line("Frame "+itos(i)+" - "+script->debug_get_stack_level_source(i)+":"+itos(script->debug_get_stack_level_line(i))+" in function '"+script->debug_get_stack_level_function(i)+"'");
+ };
+ } break;
+
+ case FUNC_MAX: {
+
+ ERR_FAIL_V();
+ } break;
+
+ }
+
+}
+
+bool GDFunctions::is_deterministic(Function p_func) {
+
+ //man i couldn't have chosen a worse function name,
+ //way too controversial..
+
+ switch(p_func) {
+
+ case MATH_SIN:
+ case MATH_COS:
+ case MATH_TAN:
+ case MATH_SINH:
+ case MATH_COSH:
+ case MATH_TANH:
+ case MATH_ASIN:
+ case MATH_ACOS:
+ case MATH_ATAN:
+ case MATH_ATAN2:
+ case MATH_SQRT:
+ case MATH_FMOD:
+ case MATH_FPOSMOD:
+ case MATH_FLOOR:
+ case MATH_CEIL:
+ case MATH_ROUND:
+ case MATH_ABS:
+ case MATH_SIGN:
+ case MATH_POW:
+ case MATH_LOG:
+ case MATH_EXP:
+ case MATH_ISNAN:
+ case MATH_ISINF:
+ case MATH_EASE:
+ case MATH_DECIMALS:
+ case MATH_STEPIFY:
+ case MATH_LERP:
+ case MATH_DECTIME:
+ case MATH_DEG2RAD:
+ case MATH_RAD2DEG:
+ case MATH_LINEAR2DB:
+ case MATH_DB2LINEAR:
+ case LOGIC_MAX:
+ case LOGIC_MIN:
+ case LOGIC_CLAMP:
+ case LOGIC_NEAREST_PO2:
+ case TYPE_CONVERT:
+ case TYPE_OF:
+ case TEXT_STR:
+// enable for debug only, otherwise not desirable - case GEN_RANGE:
+ return true;
+ default:
+ return false;
+
+ }
+
+ return false;
+
+
+}
+
+MethodInfo GDFunctions::get_info(Function p_func) {
+
+#ifdef TOOLS_ENABLED
+ //using a switch, so the compiler generates a jumptable
+
+ switch(p_func) {
+
+ case MATH_SIN: {
+ MethodInfo mi("sin",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+
+ } break;
+ case MATH_COS: {
+ MethodInfo mi("cos",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_TAN: {
+ MethodInfo mi("tan",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_SINH: {
+ MethodInfo mi("sinh",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_COSH: {
+ MethodInfo mi("cosh",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_TANH: {
+ MethodInfo mi("tanh",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_ASIN: {
+ MethodInfo mi("asin",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_ACOS: {
+ MethodInfo mi("acos",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_ATAN: {
+ MethodInfo mi("atan",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_ATAN2: {
+ MethodInfo mi("atan2",PropertyInfo(Variant::REAL,"x"),PropertyInfo(Variant::REAL,"y"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_SQRT: {
+ MethodInfo mi("sqrt",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_FMOD: {
+ MethodInfo mi("fmod",PropertyInfo(Variant::REAL,"x"),PropertyInfo(Variant::REAL,"y"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_FPOSMOD: {
+ MethodInfo mi("fposmod",PropertyInfo(Variant::REAL,"x"),PropertyInfo(Variant::REAL,"y"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_FLOOR: {
+ MethodInfo mi("floor",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_CEIL: {
+ MethodInfo mi("ceil",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_ROUND: {
+ MethodInfo mi("round",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_ABS: {
+ MethodInfo mi("abs",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_SIGN: {
+ MethodInfo mi("sign",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_POW: {
+ MethodInfo mi("pow",PropertyInfo(Variant::REAL,"x"),PropertyInfo(Variant::REAL,"y"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_LOG: {
+ MethodInfo mi("log",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_EXP: {
+ MethodInfo mi("exp",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_ISNAN: {
+ MethodInfo mi("isnan",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_ISINF: {
+ MethodInfo mi("isinf",PropertyInfo(Variant::REAL,"s"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_EASE: {
+ MethodInfo mi("ease",PropertyInfo(Variant::REAL,"s"),PropertyInfo(Variant::REAL,"curve"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_DECIMALS: {
+ MethodInfo mi("decimals",PropertyInfo(Variant::REAL,"step"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_STEPIFY: {
+ MethodInfo mi("stepify",PropertyInfo(Variant::REAL,"s"),PropertyInfo(Variant::REAL,"step"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_LERP: {
+ MethodInfo mi("lerp",PropertyInfo(Variant::REAL,"a"),PropertyInfo(Variant::REAL,"b"), PropertyInfo(Variant::REAL,"c"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_DECTIME: {
+ MethodInfo mi("dectime",PropertyInfo(Variant::REAL,"value"),PropertyInfo(Variant::REAL,"amount"),PropertyInfo(Variant::REAL,"step"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_RANDOMIZE: {
+ MethodInfo mi("randomize");
+ mi.return_val.type=Variant::NIL;
+ return mi;
+ } break;
+ case MATH_RAND: {
+ MethodInfo mi("rand");
+ mi.return_val.type=Variant::INT;
+ return mi;
+ } break;
+ case MATH_RANDF: {
+ MethodInfo mi("randf");
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_RANDOM: {
+ MethodInfo mi("rand_range",PropertyInfo(Variant::REAL,"from"),PropertyInfo(Variant::REAL,"to"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_RANDSEED: {
+ MethodInfo mi("rand_seed",PropertyInfo(Variant::REAL,"seed"));
+ mi.return_val.type=Variant::ARRAY;
+ return mi;
+ } break;
+ case MATH_DEG2RAD: {
+ MethodInfo mi("deg2rad",PropertyInfo(Variant::REAL,"deg"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_RAD2DEG: {
+ MethodInfo mi("rad2deg",PropertyInfo(Variant::REAL,"rad"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_LINEAR2DB: {
+ MethodInfo mi("linear2db",PropertyInfo(Variant::REAL,"nrg"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case MATH_DB2LINEAR: {
+ MethodInfo mi("db2linear",PropertyInfo(Variant::REAL,"db"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case LOGIC_MAX: {
+ MethodInfo mi("max",PropertyInfo(Variant::REAL,"a"),PropertyInfo(Variant::REAL,"b"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+
+ } break;
+ case LOGIC_MIN: {
+ MethodInfo mi("min",PropertyInfo(Variant::REAL,"a"),PropertyInfo(Variant::REAL,"b"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case LOGIC_CLAMP: {
+ MethodInfo mi("clamp",PropertyInfo(Variant::REAL,"val"),PropertyInfo(Variant::REAL,"min"),PropertyInfo(Variant::REAL,"max"));
+ mi.return_val.type=Variant::REAL;
+ return mi;
+ } break;
+ case LOGIC_NEAREST_PO2: {
+ MethodInfo mi("nearest_po2",PropertyInfo(Variant::INT,"val"));
+ mi.return_val.type=Variant::INT;
+ return mi;
+ } break;
+ case OBJ_WEAKREF: {
+
+ MethodInfo mi("weakref",PropertyInfo(Variant::OBJECT,"obj"));
+ mi.return_val.type=Variant::OBJECT;
+ return mi;
+
+ } break;
+ case TYPE_CONVERT: {
+
+ MethodInfo mi("convert",PropertyInfo(Variant::NIL,"what"),PropertyInfo(Variant::INT,"type"));
+ mi.return_val.type=Variant::OBJECT;
+ return mi;
+ } break;
+ case TYPE_OF: {
+ MethodInfo mi("typeof",PropertyInfo(Variant::NIL,"what"));
+ mi.return_val.type=Variant::INT;
+ };
+ case TEXT_STR: {
+
+ MethodInfo mi("str",PropertyInfo(Variant::NIL,"what"),PropertyInfo(Variant::NIL,"..."));
+ mi.return_val.type=Variant::STRING;
+ return mi;
+
+ } break;
+ case TEXT_PRINT: {
+
+ MethodInfo mi("print",PropertyInfo(Variant::NIL,"what"),PropertyInfo(Variant::NIL,"..."));
+ mi.return_val.type=Variant::NIL;
+ return mi;
+
+ } break;
+ case TEXT_PRINT_TABBED: {
+
+ MethodInfo mi("printt",PropertyInfo(Variant::NIL,"what"),PropertyInfo(Variant::NIL,"..."));
+ mi.return_val.type=Variant::NIL;
+ return mi;
+
+ } break;
+ case TEXT_PRINTERR: {
+
+ MethodInfo mi("printerr",PropertyInfo(Variant::NIL,"what"),PropertyInfo(Variant::NIL,"..."));
+ mi.return_val.type=Variant::NIL;
+ return mi;
+
+ } break;
+ case TEXT_PRINTRAW: {
+
+ MethodInfo mi("printraw",PropertyInfo(Variant::NIL,"what"),PropertyInfo(Variant::NIL,"..."));
+ mi.return_val.type=Variant::NIL;
+ return mi;
+
+ } break;
+ case GEN_RANGE: {
+
+ MethodInfo mi("range",PropertyInfo(Variant::NIL,"..."));
+ mi.return_val.type=Variant::ARRAY;
+ return mi;
+ } break;
+ case RESOURCE_LOAD: {
+
+ MethodInfo mi("load",PropertyInfo(Variant::STRING,"path"));
+ mi.return_val.type=Variant::OBJECT;
+ return mi;
+ } break;
+ case INST2DICT: {
+
+ MethodInfo mi("inst2dict",PropertyInfo(Variant::OBJECT,"inst"));
+ mi.return_val.type=Variant::DICTIONARY;
+ return mi;
+ } break;
+ case DICT2INST: {
+
+ MethodInfo mi("dict2inst",PropertyInfo(Variant::DICTIONARY,"dict"));
+ mi.return_val.type=Variant::OBJECT;
+ return mi;
+ } break;
+
+ case PRINT_STACK: {
+ MethodInfo mi("print_stack");
+ mi.return_val.type=Variant::NIL;
+ return mi;
+ } break;
+
+ case FUNC_MAX: {
+
+ ERR_FAIL_V(MethodInfo());
+ } break;
+
+ }
+#endif
+
+ return MethodInfo();
+}
diff --git a/modules/gdscript/gd_functions.h b/modules/gdscript/gd_functions.h
new file mode 100644
index 0000000000..2ab397d18a
--- /dev/null
+++ b/modules/gdscript/gd_functions.h
@@ -0,0 +1,103 @@
+/*************************************************************************/
+/* gd_functions.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_FUNCTIONS_H
+#define GD_FUNCTIONS_H
+
+#include "variant.h"
+
+class GDFunctions {
+public:
+
+ enum Function {
+ MATH_SIN,
+ MATH_COS,
+ MATH_TAN,
+ MATH_SINH,
+ MATH_COSH,
+ MATH_TANH,
+ MATH_ASIN,
+ MATH_ACOS,
+ MATH_ATAN,
+ MATH_ATAN2,
+ MATH_SQRT,
+ MATH_FMOD,
+ MATH_FPOSMOD,
+ MATH_FLOOR,
+ MATH_CEIL,
+ MATH_ROUND,
+ MATH_ABS,
+ MATH_SIGN,
+ MATH_POW,
+ MATH_LOG,
+ MATH_EXP,
+ MATH_ISNAN,
+ MATH_ISINF,
+ MATH_EASE,
+ MATH_DECIMALS,
+ MATH_STEPIFY,
+ MATH_LERP,
+ MATH_DECTIME,
+ MATH_RANDOMIZE,
+ MATH_RAND,
+ MATH_RANDF,
+ MATH_RANDOM,
+ MATH_RANDSEED,
+ MATH_DEG2RAD,
+ MATH_RAD2DEG,
+ MATH_LINEAR2DB,
+ MATH_DB2LINEAR,
+ LOGIC_MAX,
+ LOGIC_MIN,
+ LOGIC_CLAMP,
+ LOGIC_NEAREST_PO2,
+ OBJ_WEAKREF,
+ TYPE_CONVERT,
+ TYPE_OF,
+ TEXT_STR,
+ TEXT_PRINT,
+ TEXT_PRINT_TABBED,
+ TEXT_PRINTERR,
+ TEXT_PRINTRAW,
+ GEN_RANGE,
+ RESOURCE_LOAD,
+ INST2DICT,
+ DICT2INST,
+ PRINT_STACK,
+ FUNC_MAX
+
+ };
+
+ static const char *get_func_name(Function p_func);
+ static void call(Function p_func,const Variant **p_args,int p_arg_count,Variant &r_ret,Variant::CallError &r_error);
+ static bool is_deterministic(Function p_func);
+ static MethodInfo get_info(Function p_func);
+
+};
+
+#endif // GD_FUNCTIONS_H
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
new file mode 100644
index 0000000000..e558ceb416
--- /dev/null
+++ b/modules/gdscript/gd_parser.cpp
@@ -0,0 +1,2469 @@
+/*************************************************************************/
+/* gd_parser.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_parser.h"
+#include "print_string.h"
+#include "io/resource_loader.h"
+/* TODO:
+
+ *Property reduce constant expressions
+ *Implement missing operators in variant?
+ *constructor
+ */
+
+/*
+ todo:
+ fix post ++,--
+ make sure ++,-- don't work on constant expressions
+ seems passing parent node as param is not needed
+ */
+
+template<class T>
+T* GDParser::alloc_node() {
+
+ T *t = memnew( T);
+
+ t->next=list;
+ list=t;
+
+ if (!head)
+ head=t;
+
+ t->line=tokenizer.get_token_line();
+ t->column=tokenizer.get_token_column();
+ return t;
+
+}
+
+bool GDParser::_end_statement() {
+
+ if (tokenizer.get_token()==GDTokenizer::TK_SEMICOLON) {
+ tokenizer.advance();
+ return true; //handle next
+ } else if (tokenizer.get_token()==GDTokenizer::TK_NEWLINE || tokenizer.get_token()==GDTokenizer::TK_EOF) {
+ return true; //will be handled properly
+ }
+
+ return false;
+}
+
+bool GDParser::_enter_indent_block(BlockNode* p_block) {
+
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_COLON) {
+
+ _set_error("':' expected at end of line.");
+ return false;
+ }
+ tokenizer.advance();
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_NEWLINE) {
+
+ _set_error("newline expected after ':'.");
+ return false;
+ }
+
+ while(true) {
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_NEWLINE) {
+ return false; //wtf
+ } else if (tokenizer.get_token(1)!=GDTokenizer::TK_NEWLINE) {
+
+ int indent = tokenizer.get_token_line_indent();
+ int current = tab_level.back()->get();
+ if (indent<=current)
+ return false;
+
+ tab_level.push_back(indent);
+ tokenizer.advance();
+ return true;
+
+ } else if (p_block) {
+
+ NewLineNode *nl = alloc_node<NewLineNode>();
+ nl->line=tokenizer.get_token_line();
+ p_block->statements.push_back(nl);
+
+ }
+
+ tokenizer.advance(); // go to next newline
+ }
+}
+
+bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_static) {
+
+ if (tokenizer.get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ tokenizer.advance();
+ } else {
+
+ while(true) {
+
+
+ Node*arg = _parse_expression(p_parent,p_static);
+ if (!arg)
+ return false;
+
+ p_args.push_back(arg);
+
+ if (tokenizer.get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ tokenizer.advance();
+ break;
+
+ } else if (tokenizer.get_token()==GDTokenizer::TK_COMMA) {
+
+ if (tokenizer.get_token(1)==GDTokenizer::TK_PARENTHESIS_CLOSE) {
+
+ _set_error("Expression expected");
+ return false;
+ }
+
+ tokenizer.advance();
+ } else {
+ // something is broken
+ _set_error("Expected ',' or ')'");
+ return false;
+ }
+
+ }
+ }
+
+ return true;
+
+}
+
+
+
+GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_allow_assign) {
+
+// Vector<Node*> expressions;
+// Vector<OperatorNode::Operator> operators;
+
+ Vector<Expression> expression;
+
+ Node *expr=NULL;
+
+ while(true) {
+
+
+ /*****************/
+ /* Parse Operand */
+ /*****************/
+
+ if (tokenizer.get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
+ //subexpression ()
+ tokenizer.advance();
+ Node* subexpr = _parse_expression(p_parent,p_static);
+ if (!subexpr)
+ return NULL;
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+
+ _set_error("Expected ')' in expression");
+ return NULL;
+ }
+
+ tokenizer.advance();
+ expr=subexpr;
+
+ } else if (tokenizer.get_token()==GDTokenizer::TK_CONSTANT) {
+
+ //constant defined by tokenizer
+ ConstantNode *constant = alloc_node<ConstantNode>();
+ constant->value=tokenizer.get_token_constant();
+ tokenizer.advance();
+ expr=constant;
+ } else if (tokenizer.get_token()==GDTokenizer::TK_PR_PRELOAD) {
+
+ //constant defined by tokenizer
+ tokenizer.advance();
+ if (tokenizer.get_token()!=GDTokenizer::TK_PARENTHESIS_OPEN) {
+ _set_error("Expected '(' after 'preload'");
+ return NULL;
+ }
+ tokenizer.advance();
+ if (tokenizer.get_token()!=GDTokenizer::TK_CONSTANT || tokenizer.get_token_constant().get_type()!=Variant::STRING) {
+ _set_error("Expected string constant as 'preload' argument.");
+ return NULL;
+ }
+
+
+ String path = tokenizer.get_token_constant();
+ if (!path.is_abs_path() && base_path!="")
+ path=base_path+"/"+path;
+
+ Ref<Resource> res = ResourceLoader::load(path);
+ if (!res.is_valid()) {
+ _set_error("Can't preload resource at path: "+path);
+ return NULL;
+ }
+
+ tokenizer.advance();
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' after 'preload' path");
+ return NULL;
+ }
+
+ ConstantNode *constant = alloc_node<ConstantNode>();
+ constant->value=res;
+ tokenizer.advance();
+
+ expr=constant;
+
+ } else if (tokenizer.get_token()==GDTokenizer::TK_SELF) {
+
+ if (p_static) {
+ _set_error("'self'' not allowed in static function or constant expression");
+ return NULL;
+ }
+ //constant defined by tokenizer
+ SelfNode *self = alloc_node<SelfNode>();
+ tokenizer.advance();
+ expr=self;
+ } else if (tokenizer.get_token()==GDTokenizer::TK_BUILT_IN_TYPE && tokenizer.get_token(1)==GDTokenizer::TK_PERIOD) {
+
+ Variant::Type bi_type = tokenizer.get_token_type();
+ tokenizer.advance(2);
+ if (tokenizer.get_token()!=GDTokenizer::TK_IDENTIFIER) {
+
+ _set_error("Built-in type constant expected after '.'");
+ return NULL;
+ }
+ StringName identifier = tokenizer.get_token_identifier();
+ if (!Variant::has_numeric_constant(bi_type,identifier)) {
+
+ _set_error("Static constant '"+identifier.operator String()+"' not present in built-in type "+Variant::get_type_name(bi_type)+".");
+ return NULL;
+ }
+
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value=Variant::get_numeric_constant_value(bi_type,identifier);
+ expr=cn;
+ tokenizer.advance();
+
+ } else if (tokenizer.get_token(1)==GDTokenizer::TK_PARENTHESIS_OPEN && (tokenizer.get_token()==GDTokenizer::TK_BUILT_IN_TYPE || tokenizer.get_token()==GDTokenizer::TK_IDENTIFIER || tokenizer.get_token()==GDTokenizer::TK_BUILT_IN_FUNC)) {
+ //function or constructor
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=OperatorNode::OP_CALL;
+
+ if (tokenizer.get_token()==GDTokenizer::TK_BUILT_IN_TYPE) {
+
+ TypeNode *tn = alloc_node<TypeNode>();
+ tn->vtype=tokenizer.get_token_type();
+ op->arguments.push_back(tn);
+ } else if (tokenizer.get_token()==GDTokenizer::TK_BUILT_IN_FUNC) {
+
+ BuiltInFunctionNode *bn = alloc_node<BuiltInFunctionNode>();
+ bn->function=tokenizer.get_token_built_in_func();
+ op->arguments.push_back(bn);
+ } else {
+
+ SelfNode *self = alloc_node<SelfNode>();
+ op->arguments.push_back(self);
+
+ IdentifierNode* id = alloc_node<IdentifierNode>();
+ id->name=tokenizer.get_token_identifier();
+ op->arguments.push_back(id);
+ }
+
+ tokenizer.advance(2);
+ if (!_parse_arguments(op,op->arguments,p_static))
+ return NULL;
+
+ expr=op;
+
+ } else if (tokenizer.get_token()==GDTokenizer::TK_IDENTIFIER) {
+ //identifier (reference)
+
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name=tokenizer.get_token_identifier();
+ tokenizer.advance();
+ expr=id;
+
+ } else if (/*tokenizer.get_token()==GDTokenizer::TK_OP_ADD ||*/ tokenizer.get_token()==GDTokenizer::TK_OP_SUB || tokenizer.get_token()==GDTokenizer::TK_OP_NOT || tokenizer.get_token()==GDTokenizer::TK_OP_BIT_INVERT) {
+
+ //single prefix operators like !expr -expr ++expr --expr
+ OperatorNode *op = alloc_node<OperatorNode>();
+
+ Expression e;
+ e.is_op=true;
+
+ switch(tokenizer.get_token()) {
+ case GDTokenizer::TK_OP_SUB: e.op=OperatorNode::OP_NEG; break;
+ case GDTokenizer::TK_OP_NOT: e.op=OperatorNode::OP_NOT; break;
+ case GDTokenizer::TK_OP_BIT_INVERT: e.op=OperatorNode::OP_BIT_INVERT;; break;
+ default: {}
+ }
+
+
+ tokenizer.advance();
+
+ if (e.op!=OperatorNode::OP_NOT && tokenizer.get_token()==GDTokenizer::TK_OP_NOT) {
+ _set_error("Misplaced 'not'.");
+ return NULL;
+ }
+
+ expression.push_back(e);
+ continue; //only exception, must continue...
+
+ /*
+ Node *subexpr=_parse_expression(op,p_static);
+ if (!subexpr)
+ return NULL;
+ op->arguments.push_back(subexpr);
+ expr=op;*/
+
+ } else if (tokenizer.get_token()==GDTokenizer::TK_BRACKET_OPEN) {
+ // array
+ tokenizer.advance();
+
+ ArrayNode *arr = alloc_node<ArrayNode>();
+ bool expecting_comma=false;
+
+ while(true) {
+
+ if (tokenizer.get_token()==GDTokenizer::TK_EOF) {
+
+ _set_error("Unterminated array");
+ return NULL;
+
+ } else if (tokenizer.get_token()==GDTokenizer::TK_BRACKET_CLOSE) {
+ tokenizer.advance();
+ break;
+ } else if (tokenizer.get_token()==GDTokenizer::TK_NEWLINE) {
+
+ tokenizer.advance(); //ignore newline
+ } else if (tokenizer.get_token()==GDTokenizer::TK_COMMA) {
+ if (!expecting_comma) {
+ _set_error("expression or ']' expected");
+ return NULL;
+ }
+
+ expecting_comma=false;
+ tokenizer.advance(); //ignore newline
+ } else {
+ //parse expression
+ if (expecting_comma) {
+ _set_error("',' or ']' expected");
+ return NULL;
+ }
+ Node *n = _parse_expression(arr,p_static);
+ if (!n)
+ return NULL;
+ arr->elements.push_back(n);
+ expecting_comma=true;
+ }
+ }
+
+ expr=arr;
+ } else if (tokenizer.get_token()==GDTokenizer::TK_CURLY_BRACKET_OPEN) {
+ // array
+ tokenizer.advance();
+
+ DictionaryNode *dict = alloc_node<DictionaryNode>();
+
+ enum DictExpect {
+
+ DICT_EXPECT_KEY,
+ DICT_EXPECT_COLON,
+ DICT_EXPECT_VALUE,
+ DICT_EXPECT_COMMA
+
+ };
+
+ Node *key=NULL;
+
+ DictExpect expecting=DICT_EXPECT_KEY;
+
+ while(true) {
+
+ if (tokenizer.get_token()==GDTokenizer::TK_EOF) {
+
+ _set_error("Unterminated dictionary");
+ return NULL;
+
+ } else if (tokenizer.get_token()==GDTokenizer::TK_CURLY_BRACKET_CLOSE) {
+
+ if (expecting==DICT_EXPECT_COLON) {
+ _set_error("':' expected");
+ return NULL;
+ }
+ if (expecting==DICT_EXPECT_VALUE) {
+ _set_error("value expected");
+ return NULL;
+ }
+ tokenizer.advance();
+ break;
+ } else if (tokenizer.get_token()==GDTokenizer::TK_NEWLINE) {
+
+ tokenizer.advance(); //ignore newline
+ } else if (tokenizer.get_token()==GDTokenizer::TK_COMMA) {
+
+ if (expecting==DICT_EXPECT_KEY) {
+ _set_error("key or '}' expected");
+ return NULL;
+ }
+ if (expecting==DICT_EXPECT_VALUE) {
+ _set_error("value expected");
+ return NULL;
+ }
+ if (expecting==DICT_EXPECT_COLON) {
+ _set_error("':' expected");
+ return NULL;
+ }
+
+ expecting=DICT_EXPECT_KEY;
+ tokenizer.advance(); //ignore newline
+
+ } else if (tokenizer.get_token()==GDTokenizer::TK_COLON) {
+
+ if (expecting==DICT_EXPECT_KEY) {
+ _set_error("key or '}' expected");
+ return NULL;
+ }
+ if (expecting==DICT_EXPECT_VALUE) {
+ _set_error("value expected");
+ return NULL;
+ }
+ if (expecting==DICT_EXPECT_COMMA) {
+ _set_error("',' or '}' expected");
+ return NULL;
+ }
+
+ expecting=DICT_EXPECT_VALUE;
+ tokenizer.advance(); //ignore newline
+ } else {
+
+ if (expecting==DICT_EXPECT_COMMA) {
+ _set_error("',' or '}' expected");
+ return NULL;
+ }
+ if (expecting==DICT_EXPECT_COLON) {
+ _set_error("':' expected");
+ return NULL;
+ }
+
+ if (expecting==DICT_EXPECT_KEY) {
+
+ if (tokenizer.get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer.get_token(1)==GDTokenizer::TK_OP_ASSIGN) {
+ //lua style identifier, easier to write
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value = tokenizer.get_token_identifier();
+ key = cn;
+ tokenizer.advance(2);
+ expecting=DICT_EXPECT_VALUE;
+ } else {
+ //python/js style more flexible
+ key = _parse_expression(dict,p_static);
+ if (!key)
+ return NULL;
+ expecting=DICT_EXPECT_COLON;
+ }
+ }
+
+ if (expecting==DICT_EXPECT_VALUE) {
+ Node *value = _parse_expression(dict,p_static);
+ if (!value)
+ return NULL;
+ expecting=DICT_EXPECT_COMMA;
+
+ DictionaryNode::Pair pair;
+ pair.key=key;
+ pair.value=value;
+ dict->elements.push_back(pair);
+ key=NULL;
+
+ }
+
+ }
+ }
+
+ expr=dict;
+
+ } else if (tokenizer.get_token()==GDTokenizer::TK_PERIOD && tokenizer.get_token(1)==GDTokenizer::TK_IDENTIFIER && tokenizer.get_token(2)==GDTokenizer::TK_PARENTHESIS_OPEN) {
+ // parent call
+
+ tokenizer.advance(); //goto identifier
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=OperatorNode::OP_PARENT_CALL;
+
+
+ /*SelfNode *self = alloc_node<SelfNode>();
+ op->arguments.push_back(self);
+ forbidden for now */
+
+ IdentifierNode* id = alloc_node<IdentifierNode>();
+ id->name=tokenizer.get_token_identifier();
+ op->arguments.push_back(id);
+
+ tokenizer.advance(2);
+ if (!_parse_arguments(op,op->arguments,p_static))
+ return NULL;
+
+ expr=op;
+
+ } else {
+
+ //find list [ or find dictionary {
+
+ print_line("found bug?");
+
+ _set_error("Error parsing expression, misplaced: "+String(tokenizer.get_token_name(tokenizer.get_token())));
+ return NULL; //nothing
+ }
+
+ if (!expr) {
+ ERR_EXPLAIN("GDParser bug, couldn't figure out what expression is..");
+ ERR_FAIL_COND_V(!expr,NULL);
+ }
+
+
+ /******************/
+ /* Parse Indexing */
+ /******************/
+
+
+ while (true) {
+
+ //expressions can be indexed any number of times
+
+ if (tokenizer.get_token()==GDTokenizer::TK_PERIOD) {
+
+ //indexing using "."
+
+ if (tokenizer.get_token(1)!=GDTokenizer::TK_IDENTIFIER && tokenizer.get_token(1)!=GDTokenizer::TK_BUILT_IN_FUNC ) {
+ _set_error("Expected identifier as member");
+ return NULL;
+ } else if (tokenizer.get_token(2)==GDTokenizer::TK_PARENTHESIS_OPEN) {
+ //call!!
+ OperatorNode * op = alloc_node<OperatorNode>();
+ op->op=OperatorNode::OP_CALL;
+
+ IdentifierNode * id = alloc_node<IdentifierNode>();
+ if (tokenizer.get_token(1)==GDTokenizer::TK_BUILT_IN_FUNC ) {
+ //small hack so built in funcs don't obfuscate methods
+
+ id->name=GDFunctions::get_func_name(tokenizer.get_token_built_in_func(1));
+ } else {
+ id->name=tokenizer.get_token_identifier(1);
+ }
+
+ op->arguments.push_back(expr); // call what
+ op->arguments.push_back(id); // call func
+ //get arguments
+ tokenizer.advance(3);
+ if (!_parse_arguments(op,op->arguments,p_static))
+ return NULL;
+ expr=op;
+
+ } else {
+ //simple indexing!
+ OperatorNode * op = alloc_node<OperatorNode>();
+ op->op=OperatorNode::OP_INDEX_NAMED;
+
+ IdentifierNode * id = alloc_node<IdentifierNode>();
+ id->name=tokenizer.get_token_identifier(1);
+
+ op->arguments.push_back(expr);
+ op->arguments.push_back(id);
+
+ expr=op;
+
+ tokenizer.advance(2);
+ }
+
+ } else if (tokenizer.get_token()==GDTokenizer::TK_BRACKET_OPEN) {
+ //indexing using "[]"
+ OperatorNode * op = alloc_node<OperatorNode>();
+ op->op=OperatorNode::OP_INDEX;
+
+ tokenizer.advance(1);
+
+ Node *subexpr = _parse_expression(op,p_static);
+ if (!subexpr) {
+ return NULL;
+ }
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_BRACKET_CLOSE) {
+ _set_error("Expected ']'");
+ return NULL;
+ }
+
+ op->arguments.push_back(expr);
+ op->arguments.push_back(subexpr);
+ tokenizer.advance(1);
+ expr=op;
+
+ } else
+ break;
+ }
+
+ /******************/
+ /* Parse Operator */
+ /******************/
+
+
+ Expression e;
+ e.is_op=false;
+ e.node=expr;
+ expression.push_back(e);
+
+ // determine which operator is next
+
+ OperatorNode::Operator op;
+ bool valid=true;
+
+//assign, if allowed is only alowed on the first operator
+#define _VALIDATE_ASSIGN if (!p_allow_assign) { _set_error("Unexpected assign."); return NULL; } p_allow_assign=false;
+ switch(tokenizer.get_token()) { //see operator
+
+ case GDTokenizer::TK_OP_IN: op=OperatorNode::OP_IN; break;
+ case GDTokenizer::TK_OP_EQUAL: op=OperatorNode::OP_EQUAL ; break;
+ case GDTokenizer::TK_OP_NOT_EQUAL: op=OperatorNode::OP_NOT_EQUAL ; break;
+ case GDTokenizer::TK_OP_LESS: op=OperatorNode::OP_LESS ; break;
+ case GDTokenizer::TK_OP_LESS_EQUAL: op=OperatorNode::OP_LESS_EQUAL ; break;
+ case GDTokenizer::TK_OP_GREATER: op=OperatorNode::OP_GREATER ; break;
+ case GDTokenizer::TK_OP_GREATER_EQUAL: op=OperatorNode::OP_GREATER_EQUAL ; break;
+ case GDTokenizer::TK_OP_AND: op=OperatorNode::OP_AND ; break;
+ case GDTokenizer::TK_OP_OR: op=OperatorNode::OP_OR ; break;
+ case GDTokenizer::TK_OP_ADD: op=OperatorNode::OP_ADD ; break;
+ case GDTokenizer::TK_OP_SUB: op=OperatorNode::OP_SUB ; break;
+ case GDTokenizer::TK_OP_MUL: op=OperatorNode::OP_MUL ; break;
+ case GDTokenizer::TK_OP_DIV: op=OperatorNode::OP_DIV ; break;
+ case GDTokenizer::TK_OP_MOD: op=OperatorNode::OP_MOD ; break;
+ //case GDTokenizer::TK_OP_NEG: op=OperatorNode::OP_NEG ; break;
+ case GDTokenizer::TK_OP_SHIFT_LEFT: op=OperatorNode::OP_SHIFT_LEFT ; break;
+ case GDTokenizer::TK_OP_SHIFT_RIGHT: op=OperatorNode::OP_SHIFT_RIGHT ; break;
+ case GDTokenizer::TK_OP_ASSIGN: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN ; break;
+ case GDTokenizer::TK_OP_ASSIGN_ADD: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_ADD ; break;
+ case GDTokenizer::TK_OP_ASSIGN_SUB: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_SUB ; break;
+ case GDTokenizer::TK_OP_ASSIGN_MUL: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_MUL ; break;
+ case GDTokenizer::TK_OP_ASSIGN_DIV: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_DIV ; break;
+ case GDTokenizer::TK_OP_ASSIGN_MOD: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_MOD ; break;
+ case GDTokenizer::TK_OP_ASSIGN_SHIFT_LEFT: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_SHIFT_LEFT; ; break;
+ case GDTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_SHIFT_RIGHT; ; break;
+ case GDTokenizer::TK_OP_ASSIGN_BIT_AND: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_BIT_AND ; break;
+ case GDTokenizer::TK_OP_ASSIGN_BIT_OR: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_BIT_OR ; break;
+ case GDTokenizer::TK_OP_ASSIGN_BIT_XOR: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_BIT_XOR ; break;
+ case GDTokenizer::TK_OP_BIT_AND: op=OperatorNode::OP_BIT_AND ; break;
+ case GDTokenizer::TK_OP_BIT_OR: op=OperatorNode::OP_BIT_OR ; break;
+ case GDTokenizer::TK_OP_BIT_XOR: op=OperatorNode::OP_BIT_XOR ; break;
+ case GDTokenizer::TK_PR_EXTENDS: op=OperatorNode::OP_EXTENDS; break;
+ default: valid=false; break;
+ }
+
+ if (valid) {
+ e.is_op=true;
+ e.op=op;
+ expression.push_back(e);
+ tokenizer.advance();
+ } else {
+ break;
+ }
+
+ }
+
+ /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */
+
+
+ while(expression.size()>1) {
+
+ int next_op=-1;
+ int min_priority=0xFFFFF;
+ bool is_unary=false;
+
+ for(int i=0;i<expression.size();i++) {
+
+
+
+ if (!expression[i].is_op) {
+
+ continue;
+ }
+
+ int priority;
+
+ bool unary=false;
+
+ switch(expression[i].op) {
+
+ case OperatorNode::OP_EXTENDS: priority=-1; break; //before anything
+
+ case OperatorNode::OP_BIT_INVERT: priority=0; unary=true; break;
+ case OperatorNode::OP_NEG: priority=1; unary=true; break;
+
+ case OperatorNode::OP_MUL: priority=2; break;
+ case OperatorNode::OP_DIV: priority=2; break;
+ case OperatorNode::OP_MOD: priority=2; break;
+
+ case OperatorNode::OP_ADD: priority=3; break;
+ case OperatorNode::OP_SUB: priority=3; break;
+
+ case OperatorNode::OP_SHIFT_LEFT: priority=4; break;
+ case OperatorNode::OP_SHIFT_RIGHT: priority=4; break;
+
+ case OperatorNode::OP_BIT_AND: priority=5; break;
+ case OperatorNode::OP_BIT_XOR: priority=6; break;
+ case OperatorNode::OP_BIT_OR: priority=7; break;
+
+ case OperatorNode::OP_LESS: priority=8; break;
+ case OperatorNode::OP_LESS_EQUAL: priority=8; break;
+ case OperatorNode::OP_GREATER: priority=8; break;
+ case OperatorNode::OP_GREATER_EQUAL: priority=8; break;
+
+ case OperatorNode::OP_EQUAL: priority=8; break;
+ case OperatorNode::OP_NOT_EQUAL: priority=8; break;
+
+ case OperatorNode::OP_IN: priority=10; break;
+
+ case OperatorNode::OP_NOT: priority=11; unary=true; break;
+ case OperatorNode::OP_AND: priority=12; break;
+ case OperatorNode::OP_OR: priority=13; break;
+
+ // ?: = 10
+
+ case OperatorNode::OP_ASSIGN: priority=14; break;
+ case OperatorNode::OP_ASSIGN_ADD: priority=14; break;
+ case OperatorNode::OP_ASSIGN_SUB: priority=14; break;
+ case OperatorNode::OP_ASSIGN_MUL: priority=14; break;
+ case OperatorNode::OP_ASSIGN_DIV: priority=14; break;
+ case OperatorNode::OP_ASSIGN_MOD: priority=14; break;
+ case OperatorNode::OP_ASSIGN_SHIFT_LEFT: priority=14; break;
+ case OperatorNode::OP_ASSIGN_SHIFT_RIGHT: priority=14; break;
+ case OperatorNode::OP_ASSIGN_BIT_AND: priority=14; break;
+ case OperatorNode::OP_ASSIGN_BIT_OR: priority=14; break;
+ case OperatorNode::OP_ASSIGN_BIT_XOR: priority=14; break;
+
+
+ default: {
+ _set_error("GDParser bug, invalid operator in expression: "+itos(expression[i].op));
+ return NULL;
+ }
+
+ }
+
+ if (priority<min_priority) {
+ // < is used for left to right (default)
+ // <= is used for right to left
+ next_op=i;
+ min_priority=priority;
+ is_unary=unary;
+ }
+
+ }
+
+
+ if (next_op==-1) {
+
+
+ _set_error("Yet another parser bug....");
+ ERR_FAIL_COND_V(next_op==-1,NULL);
+ }
+
+
+ // OK! create operator..
+ if (is_unary) {
+
+ int expr_pos=next_op;
+ while(expression[expr_pos].is_op) {
+
+ expr_pos++;
+ if (expr_pos==expression.size()) {
+ //can happen..
+ _set_error("Unexpected end of expression..");
+ return NULL;
+ }
+ }
+
+ //consecutively do unary opeators
+ for(int i=expr_pos-1;i>=next_op;i--) {
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=expression[i].op;
+ op->arguments.push_back(expression[i+1].node);
+ expression[i].is_op=false;
+ expression[i].node=op;
+ expression.remove(i+1);
+ }
+
+
+ } else {
+
+ if (next_op <1 || next_op>=(expression.size()-1)) {
+ _set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=expression[next_op].op;
+
+ if (expression[next_op-1].is_op) {
+
+ _set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ if (expression[next_op+1].is_op) {
+ // this is not invalid and can really appear
+ // but it becomes invalid anyway because no binary op
+ // can be followed by an unary op in a valid combination,
+ // due to how precedence works, unaries will always dissapear first
+
+ _set_error("Parser bug..");
+
+ }
+
+
+ op->arguments.push_back(expression[next_op-1].node); //expression goes as left
+ op->arguments.push_back(expression[next_op+1].node); //next expression goes as right
+
+ //replace all 3 nodes by this operator and make it an expression
+ expression[next_op-1].node=op;
+ expression.remove(next_op);
+ expression.remove(next_op);
+ }
+
+ }
+
+ return expression[0].node;
+
+}
+
+
+GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
+
+ switch(p_node->type) {
+
+ case Node::TYPE_BUILT_IN_FUNCTION: {
+ //many may probably be optimizable
+ return p_node;
+ } break;
+ case Node::TYPE_ARRAY: {
+
+ ArrayNode *an = static_cast<ArrayNode*>(p_node);
+ bool all_constants=true;
+
+ for(int i=0;i<an->elements.size();i++) {
+
+ an->elements[i]=_reduce_expression(an->elements[i],p_to_const);
+ if (an->elements[i]->type!=Node::TYPE_CONSTANT)
+ all_constants=false;
+ }
+
+ if (all_constants && p_to_const) {
+ //reduce constant array expression
+
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ Array arr(!p_to_const);
+ arr.resize(an->elements.size());
+ for(int i=0;i<an->elements.size();i++) {
+ ConstantNode *acn = static_cast<ConstantNode*>(an->elements[i]);
+ arr[i]=acn->value;
+
+ }
+ cn->value=arr;
+ return cn;
+ }
+
+ return an;
+
+ } break;
+ case Node::TYPE_DICTIONARY: {
+
+ DictionaryNode *dn = static_cast<DictionaryNode*>(p_node);
+ bool all_constants=true;
+
+ for(int i=0;i<dn->elements.size();i++) {
+
+ dn->elements[i].key=_reduce_expression(dn->elements[i].key,p_to_const);
+ if (dn->elements[i].key->type!=Node::TYPE_CONSTANT)
+ all_constants=false;
+ dn->elements[i].value=_reduce_expression(dn->elements[i].value,p_to_const);
+ if (dn->elements[i].value->type!=Node::TYPE_CONSTANT)
+ all_constants=false;
+
+ }
+
+ if (all_constants && p_to_const) {
+ //reduce constant array expression
+
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ Dictionary dict(!p_to_const);
+ for(int i=0;i<dn->elements.size();i++) {
+ ConstantNode *key_c = static_cast<ConstantNode*>(dn->elements[i].key);
+ ConstantNode *value_c = static_cast<ConstantNode*>(dn->elements[i].value);
+
+ dict[key_c->value]=value_c->value;
+
+ }
+ cn->value=dict;
+ return cn;
+ }
+
+ return dn;
+
+
+ } break;
+ case Node::TYPE_OPERATOR: {
+
+ OperatorNode *op=static_cast<OperatorNode*>(p_node);
+
+ bool all_constants=true;
+ int last_not_constant=-1;
+
+ for(int i=0;i<op->arguments.size();i++) {
+
+ op->arguments[i]=_reduce_expression(op->arguments[i],p_to_const);
+ if (op->arguments[i]->type!=Node::TYPE_CONSTANT) {
+ all_constants=false;
+ last_not_constant=i;
+ }
+ }
+
+ if (op->op==OperatorNode::OP_EXTENDS) {
+ //nothing much
+ return op;
+
+ } if (op->op==OperatorNode::OP_PARENT_CALL) {
+ //nothing much
+ return op;
+
+ } else if (op->op==OperatorNode::OP_CALL) {
+ //can reduce base type constructors
+ if ((op->arguments[0]->type==Node::TYPE_TYPE || (op->arguments[0]->type==Node::TYPE_BUILT_IN_FUNCTION && GDFunctions::is_deterministic( static_cast<BuiltInFunctionNode*>(op->arguments[0])->function))) && last_not_constant==0) {
+
+ //native type constructor or intrinsic function
+ const Variant **vptr=NULL;
+ Vector<Variant*> ptrs;
+ if (op->arguments.size()>1) {
+
+ ptrs.resize(op->arguments.size()-1);
+ for(int i=0;i<ptrs.size();i++) {
+
+
+ ConstantNode *cn = static_cast<ConstantNode*>(op->arguments[i+1]);
+ ptrs[i]=&cn->value;
+ }
+
+ vptr=(const Variant**)&ptrs[0];
+
+
+ }
+
+ Variant::CallError ce;
+ Variant v;
+
+ if (op->arguments[0]->type==Node::TYPE_TYPE) {
+ TypeNode *tn = static_cast<TypeNode*>(op->arguments[0]);
+ v = Variant::construct(tn->vtype,vptr,ptrs.size(),ce);
+
+ } else {
+ GDFunctions::Function func = static_cast<BuiltInFunctionNode*>(op->arguments[0])->function;
+ GDFunctions::call(func,vptr,ptrs.size(),v,ce);
+ }
+
+
+ if (ce.error!=Variant::CallError::CALL_OK) {
+
+ String errwhere;
+ if (op->arguments[0]->type==Node::TYPE_TYPE) {
+ TypeNode *tn = static_cast<TypeNode*>(op->arguments[0]);
+ errwhere="'"+Variant::get_type_name(tn->vtype)+"'' constructor";
+
+ } else {
+ GDFunctions::Function func = static_cast<BuiltInFunctionNode*>(op->arguments[0])->function;
+ errwhere=String("'")+GDFunctions::get_func_name(func)+"'' intrinsic function";
+
+ }
+
+ switch(ce.error) {
+
+ case Variant::CallError::CALL_ERROR_INVALID_ARGUMENT: {
+
+ _set_error("Invalid argument (#"+itos(ce.argument+1)+") for "+errwhere+".");
+
+ } break;
+ case Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
+
+ _set_error("Too many arguments for "+errwhere+".");
+ } break;
+ case Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: {
+
+ _set_error("Too few arguments for "+errwhere+".");
+ } break;
+ default: {
+ _set_error("Invalid arguments for "+errwhere+".");
+
+ } break;
+ }
+
+ return p_node;
+ }
+
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value=v;
+ return cn;
+
+ } else if (op->arguments[0]->type==Node::TYPE_BUILT_IN_FUNCTION && last_not_constant==0) {
+
+
+
+
+
+ }
+
+ return op; //don't reduce yet
+ } else if (op->op==OperatorNode::OP_INDEX) {
+ //can reduce indices into constant arrays or dictionaries
+
+ if (all_constants) {
+
+ ConstantNode *ca = static_cast<ConstantNode*>(op->arguments[0]);
+ ConstantNode *cb = static_cast<ConstantNode*>(op->arguments[1]);
+
+
+
+ bool valid;
+
+ Variant v = ca->value.get(cb->value,&valid);
+ if (!valid) {
+ _set_error("invalid index in constant expression");
+ return op;
+ }
+
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value=v;
+ return cn;
+
+ } else if (op->arguments[0]->type==Node::TYPE_CONSTANT && op->arguments[1]->type==Node::TYPE_IDENTIFIER) {
+
+ ConstantNode *ca = static_cast<ConstantNode*>(op->arguments[0]);
+ IdentifierNode *ib = static_cast<IdentifierNode*>(op->arguments[1]);
+
+ bool valid;
+ Variant v = ca->value.get_named(ib->name,&valid);
+ if (!valid) {
+ _set_error("invalid index '"+String(ib->name)+"' in constant expression");
+ return op;
+ }
+
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value=v;
+ return cn;
+
+ }
+
+ return op;
+ }
+
+ //validate assignment (don't assign to cosntant expression
+ switch(op->op) {
+
+ case OperatorNode::OP_ASSIGN:
+ case OperatorNode::OP_ASSIGN_ADD:
+ case OperatorNode::OP_ASSIGN_SUB:
+ case OperatorNode::OP_ASSIGN_MUL:
+ case OperatorNode::OP_ASSIGN_DIV:
+ case OperatorNode::OP_ASSIGN_MOD:
+ case OperatorNode::OP_ASSIGN_SHIFT_LEFT:
+ case OperatorNode::OP_ASSIGN_SHIFT_RIGHT:
+ case OperatorNode::OP_ASSIGN_BIT_AND:
+ case OperatorNode::OP_ASSIGN_BIT_OR:
+ case OperatorNode::OP_ASSIGN_BIT_XOR: {
+
+ if (op->arguments[0]->type==Node::TYPE_CONSTANT) {
+ _set_error("Can't assign to constant");
+ return op;
+ }
+
+ } break;
+ default: { break; }
+ }
+ //now se if all are constants
+ if (!all_constants)
+ return op; //nothing to reduce from here on
+#define _REDUCE_UNARY(m_vop)\
+ bool valid=false;\
+ Variant res;\
+ Variant::evaluate(m_vop,static_cast<ConstantNode*>(op->arguments[0])->value,Variant(),res,valid);\
+ if (!valid) {\
+ _set_error("Invalid operand for unary operator");\
+ return p_node;\
+ }\
+ ConstantNode *cn = alloc_node<ConstantNode>();\
+ cn->value=res;\
+ return cn;
+
+#define _REDUCE_BINARY(m_vop)\
+ bool valid=false;\
+ Variant res;\
+ Variant::evaluate(m_vop,static_cast<ConstantNode*>(op->arguments[0])->value,static_cast<ConstantNode*>(op->arguments[1])->value,res,valid);\
+ if (!valid) {\
+ _set_error("Invalid operands for operator");\
+ return p_node;\
+ }\
+ ConstantNode *cn = alloc_node<ConstantNode>();\
+ cn->value=res;\
+ return cn;
+
+ switch(op->op) {
+
+ //unary operators
+ case OperatorNode::OP_NEG: { _REDUCE_UNARY(Variant::OP_NEGATE); } break;
+ case OperatorNode::OP_NOT: { _REDUCE_UNARY(Variant::OP_NOT); } break;
+ case OperatorNode::OP_BIT_INVERT: { _REDUCE_UNARY(Variant::OP_BIT_NEGATE); } break;
+ //binary operators (in precedence order)
+ case OperatorNode::OP_IN: { _REDUCE_BINARY(Variant::OP_IN); } break;
+ case OperatorNode::OP_EQUAL: { _REDUCE_BINARY(Variant::OP_EQUAL); } break;
+ case OperatorNode::OP_NOT_EQUAL: { _REDUCE_BINARY(Variant::OP_NOT_EQUAL); } break;
+ case OperatorNode::OP_LESS: { _REDUCE_BINARY(Variant::OP_LESS); } break;
+ case OperatorNode::OP_LESS_EQUAL: { _REDUCE_BINARY(Variant::OP_LESS_EQUAL); } break;
+ case OperatorNode::OP_GREATER: { _REDUCE_BINARY(Variant::OP_GREATER); } break;
+ case OperatorNode::OP_GREATER_EQUAL: { _REDUCE_BINARY(Variant::OP_GREATER_EQUAL); } break;
+ case OperatorNode::OP_AND: { _REDUCE_BINARY(Variant::OP_AND); } break;
+ case OperatorNode::OP_OR: { _REDUCE_BINARY(Variant::OP_OR); } break;
+ case OperatorNode::OP_ADD: { _REDUCE_BINARY(Variant::OP_ADD); } break;
+ case OperatorNode::OP_SUB: { _REDUCE_BINARY(Variant::OP_SUBSTRACT); } break;
+ case OperatorNode::OP_MUL: { _REDUCE_BINARY(Variant::OP_MULTIPLY); } break;
+ case OperatorNode::OP_DIV: { _REDUCE_BINARY(Variant::OP_DIVIDE); } break;
+ case OperatorNode::OP_MOD: { _REDUCE_BINARY(Variant::OP_MODULE); } break;
+ case OperatorNode::OP_SHIFT_LEFT: { _REDUCE_BINARY(Variant::OP_SHIFT_LEFT); } break;
+ case OperatorNode::OP_SHIFT_RIGHT: { _REDUCE_BINARY(Variant::OP_SHIFT_RIGHT); } break;
+ case OperatorNode::OP_BIT_AND: { _REDUCE_BINARY(Variant::OP_BIT_AND); } break;
+ case OperatorNode::OP_BIT_OR: { _REDUCE_BINARY(Variant::OP_BIT_OR); } break;
+ case OperatorNode::OP_BIT_XOR: { _REDUCE_BINARY(Variant::OP_BIT_XOR); } break;
+ default: { ERR_FAIL_V(op); }
+ }
+
+ ERR_FAIL_V(op);
+ } break;
+ default: {
+ return p_node;
+ } break;
+
+ }
+}
+
+GDParser::Node* GDParser::_parse_and_reduce_expression(Node *p_parent,bool p_static,bool p_reduce_const,bool p_allow_assign) {
+
+ Node* expr=_parse_expression(p_parent,p_static,p_allow_assign);
+ if (!expr || error_set)
+ return NULL;
+ expr = _reduce_expression(expr,p_reduce_const);
+ if (!expr || error_set)
+ return NULL;
+ return expr;
+}
+
+void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
+
+ int indent_level = tab_level.back()->get();
+
+
+#ifdef DEBUG_ENABLED
+
+ NewLineNode *nl = alloc_node<NewLineNode>();
+
+ nl->line=tokenizer.get_token_line();
+ p_block->statements.push_back(nl);
+#endif
+
+ while(true) {
+
+ GDTokenizer::Token token = tokenizer.get_token();
+ if (error_set)
+ return;
+
+ if (indent_level>tab_level.back()->get()) {
+ p_block->end_line=tokenizer.get_token_line();
+ return; //go back a level
+ }
+
+ switch(token) {
+
+
+ case GDTokenizer::TK_EOF:
+ p_block->end_line=tokenizer.get_token_line();
+ case GDTokenizer::TK_ERROR: {
+ return; //go back
+
+ //end of file!
+
+ } break;
+ case GDTokenizer::TK_NEWLINE: {
+
+ NewLineNode *nl = alloc_node<NewLineNode>();
+ nl->line=tokenizer.get_token_line();
+ p_block->statements.push_back(nl);
+
+ if (!_parse_newline()) {
+ if (!error_set) {
+ p_block->end_line=tokenizer.get_token_line();
+ }
+ return;
+ }
+ } break;
+ case GDTokenizer::TK_CF_PASS: {
+ if (tokenizer.get_token(1)!=GDTokenizer::TK_SEMICOLON && tokenizer.get_token(1)!=GDTokenizer::TK_NEWLINE ) {
+
+ _set_error("Expected ';' or <NewLine>.");
+ return;
+ }
+ tokenizer.advance();
+ } break;
+ case GDTokenizer::TK_PR_VAR: {
+ //variale declaration and (eventual) initialization
+
+ tokenizer.advance();
+ if (tokenizer.get_token()!=GDTokenizer::TK_IDENTIFIER) {
+
+ _set_error("Expected identifier for local variable name.");
+ return;
+ }
+ StringName n = tokenizer.get_token_identifier();
+ tokenizer.advance();
+
+ p_block->variables.push_back(n); //line?
+ p_block->variable_lines.push_back(tokenizer.get_token_line());
+
+
+ //must know when the local variable is declared
+ LocalVarNode *lv = alloc_node<LocalVarNode>();
+ lv->name=n;
+ p_block->statements.push_back(lv);
+
+ Node *assigned=NULL;
+
+ if (tokenizer.get_token()==GDTokenizer::TK_OP_ASSIGN) {
+
+ tokenizer.advance();
+ Node *subexpr=NULL;
+
+ subexpr = _parse_and_reduce_expression(p_block,p_static);
+ if (!subexpr)
+ return;
+
+ lv->assign=subexpr;
+ assigned=subexpr;
+ } else {
+
+ ConstantNode *c = alloc_node<ConstantNode>();
+ c->value=Variant();
+ assigned = c;
+
+ }
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name=n;
+
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=OperatorNode::OP_ASSIGN;
+ op->arguments.push_back(id);
+ op->arguments.push_back(assigned);
+ p_block->statements.push_back(op);
+
+ _end_statement();
+
+
+ } break;
+ case GDTokenizer::TK_CF_IF: {
+
+ tokenizer.advance();
+ Node *condition = _parse_and_reduce_expression(p_block,p_static);
+ if (!condition)
+ return;
+
+ ControlFlowNode *cf_if = alloc_node<ControlFlowNode>();
+
+ cf_if->cf_type=ControlFlowNode::CF_IF;
+ cf_if->arguments.push_back(condition);
+
+ cf_if->body = alloc_node<BlockNode>();
+ p_block->sub_blocks.push_back(cf_if->body);
+
+ if (!_enter_indent_block(cf_if->body)) {
+ p_block->end_line=tokenizer.get_token_line();
+ return;
+ }
+
+ _parse_block(cf_if->body,p_static);
+ if (error_set)
+ return;
+ p_block->statements.push_back(cf_if);
+
+ while(true) {
+
+ while(tokenizer.get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer.advance();
+ }
+
+ if (tab_level.back()->get() < indent_level) { //not at current indent level
+ p_block->end_line=tokenizer.get_token_line();
+ return;
+ }
+
+ if (tokenizer.get_token()==GDTokenizer::TK_CF_ELIF) {
+
+ if (tab_level.back()->get() > indent_level) {
+
+ _set_error("Invalid indent");
+ return;
+ }
+
+ tokenizer.advance();
+
+ cf_if->body_else=alloc_node<BlockNode>();
+ p_block->sub_blocks.push_back(cf_if->body_else);
+
+ ControlFlowNode *cf_else = alloc_node<ControlFlowNode>();
+ cf_else->cf_type=ControlFlowNode::CF_IF;
+
+ //condition
+ Node *condition = _parse_and_reduce_expression(p_block,p_static);
+ if (!condition)
+ return;
+ cf_else->arguments.push_back(condition);
+ cf_else->cf_type=ControlFlowNode::CF_IF;
+
+ cf_if->body_else->statements.push_back(cf_else);
+ cf_if=cf_else;
+ cf_if->body=alloc_node<BlockNode>();
+ p_block->sub_blocks.push_back(cf_if->body);
+
+
+ if (!_enter_indent_block(cf_if->body)) {
+ p_block->end_line=tokenizer.get_token_line();
+ return;
+ }
+
+ _parse_block(cf_else->body,p_static);
+ if (error_set)
+ return;
+
+
+ } else if (tokenizer.get_token()==GDTokenizer::TK_CF_ELSE) {
+
+ if (tab_level.back()->get() > indent_level) {
+
+ _set_error("Invalid indent");
+ return;
+ }
+
+
+ tokenizer.advance();
+ cf_if->body_else=alloc_node<BlockNode>();
+ p_block->sub_blocks.push_back(cf_if->body_else);
+
+ if (!_enter_indent_block(cf_if->body_else)) {
+ p_block->end_line=tokenizer.get_token_line();
+ return;
+ }
+ _parse_block(cf_if->body_else,p_static);
+ if (error_set)
+ return;
+
+
+ break; //after else, exit
+
+ } else
+ break;
+
+ }
+
+
+ } break;
+ case GDTokenizer::TK_CF_WHILE: {
+
+ tokenizer.advance();
+ Node *condition = _parse_and_reduce_expression(p_block,p_static);
+ if (!condition)
+ return;
+
+ ControlFlowNode *cf_while = alloc_node<ControlFlowNode>();
+
+ cf_while->cf_type=ControlFlowNode::CF_WHILE;
+ cf_while->arguments.push_back(condition);
+
+ cf_while->body = alloc_node<BlockNode>();
+ p_block->sub_blocks.push_back(cf_while->body);
+
+ if (!_enter_indent_block(cf_while->body)) {
+ p_block->end_line=tokenizer.get_token_line();
+ return;
+ }
+
+ _parse_block(cf_while->body,p_static);
+ if (error_set)
+ return;
+ p_block->statements.push_back(cf_while);
+ } break;
+ case GDTokenizer::TK_CF_FOR: {
+
+ tokenizer.advance();
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_IDENTIFIER) {
+
+ _set_error("identifier expected after 'for'");
+ }
+
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name=tokenizer.get_token_identifier();
+
+ tokenizer.advance();
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_OP_IN) {
+ _set_error("'in' expected after identifier");
+ return;
+ }
+
+ tokenizer.advance();
+
+ Node *container = _parse_and_reduce_expression(p_block,p_static);
+ if (!container)
+ return;
+
+ ControlFlowNode *cf_for = alloc_node<ControlFlowNode>();
+
+ cf_for->cf_type=ControlFlowNode::CF_FOR;
+ cf_for->arguments.push_back(id);
+ cf_for->arguments.push_back(container);
+
+ cf_for->body = alloc_node<BlockNode>();
+ p_block->sub_blocks.push_back(cf_for->body);
+
+ if (!_enter_indent_block(cf_for->body)) {
+ p_block->end_line=tokenizer.get_token_line();
+ return;
+ }
+
+ _parse_block(cf_for->body,p_static);
+ if (error_set)
+ return;
+ p_block->statements.push_back(cf_for);
+ } break;
+ case GDTokenizer::TK_CF_CONTINUE: {
+
+ tokenizer.advance();
+ ControlFlowNode *cf_continue = alloc_node<ControlFlowNode>();
+ cf_continue->cf_type=ControlFlowNode::CF_CONTINUE;
+ p_block->statements.push_back(cf_continue);
+ if (!_end_statement()) {
+ _set_error("Expected end of statement (continue)");
+ return;
+ }
+ } break;
+ case GDTokenizer::TK_CF_BREAK: {
+
+ tokenizer.advance();
+ ControlFlowNode *cf_break = alloc_node<ControlFlowNode>();
+ cf_break->cf_type=ControlFlowNode::CF_BREAK;
+ p_block->statements.push_back(cf_break);
+ if (!_end_statement()) {
+ _set_error("Expected end of statement (break)");
+ return;
+ }
+ } break;
+ case GDTokenizer::TK_CF_RETURN: {
+
+ tokenizer.advance();
+ ControlFlowNode *cf_return = alloc_node<ControlFlowNode>();
+ cf_return->cf_type=ControlFlowNode::CF_RETURN;
+
+
+
+ if (tokenizer.get_token()==GDTokenizer::TK_SEMICOLON || tokenizer.get_token()==GDTokenizer::TK_NEWLINE || tokenizer.get_token()==GDTokenizer::TK_EOF) {
+ //expect end of statement
+ p_block->statements.push_back(cf_return);
+ if (!_end_statement()) {
+ return;
+ }
+ } else {
+ //expect expression
+ Node *retexpr = _parse_and_reduce_expression(p_block,p_static);
+ if (!retexpr)
+ return;
+ cf_return->arguments.push_back(retexpr);
+ p_block->statements.push_back(cf_return);
+ if (!_end_statement()) {
+ _set_error("Expected end of statement after return expression.");
+ return;
+ }
+ }
+
+
+ } break;
+ case GDTokenizer::TK_PR_ASSERT: {
+
+ tokenizer.advance();
+ Node *condition = _parse_and_reduce_expression(p_block,p_static);
+ if (!condition)
+ return;
+ AssertNode *an = alloc_node<AssertNode>();
+ an->condition=condition;
+ p_block->statements.push_back(an);
+
+ if (!_end_statement()) {
+ _set_error("Expected end of statement after assert.");
+ return;
+ }
+ } break;
+ default: {
+
+ Node *expression = _parse_and_reduce_expression(p_block,p_static,false,true);
+ if (!expression)
+ return;
+ p_block->statements.push_back(expression);
+ if (!_end_statement()) {
+ _set_error("Expected end of statement after expression.");
+ return;
+ }
+
+ } break;
+ /*
+ case GDTokenizer::TK_CF_LOCAL: {
+
+ if (tokenizer.get_token(1)!=GDTokenizer::TK_SEMICOLON && tokenizer.get_token(1)!=GDTokenizer::TK_NEWLINE ) {
+
+ _set_error("Expected ';' or <NewLine>.");
+ }
+ tokenizer.advance();
+ } break;
+ */
+
+ }
+ }
+
+}
+
+bool GDParser::_parse_newline() {
+
+ if (tokenizer.get_token(1)!=GDTokenizer::TK_EOF && tokenizer.get_token(1)!=GDTokenizer::TK_NEWLINE) {
+
+ int indent = tokenizer.get_token_line_indent();
+ int current_indent = tab_level.back()->get();
+
+ if (indent>current_indent) {
+ _set_error("Unexpected indent.");
+ return false;
+ }
+
+ if (indent<current_indent) {
+
+ while(indent<current_indent) {
+
+ //exit block
+ if (tab_level.size()==1) {
+ _set_error("Invalid indent. BUG?");
+ return false;
+ }
+
+ tab_level.pop_back();
+
+ if (tab_level.back()->get()<indent) {
+
+ _set_error("Unindent does not match any outer indentation level.");
+ return false;
+ }
+ current_indent = tab_level.back()->get();
+ }
+
+ tokenizer.advance();
+ return false;
+ }
+ }
+
+ tokenizer.advance();
+ return true;
+
+}
+
+
+void GDParser::_parse_extends(ClassNode *p_class) {
+
+
+ if (p_class->extends_used) {
+
+ _set_error("'extends' already used for this class.");
+ return;
+ }
+
+ if (!p_class->constant_expressions.empty() || !p_class->subclasses.empty() || !p_class->functions.empty() || !p_class->variables.empty()) {
+
+ _set_error("'extends' must be used before anything else.");
+ return;
+ }
+
+ p_class->extends_used=true;
+
+ //see if inheritance happens from a file
+ tokenizer.advance();
+
+ if (tokenizer.get_token()==GDTokenizer::TK_CONSTANT) {
+
+ Variant constant = tokenizer.get_token_constant();
+ if (constant.get_type()!=Variant::STRING) {
+
+ _set_error("'extends' constant must be a string.");
+ return;
+ }
+
+ p_class->extends_file=constant;
+ tokenizer.advance();
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_PERIOD) {
+ return;
+ } else
+ tokenizer.advance();
+
+ }
+
+ while(true) {
+ if (tokenizer.get_token()!=GDTokenizer::TK_IDENTIFIER) {
+
+ _set_error("Invalid 'extends' syntax, expected string constant (path) and/or identifier (parent class).");
+ return;
+ }
+
+ StringName identifier=tokenizer.get_token_identifier();
+ p_class->extends_class.push_back(identifier);
+
+ tokenizer.advance(1);
+ if (tokenizer.get_token()!=GDTokenizer::TK_PERIOD)
+ return;
+ }
+
+}
+
+void GDParser::_parse_class(ClassNode *p_class) {
+
+ int indent_level = tab_level.back()->get();
+
+ while(true) {
+
+ GDTokenizer::Token token = tokenizer.get_token();
+ if (error_set)
+ return;
+
+ if (indent_level>tab_level.back()->get()) {
+ p_class->end_line=tokenizer.get_token_line();
+ return; //go back a level
+ }
+
+ switch(token) {
+
+ case GDTokenizer::TK_EOF:
+ p_class->end_line=tokenizer.get_token_line();
+ case GDTokenizer::TK_ERROR: {
+ return; //go back
+ //end of file!
+ } break;
+ case GDTokenizer::TK_NEWLINE: {
+ if (!_parse_newline()) {
+ if (!error_set) {
+ p_class->end_line=tokenizer.get_token_line();
+ }
+ return;
+ }
+ } break;
+ case GDTokenizer::TK_PR_EXTENDS: {
+
+ _parse_extends(p_class);
+ if (error_set)
+ return;
+ _end_statement();
+
+
+ } break;
+ case GDTokenizer::TK_PR_TOOL: {
+
+ if (p_class->tool) {
+
+ _set_error("tool used more than once");
+ return;
+ }
+
+ p_class->tool=true;
+ tokenizer.advance();
+
+ } break;
+ case GDTokenizer::TK_PR_CLASS: {
+ //class inside class :D
+
+ StringName name;
+ StringName extends;
+
+ if (tokenizer.get_token(1)!=GDTokenizer::TK_IDENTIFIER) {
+
+ _set_error("'class' syntax: 'class <Name>:' or 'class <Name> extends <BaseClass>:'");
+ return;
+ }
+ name = tokenizer.get_token_identifier(1);
+ tokenizer.advance(2);
+
+ ClassNode *newclass = alloc_node<ClassNode>();
+ newclass->initializer = alloc_node<BlockNode>();
+ newclass->name=name;
+
+ p_class->subclasses.push_back(newclass);
+
+
+ if (tokenizer.get_token()==GDTokenizer::TK_PR_EXTENDS) {
+
+ _parse_extends(newclass);
+ if (error_set)
+ return;
+ }
+
+ if (!_enter_indent_block()) {
+
+ _set_error("Indented block expected.");
+ return;
+ }
+ _parse_class(newclass);
+
+ } break;
+ /* this is for functions....
+ case GDTokenizer::TK_CF_PASS: {
+
+ tokenizer.advance(1);
+ } break;
+ */
+ case GDTokenizer::TK_PR_STATIC: {
+ tokenizer.advance();
+ if (tokenizer.get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+
+ _set_error("Expected 'func'.");
+ return;
+ }
+
+ }; //fallthrough to function
+ case GDTokenizer::TK_PR_FUNCTION: {
+
+ bool _static=false;
+
+ if (tokenizer.get_token(-1)==GDTokenizer::TK_PR_STATIC) {
+
+ _static=true;
+ }
+
+
+ if (tokenizer.get_token(1)!=GDTokenizer::TK_IDENTIFIER) {
+
+ _set_error("Expected identifier after 'func' (syntax: 'func <identifier>([arguments]):' ).");
+ return;
+ }
+
+ StringName name = tokenizer.get_token_identifier(1);
+
+ for(int i=0;i<p_class->functions.size();i++) {
+ if (p_class->functions[i]->name==name) {
+ _set_error("Function '"+String(name)+"' already exists in this class (at line: "+itos(p_class->functions[i]->line)+").");
+ }
+ }
+ for(int i=0;i<p_class->static_functions.size();i++) {
+ if (p_class->static_functions[i]->name==name) {
+ _set_error("Function '"+String(name)+"' already exists in this class (at line: "+itos(p_class->static_functions[i]->line)+").");
+ }
+ }
+ tokenizer.advance(2);
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_PARENTHESIS_OPEN) {
+
+ _set_error("Expected '(' after identifier (syntax: 'func <identifier>([arguments]):' ).");
+ return;
+ }
+
+ tokenizer.advance();
+
+ Vector<StringName> arguments;
+ Vector<Node*> default_values;
+
+ int fnline = tokenizer.get_token_line();
+
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ //has arguments
+ bool defaulting=false;
+ while(true) {
+
+ if (tokenizer.get_token()==GDTokenizer::TK_PR_VAR) {
+
+ tokenizer.advance(); //var before the identifier is allowed
+ }
+
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_IDENTIFIER) {
+
+ _set_error("Expected identifier for argument.");
+ return;
+ }
+
+ StringName argname=tokenizer.get_token_identifier();
+ arguments.push_back(argname);
+
+ tokenizer.advance();
+
+ if (defaulting && tokenizer.get_token()!=GDTokenizer::TK_OP_ASSIGN) {
+
+ _set_error("Default parameter expected.");
+ return;
+ }
+
+ //tokenizer.advance();
+
+
+ if (tokenizer.get_token()==GDTokenizer::TK_OP_ASSIGN) {
+ defaulting=true;
+ tokenizer.advance(1);
+ Node *defval=NULL;
+
+ defval=_parse_and_reduce_expression(p_class,_static);
+ if (!defval || error_set)
+ return;
+
+ OperatorNode *on = alloc_node<OperatorNode>();
+ on->op=OperatorNode::OP_ASSIGN;
+
+ IdentifierNode *in = alloc_node<IdentifierNode>();
+ in->name=argname;
+
+ on->arguments.push_back(in);
+ on->arguments.push_back(defval);
+ /* no ..
+ if (defval->type!=Node::TYPE_CONSTANT) {
+
+ _set_error("default argument must be constant");
+ }
+ */
+ default_values.push_back(on);
+ }
+
+ if (tokenizer.get_token()==GDTokenizer::TK_COMMA) {
+ tokenizer.advance();
+ continue;
+ } else if (tokenizer.get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+
+ _set_error("Expected ',' or ')'.");
+ return;
+ }
+
+ break;
+ }
+
+
+ }
+
+ tokenizer.advance();
+
+ BlockNode *block = alloc_node<BlockNode>();
+
+ if (name=="_init") {
+
+ if (p_class->extends_used) {
+
+ OperatorNode *cparent = alloc_node<OperatorNode>();
+ cparent->op=OperatorNode::OP_PARENT_CALL;
+ block->statements.push_back(cparent);
+
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name="_init";
+ cparent->arguments.push_back(id);
+
+ if (tokenizer.get_token()==GDTokenizer::TK_PERIOD) {
+ tokenizer.advance();
+ if (tokenizer.get_token()!=GDTokenizer::TK_PARENTHESIS_OPEN) {
+ _set_error("expected '(' for parent constructor arguments.");
+ }
+ tokenizer.advance();
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ //has arguments
+ while(true) {
+
+ Node *arg = _parse_and_reduce_expression(p_class,_static);
+ cparent->arguments.push_back(arg);
+
+ if (tokenizer.get_token()==GDTokenizer::TK_COMMA) {
+ tokenizer.advance();
+ continue;
+ } else if (tokenizer.get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+
+ _set_error("Expected ',' or ')'.");
+ return;
+ }
+
+ break;
+
+ }
+ }
+
+ tokenizer.advance();
+ }
+ } else {
+
+
+ if (tokenizer.get_token()==GDTokenizer::TK_PERIOD) {
+
+ _set_error("Parent constructor call found for a class without inheritance.");
+ return;
+ }
+
+ }
+ }
+
+ if (!_enter_indent_block(block)) {
+
+ _set_error("Indented block expected.");
+ return;
+ }
+
+ FunctionNode *function = alloc_node<FunctionNode>();
+ function->name=name;
+ function->arguments=arguments;
+ function->default_values=default_values;
+ function->_static=_static;
+ function->line=fnline;
+
+
+ if (_static)
+ p_class->static_functions.push_back(function);
+ else
+ p_class->functions.push_back(function);
+
+
+ _parse_block(block,_static);
+ function->body=block;
+ //arguments
+ } break;
+ case GDTokenizer::TK_PR_EXPORT: {
+
+ tokenizer.advance();
+
+ if (tokenizer.get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
+
+ tokenizer.advance();
+ if (tokenizer.get_token()==GDTokenizer::TK_BUILT_IN_TYPE) {
+
+ Variant::Type type = tokenizer.get_token_type();
+ if (type==Variant::NIL) {
+ _set_error("Can't export null type.");
+ return;
+ }
+ current_export.type=type;
+ tokenizer.advance();
+ if (tokenizer.get_token()==GDTokenizer::TK_COMMA) {
+ // hint expected next!
+ tokenizer.advance();
+ switch(current_export.type) {
+
+
+ case Variant::INT: {
+
+ if (tokenizer.get_token()==GDTokenizer::TK_CONSTANT && tokenizer.get_token_constant().get_type()==Variant::STRING) {
+ //enumeration
+ current_export.hint=PROPERTY_HINT_ENUM;
+ bool first=true;
+ while(true) {
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_CONSTANT || tokenizer.get_token_constant().get_type()!=Variant::STRING) {
+
+ current_export=PropertyInfo();
+ _set_error("Expected a string constant in enumeration hint.");
+ }
+
+ String c = tokenizer.get_token_constant();
+ if (!first)
+ current_export.hint_string+=",";
+ else
+ first=false;
+
+ current_export.hint_string+=c.xml_escape();
+
+ tokenizer.advance();
+ if (tokenizer.get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE)
+ break;
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_COMMA) {
+ current_export=PropertyInfo();
+ _set_error("Expected ')' or ',' in enumeration hint.");
+ }
+
+ tokenizer.advance();
+
+ }
+
+ break;
+ }
+
+ };
+ case Variant::REAL: {
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_CONSTANT || !tokenizer.get_token_constant().is_num()) {
+
+ current_export=PropertyInfo();
+ _set_error("Expected a range in numeric hint.");
+
+ }
+ //enumeration
+ current_export.hint=PROPERTY_HINT_RANGE;
+
+ current_export.hint_string=tokenizer.get_token_constant().operator String();
+ tokenizer.advance();
+
+ if (tokenizer.get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ current_export.hint_string="0,"+current_export.hint_string;
+ break;
+ }
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_COMMA) {
+
+ current_export=PropertyInfo();
+ _set_error("Expected ',' or ')' in numeric range hint.");
+ }
+
+ tokenizer.advance();
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_CONSTANT || !tokenizer.get_token_constant().is_num()) {
+
+ current_export=PropertyInfo();
+ _set_error("Expected a number as upper bound in numeric range hint.");
+ }
+
+ current_export.hint_string+=","+tokenizer.get_token_constant().operator String();
+ tokenizer.advance();
+
+ if (tokenizer.get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE)
+ break;
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_COMMA) {
+
+ current_export=PropertyInfo();
+ _set_error("Expected ',' or ')' in numeric range hint.");
+ }
+
+ tokenizer.advance();
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_CONSTANT || !tokenizer.get_token_constant().is_num()) {
+
+ current_export=PropertyInfo();
+ _set_error("Expected a number as step in numeric range hint.");
+ }
+
+ current_export.hint_string+=","+tokenizer.get_token_constant().operator String();
+ tokenizer.advance();
+
+ } break;
+ case Variant::STRING: {
+
+ if (tokenizer.get_token()==GDTokenizer::TK_CONSTANT && tokenizer.get_token_constant().get_type()==Variant::STRING) {
+ //enumeration
+ current_export.hint=PROPERTY_HINT_ENUM;
+ bool first=true;
+ while(true) {
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_CONSTANT || tokenizer.get_token_constant().get_type()!=Variant::STRING) {
+
+ current_export=PropertyInfo();
+ _set_error("Expected a string constant in enumeration hint.");
+ }
+
+ String c = tokenizer.get_token_constant();
+ if (!first)
+ current_export.hint_string+=",";
+ else
+ first=false;
+
+ current_export.hint_string+=c.xml_escape();
+ tokenizer.advance();
+ if (tokenizer.get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE)
+ break;
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_COMMA) {
+ current_export=PropertyInfo();
+ _set_error("Expected ')' or ',' in enumeration hint.");
+ return;
+ }
+ tokenizer.advance();
+
+ }
+
+ break;
+ }
+
+ if (tokenizer.get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer.get_token_identifier()=="DIR") {
+
+ current_export.hint=PROPERTY_HINT_DIR;
+ tokenizer.advance();
+ if (tokenizer.get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in hint.");
+ return;
+ }
+ break;
+ }
+
+ if (tokenizer.get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer.get_token_identifier()=="FILE") {
+
+ current_export.hint=PROPERTY_HINT_FILE;
+ tokenizer.advance();
+
+ if (tokenizer.get_token()==GDTokenizer::TK_COMMA) {
+
+ tokenizer.advance();
+ if (tokenizer.get_token()!=GDTokenizer::TK_CONSTANT || tokenizer.get_token_constant().get_type()!=Variant::STRING) {
+
+ _set_error("Expected string constant with filter");
+ return;
+ }
+ current_export.hint_string=tokenizer.get_token_constant();
+ tokenizer.advance();
+
+ }
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in hint.");
+ return;
+ }
+ break;
+ }
+ } break;
+ case Variant::COLOR: {
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_IDENTIFIER ) {
+
+ current_export=PropertyInfo();
+ _set_error("Color type hint expects RGB or RGBA as hints");
+ return;
+ }
+
+ String identifier = tokenizer.get_token_identifier();
+ if (identifier=="RGB") {
+ current_export.hint=PROPERTY_HINT_COLOR_NO_ALPHA;
+ } else if (identifier=="RGBA") {
+ //none
+ } else {
+ current_export=PropertyInfo();
+ _set_error("Color type hint expects RGB or RGBA as hints");
+ return;
+ }
+ tokenizer.advance();
+
+ } break;
+ default: {
+
+ current_export=PropertyInfo();
+ _set_error("Type '"+Variant::get_type_name(type)+"' can't take hints.");
+ return;
+ } break;
+ }
+
+ }
+
+ } else if (tokenizer.get_token()==GDTokenizer::TK_IDENTIFIER) {
+
+ String identifier = tokenizer.get_token_identifier();
+ if (!ObjectTypeDB::is_type(identifier,"Resource")) {
+
+ current_export=PropertyInfo();
+ _set_error("Export hint not a type or resource.");
+ }
+
+ current_export.type=Variant::OBJECT;
+ current_export.hint=PROPERTY_HINT_RESOURCE_TYPE;
+ current_export.hint_string=identifier;
+
+ tokenizer.advance();
+ }
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+
+ current_export=PropertyInfo();
+ _set_error("Expected ')' or ',' after export hint.");
+ return;
+
+ }
+
+ tokenizer.advance();
+
+ }
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_PR_VAR) {
+
+ current_export=PropertyInfo();
+ _set_error("Expected 'var'.");
+ return;
+ }
+
+ }; //fallthrough to var
+ case GDTokenizer::TK_PR_VAR: {
+ //variale declaration and (eventual) initialization
+
+ ClassNode::Member member;
+ bool autoexport = tokenizer.get_token(-1)==GDTokenizer::TK_PR_EXPORT;
+ if (current_export.type!=Variant::NIL) {
+ member._export=current_export;
+ current_export=PropertyInfo();
+ }
+
+ tokenizer.advance();
+ if (tokenizer.get_token()!=GDTokenizer::TK_IDENTIFIER) {
+
+ _set_error("Expected identifier for member variable name.");
+ return;
+ }
+
+ member.identifier=tokenizer.get_token_identifier();
+ member._export.name=member.identifier;
+ tokenizer.advance();
+
+ p_class->variables.push_back(member);
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_OP_ASSIGN) {
+
+ if (autoexport) {
+
+ _set_error("Type-less export needs a constant expression assigned to infer type.");
+ return;
+ }
+ break;
+ }
+#ifdef DEBUG_ENABLED
+ int line = tokenizer.get_token_line();
+#endif
+ tokenizer.advance();
+
+ Node *subexpr=NULL;
+
+ subexpr = _parse_and_reduce_expression(p_class,false);
+ if (!subexpr)
+ return;
+
+ if (autoexport) {
+ if (subexpr->type==Node::TYPE_ARRAY) {
+
+ p_class->variables[p_class->variables.size()-1]._export.type=Variant::ARRAY;
+
+ } else if (subexpr->type==Node::TYPE_DICTIONARY) {
+
+ p_class->variables[p_class->variables.size()-1]._export.type=Variant::DICTIONARY;
+
+ } else {
+
+ if (subexpr->type!=Node::TYPE_CONSTANT) {
+
+ _set_error("Type-less export needs a constant expression assigned to infer type.");
+ return;
+ }
+
+ ConstantNode *cn = static_cast<ConstantNode*>(subexpr);
+ if (cn->value.get_type()==Variant::NIL) {
+
+ _set_error("Can't accept a null constant expression for infering export type.");
+ return;
+ }
+ p_class->variables[p_class->variables.size()-1]._export.type=cn->value.get_type();
+ }
+ }
+#ifdef TOOLS_ENABLED
+ if (subexpr->type==Node::TYPE_CONSTANT && p_class->variables[p_class->variables.size()-1]._export.type!=Variant::NIL) {
+
+ ConstantNode *cn = static_cast<ConstantNode*>(subexpr);
+ if (cn->value.get_type()!=Variant::NIL) {
+ p_class->variables[p_class->variables.size()-1].default_value=cn->value;
+ }
+ }
+#endif
+
+
+
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name=member.identifier;
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=OperatorNode::OP_ASSIGN;
+ 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);
+#endif
+ p_class->initializer->statements.push_back(op);
+
+ _end_statement();
+
+ } break;
+ case GDTokenizer::TK_PR_CONST: {
+ //variale declaration and (eventual) initialization
+
+ ClassNode::Constant constant;
+
+ tokenizer.advance();
+ if (tokenizer.get_token()!=GDTokenizer::TK_IDENTIFIER) {
+
+ _set_error("Expected name (identifier) for constant.");
+ return;
+ }
+
+ constant.identifier=tokenizer.get_token_identifier();
+ tokenizer.advance();
+
+ if (tokenizer.get_token()!=GDTokenizer::TK_OP_ASSIGN) {
+ _set_error("Constant expects assignment.");
+ return;
+ }
+
+ tokenizer.advance();
+
+ Node *subexpr=NULL;
+
+ subexpr = _parse_and_reduce_expression(p_class,true,true);
+ if (!subexpr)
+ return;
+
+ if (subexpr->type!=Node::TYPE_CONSTANT) {
+ _set_error("Expected constant expression");
+ }
+ constant.expression=subexpr;
+
+ p_class->constant_expressions.push_back(constant);
+
+ _end_statement();
+
+
+ } break;
+
+
+ default: {
+
+ _set_error(String()+"Unexpected token: "+tokenizer.get_token_name(tokenizer.get_token())+":"+tokenizer.get_token_identifier());
+ return;
+
+ } break;
+
+ }
+
+ }
+
+
+}
+
+
+void GDParser::_set_error(const String& p_error, int p_line, int p_column) {
+
+
+ if (error_set)
+ return; //allow no further errors
+
+ error=p_error;
+ error_line=p_line<0?tokenizer.get_token_line():p_line;
+ error_column=p_column<0?tokenizer.get_token_column():p_column;
+ error_set=true;
+}
+
+String GDParser::get_error() const {
+
+ return error;
+}
+
+int GDParser::get_error_line() const {
+
+ return error_line;
+}
+int GDParser::get_error_column() const {
+
+ return error_column;
+}
+
+
+Error GDParser::parse(const String& p_code,const String& p_base_path) {
+
+ base_path=p_base_path;
+
+ tokenizer.set_code(p_code);
+
+ clear();
+
+ //assume class
+ ClassNode *main_class = alloc_node<ClassNode>();
+ main_class->initializer = alloc_node<BlockNode>();
+
+ _parse_class(main_class);
+
+ if (tokenizer.get_token()==GDTokenizer::TK_ERROR) {
+ error_set=false;
+ _set_error("Parse Error: "+tokenizer.get_token_error());
+ }
+
+ if (error_set) {
+
+ return ERR_PARSE_ERROR;
+ }
+ return OK;
+}
+
+const GDParser::Node *GDParser::get_parse_tree() const {
+
+ return head;
+}
+
+void GDParser::clear() {
+
+ while(list) {
+
+ Node *l=list;
+ list=list->next;
+ memdelete(l);
+ }
+
+ head=NULL;
+ list=NULL;
+
+ error_set=false;
+ tab_level.clear();
+ tab_level.push_back(0);
+ error_line=0;
+ error_column=0;
+ current_export.type=Variant::NIL;
+ error="";
+
+}
+
+GDParser::GDParser() {
+
+ head=NULL;
+ list=NULL;
+ clear();
+
+}
+
+GDParser::~GDParser() {
+
+ clear();
+}
diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h
new file mode 100644
index 0000000000..8011495340
--- /dev/null
+++ b/modules/gdscript/gd_parser.h
@@ -0,0 +1,397 @@
+/*************************************************************************/
+/* gd_parser.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_PARSER_H
+#define GD_PARSER_H
+
+#include "gd_tokenizer.h"
+#include "gd_functions.h"
+#include "map.h"
+#include "object.h"
+
+class GDParser {
+public:
+
+ struct Node {
+
+ enum Type {
+ TYPE_CLASS,
+ TYPE_FUNCTION,
+ TYPE_BUILT_IN_FUNCTION,
+ TYPE_BLOCK,
+ TYPE_IDENTIFIER,
+ TYPE_TYPE,
+ TYPE_CONSTANT,
+ TYPE_ARRAY,
+ TYPE_DICTIONARY,
+ TYPE_SELF,
+ TYPE_OPERATOR,
+ TYPE_CONTROL_FLOW,
+ TYPE_LOCAL_VAR,
+ TYPE_ASSERT,
+ TYPE_NEWLINE,
+ };
+
+ Node * next;
+ int line;
+ int column;
+ Type type;
+
+ virtual ~Node() {}
+ };
+
+ struct FunctionNode;
+ struct BlockNode;
+
+ struct ClassNode : public Node {
+
+ bool tool;
+ StringName name;
+ bool extends_used;
+ StringName extends_file;
+ Vector<StringName> extends_class;
+
+ struct Member {
+ PropertyInfo _export;
+#ifdef TOOLS_ENABLED
+ Variant default_value;
+#endif
+ StringName identifier;
+ };
+ struct Constant {
+ StringName identifier;
+ Node *expression;
+ };
+
+ Vector<ClassNode*> subclasses;
+ Vector<Member> variables;
+ Vector<Constant> constant_expressions;
+ Vector<FunctionNode*> functions;
+ Vector<FunctionNode*> static_functions;
+ BlockNode *initializer;
+ //Vector<Node*> initializers;
+ int end_line;
+
+ ClassNode() { tool=false; type=TYPE_CLASS; extends_used=false; end_line=-1;}
+ };
+
+
+
+ struct FunctionNode : public Node {
+
+ bool _static;
+ StringName name;
+ Vector<StringName> arguments;
+ Vector<Node*> default_values;
+ BlockNode *body;
+
+ FunctionNode() { type=TYPE_FUNCTION; _static=false; }
+
+ };
+
+ struct BlockNode : public Node {
+
+ Map<StringName,int> locals;
+ List<Node*> statements;
+ Vector<StringName> variables;
+ Vector<int> variable_lines;
+
+ //the following is useful for code completion
+ List<BlockNode*> sub_blocks;
+ int end_line;
+ BlockNode() { type=TYPE_BLOCK; end_line=-1;}
+ };
+
+ struct TypeNode : public Node {
+
+ Variant::Type vtype;
+ TypeNode() { type=TYPE_TYPE; }
+ };
+ struct BuiltInFunctionNode : public Node {
+ GDFunctions::Function function;
+ BuiltInFunctionNode() { type=TYPE_BUILT_IN_FUNCTION; }
+ };
+
+ struct IdentifierNode : public Node {
+
+ StringName name;
+ IdentifierNode() { type=TYPE_IDENTIFIER; }
+ };
+
+ struct LocalVarNode : public Node {
+
+ StringName name;
+ Node *assign;
+ LocalVarNode() { type=TYPE_LOCAL_VAR; assign=NULL;}
+ };
+
+ struct ConstantNode : public Node {
+ Variant value;
+ ConstantNode() { type=TYPE_CONSTANT; }
+ };
+
+ struct ArrayNode : public Node {
+
+ Vector<Node*> elements;
+ ArrayNode() { type=TYPE_ARRAY; }
+ };
+
+
+ struct DictionaryNode : public Node {
+
+ struct Pair {
+
+ Node *key;
+ Node *value;
+ };
+
+ Vector<Pair> elements;
+ DictionaryNode() { type=TYPE_DICTIONARY; }
+ };
+
+ struct SelfNode : public Node {
+ SelfNode() { type=TYPE_SELF; }
+ };
+
+ struct OperatorNode : public Node {
+ enum Operator {
+ //call/constructor operator
+ OP_CALL,
+ OP_PARENT_CALL,
+ OP_EXTENDS,
+ //indexing operator
+ OP_INDEX,
+ OP_INDEX_NAMED,
+ //unary operators
+ OP_NEG,
+ OP_NOT,
+ OP_BIT_INVERT,
+ OP_PREINC,
+ OP_PREDEC,
+ OP_INC,
+ OP_DEC,
+ //binary operators (in precedence order)
+ OP_IN,
+ OP_EQUAL,
+ OP_NOT_EQUAL,
+ OP_LESS,
+ OP_LESS_EQUAL,
+ OP_GREATER,
+ OP_GREATER_EQUAL,
+ OP_AND,
+ OP_OR,
+ OP_ADD,
+ OP_SUB,
+ OP_MUL,
+ OP_DIV,
+ OP_MOD,
+ OP_SHIFT_LEFT,
+ OP_SHIFT_RIGHT,
+ OP_ASSIGN,
+ OP_ASSIGN_ADD,
+ OP_ASSIGN_SUB,
+ OP_ASSIGN_MUL,
+ OP_ASSIGN_DIV,
+ OP_ASSIGN_MOD,
+ OP_ASSIGN_SHIFT_LEFT,
+ OP_ASSIGN_SHIFT_RIGHT,
+ OP_ASSIGN_BIT_AND,
+ OP_ASSIGN_BIT_OR,
+ OP_ASSIGN_BIT_XOR,
+ OP_BIT_AND,
+ OP_BIT_OR,
+ OP_BIT_XOR
+ };
+
+ Operator op;
+
+ Vector<Node*> arguments;
+ OperatorNode() { type=TYPE_OPERATOR; }
+ };
+
+ struct ControlFlowNode : public Node {
+ enum CFType {
+ CF_IF,
+ CF_FOR,
+ CF_WHILE,
+ CF_SWITCH,
+ CF_BREAK,
+ CF_CONTINUE,
+ CF_RETURN
+ };
+
+ CFType cf_type;
+ Vector<Node*> arguments;
+ BlockNode *body;
+ BlockNode *body_else;
+
+ ControlFlowNode *_else; //used for if
+ ControlFlowNode() { type=TYPE_CONTROL_FLOW; cf_type=CF_IF; body=NULL; body_else=NULL;}
+ };
+
+ struct AssertNode : public Node {
+ Node* condition;
+ AssertNode() { type=TYPE_ASSERT; }
+ };
+ struct NewLineNode : public Node {
+ int line;
+ NewLineNode() { type=TYPE_NEWLINE; }
+ };
+
+
+ struct Expression {
+
+ bool is_op;
+ union {
+ OperatorNode::Operator op;
+ Node *node;
+ };
+ };
+
+
+/*
+ struct OperatorNode : public Node {
+
+ DataType return_cache;
+ Operator op;
+ Vector<Node*> arguments;
+ virtual DataType get_datatype() const { return return_cache; }
+
+ OperatorNode() { type=TYPE_OPERATOR; return_cache=TYPE_VOID; }
+ };
+
+ struct VariableNode : public Node {
+
+ DataType datatype_cache;
+ StringName name;
+ virtual DataType get_datatype() const { return datatype_cache; }
+
+ VariableNode() { type=TYPE_VARIABLE; datatype_cache=TYPE_VOID; }
+ };
+
+ struct ConstantNode : public Node {
+
+ DataType datatype;
+ Variant value;
+ virtual DataType get_datatype() const { return datatype; }
+
+ ConstantNode() { type=TYPE_CONSTANT; }
+ };
+
+ struct BlockNode : public Node {
+
+ Map<StringName,DataType> variables;
+ List<Node*> statements;
+ BlockNode() { type=TYPE_BLOCK; }
+ };
+
+ struct ControlFlowNode : public Node {
+
+ FlowOperation flow_op;
+ Vector<Node*> statements;
+ ControlFlowNode() { type=TYPE_CONTROL_FLOW; flow_op=FLOW_OP_IF;}
+ };
+
+ struct MemberNode : public Node {
+
+ DataType datatype;
+ StringName name;
+ Node* owner;
+ virtual DataType get_datatype() const { return datatype; }
+ MemberNode() { type=TYPE_MEMBER; }
+ };
+
+
+ struct ProgramNode : public Node {
+
+ struct Function {
+ StringName name;
+ FunctionNode*function;
+ };
+
+ Map<StringName,DataType> builtin_variables;
+ Map<StringName,DataType> preexisting_variables;
+
+ Vector<Function> functions;
+ BlockNode *body;
+
+ ProgramNode() { type=TYPE_PROGRAM; }
+ };
+*/
+private:
+
+
+ GDTokenizer tokenizer;
+
+
+ Node *head;
+ Node *list;
+ template<class T>
+ T* alloc_node();
+
+ bool error_set;
+ String error;
+ int error_line;
+ int error_column;
+
+ List<int> tab_level;
+
+ String base_path;
+
+ PropertyInfo current_export;
+
+ void _set_error(const String& p_error, int p_line=-1, int p_column=-1);
+
+
+ bool _parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_static);
+ bool _enter_indent_block(BlockNode *p_block=NULL);
+ bool _parse_newline();
+ Node* _parse_expression(Node *p_parent,bool p_static,bool p_allow_assign=false);
+ Node* _reduce_expression(Node *p_node,bool p_to_const=false);
+ Node* _parse_and_reduce_expression(Node *p_parent,bool p_static,bool p_reduce_const=false,bool p_allow_assign=false);
+
+ void _parse_block(BlockNode *p_block,bool p_static);
+ void _parse_extends(ClassNode *p_class);
+ void _parse_class(ClassNode *p_class);
+ bool _end_statement();
+
+public:
+
+ String get_error() const;
+ int get_error_line() const;
+ int get_error_column() const;
+ Error parse(const String& p_code,const String& p_base_path="");
+
+ const Node *get_parse_tree() const;
+
+ void clear();
+ GDParser();
+ ~GDParser();
+};
+
+#endif // PARSER_H
diff --git a/modules/gdscript/gd_pretty_print.cpp b/modules/gdscript/gd_pretty_print.cpp
new file mode 100644
index 0000000000..a5a993bb3a
--- /dev/null
+++ b/modules/gdscript/gd_pretty_print.cpp
@@ -0,0 +1,34 @@
+/*************************************************************************/
+/* gd_pretty_print.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_pretty_print.h"
+
+GDPrettyPrint::GDPrettyPrint() {
+
+
+}
diff --git a/modules/gdscript/gd_pretty_print.h b/modules/gdscript/gd_pretty_print.h
new file mode 100644
index 0000000000..fbf002295b
--- /dev/null
+++ b/modules/gdscript/gd_pretty_print.h
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* gd_pretty_print.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_PRETTY_PRINT_H
+#define GD_PRETTY_PRINT_H
+
+
+
+
+class GDPrettyPrint {
+public:
+ GDPrettyPrint();
+};
+
+#endif // GD_PRETTY_PRINT_H
diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp
new file mode 100644
index 0000000000..5679e1e066
--- /dev/null
+++ b/modules/gdscript/gd_script.cpp
@@ -0,0 +1,2222 @@
+/*************************************************************************/
+/* gd_script.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_script.h"
+#include "globals.h"
+#include "global_constants.h"
+#include "gd_compiler.h"
+#include "os/file_access.h"
+
+/* TODO:
+
+ *populate globals
+ *do checks as close to debugger as possible (but don't do debugger)
+ *const check plz
+ *check arguments and default arguments in GDFunction
+ -get property list in instance?
+ *missing opcodes
+ -const checks
+ -make thread safe
+ */
+
+
+
+Variant *GDFunction::_get_variant(int p_address,GDInstance *p_instance,GDScript *p_script,Variant &self, Variant *p_stack,String& r_error) const{
+
+ int address = p_address&ADDR_MASK;
+
+ //sequential table (jump table generated by compiler)
+ switch((p_address&ADDR_TYPE_MASK)>>ADDR_BITS) {
+
+ case ADDR_TYPE_SELF: {
+
+ if (!p_instance) {
+ r_error="Cannot access self without instance.";
+ return NULL;
+ }
+ return &self;
+ } break;
+ case ADDR_TYPE_MEMBER: {
+ //member indexing is O(1)
+ if (!p_instance) {
+ r_error="Cannot access member without instance.";
+ return NULL;
+ }
+ return &p_instance->members[address];
+ } break;
+ case ADDR_TYPE_CLASS_CONSTANT: {
+
+ //todo change to index!
+ GDScript *s=p_script;
+ ERR_FAIL_INDEX_V(address,_global_names_count,NULL);
+ const StringName *sn = &_global_names_ptr[address];
+
+ while(s) {
+ Map<StringName,Variant>::Element *E=s->constants.find(*sn);
+ if (E) {
+ return &E->get();
+ }
+ s=s->_base;
+ }
+
+
+ ERR_EXPLAIN("GDCompiler bug..");
+ ERR_FAIL_V(NULL);
+ } break;
+ case ADDR_TYPE_LOCAL_CONSTANT: {
+ ERR_FAIL_INDEX_V(address,_constant_count,NULL);
+ return &_constants_ptr[address];
+ } break;
+ case ADDR_TYPE_STACK:
+ case ADDR_TYPE_STACK_VARIABLE: {
+ ERR_FAIL_INDEX_V(address,_stack_size,NULL);
+ return &p_stack[address];
+ } break;
+ case ADDR_TYPE_GLOBAL: {
+
+
+ ERR_FAIL_INDEX_V(address,GDScriptLanguage::get_singleton()->get_global_array_size(),NULL);
+
+
+ return &GDScriptLanguage::get_singleton()->get_global_array()[address];
+ } break;
+ case ADDR_TYPE_NIL: {
+ return &nil;
+ } break;
+ }
+
+ ERR_EXPLAIN("Bad Code! (Addressing Mode)");
+ ERR_FAIL_V(NULL);
+ return NULL;
+}
+
+
+String GDFunction::_get_call_error(const Variant::CallError& p_err, const String& p_where,const Variant**argptrs) const {
+
+
+
+ String err_text;
+
+ if (p_err.error==Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ int errorarg=p_err.argument;
+ err_text="Invalid type in "+p_where+". Cannot convert argument "+itos(errorarg+1)+" from "+Variant::get_type_name(argptrs[errorarg]->get_type())+" to "+Variant::get_type_name(p_err.expected)+".";
+ } else if (p_err.error==Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
+ err_text="Invalid call to "+p_where+". Expected "+itos(p_err.argument)+" arguments.";
+ } else if (p_err.error==Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
+ err_text="Invalid call to "+p_where+". Expected "+itos(p_err.argument)+" arguments.";
+ } else if (p_err.error==Variant::CallError::CALL_ERROR_INVALID_METHOD) {
+ err_text="Invalid call. Unexisting "+p_where+".";
+ } else if (p_err.error==Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
+ err_text="Attempt to call "+p_where+" on a null instance.";
+ } else {
+ err_text="Bug, call error: #"+itos(p_err.error);
+ }
+
+ return err_text;
+
+}
+
+static String _get_var_type(const Variant* p_type) {
+
+ String basestr;
+
+ if (p_type->get_type()==Variant::OBJECT) {
+ Object *bobj = *p_type;
+ if (!bobj) {
+ basestr = "null instance";
+ } else {
+#ifdef DEBUG_ENABLED
+ if (ObjectDB::instance_validate(bobj)) {
+ if (bobj->get_script_instance())
+ basestr= bobj->get_type()+" ("+bobj->get_script_instance()->get_script()->get_path().get_file()+")";
+ else
+ basestr = bobj->get_type();
+ } else {
+ basestr="previously freed instance";
+ }
+
+#else
+ basestr="Object";
+#endif
+ }
+
+ } else {
+ basestr = Variant::get_type_name(p_type->get_type());
+ }
+
+ return basestr;
+
+}
+
+Variant GDFunction::call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err) {
+
+
+ if (!_code_ptr) {
+
+ return Variant();
+ }
+
+ r_err.error=Variant::CallError::CALL_OK;
+
+ Variant self;
+ Variant retvalue;
+ Variant *stack = NULL;
+ Variant **call_args;
+ int defarg=0;
+
+#ifdef DEBUG_ENABLED
+
+ //GDScriptLanguage::get_singleton()->calls++;
+
+#endif
+
+ if (p_argcount!=_argument_count) {
+
+ if (p_argcount>_argument_count) {
+
+ r_err.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_err.argument=_argument_count;
+
+ return Variant();
+ } else if (p_argcount < _argument_count - _default_arg_count) {
+
+ r_err.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_err.argument=_argument_count - _default_arg_count;
+ return Variant();
+ } else {
+
+ defarg=_argument_count-p_argcount;
+ }
+ }
+
+ uint32_t alloca_size = sizeof(Variant*)*_call_size + sizeof(Variant)*_stack_size;
+
+ if (alloca_size) {
+
+ uint8_t *aptr = (uint8_t*)alloca(alloca_size);
+
+ if (_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 (_call_size) {
+
+ call_args = (Variant**)&aptr[sizeof(Variant)*_stack_size];
+ } else {
+
+ call_args=NULL;
+ }
+
+
+ } else {
+ stack=NULL;
+ call_args=NULL;
+ }
+
+
+ GDScript *_class;
+
+ if (p_instance) {
+ if (p_instance->base_ref && static_cast<Reference*>(p_instance->owner)->is_referenced()) {
+
+ self=REF(static_cast<Reference*>(p_instance->owner));
+ } else {
+ self=p_instance->owner;
+ }
+ _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())
+ GDScriptLanguage::get_singleton()->enter_function(p_instance,this,stack,&ip,&line);
+
+#define CHECK_SPACE(m_space)\
+ ERR_BREAK((ip+m_space)>_code_size)
+
+#define GET_VARIANT_PTR(m_v,m_code_ofs) \
+ Variant *m_v; \
+ m_v = _get_variant(_code_ptr[ip+m_code_ofs],p_instance,_class,self,stack,err_text);\
+ if (!m_v)\
+ break;
+
+
+#else
+#define CHECK_SPACE(m_space)
+#define GET_VARIANT_PTR(m_v,m_code_ofs) \
+ Variant *m_v; \
+ m_v = _get_variant(_code_ptr[ip+m_code_ofs],p_instance,_class,self,stack,err_text);
+
+#endif
+
+
+
+ bool exit_ok=false;
+
+ while(ip<_code_size) {
+
+
+ int last_opcode=_code_ptr[ip];
+ switch(_code_ptr[ip]) {
+
+ case OPCODE_OPERATOR: {
+
+ CHECK_SPACE(5);
+
+ bool valid;
+ Variant::Operator op = (Variant::Operator)_code_ptr[ip+1];
+ ERR_BREAK(op>=Variant::OP_MAX);
+
+ GET_VARIANT_PTR(a,2);
+ GET_VARIANT_PTR(b,3);
+ GET_VARIANT_PTR(dst,4);
+
+ Variant::evaluate(op,*a,*b,*dst,valid);
+ if (!valid) {
+ if (false && dst->get_type()==Variant::STRING) {
+ //return a string when invalid with the error
+ err_text=*dst;
+ } else {
+ err_text="Invalid operands '"+Variant::get_type_name(a->get_type())+"' and '"+Variant::get_type_name(b->get_type())+"' in operator '"+Variant::get_operator_name(op)+"'.";
+ }
+ break;
+ }
+
+ ip+=5;
+
+ } continue;
+ case OPCODE_EXTENDS_TEST: {
+
+ CHECK_SPACE(4);
+
+ GET_VARIANT_PTR(a,1);
+ GET_VARIANT_PTR(b,2);
+ GET_VARIANT_PTR(dst,3);
+
+#ifdef DEBUG_ENABLED
+
+ if (a->get_type()!=Variant::OBJECT || a->operator Object*()==NULL) {
+
+ err_text="Left operand of 'extends' is not an instance of anything.";
+ break;
+
+ }
+ if (b->get_type()!=Variant::OBJECT || b->operator Object*()==NULL) {
+
+ err_text="Right operand of 'extends' is not a class.";
+ break;
+
+ }
+#endif
+
+
+ Object *obj_A = *a;
+ Object *obj_B = *b;
+
+
+ GDScript *scr_B = obj_B->cast_to<GDScript>();
+
+ bool extends_ok=false;
+
+ if (scr_B) {
+ //if B is a script, the only valid condition is that A has an instance which inherits from the script
+ //in other situation, this shoul return false.
+
+ if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language()==GDScriptLanguage::get_singleton()) {
+
+ GDInstance *ins = static_cast<GDInstance*>(obj_A->get_script_instance());
+ GDScript *cmp = ins->script.ptr();
+ //bool found=false;
+ while(cmp) {
+
+ if (cmp==scr_B) {
+ //inherits from script, all ok
+ extends_ok=true;
+ break;
+
+ }
+
+ cmp=cmp->_base;
+ }
+
+ }
+ } else {
+
+ GDNativeClass *nc= obj_B->cast_to<GDNativeClass>();
+
+ if (!nc) {
+
+ err_text="Right operand of 'extends' is not a class (type: '"+obj_B->get_type()+"').";
+ break;
+ }
+
+ extends_ok=ObjectTypeDB::is_type(obj_A->get_type_name(),nc->get_name());
+ }
+
+ *dst=extends_ok;
+ ip+=4;
+
+ } continue;
+ case OPCODE_SET: {
+
+ CHECK_SPACE(3);
+
+ GET_VARIANT_PTR(dst,1);
+ GET_VARIANT_PTR(index,2);
+ GET_VARIANT_PTR(value,3);
+
+ bool valid;
+ dst->set(*index,*value,&valid);
+
+ if (!valid) {
+ String v = index->operator String();
+ if (v!="") {
+ v="'"+v+"'";
+ } else {
+ v="of type '"+_get_var_type(index)+"'";
+ }
+ err_text="Invalid set index "+v+" (on base: '"+_get_var_type(dst)+"').";
+ break;
+ }
+
+ ip+=4;
+ } continue;
+ case OPCODE_GET: {
+
+ CHECK_SPACE(3);
+
+ GET_VARIANT_PTR(src,1);
+ GET_VARIANT_PTR(index,2);
+ GET_VARIANT_PTR(dst,3);
+
+ bool valid;
+ *dst = src->get(*index,&valid);
+
+ if (!valid) {
+ String v = index->operator String();
+ if (v!="") {
+ v="'"+v+"'";
+ } else {
+ v="of type '"+_get_var_type(index)+"'";
+ }
+ err_text="Invalid get index "+v+" (on base: '"+_get_var_type(src)+"').";
+ break;
+ }
+ ip+=4;
+ } continue;
+ case OPCODE_SET_NAMED: {
+
+ CHECK_SPACE(3);
+
+ GET_VARIANT_PTR(dst,1);
+ GET_VARIANT_PTR(value,3);
+
+ int indexname = _code_ptr[ip+2];
+
+ ERR_BREAK(indexname<0 || indexname>=_global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+
+ bool valid;
+ dst->set_named(*index,*value,&valid);
+
+ if (!valid) {
+ String err_type;
+ err_text="Invalid set index '"+String(*index)+"' (on base: '"+_get_var_type(dst)+"').";
+ break;
+ }
+
+ ip+=4;
+ } continue;
+ case OPCODE_GET_NAMED: {
+
+
+ CHECK_SPACE(3);
+
+ GET_VARIANT_PTR(src,1);
+ GET_VARIANT_PTR(dst,3);
+
+ int indexname = _code_ptr[ip+2];
+
+ ERR_BREAK(indexname<0 || indexname>=_global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+
+ bool valid;
+ *dst = src->get_named(*index,&valid);
+
+ if (!valid) {
+ err_text="Invalid get index '"+index->operator String()+"' (on base: '"+_get_var_type(src)+"').";
+ break;
+ }
+
+ ip+=4;
+ } continue;
+ case OPCODE_ASSIGN: {
+
+ CHECK_SPACE(3);
+ GET_VARIANT_PTR(dst,1);
+ GET_VARIANT_PTR(src,2);
+
+ *dst = *src;
+
+ ip+=3;
+
+ } continue;
+ case OPCODE_ASSIGN_TRUE: {
+
+ CHECK_SPACE(2);
+ GET_VARIANT_PTR(dst,1);
+
+ *dst = true;
+
+ ip+=2;
+ } continue;
+ case OPCODE_ASSIGN_FALSE: {
+
+ CHECK_SPACE(2);
+ GET_VARIANT_PTR(dst,1);
+
+ *dst = false;
+
+ ip+=2;
+ } continue;
+ case OPCODE_CONSTRUCT: {
+
+ CHECK_SPACE(2);
+ Variant::Type t=Variant::Type(_code_ptr[ip+1]);
+ int argc=_code_ptr[ip+2];
+ CHECK_SPACE(argc+2);
+ Variant **argptrs = call_args;
+ for(int i=0;i<argc;i++) {
+ GET_VARIANT_PTR(v,3+i);
+ argptrs[i]=v;
+ }
+
+ GET_VARIANT_PTR(dst,3+argc);
+ Variant::CallError err;
+ *dst = Variant::construct(t,(const Variant**)argptrs,argc,err);
+
+ if (err.error!=Variant::CallError::CALL_OK) {
+
+ err_text=_get_call_error(err,"'"+Variant::get_type_name(t)+"' constructor",(const Variant**)argptrs);
+ break;
+ }
+
+ ip+=4+argc;
+ //construct a basic type
+ } continue;
+ case OPCODE_CONSTRUCT_ARRAY: {
+
+ CHECK_SPACE(1);
+ int argc=_code_ptr[ip+1];
+ Array array(true); //arrays are always shared
+ array.resize(argc);
+ CHECK_SPACE(argc+2);
+
+ for(int i=0;i<argc;i++) {
+ GET_VARIANT_PTR(v,2+i);
+ array[i]=*v;
+
+ }
+
+ GET_VARIANT_PTR(dst,2+argc);
+
+ *dst=array;
+
+ ip+=3+argc;
+
+ } continue;
+ case OPCODE_CONSTRUCT_DICTIONARY: {
+
+ CHECK_SPACE(1);
+ int argc=_code_ptr[ip+1];
+ Dictionary dict(true); //arrays are always shared
+
+ CHECK_SPACE(argc*2+2);
+
+ for(int i=0;i<argc;i++) {
+
+ GET_VARIANT_PTR(k,2+i*2+0);
+ GET_VARIANT_PTR(v,2+i*2+1);
+ dict[*k]=*v;
+
+ }
+
+ GET_VARIANT_PTR(dst,2+argc*2);
+
+ *dst=dict;
+
+ ip+=3+argc*2;
+
+ } continue;
+ case OPCODE_CALL_RETURN:
+ case OPCODE_CALL: {
+
+
+ CHECK_SPACE(4);
+ bool call_ret = _code_ptr[ip]==OPCODE_CALL_RETURN;
+
+ int argc=_code_ptr[ip+1];
+ GET_VARIANT_PTR(base,2);
+ int nameg=_code_ptr[ip+3];
+
+ ERR_BREAK(nameg<0 || nameg>=_global_names_count);
+ const StringName *methodname = &_global_names_ptr[nameg];
+
+ ERR_BREAK(argc<0);
+ ip+=4;
+ CHECK_SPACE(argc+1);
+ Variant **argptrs = call_args;
+
+ for(int i=0;i<argc;i++) {
+ GET_VARIANT_PTR(v,i);
+ argptrs[i]=v;
+ }
+
+ Variant::CallError err;
+ if (call_ret) {
+
+ GET_VARIANT_PTR(ret,argc);
+ *ret = base->call(*methodname,(const Variant**)argptrs,argc,err);
+ } else {
+
+ base->call(*methodname,(const Variant**)argptrs,argc,err);
+ }
+
+ if (err.error!=Variant::CallError::CALL_OK) {
+
+
+ String methodstr = *methodname;
+ String basestr = _get_var_type(base);
+
+ if (methodstr=="call") {
+ if (argc>=1) {
+ methodstr=String(*argptrs[0])+" (via call)";
+ if (err.error==Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ err.argument-=1;
+ }
+ }
+ }
+ err_text=_get_call_error(err,"function '"+methodstr+"' in base '"+basestr+"'",(const Variant**)argptrs);
+ break;
+ }
+
+ //_call_func(NULL,base,*methodname,ip,argc,p_instance,stack);
+ ip+=argc+1;
+
+ } continue;
+ case OPCODE_CALL_BUILT_IN: {
+
+ CHECK_SPACE(4);
+
+ GDFunctions::Function func = GDFunctions::Function(_code_ptr[ip+1]);
+ int argc=_code_ptr[ip+2];
+ ERR_BREAK(argc<0);
+
+ ip+=3;
+ CHECK_SPACE(argc+1);
+ Variant **argptrs = call_args;
+
+ for(int i=0;i<argc;i++) {
+ GET_VARIANT_PTR(v,i);
+ argptrs[i]=v;
+ }
+
+ GET_VARIANT_PTR(dst,argc);
+
+ Variant::CallError err;
+
+ GDFunctions::call(func,(const Variant**)argptrs,argc,*dst,err);
+
+ if (err.error!=Variant::CallError::CALL_OK) {
+
+
+ String methodstr = GDFunctions::get_func_name(func);
+ err_text=_get_call_error(err,"built-in function '"+methodstr+"'",(const Variant**)argptrs);
+ break;
+ }
+ ip+=argc+1;
+
+ } continue;
+ case OPCODE_CALL_SELF: {
+
+
+ } break;
+ case OPCODE_CALL_SELF_BASE: {
+
+ CHECK_SPACE(2);
+ int self_fun = _code_ptr[ip+1];
+#ifdef DEBUG_ENABLED
+
+ if (self_fun<0 || self_fun>=_global_names_count) {
+
+ err_text="compiler bug, function name not found";
+ break;
+ }
+#endif
+ const StringName *methodname = &_global_names_ptr[self_fun];
+
+ int argc=_code_ptr[ip+2];
+
+ CHECK_SPACE(2+argc+1);
+
+ Variant **argptrs = call_args;
+
+ for(int i=0;i<argc;i++) {
+ GET_VARIANT_PTR(v,i+3);
+ argptrs[i]=v;
+ }
+
+ GET_VARIANT_PTR(dst,argc+3);
+
+ const GDScript *gds = _script;
+
+
+ const Map<StringName,GDFunction>::Element *E=NULL;
+ while (gds->base.ptr()) {
+ gds=gds->base.ptr();
+ E=gds->member_functions.find(*methodname);
+ if (E)
+ break;
+ }
+
+ Variant::CallError err;
+
+ if (E) {
+
+ *dst=((GDFunction*)&E->get())->call(p_instance,(const Variant**)argptrs,argc,err);
+ } else if (gds->native.ptr()) {
+
+ if (*methodname!=GDScriptLanguage::get_singleton()->strings._init) {
+
+ MethodBind *mb = ObjectTypeDB::get_method(gds->native->get_name(),*methodname);
+ if (!mb) {
+ err.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ } else {
+ *dst=mb->call(p_instance->owner,(const Variant**)argptrs,argc,err);
+ }
+ } else {
+ err.error=Variant::CallError::CALL_OK;
+ }
+ } else {
+
+ if (*methodname!=GDScriptLanguage::get_singleton()->strings._init) {
+ err.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ } else {
+ err.error=Variant::CallError::CALL_OK;
+ }
+ }
+
+
+ if (err.error!=Variant::CallError::CALL_OK) {
+
+
+ String methodstr = *methodname;
+ err_text=_get_call_error(err,"function '"+methodstr+"'",(const Variant**)argptrs);
+
+ break;
+ }
+
+ ip+=4+argc;
+
+ } continue;
+ case OPCODE_JUMP: {
+
+ CHECK_SPACE(2);
+ int to = _code_ptr[ip+1];
+
+ ERR_BREAK(to<0 || to>_code_size);
+ ip=to;
+
+ } continue;
+ case OPCODE_JUMP_IF: {
+
+ CHECK_SPACE(3);
+
+ GET_VARIANT_PTR(test,1);
+
+ bool valid;
+ bool result = test->booleanize(valid);
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+
+ err_text="cannot evaluate conditional expression of type: "+Variant::get_type_name(test->get_type());
+ break;
+ }
+#endif
+ if (result) {
+ int to = _code_ptr[ip+2];
+ ERR_BREAK(to<0 || to>_code_size);
+ ip=to;
+ continue;
+ }
+ ip+=3;
+ } continue;
+ case OPCODE_JUMP_IF_NOT: {
+
+ CHECK_SPACE(3);
+
+ GET_VARIANT_PTR(test,1);
+
+ bool valid;
+ bool result = test->booleanize(valid);
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+
+ err_text="cannot evaluate conditional expression of type: "+Variant::get_type_name(test->get_type());
+ break;
+ }
+#endif
+ if (!result) {
+ int to = _code_ptr[ip+2];
+ ERR_BREAK(to<0 || to>_code_size);
+ ip=to;
+ continue;
+ }
+ ip+=3;
+ } continue;
+ case OPCODE_JUMP_TO_DEF_ARGUMENT: {
+
+ CHECK_SPACE(2);
+ ip=_default_arg_ptr[defarg];
+
+ } continue;
+ case OPCODE_RETURN: {
+
+ CHECK_SPACE(2);
+ GET_VARIANT_PTR(r,1);
+ retvalue=*r;
+ exit_ok=true;
+
+ } break;
+ case OPCODE_ITERATE_BEGIN: {
+
+ CHECK_SPACE(8); //space for this an regular iterate
+
+ GET_VARIANT_PTR(counter,1);
+ GET_VARIANT_PTR(container,2);
+
+ bool valid;
+ if (!container->iter_init(*counter,valid)) {
+ if (!valid) {
+ err_text="Unable to iterate on object of type "+Variant::get_type_name(container->get_type())+"'.";
+ break;
+ }
+ int jumpto=_code_ptr[ip+3];
+ ERR_BREAK(jumpto<0 || jumpto>_code_size);
+ ip=jumpto;
+ continue;
+ }
+ GET_VARIANT_PTR(iterator,4);
+
+
+ *iterator=container->iter_get(*counter,valid);
+ if (!valid) {
+ err_text="Unable to obtain iterator object of type "+Variant::get_type_name(container->get_type())+"'.";
+ break;
+ }
+
+
+ ip+=5; //skip regular iterate which is always next
+
+ } continue;
+ case OPCODE_ITERATE: {
+
+ CHECK_SPACE(4);
+
+ GET_VARIANT_PTR(counter,1);
+ GET_VARIANT_PTR(container,2);
+
+ bool valid;
+ if (!container->iter_next(*counter,valid)) {
+ if (!valid) {
+ err_text="Unable to iterate on object of type "+Variant::get_type_name(container->get_type())+"' (type changed since first iteration?).";
+ break;
+ }
+ int jumpto=_code_ptr[ip+3];
+ ERR_BREAK(jumpto<0 || jumpto>_code_size);
+ ip=jumpto;
+ continue;
+ }
+ GET_VARIANT_PTR(iterator,4);
+
+ *iterator=container->iter_get(*counter,valid);
+ if (!valid) {
+ err_text="Unable to obtain iterator object of type "+Variant::get_type_name(container->get_type())+"' (but was obtained on first iteration?).";
+ break;
+ }
+
+ ip+=5; //loop again
+ } continue;
+ case OPCODE_ASSERT: {
+ CHECK_SPACE(2);
+ GET_VARIANT_PTR(test,1);
+
+#ifdef DEBUG_ENABLED
+ bool valid;
+ bool result = test->booleanize(valid);
+
+
+ if (!valid) {
+
+ err_text="cannot evaluate conditional expression of type: "+Variant::get_type_name(test->get_type());
+ break;
+ }
+
+
+ if (!result) {
+
+ err_text="Assertion failed.";
+ break;
+ }
+
+#endif
+
+ ip+=2;
+ } continue;
+ case OPCODE_LINE: {
+ CHECK_SPACE(2);
+
+ line=_code_ptr[ip+1];
+ ip+=2;
+
+ if (ScriptDebugger::get_singleton()) {
+ // line
+ bool do_break=false;
+
+ if (ScriptDebugger::get_singleton()->get_lines_left()>0) {
+
+ if (ScriptDebugger::get_singleton()->get_depth()<=0)
+ ScriptDebugger::get_singleton()->set_lines_left( ScriptDebugger::get_singleton()->get_lines_left() -1 );
+ if (ScriptDebugger::get_singleton()->get_lines_left()<=0)
+ do_break=true;
+ }
+
+ if (ScriptDebugger::get_singleton()->is_breakpoint(line,source))
+ do_break=true;
+
+ if (do_break) {
+ GDScriptLanguage::get_singleton()->debug_break("Breakpoint",true);
+ }
+
+ ScriptDebugger::get_singleton()->line_poll();
+
+ }
+ } continue;
+ case OPCODE_END: {
+
+ exit_ok=true;
+ break;
+
+ } break;
+ default: {
+
+ err_text="Illegal opcode "+itos(_code_ptr[ip])+" at address "+itos(ip);
+ } break;
+
+ }
+
+ if (exit_ok)
+ break;
+ //error
+ // function, file, line, error, explanation
+ String err_file;
+ if (p_instance)
+ err_file=p_instance->script->path;
+ else if (_class)
+ err_file=_class->path;
+ if (err_file=="")
+ err_file="<built-in>";
+ String err_func = name;
+ if (p_instance && p_instance->script->name!="")
+ err_func=p_instance->script->name+"."+err_func;
+ int err_line=line;
+ if (err_text=="") {
+ err_text="Internal Script Error! - opcode #"+itos(last_opcode)+" (report please).";
+ }
+
+ if (!GDScriptLanguage::get_singleton()->debug_break(err_text,false)) {
+ // debugger break did not happen
+
+ _err_print_error(err_func.utf8().get_data(),err_file.utf8().get_data(),err_line,err_text.utf8().get_data());
+ }
+
+
+ break;
+ }
+
+ if (ScriptDebugger::get_singleton())
+ GDScriptLanguage::get_singleton()->exit_function();
+
+
+ if (_stack_size) {
+ //free stack
+ for(int i=0;i<_stack_size;i++)
+ stack[i].~Variant();
+ }
+
+ return retvalue;
+
+}
+
+const int* GDFunction::get_code() const {
+
+ return _code_ptr;
+}
+int GDFunction::get_code_size() const{
+
+ return _code_size;
+}
+
+Variant GDFunction::get_constant(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,constants.size(),"<errconst>");
+ return constants[p_idx];
+}
+
+StringName GDFunction::get_global_name(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,global_names.size(),"<errgname>");
+ return global_names[p_idx];
+}
+
+int GDFunction::get_default_argument_count() const {
+
+ return default_arguments.size();
+}
+int GDFunction::get_default_argument_addr(int p_arg) const{
+
+ ERR_FAIL_INDEX_V(p_arg,default_arguments.size(),-1);
+ return default_arguments[p_arg];
+}
+
+
+StringName GDFunction::get_name() const {
+
+ return name;
+}
+
+int GDFunction::get_max_stack_size() const {
+
+ return _stack_size;
+}
+
+struct _GDFKC {
+
+ int order;
+ List<int> pos;
+};
+
+struct _GDFKCS {
+
+ int order;
+ StringName id;
+ int pos;
+
+ bool operator<(const _GDFKCS &p_r) const {
+
+ return order<p_r.order;
+ }
+};
+
+void GDFunction::debug_get_stack_member_state(int p_line,List<Pair<StringName,int> > *r_stackvars) const {
+
+
+ int oc=0;
+ Map<StringName,_GDFKC> sdmap;
+ for( const List<StackDebug>::Element *E=stack_debug.front();E;E=E->next()) {
+
+ const StackDebug &sd=E->get();
+ if (sd.line>p_line)
+ break;
+
+ if (sd.added) {
+
+ if (!sdmap.has(sd.identifier)) {
+ _GDFKC d;
+ d.order=oc++;
+ d.pos.push_back(sd.pos);
+ sdmap[sd.identifier]=d;
+
+ } else {
+ sdmap[sd.identifier].pos.push_back(sd.pos);
+ }
+ } else {
+
+
+ ERR_CONTINUE(!sdmap.has(sd.identifier));
+
+ sdmap[sd.identifier].pos.pop_back();
+ if (sdmap[sd.identifier].pos.empty())
+ sdmap.erase(sd.identifier);
+ }
+
+ }
+
+
+ List<_GDFKCS> stackpositions;
+ for(Map<StringName,_GDFKC>::Element *E=sdmap.front();E;E=E->next() ) {
+
+ _GDFKCS spp;
+ spp.id=E->key();
+ spp.order=E->get().order;
+ spp.pos=E->get().pos.back()->get();
+ stackpositions.push_back(spp);
+ }
+
+ stackpositions.sort();
+
+ for(List<_GDFKCS>::Element *E=stackpositions.front();E;E=E->next()) {
+
+ Pair<StringName,int> p;
+ p.first=E->get().id;
+ p.second=E->get().pos;
+ r_stackvars->push_back(p);
+ }
+
+
+}
+
+#if 0
+void GDFunction::clear() {
+
+ name=StringName();
+ constants.clear();
+ _stack_size=0;
+ code.clear();
+ _constants_ptr=NULL;
+ _constant_count=0;
+ _global_names_ptr=NULL;
+ _global_names_count=0;
+ _code_ptr=NULL;
+ _code_size=0;
+
+}
+#endif
+GDFunction::GDFunction() {
+
+ _stack_size=0;
+ _call_size=0;
+ name="<anonymous>";
+
+}
+
+GDNativeClass::GDNativeClass(const StringName& p_name) {
+
+ name=p_name;
+}
+
+/*void GDNativeClass::call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount){
+
+
+}*/
+
+
+bool GDNativeClass::_get(const StringName& p_name,Variant &r_ret) const {
+
+ bool ok;
+ int v = ObjectTypeDB::get_integer_constant(name, p_name, &ok);
+
+ if (ok) {
+ r_ret=v;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+void GDNativeClass::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("new"),&GDNativeClass::_new);
+
+}
+
+Variant GDNativeClass::_new() {
+
+ Object *o = instance();
+ if (!o) {
+ ERR_EXPLAIN("Class type: '"+String(name)+"' is not instantiable.");
+ ERR_FAIL_COND_V(!o,Variant());
+ }
+
+ Reference *ref = o->cast_to<Reference>();
+ if (ref) {
+ return REF(ref);
+ } else {
+ return o;
+ }
+
+}
+
+Object *GDNativeClass::instance() {
+
+ return ObjectTypeDB::instance(name);
+}
+
+
+
+GDInstance* GDScript::_create_instance(const Variant** p_args,int p_argcount,Object *p_owner,bool p_isref) {
+
+
+ /* STEP 1, CREATE */
+
+ GDInstance* instance = memnew( GDInstance );
+ instance->base_ref=p_isref;
+ instance->members.resize(member_indices.size());
+ instance->script=Ref<GDScript>(this);
+ instance->owner=p_owner;
+ instance->owner->set_script_instance(instance);
+
+ /* STEP 2, INITIALIZE AND CONSRTUCT */
+
+ instances.insert(instance->owner);
+
+ Variant::CallError err;
+ initializer->call(instance,p_args,p_argcount,err);
+
+ if (err.error!=Variant::CallError::CALL_OK) {
+ instance->script=Ref<GDScript>();
+ instances.erase(p_owner);
+ memdelete(instance);
+ ERR_FAIL_COND_V(err.error!=Variant::CallError::CALL_OK, NULL); //error consrtucting
+ }
+
+ //@TODO make thread safe
+ return instance;
+
+}
+
+Variant GDScript::_new(const Variant** p_args,int p_argcount,Variant::CallError& r_error) {
+
+ /* STEP 1, CREATE */
+
+ r_error.error=Variant::CallError::CALL_OK;
+ REF ref;
+ Object *owner=NULL;
+
+ GDScript *_baseptr=this;
+ while (_baseptr->_base) {
+ _baseptr=_baseptr->_base;
+ }
+
+ if (_baseptr->native.ptr()) {
+ owner=_baseptr->native->instance();
+ } else {
+ owner=memnew( Reference ); //by default, no base means use reference
+ }
+
+ Reference *r=owner->cast_to<Reference>();
+ if (r) {
+ ref=REF(r);
+ }
+
+
+ GDInstance* instance = _create_instance(p_args,p_argcount,owner,r!=NULL);
+ if (!instance) {
+ if (ref.is_null()) {
+ memdelete(owner); //no owner, sorry
+ }
+ return Variant();
+ }
+
+ if (ref.is_valid()) {
+ return ref;
+ } else {
+ return owner;
+ }
+}
+
+bool GDScript::can_instance() const {
+
+ return valid; //any script in GDscript can instance
+}
+
+StringName GDScript::get_instance_base_type() const {
+
+ if (native.is_valid())
+ return native->get_name();
+ if (base.is_valid())
+ return base->get_instance_base_type();
+ return StringName();
+}
+
+struct _GDScriptMemberSort {
+
+ int index;
+ StringName name;
+ _FORCE_INLINE_ bool operator<(const _GDScriptMemberSort& p_member) const { return index < p_member.index; }
+
+};
+
+
+#ifdef TOOLS_ENABLED
+
+
+void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
+
+ placeholders.erase(p_placeholder);
+}
+
+void GDScript::_update_placeholder(PlaceHolderScriptInstance *p_placeholder) {
+
+
+ List<PropertyInfo> plist;
+ GDScript *scr=this;
+
+ Map<StringName,Variant> default_values;
+ while(scr) {
+
+ Vector<_GDScriptMemberSort> msort;
+ for(Map<StringName,PropertyInfo>::Element *E=scr->member_info.front();E;E=E->next()) {
+
+ _GDScriptMemberSort ms;
+ ERR_CONTINUE(!scr->member_indices.has(E->key()));
+ ms.index=scr->member_indices[E->key()];
+ ms.name=E->key();
+
+ msort.push_back(ms);
+
+ }
+
+ msort.sort();
+ msort.invert();
+ for(int i=0;i<msort.size();i++) {
+
+ plist.push_front(scr->member_info[msort[i].name]);
+ if (scr->member_default_values.has(msort[i].name))
+ default_values[msort[i].name]=scr->member_default_values[msort[i].name];
+ else {
+ Variant::CallError err;
+ default_values[msort[i].name]=Variant::construct(scr->member_info[msort[i].name].type,NULL,0,err);
+ }
+ }
+
+ scr=scr->_base;
+ }
+
+
+ p_placeholder->update(plist,default_values);
+
+}
+#endif
+ScriptInstance* GDScript::instance_create(Object *p_this) {
+
+ if (!tool && !ScriptServer::is_scripting_enabled()) {
+
+#ifdef TOOLS_ENABLED
+
+ //instance a fake script for editing the values
+ //plist.invert();
+
+ /*print_line("CREATING PLACEHOLDER");
+ for(List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) {
+ print_line(E->get().name);
+ }*/
+ PlaceHolderScriptInstance *si = memnew( PlaceHolderScriptInstance(GDScriptLanguage::get_singleton(),Ref<Script>(this),p_this) );
+ placeholders.insert(si);
+ _update_placeholder(si);
+ return si;
+#else
+ return NULL;
+#endif
+ }
+
+ GDScript *top=this;
+ while(top->_base)
+ top=top->_base;
+
+ if (top->native.is_valid()) {
+ if (!ObjectTypeDB::is_type(p_this->get_type_name(),top->native->get_name())) {
+
+ if (ScriptDebugger::get_singleton()) {
+ GDScriptLanguage::get_singleton()->debug_break_parse(get_path(),0,"Script inherits from native type '"+String(top->native->get_name())+"', so it can't be instanced in object of type: '"+p_this->get_type()+"'");
+ }
+ ERR_EXPLAIN("Script inherits from native type '"+String(top->native->get_name())+"', so it can't be instanced in object of type: '"+p_this->get_type()+"'");
+ ERR_FAIL_V(NULL);
+
+ }
+ }
+
+ return _create_instance(NULL,0,p_this,p_this->cast_to<Reference>());
+
+}
+bool GDScript::instance_has(const Object *p_this) const {
+
+ return instances.has((Object*)p_this);
+}
+
+bool GDScript::has_source_code() const {
+
+ return source!="";
+}
+String GDScript::get_source_code() const {
+
+ return source;
+}
+void GDScript::set_source_code(const String& p_code) {
+
+ source=p_code;
+
+}
+
+void GDScript::_set_subclass_path(Ref<GDScript>& p_sc,const String& p_path) {
+
+ p_sc->path=p_path;
+ for(Map<StringName,Ref<GDScript> >::Element *E=p_sc->subclasses.front();E;E=E->next()) {
+
+ _set_subclass_path(E->get(),p_path);
+ }
+}
+
+Error GDScript::reload() {
+
+
+ ERR_FAIL_COND_V(instances.size(),ERR_ALREADY_IN_USE);
+
+ String basedir=path;
+
+ if (basedir=="")
+ basedir==get_path();
+
+ if (basedir!="")
+ basedir=basedir.get_base_dir();
+
+
+
+ valid=false;
+ GDParser parser;
+ Error err = parser.parse(source,basedir);
+ if (err) {
+ if (ScriptDebugger::get_singleton()) {
+ GDScriptLanguage::get_singleton()->debug_break_parse(get_path(),parser.get_error_line(),"Parser Error: "+parser.get_error());
+ }
+ _err_print_error("GDScript::reload",path.empty()?"built-in":(const char*)path.utf8().get_data(),parser.get_error_line(),("Parse Error: "+parser.get_error()).utf8().get_data());
+ ERR_FAIL_V(ERR_PARSE_ERROR);
+ }
+
+ GDCompiler compiler;
+ err = compiler.compile(&parser,this);
+
+ if (err) {
+ if (ScriptDebugger::get_singleton()) {
+ GDScriptLanguage::get_singleton()->debug_break_parse(get_path(),compiler.get_error_line(),"Parser Error: "+compiler.get_error());
+ }
+ _err_print_error("GDScript::reload",path.empty()?"built-in":(const char*)path.utf8().get_data(),compiler.get_error_line(),("Compile Error: "+compiler.get_error()).utf8().get_data());
+ ERR_FAIL_V(ERR_COMPILATION_FAILED);
+ }
+
+ valid=true;
+
+ for(Map<StringName,Ref<GDScript> >::Element *E=subclasses.front();E;E=E->next()) {
+
+ _set_subclass_path(E->get(),path);
+ }
+
+#ifdef TOOLS_ENABLED
+ for (Set<PlaceHolderScriptInstance*>::Element *E=placeholders.front();E;E=E->next()) {
+
+ _update_placeholder(E->get());
+ }
+#endif
+ return OK;
+}
+
+String GDScript::get_node_type() const {
+
+ return ""; // ?
+}
+
+ScriptLanguage *GDScript::get_language() const {
+
+ return GDScriptLanguage::get_singleton();
+}
+
+
+Variant GDScript::call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error) {
+
+
+ GDScript *top=this;
+ while(top) {
+
+ Map<StringName,GDFunction>::Element *E=top->member_functions.find(p_method);
+ if (E) {
+
+ if (!E->get().is_static()) {
+ WARN_PRINT(String("Can't call non-static function: '"+String(p_method)+"' in script.").utf8().get_data());
+ }
+
+ return E->get().call(NULL,p_args,p_argcount,r_error);
+ }
+ top=top->_base;
+ }
+
+ //none found, regular
+
+ return Script::call(p_method,p_args,p_argcount,r_error);
+
+}
+
+bool GDScript::_get(const StringName& p_name,Variant &r_ret) const {
+
+ {
+
+
+ const GDScript *top=this;
+ while(top) {
+
+ {
+ const Map<StringName,Variant>::Element *E=top->constants.find(p_name);
+ if (E) {
+
+ r_ret= E->get();
+ return true;
+ }
+ }
+
+ {
+ const Map<StringName,Ref<GDScript> >::Element *E=subclasses.find(p_name);
+ if (E) {
+
+ r_ret=E->get();
+ return true;
+ }
+ }
+ top=top->_base;
+ }
+
+ if (p_name==GDScriptLanguage::get_singleton()->strings._script_source) {
+
+ r_ret=get_source_code();
+ return true;
+ }
+ }
+
+
+
+ return false;
+
+}
+bool GDScript::_set(const StringName& p_name, const Variant& p_value) {
+
+ if (p_name==GDScriptLanguage::get_singleton()->strings._script_source) {
+
+ set_source_code(p_value);
+ reload();
+ } else
+ return false;
+
+ return true;
+}
+
+void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const {
+
+ p_properties->push_back( PropertyInfo(Variant::STRING,"script/source",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR) );
+}
+
+
+void GDScript::_bind_methods() {
+
+ ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"new",&GDScript::_new,MethodInfo("new"));
+
+}
+
+Error GDScript::load_source_code(const String& p_path) {
+
+
+ DVector<uint8_t> sourcef;
+ Error err;
+ FileAccess *f=FileAccess::open(p_path,FileAccess::READ,&err);
+ if (err) {
+
+ ERR_FAIL_COND_V(err,err);
+ }
+
+ int len = f->get_len();
+ sourcef.resize(len+1);
+ DVector<uint8_t>::Write w = sourcef.write();
+ int r = f->get_buffer(w.ptr(),len);
+ f->close();
+ memdelete(f);
+ ERR_FAIL_COND_V(r!=len,ERR_CANT_OPEN);
+ w[len]=0;
+
+ String s;
+ if (s.parse_utf8((const char*)w.ptr())) {
+
+ ERR_EXPLAIN("Script '"+p_path+"' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode.");
+ ERR_FAIL_V(ERR_INVALID_DATA);
+ }
+
+ source=s;
+ path=p_path;
+ return OK;
+
+}
+
+
+const Map<StringName,GDFunction>& GDScript::debug_get_member_functions() const {
+
+ return member_functions;
+}
+
+
+
+StringName GDScript::debug_get_member_by_index(int p_idx) const {
+
+
+ for(const Map<StringName,int>::Element *E=member_indices.front();E;E=E->next()) {
+
+ if (E->get()==p_idx)
+ return E->key();
+ }
+
+ return "<error>";
+}
+
+
+Ref<GDScript> GDScript::get_base() const {
+
+ return base;
+}
+
+GDScript::GDScript() {
+
+
+ valid=false;
+ subclass_count=0;
+ initializer=NULL;
+ _base=NULL;
+ _owner=NULL;
+ tool=false;
+}
+
+
+
+
+
+//////////////////////////////
+// INSTANCE //
+//////////////////////////////
+
+
+bool GDInstance::set(const StringName& p_name, const Variant& p_value) {
+
+ //member
+ {
+ const Map<StringName,int>::Element *E = script->member_indices.find(p_name);
+ if (E) {
+ members[E->get()]=p_value;
+ return true;
+
+ }
+ }
+
+ GDScript *sptr=script.ptr();
+ while(sptr) {
+
+
+ Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set);
+ if (E) {
+
+ Variant name=p_name;
+ const Variant *args[2]={&name,&p_value};
+
+ Variant::CallError err;
+ Variant ret = E->get().call(this,(const Variant**)args,2,err);
+ if (err.error==Variant::CallError::CALL_OK && ret.get_type()==Variant::BOOL && ret.operator bool())
+ return true;
+ }
+ sptr = sptr->_base;
+ }
+
+ return false;
+}
+
+bool GDInstance::get(const StringName& p_name, Variant &r_ret) const {
+
+
+
+ const GDScript *sptr=script.ptr();
+ while(sptr) {
+
+ {
+ const Map<StringName,int>::Element *E = script->member_indices.find(p_name);
+ if (E) {
+ r_ret=members[E->get()];
+ return true; //index found
+
+ }
+ }
+
+ {
+ const Map<StringName,Variant>::Element *E = script->constants.find(p_name);
+ if (E) {
+ r_ret=E->get();
+ return true; //index found
+
+ }
+ }
+
+ {
+ const Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get);
+ if (E) {
+
+ Variant name=p_name;
+ const Variant *args[1]={&name};
+
+ Variant::CallError err;
+ Variant ret = const_cast<GDFunction*>(&E->get())->call(const_cast<GDInstance*>(this),(const Variant**)args,1,err);
+ if (err.error==Variant::CallError::CALL_OK && ret.get_type()!=Variant::NIL) {
+ r_ret=ret;
+ return true;
+ }
+ }
+ }
+ sptr = sptr->_base;
+ }
+
+ return false;
+
+}
+void GDInstance::get_property_list(List<PropertyInfo> *p_properties) const {
+ // exported members, not doen yet!
+
+ const GDScript *sptr=script.ptr();
+ List<PropertyInfo> props;
+
+ while(sptr) {
+
+
+ const Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get_property_list);
+ if (E) {
+
+
+ Variant::CallError err;
+ Variant ret = const_cast<GDFunction*>(&E->get())->call(const_cast<GDInstance*>(this),NULL,0,err);
+ if (err.error==Variant::CallError::CALL_OK) {
+
+ if (ret.get_type()!=Variant::ARRAY) {
+
+ ERR_EXPLAIN("Wrong type for _get_property list, must be an array of dictionaries.");
+ ERR_FAIL();
+ }
+ Array arr = ret;
+ for(int i=0;i<arr.size();i++) {
+
+ Dictionary d = arr[i];
+ ERR_CONTINUE(!d.has("name"));
+ ERR_CONTINUE(!d.has("type"));
+ PropertyInfo pinfo;
+ pinfo.type = Variant::Type( d["type"].operator int());
+ ERR_CONTINUE(pinfo.type<0 || pinfo.type>=Variant::VARIANT_MAX );
+ pinfo.name = d["name"];
+ ERR_CONTINUE(pinfo.name=="");
+ if (d.has("hint"))
+ pinfo.hint=PropertyHint(d["hint"].operator int());
+ if (d.has("hint_string"))
+ pinfo.hint_string=d["hint_string"];
+ if (d.has("usage"))
+ pinfo.usage=d["usage"];
+
+ props.push_back(pinfo);
+
+ }
+
+ }
+ }
+
+ //instance a fake script for editing the values
+
+ Vector<_GDScriptMemberSort> msort;
+ for(Map<StringName,PropertyInfo>::Element *E=sptr->member_info.front();E;E=E->next()) {
+
+ _GDScriptMemberSort ms;
+ ERR_CONTINUE(!sptr->member_indices.has(E->key()));
+ ms.index=sptr->member_indices[E->key()];
+ ms.name=E->key();
+ msort.push_back(ms);
+
+ }
+
+ msort.sort();
+ msort.invert();
+ for(int i=0;i<msort.size();i++) {
+
+ props.push_front(sptr->member_info[msort[i].name]);
+
+ }
+#if 0
+ if (sptr->member_functions.has("_get_property_list")) {
+
+ Variant::CallError err;
+ GDFunction *f = const_cast<GDFunction*>(&sptr->member_functions["_get_property_list"]);
+ Variant plv = f->call(const_cast<GDInstance*>(this),NULL,0,err);
+
+ if (plv.get_type()!=Variant::ARRAY) {
+
+ ERR_PRINT("_get_property_list: expected array returned");
+ } else {
+
+ Array pl=plv;
+
+ for(int i=0;i<pl.size();i++) {
+
+ Dictionary p = pl[i];
+ PropertyInfo pinfo;
+ if (!p.has("name")) {
+ ERR_PRINT("_get_property_list: expected 'name' key of type string.")
+ continue;
+ }
+ if (!p.has("type")) {
+ ERR_PRINT("_get_property_list: expected 'type' key of type integer.")
+ continue;
+ }
+ pinfo.name=p["name"];
+ pinfo.type=Variant::Type(int(p["type"]));
+ if (p.has("hint"))
+ pinfo.hint=PropertyHint(int(p["hint"]));
+ if (p.has("hint_string"))
+ pinfo.hint_string=p["hint_string"];
+ if (p.has("usage"))
+ pinfo.usage=p["usage"];
+
+
+ props.push_back(pinfo);
+ }
+ }
+ }
+#endif
+
+ sptr = sptr->_base;
+ }
+
+ //props.invert();
+
+ for (List<PropertyInfo>::Element *E=props.front();E;E=E->next()) {
+
+ p_properties->push_back(E->get());
+ }
+}
+
+void GDInstance::get_method_list(List<MethodInfo> *p_list) const {
+
+ const GDScript *sptr=script.ptr();
+ while(sptr) {
+
+ for (Map<StringName,GDFunction>::Element *E = sptr->member_functions.front();E;E=E->next()) {
+
+ MethodInfo mi;
+ mi.name=E->key();
+ for(int i=0;i<E->get().get_argument_count();i++)
+ mi.arguments.push_back(PropertyInfo(Variant::NIL,"arg"+itos(i)));
+ p_list->push_back(mi);
+ }
+ sptr = sptr->_base;
+ }
+
+}
+
+bool GDInstance::has_method(const StringName& p_method) const {
+
+ const GDScript *sptr=script.ptr();
+ while(sptr) {
+ const Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(p_method);
+ if (E)
+ return true;
+ sptr = sptr->_base;
+ }
+
+ return false;
+}
+Variant GDInstance::call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error) {
+
+ //printf("calling %ls:%i method %ls\n", script->get_path().c_str(), -1, String(p_method).c_str());
+
+ GDScript *sptr=script.ptr();
+ while(sptr) {
+ Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(p_method);
+ if (E) {
+ return E->get().call(this,p_args,p_argcount,r_error);
+ }
+ sptr = sptr->_base;
+ }
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+}
+
+void GDInstance::call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount) {
+
+ GDScript *sptr=script.ptr();
+ Variant::CallError ce;
+
+ while(sptr) {
+ Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(p_method);
+ if (E) {
+ E->get().call(this,p_args,p_argcount,ce);
+ }
+ sptr = sptr->_base;
+ }
+
+}
+
+
+void GDInstance::_ml_call_reversed(GDScript *sptr,const StringName& p_method,const Variant** p_args,int p_argcount) {
+
+ if (sptr->_base)
+ _ml_call_reversed(sptr->_base,p_method,p_args,p_argcount);
+
+ Variant::CallError ce;
+
+ Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(p_method);
+ if (E) {
+ E->get().call(this,p_args,p_argcount,ce);
+ }
+
+}
+
+void GDInstance::call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount) {
+
+ if (script.ptr()) {
+ _ml_call_reversed(script.ptr(),p_method,p_args,p_argcount);
+ }
+}
+
+void GDInstance::notification(int p_notification) {
+
+ //notification is not virutal, it gets called at ALL levels just like in C.
+ Variant value=p_notification;
+ const Variant *args[1]={&value };
+
+ GDScript *sptr=script.ptr();
+ while(sptr) {
+ Map<StringName,GDFunction>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._notification);
+ if (E) {
+ Variant::CallError err;
+ E->get().call(this,args,1,err);
+ if (err.error!=Variant::CallError::CALL_OK) {
+ //print error about notification call
+
+ }
+ }
+ sptr = sptr->_base;
+ }
+
+}
+
+Ref<Script> GDInstance::get_script() const {
+
+ return script;
+}
+
+ScriptLanguage *GDInstance::get_language() {
+
+ return GDScriptLanguage::get_singleton();
+}
+
+
+GDInstance::GDInstance() {
+ owner=NULL;
+ base_ref=false;
+}
+
+GDInstance::~GDInstance() {
+ if (script.is_valid() && owner) {
+ script->instances.erase(owner);
+ }
+}
+
+/************* SCRIPT LANGUAGE **************/
+/************* SCRIPT LANGUAGE **************/
+/************* SCRIPT LANGUAGE **************/
+/************* SCRIPT LANGUAGE **************/
+/************* SCRIPT LANGUAGE **************/
+
+GDScriptLanguage *GDScriptLanguage::singleton=NULL;
+
+
+String GDScriptLanguage::get_name() const {
+
+ return "GDScript";
+}
+
+/* LANGUAGE FUNCTIONS */
+
+void GDScriptLanguage::_add_global(const StringName& p_name,const Variant& p_value) {
+
+
+ if (globals.has(p_name)) {
+ //overwrite existing
+ global_array[globals[p_name]]=p_value;
+ return;
+ }
+ globals[p_name]=global_array.size();
+ global_array.push_back(p_value);
+ _global_array=global_array.ptr();
+}
+
+void GDScriptLanguage::init() {
+
+
+ //populate global constants
+ int gcc=GlobalConstants::get_global_constant_count();
+ for(int i=0;i<gcc;i++) {
+
+ _add_global(StaticCString::create(GlobalConstants::get_global_constant_name(i)),GlobalConstants::get_global_constant_value(i));
+ }
+
+ _add_global(StaticCString::create("PI"),Math_PI);
+
+ //populate native classes
+
+ List<String> class_list;
+ ObjectTypeDB::get_type_list(&class_list);
+ for(List<String>::Element *E=class_list.front();E;E=E->next()) {
+
+ StringName n = E->get();
+ String s = String(n);
+ if (s.begins_with("_"))
+ n=s.substr(1,s.length());
+
+ if (globals.has(n))
+ continue;
+ Ref<GDNativeClass> nc = memnew( GDNativeClass(E->get()) );
+ _add_global(n,nc);
+ }
+
+ //populate singletons
+
+ List<Globals::Singleton> singletons;
+ Globals::get_singleton()->get_singletons(&singletons);
+ for(List<Globals::Singleton>::Element *E=singletons.front();E;E=E->next()) {
+
+ _add_global(E->get().name,E->get().ptr);
+ }
+}
+
+String GDScriptLanguage::get_type() const {
+
+ return "GDScript";
+}
+String GDScriptLanguage::get_extension() const {
+
+ return "gd";
+}
+Error GDScriptLanguage::execute_file(const String& p_path) {
+
+ // ??
+ return OK;
+}
+void GDScriptLanguage::finish() {
+
+
+}
+
+
+void GDScriptLanguage::frame() {
+
+// print_line("calls: "+itos(calls));
+ calls=0;
+}
+
+/* EDITOR FUNCTIONS */
+void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
+
+ static const char *_reserved_words[]={
+ "break",
+ "class",
+ "continue",
+ "const",
+ "else",
+ "elif",
+ "enum",
+ "extends" ,
+ "for" ,
+ "func" ,
+ "if" ,
+ "in" ,
+ "varl",
+ "null" ,
+ "return" ,
+ "self" ,
+ "while" ,
+ "true" ,
+ "false" ,
+ "tool",
+ "var",
+ "pass",
+ "and",
+ "or",
+ "export",
+ 0};
+
+
+ const char **w=_reserved_words;
+
+
+ while (*w) {
+
+ p_words->push_back(*w);
+ w++;
+ }
+
+ for(int i=0;i<GDFunctions::FUNC_MAX;i++) {
+ p_words->push_back(GDFunctions::get_func_name(GDFunctions::Function(i)));
+ }
+
+}
+
+GDScriptLanguage::GDScriptLanguage() {
+
+ calls=0;
+ ERR_FAIL_COND(singleton);
+ singleton=this;
+ strings._init = StaticCString::create("_init");
+ strings._notification = StaticCString::create("_notification");
+ strings._set= StaticCString::create("_set");
+ strings._get= StaticCString::create("_get");
+ strings._get_property_list= StaticCString::create("_get_property_list");
+ strings._script_source=StaticCString::create("script/source");
+ _debug_parse_err_line=-1;
+ _debug_parse_err_file="";
+
+ _debug_call_stack_pos=0;
+ int dmcs=GLOBAL_DEF("debug/script_max_call_stack",1024);
+ if (ScriptDebugger::get_singleton()) {
+ //debugging enabled!
+
+ _debug_max_call_stack = dmcs;
+ if (_debug_max_call_stack<1024)
+ _debug_max_call_stack=1024;
+ _call_stack = memnew_arr( CallLevel, _debug_max_call_stack+1 );
+
+ } else {
+ _debug_max_call_stack=0;
+ _call_stack=NULL;
+ }
+
+}
+
+
+GDScriptLanguage::~GDScriptLanguage() {
+
+ if (_call_stack) {
+ memdelete_arr(_call_stack);
+ }
+ singleton=NULL;
+}
+
+/*************** RESOURCE ***************/
+
+RES ResourceFormatLoaderGDScript::load(const String &p_path,const String& p_original_path) {
+
+ GDScript *script = memnew( GDScript );
+
+ Ref<GDScript> scriptres(script);
+
+ Error err = script->load_source_code(p_path);
+
+ if (err!=OK) {
+
+ ERR_FAIL_COND_V(err!=OK, RES());
+ }
+
+ script->set_script_path(p_original_path); // script needs this.
+ script->set_path(p_original_path);
+ //script->set_name(p_path.get_file());
+
+ script->reload();
+
+ return scriptres;
+}
+void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("gd");
+}
+
+bool ResourceFormatLoaderGDScript::handles_type(const String& p_type) const {
+
+ return (p_type=="Script" || p_type=="GDScript");
+}
+
+String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const {
+
+ if (p_path.extension().to_lower()=="gd")
+ return "GDScript";
+ return "";
+}
+
+
+Error ResourceFormatSaverGDScript::save(const String &p_path,const RES& p_resource,uint32_t p_flags) {
+
+ Ref<GDScript> sqscr = p_resource;
+ ERR_FAIL_COND_V(sqscr.is_null(),ERR_INVALID_PARAMETER);
+
+ String source = sqscr->get_source_code();
+
+ Error err;
+ FileAccess *file = FileAccess::open(p_path,FileAccess::WRITE,&err);
+
+
+ if (err) {
+
+ ERR_FAIL_COND_V(err,err);
+ }
+
+ file->store_string(source);
+
+ file->close();
+ memdelete(file);
+ return OK;
+}
+
+void ResourceFormatSaverGDScript::get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const {
+
+ if (p_resource->cast_to<GDScript>()) {
+ p_extensions->push_back("gd");
+ }
+
+}
+bool ResourceFormatSaverGDScript::recognize(const RES& p_resource) const {
+
+ return p_resource->cast_to<GDScript>()!=NULL;
+}
diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h
new file mode 100644
index 0000000000..70dec4e8ee
--- /dev/null
+++ b/modules/gdscript/gd_script.h
@@ -0,0 +1,473 @@
+/*************************************************************************/
+/* gd_script.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_SCRIPT_H
+#define GD_SCRIPT_H
+
+#include "script_language.h"
+#include "io/resource_loader.h"
+#include "io/resource_saver.h"
+#include "os/thread.h"
+#include "pair.h"
+class GDInstance;
+class GDScript;
+
+class GDFunction {
+public:
+
+ enum Opcode {
+ OPCODE_OPERATOR,
+ OPCODE_EXTENDS_TEST,
+ OPCODE_SET,
+ OPCODE_GET,
+ OPCODE_SET_NAMED,
+ OPCODE_GET_NAMED,
+ OPCODE_ASSIGN,
+ OPCODE_ASSIGN_TRUE,
+ OPCODE_ASSIGN_FALSE,
+ OPCODE_CONSTRUCT, //only for basic types!!
+ OPCODE_CONSTRUCT_ARRAY,
+ OPCODE_CONSTRUCT_DICTIONARY,
+ OPCODE_CALL,
+ OPCODE_CALL_RETURN,
+ OPCODE_CALL_BUILT_IN,
+ OPCODE_CALL_SELF,
+ OPCODE_CALL_SELF_BASE,
+ OPCODE_JUMP,
+ OPCODE_JUMP_IF,
+ OPCODE_JUMP_IF_NOT,
+ OPCODE_JUMP_TO_DEF_ARGUMENT,
+ OPCODE_RETURN,
+ OPCODE_ITERATE_BEGIN,
+ OPCODE_ITERATE,
+ OPCODE_ASSERT,
+ OPCODE_LINE,
+ OPCODE_END
+ };
+
+ enum Address {
+ ADDR_BITS=24,
+ ADDR_MASK=((1<<ADDR_BITS)-1),
+ ADDR_TYPE_MASK=~ADDR_MASK,
+ ADDR_TYPE_SELF=0,
+ ADDR_TYPE_MEMBER=1,
+ ADDR_TYPE_CLASS_CONSTANT=2,
+ ADDR_TYPE_LOCAL_CONSTANT=3,
+ ADDR_TYPE_STACK=4,
+ ADDR_TYPE_STACK_VARIABLE=5,
+ ADDR_TYPE_GLOBAL=6,
+ ADDR_TYPE_NIL=7
+ };
+
+ struct StackDebug {
+
+ int line;
+ int pos;
+ bool added;
+ StringName identifier;
+ };
+
+private:
+friend class GDCompiler;
+
+ StringName source;
+
+ mutable Variant nil;
+ mutable Variant *_constants_ptr;
+ int _constant_count;
+ const StringName *_global_names_ptr;
+ int _global_names_count;
+ const int *_default_arg_ptr;
+ int _default_arg_count;
+ const int *_code_ptr;
+ int _code_size;
+ int _argument_count;
+ int _stack_size;
+ int _call_size;
+ int _initial_line;
+ bool _static;
+ GDScript *_script;
+
+ StringName name;
+ Vector<Variant> constants;
+ Vector<StringName> global_names;
+ Vector<int> default_arguments;
+
+ Vector<int> code;
+
+ List<StackDebug> stack_debug;
+
+ _FORCE_INLINE_ Variant *_get_variant(int p_address,GDInstance *p_instance,GDScript *p_script,Variant &self,Variant *p_stack,String& r_error) const;
+ _FORCE_INLINE_ String _get_call_error(const Variant::CallError& p_err, const String& p_where,const Variant**argptrs) const;
+
+
+public:
+
+
+ _FORCE_INLINE_ bool is_static() const { return _static; }
+
+ const int* get_code() const; //used for debug
+ int get_code_size() const;
+ Variant get_constant(int p_idx) const;
+ StringName get_global_name(int p_idx) const;
+ StringName get_name() const;
+ int get_max_stack_size() const;
+ int get_default_argument_count() const;
+ int get_default_argument_addr(int p_idx) const;
+ GDScript *get_script() const { return _script; }
+
+ void debug_get_stack_member_state(int p_line,List<Pair<StringName,int> > *r_stackvars) const;
+
+ _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);
+
+ GDFunction();
+};
+
+
+class GDNativeClass : public Reference {
+
+ OBJ_TYPE(GDNativeClass,Reference);
+
+ StringName name;
+protected:
+
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ static void _bind_methods();
+
+public:
+
+ _FORCE_INLINE_ const StringName& get_name() const { return name; }
+ Variant _new();
+ Object *instance();
+ GDNativeClass(const StringName& p_name);
+};
+
+
+class GDScript : public Script {
+
+
+ OBJ_TYPE(GDScript,Script);
+ bool tool;
+ bool valid;
+
+
+friend class GDInstance;
+friend class GDFunction;
+friend class GDCompiler;
+friend class GDFunctions;
+ Ref<GDNativeClass> native;
+ Ref<GDScript> base;
+ GDScript *_base; //fast pointer access
+ GDScript *_owner; //for subclasses
+
+ Set<StringName> members; //members are just indices to the instanced script.
+ Map<StringName,Variant> constants;
+ Map<StringName,GDFunction> member_functions;
+ Map<StringName,int> member_indices; //members are just indices to the instanced script.
+ Map<StringName,Ref<GDScript> > subclasses;
+
+#ifdef TOOLS_ENABLED
+ Map<StringName,Variant> member_default_values;
+#endif
+ Map<StringName,PropertyInfo> member_info;
+
+ GDFunction *initializer; //direct pointer to _init , faster to locate
+
+ int subclass_count;
+ Set<Object*> instances;
+ //exported members
+ String source;
+ String path;
+ String name;
+
+
+ GDInstance* _create_instance(const Variant** p_args,int p_argcount,Object *p_owner,bool p_isref);
+
+ void _set_subclass_path(Ref<GDScript>& p_sc,const String& p_path);
+
+#ifdef TOOLS_ENABLED
+ Set<PlaceHolderScriptInstance*> placeholders;
+ void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
+ virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
+#endif
+
+
+protected:
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ bool _set(const StringName& p_name, const Variant& p_value);
+ void _get_property_list(List<PropertyInfo> *p_properties) const;
+
+ Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error);
+// void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
+
+ static void _bind_methods();
+public:
+
+
+ const Map<StringName,Ref<GDScript> >& get_subclasses() const { return subclasses; }
+ const Map<StringName,Variant >& get_constants() const { return constants; }
+ const Set<StringName>& get_members() const { return members; }
+ const Map<StringName,GDFunction>& get_member_functions() const { return member_functions; }
+ const Ref<GDNativeClass>& get_native() const { return native; }
+
+
+ bool is_tool() const { return tool; }
+ Ref<GDScript> get_base() const;
+
+ const Map<StringName,int>& debug_get_member_indices() const { return member_indices; }
+ const Map<StringName,GDFunction>& debug_get_member_functions() const; //this is debug only
+ StringName debug_get_member_by_index(int p_idx) const;
+
+ Variant _new(const Variant** p_args,int p_argcount,Variant::CallError& r_error);
+ virtual bool can_instance() const;
+
+ virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so
+ virtual ScriptInstance* instance_create(Object *p_this);
+ virtual bool instance_has(const Object *p_this) const;
+
+ virtual bool has_source_code() const;
+ virtual String get_source_code() const;
+ virtual void set_source_code(const String& p_code);
+ virtual Error reload();
+
+ virtual String get_node_type() const;
+ void set_script_path(const String& p_path) { path=p_path; } //because subclasses need a path too...
+ Error load_source_code(const String& p_path);
+
+ virtual ScriptLanguage *get_language() const;
+
+ GDScript();
+};
+
+class GDInstance : public ScriptInstance {
+friend class GDScript;
+friend class GDFunction;
+friend class GDFunctions;
+
+ Object *owner;
+ Ref<GDScript> script;
+ Vector<Variant> members;
+ bool base_ref;
+
+ void _ml_call_reversed(GDScript *sptr,const StringName& p_method,const Variant** p_args,int p_argcount);
+
+public:
+
+ virtual bool set(const StringName& p_name, const Variant& p_value);
+ virtual bool get(const StringName& p_name, Variant &r_ret) const;
+ virtual void get_property_list(List<PropertyInfo> *p_properties) const;
+
+ virtual void get_method_list(List<MethodInfo> *p_list) const;
+ virtual bool has_method(const StringName& p_method) const;
+ virtual Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error);
+ virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
+ virtual void call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount);
+
+ Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; }
+
+ virtual void notification(int p_notification);
+
+ virtual Ref<Script> get_script() const;
+
+ virtual ScriptLanguage *get_language();
+
+ void set_path(const String& p_path);
+
+
+ GDInstance();
+ ~GDInstance();
+
+};
+
+class GDScriptLanguage : public ScriptLanguage {
+
+ static GDScriptLanguage *singleton;
+
+ Variant* _global_array;
+ Vector<Variant> global_array;
+ Map<StringName,int> globals;
+
+
+ struct CallLevel {
+
+ Variant *stack;
+ GDFunction *function;
+ GDInstance *instance;
+ int *ip;
+ int *line;
+
+ };
+
+
+ int _debug_parse_err_line;
+ String _debug_parse_err_file;
+ String _debug_error;
+ int _debug_call_stack_pos;
+ int _debug_max_call_stack;
+ CallLevel *_call_stack;
+
+ void _add_global(const StringName& p_name,const Variant& p_value);
+
+
+public:
+
+ int calls;
+
+ bool debug_break(const String& p_error,bool p_allow_continue=true);
+ bool debug_break_parse(const String& p_file, int p_line,const String& p_error);
+
+ _FORCE_INLINE_ void enter_function(GDInstance *p_instance,GDFunction *p_function, Variant *p_stack, int *p_ip, int *p_line) {
+
+ if (Thread::get_main_ID()!=Thread::get_caller_ID())
+ return; //no support for other threads than main for now
+
+ if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
+ ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() +1 );
+
+ if (_debug_call_stack_pos >= _debug_max_call_stack) {
+ //stack overflow
+ _debug_error="Stack Overflow (Stack Size: "+itos(_debug_max_call_stack)+")";
+ ScriptDebugger::get_singleton()->debug(this);
+ return;
+ }
+
+ _call_stack[_debug_call_stack_pos].stack=p_stack;
+ _call_stack[_debug_call_stack_pos].instance=p_instance;
+ _call_stack[_debug_call_stack_pos].function=p_function;
+ _call_stack[_debug_call_stack_pos].ip=p_ip;
+ _call_stack[_debug_call_stack_pos].line=p_line;
+ _debug_call_stack_pos++;
+ }
+
+ _FORCE_INLINE_ void exit_function() {
+
+ if (Thread::get_main_ID()!=Thread::get_caller_ID())
+ return; //no support for other threads than main for now
+
+ if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0)
+ ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() -1 );
+
+ if (_debug_call_stack_pos==0) {
+
+ _debug_error="Stack Underflow (Engine Bug)";
+ ScriptDebugger::get_singleton()->debug(this);
+ return;
+ }
+
+ _debug_call_stack_pos--;
+ }
+
+
+ struct {
+
+ StringName _init;
+ StringName _notification;
+ StringName _set;
+ StringName _get;
+ StringName _get_property_list;
+ StringName _script_source;
+
+ } strings;
+
+
+ _FORCE_INLINE_ int get_global_array_size() const { return global_array.size(); }
+ _FORCE_INLINE_ Variant* get_global_array() { return _global_array; }
+ _FORCE_INLINE_ const Map<StringName,int>& get_global_map() { return globals; }
+
+ _FORCE_INLINE_ static GDScriptLanguage *get_singleton() { return singleton; }
+
+ virtual String get_name() const;
+
+ /* LANGUAGE FUNCTIONS */
+ virtual void init();
+ virtual String get_type() const;
+ virtual String get_extension() const;
+ virtual Error execute_file(const String& p_path) ;
+ virtual void finish();
+
+ /* EDITOR FUNCTIONS */
+ virtual void get_reserved_words(List<String> *p_words) const;
+ virtual void get_comment_delimiters(List<String> *p_delimiters) const;
+ virtual void get_string_delimiters(List<String> *p_delimiters) const;
+ virtual String get_template(const String& p_class_name, const String& p_base_class_name) const;
+ virtual bool validate(const String& p_script,int &r_line_error,int &r_col_error,String& r_test_error, const String& p_path="",List<String> *r_functions=NULL) const;
+ virtual Script *create_script() const;
+ virtual bool has_named_classes() const;
+ virtual int find_function(const String& p_function,const String& p_code) const;
+ virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const;
+ virtual Error complete_keyword(const String& p_code, int p_line, const String& p_base_path,const String& p_keyword, List<String>* r_options);
+
+ /* DEBUGGER FUNCTIONS */
+
+ virtual String debug_get_error() const;
+ virtual int debug_get_stack_level_count() const;
+ virtual int debug_get_stack_level_line(int p_level) const;
+ virtual String debug_get_stack_level_function(int p_level) const;
+ virtual String debug_get_stack_level_source(int p_level) const;
+ virtual void debug_get_stack_level_locals(int p_level,List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1);
+ virtual void debug_get_stack_level_members(int p_level,List<String> *p_members, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1);
+ virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1);
+ virtual String debug_parse_stack_level_expression(int p_level,const String& p_expression,int p_max_subitems=-1,int p_max_depth=-1);
+
+ virtual void frame();
+
+ virtual void get_public_functions(List<MethodInfo> *p_functions) const;
+ virtual void get_public_constants(List<Pair<String,Variant> > *p_constants) const;
+
+ /* LOADER FUNCTIONS */
+
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+
+ GDScriptLanguage();
+ ~GDScriptLanguage();
+};
+
+
+class ResourceFormatLoaderGDScript : public ResourceFormatLoader {
+public:
+
+ virtual RES load(const String &p_path,const String& p_original_path="");
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+
+};
+
+class ResourceFormatSaverGDScript : public ResourceFormatSaver {
+public:
+
+ virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0);
+ virtual void get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const;
+ virtual bool recognize(const RES& p_resource) const;
+
+};
+
+#endif // GD_SCRIPT_H
diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp
new file mode 100644
index 0000000000..f7320799a5
--- /dev/null
+++ b/modules/gdscript/gd_tokenizer.cpp
@@ -0,0 +1,973 @@
+/*************************************************************************/
+/* gd_tokenizer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "gd_tokenizer.h"
+#include "print_string.h"
+#include "gd_functions.h"
+const char* GDTokenizer::token_names[TK_MAX]={
+"Empty",
+"Identifier",
+"Constant",
+"Self",
+"Built-In Type",
+"Built-In Func",
+"In",
+"'=='",
+"'!='",
+"'<'",
+"'<='",
+"'>'",
+"'>='",
+"'and'",
+"'or'",
+"'not'",
+"'+'",
+"'-'",
+"'*'",
+"'/'",
+"'%'",
+"'<<'",
+"'>>'",
+"'='",
+"'+='",
+"'-='",
+"'*='",
+"'/='",
+"'%='",
+"'<<='",
+"'>>='",
+"'&='",
+"'|='",
+"'^='",
+"'&'",
+"'|'",
+"'^'",
+"'~'",
+//"Plus Plus",
+//"Minus Minus",
+"if",
+"elif",
+"else",
+"for",
+"do",
+"while",
+"switch",
+"case",
+"break",
+"continue",
+"pass",
+"return",
+"func",
+"class",
+"extends",
+"tool",
+"static",
+"export",
+"const",
+"var",
+"preload",
+"assert",
+"'['",
+"']'",
+"'{'",
+"'}'",
+"'('",
+"')'",
+"','",
+"';'",
+"'.'",
+"'?'",
+"':'",
+"'\\n'",
+"Error",
+"EOF"};
+
+const char *GDTokenizer::get_token_name(Token p_token) {
+
+ ERR_FAIL_INDEX_V(p_token,TK_MAX,"<error>");
+ return token_names[p_token];
+}
+
+static bool _is_text_char(CharType c) {
+
+ return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
+}
+
+static bool _is_number(CharType c) {
+
+ return (c>='0' && c<='9');
+}
+
+static bool _is_hex(CharType c) {
+
+ return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F');
+}
+
+void GDTokenizer::_make_token(Token p_type) {
+
+ TokenData &tk=tk_rb[tk_rb_pos];
+
+ tk.type=p_type;
+ tk.line=line;
+ tk.col=column;
+
+ tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
+}
+void GDTokenizer::_make_identifier(const StringName& p_identifier) {
+
+ TokenData &tk=tk_rb[tk_rb_pos];
+
+ tk.type=TK_IDENTIFIER;
+ tk.identifier=p_identifier;
+ tk.line=line;
+ tk.col=column;
+
+ tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
+
+}
+
+void GDTokenizer::_make_built_in_func(GDFunctions::Function p_func) {
+
+ TokenData &tk=tk_rb[tk_rb_pos];
+
+ tk.type=TK_BUILT_IN_FUNC;
+ tk.func=p_func;
+ tk.line=line;
+ tk.col=column;
+
+ tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
+
+}
+void GDTokenizer::_make_constant(const Variant& p_constant) {
+
+ TokenData &tk=tk_rb[tk_rb_pos];
+
+ tk.type=TK_CONSTANT;
+ tk.constant=p_constant;
+ tk.line=line;
+ tk.col=column;
+
+ tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
+
+}
+
+void GDTokenizer::_make_type(const Variant::Type& p_type) {
+
+
+ TokenData &tk=tk_rb[tk_rb_pos];
+
+ tk.type=TK_BUILT_IN_TYPE;
+ tk.vtype=p_type;
+ tk.line=line;
+ tk.col=column;
+
+ tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
+
+}
+
+
+void GDTokenizer::_make_error(const String& p_error) {
+
+ error_flag=true;
+ last_error=p_error;
+
+ TokenData &tk=tk_rb[tk_rb_pos];
+ tk.type=TK_ERROR;
+ tk.constant=p_error;
+ tk.line=line;
+ tk.col=column;
+ tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
+
+}
+
+
+void GDTokenizer::_make_newline(int p_spaces) {
+
+ TokenData &tk=tk_rb[tk_rb_pos];
+ tk.type=TK_NEWLINE;
+ tk.constant=p_spaces;
+ tk.line=line;
+ tk.col=column;
+ tk_rb_pos=(tk_rb_pos+1)%TK_RB_SIZE;
+}
+
+void GDTokenizer::_advance() {
+
+ if (error_flag) {
+ //parser broke
+ _make_error(last_error);
+ return;
+ }
+
+ if (code_pos>=len) {
+ _make_token(TK_EOF);
+ return;
+ }
+#define GETCHAR(m_ofs) ((m_ofs+code_pos)>=len?0:_code[m_ofs+code_pos])
+#define INCPOS(m_amount) { code_pos+=m_amount; column+=m_amount; }
+ while (true) {
+
+
+ bool is_node_path=false;
+
+ switch(GETCHAR(0)) {
+ case 0:
+ _make_token(TK_EOF);
+ break;
+ case '\t':
+ case '\r':
+ case ' ':
+ INCPOS(1);
+ continue;
+ case '\n': {
+ line++;
+ INCPOS(1);
+ column=0;
+ int i=0;
+ while(GETCHAR(i)==' ' || GETCHAR(i)=='\t') {
+ i++;
+ }
+
+ _make_newline(i);
+ return;
+ }
+#if 1 //py style tokenizer
+ case '#': { // line comment skip
+
+ while(GETCHAR(0)!='\n') {
+ code_pos++;
+ if (GETCHAR(0)==0) { //end of file
+ _make_error("Unterminated Comment");
+ return;
+ }
+ }
+ INCPOS(1);
+ column=0;
+ line++;
+ int i=0;
+ while(GETCHAR(i)==' ' || GETCHAR(i)=='\t') {
+ i++;
+ }
+ _make_newline(i);
+ return;
+
+ } break;
+#endif
+ case '/': {
+
+ switch(GETCHAR(1)) {
+#if 0 // c style tokenizer
+ case '*': { // block comment
+ int pos = code_pos+2;
+ int new_line=line;
+ int new_col=column+2;
+
+ while(true) {
+ if (_code[pos]=='0') {
+ _make_error("Unterminated Comment");
+ code_pos=pos;
+ return;
+ }
+ if (_code[pos]=='*' && _code[pos+1]=='/') {
+ new_col+=2;
+ pos+=2; //compensate
+ break;
+ } else if (_code[pos]=='\n') {
+ new_line++;
+ new_col=0;
+ } else {
+ new_col++;
+ }
+ pos++;
+ }
+
+ column=new_col;
+ line=new_line;
+ code_pos=pos;
+ continue;
+
+ } break;
+ case '/': { // line comment skip
+
+ while(GETCHAR(0)!='\n') {
+ code_pos++;
+ if (GETCHAR(0)==0) { //end of file
+ _make_error("Unterminated Comment");
+ return;
+ }
+ }
+ INCPOS(1);
+ column=0;
+ line++;
+ continue;
+
+ } break;
+#endif
+ case '=': { // diveq
+
+ _make_token(TK_OP_ASSIGN_DIV);
+ INCPOS(1);
+
+ } break;
+ default:
+ _make_token(TK_OP_DIV);
+
+ }
+ } break;
+ case '=': {
+ if (GETCHAR(1)=='=') {
+ _make_token(TK_OP_EQUAL);
+ INCPOS(1);
+
+ } else
+ _make_token(TK_OP_ASSIGN);
+
+ } break;
+ case '<': {
+ if (GETCHAR(1)=='=') {
+
+ _make_token(TK_OP_LESS_EQUAL);
+ INCPOS(1);
+ } else if (GETCHAR(1)=='<') {
+ if (GETCHAR(2)=='=') {
+ _make_token(TK_OP_ASSIGN_SHIFT_LEFT);
+ INCPOS(1);
+ } else {
+ _make_token(TK_OP_SHIFT_LEFT);
+ }
+ INCPOS(1);
+ } else
+ _make_token(TK_OP_LESS);
+
+ } break;
+ case '>': {
+ if (GETCHAR(1)=='=') {
+ _make_token(TK_OP_GREATER_EQUAL);
+ INCPOS(1);
+ } else if (GETCHAR(1)=='>') {
+ if (GETCHAR(2)=='=') {
+ _make_token(TK_OP_ASSIGN_SHIFT_RIGHT);
+ INCPOS(1);
+
+ } else {
+ _make_token(TK_OP_SHIFT_RIGHT);
+ }
+ INCPOS(1);
+ } else {
+ _make_token(TK_OP_GREATER);
+ }
+
+ } break;
+ case '!': {
+ if (GETCHAR(1)=='=') {
+ _make_token(TK_OP_NOT_EQUAL);
+ INCPOS(1);
+ } else {
+ _make_token(TK_OP_NOT);
+ }
+
+ } break;
+ //case '"' //string - no strings in shader
+ //case '\'' //string - no strings in shader
+ case '{':
+ _make_token(TK_CURLY_BRACKET_OPEN);
+ break;
+ case '}':
+ _make_token(TK_CURLY_BRACKET_CLOSE);
+ break;
+ case '[':
+ _make_token(TK_BRACKET_OPEN);
+ break;
+ case ']':
+ _make_token(TK_BRACKET_CLOSE);
+ break;
+ case '(':
+ _make_token(TK_PARENTHESIS_OPEN);
+ break;
+ case ')':
+ _make_token(TK_PARENTHESIS_CLOSE);
+ break;
+ case ',':
+ _make_token(TK_COMMA);
+ break;
+ case ';':
+ _make_token(TK_SEMICOLON);
+ break;
+ case '?':
+ _make_token(TK_QUESTION_MARK);
+ break;
+ case ':':
+ _make_token(TK_COLON); //for methods maybe but now useless.
+ break;
+ case '^': {
+ if (GETCHAR(1)=='=') {
+ _make_token(TK_OP_ASSIGN_BIT_XOR);
+ INCPOS(1);
+ } else {
+ _make_token(TK_OP_BIT_XOR);
+ }
+
+ } break;
+ case '~':
+ _make_token(TK_OP_BIT_INVERT);
+ break;
+ case '&': {
+ if (GETCHAR(1)=='&') {
+
+ _make_token(TK_OP_AND);
+ INCPOS(1);
+ } else if (GETCHAR(1)=='=') {
+ _make_token(TK_OP_ASSIGN_BIT_AND);
+ INCPOS(1);
+ } else {
+ _make_token(TK_OP_BIT_AND);
+ }
+ } break;
+ case '|': {
+ if (GETCHAR(1)=='|') {
+
+ _make_token(TK_OP_OR);
+ INCPOS(1);
+ } else if (GETCHAR(1)=='=') {
+ _make_token(TK_OP_ASSIGN_BIT_OR);
+ INCPOS(1);
+ } else {
+ _make_token(TK_OP_BIT_OR);
+ }
+ } break;
+ case '*': {
+
+ if (GETCHAR(1)=='=') {
+ _make_token(TK_OP_ASSIGN_MUL);
+ INCPOS(1);
+ } else {
+ _make_token(TK_OP_MUL);
+ }
+ } break;
+ case '+': {
+
+ if (GETCHAR(1)=='=') {
+ _make_token(TK_OP_ASSIGN_ADD);
+ INCPOS(1);
+ //} else if (GETCHAR(1)=='+') {
+ // _make_token(TK_OP_PLUS_PLUS);
+ // INCPOS(1);
+ } else {
+ _make_token(TK_OP_ADD);
+ }
+
+ } break;
+ case '-': {
+
+ if (GETCHAR(1)=='=') {
+ _make_token(TK_OP_ASSIGN_SUB);
+ INCPOS(1);
+ //} else if (GETCHAR(1)=='-') {
+ // _make_token(TK_OP_MINUS_MINUS);
+ // INCPOS(1);
+ } else {
+ _make_token(TK_OP_SUB);
+ }
+ } break;
+ case '%': {
+
+ if (GETCHAR(1)=='=') {
+ _make_token(TK_OP_ASSIGN_MOD);
+ INCPOS(1);
+ } else {
+ _make_token(TK_OP_MOD);
+ }
+ } break;
+ case '@':
+ if (CharType(GETCHAR(1))!='"') {
+ _make_error("Unexpected '@'");
+ return;
+ }
+ INCPOS(1);
+ is_node_path=true;
+ case '"': {
+
+ int i=1;
+ String str;
+ while(true) {
+ if (CharType(GETCHAR(i)==0)) {
+
+ _make_error("Unterminated String");
+ return;
+ } else if (CharType(GETCHAR(i)=='"')) {
+ break;
+ } else if (CharType(GETCHAR(i)=='\\')) {
+ //escaped characters...
+ i++;
+ CharType next = GETCHAR(i);
+ if (next==0) {
+ _make_error("Unterminated String");
+ return;
+ }
+ CharType res=0;
+
+ switch(next) {
+
+ case 'a': res=7; break;
+ case 'b': res=8; break;
+ case 't': res=9; break;
+ case 'n': res=10; break;
+ case 'v': res=11; break;
+ case 'f': res=12; break;
+ case 'r': res=13; break;
+ case '\'': res='\''; break;
+ case '\"': res='\"'; break;
+ case '\\': res='\\'; break;
+ case 'x': {
+ //hexnumbarh - oct is deprecated
+
+ int read=0;
+ for(int j=0;j<4;j++) {
+ CharType c = GETCHAR(i+j);
+ if (c==0) {
+ _make_error("Unterminated String");
+ return;
+ }
+ if (!_is_hex(c)) {
+ if (j==0 || !(j&1)) {
+ _make_error("Malformed hex constant in string");
+ return;
+ } else
+ break;
+ }
+ CharType v;
+ if (c>='0' && c<='9') {
+ v=c-'0';
+ } else if (c>='a' && c<='f') {
+ v=c-'a';
+ v+=10;
+ } else if (c>='A' && c<='F') {
+ v=c-'A';
+ v+=10;
+ } else {
+ ERR_PRINT("BUG");
+ v=0;
+ }
+
+ res<<=4;
+ res|=v;
+
+ read++;
+ }
+ i+=read-1;
+
+
+ } break;
+ default: {
+
+ _make_error("Invalid escape sequence");
+ return;
+ } break;
+ }
+
+ str+=res;
+
+ } else {
+ str+=CharType(GETCHAR(i));
+ }
+ i++;
+ }
+ INCPOS(i);
+
+ if (is_node_path) {
+ _make_constant(NodePath(str));
+ } else {
+ _make_constant(str);
+ }
+
+ } break;
+ default: {
+
+ if (_is_number(GETCHAR(0)) || (GETCHAR(0)=='.' && _is_number(GETCHAR(1)))) {
+ // parse number
+ bool period_found=false;
+ bool exponent_found=false;
+ bool hexa_found=false;
+ bool sign_found=false;
+
+ String str;
+ int i=0;
+
+ while(true) {
+ if (GETCHAR(i)=='.') {
+ if (period_found || exponent_found) {
+ _make_error("Invalid numeric constant at '.'");
+ return;
+ }
+ period_found=true;
+ } else if (GETCHAR(i)=='x') {
+ if (hexa_found || str.length()!=1 || !( (i==1 && str[0]=='0') || (i==2 && str[1]=='0' && str[0]=='-') ) ) {
+ _make_error("Invalid numeric constant at 'x'");
+ return;
+ }
+ hexa_found=true;
+ } else if (!hexa_found && GETCHAR(i)=='e') {
+ if (hexa_found || exponent_found) {
+ _make_error("Invalid numeric constant at 'e'");
+ return;
+ }
+ exponent_found=true;
+ } else if (_is_number(GETCHAR(i))) {
+ //all ok
+ } else if (hexa_found && _is_hex(GETCHAR(i))) {
+
+ } else if ((GETCHAR(i)=='-' || GETCHAR(i)=='+') && exponent_found) {
+ if (sign_found) {
+ _make_error("Invalid numeric constant at '-'");
+ return;
+ }
+ sign_found=true;
+ } else
+ break;
+
+ str+=CharType(GETCHAR(i));
+ i++;
+ }
+
+ if (!( _is_number(str[str.length()-1]) || (hexa_found && _is_hex(str[str.length()-1])))) {
+ _make_error("Invalid numeric constant: "+str);
+ return;
+ }
+
+ INCPOS(str.length());
+ if (hexa_found) {
+ int val = str.hex_to_int();
+ _make_constant(val);
+ } else if (period_found) {
+ real_t val = str.to_double();
+ //print_line("*%*%*%*% to convert: "+str+" result: "+rtos(val));
+ _make_constant(val);
+ } else {
+ int val = str.to_int();
+ _make_constant(val);
+
+ }
+
+ return;
+ }
+
+ if (GETCHAR(0)=='.') {
+ //parse period
+ _make_token(TK_PERIOD);
+ break;
+ }
+
+ if (_is_text_char(GETCHAR(0))) {
+ // parse identifier
+ String str;
+ str+=CharType(GETCHAR(0));
+
+ int i=1;
+ while(_is_text_char(GETCHAR(i))) {
+ str+=CharType(GETCHAR(i));
+ i++;
+ }
+
+ bool identifier=false;
+
+ if (str=="null") {
+ _make_constant(Variant());
+
+ } else if (str=="true") {
+ _make_constant(true);
+
+ } else if (str=="false") {
+ _make_constant(false);
+ } else {
+
+ bool found=false;
+
+ struct _bit { Variant::Type type; const char *text;};
+ //built in types
+
+ static const _bit type_list[]={
+ //types
+ {Variant::BOOL,"bool"},
+ {Variant::INT,"int"},
+ {Variant::REAL,"float"},
+ {Variant::STRING,"String"},
+ {Variant::VECTOR2,"vec2"},
+ {Variant::VECTOR2,"Vector2"},
+ {Variant::RECT2,"Rect2"},
+ {Variant::MATRIX32,"Matrix32"},
+ {Variant::MATRIX32,"mat32"},
+ {Variant::VECTOR3,"vec3"},
+ {Variant::VECTOR3,"Vector3"},
+ {Variant::_AABB,"AABB"},
+ {Variant::_AABB,"Rect3"},
+ {Variant::PLANE,"Plane"},
+ {Variant::QUAT,"Quat"},
+ {Variant::MATRIX3,"mat3"},
+ {Variant::MATRIX3,"Matrix3"},
+ {Variant::TRANSFORM,"trn"},
+ {Variant::TRANSFORM,"Transform"},
+ {Variant::COLOR,"Color"},
+ {Variant::IMAGE,"Image"},
+ {Variant::_RID,"RID"},
+ {Variant::OBJECT,"Object"},
+ {Variant::INPUT_EVENT,"InputEvent"},
+ {Variant::DICTIONARY,"dict"},
+ {Variant::DICTIONARY,"Dictionary"},
+ {Variant::ARRAY,"Array"},
+ {Variant::RAW_ARRAY,"RawArray"},
+ {Variant::INT_ARRAY,"IntArray"},
+ {Variant::REAL_ARRAY,"FloatArray"},
+ {Variant::STRING_ARRAY,"StringArray"},
+ {Variant::VECTOR2_ARRAY,"Vector2Array"},
+ {Variant::VECTOR3_ARRAY,"Vector3Array"},
+ {Variant::COLOR_ARRAY,"ColorArray"},
+ {Variant::VARIANT_MAX,NULL},
+ };
+
+ {
+
+
+ int idx=0;
+
+ while(type_list[idx].text) {
+
+ if (str==type_list[idx].text) {
+ _make_type(type_list[idx].type);
+ found=true;
+ break;
+ }
+ idx++;
+ }
+ }
+
+ if (!found) {
+
+ //built in func?
+
+ for(int i=0;i<GDFunctions::FUNC_MAX;i++) {
+
+ if (str==GDFunctions::get_func_name(GDFunctions::Function(i))) {
+
+ _make_built_in_func(GDFunctions::Function(i));
+ found=true;
+ break;
+ }
+ }
+
+ //keywor
+ }
+
+ if (!found) {
+
+
+ struct _kws { Token token; const char *text;};
+
+ static const _kws keyword_list[]={
+ //ops
+ {TK_OP_IN,"in"},
+ {TK_OP_NOT,"not"},
+ {TK_OP_OR,"or"},
+ {TK_OP_AND,"and"},
+ //func
+ {TK_PR_FUNCTION,"func"},
+ {TK_PR_FUNCTION,"function"},
+ {TK_PR_CLASS,"class"},
+ {TK_PR_EXTENDS,"extends"},
+ {TK_PR_TOOL,"tool"},
+ {TK_PR_STATIC,"static"},
+ {TK_PR_EXPORT,"export"},
+ {TK_PR_VAR,"var"},
+ {TK_PR_PRELOAD,"preload"},
+ {TK_PR_ASSERT,"assert"},
+ {TK_PR_CONST,"const"},
+ //controlflow
+ {TK_CF_IF,"if"},
+ {TK_CF_ELIF,"elif"},
+ {TK_CF_ELSE,"else"},
+ {TK_CF_FOR,"for"},
+ {TK_CF_WHILE,"while"},
+ {TK_CF_DO,"do"},
+ {TK_CF_SWITCH,"switch"},
+ {TK_CF_BREAK,"break"},
+ {TK_CF_CONTINUE,"continue"},
+ {TK_CF_RETURN,"return"},
+ {TK_CF_PASS,"pass"},
+ {TK_SELF,"self"},
+ {TK_ERROR,NULL}
+ };
+
+ int idx=0;
+ found=false;
+
+ while(keyword_list[idx].text) {
+
+ if (str==keyword_list[idx].text) {
+ _make_token(keyword_list[idx].token);
+ found=true;
+ break;
+ }
+ idx++;
+ }
+ }
+
+ if (!found)
+ identifier=true;
+ }
+
+
+ if (identifier) {
+ _make_identifier(str);
+ }
+ INCPOS(str.length());
+ return;
+ }
+
+ _make_error("Unknown character");
+ return;
+
+ } break;
+ }
+
+ INCPOS(1);
+ break;
+ }
+
+}
+
+void GDTokenizer::set_code(const String& p_code) {
+
+ code=p_code;
+ len = p_code.length();
+ if (len) {
+ _code=&code[0];
+ } else {
+ _code=NULL;
+ }
+ code_pos=0;
+ line=1; //it is stand-ar-ized that lines begin in 1 in code..
+ column=0;
+ tk_rb_pos=0;
+ error_flag=false;
+ last_error="";
+ for(int i=0;i<MAX_LOOKAHEAD+1;i++)
+ _advance();
+}
+
+GDTokenizer::Token GDTokenizer::get_token(int p_offset) const {
+ ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, TK_ERROR);
+ ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, TK_ERROR);
+
+ int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
+ return tk_rb[ofs].type;
+}
+
+int GDTokenizer::get_token_line(int p_offset) const {
+ ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, -1);
+ ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, -1);
+
+ int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
+ return tk_rb[ofs].line;
+}
+
+int GDTokenizer::get_token_column(int p_offset) const {
+ ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, -1);
+ ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, -1);
+
+ int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
+ return tk_rb[ofs].col;
+}
+
+const Variant& GDTokenizer::get_token_constant(int p_offset) const {
+ ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, tk_rb[0].constant);
+ ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, tk_rb[0].constant);
+
+ int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
+ ERR_FAIL_COND_V(tk_rb[ofs].type!=TK_CONSTANT,tk_rb[0].constant);
+ return tk_rb[ofs].constant;
+}
+StringName GDTokenizer::get_token_identifier(int p_offset) const {
+
+ ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, StringName());
+ ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, StringName());
+
+ int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
+ ERR_FAIL_COND_V(tk_rb[ofs].type!=TK_IDENTIFIER,StringName());
+ return tk_rb[ofs].identifier;
+
+}
+
+GDFunctions::Function GDTokenizer::get_token_built_in_func(int p_offset) const {
+
+ ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, GDFunctions::FUNC_MAX);
+ ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, GDFunctions::FUNC_MAX);
+
+ int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
+ ERR_FAIL_COND_V(tk_rb[ofs].type!=TK_BUILT_IN_FUNC,GDFunctions::FUNC_MAX);
+ return tk_rb[ofs].func;
+
+}
+
+Variant::Type GDTokenizer::get_token_type(int p_offset) const {
+
+ ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, Variant::NIL);
+ ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, Variant::NIL);
+
+ int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
+ ERR_FAIL_COND_V(tk_rb[ofs].type!=TK_BUILT_IN_TYPE,Variant::NIL);
+ return tk_rb[ofs].vtype;
+
+}
+
+
+int GDTokenizer::get_token_line_indent(int p_offset) const {
+
+ ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, 0);
+ ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, 0);
+
+ int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
+ ERR_FAIL_COND_V(tk_rb[ofs].type!=TK_NEWLINE,0);
+ return tk_rb[ofs].constant;
+
+}
+
+String GDTokenizer::get_token_error(int p_offset) const {
+
+ ERR_FAIL_COND_V( p_offset <= -MAX_LOOKAHEAD, String());
+ ERR_FAIL_COND_V( p_offset >= MAX_LOOKAHEAD, String());
+
+ int ofs = (TK_RB_SIZE + tk_rb_pos + p_offset - MAX_LOOKAHEAD -1)%TK_RB_SIZE;
+ ERR_FAIL_COND_V(tk_rb[ofs].type!=TK_ERROR,String());
+ return tk_rb[ofs].constant;
+}
+
+void GDTokenizer::advance(int p_amount) {
+
+ ERR_FAIL_COND( p_amount <=0 );
+ for(int i=0;i<p_amount;i++)
+ _advance();
+}
diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h
new file mode 100644
index 0000000000..24ee2be7ad
--- /dev/null
+++ b/modules/gdscript/gd_tokenizer.h
@@ -0,0 +1,181 @@
+/*************************************************************************/
+/* gd_tokenizer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef GD_TOKENIZER_H
+#define GD_TOKENIZER_H
+
+#include "ustring.h"
+#include "variant.h"
+#include "string_db.h"
+#include "gd_functions.h"
+class GDTokenizer {
+public:
+
+ enum Token {
+
+ TK_EMPTY,
+ TK_IDENTIFIER,
+ TK_CONSTANT,
+ TK_SELF,
+ TK_BUILT_IN_TYPE,
+ TK_BUILT_IN_FUNC,
+ TK_OP_IN,
+ TK_OP_EQUAL,
+ TK_OP_NOT_EQUAL,
+ TK_OP_LESS,
+ TK_OP_LESS_EQUAL,
+ TK_OP_GREATER,
+ TK_OP_GREATER_EQUAL,
+ TK_OP_AND,
+ TK_OP_OR,
+ TK_OP_NOT,
+ TK_OP_ADD,
+ TK_OP_SUB,
+ TK_OP_MUL,
+ TK_OP_DIV,
+ TK_OP_MOD,
+ TK_OP_SHIFT_LEFT,
+ TK_OP_SHIFT_RIGHT,
+ TK_OP_ASSIGN,
+ TK_OP_ASSIGN_ADD,
+ TK_OP_ASSIGN_SUB,
+ TK_OP_ASSIGN_MUL,
+ TK_OP_ASSIGN_DIV,
+ TK_OP_ASSIGN_MOD,
+ TK_OP_ASSIGN_SHIFT_LEFT,
+ TK_OP_ASSIGN_SHIFT_RIGHT,
+ TK_OP_ASSIGN_BIT_AND,
+ TK_OP_ASSIGN_BIT_OR,
+ TK_OP_ASSIGN_BIT_XOR,
+ TK_OP_BIT_AND,
+ TK_OP_BIT_OR,
+ TK_OP_BIT_XOR,
+ TK_OP_BIT_INVERT,
+ //TK_OP_PLUS_PLUS,
+ //TK_OP_MINUS_MINUS,
+ TK_CF_IF,
+ TK_CF_ELIF,
+ TK_CF_ELSE,
+ TK_CF_FOR,
+ TK_CF_DO,
+ TK_CF_WHILE,
+ TK_CF_SWITCH,
+ TK_CF_CASE,
+ TK_CF_BREAK,
+ TK_CF_CONTINUE,
+ TK_CF_PASS,
+ TK_CF_RETURN,
+ TK_PR_FUNCTION,
+ TK_PR_CLASS,
+ TK_PR_EXTENDS,
+ TK_PR_TOOL,
+ TK_PR_STATIC,
+ TK_PR_EXPORT,
+ TK_PR_CONST,
+ TK_PR_VAR,
+ TK_PR_PRELOAD,
+ TK_PR_ASSERT,
+ TK_BRACKET_OPEN,
+ TK_BRACKET_CLOSE,
+ TK_CURLY_BRACKET_OPEN,
+ TK_CURLY_BRACKET_CLOSE,
+ TK_PARENTHESIS_OPEN,
+ TK_PARENTHESIS_CLOSE,
+ TK_COMMA,
+ TK_SEMICOLON,
+ TK_PERIOD,
+ TK_QUESTION_MARK,
+ TK_COLON,
+ TK_NEWLINE,
+ TK_ERROR,
+ TK_EOF,
+ TK_MAX
+ };
+
+
+
+private:
+
+ static const char* token_names[TK_MAX];
+ enum {
+ MAX_LOOKAHEAD=4,
+ TK_RB_SIZE=MAX_LOOKAHEAD*2+1
+
+ };
+
+ struct TokenData {
+ Token type;
+ StringName identifier; //for identifier types
+ Variant constant; //for constant types
+ union {
+ Variant::Type vtype; //for type types
+ GDFunctions::Function func; //function for built in functions
+ };
+ int line,col;
+ TokenData() { type = TK_EMPTY; line=col=0; vtype=Variant::NIL; }
+ };
+
+ void _make_token(Token p_type);
+ void _make_newline(int p_spaces=0);
+ void _make_identifier(const StringName& p_identifier);
+ void _make_built_in_func(GDFunctions::Function p_func);
+ void _make_constant(const Variant& p_constant);
+ void _make_type(const Variant::Type& p_type);
+ void _make_error(const String& p_error);
+
+ String code;
+ int len;
+ int code_pos;
+ const CharType *_code;
+ int line;
+ int column;
+ TokenData tk_rb[TK_RB_SIZE*2+1];
+ int tk_rb_pos;
+ String last_error;
+ bool error_flag;
+
+ void _advance();
+public:
+
+ static const char *get_token_name(Token p_token);
+
+ void set_code(const String& p_code);
+ Token get_token(int p_offset=0) const;
+ const Variant& get_token_constant(int p_offset=0) const;
+ StringName get_token_identifier(int p_offset=0) const;
+ GDFunctions::Function get_token_built_in_func(int p_offset=0) const;
+ Variant::Type get_token_type(int p_offset=0) const;
+ int get_token_line(int p_offset=0) const;
+ int get_token_column(int p_offset=0) const;
+ int get_token_line_indent(int p_offset=0) const;
+
+ String get_token_error(int p_offset=0) const;
+ void advance(int p_amount=1);
+};
+
+#endif // TOKENIZER_H
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
new file mode 100644
index 0000000000..d2d7bf426a
--- /dev/null
+++ b/modules/gdscript/register_types.cpp
@@ -0,0 +1,46 @@
+/*************************************************/
+/* register_script_types.cpp */
+/*************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/*************************************************/
+/* Source code within this file is: */
+/* (c) 2007-2010 Juan Linietsky, Ariel Manzur */
+/* All Rights Reserved. */
+/*************************************************/
+
+#include "register_types.h"
+
+#include "gd_script.h"
+#include "io/resource_loader.h"
+
+GDScriptLanguage *script_language_gd=NULL;
+ResourceFormatLoaderGDScript *resource_loader_gd=NULL;
+ResourceFormatSaverGDScript *resource_saver_gd=NULL;
+
+void register_gdscript_types() {
+
+
+ script_language_gd=memnew( GDScriptLanguage );
+ script_language_gd->init();
+ ScriptServer::register_language(script_language_gd);
+ ObjectTypeDB::register_type<GDScript>();
+ resource_loader_gd=memnew( ResourceFormatLoaderGDScript );
+ ResourceLoader::add_resource_format_loader(resource_loader_gd);
+ resource_saver_gd=memnew( ResourceFormatSaverGDScript );
+ ResourceSaver::add_resource_format_saver(resource_saver_gd);
+
+}
+void unregister_gdscript_types() {
+
+
+
+
+ if (script_language_gd)
+ memdelete( script_language_gd );
+ if (resource_loader_gd)
+ memdelete( resource_loader_gd );
+ if (resource_saver_gd)
+ memdelete( resource_saver_gd );
+
+} \ No newline at end of file
diff --git a/modules/gdscript/register_types.h b/modules/gdscript/register_types.h
new file mode 100644
index 0000000000..ff7c2734df
--- /dev/null
+++ b/modules/gdscript/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_gdscript_types();
+void unregister_gdscript_types();
diff --git a/modules/multiscript/SCsub b/modules/multiscript/SCsub
new file mode 100644
index 0000000000..d20da72b72
--- /dev/null
+++ b/modules/multiscript/SCsub
@@ -0,0 +1,7 @@
+Import('env')
+
+env.add_source_files(env.modules_sources,"*.cpp")
+
+Export('env')
+
+
diff --git a/modules/multiscript/config.py b/modules/multiscript/config.py
new file mode 100644
index 0000000000..f9bd7da08d
--- /dev/null
+++ b/modules/multiscript/config.py
@@ -0,0 +1,11 @@
+
+
+def can_build(platform):
+ return True
+
+
+def configure(env):
+ pass
+
+
+
diff --git a/modules/multiscript/multi_script.cpp b/modules/multiscript/multi_script.cpp
new file mode 100644
index 0000000000..1924cf2a6e
--- /dev/null
+++ b/modules/multiscript/multi_script.cpp
@@ -0,0 +1,498 @@
+/*************************************************************************/
+/* multi_script.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "multi_script.h"
+
+bool MultiScriptInstance::set(const StringName& p_name, const Variant& p_value) {
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for(int i=0;i<sc;i++) {
+
+ if (!sarr[i])
+ continue;
+
+ bool found = sarr[i]->set(p_name,p_value);
+ if (found)
+ return true;
+ }
+
+ if (String(p_name).begins_with("script_")) {
+ bool valid;
+ owner->set(p_name,p_value,&valid);
+ return valid;
+ }
+ return false;
+
+}
+
+bool MultiScriptInstance::get(const StringName& p_name, Variant &r_ret) const{
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for(int i=0;i<sc;i++) {
+
+ if (!sarr[i])
+ continue;
+
+ bool found = sarr[i]->get(p_name,r_ret);
+ if (found)
+ return true;
+ }
+ if (String(p_name).begins_with("script_")) {
+ bool valid;
+ r_ret=owner->get(p_name,&valid);
+ return valid;
+ }
+ return false;
+
+}
+void MultiScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const{
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+
+ Set<String> existing;
+
+ for(int i=0;i<sc;i++) {
+
+ if (!sarr[i])
+ continue;
+
+ List<PropertyInfo> pl;
+ sarr[i]->get_property_list(&pl);
+
+ for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
+
+ if (existing.has(E->get().name))
+ continue;
+
+ p_properties->push_back(E->get());
+ existing.insert(E->get().name);
+ }
+ }
+
+ p_properties->push_back( PropertyInfo(Variant::NIL,"Scripts",PROPERTY_HINT_NONE,String(),PROPERTY_USAGE_CATEGORY) );
+
+ for(int i=0;i<owner->scripts.size();i++) {
+
+ p_properties->push_back( PropertyInfo(Variant::OBJECT,"script_"+String::chr('a'+i),PROPERTY_HINT_RESOURCE_TYPE,"Script",PROPERTY_USAGE_EDITOR) );
+
+ }
+
+ if (owner->scripts.size()<25) {
+
+ p_properties->push_back( PropertyInfo(Variant::OBJECT,"script_"+String::chr('a'+(owner->scripts.size())),PROPERTY_HINT_RESOURCE_TYPE,"Script",PROPERTY_USAGE_EDITOR) );
+ }
+
+}
+
+void MultiScriptInstance::get_method_list(List<MethodInfo> *p_list) const{
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+
+ Set<StringName> existing;
+
+ for(int i=0;i<sc;i++) {
+
+ if (!sarr[i])
+ continue;
+
+ List<MethodInfo> ml;
+ sarr[i]->get_method_list(&ml);
+
+ for(List<MethodInfo>::Element *E=ml.front();E;E=E->next()) {
+
+ if (existing.has(E->get().name))
+ continue;
+
+ p_list->push_back(E->get());
+ existing.insert(E->get().name);
+ }
+ }
+
+}
+bool MultiScriptInstance::has_method(const StringName& p_method) const{
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for(int i=0;i<sc;i++) {
+
+ if (!sarr[i])
+ continue;
+
+ if (sarr[i]->has_method(p_method))
+ return true;
+ }
+
+ return false;
+
+}
+
+Variant MultiScriptInstance::call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error) {
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for(int i=0;i<sc;i++) {
+
+ if (!sarr[i])
+ continue;
+
+ Variant r = sarr[i]->call(p_method,p_args,p_argcount,r_error);
+ if (r_error.error==Variant::CallError::CALL_OK)
+ return r;
+ else if (r_error.error!=Variant::CallError::CALL_ERROR_INVALID_METHOD)
+ return r;
+ }
+
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+
+}
+
+void MultiScriptInstance::call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount){
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for(int i=0;i<sc;i++) {
+
+ if (!sarr[i])
+ continue;
+
+ sarr[i]->call_multilevel(p_method,p_args,p_argcount);
+ }
+
+
+}
+void MultiScriptInstance::notification(int p_notification){
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for(int i=0;i<sc;i++) {
+
+ if (!sarr[i])
+ continue;
+
+ sarr[i]->notification(p_notification);
+ }
+
+}
+
+
+Ref<Script> MultiScriptInstance::get_script() const {
+
+ return owner;
+}
+
+ScriptLanguage *MultiScriptInstance::get_language() {
+
+ return MultiScriptLanguage::get_singleton();
+}
+
+MultiScriptInstance::~MultiScriptInstance() {
+
+ owner->remove_instance(object);
+}
+
+
+///////////////////
+
+
+bool MultiScript::is_tool() const {
+
+ for(int i=0;i<scripts.size();i++) {
+
+ if (scripts[i]->is_tool())
+ return true;
+ }
+
+ return false;
+}
+
+bool MultiScript::_set(const StringName& p_name, const Variant& p_value) {
+
+ _THREAD_SAFE_METHOD_
+
+ String s = String(p_name);
+ if (s.begins_with("script_")) {
+
+ int idx = s[7];
+ if (idx==0)
+ return false;
+ idx-='a';
+
+ ERR_FAIL_COND_V(idx<0,false);
+
+ Ref<Script> s = p_value;
+
+ if (idx<scripts.size()) {
+
+
+ if (s.is_null())
+ remove_script(idx);
+ else
+ set_script(idx,s);
+ } else if (idx==scripts.size()) {
+ if (s.is_null())
+ return false;
+ add_script(s);
+ } else
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool MultiScript::_get(const StringName& p_name,Variant &r_ret) const{
+
+ _THREAD_SAFE_METHOD_
+
+ String s = String(p_name);
+ if (s.begins_with("script_")) {
+
+ int idx = s[7];
+ if (idx==0)
+ return false;
+ idx-='a';
+
+ ERR_FAIL_COND_V(idx<0,false);
+
+ if (idx<scripts.size()) {
+
+ r_ret=get_script(idx);
+ return true;
+ } else if (idx==scripts.size()) {
+ r_ret=Ref<Script>();
+ return true;
+ }
+ }
+
+ return false;
+}
+void MultiScript::_get_property_list( List<PropertyInfo> *p_list) const{
+
+ _THREAD_SAFE_METHOD_
+
+ for(int i=0;i<scripts.size();i++) {
+
+ p_list->push_back( PropertyInfo(Variant::OBJECT,"script_"+String::chr('a'+i),PROPERTY_HINT_RESOURCE_TYPE,"Script") );
+
+ }
+
+ if (scripts.size()<25) {
+
+ p_list->push_back( PropertyInfo(Variant::OBJECT,"script_"+String::chr('a'+(scripts.size())),PROPERTY_HINT_RESOURCE_TYPE,"Script") );
+ }
+}
+
+void MultiScript::set_script(int p_idx,const Ref<Script>& p_script ) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX(p_idx,scripts.size());
+ ERR_FAIL_COND( p_script.is_null() );
+
+ scripts[p_idx]=p_script;
+ Ref<Script> s=p_script;
+
+ for (Map<Object*,MultiScriptInstance*>::Element *E=instances.front();E;E=E->next()) {
+
+
+ MultiScriptInstance*msi=E->get();
+ ScriptInstance *si = msi->instances[p_idx];
+ if (si) {
+ msi->instances[p_idx]=NULL;
+ memdelete(si);
+ }
+
+ if (p_script->can_instance())
+ msi->instances[p_idx]=s->instance_create(msi->object);
+
+ }
+
+
+}
+
+
+Ref<Script> MultiScript::get_script(int p_idx) const{
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX_V(p_idx,scripts.size(),Ref<Script>());
+
+ return scripts[p_idx];
+
+}
+void MultiScript::add_script(const Ref<Script>& p_script){
+
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND( p_script.is_null() );
+ scripts.push_back(p_script);
+ Ref<Script> s=p_script;
+
+ for (Map<Object*,MultiScriptInstance*>::Element *E=instances.front();E;E=E->next()) {
+
+
+ MultiScriptInstance*msi=E->get();
+
+ if (p_script->can_instance())
+ msi->instances.push_back( s->instance_create(msi->object) );
+ else
+ msi->instances.push_back(NULL);
+
+ msi->object->_change_notify();
+
+ }
+
+
+ _change_notify();
+}
+
+
+void MultiScript::remove_script(int p_idx) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX(p_idx,scripts.size());
+
+ scripts.remove(p_idx);
+
+ for (Map<Object*,MultiScriptInstance*>::Element *E=instances.front();E;E=E->next()) {
+
+
+ MultiScriptInstance*msi=E->get();
+ ScriptInstance *si = msi->instances[p_idx];
+ msi->instances.remove(p_idx);
+ if (si) {
+ memdelete(si);
+ }
+
+ msi->object->_change_notify();
+ }
+
+
+}
+
+
+void MultiScript::remove_instance(Object *p_object) {
+
+ _THREAD_SAFE_METHOD_
+ instances.erase(p_object);
+}
+
+bool MultiScript::can_instance() const {
+
+ return true;
+}
+
+StringName MultiScript::get_instance_base_type() const {
+
+ return StringName();
+}
+ScriptInstance* MultiScript::instance_create(Object *p_this) {
+
+ _THREAD_SAFE_METHOD_
+ MultiScriptInstance *msi = memnew( MultiScriptInstance );
+ msi->object=p_this;
+ msi->owner=this;
+ for(int i=0;i<scripts.size();i++) {
+
+ ScriptInstance *si;
+
+ if (scripts[i]->can_instance())
+ si = scripts[i]->instance_create(p_this);
+ else
+ si=NULL;
+
+ msi->instances.push_back(si);
+ }
+
+ instances[p_this]=msi;
+ p_this->_change_notify();
+ return msi;
+}
+bool MultiScript::instance_has(const Object *p_this) const {
+
+ _THREAD_SAFE_METHOD_
+ return instances.has((Object*)p_this);
+}
+
+bool MultiScript::has_source_code() const {
+
+ return false;
+}
+String MultiScript::get_source_code() const {
+
+ return "";
+}
+void MultiScript::set_source_code(const String& p_code) {
+
+
+}
+Error MultiScript::reload() {
+
+ for(int i=0;i<scripts.size();i++)
+ scripts[i]->reload();
+
+ return OK;
+}
+
+String MultiScript::get_node_type() const {
+
+ return "";
+}
+
+void MultiScript::_bind_methods() {
+
+
+}
+
+ScriptLanguage *MultiScript::get_language() const {
+
+ return MultiScriptLanguage::get_singleton();
+}
+
+
+///////////////
+
+MultiScript::MultiScript() {
+}
+
+
+MultiScriptLanguage *MultiScriptLanguage::singleton=NULL;
diff --git a/modules/multiscript/multi_script.h b/modules/multiscript/multi_script.h
new file mode 100644
index 0000000000..87d4b4e4c8
--- /dev/null
+++ b/modules/multiscript/multi_script.h
@@ -0,0 +1,158 @@
+/*************************************************************************/
+/* multi_script.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef MULTI_SCRIPT_H
+#define MULTI_SCRIPT_H
+
+#include "script_language.h"
+#include "os/thread_safe.h"
+
+class MultiScript;
+
+class MultiScriptInstance : public ScriptInstance {
+friend class MultiScript;
+ mutable Vector<ScriptInstance*> instances;
+ Object *object;
+ mutable MultiScript *owner;
+
+public:
+ virtual bool set(const StringName& p_name, const Variant& p_value);
+ virtual bool get(const StringName& p_name, Variant &r_ret) const;
+ virtual void get_property_list(List<PropertyInfo> *p_properties) const;
+
+ virtual void get_method_list(List<MethodInfo> *p_list) const;
+ virtual bool has_method(const StringName& p_method) const;
+ virtual Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error);
+ virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount);
+ virtual void notification(int p_notification);
+
+
+ virtual Ref<Script> get_script() const;
+
+ virtual ScriptLanguage *get_language();
+ virtual ~MultiScriptInstance();
+};
+
+
+class MultiScript : public Script {
+
+ _THREAD_SAFE_CLASS_
+friend class MultiScriptInstance;
+ OBJ_TYPE( MultiScript,Script);
+
+ Vector<Ref<Script> > scripts;
+
+ Map<Object*,MultiScriptInstance*> instances;
+protected:
+
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
+ static void _bind_methods();
+
+public:
+
+ void remove_instance(Object *p_object);
+ virtual bool can_instance() const;
+
+ virtual StringName get_instance_base_type() const;
+ virtual ScriptInstance* instance_create(Object *p_this);
+ virtual bool instance_has(const Object *p_this) const;
+
+ virtual bool has_source_code() const;
+ virtual String get_source_code() const;
+ virtual void set_source_code(const String& p_code);
+ virtual Error reload();
+
+ virtual bool is_tool() const;
+
+ virtual String get_node_type() const;
+
+
+ void set_script(int p_idx,const Ref<Script>& p_script );
+ Ref<Script> get_script(int p_idx) const;
+ void remove_script(int p_idx);
+ void add_script(const Ref<Script>& p_script);
+
+ virtual ScriptLanguage *get_language() const;
+
+ MultiScript();
+};
+
+
+class MultiScriptLanguage : public ScriptLanguage {
+
+ static MultiScriptLanguage *singleton;
+public:
+
+ static _FORCE_INLINE_ MultiScriptLanguage *get_singleton() { return singleton; }
+ virtual String get_name() const { return "MultiScript"; }
+
+ /* LANGUAGE FUNCTIONS */
+ virtual void init() {}
+ virtual String get_type() const { return "MultiScript"; }
+ virtual String get_extension() const { return ""; }
+ virtual Error execute_file(const String& p_path) { return OK; }
+ virtual void finish() {}
+
+ /* EDITOR FUNCTIONS */
+ virtual void get_reserved_words(List<String> *p_words) const {}
+ virtual void get_comment_delimiters(List<String> *p_delimiters) const {}
+ virtual void get_string_delimiters(List<String> *p_delimiters) const {}
+ virtual String get_template(const String& p_class_name, const String& p_base_class_name) const { return ""; }
+ virtual bool validate(const String& p_script, int &r_line_error,int &r_col_error,String& r_test_error,const String& p_path="",List<String>* r_fn=NULL) const { return true; }
+ virtual Script *create_script() const { return memnew( MultiScript ); }
+ virtual bool has_named_classes() const { return false; }
+ virtual int find_function(const String& p_function,const String& p_code) const { return -1; }
+ virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const { return ""; }
+
+ /* DEBUGGER FUNCTIONS */
+
+ virtual String debug_get_error() const { return ""; }
+ virtual int debug_get_stack_level_count() const { return 0; }
+ virtual int debug_get_stack_level_line(int p_level) const { return 0; }
+ virtual String debug_get_stack_level_function(int p_level) const { return ""; }
+ virtual String debug_get_stack_level_source(int p_level) const { return ""; }
+ virtual void debug_get_stack_level_locals(int p_level,List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1) {}
+ virtual void debug_get_stack_level_members(int p_level,List<String> *p_members, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1) {}
+ virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems=-1,int p_max_depth=-1) {}
+ virtual String debug_parse_stack_level_expression(int p_level,const String& p_expression,int p_max_subitems=-1,int p_max_depth=-1) { return ""; }
+
+ /* LOADER FUNCTIONS */
+
+ virtual void get_recognized_extensions(List<String> *p_extensions) const {}
+ virtual void get_public_functions(List<MethodInfo> *p_functions) const {}
+ virtual void get_public_constants(List<Pair<String,Variant> > *p_constants) const {}
+
+ MultiScriptLanguage() { singleton=this; }
+ virtual ~MultiScriptLanguage() {};
+};
+
+
+#endif // MULTI_SCRIPT_H
diff --git a/modules/multiscript/register_types.cpp b/modules/multiscript/register_types.cpp
new file mode 100644
index 0000000000..4b25656723
--- /dev/null
+++ b/modules/multiscript/register_types.cpp
@@ -0,0 +1,32 @@
+/*************************************************/
+/* register_script_types.cpp */
+/*************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/*************************************************/
+/* Source code within this file is: */
+/* (c) 2007-2010 Juan Linietsky, Ariel Manzur */
+/* All Rights Reserved. */
+/*************************************************/
+
+#include "register_types.h"
+
+#include "multi_script.h"
+#include "io/resource_loader.h"
+
+static MultiScriptLanguage *script_multi_script=NULL;
+
+void register_multiscript_types() {
+
+
+ script_multi_script = memnew( MultiScriptLanguage );
+ ScriptServer::register_language(script_multi_script);
+ ObjectTypeDB::register_type<MultiScript>();
+
+
+}
+void unregister_multiscript_types() {
+
+ if (script_multi_script);
+ memdelete(script_multi_script);
+}
diff --git a/modules/multiscript/register_types.h b/modules/multiscript/register_types.h
new file mode 100644
index 0000000000..d137b1c63f
--- /dev/null
+++ b/modules/multiscript/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_multiscript_types();
+void unregister_multiscript_types();