/*************************************************************************/ /* shader_language.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "shader_language.h" #include "print_string.h" #include "os/os.h" static bool _is_text_char(CharType c) { return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; } static bool _is_number(CharType c) { return (c>='0' && c<='9'); } static bool _is_hex(CharType c) { return (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'); } String ShaderLanguage::get_operator_text(Operator p_op) { static const char* op_names[OP_MAX]={"==", "!=", "<", "<=", ">", ">=", "&&", "||", "!", "-", "+", "-", "*", "/", "%", "<<", ">>", "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", "&=", "|=", "^=", "&", "|", "^", "~", "++" "--", "()", "construct"}; return op_names[p_op]; } const char * ShaderLanguage::token_names[TK_MAX]={ "EMPTY", "IDENTIFIER", "TRUE", "FALSE", "REAL_CONSTANT", "INT_CONSTANT", "TYPE_VOID", "TYPE_BOOL", "TYPE_BVEC2", "TYPE_BVEC3", "TYPE_BVEC4", "TYPE_INT", "TYPE_IVEC2", "TYPE_IVEC3", "TYPE_IVEC4", "TYPE_UINT", "TYPE_UVEC2", "TYPE_UVEC3", "TYPE_UVEC4", "TYPE_FLOAT", "TYPE_VEC2", "TYPE_VEC3", "TYPE_VEC4", "TYPE_MAT2", "TYPE_MAT3", "TYPE_MAT4", "TYPE_SAMPLER2D", "TYPE_ISAMPLER2D", "TYPE_USAMPLER2D", "TYPE_SAMPLERCUBE", "PRECISION_LOW", "PRECISION_MID", "PRECISION_HIGH", "OP_EQUAL", "OP_NOT_EQUAL", "OP_LESS", "OP_LESS_EQUAL", "OP_GREATER", "OP_GREATER_EQUAL", "OP_AND", "OP_OR", "OP_NOT", "OP_ADD", "OP_SUB", "OP_MUL", "OP_DIV", "OP_MOD", "OP_SHIFT_LEFT", "OP_SHIFT_RIGHT", "OP_ASSIGN", "OP_ASSIGN_ADD", "OP_ASSIGN_SUB", "OP_ASSIGN_MUL", "OP_ASSIGN_DIV", "OP_ASSIGN_MOD", "OP_ASSIGN_SHIFT_LEFT", "OP_ASSIGN_SHIFT_RIGHT", "OP_ASSIGN_BIT_AND", "OP_ASSIGN_BIT_OR", "OP_ASSIGN_BIT_XOR", "OP_BIT_AND", "OP_BIT_OR", "OP_BIT_XOR", "OP_BIT_INVERT", "OP_INCREMENT", "OP_DECREMENT", "CF_IF", "CF_ELSE", "CF_FOR", "CF_WHILE", "CF_DO", "CF_SWITCH", "CF_CASE", "CF_BREAK", "CF_CONTINUE", "CF_RETURN", "BRACKET_OPEN", "BRACKET_CLOSE", "CURLY_BRACKET_OPEN", "CURLY_BRACKET_CLOSE", "PARENTHESIS_OPEN", "PARENTHESIS_CLOSE", "QUESTION", "COMMA", "COLON", "SEMICOLON", "PERIOD", "UNIFORM", "VARYING", "RENDER_MODE", "HINT_WHITE_TEXTURE", "HINT_BLACK_TEXTURE", "HINT_NORMAL_TEXTURE", "HINT_ALBEDO_TEXTURE", "HINT_COLOR", "HINT_RANGE", "CURSOR", "ERROR", "EOF", }; String ShaderLanguage::get_token_text(Token p_token) { String name=token_names[p_token.type]; if (p_token.type==TK_INT_CONSTANT || p_token.type==TK_REAL_CONSTANT) { name+="("+rtos(p_token.constant)+")"; } else if (p_token.type==TK_IDENTIFIER) { name+="("+String(p_token.text)+")"; } else if (p_token.type==TK_ERROR) { name+="("+String(p_token.text)+")"; } return name; } ShaderLanguage::Token ShaderLanguage::_make_token(TokenType p_type,const StringName& p_text) { Token tk; tk.type=p_type; tk.text=p_text; tk.line=tk_line; if (tk.type==TK_ERROR) { _set_error(p_text); } return tk; } const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[]={ {TK_TRUE,"true"}, {TK_FALSE,"false"}, {TK_TYPE_VOID,"void"}, {TK_TYPE_BOOL,"bool"}, {TK_TYPE_BVEC2,"bvec2"}, {TK_TYPE_BVEC3,"bvec3"}, {TK_TYPE_BVEC4,"bvec4"}, {TK_TYPE_INT,"int"}, {TK_TYPE_IVEC2,"ivec2"}, {TK_TYPE_IVEC3,"ivec3"}, {TK_TYPE_IVEC4,"ivec4"}, {TK_TYPE_UINT,"uint"}, {TK_TYPE_UVEC2,"uvec2"}, {TK_TYPE_UVEC3,"uvec3"}, {TK_TYPE_UVEC4,"uvec4"}, {TK_TYPE_FLOAT,"float"}, {TK_TYPE_VEC2,"vec2"}, {TK_TYPE_VEC3,"vec3"}, {TK_TYPE_VEC4,"vec4"}, {TK_TYPE_MAT2,"mat2"}, {TK_TYPE_MAT3,"mat3"}, {TK_TYPE_MAT4,"mat4"}, {TK_TYPE_SAMPLER2D,"sampler2D"}, {TK_TYPE_ISAMPLER2D,"isampler2D"}, {TK_TYPE_USAMPLER2D,"usampler2D"}, {TK_TYPE_SAMPLERCUBE,"samplerCube"}, {TK_PRECISION_LOW,"lowp"}, {TK_PRECISION_MID,"mediump"}, {TK_PRECISION_HIGH,"highp"}, {TK_CF_IF,"if"}, {TK_CF_ELSE,"else"}, {TK_CF_FOR,"for"}, {TK_CF_WHILE,"while"}, {TK_CF_DO,"do"}, {TK_CF_SWITCH,"switch"}, {TK_CF_CASE,"case"}, {TK_CF_BREAK,"break"}, {TK_CF_CONTINUE,"continue"}, {TK_CF_RETURN,"return"}, {TK_UNIFORM,"uniform"}, {TK_VARYING,"varying"}, {TK_RENDER_MODE,"render_mode"}, {TK_HINT_WHITE_TEXTURE,"white"}, {TK_HINT_BLACK_TEXTURE,"black"}, {TK_HINT_NORMAL_TEXTURE,"normal"}, {TK_HINT_ALBEDO_TEXTURE,"albedo"}, {TK_HINT_COLOR,"color"}, {TK_HINT_RANGE,"hint_range"}, {TK_ERROR,NULL} }; ShaderLanguage::Token ShaderLanguage::_get_token() { #define GETCHAR(m_idx) (((char_idx+m_idx)': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_GREATER_EQUAL); } else if (GETCHAR(0)=='<') { char_idx++;; if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_SHIFT_RIGHT); } return _make_token(TK_OP_SHIFT_RIGHT); } return _make_token(TK_OP_GREATER); } break; case '!': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_NOT_EQUAL); } return _make_token(TK_OP_NOT); } break; //case '"' //string - no strings in shader //case '\'' //string - no strings in shader case '{': return _make_token(TK_CURLY_BRACKET_OPEN); case '}': return _make_token(TK_CURLY_BRACKET_CLOSE); case '[': return _make_token(TK_BRACKET_OPEN); case ']': return _make_token(TK_BRACKET_CLOSE); case '(': return _make_token(TK_PARENTHESIS_OPEN); case ')': return _make_token(TK_PARENTHESIS_CLOSE); case ',': return _make_token(TK_COMMA); case ';': return _make_token(TK_SEMICOLON); case '?': return _make_token(TK_QUESTION); case ':': return _make_token(TK_COLON); case '^': return _make_token(TK_OP_BIT_XOR); case '~': return _make_token(TK_OP_BIT_INVERT); case '&': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_BIT_AND); } else if (GETCHAR(0)=='&') { char_idx++; return _make_token(TK_OP_AND); } return _make_token(TK_OP_BIT_AND); } break; case '|': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_BIT_OR); } else if (GETCHAR(0)=='|') { char_idx++; return _make_token(TK_OP_OR); } return _make_token(TK_OP_BIT_OR); } break; case '*': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_MUL); } return _make_token(TK_OP_MUL); } break; case '+': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_ADD); } else if (GETCHAR(0)=='+') { char_idx++; return _make_token(TK_OP_INCREMENT); } return _make_token(TK_OP_ADD); } break; case '-': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_SUB); }else if (GETCHAR(0)=='-') { char_idx++; return _make_token(TK_OP_DECREMENT); } return _make_token(TK_OP_SUB); } break; case '%': { if (GETCHAR(0)=='=') { char_idx++; return _make_token(TK_OP_ASSIGN_MOD); } return _make_token(TK_OP_MOD); } break; default: { char_idx--; //go back one, since we have no idea what this is if (_is_number(GETCHAR(0)) || (GETCHAR(0)=='.' && _is_number(GETCHAR(1)))) { // parse number bool period_found=false; bool exponent_found=false; bool hexa_found=false; bool sign_found=false; bool minus_exponent_found=false; String str; int i=0; while(true) { if (GETCHAR(i)=='.') { if (period_found || exponent_found) return _make_token(TK_ERROR,"Invalid numeric constant"); period_found=true; } else if (GETCHAR(i)=='x') { if (hexa_found || str.length()!=1 || str[0]!='0') return _make_token(TK_ERROR,"Invalid numeric constant"); hexa_found=true; } else if (GETCHAR(i)=='e') { if (hexa_found || exponent_found) return _make_token(TK_ERROR,"Invalid numeric constant"); exponent_found=true; } else if (_is_number(GETCHAR(i))) { //all ok } else if (hexa_found && _is_hex(GETCHAR(i))) { } else if ((GETCHAR(i)=='-' || GETCHAR(i)=='+') && exponent_found) { if (sign_found) return _make_token(TK_ERROR,"Invalid numeric constant"); sign_found=true; if (GETCHAR(i)=='-') minus_exponent_found=true; } else break; str+=CharType(GETCHAR(i)); i++; } if (!_is_number(str[str.length()-1])) return _make_token(TK_ERROR,"Invalid numeric constant"); char_idx+=str.length(); Token tk; if (period_found || minus_exponent_found) tk.type=TK_REAL_CONSTANT; else tk.type=TK_INT_CONSTANT; if (!str.is_valid_float()) { return _make_token(TK_ERROR,"Invalid numeric constant"); } tk.constant=str.to_double(); tk.line=tk_line; return tk; } if (GETCHAR(0)=='.') { //parse period char_idx++; return _make_token(TK_PERIOD); } if (_is_text_char(GETCHAR(0))) { // parse identifier String str; while(_is_text_char(GETCHAR(0))) { str+=CharType(GETCHAR(0)); char_idx++; } //see if keyword //should be converted to a static map int idx=0; while(keyword_list[idx].text) { if (str==keyword_list[idx].text) { return _make_token(keyword_list[idx].token); } idx++; } return _make_token(TK_IDENTIFIER,str); } if (GETCHAR(0)>32) return _make_token(TK_ERROR,"Tokenizer: Unknown character #"+itos(GETCHAR(0))+": '"+String::chr(GETCHAR(0))+"'"); else return _make_token(TK_ERROR,"Tokenizer: Unknown character #"+itos(GETCHAR(0))); } break; } } ERR_PRINT("BUG"); return Token(); } String ShaderLanguage::token_debug(const String& p_code) { clear(); code=p_code; String output; Token tk = _get_token(); while(tk.type!=TK_EOF && tk.type!=TK_ERROR) { print_line(get_token_text(tk)); output+=itos(tk_line)+": "+get_token_text(tk)+"\n"; tk = _get_token(); } return output; } bool ShaderLanguage::is_token_datatype(TokenType p_type) { return ( p_type==TK_TYPE_VOID || p_type==TK_TYPE_BOOL || p_type==TK_TYPE_BVEC2 || p_type==TK_TYPE_BVEC3 || p_type==TK_TYPE_BVEC4 || p_type==TK_TYPE_INT || p_type==TK_TYPE_IVEC2 || p_type==TK_TYPE_IVEC3 || p_type==TK_TYPE_IVEC4 || p_type==TK_TYPE_UINT || p_type==TK_TYPE_UVEC2 || p_type==TK_TYPE_UVEC3 || p_type==TK_TYPE_UVEC4 || p_type==TK_TYPE_FLOAT || p_type==TK_TYPE_VEC2 || p_type==TK_TYPE_VEC3 || p_type==TK_TYPE_VEC4 || p_type==TK_TYPE_MAT2 || p_type==TK_TYPE_MAT3 || p_type==TK_TYPE_MAT4 || p_type==TK_TYPE_SAMPLER2D || p_type==TK_TYPE_ISAMPLER2D || p_type==TK_TYPE_USAMPLER2D || p_type==TK_TYPE_SAMPLERCUBE ); } ShaderLanguage::DataType ShaderLanguage::get_token_datatype(TokenType p_type) { return DataType(p_type-TK_TYPE_VOID); } bool ShaderLanguage::is_token_precision(TokenType p_type) { return ( p_type==TK_PRECISION_LOW || p_type==TK_PRECISION_MID || p_type==TK_PRECISION_HIGH ); } ShaderLanguage::DataPrecision ShaderLanguage::get_token_precision(TokenType p_type) { if (p_type==TK_PRECISION_LOW) return PRECISION_LOWP; else if (p_type==TK_PRECISION_HIGH) return PRECISION_HIGHP; else return PRECISION_MEDIUMP; } String ShaderLanguage::get_datatype_name(DataType p_type) { switch(p_type) { case TYPE_VOID: return "void"; case TYPE_BOOL: return "bool"; case TYPE_BVEC2: return "bvec2"; case TYPE_BVEC3: return "bvec3"; case TYPE_BVEC4: return "bvec4"; case TYPE_INT: return "int"; case TYPE_IVEC2: return "ivec2"; case TYPE_IVEC3: return "ivec3"; case TYPE_IVEC4: return "ivec4"; case TYPE_UINT: return "uint"; case TYPE_UVEC2: return "uvec2"; case TYPE_UVEC3: return "uvec3"; case TYPE_UVEC4: return "uvec4"; case TYPE_FLOAT: return "float"; case TYPE_VEC2: return "vec2"; case TYPE_VEC3: return "vec3"; case TYPE_VEC4: return "vec4"; case TYPE_MAT2: return "mat2"; case TYPE_MAT3: return "mat3"; case TYPE_MAT4: return "mat4"; case TYPE_SAMPLER2D: return "sampler2D"; case TYPE_ISAMPLER2D: return "isampler2D"; case TYPE_USAMPLER2D: return "usampler2D"; case TYPE_SAMPLERCUBE: return "samplerCube"; } return ""; } bool ShaderLanguage::is_token_nonvoid_datatype(TokenType p_type) { return is_token_datatype(p_type) && p_type!=TK_TYPE_VOID; } void ShaderLanguage::clear() { completion_type=COMPLETION_NONE; completion_block=NULL; completion_function=StringName(); error_line=0; tk_line=1; char_idx=0; error_set=false; error_str=""; while(nodes) { Node *n = nodes; nodes=nodes->next; memdelete(n); } } bool ShaderLanguage::_find_identifier(const BlockNode* p_block,const Map &p_builtin_types,const StringName& p_identifier, DataType *r_data_type, IdentifierType *r_type) { if (p_builtin_types.has(p_identifier)) { if (r_data_type) { *r_data_type=p_builtin_types[p_identifier]; } if (r_type) { *r_type=IDENTIFIER_BUILTIN_VAR; } return true; } FunctionNode *function=NULL; while(p_block) { if (p_block->variables.has(p_identifier)) { if (r_data_type) { *r_data_type=p_block->variables[p_identifier].type; } if (r_type) { *r_type=IDENTIFIER_LOCAL_VAR; } return true; } if (p_block->parent_function) { function=p_block->parent_function; break; } else { ERR_FAIL_COND_V(!p_block->parent_block,false); p_block=p_block->parent_block; } } if (function) { for(int i=0;iarguments.size();i++) { if (function->arguments[i].name==p_identifier) { if (r_data_type) { *r_data_type=function->arguments[i].type; } if (r_type) { *r_type=IDENTIFIER_FUNCTION_ARGUMENT; } return true; } } } if (shader->varyings.has(p_identifier)) { if (r_data_type) { *r_data_type=shader->varyings[p_identifier].type; } if (r_type) { *r_type=IDENTIFIER_VARYING; } return true; } if (shader->uniforms.has(p_identifier)) { if (r_data_type) { *r_data_type=shader->uniforms[p_identifier].type; } if (r_type) { *r_type=IDENTIFIER_UNIFORM; } return true; } for(int i=0;ifunctions.size();i++) { if (!shader->functions[i].callable) continue; if (shader->functions[i].name==p_identifier) { if (r_data_type) { *r_data_type=shader->functions[i].function->return_type; } if (r_type) { *r_type=IDENTIFIER_FUNCTION; } } } return false; } bool ShaderLanguage::_validate_operator(OperatorNode *p_op,DataType *r_ret_type) { bool valid=false; DataType ret_type; switch(p_op->op) { case OP_EQUAL: case OP_NOT_EQUAL: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); valid=na==nb; ret_type=TYPE_BOOL; } break; case OP_LESS: case OP_LESS_EQUAL: case OP_GREATER: case OP_GREATER_EQUAL: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); valid = na==nb && (na==TYPE_UINT || na==TYPE_INT || na==TYPE_FLOAT); ret_type=TYPE_BOOL; } break; case OP_AND: case OP_OR: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); valid = na==nb && na==TYPE_BOOL; ret_type=TYPE_BOOL; } break; case OP_NOT: { DataType na = p_op->arguments[0]->get_datatype(); valid = na==TYPE_BOOL; ret_type=TYPE_BOOL; } break; case OP_INCREMENT: case OP_DECREMENT: case OP_POST_INCREMENT: case OP_POST_DECREMENT: case OP_NEGATE: { DataType na = p_op->arguments[0]->get_datatype(); valid = na>TYPE_BOOL && naarguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na>nb) { //make things easier; SWAP(na,nb); } if (na==nb) { valid = (na>TYPE_BOOL && naop==OP_MUL && na>=TYPE_MAT2 && na<=TYPE_MAT4); ret_type=na; } else if (na==TYPE_INT && nb==TYPE_IVEC2) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_INT && nb==TYPE_IVEC3) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_INT && nb==TYPE_IVEC4) { valid=true; ret_type=TYPE_IVEC4; } else if (na==TYPE_UINT && nb==TYPE_UVEC2) { valid=true; ret_type=TYPE_UVEC2; } else if (na==TYPE_UINT && nb==TYPE_UVEC3) { valid=true; ret_type=TYPE_UVEC3; } else if (na==TYPE_UINT && nb==TYPE_UVEC4) { valid=true; ret_type=TYPE_UVEC4; } else if (na==TYPE_FLOAT && nb==TYPE_VEC2) { valid=true; ret_type=TYPE_VEC2; } else if (na==TYPE_FLOAT && nb==TYPE_VEC3) { valid=true; ret_type=TYPE_VEC3; } else if (na==TYPE_FLOAT && nb==TYPE_VEC4) { valid=true; ret_type=TYPE_VEC4; } else if (p_op->op==OP_MUL && na==TYPE_VEC2 && nb==TYPE_MAT2) { valid=true; ret_type=TYPE_MAT2; } else if (p_op->op==OP_MUL && na==TYPE_VEC3 && nb==TYPE_MAT3) { valid=true; ret_type=TYPE_MAT3; } else if (p_op->op==OP_MUL && na==TYPE_VEC4 && nb==TYPE_MAT4) { valid=true; ret_type=TYPE_MAT4; } } break; case OP_ASSIGN_MOD: case OP_MOD: { /* * The operator modulus (%) operates on signed or unsigned integers or integer vectors. The operand * types must both be signed or both be unsigned. The operands cannot be vectors of differing size. If * one operand is a scalar and the other vector, then the scalar is applied component-wise to the vector, * resulting in the same type as the vector. If both are vectors of the same size, the result is computed * component-wise. */ DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na==TYPE_INT && nb==TYPE_INT) { valid=true; ret_type=TYPE_INT; } else if (na==TYPE_IVEC2 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC4; } else if (na==TYPE_IVEC2 && nb==TYPE_IVEC2) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_IVEC3) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_IVEC4) { valid=true; ret_type=TYPE_IVEC4; ///// } else if (na==TYPE_UINT && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UINT; } else if (na==TYPE_UVEC2 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC2; } else if (na==TYPE_UVEC3 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC3; } else if (na==TYPE_UVEC4 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC4; } else if (na==TYPE_UVEC2 && nb==TYPE_UVEC2) { valid=true; ret_type=TYPE_UVEC2; } else if (na==TYPE_UVEC3 && nb==TYPE_UVEC3) { valid=true; ret_type=TYPE_UVEC3; } else if (na==TYPE_UVEC4 && nb==TYPE_UVEC4) { valid=true; ret_type=TYPE_UVEC4; } } break; case OP_ASSIGN_SHIFT_LEFT: case OP_ASSIGN_SHIFT_RIGHT: case OP_SHIFT_LEFT: case OP_SHIFT_RIGHT: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na>=TYPE_UINT && na<=TYPE_UVEC4) { na=DataType(na-4); } if (nb>=TYPE_UINT && nb<=TYPE_UVEC4) { nb=DataType(nb-4); } if (na==TYPE_INT && nb==TYPE_INT) { valid=true; ret_type=TYPE_INT; } else if (na==TYPE_IVEC2 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC4; } else if (na==TYPE_IVEC2 && nb==TYPE_IVEC2) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_IVEC3) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_IVEC4) { valid=true; ret_type=TYPE_IVEC4; } } break; case OP_ASSIGN: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); valid=na==nb; ret_type=na; } break; case OP_ASSIGN_ADD: case OP_ASSIGN_SUB: case OP_ASSIGN_MUL: case OP_ASSIGN_DIV: { DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na==nb) { valid = (na>TYPE_BOOL && naop==OP_ASSIGN_MUL && na>=TYPE_MAT2 && na<=TYPE_MAT4); ret_type=na; } else if (na==TYPE_IVEC2 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_INT ) { valid=true; ret_type=TYPE_IVEC4; } else if (na==TYPE_UVEC2 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC2; } else if (na==TYPE_UVEC3 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC3; } else if (na==TYPE_UVEC4 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC4; } else if (na==TYPE_VEC2 && nb==TYPE_FLOAT ) { valid=true; ret_type=TYPE_VEC2; } else if (na==TYPE_VEC3 && nb==TYPE_FLOAT) { valid=true; ret_type=TYPE_VEC3; } else if (na==TYPE_VEC4 && nb==TYPE_FLOAT) { valid=true; ret_type=TYPE_VEC4; } else if (p_op->op==OP_ASSIGN_MUL && na==TYPE_MAT2 && nb==TYPE_VEC2) { valid=true; ret_type=TYPE_MAT2; } else if (p_op->op==OP_ASSIGN_MUL && na==TYPE_MAT3 && nb==TYPE_VEC3) { valid=true; ret_type=TYPE_MAT3; } else if (p_op->op==OP_ASSIGN_MUL && na==TYPE_MAT4 && nb==TYPE_VEC4) { valid=true; ret_type=TYPE_MAT4; } } break; case OP_ASSIGN_BIT_AND: case OP_ASSIGN_BIT_OR: case OP_ASSIGN_BIT_XOR: case OP_BIT_AND: case OP_BIT_OR: case OP_BIT_XOR: { /* * The bitwise operators and (&), exclusive-or (^), and inclusive-or (|). The operands must be of type * signed or unsigned integers or integer vectors. The operands cannot be vectors of differing size. If * one operand is a scalar and the other a vector, the scalar is applied component-wise to the vector, * resulting in the same type as the vector. The fundamental types of the operands (signed or unsigned) * must match. */ DataType na = p_op->arguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); if (na>nb && p_op->op>=OP_BIT_AND) { //can swap for non assign SWAP(na,nb); } if (na==TYPE_INT && nb==TYPE_INT) { valid=true; ret_type=TYPE_INT; } else if (na==TYPE_IVEC2 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_INT) { valid=true; ret_type=TYPE_IVEC4; } else if (na==TYPE_IVEC2 && nb==TYPE_IVEC2) { valid=true; ret_type=TYPE_IVEC2; } else if (na==TYPE_IVEC3 && nb==TYPE_IVEC3) { valid=true; ret_type=TYPE_IVEC3; } else if (na==TYPE_IVEC4 && nb==TYPE_IVEC4) { valid=true; ret_type=TYPE_IVEC4; ///// } else if (na==TYPE_UINT && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UINT; } else if (na==TYPE_UVEC2 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC2; } else if (na==TYPE_UVEC3 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC3; } else if (na==TYPE_UVEC4 && nb==TYPE_UINT) { valid=true; ret_type=TYPE_UVEC4; } else if (na==TYPE_UVEC2 && nb==TYPE_UVEC2) { valid=true; ret_type=TYPE_UVEC2; } else if (na==TYPE_UVEC3 && nb==TYPE_UVEC3) { valid=true; ret_type=TYPE_UVEC3; } else if (na==TYPE_UVEC4 && nb==TYPE_UVEC4) { valid=true; ret_type=TYPE_UVEC4; } } break; case OP_BIT_INVERT: { //unaries DataType na = p_op->arguments[0]->get_datatype(); valid = na>=TYPE_INT && naarguments[0]->get_datatype(); DataType nb = p_op->arguments[1]->get_datatype(); DataType nc = p_op->arguments[2]->get_datatype(); valid = na==TYPE_BOOL && (nb==nc); ret_type=nb; } break; default: { ERR_FAIL_V(false); } } if (r_ret_type) *r_ret_type=ret_type; return valid; } const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[]={ //constructors {"bool",TYPE_BOOL,{TYPE_BOOL,TYPE_VOID}}, {"bvec2",TYPE_BVEC2,{TYPE_BOOL,TYPE_VOID}}, {"bvec2",TYPE_BVEC2,{TYPE_BOOL,TYPE_BOOL,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_BOOL,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_BOOL,TYPE_BOOL,TYPE_BOOL,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_BVEC2,TYPE_BOOL,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_BOOL,TYPE_BVEC2,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BOOL,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BOOL,TYPE_BOOL,TYPE_BOOL,TYPE_BOOL,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BOOL,TYPE_BVEC2,TYPE_BOOL,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BVEC2,TYPE_BOOL,TYPE_BOOL,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BOOL,TYPE_BOOL,TYPE_BVEC2,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BOOL,TYPE_BVEC3,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BVEC3,TYPE_BOOL,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BVEC2,TYPE_BVEC2,TYPE_VOID}}, {"float",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_FLOAT,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_FLOAT,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_FLOAT,TYPE_VEC2,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_VEC2,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VEC2,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_FLOAT,TYPE_VEC3,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"int",TYPE_INT,{TYPE_INT,TYPE_VOID}}, {"ivec2",TYPE_IVEC2,{TYPE_INT,TYPE_VOID}}, {"ivec2",TYPE_IVEC2,{TYPE_INT,TYPE_INT,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_INT,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_INT,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_IVEC2,TYPE_INT,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_INT,TYPE_IVEC2,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_INT,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_INT,TYPE_INT,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_INT,TYPE_IVEC2,TYPE_INT,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_IVEC2,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_INT,TYPE_INT,TYPE_IVEC2,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_INT,TYPE_IVEC3,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_IVEC3,TYPE_INT,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"uint",TYPE_UINT,{TYPE_UINT,TYPE_VOID}}, {"uvec2",TYPE_UVEC2,{TYPE_UINT,TYPE_VOID}}, {"uvec2",TYPE_UVEC2,{TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_UINT,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_UINT,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_UVEC2,TYPE_UINT,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_UINT,TYPE_UVEC2,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UINT,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UINT,TYPE_UINT,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UINT,TYPE_UVEC2,TYPE_UINT,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UVEC2,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UINT,TYPE_UINT,TYPE_UVEC2,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UINT,TYPE_UVEC3,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UVEC3,TYPE_UINT,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"mat2",TYPE_MAT2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"mat3",TYPE_MAT3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"mat4",TYPE_MAT4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"mat2",TYPE_MAT2,{TYPE_FLOAT,TYPE_VOID}}, {"mat3",TYPE_MAT3,{TYPE_FLOAT,TYPE_VOID}}, {"mat4",TYPE_MAT4,{TYPE_FLOAT,TYPE_VOID}}, //conversion scalars {"int",TYPE_INT,{TYPE_BOOL,TYPE_VOID}}, {"int",TYPE_INT,{TYPE_INT,TYPE_VOID}}, {"int",TYPE_INT,{TYPE_UINT,TYPE_VOID}}, {"int",TYPE_INT,{TYPE_FLOAT,TYPE_VOID}}, {"float",TYPE_FLOAT,{TYPE_BOOL,TYPE_VOID}}, {"float",TYPE_FLOAT,{TYPE_INT,TYPE_VOID}}, {"float",TYPE_FLOAT,{TYPE_UINT,TYPE_VOID}}, {"float",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"uint",TYPE_UINT,{TYPE_BOOL,TYPE_VOID}}, {"uint",TYPE_UINT,{TYPE_INT,TYPE_VOID}}, {"uint",TYPE_UINT,{TYPE_UINT,TYPE_VOID}}, {"uint",TYPE_UINT,{TYPE_FLOAT,TYPE_VOID}}, {"bool",TYPE_BOOL,{TYPE_BOOL,TYPE_VOID}}, {"bool",TYPE_BOOL,{TYPE_INT,TYPE_VOID}}, {"bool",TYPE_BOOL,{TYPE_UINT,TYPE_VOID}}, {"bool",TYPE_BOOL,{TYPE_FLOAT,TYPE_VOID}}, //conversion vectors {"ivec2",TYPE_IVEC2,{TYPE_BVEC2,TYPE_VOID}}, {"ivec2",TYPE_IVEC2,{TYPE_IVEC2,TYPE_VOID}}, {"ivec2",TYPE_IVEC2,{TYPE_UVEC2,TYPE_VOID}}, {"ivec2",TYPE_IVEC2,{TYPE_VEC2,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_BVEC2,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_IVEC2,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_UVEC2,TYPE_VOID}}, {"vec2",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"uvec2",TYPE_UVEC2,{TYPE_BVEC2,TYPE_VOID}}, {"uvec2",TYPE_UVEC2,{TYPE_IVEC2,TYPE_VOID}}, {"uvec2",TYPE_UVEC2,{TYPE_UVEC2,TYPE_VOID}}, {"uvec2",TYPE_UVEC2,{TYPE_VEC2,TYPE_VOID}}, {"bvec2",TYPE_BVEC2,{TYPE_BVEC2,TYPE_VOID}}, {"bvec2",TYPE_BVEC2,{TYPE_IVEC2,TYPE_VOID}}, {"bvec2",TYPE_BVEC2,{TYPE_UVEC2,TYPE_VOID}}, {"bvec2",TYPE_BVEC2,{TYPE_VEC2,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_BVEC3,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_IVEC3,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_UVEC3,TYPE_VOID}}, {"ivec3",TYPE_IVEC3,{TYPE_VEC3,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_BVEC3,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_IVEC3,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_UVEC3,TYPE_VOID}}, {"vec3",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_BVEC3,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_IVEC3,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_UVEC3,TYPE_VOID}}, {"uvec3",TYPE_UVEC3,{TYPE_VEC3,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_BVEC3,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_IVEC3,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_UVEC3,TYPE_VOID}}, {"bvec3",TYPE_BVEC3,{TYPE_VEC3,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_BVEC4,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_IVEC4,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_UVEC4,TYPE_VOID}}, {"ivec4",TYPE_IVEC4,{TYPE_VEC4,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_BVEC4,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_IVEC4,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_UVEC4,TYPE_VOID}}, {"vec4",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_BVEC4,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_IVEC4,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_UVEC4,TYPE_VOID}}, {"uvec4",TYPE_UVEC4,{TYPE_VEC4,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_BVEC4,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_IVEC4,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_UVEC4,TYPE_VOID}}, {"bvec4",TYPE_BVEC4,{TYPE_VEC4,TYPE_VOID}}, //builtins - trigonometry {"sin",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"cos",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"tan",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"asin",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"acos",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"atan",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"atan2",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"sinh",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"cosh",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"tanh",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, //builtins - exponential {"pow",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"pow",TYPE_VEC2,{TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"pow",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"pow",TYPE_VEC3,{TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"pow",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"pow",TYPE_VEC4,{TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"pow",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"exp",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"exp",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"exp",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"exp",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"log",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"log",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"log",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"log",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"sqrt",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"sqrt",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"sqrt",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"sqrt",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, //builtins - common {"abs",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"abs",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"abs",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"abs",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"abs",TYPE_INT,{TYPE_INT,TYPE_VOID}}, {"abs",TYPE_IVEC2,{TYPE_IVEC2,TYPE_VOID}}, {"abs",TYPE_IVEC3,{TYPE_IVEC3,TYPE_VOID}}, {"abs",TYPE_IVEC4,{TYPE_IVEC4,TYPE_VOID}}, {"abs",TYPE_UINT,{TYPE_UINT,TYPE_VOID}}, {"abs",TYPE_UVEC2,{TYPE_UVEC2,TYPE_VOID}}, {"abs",TYPE_UVEC3,{TYPE_UVEC3,TYPE_VOID}}, {"abs",TYPE_UVEC4,{TYPE_UVEC4,TYPE_VOID}}, {"sign",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"sign",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"sign",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"sign",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"sign",TYPE_INT,{TYPE_INT,TYPE_VOID}}, {"sign",TYPE_IVEC2,{TYPE_IVEC2,TYPE_VOID}}, {"sign",TYPE_IVEC3,{TYPE_IVEC3,TYPE_VOID}}, {"sign",TYPE_IVEC4,{TYPE_IVEC4,TYPE_VOID}}, {"floor",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"floor",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"floor",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"floor",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"trunc",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"trunc",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"trunc",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"trunc",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"round",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"round",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"round",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"round",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"ceil",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"ceil",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"ceil",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"ceil",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"fract",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"fract",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"fract",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"fract",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"mod",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"mod",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"mod",TYPE_VEC2,{TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"mod",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"mod",TYPE_VEC3,{TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"mod",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"mod",TYPE_VEC4,{TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"modf",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"modf",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"modf",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"modf",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"min",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"min",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"min",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"min",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"min",TYPE_INT,{TYPE_INT,TYPE_INT,TYPE_VOID}}, {"min",TYPE_IVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"min",TYPE_IVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"min",TYPE_IVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"min",TYPE_UINT,{TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"min",TYPE_UVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"min",TYPE_UVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"min",TYPE_UVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"max",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"max",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"max",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"max",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"max",TYPE_INT,{TYPE_INT,TYPE_INT,TYPE_VOID}}, {"max",TYPE_IVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"max",TYPE_IVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"max",TYPE_IVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"max",TYPE_UINT,{TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"max",TYPE_UVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"max",TYPE_UVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"max",TYPE_UVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"clamp",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"clamp",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"clamp",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"clamp",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"clamp",TYPE_VEC2,{TYPE_VEC2,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"clamp",TYPE_VEC3,{TYPE_VEC3,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"clamp",TYPE_VEC4,{TYPE_VEC4,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"clamp",TYPE_INT,{TYPE_INT,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"clamp",TYPE_IVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"clamp",TYPE_IVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"clamp",TYPE_IVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"clamp",TYPE_IVEC2,{TYPE_IVEC2,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"clamp",TYPE_IVEC3,{TYPE_IVEC3,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"clamp",TYPE_IVEC4,{TYPE_IVEC4,TYPE_INT,TYPE_INT,TYPE_VOID}}, {"clamp",TYPE_UINT,{TYPE_UINT,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"clamp",TYPE_UVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"clamp",TYPE_UVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"clamp",TYPE_UVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"clamp",TYPE_UVEC2,{TYPE_UVEC2,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"clamp",TYPE_UVEC3,{TYPE_UVEC3,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"clamp",TYPE_UVEC4,{TYPE_UVEC4,TYPE_UINT,TYPE_UINT,TYPE_VOID}}, {"mix",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"mix",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_BOOL,TYPE_VOID}}, {"mix",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"mix",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_BOOL,TYPE_VOID}}, {"mix",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_BVEC2,TYPE_VOID}}, {"mix",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"mix",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"mix",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_BOOL,TYPE_VOID}}, {"mix",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_BVEC3,TYPE_VOID}}, {"mix",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"mix",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"mix",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_BOOL,TYPE_VOID}}, {"mix",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_BVEC3,TYPE_VOID}}, {"mix",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"step",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"step",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"step",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"step",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"step",TYPE_VEC2,{TYPE_FLOAT,TYPE_VEC2,TYPE_VOID}}, {"step",TYPE_VEC3,{TYPE_FLOAT,TYPE_VEC3,TYPE_VOID}}, {"step",TYPE_VEC4,{TYPE_FLOAT,TYPE_VEC4,TYPE_VOID}}, {"smoothstep",TYPE_FLOAT,{TYPE_FLOAT,TYPE_FLOAT,TYPE_FLOAT,TYPE_VOID}}, {"smoothstep",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"smoothstep",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"smoothstep",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"smoothstep",TYPE_VEC2,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VEC2,TYPE_VOID}}, {"smoothstep",TYPE_VEC3,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VEC3,TYPE_VOID}}, {"smoothstep",TYPE_VEC4,{TYPE_FLOAT,TYPE_FLOAT,TYPE_VEC4,TYPE_VOID}}, {"isnan",TYPE_BOOL,{TYPE_FLOAT,TYPE_VOID}}, {"isnan",TYPE_BOOL,{TYPE_VEC2,TYPE_VOID}}, {"isnan",TYPE_BOOL,{TYPE_VEC3,TYPE_VOID}}, {"isnan",TYPE_BOOL,{TYPE_VEC4,TYPE_VOID}}, {"isinf",TYPE_BOOL,{TYPE_FLOAT,TYPE_VOID}}, {"isinf",TYPE_BOOL,{TYPE_VEC2,TYPE_VOID}}, {"isinf",TYPE_BOOL,{TYPE_VEC3,TYPE_VOID}}, {"isinf",TYPE_BOOL,{TYPE_VEC4,TYPE_VOID}}, {"floatBitsToInt",TYPE_INT,{TYPE_FLOAT,TYPE_VOID}}, {"floatBitsToInt",TYPE_IVEC2,{TYPE_VEC2,TYPE_VOID}}, {"floatBitsToInt",TYPE_IVEC3,{TYPE_VEC3,TYPE_VOID}}, {"floatBitsToInt",TYPE_IVEC4,{TYPE_VEC4,TYPE_VOID}}, {"floatBitsToUInt",TYPE_UINT,{TYPE_FLOAT,TYPE_VOID}}, {"floatBitsToUInt",TYPE_UVEC2,{TYPE_VEC2,TYPE_VOID}}, {"floatBitsToUInt",TYPE_UVEC3,{TYPE_VEC3,TYPE_VOID}}, {"floatBitsToUInt",TYPE_UVEC4,{TYPE_VEC4,TYPE_VOID}}, {"intBitsToFloat",TYPE_FLOAT,{TYPE_INT,TYPE_VOID}}, {"intBitsToFloat",TYPE_VEC2,{TYPE_IVEC2,TYPE_VOID}}, {"intBitsToFloat",TYPE_VEC3,{TYPE_IVEC3,TYPE_VOID}}, {"intBitsToFloat",TYPE_VEC4,{TYPE_IVEC4,TYPE_VOID}}, {"uintBitsToFloat",TYPE_FLOAT,{TYPE_UINT,TYPE_VOID}}, {"uintBitsToFloat",TYPE_VEC2,{TYPE_UVEC2,TYPE_VOID}}, {"uintBitsToFloat",TYPE_VEC3,{TYPE_UVEC3,TYPE_VOID}}, {"uintBitsToFloat",TYPE_VEC4,{TYPE_UVEC4,TYPE_VOID}}, //builtins - geometric {"length",TYPE_FLOAT,{TYPE_VEC2,TYPE_VOID}}, {"length",TYPE_FLOAT,{TYPE_VEC3,TYPE_VOID}}, {"length",TYPE_FLOAT,{TYPE_VEC4,TYPE_VOID}}, {"distance",TYPE_FLOAT,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"distance",TYPE_FLOAT,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"distance",TYPE_FLOAT,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"dot",TYPE_FLOAT,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"dot",TYPE_FLOAT,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"dot",TYPE_FLOAT,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"cross",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"normalize",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"normalize",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"normalize",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"reflect",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"refract",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"facefordward",TYPE_VEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"facefordward",TYPE_VEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"facefordward",TYPE_VEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"matrixCompMult",TYPE_MAT2,{TYPE_MAT2,TYPE_MAT2,TYPE_VOID}}, {"matrixCompMult",TYPE_MAT3,{TYPE_MAT3,TYPE_MAT3,TYPE_VOID}}, {"matrixCompMult",TYPE_MAT4,{TYPE_MAT4,TYPE_MAT4,TYPE_VOID}}, {"outerProduct",TYPE_MAT2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"outerProduct",TYPE_MAT3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"outerProduct",TYPE_MAT4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"transpose",TYPE_MAT2,{TYPE_MAT2,TYPE_VOID}}, {"transpose",TYPE_MAT3,{TYPE_MAT3,TYPE_VOID}}, {"transpose",TYPE_MAT4,{TYPE_MAT4,TYPE_VOID}}, {"determinant",TYPE_FLOAT,{TYPE_MAT2,TYPE_VOID}}, {"determinant",TYPE_FLOAT,{TYPE_MAT3,TYPE_VOID}}, {"determinant",TYPE_FLOAT,{TYPE_MAT4,TYPE_VOID}}, {"inverse",TYPE_MAT2,{TYPE_MAT2,TYPE_VOID}}, {"inverse",TYPE_MAT3,{TYPE_MAT3,TYPE_VOID}}, {"inverse",TYPE_MAT4,{TYPE_MAT4,TYPE_VOID}}, {"lessThan",TYPE_BVEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"lessThan",TYPE_BVEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"lessThan",TYPE_BVEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"lessThan",TYPE_BVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"lessThan",TYPE_BVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"lessThan",TYPE_BVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"lessThan",TYPE_BVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"lessThan",TYPE_BVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"lessThan",TYPE_BVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"greaterThan",TYPE_BVEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"greaterThan",TYPE_BVEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"greaterThan",TYPE_BVEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"greaterThan",TYPE_BVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"greaterThan",TYPE_BVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"greaterThan",TYPE_BVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"greaterThan",TYPE_BVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"greaterThan",TYPE_BVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"greaterThan",TYPE_BVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"lessThanEqual",TYPE_BVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"greaterThanEqual",TYPE_BVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"equal",TYPE_BVEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"equal",TYPE_BVEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"equal",TYPE_BVEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"equal",TYPE_BVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"equal",TYPE_BVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"equal",TYPE_BVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"equal",TYPE_BVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"equal",TYPE_BVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"equal",TYPE_BVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"equal",TYPE_BVEC2,{TYPE_BVEC2,TYPE_BVEC2,TYPE_VOID}}, {"equal",TYPE_BVEC3,{TYPE_BVEC3,TYPE_BVEC3,TYPE_VOID}}, {"equal",TYPE_BVEC4,{TYPE_BVEC4,TYPE_BVEC4,TYPE_VOID}}, {"notEqual",TYPE_BVEC2,{TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"notEqual",TYPE_BVEC3,{TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"notEqual",TYPE_BVEC4,{TYPE_VEC4,TYPE_VEC4,TYPE_VOID}}, {"notEqual",TYPE_BVEC2,{TYPE_IVEC2,TYPE_IVEC2,TYPE_VOID}}, {"notEqual",TYPE_BVEC3,{TYPE_IVEC3,TYPE_IVEC3,TYPE_VOID}}, {"notEqual",TYPE_BVEC4,{TYPE_IVEC4,TYPE_IVEC4,TYPE_VOID}}, {"notEqual",TYPE_BVEC2,{TYPE_UVEC2,TYPE_UVEC2,TYPE_VOID}}, {"notEqual",TYPE_BVEC3,{TYPE_UVEC3,TYPE_UVEC3,TYPE_VOID}}, {"notEqual",TYPE_BVEC4,{TYPE_UVEC4,TYPE_UVEC4,TYPE_VOID}}, {"notEqual",TYPE_BVEC2,{TYPE_BVEC2,TYPE_BVEC2,TYPE_VOID}}, {"notEqual",TYPE_BVEC3,{TYPE_BVEC3,TYPE_BVEC3,TYPE_VOID}}, {"notEqual",TYPE_BVEC4,{TYPE_BVEC4,TYPE_BVEC4,TYPE_VOID}}, {"any",TYPE_BOOL,{TYPE_BVEC2,TYPE_VOID}}, {"any",TYPE_BOOL,{TYPE_BVEC3,TYPE_VOID}}, {"any",TYPE_BOOL,{TYPE_BVEC4,TYPE_VOID}}, {"all",TYPE_BOOL,{TYPE_BVEC2,TYPE_VOID}}, {"all",TYPE_BOOL,{TYPE_BVEC3,TYPE_VOID}}, {"all",TYPE_BOOL,{TYPE_BVEC4,TYPE_VOID}}, {"not",TYPE_BOOL,{TYPE_BVEC2,TYPE_VOID}}, {"not",TYPE_BOOL,{TYPE_BVEC3,TYPE_VOID}}, {"not",TYPE_BOOL,{TYPE_BVEC4,TYPE_VOID}}, //builtins - texture {"textureSize",TYPE_VEC2,{TYPE_SAMPLER2D,TYPE_INT,TYPE_VOID}}, {"textureSize",TYPE_VEC2,{TYPE_ISAMPLER2D,TYPE_INT,TYPE_VOID}}, {"textureSize",TYPE_VEC2,{TYPE_USAMPLER2D,TYPE_INT,TYPE_VOID}}, {"textureSize",TYPE_VEC2,{TYPE_SAMPLERCUBE,TYPE_INT,TYPE_VOID}}, {"texture",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC2,TYPE_VOID}}, {"texture",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC3,TYPE_VOID}}, {"texture",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"texture",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"texture",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC2,TYPE_VOID}}, {"texture",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC3,TYPE_VOID}}, {"texture",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"texture",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"texture",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC2,TYPE_VOID}}, {"texture",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC3,TYPE_VOID}}, {"texture",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC2,TYPE_FLOAT,TYPE_VOID}}, {"texture",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"texture",TYPE_VEC4,{TYPE_SAMPLERCUBE,TYPE_VEC3,TYPE_VOID}}, {"texture",TYPE_VEC4,{TYPE_SAMPLERCUBE,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProj",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC3,TYPE_VOID}}, {"textureProj",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC4,TYPE_VOID}}, {"textureProj",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProj",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"textureProj",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC3,TYPE_VOID}}, {"textureProj",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC4,TYPE_VOID}}, {"textureProj",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProj",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"textureProj",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC3,TYPE_VOID}}, {"textureProj",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC4,TYPE_VOID}}, {"textureProj",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProj",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"textureLod",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureLod",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureLod",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureLod",TYPE_VEC4,{TYPE_SAMPLERCUBE,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"texelFetch",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_IVEC2,TYPE_INT,TYPE_VOID}}, {"texelFetch",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_IVEC2,TYPE_INT,TYPE_VOID}}, {"texelFetch",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_IVEC2,TYPE_INT,TYPE_VOID}}, {"textureProjLod",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProjLod",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"textureProjLod",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProjLod",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"textureProjLod",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC3,TYPE_FLOAT,TYPE_VOID}}, {"textureProjLod",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC4,TYPE_FLOAT,TYPE_VOID}}, {"textureGrad",TYPE_VEC4,{TYPE_SAMPLER2D,TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"textureGrad",TYPE_IVEC4,{TYPE_ISAMPLER2D,TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"textureGrad",TYPE_UVEC4,{TYPE_USAMPLER2D,TYPE_VEC2,TYPE_VEC2,TYPE_VEC2,TYPE_VOID}}, {"textureGrad",TYPE_VEC4,{TYPE_SAMPLERCUBE,TYPE_VEC3,TYPE_VEC3,TYPE_VEC3,TYPE_VOID}}, {"textureScreen",TYPE_VEC4,{TYPE_VEC2,TYPE_VOID}}, {"dFdx",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"dFdx",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"dFdx",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"dFdx",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"dFdy",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"dFdy",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"dFdy",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"dFdy",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {"fwidth",TYPE_FLOAT,{TYPE_FLOAT,TYPE_VOID}}, {"fwidth",TYPE_VEC2,{TYPE_VEC2,TYPE_VOID}}, {"fwidth",TYPE_VEC3,{TYPE_VEC3,TYPE_VOID}}, {"fwidth",TYPE_VEC4,{TYPE_VEC4,TYPE_VOID}}, {NULL,TYPE_VOID,{TYPE_VOID}} }; bool ShaderLanguage::_validate_function_call(BlockNode* p_block, OperatorNode *p_func,DataType *r_ret_type) { ERR_FAIL_COND_V(p_func->op!=OP_CALL && p_func->op!=OP_CONSTRUCT,NULL); Vector args; ERR_FAIL_COND_V( p_func->arguments[0]->type!=Node::TYPE_VARIABLE, NULL ); StringName name = static_cast(p_func->arguments[0])->name.operator String(); bool all_const=true; for(int i=1;iarguments.size();i++) { if (p_func->arguments[i]->type!=Node::TYPE_CONSTANT) all_const=false; args.push_back(p_func->arguments[i]->get_datatype()); } int argcount=args.size(); bool failed_builtin=false; if (argcount<=4) { // test builtins int idx=0; while (builtin_func_defs[idx].name) { if (name==builtin_func_defs[idx].name) { failed_builtin=true; bool fail=false; for(int i=0;iarguments[i+1]->type==Node::TYPE_CONSTANT && convert_constant(static_cast(p_func->arguments[i+1]),builtin_func_defs[idx].args[i])) { //all good } else if (args[i]!=builtin_func_defs[idx].args[i]) { fail=true; break; } } if (!fail && argcount<4 && builtin_func_defs[idx].args[argcount]!=TYPE_VOID) fail=true; //make sure the number of arguments matches if (!fail) { if (r_ret_type) *r_ret_type=builtin_func_defs[idx].rettype; return true; } } idx++; } } if (failed_builtin) { String err ="Invalid arguments for built-in function: "+String(name)+"("; for(int i=0;i0) err+=","; if (p_func->arguments[i+1]->type==Node::TYPE_CONSTANT && p_func->arguments[i+1]->get_datatype()==TYPE_INT && static_cast(p_func->arguments[i+1])->values[0].sint<0) { err+="-"; } err+=get_datatype_name(args[i]); } err+=")"; _set_error(err); return false; } #if 0 if (found_builtin) { if (p_func->op==OP_CONSTRUCT && all_const) { Vector cdata; for(int i=0;i(p_func->arguments[i+1])->value; switch(v.get_type()) { case Variant::REAL: cdata.push_back(v); break; case Variant::INT: cdata.push_back(v); break; case Variant::VECTOR2: { Vector2 v2=v; cdata.push_back(v2.x); cdata.push_back(v2.y); } break; case Variant::VECTOR3: { Vector3 v3=v; cdata.push_back(v3.x); cdata.push_back(v3.y); cdata.push_back(v3.z);} break; case Variant::PLANE: { Plane v4=v; cdata.push_back(v4.normal.x); cdata.push_back(v4.normal.y); cdata.push_back(v4.normal.z); cdata.push_back(v4.d); } break; default: ERR_FAIL_V(NULL); } } ConstantNode *cn = parser.create_node(p_func->parent); Variant data; switch(p_func->return_cache) { case TYPE_FLOAT: data = cdata[0]; break; case TYPE_VEC2: if (cdata.size()==1) data = Vector2(cdata[0],cdata[0]); else data = Vector2(cdata[0],cdata[1]); break; case TYPE_VEC3: if (cdata.size()==1) data = Vector3(cdata[0],cdata[0],cdata[0]); else data = Vector3(cdata[0],cdata[1],cdata[2]); break; case TYPE_VEC4: if (cdata.size()==1) data = Plane(cdata[0],cdata[0],cdata[0],cdata[0]); else data = Plane(cdata[0],cdata[1],cdata[2],cdata[3]); break; } cn->datatype=p_func->return_cache; cn->value=data; return cn; } return p_func; } #endif // try existing functions.. StringName exclude_function; BlockNode *block = p_block; while(block) { if (block->parent_function) { exclude_function=block->parent_function->name; } block=block->parent_block; } if (name==exclude_function) { _set_error("Recursion is not allowed"); return false; } for(int i=0;ifunctions.size();i++) { if (name != shader->functions[i].name) continue; if (!shader->functions[i].callable) { _set_error("Function '"+String(name)+" can't be called from source code."); return false; } FunctionNode *pfunc = shader->functions[i].function; if (pfunc->arguments.size()!=args.size()) continue; bool fail=false; for(int i=0;iarguments[i+1]->type==Node::TYPE_CONSTANT && convert_constant(static_cast(p_func->arguments[i+1]),pfunc->arguments[i].type)) { //all good } else if (args[i]!=pfunc->arguments[i].type) { fail=true; break; } } if (!fail) { p_func->return_cache=pfunc->return_type; return true; } } return false; } bool ShaderLanguage::_parse_function_arguments(BlockNode* p_block,const Map &p_builtin_types,OperatorNode* p_func,int *r_complete_arg) { TkPos pos = _get_tkpos(); Token tk = _get_token(); if (tk.type==TK_PARENTHESIS_CLOSE) { return true; } _set_tkpos(pos);; while(true) { if (r_complete_arg) { pos = _get_tkpos(); tk = _get_token(); if (tk.type==TK_CURSOR) { *r_complete_arg=p_func->arguments.size()-1; } else { _set_tkpos(pos); } } Node *arg= _parse_and_reduce_expression(p_block,p_builtin_types); if (!arg) { return false; } p_func->arguments.push_back(arg); tk = _get_token(); if (tk.type==TK_PARENTHESIS_CLOSE) { return true; } else if (tk.type!=TK_COMMA) { // something is broken _set_error("Expected ',' or ')' after argument"); return false; } } return true; } bool ShaderLanguage::is_token_operator(TokenType p_type) { return (p_type==TK_OP_EQUAL || p_type==TK_OP_NOT_EQUAL || p_type==TK_OP_LESS || p_type==TK_OP_LESS_EQUAL || p_type==TK_OP_GREATER || p_type==TK_OP_GREATER_EQUAL || p_type==TK_OP_AND || p_type==TK_OP_OR || p_type==TK_OP_NOT || p_type==TK_OP_ADD || p_type==TK_OP_SUB || p_type==TK_OP_MUL || p_type==TK_OP_DIV || p_type==TK_OP_MOD || p_type==TK_OP_SHIFT_LEFT || p_type==TK_OP_SHIFT_RIGHT || p_type==TK_OP_ASSIGN || p_type==TK_OP_ASSIGN_ADD || p_type==TK_OP_ASSIGN_SUB || p_type==TK_OP_ASSIGN_MUL || p_type==TK_OP_ASSIGN_DIV || p_type==TK_OP_ASSIGN_MOD || p_type==TK_OP_ASSIGN_SHIFT_LEFT || p_type==TK_OP_ASSIGN_SHIFT_RIGHT || p_type==TK_OP_ASSIGN_BIT_AND || p_type==TK_OP_ASSIGN_BIT_OR || p_type==TK_OP_ASSIGN_BIT_XOR || p_type==TK_OP_BIT_AND || p_type==TK_OP_BIT_OR || p_type==TK_OP_BIT_XOR || p_type==TK_OP_BIT_INVERT || p_type==TK_OP_INCREMENT || p_type==TK_OP_DECREMENT || p_type==TK_QUESTION || p_type==TK_COLON ); } bool ShaderLanguage::convert_constant(ConstantNode* p_constant, DataType p_to_type,ConstantNode::Value *p_value) { if (p_constant->datatype==p_to_type) { if (p_value) { for(int i=0;ivalues.size();i++) { p_value[i]=p_constant->values[i]; } } return true; } else if (p_constant->datatype==TYPE_INT && p_to_type==TYPE_FLOAT) { if (p_value) { p_value->real=p_constant->values[0].sint; } return true; } else if (p_constant->datatype==TYPE_UINT && p_to_type==TYPE_FLOAT) { if (p_value) { p_value->real=p_constant->values[0].uint; } return true; } else if (p_constant->datatype==TYPE_INT && p_to_type==TYPE_UINT) { if (p_constant->values[0].sint<0) { return false; } if (p_value) { p_value->uint=p_constant->values[0].sint; } return true; } else if (p_constant->datatype==TYPE_UINT && p_to_type==TYPE_INT) { if (p_constant->values[0].uint>0x7FFFFFFF) { return false; } if (p_value) { p_value->sint=p_constant->values[0].uint; } return true; } else return false; } bool ShaderLanguage::is_scalar_type(DataType p_type) { return p_type==TYPE_BOOL || p_type==TYPE_INT || p_type==TYPE_UINT || p_type==TYPE_FLOAT; } void ShaderLanguage::get_keyword_list(List *r_keywords) { Set kws; int idx=0; while(keyword_list[idx].text) { kws.insert(keyword_list[idx].text); idx++; } idx=0; while (builtin_func_defs[idx].name) { kws.insert(builtin_func_defs[idx].name); idx++; } for(Set::Element *E=kws.front();E;E=E->next()) { r_keywords->push_back(E->get()); } } ShaderLanguage::DataType ShaderLanguage::get_scalar_type(DataType p_type) { static const DataType scalar_types[]={ TYPE_VOID, TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_BOOL, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_UINT, TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT, TYPE_INT, TYPE_UINT, TYPE_FLOAT, }; return scalar_types[p_type]; } bool ShaderLanguage::_get_completable_identifier(BlockNode *p_block,CompletionType p_type,StringName& identifier) { identifier=StringName(); TkPos pos; Token tk = _get_token(); if (tk.type==TK_IDENTIFIER) { identifier=tk.text; pos = _get_tkpos(); tk = _get_token(); } if (tk.type==TK_CURSOR) { completion_type=p_type; completion_line=tk_line; completion_block=p_block; pos = _get_tkpos(); tk = _get_token(); if (tk.type==TK_IDENTIFIER) { identifier=identifier.operator String() + tk.text.operator String(); } else { _set_tkpos(pos); } return true; } else if (identifier!=StringName()){ _set_tkpos(pos); } return false; } ShaderLanguage::Node* ShaderLanguage::_parse_expression(BlockNode* p_block,const Map &p_builtin_types) { Vector expression; //Vector operators; while(true) { Node *expr=NULL; TkPos prepos = _get_tkpos(); Token tk = _get_token(); TkPos pos = _get_tkpos(); print_line("in expr: "+get_token_text(tk)); if (tk.type==TK_PARENTHESIS_OPEN) { //handle subexpression expr = _parse_and_reduce_expression(p_block,p_builtin_types); if (!expr) return NULL; tk = _get_token(); if (tk.type!=TK_PARENTHESIS_CLOSE) { _set_error("Expected ')' in expression"); return NULL; } } else if (tk.type==TK_REAL_CONSTANT) { ConstantNode *constant = alloc_node(); ConstantNode::Value v; v.real=tk.constant; constant->values.push_back(v); constant->datatype=TYPE_FLOAT; expr=constant; } else if (tk.type==TK_INT_CONSTANT) { ConstantNode *constant = alloc_node(); ConstantNode::Value v; v.sint=tk.constant; constant->values.push_back(v); constant->datatype=TYPE_INT; expr=constant; } else if (tk.type==TK_TRUE) { //print_line("found true"); //handle true constant ConstantNode *constant = alloc_node(); ConstantNode::Value v; v.boolean=true; constant->values.push_back(v); constant->datatype=TYPE_BOOL; expr=constant; } else if (tk.type==TK_FALSE) { //handle false constant ConstantNode *constant = alloc_node(); ConstantNode::Value v; v.boolean=false; constant->values.push_back(v); constant->datatype=TYPE_BOOL; expr=constant; } else if (tk.type==TK_TYPE_VOID) { //make sure void is not used in expression _set_error("Void value not allowed in Expression"); return NULL; } else if (is_token_nonvoid_datatype(tk.type)) { //basic type constructor print_line("parse constructor"); OperatorNode *func = alloc_node(); func->op=OP_CONSTRUCT; if (is_token_precision(tk.type)) { func->return_precision_cache=get_token_precision(tk.type); tk=_get_token(); } VariableNode *funcname = alloc_node(); funcname->name=get_datatype_name(get_token_datatype(tk.type)); func->arguments.push_back(funcname); tk=_get_token(); if (tk.type!=TK_PARENTHESIS_OPEN) { _set_error("Expected '(' after type name"); return NULL; } int carg=-1; bool ok = _parse_function_arguments(p_block,p_builtin_types,func,&carg); if (carg>=0) { completion_type=COMPLETION_CALL_ARGUMENTS; completion_line=tk_line; completion_block=p_block; completion_function=funcname->name; completion_argument=carg; } if (!ok) return NULL; if (!_validate_function_call(p_block,func,&func->return_cache)) { _set_error("No matching constructor found for: '"+String(funcname->name)+"'"); return NULL; } //validate_Function_call() expr=_reduce_expression(p_block,func); } else if (tk.type==TK_IDENTIFIER) { _set_tkpos(prepos); StringName identifier; _get_completable_identifier(p_block,COMPLETION_IDENTIFIER,identifier); tk=_get_token(); if (tk.type==TK_PARENTHESIS_OPEN) { //a function StringName name = identifier; OperatorNode *func = alloc_node(); func->op=OP_CALL; VariableNode *funcname = alloc_node(); funcname->name=name; func->arguments.push_back(funcname); int carg=-1; bool ok =_parse_function_arguments(p_block,p_builtin_types,func,&carg); if (carg>=0) { completion_type=COMPLETION_CALL_ARGUMENTS; completion_line=tk_line; completion_block=p_block; completion_function=funcname->name; completion_argument=carg; } if (!ok) return NULL; if (!_validate_function_call(p_block,func,&func->return_cache)) { _set_error("No matching function found for: '"+String(funcname->name)+"'"); return NULL; } expr=func; } else { //an identifier _set_tkpos(pos); DataType data_type; IdentifierType ident_type; if (!_find_identifier(p_block,p_builtin_types,identifier,&data_type,&ident_type)) { _set_error("Unknown identifier in expression: "+String(identifier)); return NULL; } if (ident_type==IDENTIFIER_FUNCTION) { _set_error("Can't use function as identifier: "+String(identifier)); return NULL; } VariableNode *varname = alloc_node(); varname->name=identifier; varname->datatype_cache=data_type; expr=varname; } } else if (tk.type==TK_OP_ADD) { continue; //this one does nothing } else if (tk.type==TK_OP_SUB || tk.type==TK_OP_NOT || tk.type==TK_OP_BIT_INVERT || tk.type==TK_OP_INCREMENT || tk.type==TK_OP_DECREMENT) { Expression e; e.is_op=true; switch(tk.type) { case TK_OP_SUB: e.op=OP_NEGATE; break; case TK_OP_NOT: e.op=OP_NOT; break; case TK_OP_BIT_INVERT: e.op=OP_BIT_INVERT; break; case TK_OP_INCREMENT: e.op=OP_INCREMENT; break; case TK_OP_DECREMENT: e.op=OP_DECREMENT; break; default: ERR_FAIL_V(NULL); } expression.push_back(e); continue; } else { _set_error("Expected expression, found: "+get_token_text(tk)); return NULL; //nothing } ERR_FAIL_COND_V(!expr,NULL); /* OK now see what's NEXT to the operator.. */ /* OK now see what's NEXT to the operator.. */ /* OK now see what's NEXT to the operator.. */ while(true) { TkPos pos = _get_tkpos(); tk=_get_token(); if (tk.type==TK_PERIOD) { StringName identifier; if (_get_completable_identifier(p_block,COMPLETION_INDEX,identifier)) { completion_base=expr->get_datatype(); } if (identifier==StringName()) { _set_error("Expected identifier as member"); return NULL; } DataType dt = expr->get_datatype(); String ident = identifier; bool ok=true; DataType member_type; switch(dt) { case TYPE_BVEC2: case TYPE_IVEC2: case TYPE_UVEC2: case TYPE_VEC2: { int l = ident.length(); if (l==1) { member_type=DataType(dt-1); } else if (l==2) { member_type=dt; } else { ok=false; break; } const CharType *c=ident.ptr(); for(int i=0;i(); mn->basetype=dt; mn->datatype=member_type; mn->name=ident; mn->owner=expr; expr=mn; //todo //member (period) has priority over any operator //creates a subindexing expression in place /*} else if (tk.type==TK_BRACKET_OPEN) { //todo //subindexing has priority over any operator //creates a subindexing expression in place */ } else if (tk.type==TK_OP_INCREMENT || tk.type==TK_OP_DECREMENT) { OperatorNode *op = alloc_node(); op->op=tk.type==TK_OP_DECREMENT ? OP_POST_DECREMENT : OP_POST_INCREMENT; op->arguments.push_back(expr); if (!_validate_operator(op,&op->return_cache)) { _set_error("Invalid base type for increment/decrement operator"); return NULL; } expr=op; } else { _set_tkpos(pos); break; } } Expression e; e.is_op=false; e.node=expr; expression.push_back(e); pos = _get_tkpos(); tk = _get_token(); if (is_token_operator(tk.type)) { Expression o; o.is_op=true; switch(tk.type) { case TK_OP_EQUAL: o.op = OP_EQUAL; break; case TK_OP_NOT_EQUAL: o.op = OP_NOT_EQUAL; break; case TK_OP_LESS: o.op = OP_LESS; break; case TK_OP_LESS_EQUAL: o.op = OP_LESS_EQUAL; break; case TK_OP_GREATER: o.op = OP_GREATER; break; case TK_OP_GREATER_EQUAL: o.op = OP_GREATER_EQUAL; break; case TK_OP_AND: o.op = OP_AND; break; case TK_OP_OR: o.op = OP_OR; break; case TK_OP_ADD: o.op = OP_ADD; break; case TK_OP_SUB: o.op = OP_SUB; break; case TK_OP_MUL: o.op = OP_MUL; break; case TK_OP_DIV: o.op = OP_DIV; break; case TK_OP_MOD: o.op = OP_MOD; break; case TK_OP_SHIFT_LEFT: o.op = OP_SHIFT_LEFT; break; case TK_OP_SHIFT_RIGHT: o.op = OP_SHIFT_RIGHT; break; case TK_OP_ASSIGN: o.op = OP_ASSIGN; break; case TK_OP_ASSIGN_ADD: o.op = OP_ASSIGN_ADD; break; case TK_OP_ASSIGN_SUB: o.op = OP_ASSIGN_SUB; break; case TK_OP_ASSIGN_MUL: o.op = OP_ASSIGN_MUL; break; case TK_OP_ASSIGN_DIV: o.op = OP_ASSIGN_DIV; break; case TK_OP_ASSIGN_MOD: o.op = OP_ASSIGN_MOD; break; case TK_OP_ASSIGN_SHIFT_LEFT: o.op = OP_ASSIGN_SHIFT_LEFT; break; case TK_OP_ASSIGN_SHIFT_RIGHT: o.op = OP_ASSIGN_SHIFT_RIGHT; break; case TK_OP_ASSIGN_BIT_AND: o.op = OP_ASSIGN_BIT_AND; break; case TK_OP_ASSIGN_BIT_OR: o.op = OP_ASSIGN_BIT_OR; break; case TK_OP_ASSIGN_BIT_XOR: o.op = OP_ASSIGN_BIT_XOR; break; case TK_OP_BIT_AND: o.op = OP_BIT_AND; break; case TK_OP_BIT_OR: o.op = OP_BIT_OR ; break; case TK_OP_BIT_XOR: o.op = OP_BIT_XOR; break; case TK_QUESTION: o.op = OP_SELECT_IF; break; case TK_COLON: o.op = OP_SELECT_ELSE; break; default: { _set_error("Invalid token for operator: "+get_token_text(tk)); return NULL; } } expression.push_back(o); } else { _set_tkpos(pos); //something else, so rollback and end break; } } /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */ while(expression.size()>1) { int next_op=-1; int min_priority=0xFFFFF; bool is_unary=false; bool is_ternary=false; for(int i=0;i=next_op;i--) { OperatorNode *op = alloc_node(); op->op=expression[i].op; op->arguments.push_back(expression[i+1].node); expression[i].is_op=false; expression[i].node=op; if (!_validate_operator(op,&op->return_cache)) { String at; for(int i=0;iarguments.size();i++) { if (i>0) at+=" and "; at+=get_datatype_name(op->arguments[i]->get_datatype()); } _set_error("Invalid arguments to unary operator '"+get_operator_text(op->op)+"' :" +at); return NULL; } expression.remove(i+1); } } else if (is_ternary) { if (next_op <1 || next_op>=(expression.size()-1)) { _set_error("Parser bug.."); ERR_FAIL_V(NULL); } if (next_op+2 >= expression.size() || !expression[next_op+2].is_op || expression[next_op+2].op!=OP_SELECT_ELSE) { _set_error("Mising matching ':' for select operator"); return NULL; } OperatorNode *op = alloc_node(); op->op=expression[next_op].op; op->arguments.push_back(expression[next_op-1].node); op->arguments.push_back(expression[next_op+1].node); op->arguments.push_back(expression[next_op+3].node); expression[next_op-1].is_op=false; expression[next_op-1].node=op; if (!_validate_operator(op,&op->return_cache)) { String at; for(int i=0;iarguments.size();i++) { if (i>0) at+=" and "; at+=get_datatype_name(op->arguments[i]->get_datatype()); } _set_error("Invalid argument to ternary ?: operator: "+at); return NULL; } for(int i=0;i<4;i++) { expression.remove(next_op); } } else { if (next_op <1 || next_op>=(expression.size()-1)) { _set_error("Parser bug.."); ERR_FAIL_V(NULL); } OperatorNode *op = alloc_node(); op->op=expression[next_op].op; if (expression[next_op-1].is_op) { _set_error("Parser bug.."); ERR_FAIL_V(NULL); } if (expression[next_op+1].is_op) { // this is not invalid and can really appear // but it becomes invalid anyway because no binary op // can be followed by an unary op in a valid combination, // due to how precedence works, unaries will always dissapear first _set_error("Parser bug.."); } op->arguments.push_back(expression[next_op-1].node); //expression goes as left op->arguments.push_back(expression[next_op+1].node); //next expression goes as right expression[next_op-1].node=op; //replace all 3 nodes by this operator and make it an expression if (!_validate_operator(op,&op->return_cache)) { String at; for(int i=0;iarguments.size();i++) { if (i>0) at+=" and "; at+=get_datatype_name(op->arguments[i]->get_datatype()); } _set_error("Invalid arguments to operator '"+get_operator_text(op->op)+"' :" +at); return NULL; } expression.remove(next_op); expression.remove(next_op); } } return expression[0].node; } ShaderLanguage::Node* ShaderLanguage::_reduce_expression(BlockNode *p_block, ShaderLanguage::Node *p_node) { if (p_node->type!=Node::TYPE_OPERATOR) return p_node; //for now only reduce simple constructors OperatorNode *op=static_cast(p_node); if (op->op==OP_CONSTRUCT) { ERR_FAIL_COND_V(op->arguments[0]->type!=Node::TYPE_VARIABLE,p_node); VariableNode *vn = static_cast(op->arguments[0]); // StringName name=vn->name; DataType base=get_scalar_type(op->get_datatype()); Vector values; for(int i=1;iarguments.size();i++) { op->arguments[i]=_reduce_expression(p_block,op->arguments[i]); if (op->arguments[i]->type==Node::TYPE_CONSTANT) { ConstantNode *cn = static_cast(op->arguments[i]); if (get_scalar_type(cn->datatype)==base) { for(int j=0;jvalues.size();j++) { values.push_back(cn->values[j]); } } else if (get_scalar_type(cn->datatype)==cn->datatype) { ConstantNode::Value v; if (!convert_constant(cn,base,&v)) { return p_node; } values.push_back(v); } else { return p_node; } } else { return p_node; } } ConstantNode *cn=alloc_node(); cn->datatype=op->get_datatype(); cn->values=values; return cn; } else if (op->op==OP_NEGATE) { op->arguments[0]=_reduce_expression(p_block,op->arguments[0]); if (op->arguments[0]->type==Node::TYPE_CONSTANT) { ConstantNode *cn = static_cast(op->arguments[0]); DataType base=get_scalar_type(cn->datatype); Vector values; for(int i=0;ivalues.size();i++) { ConstantNode::Value nv; switch(base) { case TYPE_BOOL: { nv.boolean=!cn->values[i].boolean; } break; case TYPE_INT: { nv.sint=-cn->values[i].sint; } break; case TYPE_UINT: { nv.uint=-cn->values[i].uint; } break; case TYPE_FLOAT: { nv.real=-cn->values[i].real; } break; default: {} } values.push_back(nv); } cn->values=values; return cn; } } return p_node; } ShaderLanguage::Node* ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_block, const Map &p_builtin_types) { ShaderLanguage::Node* expr = _parse_expression(p_block,p_builtin_types); if (!expr) //errored return NULL; expr = _reduce_expression(p_block,expr); return expr; } Error ShaderLanguage::_parse_block(BlockNode* p_block,const Map &p_builtin_types,bool p_just_one,bool p_can_break,bool p_can_continue) { while(true) { TkPos pos = _get_tkpos(); Token tk = _get_token(); if (tk.type==TK_CURLY_BRACKET_CLOSE) { //end of block if (p_just_one) { _set_error("Unexpected '}'"); return ERR_PARSE_ERROR; } return OK; } else if (is_token_precision(tk.type) || is_token_nonvoid_datatype(tk.type)) { DataPrecision precision=PRECISION_DEFAULT; if (is_token_precision(tk.type)) { precision=get_token_precision(tk.type); tk = _get_token(); if (!is_token_nonvoid_datatype(tk.type)) { _set_error("Expected datatype after precission"); return ERR_PARSE_ERROR; } } DataType type = get_token_datatype(tk.type); tk = _get_token(); while(true) { if (tk.type!=TK_IDENTIFIER) { _set_error("Expected identifier after type"); return ERR_PARSE_ERROR; } StringName name = tk.text; if (_find_identifier(p_block,p_builtin_types,name)) { _set_error("Redefinition of '"+String(name)+"'"); return ERR_PARSE_ERROR; } BlockNode::Variable var; var.type=type; var.precision=precision; var.line=tk_line; p_block->variables[name]=var; tk = _get_token(); if (tk.type==TK_OP_ASSIGN) { //variable creted with assignment! must parse an expression Node* n = _parse_and_reduce_expression(p_block,p_builtin_types); if (!n) return ERR_PARSE_ERROR; OperatorNode *assign = alloc_node(); VariableNode *vnode = alloc_node(); 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(); } if (tk.type==TK_COMMA) { tk = _get_token(); //another variable } else if (tk.type==TK_SEMICOLON) { break; } else { _set_error("Expected ',' or ';' after variable"); return ERR_PARSE_ERROR; } } } else if (tk.type==TK_CURLY_BRACKET_OPEN) { //a sub block, just because.. BlockNode* block = alloc_node(); block->parent_block=p_block; _parse_block(block,p_builtin_types,false,p_can_break,p_can_continue); p_block->statements.push_back(block); } else if (tk.type==TK_CF_IF) { //if () {} tk = _get_token(); if (tk.type!=TK_PARENTHESIS_OPEN) { _set_error("Expected '(' after if"); return ERR_PARSE_ERROR; } ControlFlowNode *cf = alloc_node(); cf->flow_op=FLOW_OP_IF; 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; } BlockNode* block = alloc_node(); 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,p_can_break,p_can_continue); pos=_get_tkpos(); tk = _get_token(); if (tk.type==TK_CF_ELSE) { block = alloc_node(); block->parent_block=p_block; cf->blocks.push_back(block); err=_parse_block(block,p_builtin_types,true,p_can_break,p_can_continue); } else { _set_tkpos(pos); //rollback } } else { //nothng else, so expression _set_tkpos(pos); //rollback Node*expr = _parse_and_reduce_expression(p_block,p_builtin_types); if (!expr) return ERR_PARSE_ERROR; p_block->statements.push_back(expr); tk = _get_token(); if (tk.type!=TK_SEMICOLON) { _set_error("Expected ';' after statement"); return ERR_PARSE_ERROR; } } if (p_just_one) break; } return OK; } Error ShaderLanguage::_parse_shader(const Map< StringName, Map > &p_functions, const Set &p_render_modes) { Token tk = _get_token(); while(tk.type!=TK_EOF) { switch(tk.type) { case TK_RENDER_MODE: { while(true) { StringName mode; _get_completable_identifier(NULL,COMPLETION_RENDER_MODE,mode); if (mode==StringName()) { _set_error("Expected identifier for render mode"); return ERR_PARSE_ERROR; } if (!p_render_modes.has(tk.text)) { _set_error("Invalid render mode: '"+String(tk.text)+"'"); return ERR_PARSE_ERROR; } if (shader->render_modes.has(tk.text)) { _set_error("Duplicate render mode: '"+String(tk.text)+"'"); return ERR_PARSE_ERROR; } shader->render_modes.insert(tk.text); tk = _get_token(); if (tk.type==TK_COMMA) { //all good, do nothing } else if (tk.type==TK_SEMICOLON) { break; //done } else { _set_error("Unexpected token: "+get_token_text(tk)); return ERR_PARSE_ERROR; } } } break; case TK_UNIFORM: case TK_VARYING: { bool uniform = tk.type==TK_UNIFORM; DataPrecision precision = PRECISION_DEFAULT; DataType type; StringName name; tk = _get_token(); if (is_token_precision(tk.type)) { precision=get_token_precision(tk.type); tk = _get_token(); } if (!is_token_datatype(tk.type)) { _set_error("Expected datatype. "); return ERR_PARSE_ERROR; } type = get_token_datatype(tk.type); if (type==TYPE_VOID) { _set_error("void datatype not allowed here"); return ERR_PARSE_ERROR; } if (!uniform && typeTYPE_VEC4) { _set_error("Invalid type for varying, only float,vec2,vec3,vec4 allowed."); return ERR_PARSE_ERROR; } tk = _get_token(); if (tk.type!=TK_IDENTIFIER) { _set_error("Expected identifier!"); return ERR_PARSE_ERROR; } name=tk.text; if (_find_identifier(NULL,Map(),name)) { _set_error("Redefinition of '"+String(name)+"'"); return ERR_PARSE_ERROR; } if (uniform) { ShaderNode::Uniform uniform; uniform.order=shader->uniforms.size(); uniform.type=type; uniform.precission=precision; //todo parse default value tk = _get_token(); if (tk.type==TK_OP_ASSIGN) { Node* expr = _parse_and_reduce_expression(NULL,Map()); if (!expr) return ERR_PARSE_ERROR; if (expr->type!=Node::TYPE_CONSTANT) { _set_error("Expected constant expression after '='"); return ERR_PARSE_ERROR; } ConstantNode* cn = static_cast(expr); uniform.default_value.resize(cn->values.size()); if (!convert_constant(cn,uniform.type,uniform.default_value.ptr())) { _set_error("Can't convert constant to "+get_datatype_name(uniform.type)); return ERR_PARSE_ERROR; } tk = _get_token(); } if (tk.type==TK_COLON) { //hint tk = _get_token(); if (tk.type==TK_HINT_WHITE_TEXTURE) { uniform.hint=ShaderNode::Uniform::HINT_WHITE; } else if (tk.type==TK_HINT_BLACK_TEXTURE) { uniform.hint=ShaderNode::Uniform::HINT_BLACK; } else if (tk.type==TK_HINT_NORMAL_TEXTURE) { uniform.hint=ShaderNode::Uniform::HINT_NORMAL; } else if (tk.type==TK_HINT_ALBEDO_TEXTURE) { uniform.hint=ShaderNode::Uniform::HINT_ALBEDO; } else if (tk.type==TK_HINT_COLOR) { if (type!=TYPE_VEC4) { _set_error("Color hint is for vec4 only"); return ERR_PARSE_ERROR; } uniform.hint=ShaderNode::Uniform::HINT_COLOR; } else if (tk.type==TK_HINT_RANGE) { uniform.hint=ShaderNode::Uniform::HINT_RANGE; if (type!=TYPE_FLOAT && type!=TYPE_INT) { _set_error("Range hint is for float and int only"); return ERR_PARSE_ERROR; } tk = _get_token(); if (tk.type!=TK_PARENTHESIS_OPEN) { _set_error("Expected '(' after hint_range"); return ERR_PARSE_ERROR; } tk = _get_token(); if (tk.type!=TK_REAL_CONSTANT ||tk.type!=TK_INT_CONSTANT) { _set_error("Expected integer constant"); return ERR_PARSE_ERROR; } uniform.hint_range[0]=tk.constant; tk = _get_token(); if (tk.type!=TK_COMMA) { _set_error("Expected ',' after integer constant"); return ERR_PARSE_ERROR; } tk = _get_token(); if (tk.type!=TK_REAL_CONSTANT || tk.type!=TK_INT_CONSTANT) { _set_error("Expected integer constant after ','"); return ERR_PARSE_ERROR; } uniform.hint_range[1]=tk.constant; tk = _get_token(); if (tk.type==TK_COMMA) { tk = _get_token(); if (tk.type!=TK_REAL_CONSTANT || tk.type!=TK_INT_CONSTANT) { _set_error("Expected integer constant after ','"); return ERR_PARSE_ERROR; } uniform.hint_range[2]=tk.constant; tk = _get_token(); } else { if (type==TYPE_INT) { uniform.hint_range[2]=1; } else { uniform.hint_range[2]=0.001; } } if (tk.type!=TK_PARENTHESIS_CLOSE) { _set_error("Expected ','"); return ERR_PARSE_ERROR; } } else { _set_error("Expected valid type hint after ':'."); } if (uniform.hint!=ShaderNode::Uniform::HINT_RANGE && uniform.hint!=ShaderNode::Uniform::HINT_NONE && uniform.hint!=ShaderNode::Uniform::HINT_COLOR && type <=TYPE_MAT4) { _set_error("This hint is only for sampler types"); return ERR_PARSE_ERROR; } print_line("assigning name: "+String(name)); tk = _get_token(); } shader->uniforms[name]=uniform; if (tk.type!=TK_SEMICOLON) { _set_error("Expected ';'"); return ERR_PARSE_ERROR; } } else { ShaderNode::Varying varying; varying.type=type; varying.precission=precision; shader->varyings[name]=varying; tk = _get_token(); if (tk.type!=TK_SEMICOLON) { _set_error("Expected ';'"); return ERR_PARSE_ERROR; } } } break; default: { //function DataPrecision precision = PRECISION_DEFAULT; DataType type; StringName name; if (is_token_precision(tk.type)) { precision=get_token_precision(tk.type); tk = _get_token(); } if (!is_token_datatype(tk.type)) { _set_error("Expected funtion, uniform or varying "); return ERR_PARSE_ERROR; } type = get_token_datatype(tk.type); _get_completable_identifier(NULL,COMPLETION_MAIN_FUNCTION,name); if (name==StringName()) { _set_error("Expected function name after datatype"); return ERR_PARSE_ERROR; } if (_find_identifier(NULL,Map(),name)) { _set_error("Redefinition of '"+String(name)+"'"); return ERR_PARSE_ERROR; } tk = _get_token(); if (tk.type!=TK_PARENTHESIS_OPEN) { _set_error("Expected '(' after identifier"); return ERR_PARSE_ERROR; } Map builtin_types; if (p_functions.has(name)) { builtin_types=p_functions[name]; } ShaderNode::Function function; function.callable=!p_functions.has(name); function.name=name; FunctionNode* func_node=alloc_node(); function.function=func_node; shader->functions.push_back(function); func_node->name=name; func_node->return_type=type; func_node->return_precision=precision; func_node->body = alloc_node(); func_node->body->parent_function=func_node; tk = _get_token(); while(true) { if (tk.type==TK_PARENTHESIS_CLOSE) { break; } DataType ptype; StringName pname; DataPrecision pprecision = PRECISION_DEFAULT; if (is_token_precision(tk.type)) { pprecision=get_token_precision(tk.type); tk = _get_token(); } if (!is_token_datatype(tk.type)) { _set_error("Expected a valid datatype for argument"); return ERR_PARSE_ERROR; } ptype=get_token_datatype(tk.type); if (ptype==TYPE_VOID) { _set_error("void not allowed in argument"); return ERR_PARSE_ERROR; } tk = _get_token(); if (tk.type!=TK_IDENTIFIER) { _set_error("Expected identifier for argument name"); return ERR_PARSE_ERROR; } pname = tk.text; if (_find_identifier(func_node->body,builtin_types,pname)) { _set_error("Redefinition of '"+String(pname)+"'"); return ERR_PARSE_ERROR; } FunctionNode::Argument arg; arg.type=ptype; arg.name=pname; arg.precision=pprecision; func_node->arguments.push_back(arg); tk = _get_token(); if (tk.type==TK_COMMA) { tk = _get_token(); //do none and go on } else if (tk.type!=TK_PARENTHESIS_CLOSE) { _set_error("Expected ',' or ')' after identifier"); return ERR_PARSE_ERROR; } } if (p_functions.has(name)) { //if one of the core functions, make sure they are of the correct form if (func_node->arguments.size() > 0) { _set_error("Function '"+String(name)+"' expects no arguments."); return ERR_PARSE_ERROR; } if (func_node->return_type!=TYPE_VOID) { _set_error("Function '"+String(name)+"' must be of void return type."); return ERR_PARSE_ERROR; } } //all good let's parse inside the fucntion! tk = _get_token(); if (tk.type!=TK_CURLY_BRACKET_OPEN) { _set_error("Expected '{' to begin function"); return ERR_PARSE_ERROR; } Error err = _parse_block(func_node->body,builtin_types); if (err) return err; } } tk = _get_token(); } return OK; } Error ShaderLanguage::compile(const String& p_code, const Map< StringName, Map > &p_functions, const Set &p_render_modes) { clear(); code=p_code; nodes=NULL; shader = alloc_node(); Error err = _parse_shader(p_functions,p_render_modes); if (err!=OK) { return err; } return OK; } Error ShaderLanguage::complete(const String& p_code,const Map< StringName, Map > &p_functions,const Set& p_render_modes,List* r_options,String& r_call_hint) { clear(); code=p_code; nodes=NULL; shader = alloc_node(); Error err = _parse_shader(p_functions,p_render_modes); switch(completion_type) { case COMPLETION_NONE: { //do none return ERR_PARSE_ERROR; } break; case COMPLETION_RENDER_MODE: { for(const Set::Element *E=p_render_modes.front();E;E=E->next()) { r_options->push_back(E->get()); } return OK; } break; case COMPLETION_MAIN_FUNCTION: { print_line("complete main func"); for(const Map< StringName, Map >::Element *E=p_functions.front();E;E=E->next()) { r_options->push_back(E->key()); } return OK; } break; case COMPLETION_IDENTIFIER: case COMPLETION_FUNCTION_CALL: { print_line("complete identifier"); bool comp_ident=completion_type==COMPLETION_IDENTIFIER; Set matches; StringName skip_function; BlockNode *block=completion_block; while(block) { if (comp_ident) { for (const Map::Element *E=block->variables.front();E;E=E->next()) { if (E->get().linekey()); } } } if (block->parent_function) { if (comp_ident) { for(int i=0;iparent_function->arguments.size();i++) { matches.insert(block->parent_function->arguments[i].name); } } skip_function=block->parent_function->name; } block=block->parent_block; } if (comp_ident && skip_function!=StringName() && p_functions.has(skip_function)) { for (Map::Element *E=p_functions[skip_function].front();E;E=E->next()) { matches.insert(E->key()); } } if (comp_ident) { for (const Map::Element *E=shader->varyings.front();E;E=E->next()) { matches.insert(E->key()); } for (const Map::Element *E=shader->uniforms.front();E;E=E->next()) { matches.insert(E->key()); } } for(int i=0;ifunctions.size();i++) { if (!shader->functions[i].callable || shader->functions[i].name==skip_function) continue; matches.insert(String(shader->functions[i].name)+"("); } int idx=0; while (builtin_func_defs[idx].name) { matches.insert(String(builtin_func_defs[idx].name)+"("); idx++; } for(Set::Element *E=matches.front();E;E=E->next()) { r_options->push_back(E->get()); } return OK; } break; case COMPLETION_CALL_ARGUMENTS: { print_line("complete callargs"); for(int i=0;ifunctions.size();i++) { if (!shader->functions[i].callable) continue; if (shader->functions[i].name==completion_function) { String calltip; calltip+=get_datatype_name( shader->functions[i].function->return_type ); calltip+=" "; calltip+=shader->functions[i].name; calltip+="("; for(int j=0;jfunctions[i].function->arguments.size();j++) { if (j>0) calltip+=", "; else calltip+=" "; if (j==completion_argument) { calltip+=CharType(0xFFFF); } calltip+=get_datatype_name(shader->functions[i].function->arguments[j].type); calltip+=" "; calltip+=shader->functions[i].function->arguments[j].name; if (j==completion_argument) { calltip+=CharType(0xFFFF); } } if (shader->functions[i].function->arguments.size()) calltip+=" "; calltip+=")"; r_call_hint=calltip; return OK; } } int idx=0; String calltip; while (builtin_func_defs[idx].name) { if (completion_function==builtin_func_defs[idx].name) { if (calltip.length()) calltip+="\n"; calltip+=get_datatype_name( builtin_func_defs[idx].rettype ); calltip+=" "; calltip+=builtin_func_defs[idx].name; calltip+="("; bool found_arg=false; for(int i=0;i<4;i++) { if (builtin_func_defs[idx].args[i]==TYPE_VOID) break; if (i>0) calltip+=", "; else calltip+=" "; if (i==completion_argument) { calltip+=CharType(0xFFFF); } calltip+=get_datatype_name(builtin_func_defs[idx].args[i]); if (i==completion_argument) { calltip+=CharType(0xFFFF); } found_arg=true; } if (found_arg) calltip+=" "; calltip+=")"; } idx++; } r_call_hint=calltip; return OK; } break; case COMPLETION_INDEX: { const char colv[4]={'r','g','b','a'}; const char coordv[4]={'x','y','z','w'}; int limit=0; switch(completion_base) { case TYPE_BVEC2: case TYPE_IVEC2: case TYPE_UVEC2: case TYPE_VEC2: { limit=2; } break; case TYPE_BVEC3: case TYPE_IVEC3: case TYPE_UVEC3: case TYPE_VEC3: { limit=3; } break; case TYPE_BVEC4: case TYPE_IVEC4: case TYPE_UVEC4: case TYPE_VEC4: { limit=4; } break; case TYPE_MAT2: limit=2; break; case TYPE_MAT3: limit=3; break; case TYPE_MAT4: limit=4; break; default: {} } for(int i=0;ipush_back(String::chr(colv[i])); r_options->push_back(String::chr(coordv[i])); } } break; } return ERR_PARSE_ERROR; } String ShaderLanguage::get_error_text() { return error_str; } int ShaderLanguage::get_error_line() { return error_line; } ShaderLanguage::ShaderNode *ShaderLanguage::get_shader() { return shader; } ShaderLanguage::ShaderLanguage() { nodes=NULL; } ShaderLanguage::~ShaderLanguage() { clear(); }