summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/gd_compiler.cpp59
-rw-r--r--modules/gdscript/gd_editor.cpp468
-rw-r--r--modules/gdscript/gd_function.cpp2
-rw-r--r--modules/gdscript/gd_functions.cpp7
-rw-r--r--modules/gdscript/gd_parser.cpp233
-rw-r--r--modules/gdscript/gd_parser.h6
-rw-r--r--modules/gdscript/gd_script.cpp2
-rw-r--r--modules/gdscript/gd_script.h17
-rw-r--r--modules/gdscript/gd_tokenizer.cpp6
-rw-r--r--modules/gdscript/gd_tokenizer.h1
10 files changed, 771 insertions, 30 deletions
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp
index ce8b6a6ea4..2e2cbe7b29 100644
--- a/modules/gdscript/gd_compiler.cpp
+++ b/modules/gdscript/gd_compiler.cpp
@@ -662,6 +662,46 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS;
} break;
+ // ternary operators
+ case GDParser::OperatorNode::OP_TERNARY_IF: {
+
+ // x IF a ELSE y 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.alloc_stack(p_stack_level); //it will be used..
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(res);
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
+ int jump_past_pos=codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+
+ codegen.opcodes[jump_fail_pos]=codegen.opcodes.size();
+ res = _parse_expression(codegen,on->arguments[2],p_stack_level);
+ if (res<0)
+ return res;
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(res);
+
+ codegen.opcodes[jump_past_pos]=codegen.opcodes.size();
+
+ return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS;
+
+ } break;
//unary operators
case GDParser::OperatorNode::OP_NEG: { if (!_create_unary_operator(codegen,on,Variant::OP_NEGATE,p_stack_level)) return -1;} break;
case GDParser::OperatorNode::OP_NOT: { if (!_create_unary_operator(codegen,on,Variant::OP_NOT,p_stack_level)) return -1;} break;
@@ -1403,6 +1443,10 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode *
#endif
if (p_func) {
gdfunc->_initial_line=p_func->line;
+#ifdef TOOLS_ENABLED
+
+ p_script->member_lines[func_name]=p_func->line;
+#endif
} else {
gdfunc->_initial_line=0;
}
@@ -1632,6 +1676,12 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
p_script->member_indices[name]=minfo;
p_script->members.insert(name);
+#ifdef TOOLS_ENABLED
+
+ p_script->member_lines[name]=p_class->variables[i].line;
+#endif
+
+
}
for(int i=0;i<p_class->constant_expressions.size();i++) {
@@ -1643,6 +1693,11 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
p_script->constants.insert(name,constant->value);
//p_script->constants[constant->value].make_const();
+#ifdef TOOLS_ENABLED
+
+ p_script->member_lines[name]=p_class->constant_expressions[i].expression->line;
+#endif
+
}
for(int i=0;i<p_class->_signals.size();i++) {
@@ -1691,6 +1746,10 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa
if (err)
return err;
+#ifdef TOOLS_ENABLED
+
+ p_script->member_lines[name]=p_class->subclasses[i]->line;
+#endif
p_script->constants.insert(name,subclass); //once parsed, goes to the list of constants
p_script->subclasses.insert(name,subclass);
diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp
index 5b74dab889..c3e59836a2 100644
--- a/modules/gdscript/gd_editor.cpp
+++ b/modules/gdscript/gd_editor.cpp
@@ -47,16 +47,14 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
Ref<Script> 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"+
+ "extends %BASE%\n\n"+
+ "# class member variables go here, for example:\n"+
+ "# var a = 2\n"+
+ "# var b = \"textvar\"\n\n"+
"func _ready():\n"+
"\t# Called every time the node is added to the scene.\n"+
"\t# Initialization here\n"+
- "\tpass\n"+
- "\n"+
- "\n";
+ "\tpass\n";
_template = _template.replace("%BASE%",p_base_class_name);
@@ -353,6 +351,7 @@ struct GDCompletionIdentifier {
Ref<GDScript> script;
Variant::Type type;
Variant value; //im case there is a value, also return it
+
};
@@ -937,6 +936,7 @@ static bool _guess_expression_type(GDCompletionContext& context,const GDParser::
return false;
}
+
static bool _guess_identifier_type_in_block(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type) {
@@ -2096,7 +2096,7 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
GDParser p;
- Error err = p.parse(p_code,p_base_path,false,"",true);
+ p.parse(p_code,p_base_path,false,"",true);
bool isfunction=false;
Set<String> options;
@@ -2143,7 +2143,18 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
GDCompletionIdentifier t;
if (_guess_expression_type(context,static_cast<const GDParser::OperatorNode *>(node)->arguments[0],p.get_completion_line(),t)) {
- if (t.type==Variant::OBJECT && t.obj_type!=StringName()) {
+ if (t.type==Variant::OBJECT && t.obj_type=="GDNativeClass") {
+ //native enum
+ Ref<GDNativeClass> gdn = t.value;
+ if (gdn.is_valid()) {
+ StringName cn = gdn->get_name();
+ List<String> cnames;
+ ObjectTypeDB::get_integer_constant_list(cn,&cnames);
+ for (List<String>::Element *E=cnames.front();E;E=E->next()) {
+ options.insert(E->get());
+ }
+ }
+ } else if (t.type==Variant::OBJECT && t.obj_type!=StringName()) {
Ref<GDScript> on_script;
@@ -2513,3 +2524,442 @@ void GDScriptLanguage::auto_indent_code(String& p_code,int p_from_line,int p_to_
}
}
+
+#ifdef TOOLS_ENABLED
+
+Error GDScriptLanguage::lookup_code(const String& p_code, const String& p_symbol,const String& p_base_path, Object*p_owner,LookupResult& r_result) {
+
+
+ //before parsing, try the usual stuff
+ if (ObjectTypeDB::type_exists(p_symbol)) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS;
+ r_result.class_name=p_symbol;
+ return OK;
+ }
+
+ for(int i=0;i<Variant::VARIANT_MAX;i++) {
+ Variant::Type t = Variant::Type(i);
+ if (Variant::get_type_name(t)==p_symbol) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS;
+ r_result.class_name=Variant::get_type_name(t);
+ return OK;
+ }
+ }
+
+ for(int i=0;i<GDFunctions::FUNC_MAX;i++) {
+ if (GDFunctions::get_func_name(GDFunctions::Function(i))==p_symbol) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name="@GDScript";
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+ }
+
+ GDParser p;
+ p.parse(p_code,p_base_path,false,"",true);
+
+ if (p.get_completion_type()==GDParser::COMPLETION_NONE)
+ return ERR_CANT_RESOLVE;
+
+ GDCompletionContext context;
+
+ context._class=p.get_completion_class();
+ context.block=p.get_completion_block();
+ context.function=p.get_completion_function();
+ context.base=p_owner;
+ context.base_path=p_base_path;
+ bool isfunction=false;
+
+ switch(p.get_completion_type()) {
+
+ case GDParser::COMPLETION_NONE: {
+ } break;
+ case GDParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name=Variant::get_type_name(p.get_completion_built_in_constant());
+ r_result.class_member=p_symbol;
+ return OK;
+
+ } break;
+ case GDParser::COMPLETION_FUNCTION: {
+
+
+ if (context._class && context._class->functions.size()) {
+ for(int i=0;i<context._class->functions.size();i++) {
+ if (context._class->functions[i]->name==p_symbol) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=context._class->functions[i]->line;
+ return OK;
+ }
+ }
+ }
+
+ Ref<GDScript> parent = _get_parent_class(context);
+ while(parent.is_valid()) {
+ int line = parent->get_member_line(p_symbol);
+ if (line>=0) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=line;
+ r_result.script=parent;
+ return OK;
+
+ }
+
+ parent=parent->get_base();
+ }
+
+ GDCompletionIdentifier identifier = _get_native_class(context);
+ print_line("identifier: "+String(identifier.obj_type));
+
+ if (ObjectTypeDB::has_method(identifier.obj_type,p_symbol)) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name=identifier.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+
+
+ } break;
+ case GDParser::COMPLETION_IDENTIFIER: {
+
+ //check if a function
+ if (p.get_completion_identifier_is_function()) {
+ if (context._class && context._class->functions.size()) {
+ for(int i=0;i<context._class->functions.size();i++) {
+ if (context._class->functions[i]->name==p_symbol) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=context._class->functions[i]->line;
+ return OK;
+ }
+ }
+ }
+
+ Ref<GDScript> parent = _get_parent_class(context);
+ while(parent.is_valid()) {
+ int line = parent->get_member_line(p_symbol);
+ if (line>=0) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=line;
+ r_result.script=parent;
+ return OK;
+
+ }
+
+ parent=parent->get_base();
+ }
+
+ GDCompletionIdentifier identifier = _get_native_class(context);
+
+
+ if (ObjectTypeDB::has_method(identifier.obj_type,p_symbol)) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name=identifier.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+ } else {
+
+
+ const GDParser::BlockNode *block=context.block;
+ //search in blocks going up (local var?)
+ while(block) {
+
+
+
+ for (int i=0;i<block->statements.size();i++) {
+
+ if (block->statements[i]->line>p.get_completion_line())
+ continue;
+
+
+ if (block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) {
+
+ const GDParser::LocalVarNode *lv=static_cast<const GDParser::LocalVarNode *>(block->statements[i]);
+
+ if (lv->assign && lv->name==p_symbol) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=block->statements[i]->line;
+ return OK;
+ }
+ }
+ }
+ block=block->parent_block;
+ }
+
+ //guess from function arguments
+ if (context.function && context.function->name!=StringName()) {
+
+ for(int i=0;i<context.function->arguments.size();i++) {
+
+ if (context.function->arguments[i]==p_symbol) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=context.function->line;
+ return OK;
+ }
+
+ }
+ }
+
+ //guess in class constants
+
+ for(int i=0;i<context._class->constant_expressions.size();i++) {
+
+ if (context._class->constant_expressions[i].identifier==p_symbol) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=context._class->constant_expressions[i].expression->line;
+ return OK;
+ }
+ }
+
+ //guess in class variables
+ if (!(context.function && context.function->_static)) {
+
+ for(int i=0;i<context._class->variables.size();i++) {
+
+ if (context._class->variables[i].identifier==p_symbol) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=context._class->variables[i].line;
+ return OK;
+ }
+ }
+ }
+
+ //guess in autoloads as singletons
+ 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;
+ String name = s.get_slice("/",1);
+ if (name==String(p_symbol)) {
+
+ String path = Globals::get_singleton()->get(s);
+ if (path.begins_with("*")) {
+ String script =path.substr(1,path.length());
+
+ if (!script.ends_with(".gd")) {
+ //not a script, try find the script anyway,
+ //may have some success
+ script=script.basename()+".gd";
+ }
+
+ if (FileAccess::exists(script)) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=0;
+ r_result.script=ResourceLoader::load(script);
+ return OK;
+ }
+ }
+ }
+ }
+
+ //global
+ for(Map<StringName,int>::Element *E=GDScriptLanguage::get_singleton()->get_global_map().front();E;E=E->next()) {
+ if (E->key()==p_symbol) {
+
+ Variant value = GDScriptLanguage::get_singleton()->get_global_array()[E->get()];
+ if (value.get_type()==Variant::OBJECT) {
+ Object *obj = value;
+ if (obj) {
+
+ if (obj->cast_to<GDNativeClass>()) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS;
+ r_result.class_name=obj->cast_to<GDNativeClass>()->get_name();
+
+ } else {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS;
+ r_result.class_name=obj->get_type();
+ }
+ return OK;
+ }
+ } else {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name="@Global Scope";
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+ }
+
+ }
+#if 0
+ GDCompletionIdentifier identifier;
+ if (_guess_identifier_type(context,p.get_completion_line(),p_symbol,identifier)) {
+
+ print_line("var type: "+Variant::get_type_name(identifier.type));
+ if (identifier.script.is_valid()) {
+ print_line("var script: "+identifier.script->get_path());
+ }
+ print_line("obj type: "+String(identifier.obj_type));
+ print_line("value: "+String(identifier.value));
+ }
+#endif
+ }
+
+ } break;
+ case GDParser::COMPLETION_PARENT_FUNCTION: {
+
+ } break;
+ case GDParser::COMPLETION_METHOD:
+ isfunction=true;
+ case GDParser::COMPLETION_INDEX: {
+
+ const GDParser::Node *node = p.get_completion_node();
+ if (node->type!=GDParser::Node::TYPE_OPERATOR)
+ break;
+
+
+
+
+ GDCompletionIdentifier t;
+ if (_guess_expression_type(context,static_cast<const GDParser::OperatorNode *>(node)->arguments[0],p.get_completion_line(),t)) {
+
+ if (t.type==Variant::OBJECT && t.obj_type=="GDNativeClass") {
+ //native enum
+ Ref<GDNativeClass> gdn = t.value;
+ if (gdn.is_valid()) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name=gdn->get_name();;
+ r_result.class_member=p_symbol;
+ return OK;
+
+ }
+ } else if (t.type==Variant::OBJECT && t.obj_type!=StringName()) {
+
+ Ref<GDScript> on_script;
+
+ if (t.value.get_type()) {
+ Object *obj=t.value;
+
+
+ if (obj) {
+
+
+ on_script=obj->get_script();
+
+ if (on_script.is_valid()) {
+ int loc = on_script->get_member_line(p_symbol);
+ if (loc>=0) {
+ r_result.script=on_script;
+ r_result.type=ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
+ r_result.location=loc;
+ return OK;
+ }
+ }
+ }
+ }
+
+ if (ObjectTypeDB::has_method(t.obj_type,p_symbol)) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name=t.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+
+ }
+
+ bool success;
+ ObjectTypeDB::get_integer_constant(t.obj_type,p_symbol,&success);
+ if (success) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name=t.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+
+ ObjectTypeDB::get_property_type(t.obj_type,p_symbol,&success);
+
+ if (success) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
+ r_result.class_name=t.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+
+
+ } else {
+
+ Variant::CallError ce;
+ Variant v = Variant::construct(t.type,NULL,0,ce);
+
+ bool valid;
+ v.get_numeric_constant_value(t.type,p_symbol,&valid);
+ if (valid) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name=Variant::get_type_name(t.type);
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+
+ //todo check all inputevent types for property
+
+ v.get(p_symbol,&valid);
+
+ if (valid) {
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_PROPERTY;
+ r_result.class_name=Variant::get_type_name(t.type);
+ r_result.class_member=p_symbol;
+ return OK;
+ }
+
+ if (v.has_method(p_symbol)) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name=Variant::get_type_name(t.type);
+ r_result.class_member=p_symbol;
+ return OK;
+
+ }
+
+
+ }
+ }
+
+
+ } break;
+ case GDParser::COMPLETION_CALL_ARGUMENTS: {
+
+ return ERR_CANT_RESOLVE;
+ } break;
+ case GDParser::COMPLETION_VIRTUAL_FUNC: {
+
+ GDCompletionIdentifier cid = _get_native_class(context);
+
+ if (cid.obj_type!=StringName()) {
+ List<MethodInfo> vm;
+ ObjectTypeDB::get_virtual_methods(cid.obj_type,&vm);
+ for(List<MethodInfo>::Element *E=vm.front();E;E=E->next()) {
+
+ if (p_symbol==E->get().name) {
+
+ r_result.type=ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name=cid.obj_type;
+ r_result.class_member=p_symbol;
+ return OK;
+
+ }
+ }
+ }
+ } break;
+ case GDParser::COMPLETION_YIELD: {
+
+ return ERR_CANT_RESOLVE;
+
+ } break;
+
+ }
+
+
+ return ERR_CANT_RESOLVE;
+}
+
+#endif
diff --git a/modules/gdscript/gd_function.cpp b/modules/gdscript/gd_function.cpp
index b2cc6341c1..094e21bb4f 100644
--- a/modules/gdscript/gd_function.cpp
+++ b/modules/gdscript/gd_function.cpp
@@ -1437,7 +1437,7 @@ void GDFunctionState::_bind_methods() {
ObjectTypeDB::bind_method(_MD("resume:Variant","arg"),&GDFunctionState::resume,DEFVAL(Variant()));
ObjectTypeDB::bind_method(_MD("is_valid"),&GDFunctionState::is_valid);
- ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&GDFunctionState::_signal_callback,MethodInfo("_signal_callback"));
+ ObjectTypeDB::bind_vararg_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&GDFunctionState::_signal_callback,MethodInfo("_signal_callback"));
}
diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp
index a565e866d0..e82eb83773 100644
--- a/modules/gdscript/gd_functions.cpp
+++ b/modules/gdscript/gd_functions.cpp
@@ -840,8 +840,13 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_ret=Variant();
+ } else if(((String)(*p_args[0])).begins_with("/")) {
+ r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument=0;
+ r_ret=RTR("Paths cannot start with '/', absolute paths must start with 'res://', 'user://', or 'local://'");
+ } else {
+ r_ret=ResourceLoader::load(*p_args[0]);
}
- r_ret=ResourceLoader::load(*p_args[0]);
} break;
case INST2DICT: {
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index e5a8dc0152..3c01f469f9 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -203,6 +203,7 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide
completion_line=tokenizer->get_token_line();
completion_block=current_block;
completion_found=true;
+ completion_ident_is_call=false;
tokenizer->advance();
if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
@@ -210,6 +211,9 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide
tokenizer->advance();
}
+ if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
+ completion_ident_is_call=true;
+ }
return true;
}
@@ -303,6 +307,10 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
_set_error("expected string constant as 'preload' argument.");
return NULL;
}
+ if (path.begins_with("/")) {
+ _set_error("Paths cannot start with '/', absolute paths must start with \'res://\', \'user://\', or \'local://\'");
+ return NULL;
+ }
if (!path.is_abs_path() && base_path!="")
path=base_path+"/"+path;
path = path.replace("///","//").simplify_path();
@@ -936,6 +944,8 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
case GDTokenizer::TK_OP_BIT_OR: op=OperatorNode::OP_BIT_OR ; break;
case GDTokenizer::TK_OP_BIT_XOR: op=OperatorNode::OP_BIT_XOR ; break;
case GDTokenizer::TK_PR_EXTENDS: op=OperatorNode::OP_EXTENDS; break;
+ case GDTokenizer::TK_CF_IF: op=OperatorNode::OP_TERNARY_IF; break;
+ case GDTokenizer::TK_CF_ELSE: op=OperatorNode::OP_TERNARY_ELSE; break;
default: valid=false; break;
}
@@ -958,6 +968,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
int next_op=-1;
int min_priority=0xFFFFF;
bool is_unary=false;
+ bool is_ternary=false;
for(int i=0;i<expression.size();i++) {
@@ -971,6 +982,8 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
int priority;
bool unary=false;
+ bool ternary=false;
+ bool error=false;
switch(expression[i].op) {
@@ -1001,25 +1014,27 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
case OperatorNode::OP_EQUAL: priority=8; break;
case OperatorNode::OP_NOT_EQUAL: priority=8; break;
+
case OperatorNode::OP_IN: priority=10; break;
-
+
case OperatorNode::OP_NOT: priority=11; unary=true; break;
case OperatorNode::OP_AND: priority=12; break;
case OperatorNode::OP_OR: priority=13; break;
-
- // ?: = 10
-
- case OperatorNode::OP_ASSIGN: priority=14; break;
- case OperatorNode::OP_ASSIGN_ADD: priority=14; break;
- case OperatorNode::OP_ASSIGN_SUB: priority=14; break;
- case OperatorNode::OP_ASSIGN_MUL: priority=14; break;
- case OperatorNode::OP_ASSIGN_DIV: priority=14; break;
- case OperatorNode::OP_ASSIGN_MOD: priority=14; break;
- case OperatorNode::OP_ASSIGN_SHIFT_LEFT: priority=14; break;
- case OperatorNode::OP_ASSIGN_SHIFT_RIGHT: priority=14; break;
- case OperatorNode::OP_ASSIGN_BIT_AND: priority=14; break;
- case OperatorNode::OP_ASSIGN_BIT_OR: priority=14; break;
- case OperatorNode::OP_ASSIGN_BIT_XOR: priority=14; break;
+
+ case OperatorNode::OP_TERNARY_IF: priority=14; ternary=true; break;
+ case OperatorNode::OP_TERNARY_ELSE: priority=14; error=true; break; // Errors out when found without IF (since IF would consume it)
+
+ case OperatorNode::OP_ASSIGN: priority=15; break;
+ case OperatorNode::OP_ASSIGN_ADD: priority=15; break;
+ case OperatorNode::OP_ASSIGN_SUB: priority=15; break;
+ case OperatorNode::OP_ASSIGN_MUL: priority=15; break;
+ case OperatorNode::OP_ASSIGN_DIV: priority=15; break;
+ case OperatorNode::OP_ASSIGN_MOD: priority=15; break;
+ case OperatorNode::OP_ASSIGN_SHIFT_LEFT: priority=15; break;
+ case OperatorNode::OP_ASSIGN_SHIFT_RIGHT: priority=15; break;
+ case OperatorNode::OP_ASSIGN_BIT_AND: priority=15; break;
+ case OperatorNode::OP_ASSIGN_BIT_OR: priority=15; break;
+ case OperatorNode::OP_ASSIGN_BIT_XOR: priority=15; break;
default: {
@@ -1030,11 +1045,16 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
}
if (priority<min_priority) {
+ if(error) {
+ _set_error("Unexpected operator");
+ return NULL;
+ }
// < is used for left to right (default)
// <= is used for right to left
next_op=i;
min_priority=priority;
is_unary=unary;
+ is_ternary=ternary;
}
}
@@ -1075,6 +1095,62 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
}
+ } else if(is_ternary) {
+ if (next_op <1 || next_op>=(expression.size()-1)) {
+ _set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ if(next_op>=(expression.size()-2) || expression[next_op+2].op != OperatorNode::OP_TERNARY_ELSE) {
+ _set_error("Expected else after ternary if.");
+ ERR_FAIL_V(NULL);
+ }
+ if(next_op>=(expression.size()-3)) {
+ _set_error("Expected value after ternary else.");
+ ERR_FAIL_V(NULL);
+ }
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=expression[next_op].op;
+ op->line=op_line; //line might have been changed from a \n
+
+ if (expression[next_op-1].is_op) {
+
+ _set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ if (expression[next_op+1].is_op) {
+ // this is not invalid and can really appear
+ // but it becomes invalid anyway because no binary op
+ // can be followed by an unary op in a valid combination,
+ // due to how precedence works, unaries will always dissapear first
+
+ _set_error("Unexpected two consecutive operators after ternary if.");
+ return NULL;
+ }
+
+ if (expression[next_op+3].is_op) {
+ // this is not invalid and can really appear
+ // but it becomes invalid anyway because no binary op
+ // can be followed by an unary op in a valid combination,
+ // due to how precedence works, unaries will always dissapear first
+
+ _set_error("Unexpected two consecutive operators after ternary else.");
+ return NULL;
+ }
+
+
+ op->arguments.push_back(expression[next_op+1].node); //next expression goes as first
+ op->arguments.push_back(expression[next_op-1].node); //left expression goes as when-true
+ op->arguments.push_back(expression[next_op+3].node); //expression after next goes as when-false
+
+ //replace all 3 nodes by this operator and make it an expression
+ expression[next_op-1].node=op;
+ expression.remove(next_op);
+ expression.remove(next_op);
+ expression.remove(next_op);
+ expression.remove(next_op);
} else {
if (next_op <1 || next_op>=(expression.size()-1)) {
@@ -2037,6 +2113,10 @@ void GDParser::_parse_extends(ClassNode *p_class) {
_set_error("'extends' constant must be a string.");
return;
}
+ if (((String)(constant)).begins_with("/")) {
+ _set_error("Paths cannot start with '/', absolute paths must start with \'res://\', \'user://\', or \'local://\'");
+ return;
+ }
p_class->extends_file=constant;
tokenizer->advance();
@@ -3029,6 +3109,16 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
member._export.type=cn->value.get_type();
member._export.usage|=PROPERTY_USAGE_SCRIPT_VARIABLE;
+ if (cn->value.get_type()==Variant::OBJECT) {
+ Object *obj = cn->value;
+ Resource *res = obj->cast_to<Resource>();
+ if(res==NULL) {
+ _set_error("Exported constant not a type or resource.");
+ return;
+ }
+ member._export.hint=PROPERTY_HINT_RESOURCE_TYPE;
+ member._export.hint_string=res->get_type();
+ }
}
}
#ifdef TOOLS_ENABLED
@@ -3157,6 +3247,114 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
} break;
+ case GDTokenizer::TK_PR_ENUM: {
+ //mutiple constant declarations..
+
+ int last_assign = -1; // Incremented by 1 right before the assingment.
+ String enum_name;
+ Dictionary enum_dict;
+
+ tokenizer->advance();
+ if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
+ enum_name=tokenizer->get_token_identifier();
+ tokenizer->advance();
+ }
+ if (tokenizer->get_token()!=GDTokenizer::TK_CURLY_BRACKET_OPEN) {
+ _set_error("Expected '{' in enum declaration");
+ return;
+ }
+ tokenizer->advance();
+
+ while(true) {
+ if(tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+
+ tokenizer->advance(); // Ignore newlines
+ } else if (tokenizer->get_token()==GDTokenizer::TK_CURLY_BRACKET_CLOSE) {
+
+ tokenizer->advance();
+ break; // End of enum
+ } else if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
+
+ if(tokenizer->get_token()==GDTokenizer::TK_EOF) {
+ _set_error("Unexpected end of file.");
+ } else {
+ _set_error(String("Unexpected ") + GDTokenizer::get_token_name(tokenizer->get_token()) + ", expected identifier");
+ }
+
+ return;
+ } else { // tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER
+ ClassNode::Constant constant;
+
+ constant.identifier=tokenizer->get_token_identifier();
+
+ tokenizer->advance();
+
+ if (tokenizer->get_token()==GDTokenizer::TK_OP_ASSIGN) {
+ tokenizer->advance();
+
+ Node *subexpr=NULL;
+
+ subexpr = _parse_and_reduce_expression(p_class,true,true);
+ if (!subexpr) {
+ if (_recover_from_completion()) {
+ break;
+ }
+ return;
+ }
+
+ if (subexpr->type!=Node::TYPE_CONSTANT) {
+ _set_error("Expected constant expression");
+ }
+
+ const ConstantNode *subexpr_const = static_cast<const ConstantNode*>(subexpr);
+
+ if(subexpr_const->value.get_type() != Variant::INT) {
+ _set_error("Expected an int value for enum");
+ }
+
+ last_assign = subexpr_const->value;
+
+ constant.expression=subexpr;
+
+ } else {
+ last_assign = last_assign + 1;
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value = last_assign;
+ constant.expression = cn;
+ }
+
+ if(tokenizer->get_token()==GDTokenizer::TK_COMMA) {
+ tokenizer->advance();
+ }
+
+ if(enum_name != "") {
+ const ConstantNode *cn = static_cast<const ConstantNode*>(constant.expression);
+ enum_dict[constant.identifier] = cn->value;
+ }
+
+ p_class->constant_expressions.push_back(constant);
+ }
+
+ }
+
+ if(enum_name != "") {
+ ClassNode::Constant enum_constant;
+ enum_constant.identifier=enum_name;
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value = enum_dict;
+ enum_constant.expression=cn;
+ p_class->constant_expressions.push_back(enum_constant);
+ }
+
+ if (!_end_statement()) {
+ _set_error("Expected end of statement (enum)");
+ return;
+ }
+
+
+
+
+ } break;
default: {
@@ -3376,6 +3574,11 @@ int GDParser::get_completion_argument_index() {
return completion_argument;
}
+int GDParser::get_completion_identifier_is_function() {
+
+ return completion_ident_is_call;
+}
+
GDParser::GDParser() {
head=NULL;
diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h
index 9e6f6e6765..75653e0916 100644
--- a/modules/gdscript/gd_parser.h
+++ b/modules/gdscript/gd_parser.h
@@ -247,6 +247,9 @@ public:
OP_BIT_AND,
OP_BIT_OR,
OP_BIT_XOR,
+ //ternary operators
+ OP_TERNARY_IF,
+ OP_TERNARY_ELSE,
};
Operator op;
@@ -429,6 +432,7 @@ private:
int completion_line;
int completion_argument;
bool completion_found;
+ bool completion_ident_is_call;
PropertyInfo current_export;
@@ -475,7 +479,7 @@ public:
BlockNode *get_completion_block();
FunctionNode *get_completion_function();
int get_completion_argument_index();
-
+ int get_completion_identifier_is_function();
void clear();
GDParser();
diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp
index b97a0fcbb6..0ea10950df 100644
--- a/modules/gdscript/gd_script.cpp
+++ b/modules/gdscript/gd_script.cpp
@@ -751,7 +751,7 @@ void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const {
void GDScript::_bind_methods() {
- ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"new",&GDScript::_new,MethodInfo(Variant::OBJECT,"new"));
+ ObjectTypeDB::bind_vararg_method(METHOD_FLAGS_DEFAULT,"new",&GDScript::_new,MethodInfo(Variant::OBJECT,"new"));
ObjectTypeDB::bind_method(_MD("get_as_byte_code"),&GDScript::get_as_byte_code);
diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h
index 0c3e1eb614..051e80634f 100644
--- a/modules/gdscript/gd_script.h
+++ b/modules/gdscript/gd_script.h
@@ -65,6 +65,7 @@ class GDScript : public Script {
StringName setter;
StringName getter;
ScriptInstance::RPCMode rpc_mode;
+
};
friend class GDInstance;
@@ -86,8 +87,11 @@ friend class GDScriptLanguage;
Map<StringName,Ref<GDScript> > subclasses;
Map<StringName,Vector<StringName> > _signals;
+
#ifdef TOOLS_ENABLED
+ Map<StringName,int> member_lines;
+
Map<StringName,Variant> member_default_values;
List<PropertyInfo> members_cache;
@@ -193,6 +197,16 @@ public:
virtual ScriptLanguage *get_language() const;
+ virtual int get_member_line(const StringName& p_member) const {
+#ifdef TOOLS_ENABLED
+ if (member_lines.has(p_member))
+ return member_lines[p_member];
+ else
+#endif
+ return -1;
+
+ }
+
GDScript();
~GDScript();
};
@@ -394,6 +408,9 @@ public:
virtual int find_function(const String& p_function,const String& p_code) const;
virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const;
virtual Error complete_code(const String& p_code, const String& p_base_path, Object*p_owner,List<String>* r_options,String& r_call_hint);
+#ifdef TOOLS_ENABLED
+ virtual Error lookup_code(const String& p_code, const String& p_symbol, const String& p_base_path, Object*p_owner, LookupResult& r_result);
+#endif
virtual void auto_indent_code(String& p_code,int p_from_line,int p_to_line) const;
virtual void add_global_constant(const StringName& p_variable,const Variant& p_value);
diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp
index 47e740b227..2041ec12ad 100644
--- a/modules/gdscript/gd_tokenizer.cpp
+++ b/modules/gdscript/gd_tokenizer.cpp
@@ -95,6 +95,7 @@ const char* GDTokenizer::token_names[TK_MAX]={
"setget",
"const",
"var",
+"enum",
"preload",
"assert",
"yield",
@@ -874,6 +875,7 @@ void GDTokenizerText::_advance() {
{TK_PR_SLAVE,"slave"},
{TK_PR_SYNC,"sync"},
{TK_PR_CONST,"const"},
+ {TK_PR_ENUM,"enum"},
//controlflow
{TK_CF_IF,"if"},
{TK_CF_ELIF,"elif"},
@@ -1330,7 +1332,7 @@ StringName GDTokenizerBuffer::get_token_identifier(int p_offset) const{
ERR_FAIL_INDEX_V(offset,tokens.size(),StringName());
uint32_t identifier = tokens[offset]>>TOKEN_BITS;
- ERR_FAIL_INDEX_V(identifier,identifiers.size(),StringName());
+ ERR_FAIL_INDEX_V(identifier,(uint32_t)identifiers.size(),StringName());
return identifiers[identifier];
}
@@ -1389,7 +1391,7 @@ const Variant& GDTokenizerBuffer::get_token_constant(int p_offset) const{
int offset = token+p_offset;
ERR_FAIL_INDEX_V(offset,tokens.size(),nil);
uint32_t constant = tokens[offset]>>TOKEN_BITS;
- ERR_FAIL_INDEX_V(constant,constants.size(),nil);
+ ERR_FAIL_INDEX_V(constant,(uint32_t)constants.size(),nil);
return constants[constant];
}
diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h
index 1815f82894..b91229ab1e 100644
--- a/modules/gdscript/gd_tokenizer.h
+++ b/modules/gdscript/gd_tokenizer.h
@@ -102,6 +102,7 @@ public:
TK_PR_SETGET,
TK_PR_CONST,
TK_PR_VAR,
+ TK_PR_ENUM,
TK_PR_PRELOAD,
TK_PR_ASSERT,
TK_PR_YIELD,