diff options
Diffstat (limited to 'modules/gdscript')
| -rw-r--r-- | modules/gdscript/gd_compiler.cpp | 3 | ||||
| -rw-r--r-- | modules/gdscript/gd_compiler.h | 1 | ||||
| -rw-r--r-- | modules/gdscript/gd_editor.cpp | 171 | ||||
| -rw-r--r-- | modules/gdscript/gd_function.cpp | 71 | ||||
| -rw-r--r-- | modules/gdscript/gd_function.h | 3 | ||||
| -rw-r--r-- | modules/gdscript/gd_functions.cpp | 68 | ||||
| -rw-r--r-- | modules/gdscript/gd_functions.h | 2 | ||||
| -rw-r--r-- | modules/gdscript/gd_parser.cpp | 127 | ||||
| -rw-r--r-- | modules/gdscript/gd_parser.h | 4 | ||||
| -rw-r--r-- | modules/gdscript/gd_script.cpp | 36 | ||||
| -rw-r--r-- | modules/gdscript/gd_script.h | 12 | ||||
| -rw-r--r-- | modules/gdscript/gd_tokenizer.cpp | 322 | ||||
| -rw-r--r-- | modules/gdscript/gd_tokenizer.h | 5 | ||||
| -rw-r--r-- | modules/gdscript/register_types.cpp | 1 | ||||
| -rw-r--r-- | modules/gdscript/register_types.h | 1 |
15 files changed, 554 insertions, 273 deletions
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index 245f44887..d4ede4cb1 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -1013,7 +1014,7 @@ int GDCompiler::_parse_expression(CodeGen &codegen, const GDParser::Node *p_expr } } break; - case GDParser::OperatorNode::OP_EXTENDS: { + case GDParser::OperatorNode::OP_IS: { ERR_FAIL_COND_V(on->arguments.size() != 2, false); diff --git a/modules/gdscript/gd_compiler.h b/modules/gdscript/gd_compiler.h index c84bd9724..b7ff092eb 100644 --- a/modules/gdscript/gd_compiler.h +++ b/modules/gdscript/gd_compiler.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index 1499f167c..f8b45af85 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,10 +27,15 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "editor/editor_settings.h" #include "gd_compiler.h" #include "gd_script.h" -#include "global_config.h" #include "os/file_access.h" +#include "project_settings.h" +#ifdef TOOLS_ENABLED +#include "editor/editor_file_system.h" +#include "editor/editor_settings.h" +#endif void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const { @@ -49,11 +55,12 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str "# 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"; + "%TS%# Called every time the node is added to the scene.\n" + + "%TS%# Initialization here\n" + + "%TS%pass\n"; _template = _template.replace("%BASE%", p_base_class_name); + _template = _template.replace("%TS%", _get_indentation()); Ref<GDScript> script; script.instance(); @@ -62,6 +69,19 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str return script; } +bool GDScriptLanguage::is_using_templates() { + + return true; +} + +void GDScriptLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { + + String src = p_script->get_source_code(); + src = src.replace("%BASE%", p_base_class_name); + src = src.replace("%TS%", _get_indentation()); + p_script->set_source_code(src); +} + bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { GDParser parser; @@ -137,7 +157,7 @@ Script *GDScriptLanguage::create_script() const { 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()) { + if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) { _debug_parse_err_line = p_line; _debug_parse_err_file = p_file; @@ -151,7 +171,7 @@ bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const bool GDScriptLanguage::debug_break(const String &p_error, bool p_allow_continue) { - if (ScriptDebugger::get_singleton() && Thread::get_caller_ID() == Thread::get_main_ID()) { + if (ScriptDebugger::get_singleton() && Thread::get_caller_id() == Thread::get_main_id()) { _debug_parse_err_line = -1; _debug_parse_err_file = ""; @@ -618,7 +638,7 @@ static bool _guess_expression_type(GDCompletionContext &context, const GDParser: String which = arg1.get_slice("/", 2); if (which != "") { List<PropertyInfo> props; - GlobalConfig::get_singleton()->get_property_list(&props); + ProjectSettings::get_singleton()->get_property_list(&props); //print_line("find singleton"); for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { @@ -630,7 +650,7 @@ static bool _guess_expression_type(GDCompletionContext &context, const GDParser: String name = s.get_slice("/", 1); //print_line("name: "+name+", which: "+which); if (name == which) { - String script = GlobalConfig::get_singleton()->get(s); + String script = ProjectSettings::get_singleton()->get(s); if (!script.begins_with("res://")) { script = "res://" + script; @@ -1085,7 +1105,7 @@ static bool _guess_identifier_type(GDCompletionContext &context, int p_line, con //autoloads as singletons List<PropertyInfo> props; - GlobalConfig::get_singleton()->get_property_list(&props); + ProjectSettings::get_singleton()->get_property_list(&props); for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { @@ -1095,7 +1115,7 @@ static bool _guess_identifier_type(GDCompletionContext &context, int p_line, con String name = s.get_slice("/", 1); if (name == String(p_identifier)) { - String path = GlobalConfig::get_singleton()->get(s); + String path = ProjectSettings::get_singleton()->get(s); if (path.begins_with("*")) { String script = path.substr(1, path.length()); @@ -1256,7 +1276,7 @@ static void _find_identifiers_in_class(GDCompletionContext &context, bool p_stat } } List<MethodInfo> methods; - ClassDB::get_method_list(type, &methods); + ClassDB::get_method_list(type, &methods, false, true); for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { if (E->get().name.begins_with("_")) continue; @@ -1314,8 +1334,8 @@ static void _find_identifiers(GDCompletionContext &context, int p_line, bool p_o static const char *_type_names[Variant::VARIANT_MAX] = { "null", "bool", "int", "float", "String", "Vector2", "Rect2", "Vector3", "Transform2D", "Plane", "Quat", "AABB", "Basis", "Transform", - "Color", "Image", "NodePath", "RID", "Object", "InputEvent", "Dictionary", "Array", "RawArray", "IntArray", "FloatArray", "StringArray", - "Vector2Array", "Vector3Array", "ColorArray" + "Color", "NodePath", "RID", "Object", "Dictionary", "Array", "PoolByteArray", "PoolIntArray", "PoolRealArray", "PoolStringArray", + "PoolVector2Array", "PoolVector3Array", "PoolColorArray" }; for (int i = 0; i < Variant::VARIANT_MAX; i++) { @@ -1324,7 +1344,7 @@ static void _find_identifiers(GDCompletionContext &context, int p_line, bool p_o //autoload singletons List<PropertyInfo> props; - GlobalConfig::get_singleton()->get_property_list(&props); + ProjectSettings::get_singleton()->get_property_list(&props); for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { @@ -1332,7 +1352,7 @@ static void _find_identifiers(GDCompletionContext &context, int p_line, bool p_o if (!s.begins_with("autoload/")) continue; String name = s.get_slice("/", 1); - String path = GlobalConfig::get_singleton()->get(s); + String path = ProjectSettings::get_singleton()->get(s); if (path.begins_with("*")) { result.insert(name); } @@ -1404,25 +1424,21 @@ static void _make_function_hint(const GDParser::FunctionNode *p_func, int p_argi arghint += ")"; } -static void _find_type_arguments(GDCompletionContext &context, 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; - GlobalConfig::get_singleton()->get_property_list(&pinfo); +void get_directory_contents(EditorFileSystemDirectory *p_dir, Set<String> &r_list) { - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - const PropertyInfo &pi = E->get(); + for (int i = 0; i < p_dir->get_subdir_count(); i++) { + get_directory_contents(p_dir->get_subdir(i), r_list); + } - if (!pi.name.begins_with("input/")) - continue; + for (int i = 0; i < p_dir->get_file_count(); i++) { + r_list.insert("\"" + p_dir->get_file_path(i) + "\""); + } +} - String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); - result.insert("\"" + name + "\""); - } +static void _find_type_arguments(GDCompletionContext &context, const GDParser::Node *p_node, int p_line, const StringName &p_method, const GDCompletionIdentifier &id, int p_argidx, Set<String> &result, String &arghint) { - } else if (id.type == Variant::OBJECT && id.obj_type != StringName()) { + //print_line("find type arguments?"); + if (id.type == Variant::OBJECT && id.obj_type != StringName()) { MethodBind *m = ClassDB::get_method(id.obj_type, p_method); if (!m) { @@ -1627,7 +1643,7 @@ static void _find_type_arguments(GDCompletionContext &context, const GDParser::N } else { //regular method - if (p_method.operator String() == "connect") { + if (p_method.operator String() == "connect" || (p_method.operator String() == "emit_signal" && p_argidx == 0)) { if (p_argidx == 0) { List<MethodInfo> sigs; @@ -1669,7 +1685,7 @@ static void _find_type_arguments(GDCompletionContext &context, const GDParser::N if (p_argidx == 0 && (String(p_method) == "get_node" || String(p_method) == "has_node") && ClassDB::is_parent_class(id.obj_type, "Node")) { List<PropertyInfo> props; - GlobalConfig::get_singleton()->get_property_list(&props); + ProjectSettings::get_singleton()->get_property_list(&props); for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { @@ -1752,6 +1768,10 @@ static void _find_call_arguments(GDCompletionContext &context, const GDParser::N const GDParser::BuiltInFunctionNode *fn = static_cast<const GDParser::BuiltInFunctionNode *>(op->arguments[0]); MethodInfo mi = GDFunctions::get_info(fn->function); + if (mi.name == "load" && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) { + get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), result); + } + arghint = _get_visual_datatype(mi.return_val, false) + " " + GDFunctions::get_func_name(fn->function) + String("("); for (int i = 0; i < mi.arguments.size(); i++) { if (i > 0) @@ -2231,7 +2251,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base } List<MethodInfo> mi; - ClassDB::get_method_list(t.obj_type, &mi); + ClassDB::get_method_list(t.obj_type, &mi, false, true); for (List<MethodInfo>::Element *E = mi.front(); E; E = E->next()) { if (E->get().name.begins_with("_")) @@ -2244,54 +2264,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base } } else { - if (t.type == Variant::INPUT_EVENT) { - - //this is hardcoded otherwise it's not obvious - Set<String> exclude; - - for (int i = 0; i < InputEvent::TYPE_MAX; i++) { - - InputEvent ie; - ie.type = InputEvent::Type(i); - static const char *evnames[] = { - "# Common", - "# Key", - "# MouseMotion", - "# MouseButton", - "# JoypadMotion", - "# JoypadButton", - "# ScreenTouch", - "# ScreenDrag", - "# Action" - }; - - r_options->push_back(evnames[i]); - - Variant v = ie; - - if (i == 0) { - List<MethodInfo> mi; - v.get_method_list(&mi); - for (List<MethodInfo>::Element *E = mi.front(); E; E = E->next()) { - r_options->push_back(E->get().name + "("); - } - } - - List<PropertyInfo> pi; - v.get_property_list(&pi); - - for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) { - - if (i == 0) - exclude.insert(E->get().name); - else if (exclude.has(E->get().name)) - continue; - - r_options->push_back(E->get().name); - } - } - return OK; - } else { + //check InputEvent hint + { if (t.value.get_type() == Variant::NIL) { Variant::CallError ce; t.value = Variant::construct(t.type, NULL, 0, ce); @@ -2373,6 +2347,11 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base } } break; + case GDParser::COMPLETION_RESOURCE_PATH: { + + if (EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths")) + get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options); + } break; } for (Set<String>::Element *E = options.front(); E; E = E->next()) { @@ -2390,8 +2369,29 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base #endif +String GDScriptLanguage::_get_indentation() const { +#ifdef TOOLS_ENABLED + if (SceneTree::get_singleton()->is_editor_hint()) { + bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", 0); + + if (use_space_indentation) { + int indent_size = EDITOR_DEF("text_editor/indent/size", 4); + + String space_indent = ""; + for (int i = 0; i < indent_size; i++) { + space_indent += " "; + } + return space_indent; + } + } +#endif + return "\t"; +} + void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_to_line) const { + String indent = _get_indentation(); + Vector<String> lines = p_code.split("\n"); List<int> indent_stack; @@ -2431,8 +2431,9 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t if (i >= p_from_line) { l = ""; - for (int j = 0; j < indent_stack.size(); j++) - l += "\t"; + for (int j = 0; j < indent_stack.size(); j++) { + l += indent; + } l += st; } else if (i > p_to_line) { @@ -2659,7 +2660,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol //guess in autoloads as singletons List<PropertyInfo> props; - GlobalConfig::get_singleton()->get_property_list(&props); + ProjectSettings::get_singleton()->get_property_list(&props); for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { @@ -2669,7 +2670,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol String name = s.get_slice("/", 1); if (name == String(p_symbol)) { - String path = GlobalConfig::get_singleton()->get(s); + String path = ProjectSettings::get_singleton()->get(s); if (path.begins_with("*")) { String script = path.substr(1, path.length()); diff --git a/modules/gdscript/gd_function.cpp b/modules/gdscript/gd_function.cpp index 0e7ba981f..13a7480a3 100644 --- a/modules/gdscript/gd_function.cpp +++ b/modules/gdscript/gd_function.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -357,12 +358,12 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a if (a->get_type() != Variant::OBJECT || a->operator Object *() == NULL) { - err_text = "Left operand of 'extends' is not an instance of anything."; + err_text = "Left operand of 'is' is not an instance of anything."; break; } if (b->get_type() != Variant::OBJECT || b->operator Object *() == NULL) { - err_text = "Right operand of 'extends' is not a class."; + err_text = "Right operand of 'is' is not a class."; break; } #endif @@ -400,7 +401,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a if (!nc) { - err_text = "Right operand of 'extends' is not a class (type: '" + obj_B->get_class() + "')."; + err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "')."; break; } @@ -887,8 +888,8 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a gdfs->state._class = _class; gdfs->state.ip = ip + ipofs; gdfs->state.line = line; - gdfs->state.instance_id = (p_instance && p_instance->get_owner()) ? p_instance->get_owner()->get_instance_ID() : 0; - gdfs->state.script_id = _class->get_instance_ID(); + gdfs->state.instance_id = (p_instance && p_instance->get_owner()) ? p_instance->get_owner()->get_instance_id() : 0; + gdfs->state.script_id = _class->get_instance_id(); //gdfs->state.result_pos=ip+ipofs-1; gdfs->state.defarg = defarg; gdfs->state.instance = p_instance; @@ -1232,10 +1233,10 @@ int GDFunction::get_default_argument_count() const { return default_arguments.size(); } -int GDFunction::get_default_argument_addr(int p_arg) const { +int GDFunction::get_default_argument_addr(int p_idx) const { - ERR_FAIL_INDEX_V(p_arg, default_arguments.size(), -1); - return default_arguments[p_arg]; + ERR_FAIL_INDEX_V(p_idx, default_arguments.size(), -1); + return default_arguments[p_idx]; } StringName GDFunction::get_name() const { @@ -1427,14 +1428,42 @@ Variant GDFunctionState::_signal_callback(const Variant **p_args, int p_argcount state.result = arg; Variant ret = function->call(NULL, NULL, 0, r_error, &state); + + bool completed = true; + + // If the return value is a GDFunctionState reference, + // then the function did yield again after resuming. + if (ret.is_ref()) { + GDFunctionState *gdfs = ret.operator Object *()->cast_to<GDFunctionState>(); + if (gdfs && gdfs->function == function) + completed = false; + } + function = NULL; //cleaned up; state.result = Variant(); + + if (completed) { + emit_signal("completed", ret); + } + return ret; } -bool GDFunctionState::is_valid() const { +bool GDFunctionState::is_valid(bool p_extended_check) const { + + if (function == NULL) + return false; + + if (p_extended_check) { + //class instance gone? + if (state.instance_id && !ObjectDB::get_instance(state.instance_id)) + return false; + //script gone? + if (state.script_id && !ObjectDB::get_instance(state.script_id)) + return false; + } - return function != NULL; + return true; } Variant GDFunctionState::resume(const Variant &p_arg) { @@ -1455,16 +1484,34 @@ Variant GDFunctionState::resume(const Variant &p_arg) { state.result = p_arg; Variant::CallError err; Variant ret = function->call(NULL, NULL, 0, err, &state); + + bool completed = true; + + // If the return value is a GDFunctionState reference, + // then the function did yield again after resuming. + if (ret.is_ref()) { + GDFunctionState *gdfs = ret.operator Object *()->cast_to<GDFunctionState>(); + if (gdfs && gdfs->function == function) + completed = false; + } + function = NULL; //cleaned up; state.result = Variant(); + + if (completed) { + emit_signal("completed", ret); + } + return ret; } void GDFunctionState::_bind_methods() { - ClassDB::bind_method(D_METHOD("resume:Variant", "arg"), &GDFunctionState::resume, DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("is_valid"), &GDFunctionState::is_valid); + ClassDB::bind_method(D_METHOD("resume", "arg"), &GDFunctionState::resume, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("is_valid", "extended_check"), &GDFunctionState::is_valid, DEFVAL(false)); ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &GDFunctionState::_signal_callback, MethodInfo("_signal_callback")); + + ADD_SIGNAL(MethodInfo("completed", PropertyInfo(Variant::NIL, "result"))); } GDFunctionState::GDFunctionState() { diff --git a/modules/gdscript/gd_function.h b/modules/gdscript/gd_function.h index 321b3b6cf..6d20b1977 100644 --- a/modules/gdscript/gd_function.h +++ b/modules/gdscript/gd_function.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -236,7 +237,7 @@ protected: static void _bind_methods(); public: - bool is_valid() const; + bool is_valid(bool p_extended_check = false) const; Variant resume(const Variant &p_arg = Variant()); GDFunctionState(); ~GDFunctionState(); diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp index fa92f0a19..209bdadd6 100644 --- a/modules/gdscript/gd_functions.cpp +++ b/modules/gdscript/gd_functions.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -112,6 +113,7 @@ const char *GDFunctions::get_func_name(Function p_func) { "ColorN", "print_stack", "instance_from_id", + "len", }; return _names[p_func]; @@ -1153,6 +1155,62 @@ void GDFunctions::call(Function p_func, const Variant **p_args, int p_arg_count, r_ret = ObjectDB::get_instance(id); } break; + case LEN: { + + VALIDATE_ARG_COUNT(1); + switch (p_args[0]->get_type()) { + case Variant::DICTIONARY: { + Dictionary d = *p_args[0]; + r_ret = d.size(); + } break; + case Variant::ARRAY: { + Array d = *p_args[0]; + r_ret = d.size(); + } break; + case Variant::POOL_BYTE_ARRAY: { + PoolVector<uint8_t> d = *p_args[0]; + r_ret = d.size(); + + } break; + case Variant::POOL_INT_ARRAY: { + PoolVector<int> d = *p_args[0]; + r_ret = d.size(); + } break; + case Variant::POOL_REAL_ARRAY: { + + PoolVector<real_t> d = *p_args[0]; + r_ret = d.size(); + } break; + case Variant::POOL_STRING_ARRAY: { + PoolVector<String> d = *p_args[0]; + r_ret = d.size(); + + } break; + case Variant::POOL_VECTOR2_ARRAY: { + PoolVector<Vector2> d = *p_args[0]; + r_ret = d.size(); + + } break; + case Variant::POOL_VECTOR3_ARRAY: { + + PoolVector<Vector3> d = *p_args[0]; + r_ret = d.size(); + } break; + case Variant::POOL_COLOR_ARRAY: { + + PoolVector<Color> d = *p_args[0]; + r_ret = d.size(); + } break; + default: { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + r_ret = Variant(); + r_ret = RTR("Object can't provide a length."); + } + } + + } break; case FUNC_MAX: { ERR_FAIL(); @@ -1209,6 +1267,7 @@ bool GDFunctions::is_deterministic(Function p_func) { case TEXT_CHAR: case TEXT_STR: case COLOR8: + case LEN: // enable for debug only, otherwise not desirable - case GEN_RANGE: return true; default: @@ -1586,13 +1645,13 @@ MethodInfo GDFunctions::get_info(Function p_func) { } break; case TO_JSON: { - MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var:Variant")); + MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var")); mi.return_val.type = Variant::STRING; return mi; } break; case HASH: { - MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var:Variant")); + MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var")); mi.return_val.type = Variant::INT; return mi; } break; @@ -1620,6 +1679,11 @@ MethodInfo GDFunctions::get_info(Function p_func) { mi.return_val.type = Variant::OBJECT; return mi; } break; + case LEN: { + MethodInfo mi("len", PropertyInfo(Variant::NIL, "var")); + mi.return_val.type = Variant::INT; + return mi; + } break; case FUNC_MAX: { diff --git a/modules/gdscript/gd_functions.h b/modules/gdscript/gd_functions.h index cde1a9210..93cb52411 100644 --- a/modules/gdscript/gd_functions.h +++ b/modules/gdscript/gd_functions.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -104,6 +105,7 @@ public: COLORN, PRINT_STACK, INSTANCE_FROM_ID, + LEN, FUNC_MAX }; diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index 1540bb51f..9023fd4bf 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -184,8 +185,8 @@ void GDParser::_make_completable_call(int p_arg) { bool GDParser::_get_completable_identifier(CompletionType p_type, StringName &identifier) { identifier = StringName(); - if (tokenizer->get_token() == GDTokenizer::TK_IDENTIFIER) { - identifier = tokenizer->get_token_identifier(); + if (tokenizer->is_token_literal()) { + identifier = tokenizer->get_token_literal(); tokenizer->advance(); } if (tokenizer->get_token() == GDTokenizer::TK_CURSOR) { @@ -200,8 +201,8 @@ bool GDParser::_get_completable_identifier(CompletionType p_type, StringName &id completion_ident_is_call = false; tokenizer->advance(); - if (tokenizer->get_token() == GDTokenizer::TK_IDENTIFIER) { - identifier = identifier.operator String() + tokenizer->get_token_identifier().operator String(); + if (tokenizer->is_token_literal()) { + identifier = identifier.operator String() + tokenizer->get_token_literal().operator String(); tokenizer->advance(); } @@ -295,17 +296,6 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool need_identifier = false; } break; - case GDTokenizer::TK_IDENTIFIER: { - if (!need_identifier) { - done = true; - break; - } - - path += String(tokenizer->get_token_identifier()); - tokenizer->advance(); - need_identifier = false; - - } break; case GDTokenizer::TK_OP_DIV: { if (need_identifier) { @@ -319,7 +309,15 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool } break; default: { - done = true; + // Instead of checking for TK_IDENTIFIER, we check with is_token_literal, as this allows us to use match/sync/etc. as a name + if (need_identifier && tokenizer->is_token_literal()) { + path += String(tokenizer->get_token_literal()); + tokenizer->advance(); + need_identifier = false; + } else { + done = true; + } + break; } } @@ -388,6 +386,19 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool } tokenizer->advance(); + if (tokenizer->get_token() == GDTokenizer::TK_CURSOR) { + completion_cursor = StringName(); + completion_node = p_parent; + completion_type = COMPLETION_RESOURCE_PATH; + completion_class = current_class; + completion_function = current_function; + completion_line = tokenizer->get_token_line(); + completion_block = current_block; + completion_argument = 0; + completion_found = true; + tokenizer->advance(); + } + String path; bool found_constant = false; bool valid = false; @@ -459,10 +470,10 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool _set_error("Expected ')' after 'preload' path"); return NULL; } + tokenizer->advance(); ConstantNode *constant = alloc_node<ConstantNode>(); constant->value = res; - tokenizer->advance(); expr = constant; } else if (tokenizer->get_token() == GDTokenizer::TK_PR_YIELD) { @@ -571,7 +582,8 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool cn->value = Variant::get_numeric_constant_value(bi_type, identifier); expr = cn; - } else if (tokenizer->get_token(1) == GDTokenizer::TK_PARENTHESIS_OPEN && (tokenizer->get_token() == GDTokenizer::TK_BUILT_IN_TYPE || tokenizer->get_token() == GDTokenizer::TK_IDENTIFIER || tokenizer->get_token() == GDTokenizer::TK_BUILT_IN_FUNC)) { + } else if (tokenizer->get_token(1) == GDTokenizer::TK_PARENTHESIS_OPEN && tokenizer->is_token_literal()) { + // We check with is_token_literal, as this allows us to use match/sync/etc. as a name //function or constructor OperatorNode *op = alloc_node<OperatorNode>(); @@ -613,7 +625,8 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool expr = op; - } else if (tokenizer->get_token() == GDTokenizer::TK_IDENTIFIER) { + } else if (tokenizer->is_token_literal(0, true)) { + // We check with is_token_literal, as this allows us to use match/sync/etc. as a name //identifier (reference) const ClassNode *cln = current_class; @@ -813,10 +826,11 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool if (expecting == DICT_EXPECT_KEY) { - if (tokenizer->get_token() == GDTokenizer::TK_IDENTIFIER && tokenizer->get_token(1) == GDTokenizer::TK_OP_ASSIGN) { + if (tokenizer->is_token_literal() && tokenizer->get_token(1) == GDTokenizer::TK_OP_ASSIGN) { + // We check with is_token_literal, as this allows us to use match/sync/etc. as a name //lua style identifier, easier to write ConstantNode *cn = alloc_node<ConstantNode>(); - cn->value = tokenizer->get_token_identifier(); + cn->value = tokenizer->get_token_literal(); key = cn; tokenizer->advance(2); expecting = DICT_EXPECT_VALUE; @@ -856,7 +870,8 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool expr = dict; - } else if (tokenizer->get_token() == GDTokenizer::TK_PERIOD && (tokenizer->get_token(1) == GDTokenizer::TK_IDENTIFIER || tokenizer->get_token(1) == GDTokenizer::TK_CURSOR) && tokenizer->get_token(2) == GDTokenizer::TK_PARENTHESIS_OPEN) { + } else if (tokenizer->get_token() == GDTokenizer::TK_PERIOD && (tokenizer->is_token_literal(1) || tokenizer->get_token(1) == GDTokenizer::TK_CURSOR) && tokenizer->get_token(2) == GDTokenizer::TK_PARENTHESIS_OPEN) { + // We check with is_token_literal, as this allows us to use match/sync/etc. as a name // parent call tokenizer->advance(); //goto identifier @@ -908,7 +923,8 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool //indexing using "." - if (tokenizer->get_token(1) != GDTokenizer::TK_CURSOR && tokenizer->get_token(1) != GDTokenizer::TK_IDENTIFIER && tokenizer->get_token(1) != GDTokenizer::TK_BUILT_IN_FUNC) { + if (tokenizer->get_token(1) != GDTokenizer::TK_CURSOR && !tokenizer->is_token_literal(1)) { + // We check with is_token_literal, as this allows us to use match/sync/etc. as a name _set_error("Expected identifier as member"); return NULL; } else if (tokenizer->get_token(2) == GDTokenizer::TK_PARENTHESIS_OPEN) { @@ -1063,7 +1079,7 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool case GDTokenizer::TK_OP_BIT_AND: op = OperatorNode::OP_BIT_AND; break; case GDTokenizer::TK_OP_BIT_OR: op = OperatorNode::OP_BIT_OR; break; case GDTokenizer::TK_OP_BIT_XOR: op = OperatorNode::OP_BIT_XOR; break; - case GDTokenizer::TK_PR_EXTENDS: op = OperatorNode::OP_EXTENDS; break; + case GDTokenizer::TK_PR_IS: op = OperatorNode::OP_IS; 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; @@ -1103,7 +1119,7 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool switch (expression[i].op) { - case OperatorNode::OP_EXTENDS: + case OperatorNode::OP_IS: priority = -1; break; //before anything @@ -1406,7 +1422,7 @@ GDParser::Node *GDParser::_reduce_expression(Node *p_node, bool p_to_const) { } } - if (op->op == OperatorNode::OP_EXTENDS) { + if (op->op == OperatorNode::OP_IS) { //nothing much return op; } @@ -2264,6 +2280,7 @@ void GDParser::_parse_block(BlockNode *p_block, bool p_static) { if (!is_first_line && tab_level.back()->prev() && tab_level.back()->prev()->get() == indent_level) { // pythonic single-line expression, don't parse future lines tab_level.pop_back(); + p_block->end_line = tokenizer->get_token_line(); return; } is_first_line = false; @@ -2326,12 +2343,12 @@ void GDParser::_parse_block(BlockNode *p_block, bool p_static) { //variale declaration and (eventual) initialization tokenizer->advance(); - if (tokenizer->get_token() != GDTokenizer::TK_IDENTIFIER) { + if (!tokenizer->is_token_literal(0, true)) { _set_error("Expected identifier for local variable name."); return; } - StringName n = tokenizer->get_token_identifier(); + StringName n = tokenizer->get_token_literal(); tokenizer->advance(); if (current_function) { for (int i = 0; i < current_function->arguments.size(); i++) { @@ -2352,8 +2369,7 @@ void GDParser::_parse_block(BlockNode *p_block, bool p_static) { check_block = check_block->parent_block; } - p_block->variables.push_back(n); //line? - p_block->variable_lines.push_back(tokenizer->get_token_line()); + int var_line = tokenizer->get_token_line(); //must know when the local variable is declared LocalVarNode *lv = alloc_node<LocalVarNode>(); @@ -2383,6 +2399,10 @@ void GDParser::_parse_block(BlockNode *p_block, bool p_static) { c->value = Variant(); assigned = c; } + //must be added later, to avoid self-referencing. + p_block->variables.push_back(n); //line? + p_block->variable_lines.push_back(var_line); + IdentifierNode *id = alloc_node<IdentifierNode>(); id->name = n; @@ -2420,7 +2440,7 @@ void GDParser::_parse_block(BlockNode *p_block, bool p_static) { p_block->sub_blocks.push_back(cf_if->body); if (!_enter_indent_block(cf_if->body)) { - _set_error("Expected intended block after 'if'"); + _set_error("Expected indented block after 'if'"); p_block->end_line = tokenizer->get_token_line(); return; } @@ -2435,9 +2455,8 @@ void GDParser::_parse_block(BlockNode *p_block, bool p_static) { while (true) { - while (tokenizer->get_token() == GDTokenizer::TK_NEWLINE) { - tokenizer->advance(); - } + while (tokenizer->get_token() == GDTokenizer::TK_NEWLINE && _parse_newline()) + ; if (tab_level.back()->get() < indent_level) { //not at current indent level p_block->end_line = tokenizer->get_token_line(); @@ -2557,7 +2576,7 @@ void GDParser::_parse_block(BlockNode *p_block, bool p_static) { tokenizer->advance(); - if (tokenizer->get_token() != GDTokenizer::TK_IDENTIFIER) { + if (!tokenizer->is_token_literal(0, true)) { _set_error("identifier expected after 'for'"); } @@ -2612,7 +2631,7 @@ void GDParser::_parse_block(BlockNode *p_block, bool p_static) { ConstantNode *cn = alloc_node<ConstantNode>(); switch (args.size()) { - case 1: cn->value = constants[0]; break; + case 1: cn->value = (int)constants[0]; break; case 2: cn->value = Vector2(constants[0], constants[1]); break; case 3: cn->value = Vector3(constants[0], constants[1], constants[2]); break; } @@ -2625,7 +2644,7 @@ void GDParser::_parse_block(BlockNode *p_block, bool p_static) { on->arguments.push_back(tn); switch (args.size()) { - case 1: tn->vtype = Variant::REAL; break; + case 1: tn->vtype = Variant::INT; break; case 2: tn->vtype = Variant::VECTOR2; break; case 3: tn->vtype = Variant::VECTOR3; break; } @@ -3094,7 +3113,7 @@ void GDParser::_parse_class(ClassNode *p_class) { tokenizer->advance(); //var before the identifier is allowed } - if (tokenizer->get_token() != GDTokenizer::TK_IDENTIFIER) { + if (!tokenizer->is_token_literal(0, true)) { _set_error("Expected identifier for argument."); return; @@ -3246,7 +3265,7 @@ void GDParser::_parse_class(ClassNode *p_class) { case GDTokenizer::TK_PR_SIGNAL: { tokenizer->advance(); - if (tokenizer->get_token() != GDTokenizer::TK_IDENTIFIER) { + if (!tokenizer->is_token_literal()) { _set_error("Expected identifier after 'signal'."); return; } @@ -3268,7 +3287,7 @@ void GDParser::_parse_class(ClassNode *p_class) { break; } - if (tokenizer->get_token() != GDTokenizer::TK_IDENTIFIER) { + if (!tokenizer->is_token_literal(0, true)) { _set_error("Expected identifier in signal argument."); return; } @@ -3833,13 +3852,13 @@ void GDParser::_parse_class(ClassNode *p_class) { bool onready = tokenizer->get_token(-1) == GDTokenizer::TK_PR_ONREADY; tokenizer->advance(); - if (tokenizer->get_token() != GDTokenizer::TK_IDENTIFIER) { + if (!tokenizer->is_token_literal(0, true)) { _set_error("Expected identifier for member variable name."); return; } - member.identifier = tokenizer->get_token_identifier(); + member.identifier = tokenizer->get_token_literal(); member.expression = NULL; member._export.name = member.identifier; member.line = tokenizer->get_token_line(); @@ -3965,11 +3984,11 @@ void GDParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() != GDTokenizer::TK_COMMA) { //just comma means using only getter - if (tokenizer->get_token() != GDTokenizer::TK_IDENTIFIER) { - _set_error("Expected identifier for setter function after 'notify'."); + if (!tokenizer->is_token_literal()) { + _set_error("Expected identifier for setter function after 'setget'."); } - member.setter = tokenizer->get_token_identifier(); + member.setter = tokenizer->get_token_literal(); tokenizer->advance(); } @@ -3978,11 +3997,11 @@ void GDParser::_parse_class(ClassNode *p_class) { //there is a getter tokenizer->advance(); - if (tokenizer->get_token() != GDTokenizer::TK_IDENTIFIER) { + if (!tokenizer->is_token_literal()) { _set_error("Expected identifier for getter function after ','."); } - member.getter = tokenizer->get_token_identifier(); + member.getter = tokenizer->get_token_literal(); tokenizer->advance(); } } @@ -4000,13 +4019,13 @@ void GDParser::_parse_class(ClassNode *p_class) { ClassNode::Constant constant; tokenizer->advance(); - if (tokenizer->get_token() != GDTokenizer::TK_IDENTIFIER) { + if (!tokenizer->is_token_literal(0, true)) { _set_error("Expected name (identifier) for constant."); return; } - constant.identifier = tokenizer->get_token_identifier(); + constant.identifier = tokenizer->get_token_literal(); tokenizer->advance(); if (tokenizer->get_token() != GDTokenizer::TK_OP_ASSIGN) { @@ -4047,8 +4066,8 @@ void GDParser::_parse_class(ClassNode *p_class) { Dictionary enum_dict; tokenizer->advance(); - if (tokenizer->get_token() == GDTokenizer::TK_IDENTIFIER) { - enum_name = tokenizer->get_token_identifier(); + if (tokenizer->is_token_literal(0, true)) { + enum_name = tokenizer->get_token_literal(); tokenizer->advance(); } if (tokenizer->get_token() != GDTokenizer::TK_CURLY_BRACKET_OPEN) { @@ -4065,7 +4084,7 @@ void GDParser::_parse_class(ClassNode *p_class) { tokenizer->advance(); break; // End of enum - } else if (tokenizer->get_token() != GDTokenizer::TK_IDENTIFIER) { + } else if (!tokenizer->is_token_literal(0, true)) { if (tokenizer->get_token() == GDTokenizer::TK_EOF) { _set_error("Unexpected end of file."); @@ -4074,10 +4093,10 @@ void GDParser::_parse_class(ClassNode *p_class) { } return; - } else { // tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER + } else { // tokenizer->is_token_literal(0, true) ClassNode::Constant constant; - constant.identifier = tokenizer->get_token_identifier(); + constant.identifier = tokenizer->get_token_literal(); tokenizer->advance(); diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h index 8d2c13681..3ad346662 100644 --- a/modules/gdscript/gd_parser.h +++ b/modules/gdscript/gd_parser.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -215,7 +216,7 @@ public: OP_CALL, OP_PARENT_CALL, OP_YIELD, - OP_EXTENDS, + OP_IS, //indexing operator OP_INDEX, OP_INDEX_NAMED, @@ -436,6 +437,7 @@ public: COMPLETION_PARENT_FUNCTION, COMPLETION_METHOD, COMPLETION_CALL_ARGUMENTS, + COMPLETION_RESOURCE_PATH, COMPLETION_INDEX, COMPLETION_VIRTUAL_FUNC, COMPLETION_YIELD, diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index 9aafe41a0..2d06c0f5d 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,11 +29,11 @@ /*************************************************************************/ #include "gd_script.h" #include "gd_compiler.h" -#include "global_config.h" #include "global_constants.h" #include "io/file_access_encrypted.h" #include "os/file_access.h" #include "os/os.h" +#include "project_settings.h" /////////////////////////// @@ -478,7 +479,7 @@ bool GDScript::_update_exports() { const GDParser::ClassNode *c = static_cast<const GDParser::ClassNode *>(root); if (base_cache.is_valid()) { - base_cache->inheriters_cache.erase(get_instance_ID()); + base_cache->inheriters_cache.erase(get_instance_id()); base_cache = Ref<GDScript>(); } @@ -504,7 +505,7 @@ bool GDScript::_update_exports() { //print_line("parent is: "+bf->get_path()); base_cache = bf; - bf->inheriters_cache.insert(get_instance_ID()); + bf->inheriters_cache.insert(get_instance_id()); //bf->_update_exports(p_instances,true,false); } @@ -614,6 +615,11 @@ Error GDScript::reload(bool p_keep_state) { if (basedir != "") basedir = basedir.get_base_dir(); + if (basedir != "" && basedir.find("res://") == -1 && basedir.find("user://") == -1) { + //loading a template, don't parse + return OK; + } + valid = false; GDParser parser; Error err = parser.parse(source, basedir, false, path); @@ -1448,9 +1454,9 @@ void GDScriptLanguage::init() { //populate singletons - List<GlobalConfig::Singleton> singletons; - GlobalConfig::get_singleton()->get_singletons(&singletons); - for (List<GlobalConfig::Singleton>::Element *E = singletons.front(); E; E = E->next()) { + List<ProjectSettings::Singleton> singletons; + ProjectSettings::get_singleton()->get_singletons(&singletons); + for (List<ProjectSettings::Singleton>::Element *E = singletons.front(); E; E = E->next()) { _add_global(E->get().name, E->get().ptr); } @@ -1687,7 +1693,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so if (obj->get_script_instance()) { obj->get_script_instance()->get_property_state(state); - map[obj->get_instance_ID()] = state; + map[obj->get_instance_id()] = state; obj->set_script(RefPtr()); } } @@ -1696,15 +1702,18 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so #ifdef TOOLS_ENABLED while (E->get()->placeholders.size()) { - Object *obj = E->get()->placeholders.front()->get()->get_owner(); + //save instance info List<Pair<StringName, Variant> > state; if (obj->get_script_instance()) { obj->get_script_instance()->get_property_state(state); - map[obj->get_instance_ID()] = state; + map[obj->get_instance_id()] = state; obj->set_script(RefPtr()); + } else { + // no instance found. Let's remove it so we don't loop forever + E->get()->placeholders.erase(E->get()->placeholders.front()->get()); } } #endif @@ -1734,8 +1743,8 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so obj->set_script(scr.get_ref_ptr()); if (!obj->get_script_instance()) { //failed, save reload state for next time if not saved - if (!scr->pending_reload_state.has(obj->get_instance_ID())) { - scr->pending_reload_state[obj->get_instance_ID()] = F->get(); + if (!scr->pending_reload_state.has(obj->get_instance_id())) { + scr->pending_reload_state[obj->get_instance_id()] = F->get(); } continue; } @@ -1744,7 +1753,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so obj->get_script_instance()->set(G->get().first, G->get().second); } - scr->pending_reload_state.erase(obj->get_instance_ID()); //as it reloaded, remove pending state + scr->pending_reload_state.erase(obj->get_instance_id()); //as it reloaded, remove pending state } //if instance states were saved, set them! @@ -1808,6 +1817,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "breakpoint", "class", "extends", + "is", "func", "preload", "setget", @@ -1875,7 +1885,7 @@ GDScriptLanguage::GDScriptLanguage() { script_frame_time = 0; _debug_call_stack_pos = 0; - int dmcs = GLOBAL_DEF("debug/script/max_call_stack", 1024); + int dmcs = GLOBAL_DEF("debug/settings/gdscript/max_call_stack", 1024); if (ScriptDebugger::get_singleton()) { //debugging enabled! diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index d64cc9798..17e7b0bc0 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -297,7 +298,7 @@ public: _FORCE_INLINE_ void enter_function(GDInstance *p_instance, GDFunction *p_function, Variant *p_stack, int *p_ip, int *p_line) { - if (Thread::get_main_ID() != Thread::get_caller_ID()) + if (Thread::get_main_id() != Thread::get_caller_id()) return; //no support for other threads than main for now if (ScriptDebugger::get_singleton()->get_lines_left() > 0 && ScriptDebugger::get_singleton()->get_depth() >= 0) @@ -320,7 +321,7 @@ public: _FORCE_INLINE_ void exit_function() { - if (Thread::get_main_ID() != Thread::get_caller_ID()) + if (Thread::get_main_id() != Thread::get_caller_id()) return; //no support for other threads than main for now if (ScriptDebugger::get_singleton()->get_lines_left() > 0 && ScriptDebugger::get_singleton()->get_depth() >= 0) @@ -337,7 +338,7 @@ public: } virtual Vector<StackInfo> debug_get_current_stack_info() { - if (Thread::get_main_ID() != Thread::get_caller_ID()) + if (Thread::get_main_id() != Thread::get_caller_id()) return Vector<StackInfo>(); Vector<StackInfo> csi; @@ -380,15 +381,20 @@ public: virtual void get_comment_delimiters(List<String> *p_delimiters) const; virtual void get_string_delimiters(List<String> *p_delimiters) const; virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; + virtual bool is_using_templates(); + virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const; virtual Script *create_script() const; virtual bool has_named_classes() const; + virtual bool can_inherit_from_file() { return true; } 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 PoolStringArray &p_args) const; + virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return OK; } 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 String _get_indentation() const; 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 981924191..580304618 100644 --- a/modules/gdscript/gd_tokenizer.cpp +++ b/modules/gdscript/gd_tokenizer.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -89,6 +90,7 @@ const char *GDTokenizer::token_names[TK_MAX] = { "func", "class", "extends", + "is", "onready", "tool", "static", @@ -117,6 +119,7 @@ const char *GDTokenizer::token_names[TK_MAX] = { "'.'", "'?'", "':'", + "'$'", "'\\n'", "PI", "_", @@ -127,12 +130,222 @@ const char *GDTokenizer::token_names[TK_MAX] = { "Cursor" }; +struct _bit { + Variant::Type type; + const char *text; +}; +//built in types + +static const _bit _type_list[] = { + //types + { Variant::BOOL, "bool" }, + { Variant::INT, "int" }, + { Variant::REAL, "float" }, + { Variant::STRING, "String" }, + { Variant::VECTOR2, "Vector2" }, + { Variant::RECT2, "Rect2" }, + { Variant::TRANSFORM2D, "Transform2D" }, + { Variant::VECTOR3, "Vector3" }, + { Variant::RECT3, "Rect3" }, + { Variant::PLANE, "Plane" }, + { Variant::QUAT, "Quat" }, + { Variant::BASIS, "Basis" }, + { Variant::TRANSFORM, "Transform" }, + { Variant::COLOR, "Color" }, + { Variant::_RID, "RID" }, + { Variant::OBJECT, "Object" }, + { Variant::NODE_PATH, "NodePath" }, + { Variant::DICTIONARY, "Dictionary" }, + { Variant::ARRAY, "Array" }, + { Variant::POOL_BYTE_ARRAY, "PoolByteArray" }, + { Variant::POOL_INT_ARRAY, "PoolIntArray" }, + { Variant::POOL_REAL_ARRAY, "PoolRealArray" }, + { Variant::POOL_STRING_ARRAY, "PoolStringArray" }, + { Variant::POOL_VECTOR2_ARRAY, "PoolVector2Array" }, + { Variant::POOL_VECTOR3_ARRAY, "PoolVector3Array" }, + { Variant::POOL_COLOR_ARRAY, "PoolColorArray" }, + { Variant::VARIANT_MAX, NULL }, +}; + +struct _kws { + GDTokenizer::Token token; + const char *text; +}; + +static const _kws _keyword_list[] = { + //ops + { GDTokenizer::TK_OP_IN, "in" }, + { GDTokenizer::TK_OP_NOT, "not" }, + { GDTokenizer::TK_OP_OR, "or" }, + { GDTokenizer::TK_OP_AND, "and" }, + //func + { GDTokenizer::TK_PR_FUNCTION, "func" }, + { GDTokenizer::TK_PR_CLASS, "class" }, + { GDTokenizer::TK_PR_EXTENDS, "extends" }, + { GDTokenizer::TK_PR_IS, "is" }, + { GDTokenizer::TK_PR_ONREADY, "onready" }, + { GDTokenizer::TK_PR_TOOL, "tool" }, + { GDTokenizer::TK_PR_STATIC, "static" }, + { GDTokenizer::TK_PR_EXPORT, "export" }, + { GDTokenizer::TK_PR_SETGET, "setget" }, + { GDTokenizer::TK_PR_VAR, "var" }, + { GDTokenizer::TK_PR_PRELOAD, "preload" }, + { GDTokenizer::TK_PR_ASSERT, "assert" }, + { GDTokenizer::TK_PR_YIELD, "yield" }, + { GDTokenizer::TK_PR_SIGNAL, "signal" }, + { GDTokenizer::TK_PR_BREAKPOINT, "breakpoint" }, + { GDTokenizer::TK_PR_REMOTE, "remote" }, + { GDTokenizer::TK_PR_MASTER, "master" }, + { GDTokenizer::TK_PR_SLAVE, "slave" }, + { GDTokenizer::TK_PR_SYNC, "sync" }, + { GDTokenizer::TK_PR_CONST, "const" }, + { GDTokenizer::TK_PR_ENUM, "enum" }, + //controlflow + { GDTokenizer::TK_CF_IF, "if" }, + { GDTokenizer::TK_CF_ELIF, "elif" }, + { GDTokenizer::TK_CF_ELSE, "else" }, + { GDTokenizer::TK_CF_FOR, "for" }, + { GDTokenizer::TK_CF_WHILE, "while" }, + { GDTokenizer::TK_CF_DO, "do" }, + { GDTokenizer::TK_CF_SWITCH, "switch" }, + { GDTokenizer::TK_CF_CASE, "case" }, + { GDTokenizer::TK_CF_BREAK, "break" }, + { GDTokenizer::TK_CF_CONTINUE, "continue" }, + { GDTokenizer::TK_CF_RETURN, "return" }, + { GDTokenizer::TK_CF_MATCH, "match" }, + { GDTokenizer::TK_CF_PASS, "pass" }, + { GDTokenizer::TK_SELF, "self" }, + { GDTokenizer::TK_CONST_PI, "PI" }, + { GDTokenizer::TK_WILDCARD, "_" }, + { GDTokenizer::TK_CONST_INF, "INF" }, + { GDTokenizer::TK_CONST_NAN, "NAN" }, + { GDTokenizer::TK_ERROR, NULL } +}; + const char *GDTokenizer::get_token_name(Token p_token) { ERR_FAIL_INDEX_V(p_token, TK_MAX, "<error>"); return token_names[p_token]; } +bool GDTokenizer::is_token_literal(int p_offset, bool variable_safe) const { + switch (get_token(p_offset)) { + // Can always be literal: + case TK_IDENTIFIER: + + case TK_PR_ONREADY: + case TK_PR_TOOL: + case TK_PR_STATIC: + case TK_PR_EXPORT: + case TK_PR_SETGET: + case TK_PR_SIGNAL: + case TK_PR_REMOTE: + case TK_PR_MASTER: + case TK_PR_SLAVE: + case TK_PR_SYNC: + return true; + + // Literal for non-variables only: + case TK_BUILT_IN_TYPE: + case TK_BUILT_IN_FUNC: + + case TK_OP_IN: + //case TK_OP_NOT: + //case TK_OP_OR: + //case TK_OP_AND: + + case TK_PR_CLASS: + case TK_PR_CONST: + case TK_PR_ENUM: + case TK_PR_PRELOAD: + case TK_PR_FUNCTION: + case TK_PR_EXTENDS: + case TK_PR_ASSERT: + case TK_PR_YIELD: + case TK_PR_VAR: + + case TK_CF_IF: + case TK_CF_ELIF: + case TK_CF_ELSE: + case TK_CF_FOR: + case TK_CF_WHILE: + case TK_CF_DO: + case TK_CF_SWITCH: + case TK_CF_CASE: + case TK_CF_BREAK: + case TK_CF_CONTINUE: + case TK_CF_RETURN: + case TK_CF_MATCH: + case TK_CF_PASS: + case TK_SELF: + case TK_CONST_PI: + case TK_WILDCARD: + case TK_CONST_INF: + case TK_CONST_NAN: + case TK_ERROR: + return !variable_safe; + + case TK_CONSTANT: { + switch (get_token_constant(p_offset).get_type()) { + case Variant::NIL: + case Variant::BOOL: + return true; + default: + return false; + } + } + default: + return false; + } +} + +StringName GDTokenizer::get_token_literal(int p_offset) const { + Token token = get_token(p_offset); + switch (token) { + case TK_IDENTIFIER: + return get_token_identifier(p_offset); + case TK_BUILT_IN_TYPE: { + Variant::Type type = get_token_type(p_offset); + int idx = 0; + + while (_type_list[idx].text) { + if (type == _type_list[idx].type) { + return _type_list[idx].text; + } + idx++; + } + } break; // Shouldn't get here, stuff happens + case TK_BUILT_IN_FUNC: + return GDFunctions::get_func_name(get_token_built_in_func(p_offset)); + case TK_CONSTANT: { + const Variant value = get_token_constant(p_offset); + + switch (value.get_type()) { + case Variant::NIL: + return "null"; + case Variant::BOOL: + return value ? "true" : "false"; + default: {} + } + } + case TK_OP_AND: + case TK_OP_OR: + break; // Don't get into default, since they can be non-literal + default: { + int idx = 0; + + while (_keyword_list[idx].text) { + if (token == _keyword_list[idx].token) { + return _keyword_list[idx].text; + } + idx++; + } + } + } + ERR_EXPLAIN("Failed to get token literal"); + ERR_FAIL_V(""); +} + static bool _is_text_char(CharType c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; @@ -776,53 +989,14 @@ void GDTokenizerText::_advance() { bool found = false; - struct _bit { - Variant::Type type; - const char *text; - }; - //built in types - - static const _bit type_list[] = { - //types - { Variant::BOOL, "bool" }, - { Variant::INT, "int" }, - { Variant::REAL, "float" }, - { Variant::STRING, "String" }, - { Variant::VECTOR2, "Vector2" }, - { Variant::RECT2, "Rect2" }, - { Variant::TRANSFORM2D, "Transform2D" }, - { Variant::VECTOR3, "Vector3" }, - { Variant::RECT3, "Rect3" }, - { Variant::PLANE, "Plane" }, - { Variant::QUAT, "Quat" }, - { Variant::BASIS, "Basis" }, - { Variant::TRANSFORM, "Transform" }, - { Variant::COLOR, "Color" }, - { Variant::IMAGE, "Image" }, - { Variant::_RID, "RID" }, - { Variant::OBJECT, "Object" }, - { Variant::INPUT_EVENT, "InputEvent" }, - { Variant::NODE_PATH, "NodePath" }, - { Variant::DICTIONARY, "Dictionary" }, - { Variant::ARRAY, "Array" }, - { Variant::POOL_BYTE_ARRAY, "PoolByteArray" }, - { Variant::POOL_INT_ARRAY, "PoolIntArray" }, - { Variant::POOL_REAL_ARRAY, "PoolFloatArray" }, - { Variant::POOL_STRING_ARRAY, "PoolStringArray" }, - { Variant::POOL_VECTOR2_ARRAY, "PoolVector2Array" }, - { Variant::POOL_VECTOR3_ARRAY, "PoolVector3Array" }, - { Variant::POOL_COLOR_ARRAY, "PoolColorArray" }, - { Variant::VARIANT_MAX, NULL }, - }; - { int idx = 0; - while (type_list[idx].text) { + while (_type_list[idx].text) { - if (str == type_list[idx].text) { - _make_type(type_list[idx].type); + if (str == _type_list[idx].text) { + _make_type(_type_list[idx].type); found = true; break; } @@ -843,73 +1017,18 @@ void GDTokenizerText::_advance() { break; } } - - //keywor } if (!found) { - - struct _kws { - Token token; - const char *text; - }; - - static const _kws keyword_list[] = { - //ops - { TK_OP_IN, "in" }, - { TK_OP_NOT, "not" }, - { TK_OP_OR, "or" }, - { TK_OP_AND, "and" }, - //func - { TK_PR_FUNCTION, "func" }, - { TK_PR_CLASS, "class" }, - { TK_PR_EXTENDS, "extends" }, - { TK_PR_ONREADY, "onready" }, - { TK_PR_TOOL, "tool" }, - { TK_PR_STATIC, "static" }, - { TK_PR_EXPORT, "export" }, - { TK_PR_SETGET, "setget" }, - { TK_PR_VAR, "var" }, - { TK_PR_PRELOAD, "preload" }, - { TK_PR_ASSERT, "assert" }, - { TK_PR_YIELD, "yield" }, - { TK_PR_SIGNAL, "signal" }, - { TK_PR_BREAKPOINT, "breakpoint" }, - { TK_PR_REMOTE, "remote" }, - { TK_PR_MASTER, "master" }, - { 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" }, - { TK_CF_ELSE, "else" }, - { TK_CF_FOR, "for" }, - { TK_CF_WHILE, "while" }, - { TK_CF_DO, "do" }, - { TK_CF_SWITCH, "switch" }, - { TK_CF_CASE, "case" }, - { TK_CF_BREAK, "break" }, - { TK_CF_CONTINUE, "continue" }, - { TK_CF_RETURN, "return" }, - { TK_CF_MATCH, "match" }, - { TK_CF_PASS, "pass" }, - { TK_SELF, "self" }, - { TK_CONST_PI, "PI" }, - { TK_WILDCARD, "_" }, - { TK_CONST_INF, "INF" }, - { TK_CONST_NAN, "NAN" }, - { TK_ERROR, NULL } - }; + //keyword int idx = 0; found = false; - while (keyword_list[idx].text) { + while (_keyword_list[idx].text) { - if (str == keyword_list[idx].text) { - _make_token(keyword_list[idx].token); + if (str == _keyword_list[idx].text) { + _make_token(_keyword_list[idx].token); found = true; break; } @@ -990,6 +1109,7 @@ const Variant &GDTokenizerText::get_token_constant(int p_offset) const { ERR_FAIL_COND_V(tk_rb[ofs].type != TK_CONSTANT, tk_rb[0].constant); return tk_rb[ofs].constant; } + StringName GDTokenizerText::get_token_identifier(int p_offset) const { ERR_FAIL_COND_V(p_offset <= -MAX_LOOKAHEAD, StringName()); diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h index c1ed8ad92..4e868301a 100644 --- a/modules/gdscript/gd_tokenizer.h +++ b/modules/gdscript/gd_tokenizer.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -95,6 +96,7 @@ public: TK_PR_FUNCTION, TK_PR_CLASS, TK_PR_EXTENDS, + TK_PR_IS, TK_PR_ONREADY, TK_PR_TOOL, TK_PR_STATIC, @@ -147,6 +149,9 @@ protected: public: static const char *get_token_name(Token p_token); + bool is_token_literal(int p_offset = 0, bool variable_safe = false) const; + StringName get_token_literal(int p_offset = 0) const; + virtual const Variant &get_token_constant(int p_offset = 0) const = 0; virtual Token get_token(int p_offset = 0) const = 0; virtual StringName get_token_identifier(int p_offset = 0) const = 0; diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index 5acd412f7..41ea0c2a2 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/gdscript/register_types.h b/modules/gdscript/register_types.h index 5778dfcad..4e969f5bd 100644 --- a/modules/gdscript/register_types.h +++ b/modules/gdscript/register_types.h @@ -6,6 +6,7 @@ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ |
