Merge pull request #19264 from vnen/typed-gdscript-final

Typed GDScript
This commit is contained in:
Rémi Verschelde 2018-07-21 23:13:51 +02:00 committed by GitHub
commit 92415365c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 6645 additions and 2653 deletions

View file

@ -213,7 +213,7 @@ public:
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0;
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {}
virtual bool is_using_templates() { return false; }
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 = 0;
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, Set<int> *r_safe_lines = NULL) const = 0;
virtual String validate_path(const String &p_path) const { return ""; }
virtual Script *create_script() const = 0;
virtual bool has_named_classes() const = 0;

View file

@ -820,7 +820,9 @@ void ConnectionsDock::update_tree() {
if (i > 0)
signaldesc += ", ";
String tname = "var";
if (pi.type != Variant::NIL) {
if (pi.type == Variant::OBJECT && pi.class_name != StringName()) {
tname = pi.class_name.operator String();
} else if (pi.type != Variant::NIL) {
tname = Variant::get_type_name(pi.type);
}
signaldesc += tname + " " + (pi.name == "" ? String("arg " + itos(i)) : pi.name);

View file

@ -364,6 +364,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/highlighting/highlight_all_occurrences", true);
_initial_set("text_editor/highlighting/highlight_current_line", true);
_initial_set("text_editor/highlighting/highlight_type_safe_lines", true);
_initial_set("text_editor/cursor/scroll_past_end_of_file", false);
_initial_set("text_editor/indent/type", 0);
@ -404,6 +405,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/completion/callhint_tooltip_offset", Vector2());
_initial_set("text_editor/files/restore_scripts_on_load", true);
_initial_set("text_editor/completion/complete_file_paths", true);
_initial_set("text_editor/completion/add_type_hints", false);
_initial_set("docks/scene_tree/start_create_dialog_fully_expanded", false);
_initial_set("docks/scene_tree/draw_relationship_lines", false);
@ -592,6 +594,7 @@ void EditorSettings::_load_default_text_editor_theme() {
_initial_set("text_editor/highlighting/completion_font_color", Color::html("aaaaaa"));
_initial_set("text_editor/highlighting/text_color", Color::html("aaaaaa"));
_initial_set("text_editor/highlighting/line_number_color", Color::html("66aaaaaa"));
_initial_set("text_editor/highlighting/safe_line_number_color", Color::html("99aac8aa"));
_initial_set("text_editor/highlighting/caret_color", Color::html("aaaaaa"));
_initial_set("text_editor/highlighting/caret_background_color", Color::html("000000"));
_initial_set("text_editor/highlighting/text_selected_color", Color::html("000000"));

View file

@ -1089,6 +1089,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color completion_font_color = font_color;
const Color text_color = font_color;
const Color line_number_color = dim_color;
const Color safe_line_number_color = dim_color * Color(1, 1.2, 1, 1.5);
const Color caret_color = mono_color;
const Color caret_background_color = mono_color.inverted();
const Color text_selected_color = dark_color_3;
@ -1123,6 +1124,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
setting->set_initial_value("text_editor/highlighting/completion_font_color", completion_font_color, true);
setting->set_initial_value("text_editor/highlighting/text_color", text_color, true);
setting->set_initial_value("text_editor/highlighting/line_number_color", line_number_color, true);
setting->set_initial_value("text_editor/highlighting/safe_line_number_color", safe_line_number_color, true);
setting->set_initial_value("text_editor/highlighting/caret_color", caret_color, true);
setting->set_initial_value("text_editor/highlighting/caret_background_color", caret_background_color, true);
setting->set_initial_value("text_editor/highlighting/text_selected_color", text_selected_color, true);

View file

@ -116,6 +116,7 @@ void ScriptTextEditor::_load_theme_settings() {
Color completion_font_color = EDITOR_GET("text_editor/highlighting/completion_font_color");
Color text_color = EDITOR_GET("text_editor/highlighting/text_color");
Color line_number_color = EDITOR_GET("text_editor/highlighting/line_number_color");
Color safe_line_number_color = EDITOR_GET("text_editor/highlighting/safe_line_number_color");
Color caret_color = EDITOR_GET("text_editor/highlighting/caret_color");
Color caret_background_color = EDITOR_GET("text_editor/highlighting/caret_background_color");
Color text_selected_color = EDITOR_GET("text_editor/highlighting/text_selected_color");
@ -147,6 +148,7 @@ void ScriptTextEditor::_load_theme_settings() {
text_edit->add_color_override("completion_font_color", completion_font_color);
text_edit->add_color_override("font_color", text_color);
text_edit->add_color_override("line_number_color", line_number_color);
text_edit->add_color_override("safe_line_number_color", safe_line_number_color);
text_edit->add_color_override("caret_color", caret_color);
text_edit->add_color_override("caret_background_color", caret_background_color);
text_edit->add_color_override("font_selected_color", text_selected_color);
@ -589,6 +591,7 @@ void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) {
emit_signal("name_changed");
code_editor->update_line_and_column();
call_deferred("_validate_script");
}
void ScriptTextEditor::_validate_script() {
@ -599,8 +602,9 @@ void ScriptTextEditor::_validate_script() {
String text = te->get_text();
List<String> fnc;
Set<int> safe_lines;
if (!script->get_language()->validate(text, line, col, errortxt, script->get_path(), &fnc)) {
if (!script->get_language()->validate(text, line, col, errortxt, script->get_path(), &fnc, &safe_lines)) {
String error_text = "error(" + itos(line) + "," + itos(col) + "): " + errortxt;
code_editor->set_error(error_text);
} else {
@ -621,8 +625,23 @@ void ScriptTextEditor::_validate_script() {
}
line--;
bool highlight_safe = EDITOR_DEF("text_editor/highlighting/highlight_type_safe_lines", true);
bool last_is_safe = false;
for (int i = 0; i < te->get_line_count(); i++) {
te->set_line_as_marked(i, line == i);
if (highlight_safe) {
if (safe_lines.has(i + 1)) {
te->set_line_as_safe(i, true);
last_is_safe = true;
} else if (last_is_safe && (te->is_line_comment(i) || te->get_line(i).strip_edges().empty())) {
te->set_line_as_safe(i, true);
} else {
te->set_line_as_safe(i, false);
last_is_safe = false;
}
} else {
te->set_line_as_safe(i, false);
}
}
emit_signal("name_changed");

View file

@ -193,14 +193,6 @@ static String _parser_expr(const GDScriptParser::Node *p_expr) {
case GDScriptParser::OperatorNode::OP_BIT_INVERT: {
txt = "~" + _parser_expr(c_node->arguments[0]);
} break;
case GDScriptParser::OperatorNode::OP_PREINC: {
} break;
case GDScriptParser::OperatorNode::OP_PREDEC: {
} break;
case GDScriptParser::OperatorNode::OP_INC: {
} break;
case GDScriptParser::OperatorNode::OP_DEC: {
} break;
case GDScriptParser::OperatorNode::OP_IN: {
txt = _parser_expr(c_node->arguments[0]) + " in " + _parser_expr(c_node->arguments[1]);
} break;
@ -455,10 +447,9 @@ static void _parser_show_class(const GDScriptParser::ClassNode *p_class, int p_i
print_line("\n");
}
for (int i = 0; i < p_class->constant_expressions.size(); i++) {
const GDScriptParser::ClassNode::Constant &constant = p_class->constant_expressions[i];
_print_indent(p_indent, "const " + String(constant.identifier) + "=" + _parser_expr(constant.expression));
for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
const GDScriptParser::ClassNode::Constant &constant = E->get();
_print_indent(p_indent, "const " + String(E->key()) + "=" + _parser_expr(constant.expression));
}
for (int i = 0; i < p_class->variables.size(); i++) {

View file

@ -1053,7 +1053,7 @@ Ref<Script> NativeScriptLanguage::get_template(const String &p_class_name, const
s->set_class_name(p_class_name);
return Ref<NativeScript>(s);
}
bool NativeScriptLanguage::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 {
bool NativeScriptLanguage::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, Set<int> *r_safe_lines) const {
return true;
}

View file

@ -295,7 +295,7 @@ 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 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;
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, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;

View file

@ -108,7 +108,7 @@ Ref<Script> PluginScriptLanguage::get_template(const String &p_class_name, const
return script;
}
bool PluginScriptLanguage::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 {
bool PluginScriptLanguage::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, Set<int> *r_safe_lines) const {
PoolStringArray functions;
if (_desc.validate) {
bool ret = _desc.validate(

View file

@ -74,7 +74,7 @@ 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 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 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, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;

View file

@ -75,9 +75,11 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
bool in_keyword = false;
bool in_word = false;
bool in_function_name = false;
bool in_variable_declaration = false;
bool in_member_variable = false;
bool in_node_path = false;
bool is_hex_notation = false;
bool expect_type = false;
Color keyword_color;
Color color;
@ -205,6 +207,8 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
if (str[k] == '(') {
in_function_name = true;
} else if (previous_text == GDScriptTokenizer::get_token_name(GDScriptTokenizer::TK_PR_VAR)) {
in_variable_declaration = true;
}
}
@ -222,6 +226,28 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
if (is_symbol) {
in_function_name = false;
in_member_variable = false;
if (expect_type && str[j] != ' ' && str[j] != '\t' && str[j] != ':') {
expect_type = false;
}
if (j > 0 && str[j] == '>' && str[j - 1] == '-') {
expect_type = true;
}
if (in_variable_declaration || previous_text == "(" || previous_text == ",") {
int k = j;
// Skip space
while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
k++;
}
if (str[k] == ':') {
// has type hint
expect_type = true;
}
}
in_variable_declaration = false;
}
if (!in_node_path && in_region == -1 && str[j] == '$') {
@ -256,6 +282,9 @@ Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_
} else if (is_number) {
next_type = NUMBER;
color = number_color;
} else if (expect_type) {
next_type = TYPE;
color = type_color;
} else {
next_type = IDENTIFIER;
}
@ -330,6 +359,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
function_definition_color = EDITOR_GET("text_editor/highlighting/gdscript/function_definition_color");
node_path_color = EDITOR_GET("text_editor/highlighting/gdscript/node_path_color");
type_color = EDITOR_GET("text_editor/highlighting/base_type_color");
}
SyntaxHighlighter *GDScriptSyntaxHighlighter::create() {

View file

@ -44,7 +44,8 @@ private:
FUNCTION,
KEYWORD,
MEMBER,
IDENTIFIER
IDENTIFIER,
TYPE,
};
// colours
@ -56,6 +57,7 @@ private:
Color number_color;
Color member_color;
Color node_path_color;
Color type_color;
public:
static SyntaxHighlighter *create();

View file

@ -220,16 +220,14 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
for (const Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) {
GDScriptFunction *func = E->get();
MethodInfo mi;
mi.name = E->key();
for (int i = 0; i < E->get()->get_argument_count(); i++) {
PropertyInfo arg;
arg.type = Variant::NIL; //variant
arg.name = E->get()->get_argument_name(i);
mi.arguments.push_back(arg);
for (int i = 0; i < func->get_argument_count(); i++) {
mi.arguments.push_back(func->get_argument_type(i));
}
mi.return_val.name = "Variant";
mi.return_val = func->get_return_type();
p_list->push_back(mi);
}
}
@ -277,16 +275,14 @@ MethodInfo GDScript::get_method_info(const StringName &p_method) const {
if (!E)
return MethodInfo();
GDScriptFunction *func = E->get();
MethodInfo mi;
mi.name = E->key();
for (int i = 0; i < E->get()->get_argument_count(); i++) {
PropertyInfo arg;
arg.type = Variant::NIL; //variant
arg.name = E->get()->get_argument_name(i);
mi.arguments.push_back(arg);
for (int i = 0; i < func->get_argument_count(); i++) {
mi.arguments.push_back(func->get_argument_type(i));
}
mi.return_val.name = "Variant";
mi.return_val = func->get_return_type();
return mi;
}
@ -941,8 +937,12 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
if (err.error == Variant::CallError::CALL_OK) {
return true; //function exists, call was successful
}
} else
} else {
if (!E->get().data_type.is_type(p_value)) {
return false; // Type mismatch
}
members[E->get().index] = p_value;
}
return true;
}
}
@ -1735,7 +1735,9 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"NAN",
"self",
"true",
"void",
// functions
"as",
"assert",
"breakpoint",
"class",
@ -1824,8 +1826,40 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) {
const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree());
if (r_base_type && c->extends_used && c->extends_class.size() == 1) {
*r_base_type = c->extends_class[0]; //todo, should work much better
if (r_base_type) {
GDScriptParser::DataType base_type;
if (c->base_type.has_type) {
base_type = c->base_type;
while (base_type.has_type && base_type.kind != GDScriptParser::DataType::NATIVE) {
switch (base_type.kind) {
case GDScriptParser::DataType::CLASS: {
base_type = base_type.class_type->base_type;
} break;
case GDScriptParser::DataType::GDSCRIPT: {
Ref<GDScript> gds = base_type.script_type;
if (gds.is_valid()) {
base_type.kind = GDScriptParser::DataType::NATIVE;
base_type.native_type = gds->get_instance_base_type();
} else {
base_type = GDScriptParser::DataType();
}
} break;
default: {
base_type = GDScriptParser::DataType();
} break;
}
}
}
if (base_type.has_type) {
*r_base_type = base_type.native_type;
} else {
// Fallback
if (c->extends_used && c->extends_class.size() == 1) {
*r_base_type = c->extends_class[0];
} else if (!c->extends_used) {
*r_base_type = "Reference";
}
}
}
return c->name;
}

View file

@ -64,6 +64,7 @@ class GDScript : public Script {
StringName setter;
StringName getter;
MultiplayerAPI::RPCMode rpc_mode;
GDScriptDataType data_type;
};
friend class GDScriptInstance;
@ -145,8 +146,13 @@ public:
const Map<StringName, Ref<GDScript> > &get_subclasses() const { return subclasses; }
const Map<StringName, Variant> &get_constants() const { return constants; }
const Set<StringName> &get_members() const { return members; }
const GDScriptDataType &get_member_type(const StringName &p_member) const {
ERR_FAIL_COND_V(!member_indices.has(p_member), GDScriptDataType());
return member_indices[p_member].data_type;
}
const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
const Ref<GDScriptNativeClass> &get_native() const { return native; }
const String &get_script_class_name() const { return name; }
virtual bool has_script_signal(const StringName &p_signal) const;
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
@ -391,7 +397,7 @@ public:
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 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, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;

View file

@ -111,23 +111,50 @@ bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptP
return true;
}
/*
int GDScriptCompiler::_parse_subexpression(CodeGen& codegen,const GDScriptParser::Node *p_expression) {
int ret = _parse_expression(codegen,p_expression);
if (ret<0)
return ret;
if (ret&(GDScriptFunction::ADDR_TYPE_STACK<<GDScriptFunction::ADDR_BITS)) {
codegen.stack_level++;
codegen.check_max_stack_level();
//stack was used, keep value
GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const {
if (!p_datatype.has_type) {
return GDScriptDataType();
}
return ret;
GDScriptDataType result;
result.has_type = true;
switch (p_datatype.kind) {
case GDScriptParser::DataType::BUILTIN: {
result.kind = GDScriptDataType::BUILTIN;
result.builtin_type = p_datatype.builtin_type;
} break;
case GDScriptParser::DataType::NATIVE: {
result.kind = GDScriptDataType::NATIVE;
result.native_type = p_datatype.native_type;
} break;
case GDScriptParser::DataType::SCRIPT: {
result.kind = GDScriptDataType::SCRIPT;
result.script_type = p_datatype.script_type;
result.native_type = result.script_type->get_instance_base_type();
}
case GDScriptParser::DataType::GDSCRIPT: {
result.kind = GDScriptDataType::GDSCRIPT;
result.script_type = p_datatype.script_type;
result.native_type = result.script_type->get_instance_base_type();
} break;
case GDScriptParser::DataType::CLASS: {
result.kind = GDScriptDataType::GDSCRIPT;
if (p_datatype.class_type->name == StringName()) {
result.script_type = Ref<GDScript>(main_script);
} else {
result.script_type = class_map[p_datatype.class_type->name];
}
result.native_type = result.script_type->get_instance_base_type();
} break;
default: {
ERR_PRINT("Parser bug: converting unresolved type.");
result.has_type = false;
}
}
return result;
}
*/
int GDScriptCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level) {
@ -263,15 +290,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
owner = owner->_owner;
}
/*
handled in constants now
if (codegen.script->subclasses.has(identifier)) {
//same with a subclass, make it a local constant.
int idx = codegen.get_constant_pos(codegen.script->subclasses[identifier]);
return idx|(GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT<<GDScriptFunction::ADDR_BITS); //make it a local constant (faster access)
}*/
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
@ -429,6 +447,83 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
codegen.alloc_stack(p_stack_level);
return dst_addr;
} break;
case GDScriptParser::Node::TYPE_CAST: {
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
int slevel = p_stack_level;
int src_addr = _parse_expression(codegen, cn->source_node, slevel);
if (src_addr < 0)
return src_addr;
if (src_addr & GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS) {
slevel++;
codegen.alloc_stack(slevel);
}
switch (cn->cast_type.kind) {
case GDScriptParser::DataType::BUILTIN: {
codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
codegen.opcodes.push_back(cn->cast_type.builtin_type);
} break;
case GDScriptParser::DataType::NATIVE: {
int class_idx;
if (GDScriptLanguage::get_singleton()->get_global_map().has(cn->cast_type.native_type)) {
class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cn->cast_type.native_type];
class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
} else {
_set_error("Invalid native class type '" + String(cn->cast_type.native_type) + "'.", cn);
return -1;
}
codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator
codegen.opcodes.push_back(class_idx); // variable type
} break;
case GDScriptParser::DataType::CLASS: {
Variant script;
int idx = -1;
if (cn->cast_type.class_type->name == StringName()) {
script = codegen.script;
} else {
StringName name = cn->cast_type.class_type->name;
if (class_map[name] == codegen.script->subclasses[name]) {
idx = codegen.get_name_map_pos(name);
idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS;
} else {
script = class_map[name];
}
}
if (idx < 0) {
idx = codegen.get_constant_pos(script);
idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
}
codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
codegen.opcodes.push_back(idx); // variable type
} break;
case GDScriptParser::DataType::SCRIPT:
case GDScriptParser::DataType::GDSCRIPT: {
Variant script = cn->cast_type.script_type;
int idx = codegen.get_constant_pos(script);
idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
codegen.opcodes.push_back(idx); // variable type
} break;
default: {
_set_error("Parser bug: unresolved data type.", cn);
return -1;
}
}
codegen.opcodes.push_back(src_addr); // source adddress
int dst_addr = (p_stack_level) | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
codegen.opcodes.push_back(dst_addr); // append the stack level as destination address of the opcode
codegen.alloc_stack(p_stack_level);
return dst_addr;
} break;
case GDScriptParser::Node::TYPE_OPERATOR: {
//hell breaks loose
@ -782,14 +877,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
case GDScriptParser::OperatorNode::OP_BIT_INVERT: {
if (!_create_unary_operator(codegen, on, Variant::OP_BIT_NEGATE, p_stack_level)) return -1;
} break;
case GDScriptParser::OperatorNode::OP_PREINC: {
} break; //?
case GDScriptParser::OperatorNode::OP_PREDEC: {
} break;
case GDScriptParser::OperatorNode::OP_INC: {
} break;
case GDScriptParser::OperatorNode::OP_DEC: {
} break;
//binary operators (in precedence order)
case GDScriptParser::OperatorNode::OP_IN: {
if (!_create_binary_operator(codegen, on, Variant::OP_IN, p_stack_level)) return -1;
@ -1064,12 +1151,87 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
if (src_address_b < 0)
return -1;
codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
codegen.opcodes.push_back(dst_address_a); // argument 1
codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
GDScriptParser::DataType assign_type = on->arguments[0]->get_datatype();
if (assign_type.has_type && !on->arguments[1]->get_datatype().has_type) {
// Typed assignment
switch (assign_type.kind) {
case GDScriptParser::DataType::BUILTIN: {
codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator
codegen.opcodes.push_back(assign_type.builtin_type); // variable type
codegen.opcodes.push_back(dst_address_a); // argument 1
codegen.opcodes.push_back(src_address_b); // argument 2
} break;
case GDScriptParser::DataType::NATIVE: {
int class_idx;
if (GDScriptLanguage::get_singleton()->get_global_map().has(assign_type.native_type)) {
class_idx = GDScriptLanguage::get_singleton()->get_global_map()[assign_type.native_type];
class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root)
} else {
_set_error("Invalid native class type '" + String(assign_type.native_type) + "'.", on->arguments[0]);
return -1;
}
codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE); // perform operator
codegen.opcodes.push_back(class_idx); // variable type
codegen.opcodes.push_back(dst_address_a); // argument 1
codegen.opcodes.push_back(src_address_b); // argument 2
} break;
case GDScriptParser::DataType::CLASS: {
Variant script;
int idx = -1;
if (assign_type.class_type->name == StringName()) {
script = codegen.script;
} else {
StringName name = assign_type.class_type->name;
if (class_map[name] == codegen.script->subclasses[name]) {
idx = codegen.get_name_map_pos(name);
idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS;
} else {
script = class_map[name];
}
}
if (idx < 0) {
idx = codegen.get_constant_pos(script);
idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
}
codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
codegen.opcodes.push_back(idx); // variable type
codegen.opcodes.push_back(dst_address_a); // argument 1
codegen.opcodes.push_back(src_address_b); // argument 2
} break;
case GDScriptParser::DataType::SCRIPT:
case GDScriptParser::DataType::GDSCRIPT: {
Variant script = assign_type.script_type;
int idx = codegen.get_constant_pos(script);
idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
codegen.opcodes.push_back(idx); // variable type
codegen.opcodes.push_back(dst_address_a); // argument 1
codegen.opcodes.push_back(src_address_b); // argument 2
} break;
default: {
ERR_PRINT("Compiler bug: unresolved assign.");
// Shouldn't get here, but fail-safe to a regular assignment
codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
codegen.opcodes.push_back(dst_address_a); // argument 1
codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
}
}
} else {
// Either untyped assignment or already type-checked by the parser
codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN); // perform operator
codegen.opcodes.push_back(dst_address_a); // argument 1
codegen.opcodes.push_back(src_address_b); // argument 2 (unary only takes one parameter)
}
return dst_address_a; //if anything, returns wathever was assigned or correct stack position
}
} break;
case GDScriptParser::OperatorNode::OP_IS: {
@ -1525,6 +1687,18 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
if (p_func) {
gdfunc->_static = p_func->_static;
gdfunc->rpc_mode = p_func->rpc_mode;
gdfunc->argument_types.resize(p_func->argument_types.size());
for (int i = 0; i < p_func->argument_types.size(); i++) {
gdfunc->argument_types[i] = _gdtype_from_datatype(p_func->argument_types[i]);
}
gdfunc->return_type = _gdtype_from_datatype(p_func->return_type);
} else {
gdfunc->_static = false;
gdfunc->rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
gdfunc->return_type = GDScriptDataType();
gdfunc->return_type.has_type = true;
gdfunc->return_type.kind = GDScriptDataType::BUILTIN;
gdfunc->return_type.builtin_type = Variant::NIL;
}
#ifdef TOOLS_ENABLED
@ -1653,12 +1827,23 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
return OK;
}
Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
Map<StringName, Ref<GDScript> > old_subclasses;
if (p_keep_state) {
old_subclasses = p_script->subclasses;
if (p_class->owner && p_class->owner->owner) {
// Owner is not root
StringName owner_name = p_class->owner->name;
if (!parsed_classes.has(owner_name)) {
if (parsing_classes.has(owner_name)) {
_set_error("Cyclic class reference for '" + String(owner_name) + "'.", p_class);
return ERR_PARSE_ERROR;
}
parsing_classes.insert(owner_name);
Error err = _parse_class_level(class_map[owner_name].ptr(), class_map[owner_name]->_owner, p_class->owner, p_keep_state);
if (err) {
return err;
}
parsing_classes.erase(owner_name);
}
}
p_script->native = Ref<GDScriptNativeClass>();
@ -1682,236 +1867,100 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
Ref<GDScriptNativeClass> native;
if (p_class->extends_used) {
//do inheritance
String path = p_class->extends_file;
Ref<GDScript> script;
if (path != "") {
//path (and optionally subclasses)
if (path.is_rel_path()) {
String base;
if (p_owner) {
GDScript *current_class = p_owner;
while (current_class != NULL) {
base = current_class->get_path();
if (base == "")
current_class = current_class->_owner;
else
break;
}
} else {
base = p_script->get_path();
// Inheritance
switch (p_class->base_type.kind) {
case GDScriptParser::DataType::CLASS: {
StringName base_name = p_class->base_type.class_type->name;
// Make sure dependency is parsed first
if (!parsed_classes.has(base_name)) {
if (parsing_classes.has(base_name)) {
_set_error("Cyclic class reference for '" + String(base_name) + "'.", p_class);
return ERR_PARSE_ERROR;
}
if (base == "" || base.is_rel_path()) {
_set_error("Could not resolve relative path for parent class: " + path, p_class);
return ERR_FILE_NOT_FOUND;
parsing_classes.insert(base_name);
Error err = _parse_class_level(class_map[base_name].ptr(), class_map[base_name]->_owner, p_class->base_type.class_type, p_keep_state);
if (err) {
return err;
}
path = base.get_base_dir().plus_file(path).simplify_path();
parsing_classes.erase(base_name);
}
script = ResourceLoader::load(path);
if (script.is_null()) {
_set_error("Could not load base class: " + path, p_class);
return ERR_FILE_NOT_FOUND;
}
if (!script->valid) {
_set_error("Script not fully loaded (cyclic preload?): " + path, p_class);
return ERR_BUSY;
}
//print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid));
if (p_class->extends_class.size()) {
for (int i = 0; i < p_class->extends_class.size(); i++) {
String sub = p_class->extends_class[i];
if (script->subclasses.has(sub)) {
Ref<Script> subclass = script->subclasses[sub]; //avoid reference from disappearing
script = subclass;
} else {
_set_error("Could not find subclass: " + sub, p_class);
return ERR_FILE_NOT_FOUND;
}
}
}
} else {
ERR_FAIL_COND_V(p_class->extends_class.size() == 0, ERR_BUG);
//look around for the subclasses
String base = p_class->extends_class[0];
GDScript *p = p_owner;
Ref<GDScript> base_class;
while (p) {
if (p->subclasses.has(base)) {
base_class = p->subclasses[base];
break;
}
if (p->constants.has(base)) {
base_class = p->constants[base];
if (base_class.is_null()) {
_set_error("Constant is not a class: " + base, p_class);
return ERR_SCRIPT_FAILED;
}
break;
}
p = p->_owner;
}
if (base_class.is_valid()) {
String ident = base;
for (int i = 1; i < p_class->extends_class.size(); i++) {
String subclass = p_class->extends_class[i];
ident += ("." + subclass);
if (base_class->subclasses.has(subclass)) {
base_class = base_class->subclasses[subclass];
} else if (base_class->constants.has(subclass)) {
Ref<GDScript> new_base_class = base_class->constants[subclass];
if (new_base_class.is_null()) {
_set_error("Constant is not a class: " + ident, p_class);
return ERR_SCRIPT_FAILED;
}
base_class = new_base_class;
} else {
_set_error("Could not find subclass: " + ident, p_class);
return ERR_FILE_NOT_FOUND;
}
}
script = base_class;
} else {
if (p_class->extends_class.size() > 1) {
_set_error("Invalid inheritance (unknown class+subclasses)", p_class);
return ERR_FILE_NOT_FOUND;
}
//if not found, try engine classes
if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
_set_error("Unknown class: '" + base + "'", p_class);
return ERR_FILE_NOT_FOUND;
}
int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base];
native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx];
if (!native.is_valid()) {
_set_error("Global not a class: '" + base + "'", p_class);
return ERR_FILE_NOT_FOUND;
}
}
}
if (script.is_valid()) {
p_script->base = script;
Ref<GDScript> base = class_map[base_name];
p_script->base = base;
p_script->_base = p_script->base.ptr();
p_script->member_indices = script->member_indices;
} else if (native.is_valid()) {
p_script->member_indices = base->member_indices;
} break;
case GDScriptParser::DataType::GDSCRIPT: {
Ref<GDScript> base = p_class->base_type.script_type;
p_script->base = base;
p_script->_base = p_script->base.ptr();
p_script->member_indices = base->member_indices;
} break;
case GDScriptParser::DataType::NATIVE: {
int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_class->base_type.native_type];
native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
ERR_FAIL_COND_V(native.is_null(), ERR_BUG);
p_script->native = native;
} else {
_set_error("Could not determine inheritance", p_class);
return ERR_FILE_NOT_FOUND;
}
} else {
// without extends, implicitly extend Reference
int native_idx = GDScriptLanguage::get_singleton()->get_global_map()["Reference"];
native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
ERR_FAIL_COND_V(native.is_null(), ERR_BUG);
p_script->native = native;
} break;
default: {
_set_error("Parser bug: invalid inheritance.", p_class);
return ERR_BUG;
} break;
}
//print_line("Script: "+p_script->get_path()+" indices: "+itos(p_script->member_indices.size()));
for (int i = 0; i < p_class->variables.size(); i++) {
StringName name = p_class->variables[i].identifier;
if (p_script->member_indices.has(name)) {
_set_error("Member '" + name + "' already exists (in current or parent class)", p_class);
return ERR_ALREADY_EXISTS;
}
if (_is_class_member_property(p_script, name)) {
_set_error("Member '" + name + "' already exists as a class property.", p_class);
return ERR_ALREADY_EXISTS;
}
if (p_class->variables[i]._export.type != Variant::NIL) {
p_script->member_info[name] = p_class->variables[i]._export;
#ifdef TOOLS_ENABLED
if (p_class->variables[i].default_value.get_type() != Variant::NIL) {
p_script->member_default_values[name] = p_class->variables[i].default_value;
}
#endif
} else {
p_script->member_info[name] = PropertyInfo(Variant::NIL, name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
}
//int new_idx = p_script->member_indices.size();
GDScript::MemberInfo minfo;
minfo.index = p_script->member_indices.size();
minfo.setter = p_class->variables[i].setter;
minfo.getter = p_class->variables[i].getter;
minfo.rpc_mode = p_class->variables[i].rpc_mode;
minfo.data_type = _gdtype_from_datatype(p_class->variables[i].data_type);
PropertyInfo prop_info = minfo.data_type;
prop_info.name = name;
PropertyInfo export_info = p_class->variables[i]._export;
if (export_info.type != Variant::NIL) {
if (!minfo.data_type.has_type) {
prop_info.type = export_info.type;
prop_info.class_name = export_info.class_name;
}
prop_info.hint = export_info.hint;
prop_info.hint_string = export_info.hint_string;
prop_info.usage = export_info.usage;
#ifdef TOOLS_ENABLED
if (p_class->variables[i].default_value.get_type() != Variant::NIL) {
p_script->member_default_values[name] = p_class->variables[i].default_value;
}
#endif
} else {
prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
}
p_script->member_info[name] = prop_info;
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++) {
for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) {
StringName name = p_class->constant_expressions[i].identifier;
ERR_CONTINUE(p_class->constant_expressions[i].expression->type != GDScriptParser::Node::TYPE_CONSTANT);
StringName name = E->key();
if (_is_class_member_property(p_script, name)) {
_set_error("Member '" + name + "' already exists as a class property.", p_class);
return ERR_ALREADY_EXISTS;
}
ERR_CONTINUE(E->get().expression->type != GDScriptParser::Node::TYPE_CONSTANT);
GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(p_class->constant_expressions[i].expression);
GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(E->get().expression);
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;
p_script->member_lines[name] = E->get().expression->line;
#endif
}
@ -1944,23 +1993,27 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
p_script->_signals[name] = p_class->_signals[i].arguments;
}
if (p_class->name != StringName()) {
parsed_classes.insert(p_class->name);
}
//parse sub-classes
for (int i = 0; i < p_class->subclasses.size(); i++) {
StringName name = p_class->subclasses[i]->name;
Ref<GDScript> subclass;
Ref<GDScript> subclass = class_map[name];
if (old_subclasses.has(name)) {
subclass = old_subclasses[name];
} else {
subclass.instance();
// Subclass might still be parsing, just skip it
if (!parsed_classes.has(name) && !parsing_classes.has(name)) {
parsing_classes.insert(name);
Error err = _parse_class_level(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state);
if (err)
return err;
parsing_classes.erase(name);
}
Error err = _parse_class(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state);
if (err)
return err;
#ifdef TOOLS_ENABLED
p_script->member_lines[name] = p_class->subclasses[i]->line;
@ -1970,6 +2023,10 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
p_script->subclasses.insert(name, subclass);
}
return OK;
}
Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
//parse methods
bool has_initializer = false;
@ -2010,44 +2067,6 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
}
#ifdef DEBUG_ENABLED
//validate setters/getters if debug is enabled
for (int i = 0; i < p_class->variables.size(); i++) {
if (p_class->variables[i].setter) {
const Map<StringName, GDScriptFunction *>::Element *E = p_script->get_member_functions().find(p_class->variables[i].setter);
if (!E) {
_set_error("Setter function '" + String(p_class->variables[i].setter) + "' not found in class.", NULL);
err_line = p_class->variables[i].line;
err_column = 0;
return ERR_PARSE_ERROR;
}
if (E->get()->is_static()) {
_set_error("Setter function '" + String(p_class->variables[i].setter) + "' is static.", NULL);
err_line = p_class->variables[i].line;
err_column = 0;
return ERR_PARSE_ERROR;
}
}
if (p_class->variables[i].getter) {
const Map<StringName, GDScriptFunction *>::Element *E = p_script->get_member_functions().find(p_class->variables[i].getter);
if (!E) {
_set_error("Getter function '" + String(p_class->variables[i].getter) + "' not found in class.", NULL);
err_line = p_class->variables[i].line;
err_column = 0;
return ERR_PARSE_ERROR;
}
if (E->get()->is_static()) {
_set_error("Getter function '" + String(p_class->variables[i].getter) + "' is static.", NULL);
err_line = p_class->variables[i].line;
err_column = 0;
return ERR_PARSE_ERROR;
}
}
}
//validate instances if keeping state
@ -2100,22 +2119,67 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
}
#endif
for (int i = 0; i < p_class->subclasses.size(); i++) {
StringName name = p_class->subclasses[i]->name;
Ref<GDScript> subclass = class_map[name];
Error err = _parse_class_blocks(subclass.ptr(), p_class->subclasses[i], p_keep_state);
if (err) {
return err;
}
}
p_script->valid = true;
return OK;
}
void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
Map<StringName, Ref<GDScript> > old_subclasses;
if (p_keep_state) {
old_subclasses = p_script->subclasses;
}
for (int i = 0; i < p_class->subclasses.size(); i++) {
StringName name = p_class->subclasses[i]->name;
Ref<GDScript> subclass;
if (old_subclasses.has(name)) {
subclass = old_subclasses[name];
} else {
subclass.instance();
}
subclass->_owner = const_cast<GDScript *>(p_script);
class_map.insert(name, subclass);
_make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state);
}
}
Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) {
err_line = -1;
err_column = -1;
error = "";
parser = p_parser;
main_script = p_script;
const GDScriptParser::Node *root = parser->get_parse_tree();
ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_INVALID_DATA);
source = p_script->get_path();
Error err = _parse_class(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
// Create scripts for subclasses beforehand so they can be referenced
_make_scripts(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
Error err = _parse_class_level(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
if (err)
return err;
err = _parse_class_blocks(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
if (err)
return err;

View file

@ -31,12 +31,17 @@
#ifndef GDSCRIPT_COMPILER_H
#define GDSCRIPT_COMPILER_H
#include "core/set.h"
#include "gdscript.h"
#include "gdscript_parser.h"
class GDScriptCompiler {
const GDScriptParser *parser;
Map<StringName, Ref<GDScript> > class_map;
Set<StringName> parsed_classes;
Set<StringName> parsing_classes;
GDScript *main_script;
struct CodeGen {
GDScript *script;
@ -138,11 +143,15 @@ class GDScriptCompiler {
bool _create_unary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level);
bool _create_binary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level, bool p_initializer = false);
GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const;
int _parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level);
int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false);
Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1);
Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
Error _parse_class(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
Error _parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
void _make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
int err_line;
int err_column;
StringName source;

File diff suppressed because it is too large Load diff

View file

@ -200,6 +200,12 @@ static String _get_var_type(const Variant *p_type) {
&&OPCODE_ASSIGN, \
&&OPCODE_ASSIGN_TRUE, \
&&OPCODE_ASSIGN_FALSE, \
&&OPCODE_ASSIGN_TYPED_BUILTIN, \
&&OPCODE_ASSIGN_TYPED_NATIVE, \
&&OPCODE_ASSIGN_TYPED_SCRIPT, \
&&OPCODE_CAST_TO_BUILTIN, \
&&OPCODE_CAST_TO_NATIVE, \
&&OPCODE_CAST_TO_SCRIPT, \
&&OPCODE_CONSTRUCT, \
&&OPCODE_CONSTRUCT_ARRAY, \
&&OPCODE_CONSTRUCT_DICTIONARY, \
@ -318,10 +324,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (_stack_size) {
stack = (Variant *)aptr;
for (int i = 0; i < p_argcount; i++)
memnew_placement(&stack[i], Variant(*p_args[i]));
for (int i = p_argcount; i < _stack_size; i++)
for (int i = 0; i < p_argcount; i++) {
if (!argument_types[i].has_type) {
memnew_placement(&stack[i], Variant(*p_args[i]));
continue;
}
if (!argument_types[i].is_type(*p_args[i], true)) {
r_err.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_err.argument = i;
r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
return Variant();
}
if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err);
memnew_placement(&stack[i], Variant(arg));
} else {
memnew_placement(&stack[i], Variant(*p_args[i]));
}
}
for (int i = p_argcount; i < _stack_size; i++) {
memnew_placement(&stack[i], Variant);
}
} else {
stack = NULL;
}
@ -709,6 +733,199 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
CHECK_SPACE(4);
Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1];
GET_VARIANT_PTR(dst, 2);
GET_VARIANT_PTR(src, 3);
GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
if (src->get_type() != var_type) {
err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
"' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
OPCODE_BREAK;
}
*dst = *src;
ip += 4;
}
DISPATCH_OPCODE;
OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
CHECK_SPACE(4);
GET_VARIANT_PTR(type, 1);
GET_VARIANT_PTR(dst, 2);
GET_VARIANT_PTR(src, 3);
GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
GD_ERR_BREAK(!nc);
Object *src_obj = src->operator Object *();
GD_ERR_BREAK(!src_obj);
if (!ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
"' to a variable of type '" + nc->get_name() + "'.";
OPCODE_BREAK;
}
*dst = *src;
ip += 4;
}
DISPATCH_OPCODE;
OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
CHECK_SPACE(4);
GET_VARIANT_PTR(type, 1);
GET_VARIANT_PTR(dst, 2);
GET_VARIANT_PTR(src, 3);
Script *base_type = Object::cast_to<Script>(type->operator Object *());
GD_ERR_BREAK(!base_type);
if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
OPCODE_BREAK;
}
if (src->get_type() != Variant::NIL && src->operator Object *() != NULL) {
ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
if (!scr_inst) {
err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() +
"' to a variable of type '" + base_type->get_path().get_file() + "'.";
OPCODE_BREAK;
}
Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
bool valid = false;
while (src_type) {
if (src_type == base_type) {
valid = true;
break;
}
src_type = src_type->get_base_script().ptr();
}
if (!valid) {
err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() +
"' to a variable of type '" + base_type->get_path().get_file() + "'.";
OPCODE_BREAK;
}
}
*dst = *src;
ip += 4;
}
DISPATCH_OPCODE;
OPCODE(OPCODE_CAST_TO_BUILTIN) {
CHECK_SPACE(4);
Variant::Type to_type = (Variant::Type)_code_ptr[ip + 1];
GET_VARIANT_PTR(src, 2);
GET_VARIANT_PTR(dst, 3);
GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
Variant::CallError err;
*dst = Variant::construct(to_type, (const Variant **)&src, 1, err);
#ifdef DEBUG_ENABLED
if (err.error != Variant::CallError::CALL_OK) {
err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
OPCODE_BREAK;
}
#endif
ip += 4;
}
DISPATCH_OPCODE;
OPCODE(OPCODE_CAST_TO_NATIVE) {
CHECK_SPACE(4);
GET_VARIANT_PTR(to_type, 1);
GET_VARIANT_PTR(src, 2);
GET_VARIANT_PTR(dst, 3);
GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
GD_ERR_BREAK(!nc);
#ifdef DEBUG_ENABLED
if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
err_text = "Invalid cast: can't convert a non-object value to an object type.";
OPCODE_BREAK;
}
#endif
Object *src_obj = src->operator Object *();
if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
*dst = Variant(); // invalid cast, assign NULL
} else {
*dst = *src;
}
ip += 4;
}
DISPATCH_OPCODE;
OPCODE(OPCODE_CAST_TO_SCRIPT) {
CHECK_SPACE(4);
GET_VARIANT_PTR(to_type, 1);
GET_VARIANT_PTR(src, 2);
GET_VARIANT_PTR(dst, 3);
Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
GD_ERR_BREAK(!base_type);
#ifdef DEBUG_ENABLED
if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
OPCODE_BREAK;
}
#endif
bool valid = false;
if (src->get_type() != Variant::NIL && src->operator Object *() != NULL) {
ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
if (scr_inst) {
Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
while (src_type) {
if (src_type == base_type) {
valid = true;
break;
}
src_type = src_type->get_base_script().ptr();
}
}
}
if (valid) {
*dst = *src; // Valid cast, copy the source object
} else {
*dst = Variant(); // invalid cast, assign NULL
}
ip += 4;
}
DISPATCH_OPCODE;
OPCODE(OPCODE_CONSTRUCT) {
CHECK_SPACE(2);
@ -1370,6 +1587,15 @@ int GDScriptFunction::get_default_argument_addr(int p_idx) const {
return default_arguments[p_idx];
}
GDScriptDataType GDScriptFunction::get_return_type() const {
return return_type;
}
GDScriptDataType GDScriptFunction::get_argument_type(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, argument_types.size(), GDScriptDataType());
return argument_types[p_idx];
}
StringName GDScriptFunction::get_name() const {
return name;

View file

@ -42,6 +42,95 @@
class GDScriptInstance;
class GDScript;
struct GDScriptDataType {
bool has_type;
enum {
BUILTIN,
NATIVE,
SCRIPT,
GDSCRIPT
} kind;
Variant::Type builtin_type;
StringName native_type;
Ref<Script> script_type;
bool is_type(const Variant &p_variant, bool p_allow_implicit_conversion = false) const {
if (!has_type) return true; // Can't type check
switch (kind) {
case BUILTIN: {
Variant::Type var_type = p_variant.get_type();
bool valid = builtin_type == var_type;
if (!valid && p_allow_implicit_conversion) {
valid = Variant::can_convert_strict(var_type, builtin_type);
}
return valid;
} break;
case NATIVE: {
if (p_variant.get_type() == Variant::NIL) {
return true;
}
if (p_variant.get_type() != Variant::OBJECT) {
return false;
}
Object *obj = p_variant.operator Object *();
if (obj && !ClassDB::is_parent_class(obj->get_class_name(), native_type)) {
return false;
}
return true;
} break;
case SCRIPT:
case GDSCRIPT: {
if (p_variant.get_type() == Variant::NIL) {
return true;
}
if (p_variant.get_type() != Variant::OBJECT) {
return false;
}
Object *obj = p_variant.operator Object *();
Ref<Script> base = obj && obj->get_script_instance() ? obj->get_script_instance()->get_script() : NULL;
bool valid = false;
while (base.is_valid()) {
if (base == script_type) {
valid = true;
break;
}
base = base->get_base_script();
}
return valid;
} break;
}
return false;
}
operator PropertyInfo() const {
PropertyInfo info;
if (has_type) {
switch (kind) {
case BUILTIN: {
info.type = builtin_type;
} break;
case NATIVE: {
info.type = Variant::OBJECT;
info.class_name = native_type;
} break;
case SCRIPT:
case GDSCRIPT: {
info.type = Variant::OBJECT;
info.class_name = script_type->get_instance_base_type();
} break;
}
} else {
info.type = Variant::NIL;
info.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
}
return info;
}
GDScriptDataType() :
has_type(false) {}
};
class GDScriptFunction {
public:
enum Opcode {
@ -56,6 +145,12 @@ public:
OPCODE_ASSIGN,
OPCODE_ASSIGN_TRUE,
OPCODE_ASSIGN_FALSE,
OPCODE_ASSIGN_TYPED_BUILTIN,
OPCODE_ASSIGN_TYPED_NATIVE,
OPCODE_ASSIGN_TYPED_SCRIPT,
OPCODE_CAST_TO_BUILTIN,
OPCODE_CAST_TO_NATIVE,
OPCODE_CAST_TO_SCRIPT,
OPCODE_CONSTRUCT, //only for basic types!!
OPCODE_CONSTRUCT_ARRAY,
OPCODE_CONSTRUCT_DICTIONARY,
@ -139,6 +234,8 @@ private:
#endif
Vector<int> default_arguments;
Vector<int> code;
Vector<GDScriptDataType> argument_types;
GDScriptDataType return_type;
#ifdef TOOLS_ENABLED
Vector<StringName> arg_names;
@ -199,6 +296,8 @@ public:
int get_max_stack_size() const;
int get_default_argument_count() const;
int get_default_argument_addr(int p_idx) const;
GDScriptDataType get_return_type() const;
GDScriptDataType get_argument_type(int p_idx) const;
GDScript *get_script() const { return _script; }
StringName get_source() const { return source; }

View file

@ -1663,7 +1663,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
MethodInfo mi("weakref", PropertyInfo(Variant::OBJECT, "obj"));
mi.return_val.type = Variant::OBJECT;
mi.return_val.name = "WeakRef";
mi.return_val.class_name = "WeakRef";
return mi;
@ -1672,19 +1672,20 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
MethodInfo mi("funcref", PropertyInfo(Variant::OBJECT, "instance"), PropertyInfo(Variant::STRING, "funcname"));
mi.return_val.type = Variant::OBJECT;
mi.return_val.name = "FuncRef";
mi.return_val.class_name = "FuncRef";
return mi;
} break;
case TYPE_CONVERT: {
MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what"), PropertyInfo(Variant::INT, "type"));
mi.return_val.type = Variant::OBJECT;
MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::INT, "type"));
mi.return_val.type = Variant::NIL;
mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
return mi;
} break;
case TYPE_OF: {
MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what"));
MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::INT;
return mi;
@ -1760,7 +1761,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
} break;
case VAR_TO_STR: {
MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var"));
MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::STRING;
return mi;
@ -1773,7 +1774,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
case VAR_TO_BYTES: {
MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var"));
MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::POOL_BYTE_ARRAY;
return mi;
@ -1796,7 +1797,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
MethodInfo mi("load", PropertyInfo(Variant::STRING, "path"));
mi.return_val.type = Variant::OBJECT;
mi.return_val.name = "Resource";
mi.return_val.class_name = "Resource";
return mi;
} break;
case INST2DICT: {
@ -1826,13 +1827,13 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
} break;
case TO_JSON: {
MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var"));
MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::STRING;
return mi;
} break;
case HASH: {
MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var"));
MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::INT;
return mi;
} break;
@ -1868,7 +1869,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
return mi;
} break;
case LEN: {
MethodInfo mi("len", PropertyInfo(Variant::NIL, "var"));
MethodInfo mi("len", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
mi.return_val.type = Variant::INT;
return mi;
} break;

File diff suppressed because it is too large Load diff

View file

@ -37,8 +37,68 @@
#include "object.h"
#include "script_language.h"
struct GDScriptDataType;
class GDScriptParser {
public:
struct ClassNode;
struct DataType {
enum {
BUILTIN,
NATIVE,
SCRIPT,
GDSCRIPT,
CLASS,
UNRESOLVED
} kind;
bool has_type;
bool is_constant;
bool is_meta_type; // Whether the value can be used as a type
bool infer_type;
Variant::Type builtin_type;
StringName native_type;
Ref<Script> script_type;
ClassNode *class_type;
String to_string() const;
bool operator==(const DataType &other) const {
if (!has_type || !other.has_type) {
return true; // Can be considered equal for parsing purpose
}
if (kind != other.kind) {
return false;
}
switch (kind) {
case BUILTIN: {
return builtin_type == other.builtin_type;
} break;
case NATIVE: {
return native_type == other.native_type;
} break;
case GDSCRIPT:
case SCRIPT: {
return script_type == other.script_type;
} break;
case CLASS: {
return class_type == other.class_type;
} break;
}
return false;
}
DataType() :
has_type(false),
is_constant(false),
is_meta_type(false),
infer_type(false),
builtin_type(Variant::NIL),
class_type(NULL) {}
};
struct Node {
enum Type {
@ -55,6 +115,7 @@ public:
TYPE_OPERATOR,
TYPE_CONTROL_FLOW,
TYPE_LOCAL_VAR,
TYPE_CAST,
TYPE_ASSERT,
TYPE_BREAKPOINT,
TYPE_NEWLINE,
@ -65,11 +126,17 @@ public:
int column;
Type type;
virtual DataType get_datatype() const { return DataType(); }
virtual void set_datatype(const DataType &p_datatype) {}
virtual ~Node() {}
};
struct FunctionNode;
struct BlockNode;
struct ConstantNode;
struct LocalVarNode;
struct OperatorNode;
struct ClassNode : public Node {
@ -78,6 +145,7 @@ public:
bool extends_used;
StringName extends_file;
Vector<StringName> extends_class;
DataType base_type;
struct Member {
PropertyInfo _export;
@ -85,15 +153,17 @@ public:
Variant default_value;
#endif
StringName identifier;
DataType data_type;
StringName setter;
StringName getter;
int line;
Node *expression;
OperatorNode *initial_assignment;
MultiplayerAPI::RPCMode rpc_mode;
};
struct Constant {
StringName identifier;
Node *expression;
DataType type;
};
struct Signal {
@ -103,7 +173,7 @@ public:
Vector<ClassNode *> subclasses;
Vector<Member> variables;
Vector<Constant> constant_expressions;
Map<StringName, Constant> constant_expressions;
Vector<FunctionNode *> functions;
Vector<FunctionNode *> static_functions;
Vector<Signal> _signals;
@ -126,15 +196,22 @@ public:
bool _static;
MultiplayerAPI::RPCMode rpc_mode;
bool has_yield;
StringName name;
DataType return_type;
Vector<StringName> arguments;
Vector<DataType> argument_types;
Vector<Node *> default_values;
BlockNode *body;
virtual DataType get_datatype() const { return return_type; }
virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; }
FunctionNode() {
type = TYPE_FUNCTION;
_static = false;
rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
has_yield = false;
}
};
@ -142,10 +219,9 @@ public:
ClassNode *parent_class;
BlockNode *parent_block;
Map<StringName, int> locals;
List<Node *> statements;
Vector<StringName> variables;
Vector<int> variable_lines;
Map<StringName, LocalVarNode *> variables;
bool has_return;
Node *if_condition; //tiny hack to improve code completion on if () blocks
@ -158,6 +234,7 @@ public:
end_line = -1;
parent_block = NULL;
parent_class = NULL;
has_return = false;
}
};
@ -174,28 +251,53 @@ public:
struct IdentifierNode : public Node {
StringName name;
IdentifierNode() { type = TYPE_IDENTIFIER; }
BlockNode *declared_block; // Simplify lookup by checking if it is declared locally
DataType datatype;
virtual DataType get_datatype() const { return datatype; }
virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
IdentifierNode() {
type = TYPE_IDENTIFIER;
declared_block = NULL;
}
};
struct LocalVarNode : public Node {
StringName name;
Node *assign;
OperatorNode *assign_op;
int assignments;
DataType datatype;
virtual DataType get_datatype() const { return datatype; }
virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
LocalVarNode() {
type = TYPE_LOCAL_VAR;
assign = NULL;
assign_op = NULL;
assignments = 0;
}
};
struct ConstantNode : public Node {
Variant value;
DataType datatype;
virtual DataType get_datatype() const { return datatype; }
virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
ConstantNode() { type = TYPE_CONSTANT; }
};
struct ArrayNode : public Node {
Vector<Node *> elements;
ArrayNode() { type = TYPE_ARRAY; }
DataType datatype;
virtual DataType get_datatype() const { return datatype; }
virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
ArrayNode() {
type = TYPE_ARRAY;
datatype.has_type = true;
datatype.kind = DataType::BUILTIN;
datatype.builtin_type = Variant::ARRAY;
}
};
struct DictionaryNode : public Node {
@ -207,7 +309,15 @@ public:
};
Vector<Pair> elements;
DictionaryNode() { type = TYPE_DICTIONARY; }
DataType datatype;
virtual DataType get_datatype() const { return datatype; }
virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
DictionaryNode() {
type = TYPE_DICTIONARY;
datatype.has_type = true;
datatype.kind = DataType::BUILTIN;
datatype.builtin_type = Variant::DICTIONARY;
}
};
struct SelfNode : public Node {
@ -229,10 +339,6 @@ public:
OP_POS,
OP_NOT,
OP_BIT_INVERT,
OP_PREINC,
OP_PREDEC,
OP_INC,
OP_DEC,
//binary operators (in precedence order)
OP_IN,
OP_EQUAL,
@ -273,6 +379,9 @@ public:
Operator op;
Vector<Node *> arguments;
DataType datatype;
virtual DataType get_datatype() const { return datatype; }
virtual void set_datatype(const DataType &p_datatype) { datatype = p_datatype; }
OperatorNode() { type = TYPE_OPERATOR; }
};
@ -340,6 +449,15 @@ public:
}
};
struct CastNode : public Node {
Node *source_node;
DataType cast_type;
DataType return_type;
virtual DataType get_datatype() const { return return_type; }
virtual void set_datatype(const DataType &p_datatype) { return_type = p_datatype; }
CastNode() { type = TYPE_CAST; }
};
struct AssertNode : public Node {
Node *condition;
AssertNode() { type = TYPE_ASSERT; }
@ -362,76 +480,6 @@ public:
};
};
/*
struct OperatorNode : public Node {
DataType return_cache;
Operator op;
Vector<Node*> arguments;
virtual DataType get_datatype() const { return return_cache; }
OperatorNode() { type=TYPE_OPERATOR; return_cache=TYPE_VOID; }
};
struct VariableNode : public Node {
DataType datatype_cache;
StringName name;
virtual DataType get_datatype() const { return datatype_cache; }
VariableNode() { type=TYPE_VARIABLE; datatype_cache=TYPE_VOID; }
};
struct ConstantNode : public Node {
DataType datatype;
Variant value;
virtual DataType get_datatype() const { return datatype; }
ConstantNode() { type=TYPE_CONSTANT; }
};
struct BlockNode : public Node {
Map<StringName,DataType> variables;
List<Node*> statements;
BlockNode() { type=TYPE_BLOCK; }
};
struct ControlFlowNode : public Node {
FlowOperation flow_op;
Vector<Node*> statements;
ControlFlowNode() { type=TYPE_CONTROL_FLOW; flow_op=FLOW_OP_IF;}
};
struct MemberNode : public Node {
DataType datatype;
StringName name;
Node* owner;
virtual DataType get_datatype() const { return datatype; }
MemberNode() { type=TYPE_MEMBER; }
};
struct ProgramNode : public Node {
struct Function {
StringName name;
FunctionNode*function;
};
Map<StringName,DataType> builtin_variables;
Map<StringName,DataType> preexisting_variables;
Vector<Function> functions;
BlockNode *body;
ProgramNode() { type=TYPE_PROGRAM; }
};
*/
enum CompletionType {
COMPLETION_NONE,
COMPLETION_BUILT_IN_TYPE_CONSTANT,
@ -446,6 +494,8 @@ public:
COMPLETION_VIRTUAL_FUNC,
COMPLETION_YIELD,
COMPLETION_ASSIGN,
COMPLETION_TYPE_HINT,
COMPLETION_TYPE_HINT_INDEX,
};
private:
@ -463,6 +513,10 @@ private:
String error;
int error_line;
int error_column;
bool check_types;
#ifdef DEBUG_ENABLED
Set<int> *safe_lines;
#endif // DEBUG_ENABLED
int pending_newline;
@ -507,7 +561,7 @@ private:
PatternNode *_parse_pattern(bool p_static);
void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static);
void _transform_match_statment(BlockNode *p_block, MatchNode *p_match_statement);
void _transform_match_statment(MatchNode *p_match_statement);
void _generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node *> &p_bindings);
void _parse_block(BlockNode *p_block, bool p_static);
@ -515,13 +569,43 @@ private:
void _parse_class(ClassNode *p_class);
bool _end_statement();
void _determine_inheritance(ClassNode *p_class);
bool _parse_type(DataType &r_type, bool p_can_be_void = false);
DataType _resolve_type(const DataType &p_source, int p_line);
DataType _type_from_variant(const Variant &p_value) const;
DataType _type_from_property(const PropertyInfo &p_property, bool p_nil_is_variant = true) const;
DataType _type_from_gdtype(const GDScriptDataType &p_gdtype) const;
DataType _get_operation_type(const Variant::Operator p_op, const DataType &p_a, const DataType &p_b, bool &r_valid) const;
Variant::Operator _get_variant_operation(const OperatorNode::Operator &p_op) const;
bool _get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const;
bool _get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type) const;
bool _is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion = false) const;
DataType _reduce_node_type(Node *p_node);
DataType _reduce_function_call_type(const OperatorNode *p_call);
DataType _reduce_identifier_type(const DataType *p_base_type, const StringName &p_identifier, int p_line);
void _check_class_level_types(ClassNode *p_class);
void _check_class_blocks_types(ClassNode *p_class);
void _check_function_types(FunctionNode *p_function);
void _check_block_types(BlockNode *p_block);
_FORCE_INLINE_ void _mark_line_as_safe(int p_line) const {
#ifdef DEBUG_ENABLED
if (safe_lines) safe_lines->insert(p_line);
#endif // DEBUG_ENABLED
}
_FORCE_INLINE_ void _mark_line_as_unsafe(int p_line) const {
#ifdef DEBUG_ENABLED
if (safe_lines) safe_lines->erase(p_line);
#endif // DEBUG_ENABLED
}
Error _parse(const String &p_base_path);
public:
String get_error() const;
int get_error_line() const;
int get_error_column() const;
Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false);
Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL);
Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = "");
bool is_tool_script() const;

View file

@ -101,6 +101,8 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"setget",
"const",
"var",
"as",
"void",
"enum",
"preload",
"assert",
@ -125,6 +127,7 @@ const char *GDScriptTokenizer::token_names[TK_MAX] = {
"'.'",
"'?'",
"':'",
"'->'",
"'$'",
"'\\n'",
"PI",
@ -197,6 +200,8 @@ static const _kws _keyword_list[] = {
{ GDScriptTokenizer::TK_PR_EXPORT, "export" },
{ GDScriptTokenizer::TK_PR_SETGET, "setget" },
{ GDScriptTokenizer::TK_PR_VAR, "var" },
{ GDScriptTokenizer::TK_PR_AS, "as" },
{ GDScriptTokenizer::TK_PR_VOID, "void" },
{ GDScriptTokenizer::TK_PR_PRELOAD, "preload" },
{ GDScriptTokenizer::TK_PR_ASSERT, "assert" },
{ GDScriptTokenizer::TK_PR_YIELD, "yield" },
@ -707,11 +712,9 @@ void GDScriptTokenizerText::_advance() {
if (GETCHAR(1) == '=') {
_make_token(TK_OP_ASSIGN_SUB);
INCPOS(1);
/*
} else if (GETCHAR(1)=='-') {
_make_token(TK_OP_MINUS_MINUS);
} else if (GETCHAR(1) == '>') {
_make_token(TK_FORWARD_ARROW);
INCPOS(1);
*/
} else {
_make_token(TK_OP_SUB);
}

View file

@ -106,6 +106,8 @@ public:
TK_PR_SETGET,
TK_PR_CONST,
TK_PR_VAR,
TK_PR_AS,
TK_PR_VOID,
TK_PR_ENUM,
TK_PR_PRELOAD,
TK_PR_ASSERT,
@ -131,6 +133,7 @@ public:
TK_QUESTION_MARK,
TK_COLON,
TK_DOLLAR,
TK_FORWARD_ARROW,
TK_NEWLINE,
TK_CONST_PI,
TK_CONST_TAU,

View file

@ -292,7 +292,7 @@ public:
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);
/* TODO */ 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) const { return true; }
/* TODO */ 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, Set<int> *r_safe_lines = NULL) const { return true; }
virtual String validate_path(const String &p_path) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;

