Implements switch to shaders

This commit is contained in:
Yuri Roubinski 2019-08-14 14:22:25 +03:00
parent 89bcfa4b36
commit 4dda253ee0
4 changed files with 225 additions and 1 deletions

View file

@ -804,6 +804,15 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
code += "else\n";
code += _dump_node_code(cf_node->blocks[1], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
}
} else if (cf_node->flow_op == SL::FLOW_OP_SWITCH) {
code += _mktab(p_level) + "switch (" + _dump_node_code(cf_node->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ")\n";
code += _dump_node_code(cf_node->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
} else if (cf_node->flow_op == SL::FLOW_OP_CASE) {
code += _mktab(p_level) + "case " + _dump_node_code(cf_node->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ":\n";
code += _dump_node_code(cf_node->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
} else if (cf_node->flow_op == SL::FLOW_OP_DEFAULT) {
code += _mktab(p_level) + "default:\n";
code += _dump_node_code(cf_node->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
} else if (cf_node->flow_op == SL::FLOW_OP_DO) {
code += _mktab(p_level);
code += "do";

View file

@ -801,6 +801,15 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener
code += _mktab(p_level) + "else\n";
code += _dump_node_code(cfnode->blocks[1], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
}
} else if (cfnode->flow_op == SL::FLOW_OP_SWITCH) {
code += _mktab(p_level) + "switch (" + _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ")\n";
code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
} else if (cfnode->flow_op == SL::FLOW_OP_CASE) {
code += _mktab(p_level) + "case " + _dump_node_code(cfnode->expressions[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + ":\n";
code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
} else if (cfnode->flow_op == SL::FLOW_OP_DEFAULT) {
code += _mktab(p_level) + "default:\n";
code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
} else if (cfnode->flow_op == SL::FLOW_OP_DO) {
code += _mktab(p_level) + "do";
code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);

View file

@ -283,6 +283,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
{ TK_CF_DO, "do" },
{ TK_CF_SWITCH, "switch" },
{ TK_CF_CASE, "case" },
{ TK_CF_DEFAULT, "default" },
{ TK_CF_BREAK, "break" },
{ TK_CF_CONTINUE, "continue" },
{ TK_CF_RETURN, "return" },
@ -3778,6 +3779,14 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
TkPos pos = _get_tkpos();
Token tk = _get_token();
if (p_block && p_block->block_type == BlockNode::BLOCK_TYPE_SWITCH) {
if (tk.type != TK_CF_CASE && tk.type != TK_CF_DEFAULT && tk.type != TK_CURLY_BRACKET_CLOSE) {
_set_error("Switch may contains only case and default blocks");
return ERR_PARSE_ERROR;
}
}
if (tk.type == TK_CURLY_BRACKET_CLOSE) { //end of block
if (p_just_one) {
_set_error("Unexpected '}'");
@ -4132,6 +4141,183 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
} else {
_set_tkpos(pos); //rollback
}
} else if (tk.type == TK_CF_SWITCH) {
// switch() {}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_OPEN) {
_set_error("Expected '(' after switch");
return ERR_PARSE_ERROR;
}
ControlFlowNode *cf = alloc_node<ControlFlowNode>();
cf->flow_op = FLOW_OP_SWITCH;
Node *n = _parse_and_reduce_expression(p_block, p_builtin_types);
if (!n)
return ERR_PARSE_ERROR;
if (n->get_datatype() != TYPE_INT) {
_set_error("Expected integer expression");
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_PARENTHESIS_CLOSE) {
_set_error("Expected ')' after expression");
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_CURLY_BRACKET_OPEN) {
_set_error("Expected '{' after switch statement");
return ERR_PARSE_ERROR;
}
BlockNode *switch_block = alloc_node<BlockNode>();
switch_block->block_type = BlockNode::BLOCK_TYPE_SWITCH;
switch_block->parent_block = p_block;
cf->expressions.push_back(n);
cf->blocks.push_back(switch_block);
p_block->statements.push_back(cf);
int prev_type = TK_CF_CASE;
while (true) { // Go-through multiple cases.
if (_parse_block(switch_block, p_builtin_types, true, true, false) != OK) {
return ERR_PARSE_ERROR;
}
pos = _get_tkpos();
tk = _get_token();
if (tk.type == TK_CF_CASE || tk.type == TK_CF_DEFAULT) {
if (prev_type == TK_CF_DEFAULT) {
if (tk.type == TK_CF_CASE) {
_set_error("Cases must be defined before default case.");
return ERR_PARSE_ERROR;
} else if (prev_type == TK_CF_DEFAULT) {
_set_error("Default case must be defined only once.");
return ERR_PARSE_ERROR;
}
}
prev_type = tk.type;
_set_tkpos(pos);
continue;
} else {
Set<int> constants;
for (int i = 0; i < switch_block->statements.size(); i++) { // Checks for duplicates.
ControlFlowNode *flow = (ControlFlowNode *)switch_block->statements[i];
if (flow) {
if (flow->flow_op == FLOW_OP_CASE) {
ConstantNode *n2 = static_cast<ConstantNode *>(flow->expressions[0]);
if (!n2) {
return ERR_PARSE_ERROR;
}
if (n2->values.empty()) {
return ERR_PARSE_ERROR;
}
if (constants.has(n2->values[0].sint)) {
_set_error("Duplicated case label: '" + itos(n2->values[0].sint) + "'");
return ERR_PARSE_ERROR;
}
constants.insert(n2->values[0].sint);
} else if (flow->flow_op == FLOW_OP_DEFAULT) {
continue;
} else {
return ERR_PARSE_ERROR;
}
} else {
return ERR_PARSE_ERROR;
}
}
break;
}
}
} else if (tk.type == TK_CF_CASE) {
// case x : break; | return;
if (p_block && p_block->block_type == BlockNode::BLOCK_TYPE_CASE) {
_set_tkpos(pos);
return OK;
}
if (!p_block || (p_block->block_type != BlockNode::BLOCK_TYPE_SWITCH)) {
_set_error("case must be placed within switch block");
return ERR_PARSE_ERROR;
}
tk = _get_token();
int sign = 1;
if (tk.type == TK_OP_SUB) {
sign = -1;
tk = _get_token();
}
if (tk.type != TK_INT_CONSTANT) {
_set_error("Expected integer constant");
return ERR_PARSE_ERROR;
}
int constant = (int)tk.constant * sign;
tk = _get_token();
if (tk.type != TK_COLON) {
_set_error("Expected ':'");
return ERR_PARSE_ERROR;
}
ControlFlowNode *cf = alloc_node<ControlFlowNode>();
cf->flow_op = FLOW_OP_CASE;
ConstantNode *n = alloc_node<ConstantNode>();
ConstantNode::Value v;
v.sint = constant;
n->values.push_back(v);
n->datatype = TYPE_INT;
BlockNode *case_block = alloc_node<BlockNode>();
case_block->block_type = BlockNode::BLOCK_TYPE_CASE;
case_block->parent_block = p_block;
cf->expressions.push_back(n);
cf->blocks.push_back(case_block);
p_block->statements.push_back(cf);
Error err = _parse_block(case_block, p_builtin_types, false, true, false);
if (err)
return err;
return OK;
} else if (tk.type == TK_CF_DEFAULT) {
if (p_block && p_block->block_type == BlockNode::BLOCK_TYPE_CASE) {
_set_tkpos(pos);
return OK;
}
if (!p_block || (p_block->block_type != BlockNode::BLOCK_TYPE_SWITCH)) {
_set_error("default must be placed within switch block");
return ERR_PARSE_ERROR;
}
tk = _get_token();
if (tk.type != TK_COLON) {
_set_error("Expected ':'");
return ERR_PARSE_ERROR;
}
ControlFlowNode *cf = alloc_node<ControlFlowNode>();
cf->flow_op = FLOW_OP_DEFAULT;
BlockNode *default_block = alloc_node<BlockNode>();
default_block->block_type = BlockNode::BLOCK_TYPE_DEFAULT;
default_block->parent_block = p_block;
cf->blocks.push_back(default_block);
p_block->statements.push_back(cf);
Error err = _parse_block(default_block, p_builtin_types, false, true, false);
if (err)
return err;
return OK;
} else if (tk.type == TK_CF_DO || tk.type == TK_CF_WHILE) {
// do {} while()
// while() {}
@ -4299,6 +4485,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
}
p_block->statements.push_back(flow);
if (p_block->block_type == BlockNode::BLOCK_TYPE_CASE || p_block->block_type == BlockNode::BLOCK_TYPE_DEFAULT) {
return OK;
}
} else if (tk.type == TK_CF_DISCARD) {
//check return type
@ -4345,9 +4534,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
}
p_block->statements.push_back(flow);
if (p_block->block_type == BlockNode::BLOCK_TYPE_CASE || p_block->block_type == BlockNode::BLOCK_TYPE_DEFAULT) {
return OK;
}
} else if (tk.type == TK_CF_CONTINUE) {
if (!p_can_break) {
if (!p_can_continue) {
//all is good
_set_error("Continuing is not allowed here");
}

View file

@ -125,6 +125,7 @@ public:
TK_CF_DO,
TK_CF_SWITCH,
TK_CF_CASE,
TK_CF_DEFAULT,
TK_CF_BREAK,
TK_CF_CONTINUE,
TK_CF_RETURN,
@ -266,6 +267,8 @@ public:
FLOW_OP_DO,
FLOW_OP_BREAK,
FLOW_OP_SWITCH,
FLOW_OP_CASE,
FLOW_OP_DEFAULT,
FLOW_OP_CONTINUE,
FLOW_OP_DISCARD
};
@ -420,6 +423,15 @@ public:
FunctionNode *parent_function;
BlockNode *parent_block;
enum BlockType {
BLOCK_TYPE_STANDART,
BLOCK_TYPE_SWITCH,
BLOCK_TYPE_CASE,
BLOCK_TYPE_DEFAULT,
};
int block_type;
struct Variable {
DataType type;
DataPrecision precision;
@ -436,6 +448,7 @@ public:
Node(TYPE_BLOCK),
parent_function(NULL),
parent_block(NULL),
block_type(BLOCK_TYPE_STANDART),
single_statement(false) {}
};