aboutsummaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
authorRémi Verschelde2017-01-11 10:35:36 +0100
committerGitHub2017-01-11 10:35:36 +0100
commitb328d2eb00f20ecb4abe26e1f969a32ef829fbbb (patch)
treee3e8f86f74502b6cb0fffc464260ab8bc8d557fd /modules/gdscript
parentfde651ebf8786f195cff09f14bf87b721269eb57 (diff)
parent39ce4a49fa52572947a677b6e3afad6ba094e60b (diff)
downloadgodot-b328d2eb00f20ecb4abe26e1f969a32ef829fbbb.tar.gz
godot-b328d2eb00f20ecb4abe26e1f969a32ef829fbbb.tar.zst
godot-b328d2eb00f20ecb4abe26e1f969a32ef829fbbb.zip
Merge pull request #7494 from RandomShaper/cherry-pick-backcompat-stuff
Cherry pick backcompat stuff (2.1)
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/gd_compiler.cpp40
-rw-r--r--modules/gdscript/gd_parser.cpp128
-rw-r--r--modules/gdscript/gd_parser.h3
3 files changed, 155 insertions, 16 deletions
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp
index 3bbd8e8f0..40bcce91c 100644
--- a/modules/gdscript/gd_compiler.cpp
+++ b/modules/gdscript/gd_compiler.cpp
@@ -662,6 +662,46 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre
return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS;
} break;
+ // ternary operators
+ case GDParser::OperatorNode::OP_TERNARY_IF: {
+
+ // x IF a ELSE y operator with early out on failure
+
+ int res = _parse_expression(codegen,on->arguments[0],p_stack_level);
+ if (res<0)
+ return res;
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP_IF_NOT);
+ codegen.opcodes.push_back(res);
+ int jump_fail_pos=codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+
+
+ res = _parse_expression(codegen,on->arguments[1],p_stack_level);
+ if (res<0)
+ return res;
+
+ codegen.alloc_stack(p_stack_level); //it will be used..
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(res);
+ codegen.opcodes.push_back(GDFunction::OPCODE_JUMP);
+ int jump_past_pos=codegen.opcodes.size();
+ codegen.opcodes.push_back(0);
+
+ codegen.opcodes[jump_fail_pos]=codegen.opcodes.size();
+ res = _parse_expression(codegen,on->arguments[2],p_stack_level);
+ if (res<0)
+ return res;
+
+ codegen.opcodes.push_back(GDFunction::OPCODE_ASSIGN);
+ codegen.opcodes.push_back(p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS);
+ codegen.opcodes.push_back(res);
+
+ codegen.opcodes[jump_past_pos]=codegen.opcodes.size();
+
+ return p_stack_level|GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS;
+
+ } break;
//unary operators
case GDParser::OperatorNode::OP_NEG: { if (!_create_unary_operator(codegen,on,Variant::OP_NEGATE,p_stack_level)) return -1;} break;
case GDParser::OperatorNode::OP_NOT: { if (!_create_unary_operator(codegen,on,Variant::OP_NOT,p_stack_level)) return -1;} break;
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 749072f68..342c17293 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -121,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) {
@@ -165,6 +166,7 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat
}
}
+ parenthesis --;
}
return true;
@@ -360,18 +362,23 @@ 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;
}
@@ -384,11 +391,12 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
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;
@@ -921,6 +929,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;
}
@@ -943,6 +953,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++) {
@@ -956,6 +967,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) {
@@ -986,25 +999,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;
+
+ 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)
- // ?: = 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_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: {
@@ -1015,11 +1030,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;
}
}
@@ -1060,6 +1080,62 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
}
+ } 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) {
+
+ _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 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)) {
@@ -1618,6 +1694,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()) {
@@ -2217,6 +2294,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 +2351,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 +2396,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 +2414,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
break;
}
+ parenthesis --;
}
tokenizer->advance();
@@ -2387,6 +2475,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 +2494,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) {
diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h
index 4afc534a8..f20dffa48 100644
--- a/modules/gdscript/gd_parser.h
+++ b/modules/gdscript/gd_parser.h
@@ -244,6 +244,9 @@ public:
OP_BIT_AND,
OP_BIT_OR,
OP_BIT_XOR,
+ //ternary operators
+ OP_TERNARY_IF,
+ OP_TERNARY_ELSE,
};
Operator op;