diff options
Diffstat (limited to 'modules/gdscript')
-rw-r--r-- | modules/gdscript/SCsub | 2 | ||||
-rw-r--r-- | modules/gdscript/gd_compiler.cpp | 40 | ||||
-rw-r--r-- | modules/gdscript/gd_editor.cpp | 624 | ||||
-rw-r--r-- | modules/gdscript/gd_functions.cpp | 33 | ||||
-rw-r--r-- | modules/gdscript/gd_functions.h | 1 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.cpp | 224 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.h | 13 | ||||
-rw-r--r-- | modules/gdscript/gd_script.cpp | 107 | ||||
-rw-r--r-- | modules/gdscript/gd_script.h | 21 | ||||
-rw-r--r-- | modules/gdscript/gd_tokenizer.cpp | 42 | ||||
-rw-r--r-- | modules/gdscript/gd_tokenizer.h | 1 |
11 files changed, 961 insertions, 147 deletions
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub index d20da72b72..403fe68f66 100644 --- a/modules/gdscript/SCsub +++ b/modules/gdscript/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.modules_sources,"*.cpp") Export('env') - - diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index b405555ec6..a62225f663 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -28,15 +28,6 @@ /*************************************************************************/ #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) { @@ -1397,13 +1388,14 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars int index_from=0; + Ref<GDNativeClass> native; 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) @@ -1573,7 +1565,35 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars //p_script->constants[constant->value].make_const(); } + for(int i=0;i<p_class->_signals.size();i++) { + + StringName name = p_class->_signals[i].name; + + GDScript *c = p_script; + while(c) { + + if (c->_signals.has(name)) { + _set_error("Signal '"+name+"' redefined (in current or parent class)",p_class); + return ERR_ALREADY_EXISTS; + } + + if (c->base.is_valid()) { + c=c->base.ptr(); + } else { + c=NULL; + } + } + + if (native.is_valid()) { + if (ObjectTypeDB::has_signal(native->get_name(),name)) { + _set_error("Signal '"+name+"' redefined (original in native class '"+String(native->get_name())+"')",p_class); + return ERR_ALREADY_EXISTS; + } + } + + p_script->_signals[name]=p_class->_signals[i].arguments; + } //parse sub-classes for(int i=0;i<p_class->subclasses.size();i++) { diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index f59dbd91d3..0d986e92a2 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -29,6 +29,7 @@ #include "gd_script.h" #include "gd_compiler.h" #include "globals.h" +#include "os/file_access.h" void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const { @@ -238,26 +239,26 @@ void GDScriptLanguage::debug_get_stack_level_members(int p_level,List<String> *p 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; + ERR_FAIL_INDEX(p_level,_debug_call_stack_pos); + int l = _debug_call_stack_pos - p_level -1; - GDInstance *instance = _call_stack[l].instance; + GDInstance *instance = _call_stack[l].instance; - if (!instance) - return; + if (!instance) + return; - Ref<GDScript> script = instance->get_script(); - ERR_FAIL_COND( script.is_null() ); + Ref<GDScript> script = instance->get_script(); + ERR_FAIL_COND( script.is_null() ); - const Map<StringName,GDScript::MemberInfo>& mi = script->debug_get_member_indices(); + const Map<StringName,GDScript::MemberInfo>& mi = script->debug_get_member_indices(); - for(const Map<StringName,GDScript::MemberInfo>::Element *E=mi.front();E;E=E->next()) { + for(const Map<StringName,GDScript::MemberInfo>::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().index)); - } + p_members->push_back(E->key()); + p_values->push_back( instance->debug_get_member_by_index(E->get().index)); + } } void GDScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems,int p_max_depth) { @@ -317,6 +318,7 @@ String GDScriptLanguage::make_function(const String& p_class,const String& p_nam struct GDCompletionIdentifier { StringName obj_type; + Ref<GDScript> script; Variant::Type type; Variant value; //im case there is a value, also return it }; @@ -381,7 +383,12 @@ static Ref<Reference> _get_parent_class(GDCompletionContext& context) { path=context.base_path.plus_file(path); } - script = ResourceLoader::load(path); + + if (ScriptCodeCompletionCache::get_sigleton()) + script = ScriptCodeCompletionCache::get_sigleton()->get_cached_resource(path); + else + script = ResourceLoader::load(path); + if (script.is_null()) { return REF(); } @@ -441,7 +448,7 @@ static Ref<Reference> _get_parent_class(GDCompletionContext& context) { base_class=base_class->subclasses[subclass]; } else { - print_line("Could not find subclass: "+subclass); + //print_line("Could not find subclass: "+subclass); return _get_type_from_class(context); //fail please } } @@ -626,7 +633,9 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser:: //try calling the function if constant and all args are constant, should not crash.. Object *baseptr = base.value; - if (baseptr && mb->is_const() && pi.type==Variant::OBJECT) { + + if (mb->is_const() && pi.type==Variant::OBJECT) { + bool all_valid=true; Vector<Variant> args; for(int i=2;i<op->arguments.size();i++) { @@ -643,25 +652,88 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser:: all_valid=false; } } - if (all_valid) { - Vector<const Variant*> argptr; - for(int i=0;i<args.size();i++) { - argptr.push_back(&args[i]); - } - Variant::CallError ce; - Variant ret=mb->call(baseptr,argptr.ptr(),argptr.size(),ce); + if (all_valid && String(id)=="get_node" && ObjectTypeDB::is_type(base.obj_type,"Node") && args.size()) { + + String arg1=args[0]; + if (arg1.begins_with("/root/")) { + String which = arg1.get_slice("/",2); + if (which!="") { + List<PropertyInfo> props; + Globals::get_singleton()->get_property_list(&props); + //print_line("find singleton"); + + for(List<PropertyInfo>::Element *E=props.front();E;E=E->next()) { + + String s = E->get().name; + if (!s.begins_with("autoload/")) + continue; + //print_line("found "+s); + String name = s.get_slice("/",1); + //print_line("name: "+name+", which: "+which); + if (name==which) { + String script = Globals::get_singleton()->get(s); + if (!script.begins_with("res://")) { + script="res://"+script; + } - if (ce.error==Variant::CallError::CALL_OK && ret.get_type()!=Variant::NIL) { + if (!script.ends_with(".gd")) { + //not a script, try find the script anyway, + //may have some success + script=script.basename()+".gd"; + } - if (ret.get_type()!=Variant::OBJECT || ret.operator Object*()!=NULL) { + if (FileAccess::exists(script)) { - r_type=_get_type_from_variant(ret); - return true; + //print_line("is a script"); + + + Ref<Script> scr; + if (ScriptCodeCompletionCache::get_sigleton()) + scr = ScriptCodeCompletionCache::get_sigleton()->get_cached_resource(script); + else + scr = ResourceLoader::load(script); + + + r_type.obj_type="Node"; + r_type.type=Variant::OBJECT; + r_type.script=scr; + r_type.value=Variant(); + + return true; + + } + } + } } } + } + + + + if (baseptr) { + + if (all_valid) { + Vector<const Variant*> argptr; + for(int i=0;i<args.size();i++) { + argptr.push_back(&args[i]); + } + + Variant::CallError ce; + Variant ret=mb->call(baseptr,argptr.ptr(),argptr.size(),ce); + + if (ce.error==Variant::CallError::CALL_OK && ret.get_type()!=Variant::NIL) { + + if (ret.get_type()!=Variant::OBJECT || ret.operator Object*()!=NULL) { + + r_type=_get_type_from_variant(ret); + return true; + } + } + + } } } @@ -926,6 +998,44 @@ static bool _guess_identifier_type_in_block(GDCompletionContext& context,int p_l return false; } + +static bool _guess_identifier_from_assignment_in_function(GDCompletionContext& context,const StringName& p_identifier, const StringName& p_function,GDCompletionIdentifier &r_type) { + + const GDParser::FunctionNode* func=NULL; + for(int i=0;i<context._class->functions.size();i++) { + if (context._class->functions[i]->name==p_function) { + func=context._class->functions[i]; + break; + } + } + + if (!func) + return false; + + for(int i=0;i<func->body->statements.size();i++) { + + + + if (func->body->statements[i]->type==GDParser::BlockNode::TYPE_OPERATOR) { + const GDParser::OperatorNode *op = static_cast<const GDParser::OperatorNode *>(func->body->statements[i]); + if (op->op==GDParser::OperatorNode::OP_ASSIGN) { + + if (op->arguments.size() && op->arguments[0]->type==GDParser::Node::TYPE_IDENTIFIER) { + + const GDParser::IdentifierNode *id = static_cast<const GDParser::IdentifierNode *>(op->arguments[0]); + + if (id->name==p_identifier) { + + return _guess_expression_type(context,op->arguments[1],func->body->statements[i]->line,r_type); + } + } + } + } + } + + return false; +} + static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type) { //go to block first @@ -1017,8 +1127,22 @@ static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const r_type=_get_type_from_pinfo(context._class->variables[i]._export); return true; } else if (context._class->variables[i].expression) { - return _guess_expression_type(context,context._class->variables[i].expression,context._class->variables[i].line,r_type); + + bool rtype = _guess_expression_type(context,context._class->variables[i].expression,context._class->variables[i].line,r_type); + if (rtype && r_type.type!=Variant::NIL) + return true; + //return _guess_expression_type(context,context._class->variables[i].expression,context._class->variables[i].line,r_type); } + + //try to guess from assignment in construtor or _ready + if (_guess_identifier_from_assignment_in_function(context,p_identifier,"_ready",r_type)) + return true; + if (_guess_identifier_from_assignment_in_function(context,p_identifier,"_enter_tree",r_type)) + return true; + if (_guess_identifier_from_assignment_in_function(context,p_identifier,"_init",r_type)) + return true; + + return false; } } } @@ -1195,7 +1319,7 @@ static void _find_identifiers(GDCompletionContext& context,int p_line,bool p_onl } static const char*_type_names[Variant::VARIANT_MAX]={ - "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Trasnform", + "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Transform", "Color","Image","NodePath","RID","Object","InputEvent","Dictionary","Array","RawArray","IntArray","FloatArray","StringArray", "Vector2Array","Vector3Array","ColorArray"}; @@ -1276,6 +1400,7 @@ static void _make_function_hint(const GDParser::FunctionNode* p_func,int p_argid static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const StringName& p_method,const GDCompletionIdentifier& id, int p_argidx, Set<String>& result, String& arghint) { + //print_line("find type arguments?"); if (id.type==Variant::INPUT_EVENT && String(p_method)=="is_action" && p_argidx==0) { List<PropertyInfo> pinfo; @@ -1296,78 +1421,300 @@ static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const St MethodBind *m = ObjectTypeDB::get_method(id.obj_type,p_method); - if (!m) - return; + if (!m) { + //not in static method, see script + + //print_line("not in static: "+String(p_method)); + Ref<GDScript> on_script; + + if (id.value.get_type()) { + Object *obj=id.value; + + + if (obj) { + + + GDScript *scr = obj->cast_to<GDScript>(); + if (scr) { + while (scr) { + + for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + if (E->get().is_static() && p_method==E->get().get_name()) { + arghint="static func "+String(p_method)+"("; + for(int i=0;i<E->get().get_argument_count();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+="var "+E->get().get_argument_name(i); + int deffrom = E->get().get_argument_count()-E->get().get_default_argument_count(); + if (i>=deffrom) { + int defidx = deffrom-i; + if (defidx>=0 && defidx<E->get().get_default_argument_count()) { + arghint+="="+E->get().get_default_argument(defidx).get_construct_string(); + } + } + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + } + arghint+=")"; + return; //found + } + } + + if (scr->get_base().is_valid()) + scr=scr->get_base().ptr(); + else + scr=NULL; + } + } else { + on_script=obj->get_script(); + } + } + } + + //print_line("but it has a script?"); + if (!on_script.is_valid() && id.script.is_valid()) { + //print_line("yes"); + on_script=id.script; + } - if (p_method.operator String()=="connect") { + if (on_script.is_valid()) { + + GDScript *scr = on_script.ptr(); + if (scr) { + while (scr) { + + String code = scr->get_source_code(); + //print_line("has source code!"); + + if (code!="") { + //if there is code, parse it. This way is slower but updates in real-time + GDParser p; + //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false); + + Error err = p.parse(scr->get_source_code(),scr->get_path().get_base_dir(),true,"",false); + + if (err==OK) { + //print_line("checking the functions..."); + //only if ok, otherwise use what is cached on the script + //GDParser::ClassNode *base = p. + const GDParser::Node *root = p.get_parse_tree(); + ERR_FAIL_COND(root->type!=GDParser::Node::TYPE_CLASS); + + const GDParser::ClassNode *cl = static_cast<const GDParser::ClassNode*>(root); + + const GDParser::FunctionNode* func=NULL; + bool st=false; + + for(int i=0;i<cl->functions.size();i++) { + //print_line(String(cl->functions[i]->name)+" vs "+String(p_method)); + if (cl->functions[i]->name==p_method) { + func=cl->functions[i]; + } + } + + for(int i=0;i<cl->static_functions.size();i++) { + + //print_line(String(cl->static_functions[i]->name)+" vs "+String(p_method)); + if (cl->static_functions[i]->name==p_method) { + func=cl->static_functions[i]; + st=true; + } + + } + + if (func) { + + arghint="func "+String(p_method)+"("; + if (st) + arghint="static "+arghint; + for(int i=0;i<func->arguments.size();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+="var "+String(func->arguments[i]); + int deffrom = func->arguments.size()-func->default_values.size(); + if (i>=deffrom) { + + int defidx = deffrom-i; + + if (defidx>=0 && defidx<func->default_values.size() && func->default_values[defidx]->type==GDParser::Node::TYPE_OPERATOR) { + const GDParser::OperatorNode *op=static_cast<const GDParser::OperatorNode *>(func->default_values[defidx]); + if (op->op==GDParser::OperatorNode::OP_ASSIGN) { + const GDParser::ConstantNode *cn=static_cast<const GDParser::ConstantNode *>(op->arguments[1]); + arghint+="="+cn->value.get_construct_string(); + } + } + } + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + } + + arghint+=" )"; + return; + } + } else { + //print_line("failed parsing?"); + code=""; + } + + } + + if (code=="") { + + for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + if (p_method==E->get().get_name()) { + arghint="func "+String(p_method)+"("; + for(int i=0;i<E->get().get_argument_count();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+="var "+E->get().get_argument_name(i); + int deffrom = E->get().get_argument_count()-E->get().get_default_argument_count(); + if (i>=deffrom) { + int defidx = deffrom-i; + if (defidx>=0 && defidx<E->get().get_default_argument_count()) { + arghint+="="+E->get().get_default_argument(defidx).get_construct_string(); + } + } + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + } + arghint+=")"; + return; //found + } + } +#if 0 + //use class directly, no code was found + if (!isfunction) { + for (const Map<StringName,Variant>::Element *E=scr->get_constants().front();E;E=E->next()) { + options.insert(E->key()); + } + } + for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + options.insert(String(E->key())+"("); + } + for (const Set<StringName>::Element *E=scr->get_members().front();E;E=E->next()) { + options.insert(E->get()); + } +#endif + } - if (p_argidx==0) { - List<MethodInfo> sigs; - ObjectTypeDB::get_signal_list(id.obj_type,&sigs); - for (List<MethodInfo>::Element *E=sigs.front();E;E=E->next()) { - result.insert("\""+E->get().name+"\""); + if (scr->get_base().is_valid()) + scr=scr->get_base().ptr(); + else + scr=NULL; + } } } - /*if (p_argidx==2) { - ERR_FAIL_COND(p_node->type!=GDParser::Node::TYPE_OPERATOR); - const GDParser::OperatorNode *op=static_cast<const GDParser::OperatorNode *>(p_node); - if (op->arguments.size()>) - }*/ } else { + //regular method - Object *obj=id.value; - if (obj) { - List<String> options; - obj->get_argument_options(p_method,p_argidx,&options); - for(List<String>::Element *E=options.front();E;E=E->next()) { + if (p_method.operator String()=="connect") { - result.insert(E->get()); + + if (p_argidx==0) { + List<MethodInfo> sigs; + ObjectTypeDB::get_signal_list(id.obj_type,&sigs); + for (List<MethodInfo>::Element *E=sigs.front();E;E=E->next()) { + result.insert("\""+E->get().name+"\""); + } } - } + /*if (p_argidx==2) { - } + ERR_FAIL_COND(p_node->type!=GDParser::Node::TYPE_OPERATOR); + const GDParser::OperatorNode *op=static_cast<const GDParser::OperatorNode *>(p_node); + if (op->arguments.size()>) - arghint = _get_visual_datatype(m->get_argument_info(-1),false)+" "+p_method.operator String()+String("("); + }*/ + } else { - for(int i=0;i<m->get_argument_count();i++) { - if (i>0) - arghint+=", "; - else - arghint+=" "; + if (p_argidx==0 && (String(p_method)=="get_node" || String(p_method)=="has_node") && ObjectTypeDB::is_type(id.obj_type,"Node")) { - if (i==p_argidx) { - arghint+=String::chr(0xFFFF); - } - String n = m->get_argument_info(i).name; - int dp = n.find(":"); - if (dp!=-1) - n=n.substr(0,dp); - arghint+=_get_visual_datatype(m->get_argument_info(i))+" "+n; - int deffrom = m->get_argument_count()-m->get_default_argument_count(); + List<PropertyInfo> props; + Globals::get_singleton()->get_property_list(&props); + + for(List<PropertyInfo>::Element *E=props.front();E;E=E->next()) { + + String s = E->get().name; + if (!s.begins_with("autoload/")) + continue; + // print_line("found "+s); + String name = s.get_slice("/",1); + result.insert("\"/root/"+name+"\""); + } + } + Object *obj=id.value; + if (obj) { + List<String> options; + obj->get_argument_options(p_method,p_argidx,&options); - if (i>=deffrom) { - int defidx = i-deffrom; + for(List<String>::Element *E=options.front();E;E=E->next()) { - if (defidx>=0 && defidx<m->get_default_argument_count()) { - Variant v= m->get_default_argument(i); - arghint+="="+v.get_construct_string(); + result.insert(E->get()); + } } - } - if (i==p_argidx) { - arghint+=String::chr(0xFFFF); } - } - if (m->get_argument_count()>0) - arghint+=" "; + arghint = _get_visual_datatype(m->get_argument_info(-1),false)+" "+p_method.operator String()+String("("); + for(int i=0;i<m->get_argument_count();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; - arghint+=")"; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + String n = m->get_argument_info(i).name; + int dp = n.find(":"); + if (dp!=-1) + n=n.substr(0,dp); + arghint+=_get_visual_datatype(m->get_argument_info(i))+" "+n; + int deffrom = m->get_argument_count()-m->get_default_argument_count(); + + + if (i>=deffrom) { + int defidx = i-deffrom; + + if (defidx>=0 && defidx<m->get_default_argument_count()) { + Variant v= m->get_default_argument(i); + arghint+="="+v.get_construct_string(); + } + } + + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + + } + if (m->get_argument_count()>0) + arghint+=" "; + + + arghint+=")"; + } } } @@ -1414,7 +1761,7 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No arghint+=")"; } else if (op->arguments[0]->type==GDParser::Node::TYPE_TYPE) { - //complete built-in function + //complete constructor const GDParser::TypeNode *tn = static_cast<const GDParser::TypeNode*>(op->arguments[0]); List<MethodInfo> mil; @@ -1549,7 +1896,7 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No } } else { - + //indexed lookup GDCompletionIdentifier ci; if (_guess_expression_type(context,op->arguments[0],p_line,ci)) { @@ -1661,7 +2008,9 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base //print_line( p_code.replace(String::chr(0xFFFF),"<cursor>")); GDParser p; - Error err = p.parse(p_code,p_base_path,true); + //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false); + + Error err = p.parse(p_code,p_base_path,false,"",true); bool isfunction=false; Set<String> options; @@ -1713,20 +2062,123 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base if (t.type==Variant::OBJECT && t.obj_type!=StringName()) { + Ref<GDScript> on_script; if (t.value.get_type()) { Object *obj=t.value; + + if (obj) { + + GDScript *scr = obj->cast_to<GDScript>(); + if (scr) { + while (scr) { + + if (!isfunction) { + for (const Map<StringName,Variant>::Element *E=scr->get_constants().front();E;E=E->next()) { + options.insert(E->key()); + } + } + for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + if (E->get().is_static()) + options.insert(E->key()); + } + + if (scr->get_base().is_valid()) + scr=scr->get_base().ptr(); + else + scr=NULL; + } + } else { + on_script=obj->get_script(); + } + } + } + + + if (!on_script.is_valid() && t.script.is_valid()) { + on_script=t.script; + } + + if (on_script.is_valid()) { + + GDScript *scr = on_script.ptr(); + if (scr) { while (scr) { - if (!isfunction) { - for (const Map<StringName,Variant>::Element *E=scr->get_constants().front();E;E=E->next()) { - options.insert(E->key()); + String code = scr->get_source_code(); + + if (code!="") { + //if there is code, parse it. This way is slower but updates in real-time + GDParser p; + //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false); + + Error err = p.parse(scr->get_source_code(),scr->get_path().get_base_dir(),true,"",false); + + if (err==OK) { + //only if ok, otherwise use what is cached on the script + //GDParser::ClassNode *base = p. + const GDParser::Node *root = p.get_parse_tree(); + ERR_FAIL_COND_V(root->type!=GDParser::Node::TYPE_CLASS,ERR_PARSE_ERROR); + + const GDParser::ClassNode *cl = static_cast<const GDParser::ClassNode*>(root); + + for(int i=0;i<cl->functions.size();i++) { + + if (cl->functions[i]->arguments.size()) + options.insert(String(cl->functions[i]->name)+"("); + else + options.insert(String(cl->functions[i]->name)+"()"); + } + + for(int i=0;i<cl->static_functions.size();i++) { + + if (cl->static_functions[i]->arguments.size()) + options.insert(String(cl->static_functions[i]->name)+"("); + else + options.insert(String(cl->static_functions[i]->name)+"()"); + + } + + if (!isfunction) { + for(int i=0;i<cl->variables.size();i++) { + + options.insert(String(cl->variables[i].identifier)); + } + + for(int i=0;i<cl->constant_expressions.size();i++) { + + options.insert(String(cl->constant_expressions[i].identifier)); + } + + } + + + } else { + code=""; //well, then no code } + } - for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { - options.insert(E->key()); + + if (code=="") { + //use class directly, no code was found + if (!isfunction) { + for (const Map<StringName,Variant>::Element *E=scr->get_constants().front();E;E=E->next()) { + options.insert(E->key()); + } + } + for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + if (E->get().get_argument_count()) + options.insert(String(E->key())+"()"); + else + options.insert(String(E->key())+"("); + + } + + for (const Set<StringName>::Element *E=scr->get_members().front();E;E=E->next()) { + options.insert(E->get()); + } } if (scr->get_base().is_valid()) @@ -1738,6 +2190,10 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base } + + + + if (!isfunction) { ObjectTypeDB::get_integer_constant_list(t.obj_type,r_options); } diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp index e014921364..6f51ac5312 100644 --- a/modules/gdscript/gd_functions.cpp +++ b/modules/gdscript/gd_functions.cpp @@ -88,6 +88,7 @@ const char *GDFunctions::get_func_name(Function p_func) { "str", "print", "printt", + "prints", "printerr", "printraw", "var2str", @@ -562,6 +563,22 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va } break; + case TEXT_PRINT_SPACED: { + + String str; + for(int i=0;i<p_arg_count;i++) { + + if (i) + str+=" "; + str+=p_args[i]->operator String(); + } + + //str+="\n"; + print_line(str); + r_ret=Variant(); + + + } break; case TEXT_PRINTERR: { @@ -887,6 +904,15 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va r_ret = gdscr->_new(NULL,0,r_error); + GDInstance *ins = static_cast<GDInstance*>(static_cast<Object*>(r_ret)->get_script_instance()); + Ref<GDScript> gd_ref = ins->get_script(); + + for(Map<StringName,GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) { + if(d.has(E->key())) { + ins->members[E->get().index] = d[E->key()]; + } + } + } break; case HASH: { @@ -1252,6 +1278,13 @@ MethodInfo GDFunctions::get_info(Function p_func) { return mi; } break; + case TEXT_PRINT_SPACED: { + + MethodInfo mi("prints",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,"...")); diff --git a/modules/gdscript/gd_functions.h b/modules/gdscript/gd_functions.h index ecd7d158be..ad35a628d5 100644 --- a/modules/gdscript/gd_functions.h +++ b/modules/gdscript/gd_functions.h @@ -84,6 +84,7 @@ public: TEXT_STR, TEXT_PRINT, TEXT_PRINT_TABBED, + TEXT_PRINT_SPACED, TEXT_PRINTERR, TEXT_PRINTRAW, VAR_TO_STR, diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index 6242400663..202ab76da0 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -30,6 +30,7 @@ #include "print_string.h" #include "io/resource_loader.h" #include "os/file_access.h" +#include "script_language.h" template<class T> T* GDParser::alloc_node() { @@ -116,6 +117,14 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) { _make_completable_call(argidx); completion_node=p_parent; + } else if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type()==Variant::STRING && tokenizer->get_token(1)==GDTokenizer::TK_CURSOR) { + //completing a string argument.. + completion_cursor=tokenizer->get_token_constant(); + + _make_completable_call(argidx); + completion_node=p_parent; + tokenizer->advance(1); + return false; } Node*arg = _parse_expression(p_parent,p_static); @@ -161,6 +170,7 @@ void GDParser::_make_completable_call(int p_arg) { completion_line=tokenizer->get_token_line(); completion_argument=p_arg; completion_block=current_block; + completion_found=true; tokenizer->advance(); } @@ -181,6 +191,7 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide completion_function=current_function; completion_line=tokenizer->get_token_line(); completion_block=current_block; + completion_found=true; tokenizer->advance(); if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) { @@ -277,7 +288,11 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ if (!validating) { //this can be too slow for just validating code - res = ResourceLoader::load(path); + if (for_completion && ScriptCodeCompletionCache::get_sigleton()) { + res = ScriptCodeCompletionCache::get_sigleton()->get_cached_resource(path); + } else { + res = ResourceLoader::load(path); + } if (!res.is_valid()) { _set_error("Can't preload resource at path: "+path); return NULL; @@ -1401,6 +1416,24 @@ GDParser::Node* GDParser::_parse_and_reduce_expression(Node *p_parent,bool p_sta return expr; } +bool GDParser::_recover_from_completion() { + + if (!completion_found) { + return false; //can't recover if no completion + } + //skip stuff until newline + while(tokenizer->get_token()!=GDTokenizer::TK_NEWLINE && tokenizer->get_token()!=GDTokenizer::TK_EOF && tokenizer->get_token()!=GDTokenizer::TK_ERROR) { + tokenizer->advance(); + } + completion_found=false; + error_set=false; + if(tokenizer->get_token() == GDTokenizer::TK_ERROR){ + error_set = true; + } + + return true; +} + void GDParser::_parse_block(BlockNode *p_block,bool p_static) { int indent_level = tab_level.back()->get(); @@ -1498,8 +1531,14 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { Node *subexpr=NULL; subexpr = _parse_and_reduce_expression(p_block,p_static); - if (!subexpr) + if (!subexpr) { + if (_recover_from_completion()) { + break; + } return; + } + + lv->assign=subexpr; assigned=subexpr; @@ -1520,16 +1559,22 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { op->arguments.push_back(assigned); p_block->statements.push_back(op); - _end_statement(); - + if (!_end_statement()) { + _set_error("Expected end of statement (var)"); + return; + } } break; case GDTokenizer::TK_CF_IF: { tokenizer->advance(); Node *condition = _parse_and_reduce_expression(p_block,p_static); - if (!condition) + if (!condition) { + if (_recover_from_completion()) { + break; + } return; + } ControlFlowNode *cf_if = alloc_node<ControlFlowNode>(); @@ -1583,8 +1628,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { //condition Node *condition = _parse_and_reduce_expression(p_block,p_static); - if (!condition) + if (!condition) { + if (_recover_from_completion()) { + break; + } return; + } cf_else->arguments.push_back(condition); cf_else->cf_type=ControlFlowNode::CF_IF; @@ -1645,8 +1694,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { tokenizer->advance(); Node *condition = _parse_and_reduce_expression(p_block,p_static); - if (!condition) + if (!condition) { + if (_recover_from_completion()) { + break; + } return; + } ControlFlowNode *cf_while = alloc_node<ControlFlowNode>(); @@ -1691,8 +1744,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { tokenizer->advance(); Node *container = _parse_and_reduce_expression(p_block,p_static); - if (!container) + if (!container) { + if (_recover_from_completion()) { + break; + } return; + } ControlFlowNode *cf_for = alloc_node<ControlFlowNode>(); @@ -1756,8 +1813,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { } else { //expect expression Node *retexpr = _parse_and_reduce_expression(p_block,p_static); - if (!retexpr) + if (!retexpr) { + if (_recover_from_completion()) { + break; + } return; + } cf_return->arguments.push_back(retexpr); p_block->statements.push_back(cf_return); if (!_end_statement()) { @@ -1772,8 +1833,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { tokenizer->advance(); Node *condition = _parse_and_reduce_expression(p_block,p_static); - if (!condition) + if (!condition) { + if (_recover_from_completion()) { + break; + } return; + } AssertNode *an = alloc_node<AssertNode>(); an->condition=condition; p_block->statements.push_back(an); @@ -1786,8 +1851,12 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { default: { Node *expression = _parse_and_reduce_expression(p_block,p_static,false,true); - if (!expression) + if (!expression) { + if (_recover_from_completion()) { + break; + } return; + } p_block->statements.push_back(expression); if (!_end_statement()) { _set_error("Expected end of statement after expression."); @@ -1871,9 +1940,15 @@ void GDParser::_parse_extends(ClassNode *p_class) { p_class->extends_used=true; - //see if inheritance happens from a file tokenizer->advance(); + if (tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_TYPE && tokenizer->get_token_type()==Variant::OBJECT) { + p_class->extends_class.push_back(Variant::get_type_name(Variant::OBJECT)); + tokenizer->advance(); + return; + } + + // see if inheritance happens from a file if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT) { Variant constant = tokenizer->get_token_constant(); @@ -1946,8 +2021,10 @@ void GDParser::_parse_class(ClassNode *p_class) { _parse_extends(p_class); if (error_set) return; - _end_statement(); - + if (!_end_statement()) { + _set_error("Expected end of statement after extends"); + return; + } } break; case GDTokenizer::TK_PR_TOOL: { @@ -2227,6 +2304,53 @@ void GDParser::_parse_class(ClassNode *p_class) { //arguments } break; + case GDTokenizer::TK_PR_SIGNAL: { + tokenizer->advance(); + + if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) { + _set_error("Expected identifier after 'signal'."); + return; + } + + ClassNode::Signal sig; + sig.name = tokenizer->get_token_identifier(); + tokenizer->advance(); + + + if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) { + tokenizer->advance(); + while(true) { + + + if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) { + tokenizer->advance(); + break; + } + + if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) { + _set_error("Expected identifier in signal argument."); + return; + } + + sig.arguments.push_back(tokenizer->get_token_identifier()); + tokenizer->advance(); + + if (tokenizer->get_token()==GDTokenizer::TK_COMMA) { + tokenizer->advance(); + } else if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ',' or ')' after signal parameter identifier."); + return; + } + } + } + + p_class->_signals.push_back(sig); + + if (!_end_statement()) { + _set_error("Expected end of statement (signal)"); + return; + } + } break; case GDTokenizer::TK_PR_EXPORT: { tokenizer->advance(); @@ -2251,6 +2375,17 @@ void GDParser::_parse_class(ClassNode *p_class) { case Variant::INT: { + if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="FLAGS") { + + current_export.hint=PROPERTY_HINT_ALL_FLAGS; + tokenizer->advance(); + if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in hint."); + return; + } + break; + } + if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type()==Variant::STRING) { //enumeration current_export.hint=PROPERTY_HINT_ENUM; @@ -2292,6 +2427,16 @@ void GDParser::_parse_class(ClassNode *p_class) { }; //fallthrough to use the same case Variant::REAL: { + if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="EASE") { + current_export.hint=PROPERTY_HINT_EXP_EASING; + tokenizer->advance(); + if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in hint."); + return; + } + break; + } + float sign=1.0; if (tokenizer->get_token()==GDTokenizer::TK_OP_SUB) { @@ -2442,6 +2587,17 @@ void GDParser::_parse_class(ClassNode *p_class) { } break; } + + if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="MULTILINE") { + + current_export.hint=PROPERTY_HINT_MULTILINE_TEXT; + tokenizer->advance(); + if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' in hint."); + return; + } + break; + } } break; case Variant::COLOR: { @@ -2543,14 +2699,18 @@ void GDParser::_parse_class(ClassNode *p_class) { Node *subexpr=NULL; - subexpr = _parse_and_reduce_expression(p_class,false); - if (!subexpr) + subexpr = _parse_and_reduce_expression(p_class,false,autoexport); + if (!subexpr) { + if (_recover_from_completion()) { + break; + } return; + } member.expression=subexpr; if (autoexport) { - if (subexpr->type==Node::TYPE_ARRAY) { + if (1)/*(subexpr->type==Node::TYPE_ARRAY) { member._export.type=Variant::ARRAY; @@ -2558,7 +2718,7 @@ void GDParser::_parse_class(ClassNode *p_class) { member._export.type=Variant::DICTIONARY; - } else { + } else*/ { if (subexpr->type!=Node::TYPE_CONSTANT) { @@ -2644,8 +2804,10 @@ void GDParser::_parse_class(ClassNode *p_class) { p_class->variables.push_back(member); - _end_statement(); - + if (!_end_statement()) { + _set_error("Expected end of statement (continue)"); + return; + } } break; case GDTokenizer::TK_PR_CONST: { //variale declaration and (eventual) initialization @@ -2672,8 +2834,12 @@ void GDParser::_parse_class(ClassNode *p_class) { Node *subexpr=NULL; subexpr = _parse_and_reduce_expression(p_class,true,true); - if (!subexpr) + if (!subexpr) { + if (_recover_from_completion()) { + break; + } return; + } if (subexpr->type!=Node::TYPE_CONSTANT) { _set_error("Expected constant expression"); @@ -2682,8 +2848,10 @@ void GDParser::_parse_class(ClassNode *p_class) { p_class->constant_expressions.push_back(constant); - _end_statement(); - + if (!_end_statement()) { + _set_error("Expected end of statement (constant)"); + return; + } } break; @@ -2759,11 +2927,14 @@ Error GDParser::_parse(const String& p_base_path) { Error GDParser::parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p_base_path, const String &p_self_path) { + for_completion=false; + validating=false; completion_type=COMPLETION_NONE; completion_node=NULL; completion_class=NULL; completion_function=NULL; completion_block=NULL; + completion_found=false; current_block=NULL; current_class=NULL; current_function=NULL; @@ -2779,13 +2950,14 @@ Error GDParser::parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p } -Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_just_validate, const String &p_self_path) { +Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_just_validate, const String &p_self_path,bool p_for_completion) { completion_type=COMPLETION_NONE; completion_node=NULL; completion_class=NULL; completion_function=NULL; completion_block=NULL; + completion_found=false; current_block=NULL; current_class=NULL; @@ -2796,6 +2968,7 @@ Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_ju tt->set_code(p_code); validating=p_just_validate; + for_completion=p_for_completion; tokenizer=tt; Error ret = _parse(p_base_path); memdelete(tt); @@ -2828,9 +3001,12 @@ void GDParser::clear() { current_block=NULL; current_class=NULL; + completion_found=false; + current_function=NULL; validating=false; + for_completion=false; error_set=false; tab_level.clear(); tab_level.push_back(0); diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h index bf2c8c9ef2..04f3dff3de 100644 --- a/modules/gdscript/gd_parser.h +++ b/modules/gdscript/gd_parser.h @@ -76,6 +76,7 @@ public: StringName extends_file; Vector<StringName> extends_class; + struct Member { PropertyInfo _export; #ifdef TOOLS_ENABLED @@ -92,11 +93,17 @@ public: Node *expression; }; + struct Signal { + StringName name; + Vector<StringName> arguments; + }; + Vector<ClassNode*> subclasses; Vector<Member> variables; Vector<Constant> constant_expressions; Vector<FunctionNode*> functions; Vector<FunctionNode*> static_functions; + Vector<Signal> _signals; BlockNode *initializer; ClassNode *owner; //Vector<Node*> initializers; @@ -269,7 +276,6 @@ public: }; struct NewLineNode : public Node { - int line; NewLineNode() { type=TYPE_NEWLINE; } }; @@ -380,6 +386,7 @@ private: T* alloc_node(); bool validating; + bool for_completion; int parenthesis; bool error_set; String error; @@ -411,10 +418,12 @@ private: BlockNode *completion_block; int completion_line; int completion_argument; + bool completion_found; PropertyInfo current_export; void _set_error(const String& p_error, int p_line=-1, int p_column=-1); + bool _recover_from_completion(); bool _parse_arguments(Node* p_parent, Vector<Node*>& p_args, bool p_static, bool p_can_codecomplete=false); @@ -436,7 +445,7 @@ 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="", bool p_just_validate=false,const String& p_self_path=""); + Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false); Error parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p_base_path="",const String& p_self_path=""); const Node *get_parse_tree() const; diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index e260f70a91..99ddc74bb4 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -140,7 +140,7 @@ String GDFunction::_get_call_error(const Variant::CallError& p_err, const String } 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+"."; + err_text="Invalid call. Nonexistent "+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 { @@ -335,17 +335,30 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GET_VARIANT_PTR(b,3); GET_VARIANT_PTR(dst,4); +#ifdef DEBUG_ENABLED + Variant ret; + Variant::evaluate(op,*a,*b,ret,valid); +#else Variant::evaluate(op,*a,*b,*dst,valid); +#endif + if (!valid) { - if (dst->get_type()==Variant::STRING) { +#ifdef DEBUG_ENABLED + + if (ret.get_type()==Variant::STRING) { //return a string when invalid with the error - err_text=*dst; + err_text=ret; err_text += " in operator '"+Variant::get_operator_name(op)+"'."; } 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)+"'."; } +#endif break; + } +#ifdef DEBUG_ENABLED + *dst=ret; +#endif ip+=5; @@ -457,8 +470,13 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a GET_VARIANT_PTR(dst,3); bool valid; +#ifdef DEBUG_ENABLED +//allow better error message in cases where src and dst are the same stack position + Variant ret = src->get(*index,&valid); +#else *dst = src->get(*index,&valid); +#endif if (!valid) { String v = index->operator String(); if (v!="") { @@ -469,6 +487,9 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a err_text="Invalid get index "+v+" (on base: '"+_get_var_type(src)+"')."; break; } +#ifdef DEBUG_ENABLED + *dst=ret; +#endif ip+=4; } continue; case OPCODE_SET_NAMED: { @@ -508,7 +529,13 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a const StringName *index = &_global_names_ptr[indexname]; bool valid; +#ifdef DEBUG_ENABLED +//allow better error message in cases where src and dst are the same stack position + Variant ret = src->get_named(*index,&valid); + +#else *dst = src->get_named(*index,&valid); +#endif if (!valid) { if (src->has_method(*index)) { @@ -518,7 +545,9 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a } break; } - +#ifdef DEBUG_ENABLED + *dst=ret; +#endif ip+=4; } continue; case OPCODE_ASSIGN: { @@ -1727,6 +1756,12 @@ bool GDScript::_update_exports() { //print_line("found "+c->variables[i]._export.name); member_default_values_cache[c->variables[i].identifier]=c->variables[i].default_value; } + + _signals.clear(); + + for(int i=0;i<c->_signals.size();i++) { + _signals[c->_signals[i].name]=c->_signals[i].arguments; + } } } else { //print_line("unchaged is "+get_path()); @@ -1942,9 +1977,17 @@ void GDScript::_bind_methods() { ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"new",&GDScript::_new,MethodInfo("new")); + ObjectTypeDB::bind_method(_MD("get_as_byte_code"),&GDScript::get_as_byte_code); + } +Vector<uint8_t> GDScript::get_as_byte_code() const { + + GDTokenizerBuffer tokenizer; + return tokenizer.parse_code_string(source); +}; + Error GDScript::load_byte_code(const String& p_path) { @@ -2071,6 +2114,47 @@ Ref<GDScript> GDScript::get_base() const { return base; } +bool GDScript::has_script_signal(const StringName& p_signal) const { + if (_signals.has(p_signal)) + return true; + if (base.is_valid()) { + return base->has_script_signal(p_signal); + } +#ifdef TOOLS_ENABLED + else if (base_cache.is_valid()){ + return base_cache->has_script_signal(p_signal); + } + +#endif + return false; +} +void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const { + + for(const Map<StringName,Vector<StringName> >::Element *E=_signals.front();E;E=E->next()) { + + MethodInfo mi; + mi.name=E->key(); + for(int i=0;i<E->get().size();i++) { + PropertyInfo arg; + arg.name=E->get()[i]; + mi.arguments.push_back(arg); + } + r_signals->push_back(mi); + } + + if (base.is_valid()) { + base->get_script_signal_list(r_signals); + } +#ifdef TOOLS_ENABLED + else if (base_cache.is_valid()){ + base_cache->get_script_signal_list(r_signals); + } + +#endif + +} + + GDScript::GDScript() { @@ -2102,7 +2186,6 @@ bool GDInstance::set(const StringName& p_name, const Variant& p_value) { { const Map<StringName,GDScript::MemberInfo>::Element *E = script->member_indices.find(p_name); if (E) { - members[E->get().index]=p_value; if (E->get().setter) { const Variant *val=&p_value; Variant::CallError err; @@ -2111,6 +2194,8 @@ bool GDInstance::set(const StringName& p_name, const Variant& p_value) { return true; //function exists, call was successful } } + else + members[E->get().index] = p_value; return true; } } @@ -2479,9 +2564,9 @@ void GDScriptLanguage::init() { //populate native classes - List<String> class_list; + List<StringName> class_list; ObjectTypeDB::get_type_list(&class_list); - for(List<String>::Element *E=class_list.front();E;E=E->next()) { + for(List<StringName>::Element *E=class_list.front();E;E=E->next()) { StringName n = E->get(); String s = String(n); @@ -2564,6 +2649,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "static", "float", "int", + "signal", 0}; @@ -2624,7 +2710,10 @@ GDScriptLanguage::~GDScriptLanguage() { /*************** RESOURCE ***************/ -RES ResourceFormatLoaderGDScript::load(const String &p_path,const String& p_original_path) { +RES ResourceFormatLoaderGDScript::load(const String &p_path, const String& p_original_path, Error *r_error) { + + if (r_error) + *r_error=ERR_FILE_CANT_OPEN; GDScript *script = memnew( GDScript ); @@ -2656,6 +2745,8 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path,const String& p_orig script->reload(); } + if (r_error) + *r_error=OK; return scriptres; } diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index f505d51ba4..37ef47af6c 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -260,6 +260,7 @@ friend class GDScriptLanguage; Map<StringName,GDFunction> member_functions; Map<StringName,MemberInfo> member_indices; //members are just indices to the instanced script. Map<StringName,Ref<GDScript> > subclasses; + Map<StringName,Vector<StringName> > _signals; #ifdef TOOLS_ENABLED @@ -318,6 +319,9 @@ public: const Map<StringName,GDFunction>& get_member_functions() const { return member_functions; } const Ref<GDNativeClass>& get_native() const { return native; } + virtual bool has_script_signal(const StringName& p_signal) const; + virtual void get_script_signal_list(List<MethodInfo> *r_signals) const; + bool is_tool() const { return tool; } Ref<GDScript> get_base() const; @@ -345,6 +349,8 @@ public: Error load_source_code(const String& p_path); Error load_byte_code(const String& p_path); + Vector<uint8_t> get_as_byte_code() const; + virtual ScriptLanguage *get_language() const; GDScript(); @@ -469,6 +475,19 @@ public: } + virtual Vector<StackInfo> debug_get_current_stack_info() { + if (Thread::get_main_ID()!=Thread::get_caller_ID()) + return Vector<StackInfo>(); + + Vector<StackInfo> csi; + csi.resize(_debug_call_stack_pos); + for(int i=0;i<_debug_call_stack_pos;i++) { + csi[_debug_call_stack_pos-i-1].line=_call_stack[i].line?*_call_stack[i].line:0; + csi[_debug_call_stack_pos-i-1].script=Ref<GDScript>(_call_stack[i].function->get_script()); + } + return csi; + } + struct { StringName _init; @@ -538,7 +557,7 @@ public: class ResourceFormatLoaderGDScript : public ResourceFormatLoader { public: - virtual RES load(const String &p_path,const String& p_original_path=""); + virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL); 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; diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp index 56b283aa32..b591ed3b4b 100644 --- a/modules/gdscript/gd_tokenizer.cpp +++ b/modules/gdscript/gd_tokenizer.cpp @@ -97,6 +97,7 @@ const char* GDTokenizer::token_names[TK_MAX]={ "preload", "assert", "yield", +"signal", "'['", "']'", "'{'", @@ -568,7 +569,10 @@ void GDTokenizerText::_advance() { } else if( string_mode!=STRING_MULTILINE && CharType(GETCHAR(i))=='\n') { _make_error("Unexpected EOL at String."); return; - + } else if( CharType(GETCHAR(i))==0xFFFF) { + //string ends here, next will be TK + i--; + break; } else if (CharType(GETCHAR(i))=='\\') { //escaped characters... i++; @@ -639,6 +643,11 @@ void GDTokenizerText::_advance() { str+=res; } else { + if (CharType(GETCHAR(i))=='\n') { + line++; + column=0; + } + str+=CharType(GETCHAR(i)); } i++; @@ -670,19 +679,19 @@ void GDTokenizerText::_advance() { while(true) { if (GETCHAR(i)=='.') { if (period_found || exponent_found) { - _make_error("Invalid numeric constant at '.'"); + _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'"); + 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') { + } else if (!hexa_found && GETCHAR(i)=='e') { if (hexa_found || exponent_found) { - _make_error("Invalid numeric constant at 'e'"); + _make_error("Invalid numeric constant at 'e'"); return; } exponent_found=true; @@ -692,7 +701,7 @@ void GDTokenizerText::_advance() { } else if ((GETCHAR(i)=='-' || GETCHAR(i)=='+') && exponent_found) { if (sign_found) { - _make_error("Invalid numeric constant at '-'"); + _make_error("Invalid numeric constant at '-'"); return; } sign_found=true; @@ -703,20 +712,20 @@ void GDTokenizerText::_advance() { i++; } - if (!( _is_number(str[str.length()-1]) || (hexa_found && _is_hex(str[str.length()-1])))) { - _make_error("Invalid numeric constant: "+str); + 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) { + 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 { + } else { int val = str.to_int(); _make_constant(val); @@ -825,7 +834,7 @@ void GDTokenizerText::_advance() { _make_built_in_func(GDFunctions::Function(i)); found=true; - break; + break; } } @@ -856,6 +865,7 @@ void GDTokenizerText::_advance() { {TK_PR_PRELOAD,"preload"}, {TK_PR_ASSERT,"assert"}, {TK_PR_YIELD,"yield"}, + {TK_PR_SIGNAL,"signal"}, {TK_PR_CONST,"const"}, //controlflow {TK_CF_IF,"if"}, @@ -1036,7 +1046,7 @@ void GDTokenizerText::advance(int p_amount) { ////////////////////////////////////////////////////////////////////////////////////////////////////// -#define BYTECODE_VERSION 3 +#define BYTECODE_VERSION 5 Error GDTokenizerBuffer::set_code_buffer(const Vector<uint8_t> & p_buffer) { diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h index b63687d2b4..d6bd63c5b8 100644 --- a/modules/gdscript/gd_tokenizer.h +++ b/modules/gdscript/gd_tokenizer.h @@ -104,6 +104,7 @@ public: TK_PR_PRELOAD, TK_PR_ASSERT, TK_PR_YIELD, + TK_PR_SIGNAL, TK_BRACKET_OPEN, TK_BRACKET_CLOSE, TK_CURLY_BRACKET_OPEN, |