Merge pull request #19264 from vnen/typed-gdscript-final
Typed GDScript
This commit is contained in:
commit
92415365c8
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"));
|
||||
|
|
Loading…
Reference in New Issue