summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_parser.cpp')
-rw-r--r--modules/gdscript/gdscript_parser.cpp272
1 files changed, 179 insertions, 93 deletions
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index e96bf0238a..9d229adb2a 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -89,8 +89,8 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) {
if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) {
// be more python-like
- int current = tab_level.back()->get();
- tab_level.push_back(current);
+ IndentLevel current_level = indent_level.back()->get();
+ indent_level.push_back(current_level);
return true;
//_set_error("newline expected after ':'.");
//return false;
@@ -105,12 +105,19 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) {
} else if (tokenizer->get_token(1) != GDScriptTokenizer::TK_NEWLINE) {
int indent = tokenizer->get_token_line_indent();
- int current = tab_level.back()->get();
- if (indent <= current) {
+ int tabs = tokenizer->get_token_line_tab_indent();
+ IndentLevel current_level = indent_level.back()->get();
+ IndentLevel new_indent(indent, tabs);
+ if (new_indent.is_mixed(current_level)) {
+ _set_error("Mixed tabs and spaces in indentation.");
return false;
}
- tab_level.push_back(indent);
+ if (indent <= current_level.indent) {
+ return false;
+ }
+
+ indent_level.push_back(new_indent);
tokenizer->advance();
return true;
@@ -858,11 +865,23 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
if (current_function) {
int arg_idx = current_function->arguments.find(identifier);
if (arg_idx != -1) {
- if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) {
- // Assignment is not really usage
- current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] - 1;
- } else {
- current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] + 1;
+ switch (tokenizer->get_token()) {
+ case GDScriptTokenizer::TK_OP_ASSIGN_ADD:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_AND:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_OR:
+ case GDScriptTokenizer::TK_OP_ASSIGN_BIT_XOR:
+ case GDScriptTokenizer::TK_OP_ASSIGN_DIV:
+ case GDScriptTokenizer::TK_OP_ASSIGN_MOD:
+ case GDScriptTokenizer::TK_OP_ASSIGN_MUL:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_LEFT:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT:
+ case GDScriptTokenizer::TK_OP_ASSIGN_SUB:
+ case GDScriptTokenizer::TK_OP_ASSIGN: {
+ // Assignment is not really usage
+ } break;
+ default: {
+ current_function->arguments_usage.write[arg_idx] = current_function->arguments_usage[arg_idx] + 1;
+ }
}
}
}
@@ -1207,7 +1226,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
if (_get_completable_identifier(COMPLETION_INDEX, identifier)) {
if (identifier == StringName()) {
- identifier = "@temp"; //so it parses allright
+ identifier = "@temp"; //so it parses alright
}
completion_node = op;
@@ -1399,9 +1418,6 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
unary = true;
break;
case OperatorNode::OP_NEG:
- priority = 1;
- unary = true;
- break;
case OperatorNode::OP_POS:
priority = 1;
unary = true;
@@ -2216,7 +2232,7 @@ GDScriptParser::PatternNode *GDScriptParser::_parse_pattern(bool p_static) {
}
void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static) {
- int indent_level = tab_level.back()->get();
+ IndentLevel current_level = indent_level.back()->get();
p_block->has_return = true;
@@ -2231,13 +2247,11 @@ void GDScriptParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBran
if (error_set)
return;
- if (indent_level > tab_level.back()->get()) {
+ if (current_level.indent > indent_level.back()->get().indent) {
break; // go back a level
}
- if (pending_newline != -1) {
- pending_newline = -1;
- }
+ pending_newline = -1;
PatternBranchNode *branch = alloc_node<PatternBranchNode>();
branch->body = alloc_node<BlockNode>();
@@ -2688,7 +2702,7 @@ void GDScriptParser::_transform_match_statment(MatchNode *p_match_statement) {
void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
- int indent_level = tab_level.back()->get();
+ IndentLevel current_level = indent_level.back()->get();
#ifdef DEBUG_ENABLED
@@ -2701,9 +2715,13 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
bool is_first_line = true;
while (true) {
- if (!is_first_line && tab_level.back()->prev() && tab_level.back()->prev()->get() == indent_level) {
+ if (!is_first_line && indent_level.back()->prev() && indent_level.back()->prev()->get().indent == current_level.indent) {
+ if (indent_level.back()->prev()->get().is_mixed(current_level)) {
+ _set_error("Mixed tabs and spaces in indentation.");
+ return;
+ }
// pythonic single-line expression, don't parse future lines
- tab_level.pop_back();
+ indent_level.pop_back();
p_block->end_line = tokenizer->get_token_line();
return;
}
@@ -2713,7 +2731,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
if (error_set)
return;
- if (indent_level > tab_level.back()->get()) {
+ if (current_level.indent > indent_level.back()->get().indent) {
p_block->end_line = tokenizer->get_token_line();
return; //go back a level
}
@@ -2846,15 +2864,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
assigned = subexpr;
} else {
- ConstantNode *c = alloc_node<ConstantNode>();
- if (lv->datatype.has_type && lv->datatype.kind == DataType::BUILTIN) {
- Variant::CallError err;
- c->value = Variant::construct(lv->datatype.builtin_type, NULL, 0, err);
- } else {
- c->value = Variant();
- }
- c->line = var_line;
- assigned = c;
+ assigned = _get_default_value_for_type(lv->datatype, var_line);
}
lv->assign = assigned;
//must be added later, to avoid self-referencing.
@@ -2925,14 +2935,14 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE && _parse_newline())
;
- if (tab_level.back()->get() < indent_level) { //not at current indent level
+ if (indent_level.back()->get().indent < current_level.indent) { //not at current indent level
p_block->end_line = tokenizer->get_token_line();
return;
}
if (tokenizer->get_token() == GDScriptTokenizer::TK_CF_ELIF) {
- if (tab_level.back()->get() > indent_level) {
+ if (indent_level.back()->get().indent > current_level.indent) {
_set_error("Invalid indentation.");
return;
@@ -2980,7 +2990,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
} else if (tokenizer->get_token() == GDScriptTokenizer::TK_CF_ELSE) {
- if (tab_level.back()->get() > indent_level) {
+ if (indent_level.back()->get().indent > current_level.indent) {
_set_error("Invalid indentation.");
return;
}
@@ -3280,15 +3290,36 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {
case GDScriptTokenizer::TK_PR_ASSERT: {
tokenizer->advance();
- Node *condition = _parse_and_reduce_expression(p_block, p_static);
- if (!condition) {
- if (_recover_from_completion()) {
- break;
- }
+
+ if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
+ _set_error("Expected '(' after assert");
+ return;
+ }
+
+ tokenizer->advance();
+
+ Vector<Node *> args;
+ const bool result = _parse_arguments(p_block, args, p_static);
+ if (!result) {
return;
}
+
+ if (args.empty() || args.size() > 2) {
+ _set_error("Wrong number of arguments, expected 1 or 2");
+ return;
+ }
+
AssertNode *an = alloc_node<AssertNode>();
- an->condition = condition;
+ an->condition = _reduce_expression(args[0], p_static);
+
+ if (args.size() == 2) {
+ an->message = _reduce_expression(args[1], p_static);
+ } else {
+ ConstantNode *message_node = alloc_node<ConstantNode>();
+ message_node->value = String();
+ an->message = message_node;
+ }
+
p_block->statements.push_back(an);
if (!_end_statement()) {
@@ -3331,32 +3362,45 @@ bool GDScriptParser::_parse_newline() {
if (tokenizer->get_token(1) != GDScriptTokenizer::TK_EOF && tokenizer->get_token(1) != GDScriptTokenizer::TK_NEWLINE) {
+ IndentLevel current_level = indent_level.back()->get();
int indent = tokenizer->get_token_line_indent();
- int current_indent = tab_level.back()->get();
+ int tabs = tokenizer->get_token_line_tab_indent();
+ IndentLevel new_level(indent, tabs);
+
+ if (new_level.is_mixed(current_level)) {
+ _set_error("Mixed tabs and spaces in indentation.");
+ return false;
+ }
- if (indent > current_indent) {
+ if (indent > current_level.indent) {
_set_error("Unexpected indentation.");
return false;
}
- if (indent < current_indent) {
+ if (indent < current_level.indent) {
- while (indent < current_indent) {
+ while (indent < current_level.indent) {
//exit block
- if (tab_level.size() == 1) {
+ if (indent_level.size() == 1) {
_set_error("Invalid indentation. Bug?");
return false;
}
- tab_level.pop_back();
+ indent_level.pop_back();
- if (tab_level.back()->get() < indent) {
+ if (indent_level.back()->get().indent < indent) {
_set_error("Unindent does not match any outer indentation level.");
return false;
}
- current_indent = tab_level.back()->get();
+
+ if (indent_level.back()->get().is_mixed(current_level)) {
+ _set_error("Mixed tabs and spaces in indentation.");
+ return false;
+ }
+
+ current_level = indent_level.back()->get();
}
tokenizer->advance();
@@ -3454,7 +3498,7 @@ void GDScriptParser::_parse_extends(ClassNode *p_class) {
void GDScriptParser::_parse_class(ClassNode *p_class) {
- int indent_level = tab_level.back()->get();
+ IndentLevel current_level = indent_level.back()->get();
while (true) {
@@ -3462,7 +3506,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (error_set)
return;
- if (indent_level > tab_level.back()->get()) {
+ if (current_level.indent > indent_level.back()->get().indent) {
p_class->end_line = tokenizer->get_token_line();
return; //go back a level
}
@@ -3533,6 +3577,15 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
+ if (p_class->classname_used && ProjectSettings::get_singleton()->has_setting("autoload/" + p_class->name)) {
+ const String autoload_path = ProjectSettings::get_singleton()->get_setting("autoload/" + p_class->name);
+ if (autoload_path.begins_with("*")) {
+ // It's a singleton, and not just a regular AutoLoad script.
+ _set_error("The class \"" + p_class->name + "\" conflicts with the AutoLoad singleton of the same name, and is therefore redundant. Remove the class_name declaration to fix this error.");
+ }
+ return;
+ }
+
tokenizer->advance(2);
if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
@@ -4839,35 +4892,29 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
- Variant::Type initial_type = member.data_type.has_type ? member.data_type.builtin_type : member._export.type;
-
- if (initial_type != Variant::NIL && initial_type != Variant::OBJECT) {
- IdentifierNode *id = alloc_node<IdentifierNode>();
- id->name = member.identifier;
+ Node *expr;
- Node *expr;
+ if (member.data_type.has_type) {
+ expr = _get_default_value_for_type(member.data_type);
+ } else {
+ DataType exported_type;
+ exported_type.has_type = true;
+ exported_type.kind = DataType::BUILTIN;
+ exported_type.builtin_type = member._export.type;
+ expr = _get_default_value_for_type(exported_type);
+ }
- // Make sure arrays and dictionaries are not shared
- if (initial_type == Variant::ARRAY) {
- expr = alloc_node<ArrayNode>();
- } else if (initial_type == Variant::DICTIONARY) {
- expr = alloc_node<DictionaryNode>();
- } else {
- ConstantNode *cn = alloc_node<ConstantNode>();
- Variant::CallError ce2;
- cn->value = Variant::construct(initial_type, NULL, 0, ce2);
- expr = cn;
- }
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name = member.identifier;
- OperatorNode *op = alloc_node<OperatorNode>();
- op->op = OperatorNode::OP_INIT_ASSIGN;
- op->arguments.push_back(id);
- op->arguments.push_back(expr);
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op = OperatorNode::OP_INIT_ASSIGN;
+ op->arguments.push_back(id);
+ op->arguments.push_back(expr);
- p_class->initializer->statements.push_back(op);
+ p_class->initializer->statements.push_back(op);
- member.initial_assignment = op;
- }
+ member.initial_assignment = op;
}
if (autoexport && member.data_type.has_type) {
@@ -5727,28 +5774,35 @@ GDScriptParser::DataType GDScriptParser::_resolve_type(const DataType &p_source,
}
}
- // Still look for class constants in parent script
+ // Still look for class constants in parent scripts
if (!found && (base_type.kind == DataType::GDSCRIPT || base_type.kind == DataType::SCRIPT)) {
Ref<Script> scr = base_type.script_type;
ERR_FAIL_COND_V(scr.is_null(), result);
- Map<StringName, Variant> constants;
- scr->get_constants(&constants);
+ while (scr.is_valid()) {
+ Map<StringName, Variant> constants;
+ scr->get_constants(&constants);
- if (constants.has(id)) {
- Ref<GDScript> gds = constants[id];
+ if (constants.has(id)) {
+ Ref<GDScript> gds = constants[id];
- if (gds.is_valid()) {
- result.kind = DataType::GDSCRIPT;
- result.script_type = gds;
- found = true;
- } else {
- Ref<Script> scr2 = constants[id];
- if (scr2.is_valid()) {
- result.kind = DataType::SCRIPT;
- result.script_type = scr2;
+ if (gds.is_valid()) {
+ result.kind = DataType::GDSCRIPT;
+ result.script_type = gds;
found = true;
+ } else {
+ Ref<Script> scr2 = constants[id];
+ if (scr2.is_valid()) {
+ result.kind = DataType::SCRIPT;
+ result.script_type = scr2;
+ found = true;
+ }
}
}
+ if (found) {
+ break;
+ } else {
+ scr = scr->get_base_script();
+ }
}
}
@@ -6091,12 +6145,18 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data
break;
}
+ // Some classes are prefixed with `_` internally
+ if (!ClassDB::class_exists(expr_native)) {
+ expr_native = "_" + expr_native;
+ }
+
switch (p_container.kind) {
case DataType::NATIVE: {
if (p_container.is_meta_type) {
return ClassDB::is_parent_class(expr_native, GDScriptNativeClass::get_class_static());
} else {
- return ClassDB::is_parent_class(expr_native, p_container.native_type);
+ StringName container_native = ClassDB::class_exists(p_container.native_type) ? p_container.native_type : StringName("_" + p_container.native_type);
+ return ClassDB::is_parent_class(expr_native, container_native);
}
} break;
case DataType::SCRIPT:
@@ -6140,6 +6200,31 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data
return false;
}
+GDScriptParser::Node *GDScriptParser::_get_default_value_for_type(const DataType &p_type, int p_line) {
+ Node *result;
+
+ if (p_type.has_type && p_type.kind == DataType::BUILTIN && p_type.builtin_type != Variant::NIL && p_type.builtin_type != Variant::OBJECT) {
+ if (p_type.builtin_type == Variant::ARRAY) {
+ result = alloc_node<ArrayNode>();
+ } else if (p_type.builtin_type == Variant::DICTIONARY) {
+ result = alloc_node<DictionaryNode>();
+ } else {
+ ConstantNode *c = alloc_node<ConstantNode>();
+ Variant::CallError err;
+ c->value = Variant::construct(p_type.builtin_type, NULL, 0, err);
+ result = c;
+ }
+ } else {
+ ConstantNode *c = alloc_node<ConstantNode>();
+ c->value = Variant();
+ result = c;
+ }
+
+ result->line = p_line;
+
+ return result;
+}
+
GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
#ifdef DEBUG_ENABLED
if (p_node->get_datatype().has_type && p_node->type != Node::TYPE_ARRAY && p_node->type != Node::TYPE_DICTIONARY) {
@@ -6655,7 +6740,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
}
}
- p_node->set_datatype(_resolve_type(node_type, p_node->line));
+ node_type = _resolve_type(node_type, p_node->line);
+ p_node->set_datatype(node_type);
return node_type;
}
@@ -8500,8 +8586,8 @@ void GDScriptParser::clear() {
validating = false;
for_completion = false;
error_set = false;
- tab_level.clear();
- tab_level.push_back(0);
+ indent_level.clear();
+ indent_level.push_back(IndentLevel(0, 0));
error_line = 0;
error_column = 0;
pending_newline = -1;