diff options
Diffstat (limited to 'servers/visual/shader_language.cpp')
-rw-r--r-- | servers/visual/shader_language.cpp | 325 |
1 files changed, 311 insertions, 14 deletions
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 5c28705192..4bc65a8f4f 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -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" }, @@ -961,6 +962,7 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<String if (r_type) { *r_type = IDENTIFIER_FUNCTION; } + return true; } } @@ -3777,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 '}'"); @@ -3861,14 +3871,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui return ERR_PARSE_ERROR; } + decl.size = ((uint32_t)tk.constant); tk = _get_token(); if (tk.type != TK_BRACKET_CLOSE) { _set_error("Expected ']'"); return ERR_PARSE_ERROR; } - - decl.size = ((uint32_t)tk.constant); var.array_size = decl.size; } @@ -4021,6 +4030,10 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui _set_error("Expected array initialization"); return ERR_PARSE_ERROR; } + if (is_const) { + _set_error("Expected initialization of constant"); + return ERR_PARSE_ERROR; + } } node->declarations.push_back(decl); @@ -4050,6 +4063,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui tk = _get_token(); node->declarations.push_back(decl); } else { + if (is_const) { + _set_error("Expected initialization of constant"); + return ERR_PARSE_ERROR; + } + VariableDeclarationNode *node = alloc_node<VariableDeclarationNode>(); node->datatype = type; node->precision = precision; @@ -4123,37 +4141,250 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui } else { _set_tkpos(pos); //rollback } - } else if (tk.type == TK_CF_WHILE) { - //if () {} + } else if (tk.type == TK_CF_SWITCH) { + // switch() {} tk = _get_token(); if (tk.type != TK_PARENTHESIS_OPEN) { - _set_error("Expected '(' after while"); + _set_error("Expected '(' after switch"); return ERR_PARSE_ERROR; } - ControlFlowNode *cf = alloc_node<ControlFlowNode>(); - cf->flow_op = FLOW_OP_WHILE; + 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); - BlockNode *block = alloc_node<BlockNode>(); - block->parent_block = p_block; + 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(block); + cf->blocks.push_back(case_block); p_block->statements.push_back(cf); - Error err = _parse_block(block, p_builtin_types, true, true, true); + 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() {} + bool is_do = tk.type == TK_CF_DO; + + BlockNode *do_block = NULL; + if (is_do) { + + do_block = alloc_node<BlockNode>(); + do_block->parent_block = p_block; + + Error err = _parse_block(do_block, p_builtin_types, true, true, true); + if (err) + return err; + + tk = _get_token(); + if (tk.type != TK_CF_WHILE) { + _set_error("Expected while after do"); + return ERR_PARSE_ERROR; + } + } + tk = _get_token(); + + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after while"); + return ERR_PARSE_ERROR; + } + + ControlFlowNode *cf = alloc_node<ControlFlowNode>(); + if (is_do) { + cf->flow_op = FLOW_OP_DO; + } else { + cf->flow_op = FLOW_OP_WHILE; + } + Node *n = _parse_and_reduce_expression(p_block, p_builtin_types); + if (!n) + return ERR_PARSE_ERROR; + + tk = _get_token(); + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')' after expression"); + return ERR_PARSE_ERROR; + } + if (!is_do) { + BlockNode *block = alloc_node<BlockNode>(); + block->parent_block = p_block; + cf->expressions.push_back(n); + 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; + } else { + + cf->expressions.push_back(n); + cf->blocks.push_back(do_block); + p_block->statements.push_back(cf); + + tk = _get_token(); + if (tk.type != TK_SEMICOLON) { + _set_error("Expected ';'"); + return ERR_PARSE_ERROR; + } + } } else if (tk.type == TK_CF_FOR) { - //if () {} + // for() {} tk = _get_token(); if (tk.type != TK_PARENTHESIS_OPEN) { _set_error("Expected '(' after for"); @@ -4254,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 @@ -4300,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"); } @@ -4903,6 +5141,14 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct if (err) return err; + if (func_node->return_type != DataType::TYPE_VOID) { + + BlockNode *block = func_node->body; + if (_find_last_flow_op_in_block(block, FlowOperation::FLOW_OP_RETURN) != OK) { + _set_error("Expected at least one return statement in a non-void function."); + return ERR_PARSE_ERROR; + } + } current_function = StringName(); } } @@ -4913,6 +5159,57 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct return OK; } +Error ShaderLanguage::_find_last_flow_op_in_op(ControlFlowNode *p_flow, FlowOperation p_op) { + + bool found = false; + + for (int i = p_flow->blocks.size() - 1; i >= 0; i--) { + if (p_flow->blocks[i]->type == Node::TYPE_BLOCK) { + BlockNode *last_block = (BlockNode *)p_flow->blocks[i]; + if (_find_last_flow_op_in_block(last_block, p_op) == OK) { + found = true; + break; + } + } + } + if (found) { + return OK; + } + return FAILED; +} + +Error ShaderLanguage::_find_last_flow_op_in_block(BlockNode *p_block, FlowOperation p_op) { + + bool found = false; + + for (int i = p_block->statements.size() - 1; i >= 0; i--) { + + if (p_block->statements[i]->type == Node::TYPE_CONTROL_FLOW) { + ControlFlowNode *flow = (ControlFlowNode *)p_block->statements[i]; + if (flow->flow_op == p_op) { + found = true; + break; + } else { + if (_find_last_flow_op_in_op(flow, p_op) == OK) { + found = true; + break; + } + } + } else if (p_block->statements[i]->type == Node::TYPE_BLOCK) { + BlockNode *block = (BlockNode *)p_block->statements[i]; + if (_find_last_flow_op_in_block(block, p_op) == OK) { + found = true; + break; + } + } + } + + if (found) { + return OK; + } + return FAILED; +} + // skips over whitespace and /* */ and // comments static int _get_first_ident_pos(const String &p_code) { |