View file

@ -2402,7 +2402,7 @@ void VisualScriptLanguage::make_template(const String &p_class_name, const Strin
script->set_instance_base_type(p_base_class_name);
}
bool VisualScriptLanguage::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 {
bool VisualScriptLanguage::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, Set<int> *r_safe_lines) const {
return false;
}

View file

@ -563,7 +563,7 @@ public:
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 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, Set<int> *r_safe_lines = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;

View file

@ -290,6 +290,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) {
Line line;
line.marked = false;
line.safe = false;
line.breakpoint = false;
line.hidden = false;
line.width_cache = -1;
@ -972,7 +973,7 @@ void TextEdit::_notification(int p_what) {
fc = line_num_padding + fc;
}
cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, cache.line_number_color);
cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, text.is_safe(line) ? cache.safe_line_number_color : cache.line_number_color);
}
}
@ -4314,6 +4315,7 @@ void TextEdit::_update_caches() {
cache.caret_color = get_color("caret_color");
cache.caret_background_color = get_color("caret_background_color");
cache.line_number_color = get_color("line_number_color");
cache.safe_line_number_color = get_color("safe_line_number_color");
cache.font_color = get_color("font_color");
cache.font_selected_color = get_color("font_selected_color");
cache.keyword_color = get_color("keyword_color");
@ -4885,6 +4887,17 @@ void TextEdit::set_line_as_marked(int p_line, bool p_marked) {
update();
}
void TextEdit::set_line_as_safe(int p_line, bool p_safe) {
ERR_FAIL_INDEX(p_line, text.size());
text.set_safe(p_line, p_safe);
update();
}
bool TextEdit::is_line_set_as_safe(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), false);
return text.is_safe(p_line);
}
bool TextEdit::is_line_set_as_breakpoint(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), false);

