From 4b07eb8deb03ce8c7870d621cd03d04d45f4caaa Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Mon, 24 Feb 2014 09:53:33 -0300 Subject: -moved script to modules --- SConstruct | 10 - bin/tests/test_gdscript.cpp | 8 +- main/main.cpp | 3 - modules/gdscript/SCsub | 7 + modules/gdscript/config.py | 11 + modules/gdscript/gd_compiler.cpp | 1531 ++++++++++++ modules/gdscript/gd_compiler.h | 181 ++ modules/gdscript/gd_editor.cpp | 789 +++++++ modules/gdscript/gd_functions.cpp | 1218 ++++++++++ modules/gdscript/gd_functions.h | 103 + modules/gdscript/gd_parser.cpp | 2469 ++++++++++++++++++++ modules/gdscript/gd_parser.h | 397 ++++ modules/gdscript/gd_pretty_print.cpp | 34 + modules/gdscript/gd_pretty_print.h | 40 + modules/gdscript/gd_script.cpp | 2222 ++++++++++++++++++ modules/gdscript/gd_script.h | 473 ++++ modules/gdscript/gd_tokenizer.cpp | 973 ++++++++ modules/gdscript/gd_tokenizer.h | 181 ++ modules/gdscript/register_types.cpp | 46 + modules/gdscript/register_types.h | 30 + modules/multiscript/SCsub | 7 + modules/multiscript/config.py | 11 + modules/multiscript/multi_script.cpp | 498 ++++ modules/multiscript/multi_script.h | 158 ++ modules/multiscript/register_types.cpp | 32 + modules/multiscript/register_types.h | 30 + plugins/terrain/plugin.cfg | 16 - plugins/terrain/terrain.gd | 17 - plugins/terrain/terrain.png | Bin 762 -> 0 bytes plugins/terrain/terrain_node.gd | 3 - plugins/time/plugin.cfg | 14 - plugins/time/time.gd | 32 - script/SCsub | 16 - script/gdscript/SCsub | 7 - script/gdscript/gd_compiler.cpp | 1531 ------------ script/gdscript/gd_compiler.h | 181 -- script/gdscript/gd_editor.cpp | 789 ------- script/gdscript/gd_functions.cpp | 1218 ---------- script/gdscript/gd_functions.h | 103 - script/gdscript/gd_parser.cpp | 2469 -------------------- script/gdscript/gd_parser.h | 397 ---- script/gdscript/gd_pretty_print.cpp | 34 - script/gdscript/gd_pretty_print.h | 40 - script/gdscript/gd_script.cpp | 2222 ------------------ script/gdscript/gd_script.h | 473 ---- script/gdscript/gd_tokenizer.cpp | 973 -------- script/gdscript/gd_tokenizer.h | 181 -- script/multiscript/SCsub | 7 - script/multiscript/multi_script.cpp | 498 ---- script/multiscript/multi_script.h | 158 -- script/register_script_types.cpp | 66 - script/register_script_types.h | 38 - script/script_binder.cpp | 13 - script/script_binder.h | 15 - tools/docdump/makemd.py | 345 +++ tools/editor/editor_import_export.cpp | 94 +- tools/editor/editor_import_export.h | 20 + tools/editor/editor_node.cpp | 2 + .../io_plugins/editor_texture_import_plugin.cpp | 53 + .../io_plugins/editor_texture_import_plugin.h | 10 + tools/script_plugins/terrain/plugin.cfg | 16 + tools/script_plugins/terrain/terrain.gd | 17 + tools/script_plugins/terrain/terrain.png | Bin 0 -> 762 bytes tools/script_plugins/terrain/terrain_node.gd | 3 + tools/script_plugins/time/plugin.cfg | 14 + tools/script_plugins/time/time.gd | 32 + 66 files changed, 12014 insertions(+), 11565 deletions(-) create mode 100644 modules/gdscript/SCsub create mode 100644 modules/gdscript/config.py create mode 100644 modules/gdscript/gd_compiler.cpp create mode 100644 modules/gdscript/gd_compiler.h create mode 100644 modules/gdscript/gd_editor.cpp create mode 100644 modules/gdscript/gd_functions.cpp create mode 100644 modules/gdscript/gd_functions.h create mode 100644 modules/gdscript/gd_parser.cpp create mode 100644 modules/gdscript/gd_parser.h create mode 100644 modules/gdscript/gd_pretty_print.cpp create mode 100644 modules/gdscript/gd_pretty_print.h create mode 100644 modules/gdscript/gd_script.cpp create mode 100644 modules/gdscript/gd_script.h create mode 100644 modules/gdscript/gd_tokenizer.cpp create mode 100644 modules/gdscript/gd_tokenizer.h create mode 100644 modules/gdscript/register_types.cpp create mode 100644 modules/gdscript/register_types.h create mode 100644 modules/multiscript/SCsub create mode 100644 modules/multiscript/config.py create mode 100644 modules/multiscript/multi_script.cpp create mode 100644 modules/multiscript/multi_script.h create mode 100644 modules/multiscript/register_types.cpp create mode 100644 modules/multiscript/register_types.h delete mode 100644 plugins/terrain/plugin.cfg delete mode 100644 plugins/terrain/terrain.gd delete mode 100644 plugins/terrain/terrain.png delete mode 100644 plugins/terrain/terrain_node.gd delete mode 100644 plugins/time/plugin.cfg delete mode 100644 plugins/time/time.gd delete mode 100644 script/SCsub delete mode 100644 script/gdscript/SCsub delete mode 100644 script/gdscript/gd_compiler.cpp delete mode 100644 script/gdscript/gd_compiler.h delete mode 100644 script/gdscript/gd_editor.cpp delete mode 100644 script/gdscript/gd_functions.cpp delete mode 100644 script/gdscript/gd_functions.h delete mode 100644 script/gdscript/gd_parser.cpp delete mode 100644 script/gdscript/gd_parser.h delete mode 100644 script/gdscript/gd_pretty_print.cpp delete mode 100644 script/gdscript/gd_pretty_print.h delete mode 100644 script/gdscript/gd_script.cpp delete mode 100644 script/gdscript/gd_script.h delete mode 100644 script/gdscript/gd_tokenizer.cpp delete mode 100644 script/gdscript/gd_tokenizer.h delete mode 100644 script/multiscript/SCsub delete mode 100644 script/multiscript/multi_script.cpp delete mode 100644 script/multiscript/multi_script.h delete mode 100644 script/register_script_types.cpp delete mode 100644 script/register_script_types.h delete mode 100644 script/script_binder.cpp delete mode 100644 script/script_binder.h create mode 100644 tools/docdump/makemd.py create mode 100644 tools/script_plugins/terrain/plugin.cfg create mode 100644 tools/script_plugins/terrain/terrain.gd create mode 100644 tools/script_plugins/terrain/terrain.png create mode 100644 tools/script_plugins/terrain/terrain_node.gd create mode 100644 tools/script_plugins/time/plugin.cfg create mode 100644 tools/script_plugins/time/time.gd diff --git a/SConstruct b/SConstruct index 1fb82bfc33..93e48a069f 100644 --- a/SConstruct +++ b/SConstruct @@ -210,18 +210,9 @@ for p in platform_list: if (env["builtin_zlib"]=='yes'): env.Append(CPPPATH=['#drivers/builtin_zlib/zlib']) - if (env['squirrel']=='yes'): - - env.Append(CPPFLAGS=['-DSQUIRREL_ENABLED']) - env.Append(CPPPATH=['#script/squirrel/src']) - # to test 64 bits compiltion # env.Append(CPPFLAGS=['-m64']) - if (env['lua']=='yes'): - - env.Append(CPPFLAGS=['-DLUA_ENABLED']) - env.Append(CPPPATH=['#script/lua/src']) if (env_base['squish']=='yes'): env.Append(CPPFLAGS=['-DSQUISH_ENABLED']); @@ -297,7 +288,6 @@ for p in platform_list: SConscript("servers/SCsub") SConscript("scene/SCsub") SConscript("tools/SCsub") - SConscript("script/SCsub"); SConscript("drivers/SCsub") SConscript("bin/SCsub") diff --git a/bin/tests/test_gdscript.cpp b/bin/tests/test_gdscript.cpp index 48f982425b..847be39fb4 100644 --- a/bin/tests/test_gdscript.cpp +++ b/bin/tests/test_gdscript.cpp @@ -35,10 +35,10 @@ #ifdef GDSCRIPT_ENABLED -#include "script/gdscript/gd_tokenizer.h" -#include "script/gdscript/gd_parser.h" -#include "script/gdscript/gd_compiler.h" -#include "script/gdscript/gd_script.h" +#include "modules/gdscript/gd_tokenizer.h" +#include "modules/gdscript/gd_parser.h" +#include "modules/gdscript/gd_compiler.h" +#include "modules/gdscript/gd_script.h" namespace TestGDScript { diff --git a/main/main.cpp b/main/main.cpp index 305cc88be7..8ed49a38e4 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -33,7 +33,6 @@ #include "core/register_core_types.h" #include "scene/register_scene_types.h" #include "drivers/register_driver_types.h" -#include "script/register_script_types.h" #include "servers/register_server_types.h" #include "modules/register_module_types.h" #include "script_debugger_local.h" @@ -766,7 +765,6 @@ Error Main::setup2() { MAIN_PRINT("Main: Load Scripts, Modules, Drivers"); register_module_types(); - register_script_types(); register_driver_types(); MAIN_PRINT("Main: Load Translations"); @@ -1371,7 +1369,6 @@ void Main::cleanup() { unregister_module_types(); unregister_scene_types(); unregister_server_types(); - unregister_script_types(); OS::get_singleton()->finalize(); 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<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<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<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(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<_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<constants.has(identifier)) { + + //int idx=scr->constants[identifier]; + int idx = codegen.get_name_map_pos(identifier); + return idx|(GDFunction::ADDR_TYPE_CLASS_CONSTANT<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<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<get_global_map().has(identifier)) { + + int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier]; + return idx|(GDFunction::ADDR_TYPE_GLOBAL<(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<_static) { + _set_error("'self' not present in static function!",p_expression); + return -1; + } + return (GDFunction::ADDR_TYPE_SELF<(p_expression); + Vector values; + + int slevel=p_stack_level; + + for(int i=0;ielements.size();i++) { + + int ret = _parse_expression(codegen,an->elements[i],slevel); + if (ret<0) + return ret; + if (ret&GDFunction::ADDR_TYPE_STACK<(p_expression); + Vector values; + + int slevel=p_stack_level; + + for(int i=0;ielements.size();i++) { + + int ret = _parse_expression(codegen,dn->elements[i].key,slevel); + if (ret<0) + return ret; + if (ret&GDFunction::ADDR_TYPE_STACK<elements[i].value,slevel); + if (ret<0) + return ret; + if (ret&GDFunction::ADDR_TYPE_STACK<elements.size()); + for(int i=0;i(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 arguments; + int slevel = p_stack_level; + for(int i=1;iarguments.size();i++) { + + int ret = _parse_expression(codegen,on->arguments[i],slevel); + if (ret<0) + return ret; + if (ret&GDFunction::ADDR_TYPE_STACK<name)); //instance + codegen.opcodes.push_back(arguments.size()); //argument count + codegen.alloc_call(arguments.size()); + for(int i=0;iarguments[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 arguments; + int slevel = p_stack_level; + for(int i=1;iarguments.size();i++) { + + int ret = _parse_expression(codegen,on->arguments[i],slevel); + if (ret<0) + return ret; + if (ret&GDFunction::ADDR_TYPE_STACK<arguments[0]->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) { + //built in function + + ERR_FAIL_COND_V(on->arguments.size()<1,-1); + + + Vector arguments; + int slevel = p_stack_level; + for(int i=1;iarguments.size();i++) { + + int ret = _parse_expression(codegen,on->arguments[i],slevel); + if (ret<0) + return ret; + + if (ret&GDFunction::ADDR_TYPE_STACK<(on->arguments[0])->function); + codegen.opcodes.push_back(on->arguments.size()-1); + codegen.alloc_call(on->arguments.size()-1); + for(int i=0;iarguments.size()<2,-1); + + const GDParser::Node *instance = on->arguments[0]; + + if (instance->type==GDParser::Node::TYPE_SELF) { + //room for optimization + + } + + + Vector arguments; + int slevel = p_stack_level; + + for(int i=0;iarguments.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(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<arguments.size()-2); + codegen.alloc_call(on->arguments.size()-2); + for(int i=0;iarguments.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(on->arguments[1])->name); + + } else { + + if (on->arguments[1]->type==GDParser::Node::TYPE_CONSTANT && static_cast(on->arguments[1])->value.get_type()==Variant::STRING) { + //also, somehow, named (speed up anyway) + StringName name = static_cast(on->arguments[1])->value; + index=codegen.get_name_map_pos(name); + named=true; + + } else { + //regular indexing + if (from&GDFunction::ADDR_TYPE_STACK<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<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<arguments.size()!=2,-1); + + + if (on->arguments[0]->type==GDParser::Node::TYPE_OPERATOR && (static_cast(on->arguments[0])->op==GDParser::OperatorNode::OP_INDEX || static_cast(on->arguments[0])->op==GDParser::OperatorNode::OP_INDEX_NAMED)) { + //SET (chained) MODE!! + + int slevel=p_stack_level; + + GDParser::OperatorNode* op = static_cast(on->arguments[0]); + + /* Find chain of sets */ + + List 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(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< setchain; + + for(List::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(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<(op)->op==GDParser::OperatorNode::OP_INDEX_NAMED) { + + + set_index=codegen.get_name_map_pos(static_cast(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<arguments[0],slevel); + if (dst_address_a<0) + return -1; + + if (dst_address_a&GDFunction::ADDR_TYPE_STACK<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<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<line; + + for(int i=0;istatements.size();i++) { + + const GDParser::Node *s = p_block->statements[i]; + + + switch(s->type) { + case GDParser::Node::TYPE_NEWLINE: { + + const GDParser::NewLineNode *nl = static_cast(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(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<(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(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(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 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;iarguments.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<initializer,stack_level); + if (err) + return err; + is_initializer=true; + + } + + /* Parse default argument code -if applies- */ + + Vector 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;idefault_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::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(); + p_script->base=Ref(); + 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 script; + Ref 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;iextends_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 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;iextends_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;ivariables.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;iconstant_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(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;isubclasses.size();i++) { + StringName name = p_class->subclasses[i]->name; + + Ref 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;ifunctions.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;istatic_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(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 > stack_id_stack; + Map stack_identifiers; + + List stack_debug; + List< Map > block_identifier_stack; + Map 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::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 constant_map; + Map 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 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 _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 *p_delimiters) const { + + p_delimiters->push_back("#"); + +} +void GDScriptLanguage::get_string_delimiters(List *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 *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(root); + Map funcs; + for(int i=0;ifunctions.size();i++) { + + funcs[cl->functions[i]->line]=cl->functions[i]->name; + } + + for(int i=0;istatic_functions.size();i++) { + + funcs[cl->static_functions[i]->line]=cl->static_functions[i]->name; + } + + for (Map::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 *p_locals, List *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 > locals; + + f->debug_get_stack_member_state(*_call_stack[l].line,&locals); + for( List >::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 *p_members, List *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 script = instance->get_script(); + ERR_FAIL_COND( script.is_null() ); + + + const Map& mi = script->debug_get_member_indices(); + + for(const Map::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 *p_locals, List *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 *p_extensions) const { + + p_extensions->push_back("gd"); +} + + +void GDScriptLanguage::get_public_functions(List *p_functions) const { + + + for(int i=0;ipush_back(GDFunctions::get_info(GDFunctions::Function(i))); + } +} + +void GDScriptLanguage::get_public_constants(List > *p_constants) const { + + Pair 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;i0) + 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* r_options) { + + if (!p_static) { + List methods; + ObjectTypeDB::get_method_list(p_native,&methods); + for(List::Element *E=methods.front();E;E=E->next()) { + if (!E->get().name.begins_with("_")) { + r_options->push_back(E->get().name); + } + } + } + + List constants; + ObjectTypeDB::get_integer_constant_list(p_native,&constants); + + for(List::Element *E=constants.front();E;E=E->next()) { + r_options->push_back(E->get()); + } + +} + + +static bool _parse_script_symbols(const Ref& p_script,bool p_static,List* r_options,List::Element *p_indices); + + +static bool _parse_completion_variant(const Variant& p_var,List* r_options,List::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 vl; + d.get_key_list(&vl); + for (List::Element *E=vl.front();E;E=E->next()) { + + if (E->get().get_type()==Variant::STRING) + r_options->push_back(E->get()); + } + + + List ml; + p_var.get_method_list(&ml); + for(List::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()) { + + Ref gds = p_var; + _parse_script_symbols(gds,true,r_options,NULL); + } else if (o->is_type("GDNativeClass")){ + + GDNativeClass *gnc = o->cast_to(); + _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 pi; + p_var.get_property_list(&pi); + for(List::Element *E=pi.front();E;E=E->next()) { + r_options->push_back(E->get().name); + } + List cl; + + p_var.get_numeric_constants_for_type(p_var.get_type(),&cl); + for(List::Element *E=cl.front();E;E=E->next()) { + r_options->push_back(E->get()); + } + + List ml; + p_var.get_method_list(&ml); + for(List::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* r_options,List::Element *p_indices) { + + + + if (p_node->type==GDParser::Node::TYPE_CONSTANT) { + + const GDParser::ConstantNode *cn=static_cast(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(p_node); + for(int i=0;ielements.size();i++) { + + if (dn->elements[i].key->type==GDParser::Node::TYPE_CONSTANT) { + + const GDParser::ConstantNode *cn=static_cast(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* r_options,List::Element *p_indices) { + + for(int i=0;isub_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;istatements.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(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;ivariables.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& p_script,bool p_static,List* r_options,List::Element *p_indices) { + + //for (Map >::Element ? + + if (!p_static && !p_indices) { + for(const Set::Element *E=p_script->get_members().front();E;E=E->next()) { + + r_options->push_back(E->get()); + } + } + + for (const Map::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::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* r_options,List::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;iget()==_type_names[i]) { + + List ic; + + Variant::get_numeric_constants_for_type(Variant::Type(i),&ic); + for(List::Element *E=ic.front();E;E=E->next()) { + r_options->push_back(E->get()); + } + return true; + } + } + } + + + + for(int i=0;isubclasses.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;ifunctions.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;jarguments.size();j++) { + + r_options->push_back(fu->arguments[j]); + } + } + } + + } + + for(int i=0;istatic_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;jarguments.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;ivariables.size();i++) { + + r_options->push_back(p_class->variables[i].identifier); + } + } + + for(int i=0;iconstant_expressions.size();i++) { + + r_options->push_back(p_class->constant_expressions[i].identifier); + } + + if (!in_static_func) { + for(int i=0;ifunctions.size();i++) { + + r_options->push_back(p_class->functions[i]->name); + } + } + + for(int i=0;istatic_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 script; + Ref 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;iextends_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 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;iextends_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* 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(root); + + List indices; + Vector spl = p_base.split("."); + + for(int i=0;i::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_countm_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 wref = memnew( WeakRef ); + wref->set_ref(r); + r_ret=wref; + } else { + Object *obj = *p_args[0]; + if (!obj) { + r_ret=Variant(); + return; + } + Ref 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;ioperator String();; + if (i==0) + str=os; + else + str+=os; + } + + r_ret=str; + + } break; + case TEXT_PRINT: { + + String str; + for(int i=0;ioperator String(); + } + + //str+="\n"; + print_line(str); + r_ret=Variant(); + + + } break; + case TEXT_PRINT_TABBED: { + + String str; + for(int i=0;ioperator String(); + } + + //str+="\n"; + print_line(str); + r_ret=Variant(); + + + } break; + + case TEXT_PRINTERR: { + + String str; + for(int i=0;ioperator 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;ioperator 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=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 && 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;ito;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(obj->get_script_instance()); + Ref 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 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(),false); + + Dictionary d(true); + d["@subpath"]=cp; + d["@path"]=p->path; + + + p = base.ptr(); + + while(p) { + + for(Set::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