summaryrefslogtreecommitdiffstats
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.cpp660
1 files changed, 607 insertions, 53 deletions
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 901a458179..c783fac429 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 */
@@ -31,6 +31,7 @@
#include "io/resource_loader.h"
#include "os/file_access.h"
#include "script_language.h"
+#include "gd_script.h"
template<class T>
T* GDParser::alloc_node() {
@@ -120,6 +121,7 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat
tokenizer->advance();
} else {
+ parenthesis ++;
int argidx=0;
while(true) {
@@ -164,6 +166,7 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat
}
}
+ parenthesis --;
}
return true;
@@ -202,6 +205,7 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide
completion_line=tokenizer->get_token_line();
completion_block=current_block;
completion_found=true;
+ completion_ident_is_call=false;
tokenizer->advance();
if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
@@ -209,6 +213,9 @@ bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& ide
tokenizer->advance();
}
+ if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
+ completion_ident_is_call=true;
+ }
return true;
}
@@ -216,15 +223,17 @@ 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) {
+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;
Node *expr=NULL;
+ int op_line = tokenizer->get_token_line(); // when operators are created at the bottom, the line might have been changed (\n found)
+
while(true) {
@@ -243,7 +252,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
//subexpression ()
tokenizer->advance();
parenthesis++;
- Node* subexpr = _parse_expression(p_parent,p_static);
+ Node* subexpr = _parse_expression(p_parent,p_static,p_allow_assign,p_parsing_constant);
parenthesis--;
if (!subexpr)
return NULL;
@@ -256,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
@@ -357,35 +458,56 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
OperatorNode *yield = alloc_node<OperatorNode>();
yield->op=OperatorNode::OP_YIELD;
+ while (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
expr=yield;
tokenizer->advance();
} else {
+ parenthesis ++;
+
Node *object = _parse_and_reduce_expression(p_parent,p_static);
if (!object)
return NULL;
yield->arguments.push_back(object);
if (tokenizer->get_token()!=GDTokenizer::TK_COMMA) {
-
_set_error("Expected ',' after first argument of 'yield'");
return NULL;
}
tokenizer->advance();
+ if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) {
+
+
+ completion_cursor=StringName();
+ completion_node=object;
+ completion_type=COMPLETION_YIELD;
+ completion_class=current_class;
+ completion_function=current_function;
+ completion_line=tokenizer->get_token_line();
+ completion_argument=0;
+ completion_block=current_block;
+ completion_found=true;
+ tokenizer->advance();
+ }
+
Node *signal = _parse_and_reduce_expression(p_parent,p_static);
if (!signal)
return NULL;
yield->arguments.push_back(signal);
if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
-
_set_error("Expected ')' after second argument of 'yield'");
return NULL;
}
+ parenthesis --;
+
tokenizer->advance();
expr=yield;
@@ -477,20 +599,30 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
} else if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
//identifier (reference)
- const ClassNode* cln = static_cast<const ClassNode*>(get_parse_tree());
+ const ClassNode* cln = current_class;
bool bfn = false;
StringName identifier;
if (_get_completable_identifier(COMPLETION_IDENTIFIER,identifier)) {
}
- for( int i=0; i<cln->constant_expressions.size(); ++i ) {
+ if (p_parsing_constant) {
+ for( int i=0; i<cln->constant_expressions.size(); ++i ) {
- if( cln->constant_expressions[i].identifier == identifier ) {
+ if( cln->constant_expressions[i].identifier == identifier ) {
- expr = cln->constant_expressions[i].expression;
- bfn = true;
- break;
+ expr = cln->constant_expressions[i].expression;
+ bfn = true;
+ break;
+ }
+ }
+
+ if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
+ //check from constants
+ ConstantNode *constant = alloc_node<ConstantNode>();
+ constant->value = GDScriptLanguage::get_singleton()->get_global_array()[ GDScriptLanguage::get_singleton()->get_global_map()[identifier] ];
+ expr=constant;
+ bfn = true;
}
}
@@ -500,15 +632,15 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
expr = id;
}
- } else if (/*tokenizer->get_token()==GDTokenizer::TK_OP_ADD ||*/ tokenizer->get_token()==GDTokenizer::TK_OP_SUB || tokenizer->get_token()==GDTokenizer::TK_OP_NOT || tokenizer->get_token()==GDTokenizer::TK_OP_BIT_INVERT) {
-
- //single prefix operators like !expr -expr ++expr --expr
- OperatorNode *op = alloc_node<OperatorNode>();
+ } else if (tokenizer->get_token()==GDTokenizer::TK_OP_ADD || tokenizer->get_token()==GDTokenizer::TK_OP_SUB || tokenizer->get_token()==GDTokenizer::TK_OP_NOT || tokenizer->get_token()==GDTokenizer::TK_OP_BIT_INVERT) {
+ //single prefix operators like !expr +expr -expr ++expr --expr
+ alloc_node<OperatorNode>();
Expression e;
e.is_op=true;
switch(tokenizer->get_token()) {
+ 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;
@@ -567,7 +699,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
_set_error("',' or ']' expected");
return NULL;
}
- Node *n = _parse_expression(arr,p_static);
+ Node *n = _parse_expression(arr,p_static,p_allow_assign,p_parsing_constant);
if (!n)
return NULL;
arr->elements.push_back(n);
@@ -674,7 +806,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
expecting=DICT_EXPECT_VALUE;
} else {
//python/js style more flexible
- key = _parse_expression(dict,p_static);
+ key = _parse_expression(dict,p_static,p_allow_assign,p_parsing_constant);
if (!key)
return NULL;
expecting=DICT_EXPECT_COLON;
@@ -682,7 +814,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
}
if (expecting==DICT_EXPECT_VALUE) {
- Node *value = _parse_expression(dict,p_static);
+ Node *value = _parse_expression(dict,p_static,p_allow_assign,p_parsing_constant);
if (!value)
return NULL;
expecting=DICT_EXPECT_COMMA;
@@ -730,7 +862,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
//find list [ or find dictionary {
- print_line("found bug?");
+ //print_line("found bug?");
_set_error("Error parsing expression, misplaced: "+String(tokenizer->get_token_name(tokenizer->get_token())));
return NULL; //nothing
@@ -833,7 +965,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
tokenizer->advance(1);
- Node *subexpr = _parse_expression(op,p_static);
+ Node *subexpr = _parse_expression(op,p_static,p_allow_assign,p_parsing_constant);
if (!subexpr) {
return NULL;
}
@@ -909,6 +1041,8 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
case GDTokenizer::TK_OP_BIT_OR: op=OperatorNode::OP_BIT_OR ; break;
case GDTokenizer::TK_OP_BIT_XOR: op=OperatorNode::OP_BIT_XOR ; break;
case GDTokenizer::TK_PR_EXTENDS: op=OperatorNode::OP_EXTENDS; break;
+ case GDTokenizer::TK_CF_IF: op=OperatorNode::OP_TERNARY_IF; break;
+ case GDTokenizer::TK_CF_ELSE: op=OperatorNode::OP_TERNARY_ELSE; break;
default: valid=false; break;
}
@@ -931,6 +1065,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
int next_op=-1;
int min_priority=0xFFFFF;
bool is_unary=false;
+ bool is_ternary=false;
for(int i=0;i<expression.size();i++) {
@@ -944,6 +1079,8 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
int priority;
bool unary=false;
+ bool ternary=false;
+ bool error=false;
switch(expression[i].op) {
@@ -951,6 +1088,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
case OperatorNode::OP_BIT_INVERT: priority=0; unary=true; break;
case OperatorNode::OP_NEG: priority=1; unary=true; break;
+ case OperatorNode::OP_POS: priority=1; unary=true; break;
case OperatorNode::OP_MUL: priority=2; break;
case OperatorNode::OP_DIV: priority=2; break;
@@ -974,25 +1112,27 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
case OperatorNode::OP_EQUAL: priority=8; break;
case OperatorNode::OP_NOT_EQUAL: priority=8; break;
+
case OperatorNode::OP_IN: priority=10; break;
-
+
case OperatorNode::OP_NOT: priority=11; unary=true; break;
case OperatorNode::OP_AND: priority=12; break;
case OperatorNode::OP_OR: priority=13; break;
-
- // ?: = 10
-
- case OperatorNode::OP_ASSIGN: priority=14; break;
- case OperatorNode::OP_ASSIGN_ADD: priority=14; break;
- case OperatorNode::OP_ASSIGN_SUB: priority=14; break;
- case OperatorNode::OP_ASSIGN_MUL: priority=14; break;
- case OperatorNode::OP_ASSIGN_DIV: priority=14; break;
- case OperatorNode::OP_ASSIGN_MOD: priority=14; break;
- case OperatorNode::OP_ASSIGN_SHIFT_LEFT: priority=14; break;
- case OperatorNode::OP_ASSIGN_SHIFT_RIGHT: priority=14; break;
- case OperatorNode::OP_ASSIGN_BIT_AND: priority=14; break;
- case OperatorNode::OP_ASSIGN_BIT_OR: priority=14; break;
- case OperatorNode::OP_ASSIGN_BIT_XOR: priority=14; break;
+
+ case OperatorNode::OP_TERNARY_IF: priority=14; ternary=true; break;
+ case OperatorNode::OP_TERNARY_ELSE: priority=14; error=true; break; // Errors out when found without IF (since IF would consume it)
+
+ case OperatorNode::OP_ASSIGN: priority=15; break;
+ case OperatorNode::OP_ASSIGN_ADD: priority=15; break;
+ case OperatorNode::OP_ASSIGN_SUB: priority=15; break;
+ case OperatorNode::OP_ASSIGN_MUL: priority=15; break;
+ case OperatorNode::OP_ASSIGN_DIV: priority=15; break;
+ case OperatorNode::OP_ASSIGN_MOD: priority=15; break;
+ case OperatorNode::OP_ASSIGN_SHIFT_LEFT: priority=15; break;
+ case OperatorNode::OP_ASSIGN_SHIFT_RIGHT: priority=15; break;
+ case OperatorNode::OP_ASSIGN_BIT_AND: priority=15; break;
+ case OperatorNode::OP_ASSIGN_BIT_OR: priority=15; break;
+ case OperatorNode::OP_ASSIGN_BIT_XOR: priority=15; break;
default: {
@@ -1003,11 +1143,16 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
}
if (priority<min_priority) {
+ 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;
+ is_ternary=ternary;
}
}
@@ -1041,21 +1186,31 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
OperatorNode *op = alloc_node<OperatorNode>();
op->op=expression[i].op;
op->arguments.push_back(expression[i+1].node);
+ op->line=op_line; //line might have been changed from a \n
expression[i].is_op=false;
expression[i].node=op;
expression.remove(i+1);
}
- } else {
-
+ } else if(is_ternary) {
if (next_op <1 || next_op>=(expression.size()-1)) {
_set_error("Parser bug..");
ERR_FAIL_V(NULL);
}
+
+ if(next_op>=(expression.size()-2) || expression[next_op+2].op != OperatorNode::OP_TERNARY_ELSE) {
+ _set_error("Expected else after ternary if.");
+ ERR_FAIL_V(NULL);
+ }
+ if(next_op>=(expression.size()-3)) {
+ _set_error("Expected value after ternary else.");
+ ERR_FAIL_V(NULL);
+ }
OperatorNode *op = alloc_node<OperatorNode>();
op->op=expression[next_op].op;
+ op->line=op_line; //line might have been changed from a \n
if (expression[next_op-1].is_op) {
@@ -1069,8 +1224,56 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
// can be followed by an unary op in a valid combination,
// due to how precedence works, unaries will always dissapear first
+ _set_error("Unexpected two consecutive operators after ternary if.");
+ return NULL;
+ }
+
+ if (expression[next_op+3].is_op) {
+ // this is not invalid and can really appear
+ // but it becomes invalid anyway because no binary op
+ // can be followed by an unary op in a valid combination,
+ // due to how precedence works, unaries will always dissapear first
+
+ _set_error("Unexpected two consecutive operators after ternary else.");
+ return NULL;
+ }
+
+
+ op->arguments.push_back(expression[next_op+1].node); //next expression goes as first
+ op->arguments.push_back(expression[next_op-1].node); //left expression goes as when-true
+ op->arguments.push_back(expression[next_op+3].node); //expression after next goes as when-false
+
+ //replace all 3 nodes by this operator and make it an expression
+ expression[next_op-1].node=op;
+ expression.remove(next_op);
+ expression.remove(next_op);
+ expression.remove(next_op);
+ expression.remove(next_op);
+ } else {
+
+ if (next_op <1 || next_op>=(expression.size()-1)) {
_set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ OperatorNode *op = alloc_node<OperatorNode>();
+ op->op=expression[next_op].op;
+ op->line=op_line; //line might have been changed from a \n
+ if (expression[next_op-1].is_op) {
+
+ _set_error("Parser bug..");
+ ERR_FAIL_V(NULL);
+ }
+
+ if (expression[next_op+1].is_op) {
+ // this is not invalid and can really appear
+ // but it becomes invalid anyway because no binary op
+ // can be followed by an unary op in a valid combination,
+ // due to how precedence works, unaries will always dissapear first
+
+ _set_error("Unexpected two consecutive operators.");
+ return NULL;
}
@@ -1114,7 +1317,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++) {
@@ -1149,7 +1352,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);
@@ -1258,6 +1461,8 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
} break;
}
+ error_line=op->line;
+
return p_node;
}
@@ -1293,6 +1498,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
Variant v = ca->value.get(cb->value,&valid);
if (!valid) {
_set_error("invalid index in constant expression");
+ error_line=op->line;
return op;
}
@@ -1330,6 +1536,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
Variant v = ca->value.get_named(ib->name,&valid);
if (!valid) {
_set_error("invalid index '"+String(ib->name)+"' in constant expression");
+ error_line=op->line;
return op;
}
@@ -1359,9 +1566,19 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
if (op->arguments[0]->type==Node::TYPE_CONSTANT) {
_set_error("Can't assign to constant",tokenizer->get_token_line()-1);
+ error_line=op->line;
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; }
}
@@ -1374,6 +1591,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
Variant::evaluate(m_vop,static_cast<ConstantNode*>(op->arguments[0])->value,Variant(),res,valid);\
if (!valid) {\
_set_error("Invalid operand for unary operator");\
+ error_line=op->line;\
return p_node;\
}\
ConstantNode *cn = alloc_node<ConstantNode>();\
@@ -1386,6 +1604,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
Variant::evaluate(m_vop,static_cast<ConstantNode*>(op->arguments[0])->value,static_cast<ConstantNode*>(op->arguments[1])->value,res,valid);\
if (!valid) {\
_set_error("Invalid operands for operator");\
+ error_line=op->line;\
return p_node;\
}\
ConstantNode *cn = alloc_node<ConstantNode>();\
@@ -1396,6 +1615,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
//unary operators
case OperatorNode::OP_NEG: { _REDUCE_UNARY(Variant::OP_NEGATE); } break;
+ case OperatorNode::OP_POS: { _REDUCE_UNARY(Variant::OP_POSITIVE); } break;
case OperatorNode::OP_NOT: { _REDUCE_UNARY(Variant::OP_NOT); } break;
case OperatorNode::OP_BIT_INVERT: { _REDUCE_UNARY(Variant::OP_BIT_NEGATE); } break;
//binary operators (in precedence order)
@@ -1432,7 +1652,7 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) {
GDParser::Node* GDParser::_parse_and_reduce_expression(Node *p_parent,bool p_static,bool p_reduce_const,bool p_allow_assign) {
- Node* expr=_parse_expression(p_parent,p_static,p_allow_assign);
+ Node* expr=_parse_expression(p_parent,p_static,p_allow_assign,p_reduce_const);
if (!expr || error_set)
return NULL;
expr = _reduce_expression(expr,p_reduce_const);
@@ -1526,6 +1746,10 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
return;
}
tokenizer->advance();
+ if(tokenizer->get_token()==GDTokenizer::TK_SEMICOLON) {
+ // Ignore semicolon after 'pass'
+ tokenizer->advance();
+ }
} break;
case GDTokenizer::TK_PR_VAR: {
//variale declaration and (eventual) initialization
@@ -1611,6 +1835,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
case GDTokenizer::TK_CF_IF: {
tokenizer->advance();
+
Node *condition = _parse_and_reduce_expression(p_block,p_static);
if (!condition) {
if (_recover_from_completion()) {
@@ -1797,6 +2022,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;
@@ -2060,6 +2343,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
if (error_set)
return;
+
if (indent_level>tab_level.back()->get()) {
p_class->end_line=tokenizer->get_token_line();
return; //go back a level
@@ -2217,6 +2501,11 @@ void GDParser::_parse_class(ClassNode *p_class) {
bool defaulting=false;
while(true) {
+ if (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ continue;
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_PR_VAR) {
tokenizer->advance(); //var before the identifier is allowed
@@ -2269,6 +2558,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
default_values.push_back(on);
}
+ while (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
tokenizer->advance();
continue;
@@ -2310,6 +2603,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
//has arguments
+ parenthesis ++;
while(true) {
Node *arg = _parse_and_reduce_expression(p_class,_static);
@@ -2327,6 +2621,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
break;
}
+ parenthesis --;
}
tokenizer->advance();
@@ -2356,6 +2651,9 @@ void GDParser::_parse_class(ClassNode *p_class) {
function->_static=_static;
function->line=fnline;
+ function->rpc_mode=rpc_mode;
+ rpc_mode=ScriptInstance::RPC_MODE_DISABLED;
+
if (_static)
p_class->static_functions.push_back(function);
@@ -2387,6 +2685,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
tokenizer->advance();
while(true) {
+ if (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ continue;
+ }
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
@@ -2402,6 +2704,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
sig.arguments.push_back(tokenizer->get_token_identifier());
tokenizer->advance();
+ while (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
tokenizer->advance();
} else if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
@@ -2433,18 +2739,43 @@ void GDParser::_parse_class(ClassNode *p_class) {
return;
}
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) {
@@ -2793,13 +3124,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.");
@@ -2807,6 +3145,8 @@ void GDParser::_parse_class(ClassNode *p_class) {
current_export.type=Variant::OBJECT;
current_export.hint=PROPERTY_HINT_RESOURCE_TYPE;
+ current_export.usage|=PROPERTY_USAGE_SCRIPT_VARIABLE;
+
current_export.hint_string=identifier;
tokenizer->advance();
@@ -2824,25 +3164,101 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
- if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_ONREADY && tokenizer->get_token()!=GDTokenizer::TK_PR_REMOTE && tokenizer->get_token()!=GDTokenizer::TK_PR_MASTER && tokenizer->get_token()!=GDTokenizer::TK_PR_SLAVE && tokenizer->get_token()!=GDTokenizer::TK_PR_SYNC) {
current_export=PropertyInfo();
- _set_error("Expected 'var'.");
+ _set_error("Expected 'var', 'onready', 'remote', 'master', 'slave' or 'sync'.");
return;
}
- }; //fallthrough to var
+ continue;
+ } break;
case GDTokenizer::TK_PR_ONREADY: {
- if (token==GDTokenizer::TK_PR_ONREADY) {
- //may be fallthrough from export, ignore if so
- tokenizer->advance();
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+ _set_error("Expected 'var'.");
+ return;
+ }
+
+ continue;
+ } break;
+ case GDTokenizer::TK_PR_REMOTE: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (current_export.type) {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+ _set_error("Expected 'var'.");
+ return;
+ }
+
+ } else {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+ }
+ rpc_mode=ScriptInstance::RPC_MODE_REMOTE;
+
+ continue;
+ } break;
+ case GDTokenizer::TK_PR_MASTER: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (current_export.type) {
if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
_set_error("Expected 'var'.");
return;
}
+
+ } else {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
}
- }; //fallthrough to var
+
+ rpc_mode=ScriptInstance::RPC_MODE_MASTER;
+ continue;
+ } break;
+ case GDTokenizer::TK_PR_SLAVE: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (current_export.type) {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) {
+ _set_error("Expected 'var'.");
+ return;
+ }
+
+ } else {
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+ }
+
+ rpc_mode=ScriptInstance::RPC_MODE_SLAVE;
+ continue;
+ } break;
+ case GDTokenizer::TK_PR_SYNC: {
+
+ //may be fallthrough from export, ignore if so
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) {
+ if (current_export.type)
+ _set_error("Expected 'var'.");
+ else
+ _set_error("Expected 'var' or 'func'.");
+ return;
+ }
+
+ rpc_mode=ScriptInstance::RPC_MODE_SYNC;
+ continue;
+ } break;
case GDTokenizer::TK_PR_VAR: {
//variale declaration and (eventual) initialization
@@ -2866,8 +3282,12 @@ void GDParser::_parse_class(ClassNode *p_class) {
member.expression=NULL;
member._export.name=member.identifier;
member.line=tokenizer->get_token_line();
+ member.rpc_mode=rpc_mode;
+
tokenizer->advance();
+ rpc_mode=ScriptInstance::RPC_MODE_DISABLED;
+
if (tokenizer->get_token()==GDTokenizer::TK_OP_ASSIGN) {
#ifdef DEBUG_ENABLED
@@ -2926,6 +3346,17 @@ void GDParser::_parse_class(ClassNode *p_class) {
return;
}
member._export.type=cn->value.get_type();
+ member._export.usage|=PROPERTY_USAGE_SCRIPT_VARIABLE;
+ if (cn->value.get_type()==Variant::OBJECT) {
+ Object *obj = cn->value;
+ Resource *res = obj->cast_to<Resource>();
+ if(res==NULL) {
+ _set_error("Exported constant not a type or resource.");
+ return;
+ }
+ member._export.hint=PROPERTY_HINT_RESOURCE_TYPE;
+ member._export.hint_string=res->get_class();
+ }
}
}
#ifdef TOOLS_ENABLED
@@ -3054,7 +3485,124 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
} break;
+ case GDTokenizer::TK_PR_ENUM: {
+ //mutiple constant declarations..
+
+ int last_assign = -1; // Incremented by 1 right before the assingment.
+ String enum_name;
+ Dictionary enum_dict;
+
+ tokenizer->advance();
+ if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
+ enum_name=tokenizer->get_token_identifier();
+ tokenizer->advance();
+ }
+ if (tokenizer->get_token()!=GDTokenizer::TK_CURLY_BRACKET_OPEN) {
+ _set_error("Expected '{' in enum declaration");
+ return;
+ }
+ tokenizer->advance();
+
+ while(true) {
+ if(tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+
+ tokenizer->advance(); // Ignore newlines
+ } else if (tokenizer->get_token()==GDTokenizer::TK_CURLY_BRACKET_CLOSE) {
+
+ tokenizer->advance();
+ break; // End of enum
+ } else if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) {
+
+ if(tokenizer->get_token()==GDTokenizer::TK_EOF) {
+ _set_error("Unexpected end of file.");
+ } else {
+ _set_error(String("Unexpected ") + GDTokenizer::get_token_name(tokenizer->get_token()) + ", expected identifier");
+ }
+
+ return;
+ } else { // tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER
+ ClassNode::Constant constant;
+
+ constant.identifier=tokenizer->get_token_identifier();
+
+ tokenizer->advance();
+
+ if (tokenizer->get_token()==GDTokenizer::TK_OP_ASSIGN) {
+ tokenizer->advance();
+
+ Node *subexpr=NULL;
+
+ subexpr = _parse_and_reduce_expression(p_class,true,true);
+ if (!subexpr) {
+ if (_recover_from_completion()) {
+ break;
+ }
+ return;
+ }
+
+ if (subexpr->type!=Node::TYPE_CONSTANT) {
+ _set_error("Expected constant expression");
+ }
+
+ const ConstantNode *subexpr_const = static_cast<const ConstantNode*>(subexpr);
+
+ if(subexpr_const->value.get_type() != Variant::INT) {
+ _set_error("Expected an int value for enum");
+ }
+
+ last_assign = subexpr_const->value;
+
+ constant.expression=subexpr;
+
+ } else {
+ last_assign = last_assign + 1;
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value = last_assign;
+ constant.expression = cn;
+ }
+
+ if(tokenizer->get_token()==GDTokenizer::TK_COMMA) {
+ tokenizer->advance();
+ }
+
+ if(enum_name != "") {
+ const ConstantNode *cn = static_cast<const ConstantNode*>(constant.expression);
+ enum_dict[constant.identifier] = cn->value;
+ }
+
+ p_class->constant_expressions.push_back(constant);
+ }
+
+ }
+
+ if(enum_name != "") {
+ ClassNode::Constant enum_constant;
+ enum_constant.identifier=enum_name;
+ ConstantNode *cn = alloc_node<ConstantNode>();
+ cn->value = enum_dict;
+ enum_constant.expression=cn;
+ p_class->constant_expressions.push_back(enum_constant);
+ }
+
+ if (!_end_statement()) {
+ _set_error("Expected end of statement (enum)");
+ return;
+ }
+
+
+
+ } break;
+
+ case GDTokenizer::TK_CONSTANT: {
+ if(tokenizer->get_token_constant().get_type() == Variant::STRING) {
+ tokenizer->advance();
+ // Ignore
+ } else {
+ _set_error(String()+"Unexpected constant of type: "+Variant::get_type_name(tokenizer->get_token_constant().get_type()));
+ return;
+ }
+ } break;
default: {
@@ -3209,6 +3757,7 @@ void GDParser::clear() {
current_class=NULL;
completion_found=false;
+ rpc_mode=ScriptInstance::RPC_MODE_DISABLED;
current_function=NULL;
@@ -3272,6 +3821,11 @@ int GDParser::get_completion_argument_index() {
return completion_argument;
}
+int GDParser::get_completion_identifier_is_function() {
+
+ return completion_ident_is_call;
+}
+
GDParser::GDParser() {
head=NULL;