Browse Source

Added support for for, break and continue. Closes #10560, closes #10661

Juan Linietsky 8 years ago
parent
commit
7eb8760477

+ 39 - 8
drivers/gles3/shader_compiler_gles3.cpp

@@ -438,26 +438,44 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener
 			SL::BlockNode *bnode = (SL::BlockNode *)p_node;
 
 			//variables
-			code += _mktab(p_level - 1) + "{\n";
-			for (Map<StringName, SL::BlockNode::Variable>::Element *E = bnode->variables.front(); E; E = E->next()) {
-
-				code += _mktab(p_level) + _prestr(E->get().precision) + _typestr(E->get().type) + " " + _mkid(E->key()) + ";\n";
+			if (!bnode->single_statement) {
+				code += _mktab(p_level - 1) + "{\n";
 			}
-
+	
 			for (int i = 0; i < bnode->statements.size(); i++) {
 
 				String scode = _dump_node_code(bnode->statements[i], p_level, r_gen_code, p_actions, p_default_actions);
 
-				if (bnode->statements[i]->type == SL::Node::TYPE_CONTROL_FLOW || bnode->statements[i]->type == SL::Node::TYPE_CONTROL_FLOW) {
-					// FIXME: if (A || A) ? I am hesitant to delete one of them, could be copy-paste error.
+				if (bnode->statements[i]->type == SL::Node::TYPE_CONTROL_FLOW || bnode->single_statement ) {
 					code += scode; //use directly
 				} else {
 					code += _mktab(p_level) + scode + ";\n";
 				}
 			}
-			code += _mktab(p_level - 1) + "}\n";
+			if (!bnode->single_statement) {
+				code += _mktab(p_level - 1) + "}\n";
+			}
 
 		} break;
+		case SL::Node::TYPE_VARIABLE_DECLARATION: {
+			SL::VariableDeclarationNode *vdnode = (SL::VariableDeclarationNode *)p_node;
+
+			String declaration = _prestr(vdnode->precision) + _typestr(vdnode->datatype);
+			for(int i=0;i<vdnode->declarations.size();i++) {
+				if (i>0) {
+					declaration+=",";
+				} else {
+					declaration+=" ";
+				}
+				declaration +=  _mkid(vdnode->declarations[i].name);
+				if (vdnode->declarations[i].initializer) {
+					declaration+="=";
+					declaration+=_dump_node_code(vdnode->declarations[i].initializer, p_level, r_gen_code, p_actions, p_default_actions);
+				}
+			}
+
+			code+=declaration;
+		} break;
 		case SL::Node::TYPE_VARIABLE: {
 			SL::VariableNode *vnode = (SL::VariableNode *)p_node;
 
@@ -600,6 +618,13 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener
 
 				code += _mktab(p_level) + "while (" + _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions) + ")\n";
 				code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions);
+			} else if (cfnode->flow_op == SL::FLOW_OP_FOR) {
+
+				String left = _dump_node_code(cfnode->blocks[0], p_level, r_gen_code, p_actions, p_default_actions);
+				String middle = _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions);
+				String right = _dump_node_code(cfnode->expressions[1], p_level, r_gen_code, p_actions, p_default_actions);
+				code += _mktab(p_level) + "for (" +left+";"+middle+";"+right+")\n";
+				code += _dump_node_code(cfnode->blocks[1], p_level + 1, r_gen_code, p_actions, p_default_actions);
 
 			} else if (cfnode->flow_op == SL::FLOW_OP_RETURN) {
 
@@ -611,6 +636,12 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener
 			} else if (cfnode->flow_op == SL::FLOW_OP_DISCARD) {
 
 				code = "discard;";
+			} else if (cfnode->flow_op == SL::FLOW_OP_CONTINUE) {
+
+				code = "continue;";
+			} else if (cfnode->flow_op == SL::FLOW_OP_BREAK) {
+
+				code = "break;";
 			}
 
 		} break;

+ 122 - 14
servers/visual/shader_language.cpp

