aboutsummaryrefslogtreecommitdiff
path: root/modules/gdscript/gd_parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gd_parser.cpp')
-rw-r--r--modules/gdscript/gd_parser.cpp828
1 files changed, 815 insertions, 13 deletions
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 1f2b7291e..70659326e 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -5,7 +5,7 @@
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -225,8 +225,8 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide
GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_allow_assign,bool p_parsing_constant) {
-// Vector<Node*> expressions;
-// Vector<OperatorNode::Operator> operators;
+ //Vector<Node*> expressions;
+ //Vector<OperatorNode::Operator> operators;
Vector<Expression> expression;
@@ -265,6 +265,98 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
tokenizer->advance();
expr=subexpr;
+ } else if (tokenizer->get_token()==GDTokenizer::TK_DOLLAR) {
+ tokenizer->advance();
+
+ String path;
+
+ bool need_identifier=true;
+ bool done=false;
+
+ while(!done) {
+
+ switch(tokenizer->get_token()) {
+ case GDTokenizer::TK_CURSOR: {
+ completion_cursor=StringName();
+ completion_type=COMPLETION_GET_NODE;
+ completion_class=current_class;
+ completion_function=current_function;
+ completion_line=tokenizer->get_token_line();
+ completion_cursor=path;
+ completion_argument=0;
+ completion_block=current_block;
+ completion_found=true;
+ tokenizer->advance();
+ } break;
+ case GDTokenizer::TK_CONSTANT: {
+
+ if (!need_identifier) {
+ done=true;
+ break;
+ }
+
+ if (tokenizer->get_token_constant().get_type()!=Variant::STRING) {
+ _set_error("Expected string constant or identifier after '$' or '/'.");
+ return NULL;
+ }
+
+ path+=String(tokenizer->get_token_constant());
+ tokenizer->advance();
+ need_identifier=false;
+
+ } break;
+ case GDTokenizer::TK_IDENTIFIER: {
+ if (!need_identifier) {
+ done=true;
+ break;
+ }
+
+ path+=String(tokenizer->get_token_identifier());
+ tokenizer->advance();
+ need_identifier=false;
+
+ } break;
+ case GDTokenizer::TK_OP_DIV: {
+
+ if (need_identifier) {
+ done=true;
+ break;
+ }
+
+ path+="/";
+ tokenizer->advance();
+ need_identifier=true;
+
+ } break;
+ default: {
+ done=true;
+ break;
+ }
+ }
+ }
+
+ if (path=="") {
+ _set_error("Path expected after $.");
+ return NULL;
+
+ }
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=OperatorNode::OP_CALL;
+
+ op->arguments.push_back(alloc_node<SelfNode>());
+
+ IdentifierNode *funcname = alloc_node<IdentifierNode>();
+ funcname->name="get_node";
+
+ op->arguments.push_back(funcname);
+
+ ConstantNode *nodepath = alloc_node<ConstantNode>();
+ nodepath->value = NodePath(StringName(path));
+ op->arguments.push_back(nodepath);
+
+ expr=op;
+
} else if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) {
tokenizer->advance();
continue; //no point in cursor in the middle of expression
@@ -551,7 +643,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
case GDTokenizer::TK_OP_ADD: e.op=OperatorNode::OP_POS; break;
case GDTokenizer::TK_OP_SUB: e.op=OperatorNode::OP_NEG; break;
case GDTokenizer::TK_OP_NOT: e.op=OperatorNode::OP_NOT; break;
- case GDTokenizer::TK_OP_BIT_INVERT: e.op=OperatorNode::OP_BIT_INVERT;; break;
+ case GDTokenizer::TK_OP_BIT_INVERT: e.op=OperatorNode::OP_BIT_INVERT; break;
default: {}
}
@@ -632,6 +724,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
};
Node *key=NULL;
+ Set<Variant> keys;
DictExpect expecting=DICT_EXPECT_KEY;
@@ -727,6 +820,16 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
return NULL;
expecting=DICT_EXPECT_COMMA;
+ if (key->type == GDParser::Node::TYPE_CONSTANT) {
+ Variant const& keyName = static_cast<const GDParser::ConstantNode*>(key)->value;
+
+ if (keys.has(keyName)) {
+ _set_error("Duplicate key found in Dictionary literal");
+ return NULL;
+ }
+ keys.insert(keyName);
+ }
+
DictionaryNode::Pair pair;
pair.key=key;
pair.value=value;
@@ -940,8 +1043,8 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
case GDTokenizer::TK_OP_ASSIGN_MUL: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_MUL ; break;
case GDTokenizer::TK_OP_ASSIGN_DIV: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_DIV ; break;
case GDTokenizer::TK_OP_ASSIGN_MOD: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_MOD ; break;
- case GDTokenizer::TK_OP_ASSIGN_SHIFT_LEFT: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_SHIFT_LEFT; ; break;
- case GDTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_SHIFT_RIGHT; ; break;
+ case GDTokenizer::TK_OP_ASSIGN_SHIFT_LEFT: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_SHIFT_LEFT; break;
+ case GDTokenizer::TK_OP_ASSIGN_SHIFT_RIGHT: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_SHIFT_RIGHT; break;
case GDTokenizer::TK_OP_ASSIGN_BIT_AND: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_BIT_AND ; break;
case GDTokenizer::TK_OP_ASSIGN_BIT_OR: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_BIT_OR ; break;
case GDTokenizer::TK_OP_ASSIGN_BIT_XOR: _VALIDATE_ASSIGN op=OperatorNode::OP_ASSIGN_BIT_XOR ; break;
@@ -1225,7 +1328,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
//reduce constant array expression
ConstantNode *cn = alloc_node<ConstantNode>();
- Array arr(!p_to_const);
+ Array arr;
//print_line("mk array "+itos(!p_to_const));
arr.resize(an->elements.size());
for(int i=0;i<an->elements.size();i++) {
@@ -1260,7 +1363,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
//reduce constant array expression
ConstantNode *cn = alloc_node<ConstantNode>();
- Dictionary dict(!p_to_const);
+ Dictionary dict;
for(int i=0;i<dn->elements.size();i++) {
ConstantNode *key_c = static_cast<ConstantNode*>(dn->elements[i].key);
ConstantNode *value_c = static_cast<ConstantNode*>(dn->elements[i].value);
@@ -1478,6 +1581,15 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
return op;
}
+ if (op->arguments[0]->type==Node::TYPE_OPERATOR) {
+ OperatorNode *on = static_cast<OperatorNode*>(op->arguments[0]);
+ if (on->op != OperatorNode::OP_INDEX && on->op != OperatorNode::OP_INDEX_NAMED) {
+ _set_error("Can't assign to an expression",tokenizer->get_token_line()-1);
+ error_line=op->line;
+ return op;
+ }
+ }
+
} break;
default: { break; }
}
@@ -1578,6 +1690,542 @@ bool GDParser::_recover_from_completion() {
return true;
}
+GDParser::PatternNode *GDParser::_parse_pattern(bool p_static)
+{
+
+ PatternNode *pattern = alloc_node<PatternNode>();
+
+ GDTokenizer::Token token = tokenizer->get_token();
+ if (error_set)
+ return NULL;
+
+ if (token == GDTokenizer::TK_EOF) {
+ return NULL;
+ }
+
+ switch (token) {
+ // dictionary
+ case GDTokenizer::TK_BRACKET_OPEN: {
+ tokenizer->advance();
+ pattern->pt_type = GDParser::PatternNode::PT_ARRAY;
+ while (true) {
+
+ if (tokenizer->get_token() == GDTokenizer::TK_BRACKET_CLOSE) {
+ tokenizer->advance();
+ break;
+ }
+
+ if (tokenizer->get_token() == GDTokenizer::TK_PERIOD && tokenizer->get_token(1) == GDTokenizer::TK_PERIOD) {
+ // match everything
+ tokenizer->advance(2);
+ PatternNode *sub_pattern = alloc_node<PatternNode>();
+ sub_pattern->pt_type = GDParser::PatternNode::PT_IGNORE_REST;
+ pattern->array.push_back(sub_pattern);
+ if (tokenizer->get_token() == GDTokenizer::TK_COMMA && tokenizer->get_token(1) == GDTokenizer::TK_BRACKET_CLOSE) {
+ tokenizer->advance(2);
+ break;
+ } else if (tokenizer->get_token() == GDTokenizer::TK_BRACKET_CLOSE) {
+ tokenizer->advance(1);
+ break;
+ } else {
+ _set_error("'..' pattern only allowed at the end of an array pattern");
+ return NULL;
+ }
+ }
+
+ PatternNode *sub_pattern = _parse_pattern(p_static);
+ if (!sub_pattern) {
+ return NULL;
+ }
+
+ pattern->array.push_back(sub_pattern);
+
+ if (tokenizer->get_token() == GDTokenizer::TK_COMMA) {
+ tokenizer->advance();
+ continue;
+ } else if (tokenizer->get_token() == GDTokenizer::TK_BRACKET_CLOSE) {
+ tokenizer->advance();
+ break;
+ } else {
+ _set_error("Not a valid pattern");
+ return NULL;
+ }
+ }
+ } break;
+ // bind
+ case GDTokenizer::TK_PR_VAR: {
+ tokenizer->advance();
+ pattern->pt_type = GDParser::PatternNode::PT_BIND;
+ pattern->bind = tokenizer->get_token_identifier();
+ tokenizer->advance();
+ } break;
+ // array
+ case GDTokenizer::TK_CURLY_BRACKET_OPEN: {
+ tokenizer->advance();
+ pattern->pt_type = GDParser::PatternNode::PT_DICTIONARY;
+ while (true) {
+
+ if (tokenizer->get_token() == GDTokenizer::TK_CURLY_BRACKET_CLOSE) {
+ tokenizer->advance();
+ break;
+ }
+
+ if (tokenizer->get_token() == GDTokenizer::TK_PERIOD && tokenizer->get_token(1) == GDTokenizer::TK_PERIOD) {
+ // match everything
+ tokenizer->advance(2);
+ PatternNode *sub_pattern = alloc_node<PatternNode>();
+ sub_pattern->pt_type = PatternNode::PT_IGNORE_REST;
+ pattern->array.push_back(sub_pattern);
+ if (tokenizer->get_token() == GDTokenizer::TK_COMMA && tokenizer->get_token(1) == GDTokenizer::TK_CURLY_BRACKET_CLOSE) {
+ tokenizer->advance(2);
+ break;
+ } else if (tokenizer->get_token() == GDTokenizer::TK_CURLY_BRACKET_CLOSE) {
+ tokenizer->advance(1);
+ break;
+ } else {
+ _set_error("'..' pattern only allowed at the end of an dictionary pattern");
+ return NULL;
+ }
+ }
+
+ Node *key = _parse_and_reduce_expression(pattern, p_static);
+ if (!key) {
+ _set_error("Not a valid key in pattern");
+ return NULL;
+ }
+
+ if (key->type != GDParser::Node::TYPE_CONSTANT) {
+ _set_error("Not a constant expression as key");
+ return NULL;
+ }
+
+ if (tokenizer->get_token() == GDTokenizer::TK_COLON) {
+ tokenizer->advance();
+
+ PatternNode *value = _parse_pattern(p_static);
+ if (!value) {
+ _set_error("Expected pattern in dictionary value");
+ return NULL;
+ }
+
+ pattern->dictionary.insert(static_cast<ConstantNode*>(key), value);
+ } else {
+ pattern->dictionary.insert(static_cast<ConstantNode*>(key), NULL);
+ }
+
+
+ if (tokenizer->get_token() == GDTokenizer::TK_COMMA) {
+ tokenizer->advance();
+ continue;
+ } else if (tokenizer->get_token() == GDTokenizer::TK_CURLY_BRACKET_CLOSE) {
+ tokenizer->advance();
+ break;
+ } else {
+ _set_error("Not a valid pattern");
+ return NULL;
+ }
+ }
+ } break;
+ // all the constants like strings and numbers
+ default: {
+ Node *value = _parse_and_reduce_expression(pattern, p_static);
+ if (error_set) {
+ return NULL;
+ }
+ if (value->type == Node::TYPE_IDENTIFIER && static_cast<IdentifierNode*>(value)->name == "_") {
+ // wildcard pattern
+ pattern->pt_type = PatternNode::PT_WILDCARD;
+ break;
+ }
+
+ if (value->type != Node::TYPE_IDENTIFIER && value->type != Node::TYPE_CONSTANT) {
+ _set_error("Only constant expressions or variables allowed in a pattern");
+ return NULL;
+ }
+
+ pattern->pt_type = PatternNode::PT_CONSTANT;
+ pattern->constant = value;
+ } break;
+ }
+
+ return pattern;
+}
+
+void GDParser::_parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode*> &p_branches, bool p_static)
+{
+ int indent_level = tab_level.back()->get();
+
+ while (true) {
+
+ while (tokenizer->get_token() == GDTokenizer::TK_NEWLINE && _parse_newline());
+
+ // GDTokenizer::Token token = tokenizer->get_token();
+ if (error_set)
+ return;
+
+ if (indent_level > tab_level.back()->get()) {
+ return; // go back a level
+ }
+
+ if (pending_newline!=-1) {
+ pending_newline=-1;
+ }
+
+ PatternBranchNode *branch = alloc_node<PatternBranchNode>();
+
+ branch->patterns.push_back(_parse_pattern(p_static));
+ if (!branch->patterns[0]) {
+ return;
+ }
+
+ while (tokenizer->get_token() == GDTokenizer::TK_COMMA) {
+ tokenizer->advance();
+ branch->patterns.push_back(_parse_pattern(p_static));
+ if (!branch->patterns[branch->patterns.size() - 1]) {
+ return;
+ }
+ }
+
+ if(!_enter_indent_block()) {
+ _set_error("Expected block in pattern branch");
+ return;
+ }
+
+ branch->body = alloc_node<BlockNode>();
+ branch->body->parent_block = p_block;
+ p_block->sub_blocks.push_back(branch->body);
+ current_block = branch->body;
+
+ _parse_block(branch->body, p_static);
+
+ current_block = p_block;
+
+ p_branches.push_back(branch);
+ }
+}
+
+
+void GDParser::_generate_pattern(PatternNode *p_pattern, Node *p_node_to_match, Node *&p_resulting_node, Map<StringName, Node*> &p_bindings)
+{
+ switch (p_pattern->pt_type) {
+ case PatternNode::PT_CONSTANT: {
+
+ // typecheck
+ BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
+ typeof_node->function = GDFunctions::TYPE_OF;
+
+ OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
+ typeof_match_value->op = OperatorNode::OP_CALL;
+ typeof_match_value->arguments.push_back(typeof_node);
+ typeof_match_value->arguments.push_back(p_node_to_match);
+
+ OperatorNode *typeof_pattern_value = alloc_node<OperatorNode>();
+ typeof_pattern_value->op = OperatorNode::OP_CALL;
+ typeof_pattern_value->arguments.push_back(typeof_node);
+ typeof_pattern_value->arguments.push_back(p_pattern->constant);
+
+ OperatorNode *type_comp = alloc_node<OperatorNode>();
+ type_comp->op = OperatorNode::OP_EQUAL;
+ type_comp->arguments.push_back(typeof_match_value);
+ type_comp->arguments.push_back(typeof_pattern_value);
+
+
+ // comare the actual values
+ OperatorNode *value_comp = alloc_node<OperatorNode>();
+ value_comp->op = OperatorNode::OP_EQUAL;
+ value_comp->arguments.push_back(p_pattern->constant);
+ value_comp->arguments.push_back(p_node_to_match);
+
+
+ OperatorNode *comparison = alloc_node<OperatorNode>();
+ comparison->op = OperatorNode::OP_AND;
+ comparison->arguments.push_back(type_comp);
+ comparison->arguments.push_back(value_comp);
+
+ p_resulting_node = comparison;
+
+ } break;
+ case PatternNode::PT_BIND: {
+ p_bindings[p_pattern->bind] = p_node_to_match;
+
+ // a bind always matches
+ ConstantNode *true_value = alloc_node<ConstantNode>();
+ true_value->value = Variant(true);
+ p_resulting_node = true_value;
+ } break;
+ case PatternNode::PT_ARRAY: {
+
+ bool open_ended = false;
+
+ if (p_pattern->array.size() > 0) {
+ if (p_pattern->array[p_pattern->array.size() - 1]->pt_type == PatternNode::PT_IGNORE_REST) {
+ open_ended = true;
+ }
+ }
+
+ // typeof(value_to_match) == TYPE_ARRAY && value_to_match.size() >= length
+ // typeof(value_to_match) == TYPE_ARRAY && value_to_match.size() == length
+
+ {
+ // typecheck
+ BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
+ typeof_node->function = GDFunctions::TYPE_OF;
+
+ OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
+ typeof_match_value->op = OperatorNode::OP_CALL;
+ typeof_match_value->arguments.push_back(typeof_node);
+ typeof_match_value->arguments.push_back(p_node_to_match);
+
+ IdentifierNode *typeof_array = alloc_node<IdentifierNode>();
+ typeof_array->name = "TYPE_ARRAY";
+
+ OperatorNode *type_comp = alloc_node<OperatorNode>();
+ type_comp->op = OperatorNode::OP_EQUAL;
+ type_comp->arguments.push_back(typeof_match_value);
+ type_comp->arguments.push_back(typeof_array);
+
+
+ // size
+ ConstantNode *length = alloc_node<ConstantNode>();
+ length->value = Variant(open_ended ? p_pattern->array.size() - 1 : p_pattern->array.size());
+
+ OperatorNode *call = alloc_node<OperatorNode>();
+ call->op = OperatorNode::OP_CALL;
+ call->arguments.push_back(p_node_to_match);
+
+ IdentifierNode *size = alloc_node<IdentifierNode>();
+ size->name = "size";
+ call->arguments.push_back(size);
+
+ OperatorNode *length_comparison = alloc_node<OperatorNode>();
+ length_comparison->op = open_ended ? OperatorNode::OP_GREATER_EQUAL : OperatorNode::OP_EQUAL;
+ length_comparison->arguments.push_back(call);
+ length_comparison->arguments.push_back(length);
+
+ OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
+ type_and_length_comparison->op = OperatorNode::OP_AND;
+ type_and_length_comparison->arguments.push_back(type_comp);
+ type_and_length_comparison->arguments.push_back(length_comparison);
+
+ p_resulting_node = type_and_length_comparison;
+ }
+
+
+
+ for (int i = 0; i < p_pattern->array.size(); i++) {
+ PatternNode *pattern = p_pattern->array[i];
+
+ Node *condition = NULL;
+
+ ConstantNode *index = alloc_node<ConstantNode>();
+ index->value = Variant(i);
+
+ OperatorNode *indexed_value = alloc_node<OperatorNode>();
+ indexed_value->op = OperatorNode::OP_INDEX;
+ indexed_value->arguments.push_back(p_node_to_match);
+ indexed_value->arguments.push_back(index);
+
+ _generate_pattern(pattern, indexed_value, condition, p_bindings);
+
+ // concatenate all the patterns with &&
+ OperatorNode *and_node = alloc_node<OperatorNode>();
+ and_node->op = OperatorNode::OP_AND;
+ and_node->arguments.push_back(p_resulting_node);
+ and_node->arguments.push_back(condition);
+
+ p_resulting_node = and_node;
+ }
+
+
+ } break;
+ case PatternNode::PT_DICTIONARY: {
+
+ bool open_ended = false;
+
+ if (p_pattern->array.size() > 0) {
+ open_ended = true;
+ }
+
+ // typeof(value_to_match) == TYPE_DICTIONARY && value_to_match.size() >= length
+ // typeof(value_to_match) == TYPE_DICTIONARY && value_to_match.size() == length
+
+
+ {
+ // typecheck
+ BuiltInFunctionNode *typeof_node = alloc_node<BuiltInFunctionNode>();
+ typeof_node->function = GDFunctions::TYPE_OF;
+
+ OperatorNode *typeof_match_value = alloc_node<OperatorNode>();
+ typeof_match_value->op = OperatorNode::OP_CALL;
+ typeof_match_value->arguments.push_back(typeof_node);
+ typeof_match_value->arguments.push_back(p_node_to_match);
+
+ IdentifierNode *typeof_dictionary = alloc_node<IdentifierNode>();
+ typeof_dictionary->name = "TYPE_DICTIONARY";
+
+ OperatorNode *type_comp = alloc_node<OperatorNode>();
+ type_comp->op = OperatorNode::OP_EQUAL;
+ type_comp->arguments.push_back(typeof_match_value);
+ type_comp->arguments.push_back(typeof_dictionary);
+
+ // size
+ ConstantNode *length = alloc_node<ConstantNode>();
+ length->value = Variant(open_ended ? p_pattern->dictionary.size() - 1 : p_pattern->dictionary.size());
+
+ OperatorNode *call = alloc_node<OperatorNode>();
+ call->op = OperatorNode::OP_CALL;
+ call->arguments.push_back(p_node_to_match);
+
+ IdentifierNode *size = alloc_node<IdentifierNode>();
+ size->name = "size";
+ call->arguments.push_back(size);
+
+ OperatorNode *length_comparison = alloc_node<OperatorNode>();
+ length_comparison->op = open_ended ? OperatorNode::OP_GREATER_EQUAL : OperatorNode::OP_EQUAL;
+ length_comparison->arguments.push_back(call);
+ length_comparison->arguments.push_back(length);
+
+ OperatorNode *type_and_length_comparison = alloc_node<OperatorNode>();
+ type_and_length_comparison->op = OperatorNode::OP_AND;
+ type_and_length_comparison->arguments.push_back(type_comp);
+ type_and_length_comparison->arguments.push_back(length_comparison);
+
+ p_resulting_node = type_and_length_comparison;
+ }
+
+
+
+ for (Map<ConstantNode*, PatternNode*>::Element *e = p_pattern->dictionary.front(); e; e = e->next()) {
+
+ Node *condition = NULL;
+
+ // chech for has, then for pattern
+
+ IdentifierNode *has = alloc_node<IdentifierNode>();
+ has->name = "has";
+
+ OperatorNode *has_call = alloc_node<OperatorNode>();
+ has_call->op = OperatorNode::OP_CALL;
+ has_call->arguments.push_back(p_node_to_match);
+ has_call->arguments.push_back(has);
+ has_call->arguments.push_back(e->key());
+
+
+ if (e->value()) {
+
+ OperatorNode *indexed_value = alloc_node<OperatorNode>();
+ indexed_value->op = OperatorNode::OP_INDEX;
+ indexed_value->arguments.push_back(p_node_to_match);
+ indexed_value->arguments.push_back(e->key());
+
+ _generate_pattern(e->value(), indexed_value, condition, p_bindings);
+
+ OperatorNode *has_and_pattern = alloc_node<OperatorNode>();
+ has_and_pattern->op = OperatorNode::OP_AND;
+ has_and_pattern->arguments.push_back(has_call);
+ has_and_pattern->arguments.push_back(condition);
+
+ condition = has_and_pattern;
+
+ } else {
+ condition = has_call;
+ }
+
+
+
+ // concatenate all the patterns with &&
+ OperatorNode *and_node = alloc_node<OperatorNode>();
+ and_node->op = OperatorNode::OP_AND;
+ and_node->arguments.push_back(p_resulting_node);
+ and_node->arguments.push_back(condition);
+
+ p_resulting_node = and_node;
+ }
+
+ } break;
+ case PatternNode::PT_IGNORE_REST:
+ case PatternNode::PT_WILDCARD: {
+ // simply generate a `true`
+ ConstantNode *true_value = alloc_node<ConstantNode>();
+ true_value->value = Variant(true);
+ p_resulting_node = true_value;
+ } break;
+ default: {
+
+ } break;
+ }
+}
+
+void GDParser::_transform_match_statment(BlockNode *p_block, MatchNode *p_match_statement)
+{
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name = "#match_value";
+
+ for (int i = 0; i < p_match_statement->branches.size(); i++) {
+
+ PatternBranchNode *branch = p_match_statement->branches[i];
+
+ MatchNode::CompiledPatternBranch compiled_branch;
+ compiled_branch.compiled_pattern = NULL;
+
+ Map<StringName, Node*> binding;
+
+ for (int j = 0; j < branch->patterns.size(); j++) {
+ PatternNode *pattern = branch->patterns[j];
+
+ Map<StringName, Node*> bindings;
+ Node *resulting_node;
+ _generate_pattern(pattern, id, resulting_node, bindings);
+
+ if (!binding.empty() && !bindings.empty()) {
+ _set_error("Multipatterns can't contain bindings");
+ return;
+ } else {
+ binding = bindings;
+ }
+
+ if (compiled_branch.compiled_pattern) {
+ OperatorNode *or_node = alloc_node<OperatorNode>();
+ or_node->op = OperatorNode::OP_OR;
+ or_node->arguments.push_back(compiled_branch.compiled_pattern);
+ or_node->arguments.push_back(resulting_node);
+
+ compiled_branch.compiled_pattern = or_node;
+ } else {
+ // single pattern | first one
+ compiled_branch.compiled_pattern = resulting_node;
+ }
+
+ }
+
+
+ // prepare the body ...hehe
+ for (Map<StringName, Node*>::Element *e = binding.front(); e; e = e->next()) {
+ LocalVarNode *local_var = alloc_node<LocalVarNode>();
+ local_var->name = e->key();
+ local_var->assign = e->value();
+
+
+ IdentifierNode *id = alloc_node<IdentifierNode>();
+ id->name = local_var->name;
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=OperatorNode::OP_ASSIGN;
+ op->arguments.push_back(id);
+ op->arguments.push_back(local_var->assign);
+
+ branch->body->statements.push_front(op);
+ branch->body->statements.push_front(local_var);
+ }
+
+ compiled_branch.body = branch->body;
+
+
+ p_match_statement->compiled_pattern_branches.push_back(compiled_branch);
+ }
+
+}
+
void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
int indent_level = tab_level.back()->get();
@@ -1661,6 +2309,24 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
}
StringName n = tokenizer->get_token_identifier();
tokenizer->advance();
+ if (current_function){
+ for (int i=0;i<current_function->arguments.size();i++){
+ if (n == current_function->arguments[i]){
+ _set_error("Variable '"+String(n)+"' already defined in the scope (at line: "+itos(current_function->line)+").");
+ return;
+ }
+ }
+ }
+ BlockNode *check_block = p_block;
+ while (check_block){
+ for (int i=0;i<check_block->variables.size();i++){
+ if (n == check_block->variables[i]){
+ _set_error("Variable '"+String(n)+"' already defined in the scope (at line: "+itos(check_block->variable_lines[i])+").");
+ return;
+ }
+ }
+ check_block = check_block->parent_block;
+ }
p_block->variables.push_back(n); //line?
p_block->variable_lines.push_back(tokenizer->get_token_line());
@@ -1903,6 +2569,64 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
return;
}
+ if (container->type==Node::TYPE_OPERATOR) {
+
+ OperatorNode* op = static_cast<OperatorNode*>(container);
+ if (op->op==OperatorNode::OP_CALL && op->arguments[0]->type==Node::TYPE_BUILT_IN_FUNCTION && static_cast<BuiltInFunctionNode*>(op->arguments[0])->function==GDFunctions::GEN_RANGE) {
+ //iterating a range, so see if range() can be optimized without allocating memory, by replacing it by vectors (which can work as iterable too!)
+
+ Vector<Node*> args;
+ Vector<double> constants;
+
+ bool constant=true;
+
+ for(int i=1;i<op->arguments.size();i++) {
+ args.push_back(op->arguments[i]);
+ if (constant && op->arguments[i]->type==Node::TYPE_CONSTANT) {
+ ConstantNode *c = static_cast<ConstantNode*>(op->arguments[i]);
+ if (c->value.get_type()==Variant::REAL || c->value.get_type()==Variant::INT) {
+ constants.push_back(c->value);
+ } else {
+ constant=false;
+ }
+ }
+ }
+
+ if (args.size()>0 || args.size()<4) {
+
+ if (constant) {
+
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ switch(args.size()) {
+ case 1: cn->value=constants[0]; break;
+ case 2: cn->value=Vector2(constants[0],constants[1]); break;
+ case 3: cn->value=Vector3(constants[0],constants[1],constants[2]); break;
+ }
+ container=cn;
+ } else {
+ OperatorNode *on = alloc_node<OperatorNode>();
+ on->op=OperatorNode::OP_CALL;
+
+ TypeNode *tn = alloc_node<TypeNode>();
+ on->arguments.push_back(tn);
+
+ switch(args.size()) {
+ case 1: tn->vtype=Variant::REAL; break;
+ case 2: tn->vtype=Variant::VECTOR2; break;
+ case 3: tn->vtype=Variant::VECTOR3; break;
+ }
+
+ for(int i=0;i<args.size();i++) {
+ on->arguments.push_back(args[i]);
+ }
+
+ container=on;
+ }
+ }
+ }
+
+ }
+
ControlFlowNode *cf_for = alloc_node<ControlFlowNode>();
cf_for->cf_type=ControlFlowNode::CF_FOR;
@@ -1920,7 +2644,14 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
}
current_block=cf_for->body;
+
+ // this is for checking variable for redefining
+ // inside this _parse_block
+ cf_for->body->variables.push_back(id->name);
+ cf_for->body->variable_lines.push_back(id->line);
_parse_block(cf_for->body,p_static);
+ cf_for->body->variables.remove(0);
+ cf_for->body->variable_lines.remove(0);
current_block=p_block;
if (error_set)
@@ -1982,6 +2713,46 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
} break;
+ case GDTokenizer::TK_CF_MATCH: {
+
+ tokenizer->advance();
+
+ MatchNode *match_node = alloc_node<MatchNode>();
+
+ Node *val_to_match = _parse_and_reduce_expression(p_block, p_static);
+
+ if (!val_to_match) {
+ if (_recover_from_completion()) {
+ break;
+ }
+ return;
+ }
+
+ match_node->val_to_match = val_to_match;
+
+ if (!_enter_indent_block()) {
+ _set_error("Expected indented pattern matching block after 'match'");
+ return;
+ }
+
+ BlockNode *compiled_branches = alloc_node<BlockNode>();
+ compiled_branches->parent_block = p_block;
+ compiled_branches->parent_class = p_block->parent_class;
+
+ p_block->sub_blocks.push_back(compiled_branches);
+
+ _parse_pattern_block(compiled_branches, match_node->branches, p_static);
+
+ _transform_match_statment(compiled_branches, match_node);
+
+ ControlFlowNode *match_cf_node = alloc_node<ControlFlowNode>();
+ match_cf_node->cf_type = ControlFlowNode::CF_MATCH;
+ match_cf_node->match = match_node;
+
+ p_block->statements.push_back(match_cf_node);
+
+ _end_statement();
+ } break;
case GDTokenizer::TK_PR_ASSERT: {
tokenizer->advance();
@@ -2557,17 +3328,41 @@ void GDParser::_parse_class(ClassNode *p_class) {
current_export.type=type;
current_export.usage|=PROPERTY_USAGE_SCRIPT_VARIABLE;
tokenizer->advance();
+
+ String hint_prefix ="";
+
+ if(type == Variant::ARRAY && tokenizer->get_token()==GDTokenizer::TK_COMMA) {
+ tokenizer->advance();
+
+ while(tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_TYPE) {
+ type = tokenizer->get_token_type();
+
+ tokenizer->advance();
+
+ if(type == Variant::ARRAY) {
+ hint_prefix += itos(Variant::ARRAY)+":";
+ if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
+ tokenizer->advance();
+ }
+ } else {
+ hint_prefix += itos(type);
+ break;
+ }
+ }
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
// hint expected next!
tokenizer->advance();
- switch(current_export.type) {
+
+ switch(type) {
case Variant::INT: {
if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="FLAGS") {
- current_export.hint=PROPERTY_HINT_ALL_FLAGS;
+ //current_export.hint=PROPERTY_HINT_ALL_FLAGS;
tokenizer->advance();
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
@@ -2916,13 +3711,20 @@ void GDParser::_parse_class(ClassNode *p_class) {
return;
} break;
}
-
+
+ }
+ if(current_export.type == Variant::ARRAY && !hint_prefix.empty()) {
+ if(current_export.hint) {
+ hint_prefix += "/"+itos(current_export.hint);
+ }
+ current_export.hint_string=hint_prefix+":"+current_export.hint_string;
+ current_export.hint=PROPERTY_HINT_NONE;
}
} else if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
String identifier = tokenizer->get_token_identifier();
- if (!ObjectTypeDB::is_type(identifier,"Resource")) {
+ if (!ClassDB::is_parent_class(identifier,"Resource")) {
current_export=PropertyInfo();
_set_error("Export hint not a type or resource.");
@@ -3140,7 +3942,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
return;
}
member._export.hint=PROPERTY_HINT_RESOURCE_TYPE;
- member._export.hint_string=res->get_type();
+ member._export.hint_string=res->get_class();
}
}
}