View file

@ -76,6 +76,7 @@ public:
bool marked : 1;
bool breakpoint : 1;
bool hidden : 1;
bool safe : 1;
int wrap_amount_cache : 24;
Map<int, ColorRegionInfo> region_info;
String data;
@ -106,6 +107,8 @@ public:
bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; }
bool is_hidden(int p_line) const { return text[p_line].hidden; }
void set_safe(int p_line, bool p_safe) { text[p_line].safe = p_safe; }
bool is_safe(int p_line) const { return text[p_line].safe; }
void insert(int p_at, const String &p_text);
void remove(int p_at);
int size() const { return text.size(); }
@ -165,6 +168,7 @@ private:
Color caret_color;
Color caret_background_color;
Color line_number_color;
Color safe_line_number_color;
Color font_color;
Color font_selected_color;
Color keyword_color;
@ -472,6 +476,8 @@ public:
void set_line_as_marked(int p_line, bool p_marked);
void set_line_as_breakpoint(int p_line, bool p_breakpoint);
bool is_line_set_as_breakpoint(int p_line) const;
void set_line_as_safe(int p_line, bool p_safe);
bool is_line_set_as_safe(int p_line) const;
void get_breakpoints(List<int> *p_breakpoints) const;
Array get_breakpoints_array() const;
void remove_breakpoints();

View file

@ -476,6 +476,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("symbol_color", "TextEdit", control_font_color_hover);
theme->set_color("brace_mismatch_color", "TextEdit", Color(1, 0.2, 0.2));
theme->set_color("line_number_color", "TextEdit", Color::html("66aaaaaa"));
theme->set_color("safe_line_number_color", "TextEdit", Color::html("99aac8aa"));
theme->set_color("function_color", "TextEdit", Color::html("66a2ce"));
theme->set_color("member_variable_color", "TextEdit", Color::html("e64e59"));
theme->set_color("number_color", "TextEdit", Color::html("EB9532"));