@@ -79,7 +79,11 @@ String ShaderLanguage::get_operator_text(Operator p_op) {
 		"|",
 		"^",
 		"~",
-		"++"
+		"++",
+		"--",
+		"?",
+		":",
+		"++",
 		"--",
 		"()",
 		"construct",
@@ -3116,6 +3120,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Dat
 
 			tk = _get_token();
 
+			VariableDeclarationNode *vardecl = alloc_node<VariableDeclarationNode>();
+			vardecl->datatype=type;
+			vardecl->precision=precision;
+
+			p_block->statements.push_back(vardecl);
+
 			while (true) {
 
 				if (tk.type != TK_IDENTIFIER) {
@@ -3133,8 +3143,14 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Dat
 				var.type = type;
 				var.precision = precision;
 				var.line = tk_line;
+
 				p_block->variables[name] = var;
 
+				VariableDeclarationNode::Declaration decl;
+
+				decl.name=name;
+				decl.initializer=NULL;
+
 				tk = _get_token();
 
 				if (tk.type == TK_OP_ASSIGN) {
@@ -3143,22 +3159,19 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Dat
 					if (!n)
 						return ERR_PARSE_ERROR;
 
-					OperatorNode *assign = alloc_node<OperatorNode>();
-					VariableNode *vnode = alloc_node<VariableNode>();
-					vnode->name = name;
-					vnode->datatype_cache = type;
-					assign->arguments.push_back(vnode);
-					assign->arguments.push_back(n);
-					assign->op = OP_ASSIGN;
-					p_block->statements.push_back(assign);
-					tk = _get_token();
+					decl.initializer = n;
 
-					if (!_validate_operator(assign)) {
-						_set_error("Invalid assignment of '" + get_datatype_name(n->get_datatype()) + "' to '" + get_datatype_name(type) + "'");
+					if (var.type!=n->get_datatype()) {
+						_set_error("Invalid assignment of '" + get_datatype_name(n->get_datatype()) + "' to '" + get_datatype_name(var.type) + "'");
 						return ERR_PARSE_ERROR;
+
 					}
+					tk = _get_token();
+
 				}
 
+				vardecl->declarations.push_back(decl);
+
 				if (tk.type == TK_COMMA) {
 					tk = _get_token();
 					//another variable
@@ -3221,7 +3234,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Dat
 			//if () {}
 			tk = _get_token();
 			if (tk.type != TK_PARENTHESIS_OPEN) {
-				_set_error("Expected '(' after if");
+				_set_error("Expected '(' after while");
 				return ERR_PARSE_ERROR;
 			}
 
@@ -3243,7 +3256,64 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Dat
 			cf->blocks.push_back(block);
 			p_block->statements.push_back(cf);
 
-			Error err = _parse_block(block, p_builtin_types, true, p_can_break, p_can_continue);
+			Error err = _parse_block(block, p_builtin_types, true, true, true);
+			if (err)
+				return err;
+		} else if (tk.type == TK_CF_FOR) {
+			//if () {}
+			tk = _get_token();
+			if (tk.type != TK_PARENTHESIS_OPEN) {
+				_set_error("Expected '(' after for");
+				return ERR_PARSE_ERROR;
+			}
+
+			ControlFlowNode *cf = alloc_node<ControlFlowNode>();
+			cf->flow_op = FLOW_OP_FOR;
+
+			BlockNode *init_block = alloc_node<BlockNode>();
+			init_block->parent_block = p_block;
+			init_block->single_statement=true;
+			cf->blocks.push_back(init_block);
+			if (_parse_block(init_block,p_builtin_types,true,false,false)!=OK) {
+				return ERR_PARSE_ERROR;
+			}
+
+			Node *n = _parse_and_reduce_expression(init_block, p_builtin_types);
+			if (!n)
+				return ERR_PARSE_ERROR;
+
+			if (n->get_datatype()!=TYPE_BOOL) {
+				_set_error("Middle expression is expected to be boolean.");
+				return ERR_PARSE_ERROR;
+
+			}
+
+			tk = _get_token();
+			if (tk.type != TK_SEMICOLON) {
+				_set_error("Expected ';' after middle expression");
+				return ERR_PARSE_ERROR;
+			}
+
+			cf->expressions.push_back(n);
+
+			n = _parse_and_reduce_expression(init_block, p_builtin_types);
+			if (!n)
+				return ERR_PARSE_ERROR;
+
+			cf->expressions.push_back(n);
+
+			tk = _get_token();
+			if (tk.type != TK_PARENTHESIS_CLOSE) {
+				_set_error("Expected ')' after third expression");
+				return ERR_PARSE_ERROR;
+			}
+
+			BlockNode *block = alloc_node<BlockNode>();
+			block->parent_block = p_block;
+			cf->blocks.push_back(block);
+			p_block->statements.push_back(cf);
+
+			Error err = _parse_block(block, p_builtin_types, true, true, true);
 			if (err)
 				return err;
 
@@ -3319,6 +3389,44 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Dat
 				_set_error("Expected ';' after discard");
 			}
 
+			p_block->statements.push_back(flow);
+		} else if (tk.type == TK_CF_BREAK) {
+
+
+			if (!p_can_break) {
+				//all is good
+				_set_error("Breaking is not allowed here");
+			}
+
+			ControlFlowNode *flow = alloc_node<ControlFlowNode>();
+			flow->flow_op = FLOW_OP_BREAK;
+
+			pos = _get_tkpos();
+			tk = _get_token();
+			if (tk.type != TK_SEMICOLON) {
+				//all is good
+				_set_error("Expected ';' after break");
+			}
+
+			p_block->statements.push_back(flow);
+		} else if (tk.type == TK_CF_CONTINUE) {
+
+
+			if (!p_can_break) {
+				//all is good
+				_set_error("Contiuning is not allowed here");
+			}
+
+			ControlFlowNode *flow = alloc_node<ControlFlowNode>();
+			flow->flow_op = FLOW_OP_CONTINUE;
+
+			pos = _get_tkpos();
+			tk = _get_token();
+			if (tk.type != TK_SEMICOLON) {
+				//all is good
+				_set_error("Expected ';' after continue");
+			}
+
 			p_block->statements.push_back(flow);
 
 		} else {

+ 22 - 0
servers/visual/shader_language.h

@@ -266,6 +266,7 @@ public:
 			TYPE_FUNCTION,
 			TYPE_BLOCK,
 			TYPE_VARIABLE,
+			TYPE_VARIABLE_DECLARATION,
 			TYPE_CONSTANT,
 			TYPE_OPERATOR,
 			TYPE_CONTROL_FLOW,
@@ -315,6 +316,25 @@ public:
 		}
 	};
 
+	struct VariableDeclarationNode : public Node {
+
+		DataPrecision precision;
+		DataType datatype;
+
+		struct Declaration {
+
+			StringName name;
+			Node *initializer;
+		};
+
+		Vector<Declaration> declarations;
+		virtual DataType get_datatype() const { return datatype; }
+
+		VariableDeclarationNode() {
+			type = TYPE_VARIABLE_DECLARATION;
+		}
+	};
+
 	struct ConstantNode : public Node {
 
 		DataType datatype;
@@ -346,10 +366,12 @@ public:
 
 		Map<StringName, Variable> variables;
 		List<Node *> statements;
+		bool single_statement;
 		BlockNode() {
 			type = TYPE_BLOCK;
 			parent_block = NULL;
 			parent_function = NULL;
+			single_statement=false;
 		}
 	};