diff options
Diffstat (limited to 'modules/gdscript/gdscript_parser.cpp')
-rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 127 |
1 files changed, 106 insertions, 21 deletions
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index afefecbdf3..8c110143b8 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -1164,6 +1164,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s bool unary = false; bool ternary = false; bool error = false; + bool right_to_left = false; switch (expression[i].op) { @@ -1218,11 +1219,13 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s case OperatorNode::OP_TERNARY_IF: priority = 14; ternary = true; + right_to_left = true; break; case OperatorNode::OP_TERNARY_ELSE: priority = 14; error = true; - break; // Errors out when found without IF (since IF would consume it) + // Rigth-to-left should be false in this case, otherwise it would always error. + break; case OperatorNode::OP_ASSIGN: priority = 15; break; case OperatorNode::OP_ASSIGN_ADD: priority = 15; break; @@ -1242,13 +1245,13 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } } - if (priority < min_priority) { + if (priority < min_priority || (right_to_left && priority == min_priority)) { + // < is used for left to right (default) + // <= is used for right to left if (error) { _set_error("Unexpected operator"); return NULL; } - // < is used for left to right (default) - // <= is used for right to left next_op = i; min_priority = priority; is_unary = unary; @@ -2995,18 +2998,36 @@ void GDScriptParser::_parse_extends(ClassNode *p_class) { } while (true) { - if (tokenizer->get_token() != GDScriptTokenizer::TK_IDENTIFIER) { - _set_error("Invalid 'extends' syntax, expected string constant (path) and/or identifier (parent class)."); - return; - } + switch (tokenizer->get_token()) { - StringName identifier = tokenizer->get_token_identifier(); - p_class->extends_class.push_back(identifier); + case GDScriptTokenizer::TK_IDENTIFIER: { + + StringName identifier = tokenizer->get_token_identifier(); + p_class->extends_class.push_back(identifier); + } break; + + case GDScriptTokenizer::TK_PERIOD: + break; + + default: { + + _set_error("Invalid 'extends' syntax, expected string constant (path) and/or identifier (parent class)."); + return; + } + } tokenizer->advance(1); - if (tokenizer->get_token() != GDScriptTokenizer::TK_PERIOD) - return; + + switch (tokenizer->get_token()) { + + case GDScriptTokenizer::TK_IDENTIFIER: + case GDScriptTokenizer::TK_PERIOD: + continue; + + default: + return; + } } } @@ -3395,6 +3416,10 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("Can't export null type."); return; } + if (type == Variant::OBJECT) { + _set_error("Can't export raw object type."); + return; + } current_export.type = type; current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; tokenizer->advance(); @@ -3782,22 +3807,82 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { current_export.hint = PROPERTY_HINT_NONE; } - } else if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER) { + } else { - String identifier = tokenizer->get_token_identifier(); - if (!ClassDB::is_parent_class(identifier, "Resource")) { + parenthesis++; + Node *subexpr = _parse_and_reduce_expression(p_class, true, true); + if (!subexpr) { + if (_recover_from_completion()) { + break; + } + return; + } + parenthesis--; + if (subexpr->type != Node::TYPE_CONSTANT) { current_export = PropertyInfo(); - _set_error("Export hint not a type or resource."); + _set_error("Expected a constant expression."); } - current_export.type = Variant::OBJECT; - current_export.hint = PROPERTY_HINT_RESOURCE_TYPE; - current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; + Variant constant = static_cast<ConstantNode *>(subexpr)->value; - current_export.hint_string = identifier; + if (constant.get_type() == Variant::OBJECT) { + GDScriptNativeClass *native_class = Object::cast_to<GDScriptNativeClass>(constant); - tokenizer->advance(); + if (native_class && ClassDB::is_parent_class(native_class->get_name(), "Resource")) { + current_export.type = Variant::OBJECT; + current_export.hint = PROPERTY_HINT_RESOURCE_TYPE; + current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; + + current_export.hint_string = native_class->get_name(); + + } else { + current_export = PropertyInfo(); + _set_error("Export hint not a resource type."); + } + } else if (constant.get_type() == Variant::DICTIONARY) { + // Enumeration + bool is_flags = false; + + if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { + tokenizer->advance(); + + if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "FLAGS") { + is_flags = true; + tokenizer->advance(); + } else { + current_export = PropertyInfo(); + _set_error("Expected 'FLAGS' after comma."); + } + } + + current_export.type = Variant::INT; + current_export.hint = is_flags ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM; + Dictionary enum_values = constant; + + List<Variant> keys; + enum_values.get_key_list(&keys); + + bool first = true; + for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { + if (enum_values[E->get()].get_type() == Variant::INT) { + if (!first) + current_export.hint_string += ","; + else + first = false; + + current_export.hint_string += E->get().operator String().camelcase_to_underscore(true).capitalize().xml_escape(); + if (!is_flags) { + current_export.hint_string += ":"; + current_export.hint_string += enum_values[E->get()].operator String().xml_escape(); + } + } + } + } else { + current_export = PropertyInfo(); + _set_error("Expected type for export."); + return; + } } if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { |