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.cpp90
1 files changed, 59 insertions, 31 deletions
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 5e210074ed..8563f2b432 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -100,10 +100,8 @@ void GDScriptParser::cleanup() {
}
void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const {
- List<StringName> keys;
- valid_annotations.get_key_list(&keys);
- for (const StringName &E : keys) {
- r_annotations->push_back(valid_annotations[E].info);
+ for (const KeyValue<StringName, AnnotationInfo> &E : valid_annotations) {
+ r_annotations->push_back(E.value.info);
}
}
@@ -519,7 +517,7 @@ void GDScriptParser::parse_program() {
// Check for @tool annotation.
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
- if (annotation->name == "@tool") {
+ if (annotation->name == SNAME("@tool")) {
// TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?).
_is_tool = true;
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
@@ -573,7 +571,7 @@ void GDScriptParser::parse_program() {
// Check for @icon annotation.
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
- if (annotation->name == "@icon") {
+ if (annotation->name == SNAME("@icon")) {
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
push_error(R"(Expected newline after "@icon" annotation.)");
}
@@ -1380,6 +1378,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
push_completion_call(annotation);
make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, 0, true);
if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE) && !is_at_end()) {
+ push_multiline(true);
int argument_index = 0;
do {
make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index, true);
@@ -1391,6 +1390,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
}
annotation->arguments.push_back(argument);
} while (match(GDScriptTokenizer::Token::COMMA));
+ pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after annotation arguments.)*");
}
@@ -1861,7 +1861,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
if (pattern == nullptr) {
continue;
}
- if (pattern->pattern_type == PatternNode::PT_BIND) {
+ if (pattern->binds.size() > 0) {
has_bind = true;
}
if (branch->patterns.size() > 0 && has_bind) {
@@ -1892,11 +1892,9 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
SuiteNode *suite = alloc_node<SuiteNode>();
if (branch->patterns.size() > 0) {
- List<StringName> binds;
- branch->patterns[0]->binds.get_key_list(&binds);
-
- for (const StringName &E : binds) {
- SuiteNode::Local local(branch->patterns[0]->binds[E], current_function);
+ for (const KeyValue<StringName, IdentifierNode *> &E : branch->patterns[0]->binds) {
+ SuiteNode::Local local(E.value, current_function);
+ local.type = SuiteNode::Local::PATTERN_BIND;
suite->add_local(local);
}
}
@@ -2105,7 +2103,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign);
while (p_precedence <= get_rule(current.type)->precedence) {
- if (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) {
+ if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL)) {
return previous_operand;
}
// Also switch multiline mode on here for infix operators.
@@ -2198,9 +2196,6 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre
if (current_function && current_function->is_static) {
push_error(R"(Cannot use "self" inside a static function.)");
}
- if (in_lambda) {
- push_error(R"(Cannot use "self" inside a lambda.)");
- }
SelfNode *self = alloc_node<SelfNode>();
self->current_class = current_class;
return self;
@@ -2320,6 +2315,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression
operation->operation = BinaryOpNode::OP_MODULO;
operation->variant_op = Variant::OP_MODULE;
break;
+ case GDScriptTokenizer::Token::STAR_STAR:
+ operation->operation = BinaryOpNode::OP_POWER;
+ operation->variant_op = Variant::OP_POWER;
+ break;
case GDScriptTokenizer::Token::LESS_LESS:
operation->operation = BinaryOpNode::OP_BIT_LEFT_SHIFT;
operation->variant_op = Variant::OP_SHIFT_LEFT;
@@ -2415,6 +2414,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
push_error("Assignment is not allowed inside an expression.");
return parse_expression(false); // Return the following expression.
}
+ if (p_previous_operand == nullptr) {
+ return parse_expression(false); // Return the following expression.
+ }
#ifdef DEBUG_ENABLED
VariableNode *source_variable = nullptr;
@@ -2480,6 +2482,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
assignment->operation = AssignmentNode::OP_MULTIPLICATION;
assignment->variant_op = Variant::OP_MULTIPLY;
break;
+ case GDScriptTokenizer::Token::STAR_STAR_EQUAL:
+ assignment->operation = AssignmentNode::OP_POWER;
+ assignment->variant_op = Variant::OP_POWER;
+ break;
case GDScriptTokenizer::Token::SLASH_EQUAL:
assignment->operation = AssignmentNode::OP_DIVISION;
assignment->variant_op = Variant::OP_DIVIDE;
@@ -2680,7 +2686,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *
const IdentifierNode *id = static_cast<const IdentifierNode *>(p_previous_operand);
Variant::Type builtin_type = get_builtin_type(id->name);
if (builtin_type < Variant::VARIANT_MAX) {
- make_completion_context(COMPLETION_BUILT_IN_TYPE_CONSTANT, builtin_type, true);
+ make_completion_context(COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD, builtin_type, true);
is_builtin = true;
}
}
@@ -2689,12 +2695,13 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *
}
}
- attribute->is_attribute = true;
attribute->base = p_previous_operand;
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier after "." for attribute access.)")) {
return attribute;
}
+
+ attribute->is_attribute = true;
attribute->attribute = parse_identifier();
return attribute;
@@ -2746,7 +2753,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
pop_multiline();
return nullptr;
}
- call->function_name = current_function->identifier->name;
+ if (current_function->identifier) {
+ call->function_name = current_function->identifier->name;
+ } else {
+ call->function_name = SNAME("<anonymous>");
+ }
} else {
consume(GDScriptTokenizer::Token::PERIOD, R"(Expected "." or "(" after "super".)");
make_completion_context(COMPLETION_SUPER_METHOD, call, true);
@@ -3042,7 +3053,7 @@ bool GDScriptParser::has_comment(int p_line) {
}
String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
- const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
+ const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
ERR_FAIL_COND_V(!comments.has(p_line), String());
if (p_single_line) {
@@ -3094,7 +3105,7 @@ String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
}
void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class) {
- const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
+ const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
if (!comments.has(p_line)) {
return;
}
@@ -3257,6 +3268,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // PLUS,
{ &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // MINUS,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR,
+ { nullptr, &GDScriptParser::parse_binary_operator, PREC_POWER }, // STAR_STAR,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT,
// Assignment
@@ -3264,6 +3276,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PLUS_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // MINUS_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_EQUAL,
+ { nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // STAR_STAR_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // SLASH_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PERCENT_EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // LESS_LESS_EQUAL,
@@ -3476,6 +3489,15 @@ template <PropertyHint t_hint, Variant::Type t_type>
bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node *p_node) {
ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
+ {
+ const int max_flags = 32;
+
+ if (t_hint == PropertyHint::PROPERTY_HINT_FLAGS && p_annotation->resolved_arguments.size() > max_flags) {
+ push_error(vformat(R"(The argument count limit for "@export_flags" is exceeded (%d/%d).)", p_annotation->resolved_arguments.size(), max_flags), p_annotation);
+ return false;
+ }
+ }
+
VariableNode *variable = static_cast<VariableNode *>(p_node);
if (variable->exported) {
push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
@@ -3500,7 +3522,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
// This is called after the analyzer is done finding the type, so this should be set here.
DataType export_type = variable->get_datatype();
- if (p_annotation->name == "@export") {
+ if (p_annotation->name == SNAME("@export")) {
if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
push_error(R"(Cannot use simple "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
return false;
@@ -3525,7 +3547,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint_string = Variant::get_type_name(export_type.builtin_type);
break;
case GDScriptParser::DataType::NATIVE:
- if (ClassDB::is_parent_class(export_type.native_type, "Resource")) {
+ if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
variable->export_info.hint_string = export_type.native_type;
@@ -3539,14 +3561,16 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint = PROPERTY_HINT_ENUM;
String enum_hint_string;
- for (OrderedHashMap<StringName, int>::Element E = export_type.enum_values.front(); E; E = E.next()) {
- enum_hint_string += E.key().operator String().capitalize().xml_escape();
- enum_hint_string += ":";
- enum_hint_string += String::num_int64(E.value()).xml_escape();
-
- if (E.next()) {
+ bool first = true;
+ for (const KeyValue<StringName, int> &E : export_type.enum_values) {
+ if (!first) {
enum_hint_string += ",";
+ } else {
+ first = false;
}
+ enum_hint_string += E.key.operator String().capitalize().xml_escape();
+ enum_hint_string += ":";
+ enum_hint_string += String::num_int64(E.value).xml_escape();
}
variable->export_info.hint_string = enum_hint_string;
@@ -3737,8 +3761,6 @@ String GDScriptParser::DataType::to_string() const {
}
case ENUM:
return enum_type.operator String() + " (enum)";
- case ENUM_VALUE:
- return enum_type.operator String() + " (enum value)";
case UNRESOLVED:
return "<unresolved type>";
}
@@ -3881,6 +3903,9 @@ void GDScriptParser::TreePrinter::print_assignment(AssignmentNode *p_assignment)
case AssignmentNode::OP_MODULO:
push_text("%");
break;
+ case AssignmentNode::OP_POWER:
+ push_text("**");
+ break;
case AssignmentNode::OP_BIT_SHIFT_LEFT:
push_text("<<");
break;
@@ -3929,6 +3954,9 @@ void GDScriptParser::TreePrinter::print_binary_op(BinaryOpNode *p_binary_op) {
case BinaryOpNode::OP_MODULO:
push_text(" % ");
break;
+ case BinaryOpNode::OP_POWER:
+ push_text(" ** ");
+ break;
case BinaryOpNode::OP_BIT_LEFT_SHIFT:
push_text(" << ");
break;