1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-06 12:20:30 +00:00

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

@@ -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");
}