Merge pull request #60568 from Chaosus/shader_keyword_completions
Add keyword completion to shader editor
This commit is contained in:
commit
477b53d280
2 changed files with 495 additions and 109 deletions
|
@ -237,92 +237,141 @@ ShaderLanguage::Token ShaderLanguage::_make_token(TokenType p_type, const String
|
||||||
return tk;
|
return tk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ContextFlag : uint32_t {
|
||||||
|
CF_UNSPECIFIED = 0U,
|
||||||
|
CF_BLOCK = 1U, // "void test() { <x> }"
|
||||||
|
CF_FUNC_DECL_PARAM_SPEC = 2U, // "void test(<x> int param) {}"
|
||||||
|
CF_FUNC_DECL_PARAM_TYPE = 4U, // "void test(<x> param) {}"
|
||||||
|
CF_IF_DECL = 8U, // "if(<x>) {}"
|
||||||
|
CF_BOOLEAN = 16U, // "bool t = <x>;"
|
||||||
|
CF_GLOBAL_SPACE = 32U, // "struct", "const", "void" etc.
|
||||||
|
CF_DATATYPE = 64U, // "<x> value;"
|
||||||
|
CF_UNIFORM_TYPE = 128U, // "uniform <x> myUniform;"
|
||||||
|
CF_VARYING_TYPE = 256U, // "varying <x> myVarying;"
|
||||||
|
CF_PRECISION_MODIFIER = 512U, // "<x> vec4 a = vec4(0.0, 1.0, 2.0, 3.0);"
|
||||||
|
CF_INTERPOLATION_QUALIFIER = 1024U, // "varying <x> vec3 myColor;"
|
||||||
|
CF_UNIFORM_KEYWORD = 2048U, // "uniform"
|
||||||
|
CF_CONST_KEYWORD = 4096U, // "const"
|
||||||
|
CF_UNIFORM_QUALIFIER = 8192U, // "<x> uniform float t;"
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32_t KCF_DATATYPE = CF_BLOCK | CF_GLOBAL_SPACE | CF_DATATYPE | CF_FUNC_DECL_PARAM_TYPE | CF_UNIFORM_TYPE;
|
||||||
|
const uint32_t KCF_SAMPLER_DATATYPE = CF_FUNC_DECL_PARAM_TYPE | CF_UNIFORM_TYPE;
|
||||||
|
|
||||||
const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
|
const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
|
||||||
{ TK_TRUE, "true" },
|
{ TK_TRUE, "true", CF_BLOCK | CF_IF_DECL | CF_BOOLEAN, {}, {} },
|
||||||
{ TK_FALSE, "false" },
|
{ TK_FALSE, "false", CF_BLOCK | CF_IF_DECL | CF_BOOLEAN, {}, {} },
|
||||||
{ TK_TYPE_VOID, "void" },
|
|
||||||
{ TK_TYPE_BOOL, "bool" },
|
// data types
|
||||||
{ TK_TYPE_BVEC2, "bvec2" },
|
|
||||||
{ TK_TYPE_BVEC3, "bvec3" },
|
{ TK_TYPE_VOID, "void", CF_GLOBAL_SPACE, {}, {} },
|
||||||
{ TK_TYPE_BVEC4, "bvec4" },
|
{ TK_TYPE_BOOL, "bool", KCF_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_INT, "int" },
|
{ TK_TYPE_BVEC2, "bvec2", KCF_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_IVEC2, "ivec2" },
|
{ TK_TYPE_BVEC3, "bvec3", KCF_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_IVEC3, "ivec3" },
|
{ TK_TYPE_BVEC4, "bvec4", KCF_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_IVEC4, "ivec4" },
|
{ TK_TYPE_INT, "int", KCF_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_UINT, "uint" },
|
{ TK_TYPE_IVEC2, "ivec2", KCF_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_UVEC2, "uvec2" },
|
{ TK_TYPE_IVEC3, "ivec3", KCF_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_UVEC3, "uvec3" },
|
{ TK_TYPE_IVEC4, "ivec4", KCF_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_UVEC4, "uvec4" },
|
{ TK_TYPE_UINT, "uint", KCF_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_FLOAT, "float" },
|
{ TK_TYPE_UVEC2, "uvec2", KCF_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_VEC2, "vec2" },
|
{ TK_TYPE_UVEC3, "uvec3", KCF_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_VEC3, "vec3" },
|
{ TK_TYPE_UVEC4, "uvec4", KCF_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_VEC4, "vec4" },
|
{ TK_TYPE_FLOAT, "float", KCF_DATATYPE | CF_VARYING_TYPE, {}, {} },
|
||||||
{ TK_TYPE_MAT2, "mat2" },
|
{ TK_TYPE_VEC2, "vec2", KCF_DATATYPE | CF_VARYING_TYPE, {}, {} },
|
||||||
{ TK_TYPE_MAT3, "mat3" },
|
{ TK_TYPE_VEC3, "vec3", KCF_DATATYPE | CF_VARYING_TYPE, {}, {} },
|
||||||
{ TK_TYPE_MAT4, "mat4" },
|
{ TK_TYPE_VEC4, "vec4", KCF_DATATYPE | CF_VARYING_TYPE, {}, {} },
|
||||||
{ TK_TYPE_SAMPLER2D, "sampler2D" },
|
{ TK_TYPE_MAT2, "mat2", KCF_DATATYPE | CF_VARYING_TYPE, {}, {} },
|
||||||
{ TK_TYPE_ISAMPLER2D, "isampler2D" },
|
{ TK_TYPE_MAT3, "mat3", KCF_DATATYPE | CF_VARYING_TYPE, {}, {} },
|
||||||
{ TK_TYPE_USAMPLER2D, "usampler2D" },
|
{ TK_TYPE_MAT4, "mat4", KCF_DATATYPE | CF_VARYING_TYPE, {}, {} },
|
||||||
{ TK_TYPE_SAMPLER2DARRAY, "sampler2DArray" },
|
{ TK_TYPE_SAMPLER2D, "sampler2D", KCF_SAMPLER_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_ISAMPLER2DARRAY, "isampler2DArray" },
|
{ TK_TYPE_ISAMPLER2D, "isampler2D", KCF_SAMPLER_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_USAMPLER2DARRAY, "usampler2DArray" },
|
{ TK_TYPE_USAMPLER2D, "usampler2D", KCF_SAMPLER_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_SAMPLER3D, "sampler3D" },
|
{ TK_TYPE_SAMPLER2DARRAY, "sampler2DArray", KCF_SAMPLER_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_ISAMPLER3D, "isampler3D" },
|
{ TK_TYPE_ISAMPLER2DARRAY, "isampler2DArray", KCF_SAMPLER_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_USAMPLER3D, "usampler3D" },
|
{ TK_TYPE_USAMPLER2DARRAY, "usampler2DArray", KCF_SAMPLER_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_SAMPLERCUBE, "samplerCube" },
|
{ TK_TYPE_SAMPLER3D, "sampler3D", KCF_SAMPLER_DATATYPE, {}, {} },
|
||||||
{ TK_TYPE_SAMPLERCUBEARRAY, "samplerCubeArray" },
|
{ TK_TYPE_ISAMPLER3D, "isampler3D", KCF_SAMPLER_DATATYPE, {}, {} },
|
||||||
{ TK_INTERPOLATION_FLAT, "flat" },
|
{ TK_TYPE_USAMPLER3D, "usampler3D", KCF_SAMPLER_DATATYPE, {}, {} },
|
||||||
{ TK_INTERPOLATION_SMOOTH, "smooth" },
|
{ TK_TYPE_SAMPLERCUBE, "samplerCube", KCF_SAMPLER_DATATYPE, {}, {} },
|
||||||
{ TK_CONST, "const" },
|
{ TK_TYPE_SAMPLERCUBEARRAY, "samplerCubeArray", KCF_SAMPLER_DATATYPE, {}, {} },
|
||||||
{ TK_STRUCT, "struct" },
|
|
||||||
{ TK_PRECISION_LOW, "lowp" },
|
// interpolation qualifiers
|
||||||
{ TK_PRECISION_MID, "mediump" },
|
|
||||||
{ TK_PRECISION_HIGH, "highp" },
|
{ TK_INTERPOLATION_FLAT, "flat", CF_INTERPOLATION_QUALIFIER, {}, {} },
|
||||||
{ TK_CF_IF, "if" },
|
{ TK_INTERPOLATION_SMOOTH, "smooth", CF_INTERPOLATION_QUALIFIER, {}, {} },
|
||||||
{ TK_CF_ELSE, "else" },
|
|
||||||
{ TK_CF_FOR, "for" },
|
// precision modifiers
|
||||||
{ TK_CF_WHILE, "while" },
|
|
||||||
{ TK_CF_DO, "do" },
|
{ TK_PRECISION_LOW, "lowp", CF_BLOCK | CF_PRECISION_MODIFIER, {}, {} },
|
||||||
{ TK_CF_SWITCH, "switch" },
|
{ TK_PRECISION_MID, "mediump", CF_BLOCK | CF_PRECISION_MODIFIER, {}, {} },
|
||||||
{ TK_CF_CASE, "case" },
|
{ TK_PRECISION_HIGH, "highp", CF_BLOCK | CF_PRECISION_MODIFIER, {}, {} },
|
||||||
{ TK_CF_DEFAULT, "default" },
|
|
||||||
{ TK_CF_BREAK, "break" },
|
// global space keywords
|
||||||
{ TK_CF_CONTINUE, "continue" },
|
|
||||||
{ TK_CF_RETURN, "return" },
|
{ TK_UNIFORM, "uniform", CF_GLOBAL_SPACE | CF_UNIFORM_KEYWORD, {}, {} },
|
||||||
{ TK_CF_DISCARD, "discard" },
|
{ TK_VARYING, "varying", CF_GLOBAL_SPACE, { "particles", "sky", "fog" }, {} },
|
||||||
{ TK_UNIFORM, "uniform" },
|
{ TK_CONST, "const", CF_BLOCK | CF_GLOBAL_SPACE | CF_CONST_KEYWORD, {}, {} },
|
||||||
{ TK_INSTANCE, "instance" },
|
{ TK_STRUCT, "struct", CF_GLOBAL_SPACE, {}, {} },
|
||||||
{ TK_GLOBAL, "global" },
|
{ TK_SHADER_TYPE, "shader_type", CF_GLOBAL_SPACE, {}, {} },
|
||||||
{ TK_VARYING, "varying" },
|
{ TK_RENDER_MODE, "render_mode", CF_GLOBAL_SPACE, {}, {} },
|
||||||
{ TK_ARG_IN, "in" },
|
|
||||||
{ TK_ARG_OUT, "out" },
|
// uniform qualifiers
|
||||||
{ TK_ARG_INOUT, "inout" },
|
|
||||||
{ TK_RENDER_MODE, "render_mode" },
|
{ TK_INSTANCE, "instance", CF_GLOBAL_SPACE | CF_UNIFORM_QUALIFIER, {}, {} },
|
||||||
{ TK_HINT_WHITE_TEXTURE, "hint_white" },
|
{ TK_GLOBAL, "global", CF_GLOBAL_SPACE | CF_UNIFORM_QUALIFIER, {}, {} },
|
||||||
{ TK_HINT_BLACK_TEXTURE, "hint_black" },
|
|
||||||
{ TK_HINT_NORMAL_TEXTURE, "hint_normal" },
|
// block keywords
|
||||||
{ TK_HINT_ROUGHNESS_NORMAL_TEXTURE, "hint_roughness_normal" },
|
|
||||||
{ TK_HINT_ROUGHNESS_R, "hint_roughness_r" },
|
{ TK_CF_IF, "if", CF_BLOCK, {}, {} },
|
||||||
{ TK_HINT_ROUGHNESS_G, "hint_roughness_g" },
|
{ TK_CF_ELSE, "else", CF_BLOCK, {}, {} },
|
||||||
{ TK_HINT_ROUGHNESS_B, "hint_roughness_b" },
|
{ TK_CF_FOR, "for", CF_BLOCK, {}, {} },
|
||||||
{ TK_HINT_ROUGHNESS_A, "hint_roughness_a" },
|
{ TK_CF_WHILE, "while", CF_BLOCK, {}, {} },
|
||||||
{ TK_HINT_ROUGHNESS_GRAY, "hint_roughness_gray" },
|
{ TK_CF_DO, "do", CF_BLOCK, {}, {} },
|
||||||
{ TK_HINT_ANISOTROPY_TEXTURE, "hint_anisotropy" },
|
{ TK_CF_SWITCH, "switch", CF_BLOCK, {}, {} },
|
||||||
{ TK_HINT_ALBEDO_TEXTURE, "hint_albedo" },
|
{ TK_CF_CASE, "case", CF_BLOCK, {}, {} },
|
||||||
{ TK_HINT_BLACK_ALBEDO_TEXTURE, "hint_black_albedo" },
|
{ TK_CF_DEFAULT, "default", CF_BLOCK, {}, {} },
|
||||||
{ TK_HINT_COLOR, "hint_color" },
|
{ TK_CF_BREAK, "break", CF_BLOCK, {}, {} },
|
||||||
{ TK_HINT_RANGE, "hint_range" },
|
{ TK_CF_CONTINUE, "continue", CF_BLOCK, {}, {} },
|
||||||
{ TK_HINT_INSTANCE_INDEX, "instance_index" },
|
{ TK_CF_RETURN, "return", CF_BLOCK, {}, {} },
|
||||||
{ TK_FILTER_NEAREST, "filter_nearest" },
|
{ TK_CF_DISCARD, "discard", CF_BLOCK, { "particles", "sky", "fog" }, { "fragment" } },
|
||||||
{ TK_FILTER_LINEAR, "filter_linear" },
|
|
||||||
{ TK_FILTER_NEAREST_MIPMAP, "filter_nearest_mipmap" },
|
// function specifier keywords
|
||||||
{ TK_FILTER_LINEAR_MIPMAP, "filter_linear_mipmap" },
|
|
||||||
{ TK_FILTER_NEAREST_MIPMAP_ANISOTROPIC, "filter_nearest_mipmap_anisotropic" },
|
{ TK_ARG_IN, "in", CF_FUNC_DECL_PARAM_SPEC, {}, {} },
|
||||||
{ TK_FILTER_LINEAR_MIPMAP_ANISOTROPIC, "filter_linear_mipmap_anisotropic" },
|
{ TK_ARG_OUT, "out", CF_FUNC_DECL_PARAM_SPEC, {}, {} },
|
||||||
{ TK_REPEAT_ENABLE, "repeat_enable" },
|
{ TK_ARG_INOUT, "inout", CF_FUNC_DECL_PARAM_SPEC, {}, {} },
|
||||||
{ TK_REPEAT_DISABLE, "repeat_disable" },
|
|
||||||
{ TK_SHADER_TYPE, "shader_type" },
|
// hints
|
||||||
{ TK_ERROR, nullptr }
|
|
||||||
|
{ TK_HINT_RANGE, "hint_range", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_COLOR, "hint_color", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_INSTANCE_INDEX, "instance_index", CF_UNSPECIFIED, {}, {} },
|
||||||
|
|
||||||
|
// sampler hints
|
||||||
|
|
||||||
|
{ TK_HINT_ALBEDO_TEXTURE, "hint_albedo", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_BLACK_ALBEDO_TEXTURE, "hint_black_albedo", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_NORMAL_TEXTURE, "hint_normal", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_WHITE_TEXTURE, "hint_white", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_BLACK_TEXTURE, "hint_black", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_ANISOTROPY_TEXTURE, "hint_anisotropy", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_ROUGHNESS_R, "hint_roughness_r", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_ROUGHNESS_G, "hint_roughness_g", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_ROUGHNESS_B, "hint_roughness_b", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_ROUGHNESS_A, "hint_roughness_a", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_ROUGHNESS_NORMAL_TEXTURE, "hint_roughness_normal", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_HINT_ROUGHNESS_GRAY, "hint_roughness_gray", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_FILTER_NEAREST, "filter_nearest", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_FILTER_LINEAR, "filter_linear", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_FILTER_NEAREST_MIPMAP, "filter_nearest_mipmap", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_FILTER_LINEAR_MIPMAP, "filter_linear_mipmap", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_FILTER_NEAREST_MIPMAP_ANISOTROPIC, "filter_nearest_mipmap_anisotropic", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_FILTER_LINEAR_MIPMAP_ANISOTROPIC, "filter_linear_mipmap_anisotropic", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_REPEAT_ENABLE, "repeat_enable", CF_UNSPECIFIED, {}, {} },
|
||||||
|
{ TK_REPEAT_DISABLE, "repeat_disable", CF_UNSPECIFIED, {}, {} },
|
||||||
|
|
||||||
|
{ TK_ERROR, nullptr, CF_UNSPECIFIED, {}, {} }
|
||||||
};
|
};
|
||||||
|
|
||||||
ShaderLanguage::Token ShaderLanguage::_get_token() {
|
ShaderLanguage::Token ShaderLanguage::_get_token() {
|
||||||
|
@ -752,6 +801,19 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
|
||||||
#undef GETCHAR
|
#undef GETCHAR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShaderLanguage::_lookup_next(Token &r_tk) {
|
||||||
|
TkPos pre_pos = _get_tkpos();
|
||||||
|
int line = pre_pos.tk_line;
|
||||||
|
_get_token();
|
||||||
|
Token tk = _get_token();
|
||||||
|
_set_tkpos(pre_pos);
|
||||||
|
if (tk.line == line) {
|
||||||
|
r_tk = tk;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
String ShaderLanguage::token_debug(const String &p_code) {
|
String ShaderLanguage::token_debug(const String &p_code) {
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
|
@ -852,6 +914,13 @@ bool ShaderLanguage::is_token_precision(TokenType p_type) {
|
||||||
p_type == TK_PRECISION_HIGH);
|
p_type == TK_PRECISION_HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShaderLanguage::is_token_arg_qual(TokenType p_type) {
|
||||||
|
return (
|
||||||
|
p_type == TK_ARG_IN ||
|
||||||
|
p_type == TK_ARG_OUT ||
|
||||||
|
p_type == TK_ARG_INOUT);
|
||||||
|
}
|
||||||
|
|
||||||
ShaderLanguage::DataPrecision ShaderLanguage::get_token_precision(TokenType p_type) {
|
ShaderLanguage::DataPrecision ShaderLanguage::get_token_precision(TokenType p_type) {
|
||||||
if (p_type == TK_PRECISION_LOW) {
|
if (p_type == TK_PRECISION_LOW) {
|
||||||
return PRECISION_LOWP;
|
return PRECISION_LOWP;
|
||||||
|
@ -967,6 +1036,7 @@ void ShaderLanguage::clear() {
|
||||||
completion_base_array = false;
|
completion_base_array = false;
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_GLOBAL_SPACE;
|
||||||
used_constants.clear();
|
used_constants.clear();
|
||||||
used_varyings.clear();
|
used_varyings.clear();
|
||||||
used_uniforms.clear();
|
used_uniforms.clear();
|
||||||
|
@ -6395,8 +6465,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_and_reduce_expression(BlockNode *p_
|
||||||
Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_function_info, bool p_just_one, bool p_can_break, bool p_can_continue) {
|
Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_function_info, bool p_just_one, bool p_can_break, bool p_can_continue) {
|
||||||
while (true) {
|
while (true) {
|
||||||
TkPos pos = _get_tkpos();
|
TkPos pos = _get_tkpos();
|
||||||
|
|
||||||
Token tk = _get_token();
|
Token tk = _get_token();
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
Token next;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
if (p_block && p_block->block_type == BlockNode::BLOCK_TYPE_SWITCH) {
|
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) {
|
if (tk.type != TK_CF_CASE && tk.type != TK_CF_DEFAULT && tk.type != TK_CURLY_BRACKET_CLOSE) {
|
||||||
|
@ -6429,6 +6501,16 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
|
||||||
}
|
}
|
||||||
#endif // DEBUG_ENABLED
|
#endif // DEBUG_ENABLED
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
uint32_t precision_flag = CF_PRECISION_MODIFIER;
|
||||||
|
|
||||||
|
keyword_completion_context = CF_DATATYPE;
|
||||||
|
if (!is_token_precision(tk.type)) {
|
||||||
|
if (!is_struct) {
|
||||||
|
keyword_completion_context |= precision_flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
bool is_const = false;
|
bool is_const = false;
|
||||||
|
|
||||||
|
@ -6450,6 +6532,26 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
|
||||||
if (!is_struct) {
|
if (!is_struct) {
|
||||||
is_struct = shader->structs.has(tk.text); // check again.
|
is_struct = shader->structs.has(tk.text); // check again.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (keyword_completion_context & precision_flag) {
|
||||||
|
keyword_completion_context ^= precision_flag;
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (is_const && _lookup_next(next)) {
|
||||||
|
if (is_token_precision(next.type)) {
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
if (is_token_datatype(next.type)) {
|
||||||
|
keyword_completion_context ^= CF_DATATYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
|
if (precision != PRECISION_DEFAULT) {
|
||||||
if (!is_token_nonvoid_datatype(tk.type)) {
|
if (!is_token_nonvoid_datatype(tk.type)) {
|
||||||
_set_error(RTR("Expected variable type after precision modifier."));
|
_set_error(RTR("Expected variable type after precision modifier."));
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
|
@ -6473,6 +6575,10 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
int array_size = 0;
|
int array_size = 0;
|
||||||
bool fixed_array_size = false;
|
bool fixed_array_size = false;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
@ -6576,7 +6682,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
|
||||||
|
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (var.type == DataType::TYPE_BOOL) {
|
||||||
|
keyword_completion_context = CF_BOOLEAN;
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
if (var.array_size > 0 || unknown_size) {
|
if (var.array_size > 0 || unknown_size) {
|
||||||
bool full_def = false;
|
bool full_def = false;
|
||||||
|
|
||||||
|
@ -6823,7 +6933,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
} while (tk.type == TK_COMMA); //another variable
|
} while (tk.type == TK_COMMA); //another variable
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_BLOCK;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
p_block->statements.push_back(static_cast<Node *>(vdnode));
|
p_block->statements.push_back(static_cast<Node *>(vdnode));
|
||||||
} else if (tk.type == TK_CURLY_BRACKET_OPEN) {
|
} else if (tk.type == TK_CURLY_BRACKET_OPEN) {
|
||||||
//a sub block, just because..
|
//a sub block, just because..
|
||||||
|
@ -6843,10 +6955,16 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
|
||||||
|
|
||||||
ControlFlowNode *cf = alloc_node<ControlFlowNode>();
|
ControlFlowNode *cf = alloc_node<ControlFlowNode>();
|
||||||
cf->flow_op = FLOW_OP_IF;
|
cf->flow_op = FLOW_OP_IF;
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_IF_DECL;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
|
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
|
||||||
if (!n) {
|
if (!n) {
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_BLOCK;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
if (n->get_datatype() != TYPE_BOOL) {
|
if (n->get_datatype() != TYPE_BOOL) {
|
||||||
_set_error(RTR("Expected a boolean expression."));
|
_set_error(RTR("Expected a boolean expression."));
|
||||||
|
@ -7184,10 +7302,17 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
|
||||||
init_block->parent_block = p_block;
|
init_block->parent_block = p_block;
|
||||||
init_block->single_statement = true;
|
init_block->single_statement = true;
|
||||||
cf->blocks.push_back(init_block);
|
cf->blocks.push_back(init_block);
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_DATATYPE;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
Error err = _parse_block(init_block, p_function_info, true, false, false);
|
Error err = _parse_block(init_block, p_function_info, true, false, false);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
BlockNode *condition_block = alloc_node<BlockNode>();
|
BlockNode *condition_block = alloc_node<BlockNode>();
|
||||||
condition_block->block_type = BlockNode::BLOCK_TYPE_FOR_CONDITION;
|
condition_block->block_type = BlockNode::BLOCK_TYPE_FOR_CONDITION;
|
||||||
|
@ -7216,6 +7341,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
|
||||||
cf->blocks.push_back(block);
|
cf->blocks.push_back(block);
|
||||||
p_block->statements.push_back(cf);
|
p_block->statements.push_back(cf);
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_BLOCK;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
err = _parse_block(block, p_function_info, true, true, true);
|
err = _parse_block(block, p_function_info, true, true, true);
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
return err;
|
||||||
|
@ -7260,6 +7388,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
|
||||||
} else {
|
} else {
|
||||||
_set_tkpos(pos); //rollback, wants expression
|
_set_tkpos(pos); //rollback, wants expression
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (b->parent_function->return_type == DataType::TYPE_BOOL) {
|
||||||
|
keyword_completion_context = CF_BOOLEAN;
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
Node *expr = _parse_and_reduce_expression(p_block, p_function_info);
|
Node *expr = _parse_and_reduce_expression(p_block, p_function_info);
|
||||||
if (!expr) {
|
if (!expr) {
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
|
@ -7276,6 +7410,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (b->parent_function->return_type == DataType::TYPE_BOOL) {
|
||||||
|
keyword_completion_context = CF_BLOCK;
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
flow->expressions.push_back(expr);
|
flow->expressions.push_back(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7502,15 +7642,17 @@ Error ShaderLanguage::_validate_datatype(DataType p_type) {
|
||||||
Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Vector<ModeInfo> &p_render_modes, const Set<String> &p_shader_types) {
|
Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_functions, const Vector<ModeInfo> &p_render_modes, const Set<String> &p_shader_types) {
|
||||||
Token tk = _get_token();
|
Token tk = _get_token();
|
||||||
TkPos prev_pos;
|
TkPos prev_pos;
|
||||||
|
Token next;
|
||||||
|
|
||||||
if (tk.type != TK_SHADER_TYPE) {
|
if (tk.type != TK_SHADER_TYPE) {
|
||||||
_set_error(vformat(RTR("Expected '%s' at the beginning of shader. Valid types are: %s."), "shader_type", _get_shader_type_list(p_shader_types)));
|
_set_error(vformat(RTR("Expected '%s' at the beginning of shader. Valid types are: %s."), "shader_type", _get_shader_type_list(p_shader_types)));
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
StringName shader_type_identifier;
|
|
||||||
_get_completable_identifier(nullptr, COMPLETION_SHADER_TYPE, shader_type_identifier);
|
_get_completable_identifier(nullptr, COMPLETION_SHADER_TYPE, shader_type_identifier);
|
||||||
|
|
||||||
if (shader_type_identifier == StringName()) {
|
if (shader_type_identifier == StringName()) {
|
||||||
_set_error(vformat(RTR("Expected an identifier after '%s', indicating the type of shader. Valid types are: %s."), "shader_type", _get_shader_type_list(p_shader_types)));
|
_set_error(vformat(RTR("Expected an identifier after '%s', indicating the type of shader. Valid types are: %s."), "shader_type", _get_shader_type_list(p_shader_types)));
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
|
@ -7528,6 +7670,9 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_GLOBAL_SPACE;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
|
|
||||||
int texture_uniforms = 0;
|
int texture_uniforms = 0;
|
||||||
|
@ -7621,7 +7766,9 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
case TK_STRUCT: {
|
case TK_STRUCT: {
|
||||||
ShaderNode::Struct st;
|
ShaderNode::Struct st;
|
||||||
DataType type;
|
DataType type;
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
if (tk.type == TK_IDENTIFIER) {
|
if (tk.type == TK_IDENTIFIER) {
|
||||||
st.name = tk.text;
|
st.name = tk.text;
|
||||||
|
@ -7644,7 +7791,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
|
|
||||||
int member_count = 0;
|
int member_count = 0;
|
||||||
Set<String> member_names;
|
Set<String> member_names;
|
||||||
|
|
||||||
while (true) { // variables list
|
while (true) { // variables list
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_DATATYPE | CF_PRECISION_MODIFIER;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
if (tk.type == TK_CURLY_BRACKET_CLOSE) {
|
if (tk.type == TK_CURLY_BRACKET_CLOSE) {
|
||||||
break;
|
break;
|
||||||
|
@ -7661,6 +7813,9 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
if (is_token_precision(tk.type)) {
|
if (is_token_precision(tk.type)) {
|
||||||
precision = get_token_precision(tk.type);
|
precision = get_token_precision(tk.type);
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context ^= CF_PRECISION_MODIFIER;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shader->structs.has(tk.text)) {
|
if (shader->structs.has(tk.text)) {
|
||||||
|
@ -7687,6 +7842,9 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
_set_error(vformat(RTR("A '%s' data type is not allowed here."), get_datatype_name(type)));
|
_set_error(vformat(RTR("A '%s' data type is not allowed here."), get_datatype_name(type)));
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
bool fixed_array_size = false;
|
bool fixed_array_size = false;
|
||||||
|
@ -7758,12 +7916,19 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
_set_error(RTR("Empty structs are not allowed."));
|
_set_error(RTR("Empty structs are not allowed."));
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
if (tk.type != TK_SEMICOLON) {
|
if (tk.type != TK_SEMICOLON) {
|
||||||
_set_expected_error(";");
|
_set_expected_error(";");
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_GLOBAL_SPACE;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
shader->structs[st.name] = st;
|
shader->structs[st.name] = st;
|
||||||
shader->vstructs.push_back(st); // struct's order is important!
|
shader->vstructs.push_back(st); // struct's order is important!
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
|
@ -7773,6 +7938,14 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
#endif // DEBUG_ENABLED
|
#endif // DEBUG_ENABLED
|
||||||
} break;
|
} break;
|
||||||
case TK_GLOBAL: {
|
case TK_GLOBAL: {
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_UNIFORM_KEYWORD;
|
||||||
|
if (_lookup_next(next)) {
|
||||||
|
if (next.type == TK_UNIFORM) {
|
||||||
|
keyword_completion_context ^= CF_UNIFORM_KEYWORD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
if (tk.type != TK_UNIFORM) {
|
if (tk.type != TK_UNIFORM) {
|
||||||
_set_expected_after_error("uniform", "global");
|
_set_expected_after_error("uniform", "global");
|
||||||
|
@ -7782,6 +7955,14 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
};
|
};
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case TK_INSTANCE: {
|
case TK_INSTANCE: {
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_UNIFORM_KEYWORD;
|
||||||
|
if (_lookup_next(next)) {
|
||||||
|
if (next.type == TK_UNIFORM) {
|
||||||
|
keyword_completion_context ^= CF_UNIFORM_KEYWORD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
if (uniform_scope == ShaderNode::Uniform::SCOPE_LOCAL) {
|
if (uniform_scope == ShaderNode::Uniform::SCOPE_LOCAL) {
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
if (tk.type != TK_UNIFORM) {
|
if (tk.type != TK_UNIFORM) {
|
||||||
|
@ -7795,14 +7976,15 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
case TK_UNIFORM:
|
case TK_UNIFORM:
|
||||||
case TK_VARYING: {
|
case TK_VARYING: {
|
||||||
bool uniform = tk.type == TK_UNIFORM;
|
bool uniform = tk.type == TK_UNIFORM;
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
if (!uniform) {
|
if (!uniform) {
|
||||||
if (shader_type_identifier == "particles" || shader_type_identifier == "sky" || shader_type_identifier == "fog") {
|
if (shader_type_identifier == "particles" || shader_type_identifier == "sky" || shader_type_identifier == "fog") {
|
||||||
_set_error(vformat(RTR("Varyings cannot be used in '%s' shaders."), shader_type_identifier));
|
_set_error(vformat(RTR("Varyings cannot be used in '%s' shaders."), shader_type_identifier));
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DataPrecision precision = PRECISION_DEFAULT;
|
DataPrecision precision = PRECISION_DEFAULT;
|
||||||
DataInterpolation interpolation = INTERPOLATION_SMOOTH;
|
DataInterpolation interpolation = INTERPOLATION_SMOOTH;
|
||||||
DataType type;
|
DataType type;
|
||||||
|
@ -7810,18 +7992,81 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
int array_size = 0;
|
int array_size = 0;
|
||||||
|
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
bool temp_error = false;
|
||||||
|
uint32_t datatype_flag;
|
||||||
|
|
||||||
|
if (!uniform) {
|
||||||
|
datatype_flag = CF_VARYING_TYPE;
|
||||||
|
keyword_completion_context = CF_INTERPOLATION_QUALIFIER | CF_PRECISION_MODIFIER | datatype_flag;
|
||||||
|
|
||||||
|
if (_lookup_next(next)) {
|
||||||
|
if (is_token_interpolation(next.type)) {
|
||||||
|
keyword_completion_context ^= (CF_INTERPOLATION_QUALIFIER | datatype_flag);
|
||||||
|
} else if (is_token_precision(next.type)) {
|
||||||
|
keyword_completion_context ^= (CF_PRECISION_MODIFIER | datatype_flag);
|
||||||
|
} else if (is_token_datatype(next.type)) {
|
||||||
|
keyword_completion_context ^= datatype_flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
datatype_flag = CF_UNIFORM_TYPE;
|
||||||
|
keyword_completion_context = CF_PRECISION_MODIFIER | datatype_flag;
|
||||||
|
|
||||||
|
if (_lookup_next(next)) {
|
||||||
|
if (is_token_precision(next.type)) {
|
||||||
|
keyword_completion_context ^= (CF_PRECISION_MODIFIER | datatype_flag);
|
||||||
|
} else if (is_token_datatype(next.type)) {
|
||||||
|
keyword_completion_context ^= datatype_flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
if (is_token_interpolation(tk.type)) {
|
if (is_token_interpolation(tk.type)) {
|
||||||
if (uniform) {
|
if (uniform) {
|
||||||
_set_error(RTR("Interpolation qualifiers are not supported for uniforms."));
|
_set_error(RTR("Interpolation qualifiers are not supported for uniforms."));
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
temp_error = true;
|
||||||
|
#else
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
}
|
}
|
||||||
interpolation = get_token_interpolation(tk.type);
|
interpolation = get_token_interpolation(tk.type);
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (keyword_completion_context & CF_INTERPOLATION_QUALIFIER) {
|
||||||
|
keyword_completion_context ^= CF_INTERPOLATION_QUALIFIER;
|
||||||
|
}
|
||||||
|
if (_lookup_next(next)) {
|
||||||
|
if (is_token_precision(next.type)) {
|
||||||
|
keyword_completion_context ^= CF_PRECISION_MODIFIER;
|
||||||
|
} else if (is_token_datatype(next.type)) {
|
||||||
|
keyword_completion_context ^= datatype_flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (temp_error) {
|
||||||
|
return ERR_PARSE_ERROR;
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_token_precision(tk.type)) {
|
if (is_token_precision(tk.type)) {
|
||||||
precision = get_token_precision(tk.type);
|
precision = get_token_precision(tk.type);
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (keyword_completion_context & CF_INTERPOLATION_QUALIFIER) {
|
||||||
|
keyword_completion_context ^= CF_INTERPOLATION_QUALIFIER;
|
||||||
|
}
|
||||||
|
if (keyword_completion_context & CF_PRECISION_MODIFIER) {
|
||||||
|
keyword_completion_context ^= CF_PRECISION_MODIFIER;
|
||||||
|
}
|
||||||
|
if (_lookup_next(next)) {
|
||||||
|
if (is_token_datatype(next.type)) {
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shader->structs.has(tk.text)) {
|
if (shader->structs.has(tk.text)) {
|
||||||
|
@ -7855,6 +8100,9 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
|
|
||||||
if (tk.type != TK_IDENTIFIER && tk.type != TK_BRACKET_OPEN) {
|
if (tk.type != TK_IDENTIFIER && tk.type != TK_BRACKET_OPEN) {
|
||||||
|
@ -8226,6 +8474,9 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_GLOBAL_SPACE;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
completion_type = COMPLETION_NONE;
|
completion_type = COMPLETION_NONE;
|
||||||
} else { // varying
|
} else { // varying
|
||||||
ShaderNode::Varying varying;
|
ShaderNode::Varying varying;
|
||||||
|
@ -8291,6 +8542,13 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
is_struct = true;
|
is_struct = true;
|
||||||
struct_name = tk.text;
|
struct_name = tk.text;
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (_lookup_next(next)) {
|
||||||
|
if (next.type == TK_UNIFORM) {
|
||||||
|
keyword_completion_context = CF_UNIFORM_QUALIFIER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
if (!is_token_datatype(tk.type)) {
|
if (!is_token_datatype(tk.type)) {
|
||||||
_set_error(RTR("Expected constant, function, uniform or varying."));
|
_set_error(RTR("Expected constant, function, uniform or varying."));
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
|
@ -8319,6 +8577,10 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
prev_pos = _get_tkpos();
|
prev_pos = _get_tkpos();
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
bool unknown_size = false;
|
bool unknown_size = false;
|
||||||
bool fixed_array_size = false;
|
bool fixed_array_size = false;
|
||||||
|
|
||||||
|
@ -8555,11 +8817,22 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
|
|
||||||
constant.initializer = static_cast<ConstantNode *>(expr);
|
constant.initializer = static_cast<ConstantNode *>(expr);
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (constant.type == DataType::TYPE_BOOL) {
|
||||||
|
keyword_completion_context = CF_BOOLEAN;
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
//variable created with assignment! must parse an expression
|
//variable created with assignment! must parse an expression
|
||||||
Node *expr = _parse_and_reduce_expression(nullptr, constants);
|
Node *expr = _parse_and_reduce_expression(nullptr, constants);
|
||||||
if (!expr) {
|
if (!expr) {
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (constant.type == DataType::TYPE_BOOL) {
|
||||||
|
keyword_completion_context = CF_GLOBAL_SPACE;
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
if (expr->type == Node::TYPE_OPERATOR && static_cast<OperatorNode *>(expr)->op == OP_CALL) {
|
if (expr->type == Node::TYPE_OPERATOR && static_cast<OperatorNode *>(expr)->op == OP_CALL) {
|
||||||
OperatorNode *op = static_cast<OperatorNode *>(expr);
|
OperatorNode *op = static_cast<OperatorNode *>(expr);
|
||||||
for (int i = 1; i < op->arguments.size(); i++) {
|
for (int i = 1; i < op->arguments.size(); i++) {
|
||||||
|
@ -8691,31 +8964,88 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
if (tk.type == TK_PARENTHESIS_CLOSE) {
|
if (tk.type == TK_PARENTHESIS_CLOSE) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_CONST_KEYWORD | CF_FUNC_DECL_PARAM_SPEC | CF_PRECISION_MODIFIER | CF_FUNC_DECL_PARAM_TYPE; // eg. const in mediump float
|
||||||
|
|
||||||
|
if (_lookup_next(next)) {
|
||||||
|
if (next.type == TK_CONST) {
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
} else if (is_token_arg_qual(next.type)) {
|
||||||
|
keyword_completion_context = CF_CONST_KEYWORD;
|
||||||
|
} else if (is_token_precision(next.type)) {
|
||||||
|
keyword_completion_context = (CF_CONST_KEYWORD | CF_FUNC_DECL_PARAM_SPEC | CF_FUNC_DECL_PARAM_TYPE);
|
||||||
|
} else if (is_token_datatype(next.type)) {
|
||||||
|
keyword_completion_context = (CF_CONST_KEYWORD | CF_FUNC_DECL_PARAM_SPEC | CF_PRECISION_MODIFIER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
bool param_is_const = false;
|
bool param_is_const = false;
|
||||||
if (tk.type == TK_CONST) {
|
if (tk.type == TK_CONST) {
|
||||||
param_is_const = true;
|
param_is_const = true;
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (keyword_completion_context & CF_CONST_KEYWORD) {
|
||||||
|
keyword_completion_context ^= CF_CONST_KEYWORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_lookup_next(next)) {
|
||||||
|
if (is_token_arg_qual(next.type)) {
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
} else if (is_token_precision(next.type)) {
|
||||||
|
keyword_completion_context = (CF_FUNC_DECL_PARAM_SPEC | CF_FUNC_DECL_PARAM_TYPE);
|
||||||
|
} else if (is_token_datatype(next.type)) {
|
||||||
|
keyword_completion_context = (CF_FUNC_DECL_PARAM_SPEC | CF_PRECISION_MODIFIER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
ArgumentQualifier param_qualifier = ARGUMENT_QUALIFIER_IN;
|
ArgumentQualifier param_qualifier = ARGUMENT_QUALIFIER_IN;
|
||||||
if (tk.type == TK_ARG_IN) {
|
if (is_token_arg_qual(tk.type)) {
|
||||||
param_qualifier = ARGUMENT_QUALIFIER_IN;
|
bool error = false;
|
||||||
|
switch (tk.type) {
|
||||||
|
case TK_ARG_IN: {
|
||||||
|
param_qualifier = ARGUMENT_QUALIFIER_IN;
|
||||||
|
} break;
|
||||||
|
case TK_ARG_OUT: {
|
||||||
|
if (param_is_const) {
|
||||||
|
_set_error(vformat(RTR("The '%s' qualifier cannot be used within a function parameter declared with '%s'."), "out", "const"));
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
param_qualifier = ARGUMENT_QUALIFIER_OUT;
|
||||||
|
} break;
|
||||||
|
case TK_ARG_INOUT: {
|
||||||
|
if (param_is_const) {
|
||||||
|
_set_error(vformat(RTR("The '%s' qualifier cannot be used within a function parameter declared with '%s'."), "inout", "const"));
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
param_qualifier = ARGUMENT_QUALIFIER_INOUT;
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
} else if (tk.type == TK_ARG_OUT) {
|
#ifdef DEBUG_ENABLED
|
||||||
if (param_is_const) {
|
if (keyword_completion_context & CF_CONST_KEYWORD) {
|
||||||
_set_error(vformat(RTR("The '%s' qualifier cannot be used within a function parameter declared with '%s'."), "out", "const"));
|
keyword_completion_context ^= CF_CONST_KEYWORD;
|
||||||
|
}
|
||||||
|
if (keyword_completion_context & CF_FUNC_DECL_PARAM_SPEC) {
|
||||||
|
keyword_completion_context ^= CF_FUNC_DECL_PARAM_SPEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_lookup_next(next)) {
|
||||||
|
if (is_token_precision(next.type)) {
|
||||||
|
keyword_completion_context = CF_FUNC_DECL_PARAM_TYPE;
|
||||||
|
} else if (is_token_datatype(next.type)) {
|
||||||
|
keyword_completion_context = CF_PRECISION_MODIFIER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
if (error) {
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
param_qualifier = ARGUMENT_QUALIFIER_OUT;
|
|
||||||
tk = _get_token();
|
|
||||||
} else if (tk.type == TK_ARG_INOUT) {
|
|
||||||
if (param_is_const) {
|
|
||||||
_set_error(vformat(RTR("The '%s' qualifier cannot be used within a function parameter declared with '%s'."), "inout", "const"));
|
|
||||||
return ERR_PARSE_ERROR;
|
|
||||||
}
|
|
||||||
param_qualifier = ARGUMENT_QUALIFIER_INOUT;
|
|
||||||
tk = _get_token();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DataType param_type;
|
DataType param_type;
|
||||||
|
@ -8727,6 +9057,23 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
if (is_token_precision(tk.type)) {
|
if (is_token_precision(tk.type)) {
|
||||||
param_precision = get_token_precision(tk.type);
|
param_precision = get_token_precision(tk.type);
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (keyword_completion_context & CF_CONST_KEYWORD) {
|
||||||
|
keyword_completion_context ^= CF_CONST_KEYWORD;
|
||||||
|
}
|
||||||
|
if (keyword_completion_context & CF_FUNC_DECL_PARAM_SPEC) {
|
||||||
|
keyword_completion_context ^= CF_FUNC_DECL_PARAM_SPEC;
|
||||||
|
}
|
||||||
|
if (keyword_completion_context & CF_PRECISION_MODIFIER) {
|
||||||
|
keyword_completion_context ^= CF_PRECISION_MODIFIER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_lookup_next(next)) {
|
||||||
|
if (is_token_datatype(next.type)) {
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
is_struct = false;
|
is_struct = false;
|
||||||
|
@ -8769,7 +9116,9 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
if (param_precision != PRECISION_DEFAULT && _validate_precision(param_type, param_precision) != OK) {
|
if (param_precision != PRECISION_DEFAULT && _validate_precision(param_type, param_precision) != OK) {
|
||||||
return ERR_PARSE_ERROR;
|
return ERR_PARSE_ERROR;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_UNSPECIFIED;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
tk = _get_token();
|
tk = _get_token();
|
||||||
|
|
||||||
if (tk.type == TK_BRACKET_OPEN) {
|
if (tk.type == TK_BRACKET_OPEN) {
|
||||||
|
@ -8853,11 +9202,16 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
|
||||||
|
|
||||||
current_function = name;
|
current_function = name;
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_BLOCK;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
Error err = _parse_block(func_node->body, builtins);
|
Error err = _parse_block(func_node->body, builtins);
|
||||||
if (err) {
|
if (err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
keyword_completion_context = CF_GLOBAL_SPACE;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
if (func_node->return_type != DataType::TYPE_VOID) {
|
if (func_node->return_type != DataType::TYPE_VOID) {
|
||||||
BlockNode *block = func_node->body;
|
BlockNode *block = func_node->body;
|
||||||
if (_find_last_flow_op_in_block(block, FlowOperation::FLOW_OP_RETURN) != OK) {
|
if (_find_last_flow_op_in_block(block, FlowOperation::FLOW_OP_RETURN) != OK) {
|
||||||
|
@ -9092,6 +9446,28 @@ Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_
|
||||||
shader = alloc_node<ShaderNode>();
|
shader = alloc_node<ShaderNode>();
|
||||||
_parse_shader(p_info.functions, p_info.render_modes, p_info.shader_types);
|
_parse_shader(p_info.functions, p_info.render_modes, p_info.shader_types);
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
// Adds context keywords.
|
||||||
|
if (keyword_completion_context != CF_UNSPECIFIED) {
|
||||||
|
int sz = sizeof(keyword_list) / sizeof(KeyWord);
|
||||||
|
for (int i = 0; i < sz; i++) {
|
||||||
|
if (keyword_list[i].flags == CF_UNSPECIFIED) {
|
||||||
|
break; // Ignore hint keywords (parsed below).
|
||||||
|
}
|
||||||
|
if (keyword_list[i].flags & keyword_completion_context) {
|
||||||
|
if (keyword_list[i].excluded_shader_types.has(shader_type_identifier)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!keyword_list[i].functions.is_empty() && !keyword_list[i].functions.has(current_function)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ScriptLanguage::CodeCompletionOption option(keyword_list[i].text, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
|
||||||
|
r_options->push_back(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
switch (completion_type) {
|
switch (completion_type) {
|
||||||
case COMPLETION_NONE: {
|
case COMPLETION_NONE: {
|
||||||
//do nothing
|
//do nothing
|
||||||
|
|
|
@ -756,6 +756,7 @@ public:
|
||||||
static bool is_token_interpolation(TokenType p_type);
|
static bool is_token_interpolation(TokenType p_type);
|
||||||
static DataInterpolation get_token_interpolation(TokenType p_type);
|
static DataInterpolation get_token_interpolation(TokenType p_type);
|
||||||
static bool is_token_precision(TokenType p_type);
|
static bool is_token_precision(TokenType p_type);
|
||||||
|
static bool is_token_arg_qual(TokenType p_type);
|
||||||
static DataPrecision get_token_precision(TokenType p_type);
|
static DataPrecision get_token_precision(TokenType p_type);
|
||||||
static String get_precision_name(DataPrecision p_type);
|
static String get_precision_name(DataPrecision p_type);
|
||||||
static String get_datatype_name(DataType p_type);
|
static String get_datatype_name(DataType p_type);
|
||||||
|
@ -870,6 +871,9 @@ private:
|
||||||
struct KeyWord {
|
struct KeyWord {
|
||||||
TokenType token;
|
TokenType token;
|
||||||
const char *text;
|
const char *text;
|
||||||
|
uint32_t flags;
|
||||||
|
const Vector<String> excluded_shader_types;
|
||||||
|
const Vector<String> functions;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const KeyWord keyword_list[];
|
static const KeyWord keyword_list[];
|
||||||
|
@ -920,6 +924,7 @@ private:
|
||||||
int char_idx = 0;
|
int char_idx = 0;
|
||||||
int tk_line = 0;
|
int tk_line = 0;
|
||||||
|
|
||||||
|
StringName shader_type_identifier;
|
||||||
StringName current_function;
|
StringName current_function;
|
||||||
bool last_const = false;
|
bool last_const = false;
|
||||||
StringName last_name;
|
StringName last_name;
|
||||||
|
@ -972,6 +977,7 @@ private:
|
||||||
|
|
||||||
Token _make_token(TokenType p_type, const StringName &p_text = StringName());
|
Token _make_token(TokenType p_type, const StringName &p_text = StringName());
|
||||||
Token _get_token();
|
Token _get_token();
|
||||||
|
bool _lookup_next(Token &r_tk);
|
||||||
|
|
||||||
ShaderNode *shader = nullptr;
|
ShaderNode *shader = nullptr;
|
||||||
|
|
||||||
|
@ -1029,6 +1035,10 @@ private:
|
||||||
StringName completion_struct;
|
StringName completion_struct;
|
||||||
int completion_argument = 0;
|
int completion_argument = 0;
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
uint32_t keyword_completion_context;
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
const Map<StringName, FunctionInfo> *stages = nullptr;
|
const Map<StringName, FunctionInfo> *stages = nullptr;
|
||||||
|
|
||||||
bool _get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier);
|
bool _get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier);
|
||||||
|
|
Loading…
Reference in a new issue