Implements switch to shaders
This commit is contained in:
parent
89bcfa4b36
commit
4dda253ee0
@ -804,6 +804,15 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
|
|||||||
code += "else\n";
|
code += "else\n";
|
||||||
code += _dump_node_code(cf_node->blocks[1], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
|
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) {
|
} else if (cf_node->flow_op == SL::FLOW_OP_DO) {
|
||||||
code += _mktab(p_level);
|
code += _mktab(p_level);
|
||||||
code += "do";
|
code += "do";
|
||||||
|
@ -801,6 +801,15 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener
|
|||||||
code += _mktab(p_level) + "else\n";
|
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);
|
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) {
|
} else if (cfnode->flow_op == SL::FLOW_OP_DO) {
|
||||||
code += _mktab(p_level) + "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);
|
code += _dump_node_code(cfnode->blocks[0], p_level + 1, r_gen_code, p_actions, p_default_actions, p_assigning);
|
||||||
|
@ -283,6 +283,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
|
|||||||
{ TK_CF_DO, "do" },
|
{ TK_CF_DO, "do" },
|
||||||
{ TK_CF_SWITCH, "switch" },
|
{ TK_CF_SWITCH, "switch" },
|
||||||
{ TK_CF_CASE, "case" },
|
{ TK_CF_CASE, "case" },
|
||||||
|
{ TK_CF_DEFAULT, "default" },
|
||||||
{ TK_CF_BREAK, "break" },
|
{ TK_CF_BREAK, "break" },
|
||||||
{ TK_CF_CONTINUE, "continue" },
|
{ TK_CF_CONTINUE, "continue" },
|
||||||
{ TK_CF_RETURN, "return" },
|
{ TK_CF_RETURN, "return" },
|
||||||
@ -3778,6 +3779,14 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||||||
TkPos pos = _get_tkpos();
|
TkPos pos = _get_tkpos();
|
||||||
|
|
||||||
Token tk = _get_token();
|
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 (tk.type == TK_CURLY_BRACKET_CLOSE) { //end of block
|
||||||
if (p_just_one) {
|
if (p_just_one) {
|
||||||
_set_error("Unexpected '}'");
|
_set_error("Unexpected '}'");
|
||||||
@ -4132,6 +4141,183 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||||||
} else {
|
} else {
|
||||||
_set_tkpos(pos); //rollback
|
_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) {
|
} else if (tk.type == TK_CF_DO || tk.type == TK_CF_WHILE) {
|
||||||
// do {} while()
|
// do {} while()
|
||||||
// while() {}
|
// while() {}
|
||||||
@ -4299,6 +4485,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||||||
}
|
}
|
||||||
|
|
||||||
p_block->statements.push_back(flow);
|
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) {
|
} else if (tk.type == TK_CF_DISCARD) {
|
||||||
|
|
||||||
//check return type
|
//check return type
|
||||||
@ -4345,9 +4534,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
|
|||||||
}
|
}
|
||||||
|
|
||||||
p_block->statements.push_back(flow);
|
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) {
|
} else if (tk.type == TK_CF_CONTINUE) {
|
||||||
|
|
||||||
if (!p_can_break) {
|
if (!p_can_continue) {
|
||||||
//all is good
|
//all is good
|
||||||
_set_error("Continuing is not allowed here");
|
_set_error("Continuing is not allowed here");
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,7 @@ public:
|
|||||||
TK_CF_DO,
|
TK_CF_DO,
|
||||||
TK_CF_SWITCH,
|
TK_CF_SWITCH,
|
||||||
TK_CF_CASE,
|
TK_CF_CASE,
|
||||||
|
TK_CF_DEFAULT,
|
||||||
TK_CF_BREAK,
|
TK_CF_BREAK,
|
||||||
TK_CF_CONTINUE,
|
TK_CF_CONTINUE,
|
||||||
TK_CF_RETURN,
|
TK_CF_RETURN,
|
||||||
@ -266,6 +267,8 @@ public:
|
|||||||
FLOW_OP_DO,
|
FLOW_OP_DO,
|
||||||
FLOW_OP_BREAK,
|
FLOW_OP_BREAK,
|
||||||
FLOW_OP_SWITCH,
|
FLOW_OP_SWITCH,
|
||||||
|
FLOW_OP_CASE,
|
||||||
|
FLOW_OP_DEFAULT,
|
||||||
FLOW_OP_CONTINUE,
|
FLOW_OP_CONTINUE,
|
||||||
FLOW_OP_DISCARD
|
FLOW_OP_DISCARD
|
||||||
};
|
};
|
||||||
@ -420,6 +423,15 @@ public:
|
|||||||
FunctionNode *parent_function;
|
FunctionNode *parent_function;
|
||||||
BlockNode *parent_block;
|
BlockNode *parent_block;
|
||||||
|
|
||||||
|
enum BlockType {
|
||||||
|
BLOCK_TYPE_STANDART,
|
||||||
|
BLOCK_TYPE_SWITCH,
|
||||||
|
BLOCK_TYPE_CASE,
|
||||||
|
BLOCK_TYPE_DEFAULT,
|
||||||
|
};
|
||||||
|
|
||||||
|
int block_type;
|
||||||
|
|
||||||
struct Variable {
|
struct Variable {
|
||||||
DataType type;
|
DataType type;
|
||||||
DataPrecision precision;
|
DataPrecision precision;
|
||||||
@ -436,6 +448,7 @@ public:
|
|||||||
Node(TYPE_BLOCK),
|
Node(TYPE_BLOCK),
|
||||||
parent_function(NULL),
|
parent_function(NULL),
|
||||||
parent_block(NULL),
|
parent_block(NULL),
|
||||||
|
block_type(BLOCK_TYPE_STANDART),
|
||||||
single_statement(false) {}
|
single_statement(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user