一个GLSL Shader的格式化算法(LALR解析器)

news/2024/12/27 2:13:14/文章来源:https://www.cnblogs.com/bitzhuwei/p/18631231

一个GLSL Shader的格式化算法(LALR解析器)

在进行OpenGL程序开发时,我需要自行解析`string`类型的Shader代码,抽取出里面的某些变量名和subroutine名。

由于找不到可用的GLSL Shader解析器,就照着虎书(《现代编译原理-c语言描述》)自己写了个LALR Generator,实际上包含了(词法分析器+语法分析器+格式化框架)的(LR(0)、SLR(1)、LALR(1)、LR(1))全自动生成,支持yacc的优先级指令和Shift/Reduce、Reduce/Reduc冲突解决功能。

本文的GLSL Shader的格式化工具(下载链接在这里https://files.cnblogs.com/files/bitzhuwei/GLSL.Formatter.net8.0.rar?t=1735122490&download=true ),就是用我的LALR Generator,根据GLSL4.60.8的文法生成的,如下所述。

GLSL4.60.8的文法
#extractor <GLSL4.60.8.st.ext>translation_unit :external_declaration| translation_unit external_declaration;
external_declaration :function_definition| declaration| ';';
function_definition :function_prototype compound_statement/* compound_statement_no_new_scope */;
variable_identifier :'identifier';
primary_expression :variable_identifier| 'intConstant'| 'uintConstant'| 'floatConstant'| 'boolConstant'| 'doubleConstant'| '(' expression ')';
postfix_expression :primary_expression| postfix_expression '[' integer_expression ']'| function_call| postfix_expression '.' 'identifier' // FIELD_SELECTION| postfix_expression '++'| postfix_expression '--';
integer_expression :expression;
function_call :function_call_or_method;
function_call_or_method :function_call_generic;
function_call_generic :function_call_header_with_parameters ')'| function_call_header_no_parameters ')';
function_call_header_no_parameters :function_call_header 'void'| function_call_header;
function_call_header_with_parameters :function_call_header assignment_expression| function_call_header_with_parameters ',' assignment_expression;
function_call_header :function_identifier '(';
function_identifier :type_specifier| postfix_expression;
unary_expression :postfix_expression| '++' unary_expression| '--' unary_expression| unary_operator unary_expression;
unary_operator :'+'| '-'| '!'| '~';
multiplicative_expression :unary_expression| multiplicative_expression '*' unary_expression| multiplicative_expression '/' unary_expression| multiplicative_expression '%' unary_expression;
additive_expression :multiplicative_expression| additive_expression '+' multiplicative_expression| additive_expression '-' multiplicative_expression;
shift_expression :additive_expression| shift_expression '<<' additive_expression| shift_expression '>>' additive_expression;
relational_expression :shift_expression| relational_expression '<' shift_expression| relational_expression '>' shift_expression| relational_expression '<=' shift_expression| relational_expression '>=' shift_expression;
equality_expression :relational_expression| equality_expression '==' relational_expression| equality_expression '!=' relational_expression;
and_expression :equality_expression| and_expression '&' equality_expression;
exclusive_or_expression :and_expression| exclusive_or_expression '^' and_expression;
inclusive_or_expression :exclusive_or_expression| inclusive_or_expression '|' exclusive_or_expression;
logical_and_expression :inclusive_or_expression| logical_and_expression '&&' inclusive_or_expression;
logical_xor_expression :logical_and_expression| logical_xor_expression '^^' logical_and_expression;
logical_or_expression :logical_xor_expression| logical_or_expression '||' logical_xor_expression;
conditional_expression :logical_or_expression| logical_or_expression '?' expression ':' assignment_expression;
assignment_expression :conditional_expression| unary_expression assignment_operator assignment_expression;
assignment_operator :'='| '*='| '/='| '%='| '+='| '-='| '<<='| '>>='| '&='| '^='| '|=';
expression :assignment_expression| expression ',' assignment_expression;
constant_expression :conditional_expression;
declaration :function_prototype ';'| init_declarator_list ';'| 'precision' precision_qualifier type_specifier ';'| type_qualifier 'identifier' '{' struct_declaration_list '}' ';'| type_qualifier 'identifier' '{' struct_declaration_list '}' 'identifier' ';'| type_qualifier 'identifier' '{' struct_declaration_list '}' 'identifier' array_specifier ';'| type_qualifier ';'| type_qualifier 'identifier' ';'| type_qualifier 'identifier' identifier_list ';';
identifier_list :',' 'identifier'| identifier_list ',' 'identifier';
function_prototype :function_declarator ')';
function_declarator :function_header| function_header_with_parameters;
function_header_with_parameters :function_header parameter_declaration| function_header_with_parameters ',' parameter_declaration;
function_header :fully_specified_type 'identifier' '(';
parameter_declarator :type_specifier 'identifier'| type_specifier 'identifier' array_specifier;
parameter_declaration :type_qualifier parameter_declarator| parameter_declarator| type_qualifier parameter_type_specifier| parameter_type_specifier;
parameter_type_specifier :type_specifier;
init_declarator_list :single_declaration| init_declarator_list ',' 'identifier'| init_declarator_list ',' 'identifier' array_specifier| init_declarator_list ',' 'identifier' array_specifier '=' initializer| init_declarator_list ',' 'identifier' '=' initializer;
single_declaration :fully_specified_type| fully_specified_type 'identifier'| fully_specified_type 'identifier' array_specifier| fully_specified_type 'identifier' array_specifier '=' initializer| fully_specified_type 'identifier' '=' initializer;
fully_specified_type :type_specifier| type_qualifier type_specifier;
invariant_qualifier :'invariant';
interpolation_qualifier :'smooth'| 'flat'| 'noperspective';
layout_qualifier :'layout' '(' layout_qualifier_id_list ')';
layout_qualifier_id_list :layout_qualifier_id| layout_qualifier_id_list ',' layout_qualifier_id;
layout_qualifier_id :'identifier'| 'identifier' '=' constant_expression| 'shared';
precise_qualifier :'precise';
type_qualifier :single_type_qualifier| type_qualifier single_type_qualifier;
single_type_qualifier :storage_qualifier| layout_qualifier| precision_qualifier| interpolation_qualifier| invariant_qualifier| precise_qualifier;
storage_qualifier :'const'| 'in'| 'out'| 'inout'| 'centroid'| 'patch'| 'sample'| 'uniform'| 'buffer'| 'shared'| 'coherent'| 'volatile'| 'restrict'| 'readonly'| 'writeonly'| 'subroutine'| 'subroutine' '(' type_name_list ')';
type_name_list :'type_name'| type_name_list ',' 'type_name';
type_specifier :type_specifier_nonarray| type_specifier_nonarray array_specifier;
array_specifier :'[' ']'| '[' conditional_expression ']'| array_specifier '[' ']'| array_specifier '[' conditional_expression ']';
type_specifier_nonarray :'void'| 'float'| 'double'| 'int'| 'uint'| 'bool'| 'vec2'| 'vec3'| 'vec4'| 'dvec2'| 'dvec3'| 'dvec4'| 'bvec2'| 'bvec3'| 'bvec4'| 'ivec2'| 'ivec3'| 'ivec4'| 'uvec2'| 'uvec3'| 'uvec4'| 'mat2'| 'mat3'| 'mat4'| 'mat2x2'| 'mat2x3'| 'mat2x4'| 'mat3x2'| 'mat3x3'| 'mat3x4'| 'mat4x2'| 'mat4x3'| 'mat4x4'| 'dmat2'| 'dmat3'| 'dmat4'| 'dmat2x2'| 'dmat2x3'| 'dmat2x4'| 'dmat3x2'| 'dmat3x3'| 'dmat3x4'| 'dmat4x2'| 'dmat4x3'| 'dmat4x4'| 'atomic_uint'| 'sampler2D'| 'sampler3D'| 'samplerCube'| 'sampler2DShadow'| 'samplerCubeShadow'| 'sampler2DArray'| 'sampler2DArrayShadow'| 'samplerCubeArray'| 'samplerCubeArrayShadow'| 'isampler2D'| 'isampler3D'| 'isamplerCube'| 'isampler2DArray'| 'isamplerCubeArray'| 'usampler2D'| 'usampler3D'| 'usamplerCube'| 'usampler2DArray'| 'usamplerCubeArray'| 'sampler1D'| 'sampler1DShadow'| 'sampler1DArray'| 'sampler1DArrayShadow'| 'isampler1D'| 'isampler1DArray'| 'usampler1D'| 'usampler1DArray'| 'sampler2DRect'| 'sampler2DRectShadow'| 'isampler2DRect'| 'usampler2DRect'| 'samplerBuffer'| 'isamplerBuffer'| 'usamplerBuffer'| 'sampler2DMS'| 'isampler2DMS'| 'usampler2DMS'| 'sampler2DMSArray'| 'isampler2DMSArray'| 'usampler2DMSArray'| 'image2D'| 'iimage2D'| 'uimage2D'| 'image3D'| 'iimage3D'| 'uimage3D'| 'imageCube'| 'iimageCube'| 'uimageCube'| 'imageBuffer'| 'iimageBuffer'| 'uimageBuffer'| 'image1D'| 'iimage1D'| 'uimage1D'| 'image1DArray'| 'iimage1DArray'| 'uimage1DArray'| 'image2DRect'| 'iimage2DRect'| 'uimage2DRect'| 'image2DArray'| 'iimage2DArray'| 'uimage2DArray'| 'imageCubeArray'| 'iimageCubeArray'| 'uimageCubeArray'| 'image2DMS'| 'iimage2DMS'| 'uimage2DMS'| 'image2DMSArray'| 'iimage2DMSArray'| 'uimage2DMSArray'| struct_specifier| 'type_name';
precision_qualifier :'highp'| 'mediump'| 'lowp';
struct_specifier :'struct' 'type_name'/* 'identifier' */ '{' struct_declaration_list '}'| 'struct' '{' struct_declaration_list '}';
struct_declaration_list :struct_declaration| struct_declaration_list struct_declaration;
struct_declaration :type_specifier struct_declarator_list ';'| type_qualifier type_specifier struct_declarator_list ';';
struct_declarator_list :struct_declarator| struct_declarator_list ',' struct_declarator;
struct_declarator :'identifier'| 'identifier' array_specifier;
initializer :assignment_expression| '{' initializer_list '}'| '{' initializer_list ',' '}';
initializer_list :initializer| initializer_list ',' initializer;
declaration_statement :declaration;
statement :compound_statement| simple_statement;
simple_statement :declaration_statement| expression_statement| selection_statement| switch_statement| case_label| iteration_statement| jump_statement;
compound_statement :'{' '}'| '{' statement_list '}';/* merge into statement
statement_no_new_scope :compound_statement_no_new_scope| simple_statement;*//* merge into compound_statement
compound_statement_no_new_scope :'{' '}'| '{' statement_list '}';*/
statement_list :statement| statement_list statement;
expression_statement :';'| expression ';';
selection_statement :'if' '(' expression ')' selection_rest_statement;
selection_rest_statement :statement 'else' statement| statement;
condition :expression| fully_specified_type 'identifier' '=' initializer;
switch_statement :'switch' '(' expression ')' '{' switch_statement_list '}';
switch_statement_list :empty| statement_list;
case_label :'case' expression ':'| 'default' ':';
iteration_statement :'while' '(' condition ')' statement/* statement_no_new_scope */| 'do' statement 'while' '(' expression ')' ';'| 'for' '(' for_init_statement for_rest_statement ')' statement/* statement_no_new_scope */;
for_init_statement :expression_statement| declaration_statement;
conditionopt :condition| empty;
for_rest_statement :conditionopt ';'| conditionopt ';' expression;
jump_statement :'continue' ';'| 'break' ';'| 'return' ';'| 'return' expression ';'| 'discard' ';';// lexical statements// no need : 'struct' 'identifier' '{' struct_declaration_list '}'
// now I changed it into 'struct' 'type_name' '{' struct_declaration_list '}'
// only identifier next to 'struct' is a user-defined type and should be a 'type_name' token.
'type_name' %%<'struct'>[a-zA-Z_][a-zA-Z0-9_]*%%'intConstant' %%[-+]?[0-9]+%%
'intConstant' %%0x[0-9A-Fa-f]+%%
'uintConstant' %%[-+]?[0-9]+[uU]%%
'uintConstant' %%0x[0-9A-Fa-f]+[uU]%%
'floatConstant' %%[-+]?[0-9]+([.][0-9]+)?([Ee][-+]?[0-9]+)?[fF]%%
'boolConstant' %%true/[^a-zA-Z0-9_]%%
'boolConstant' %%false/[^a-zA-Z0-9_]%%
'doubleConstant' %%[-+]?[0-9]+([.][0-9]+)?([Ee][-+]?[0-9]+)?%%'identifier' %%[a-zA-Z_][a-zA-Z0-9_]*%%%grammarName   GLSL
%blockComment  on
%inlineComment on

有了解析器,就有了单词流List<Token>和语法树Node。面对GLSL代码的语法树,我们如何对源代码进行格式化呢?

格式化的目的是让源代码从书写上更适宜人类阅读。具体来说,格式化要做的事,只是增减某些空白符(空格、tab符、换行符)而已,不会修改具有语法意义的内容。

在C#中,

if (x >0) {t = x;x = 0;
}

会被格式化为

if (x > 0) {t = x; x = 0;
}

if (x > 0) {t = x;
x= 0;
}

会被格式化为

if (x > 0) {t = x;x = 0;
}

同样意义的源代码,格式化结果却不同。这有些复杂。

我们将问题拆开,一步一步来。

首先,我们忽略多行注释blockComment、单行注释inlineComment和源代码中原有的空白符。

此时,一个if(x>0){t=x;x=0;}就应当被格式化为:

if (x > 0) {t = x;x = 0;
}

也就是说,语法树中的每一部分,都可以自己判断出应当如何格式化。用递归的方式遍历语法树即可。

现在来考虑注释。注释可能出现在List<Token>(词法分析器的分析结果)里的任意位置上,属实调皮。于是就有这样一个想法,利用C#的yield return语法糖,我们执行下述算法:

遍历`List<Token>`:如果下一个要进行格式化的Token是注释,那么就在当前位置输出它并换行。如果下一个要进行格式化的Token不是注释,那么就用`yield return`的方式,在语法树上格式化地输出它(输出它+某些空白符)。

只要在语法树每输出一个Token时,都进行一次yield return(return什么无所谓),那么yield return就能和遍历List<Token>的过程同步完成。

现在能够带着注释格式化了,但是无法解决这样的情况:上一行和下一行代码之间有好几个空行(即换行符),上述格式化过程会无视之。

完全的格式化算法要继承上述方法的思路,并加强如下:

语法树的各类语法块`Node`分别实现自己内部的格式化办法。
语法块`Node`要根据上一级`Node`传递来的要求,在输出自己内部的第一个`Token`之前,先输出多少个换行符和空格。
语法块`Node`要告知自己的每个下一级`Node`,它希望这些子级先输出多少个换行符和空格。
每个`Token`根据(上级传递来的要求+它与前一个`Token`之间的位置关系(间隔多少空格和换行符))输出自己前面的空白符,而后输出自己的内容。

每个语法块`Node`都服从直接上级的要求,并对各个直接下级发出要求。通过这样接力的方式,就可以通过遍历一次语法树来输出格式化的代码了。

同学们可以下载链接中的工具查看效果,里面还有一些示例shader。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/858765.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

adb使用教程

谷歌官方出品用来控制安卓手机的工具1、作用打印日志定位bug稳定性测试运行设备的shell命令上传和下载文件安装和卸载设备上的应用等2、adb的安装配置Android开发官网下载ADB压缩包解压压缩包将ADB包放到根目录下将ADB路径加入到环境变量里3、用adb连接手机 进入开发者模式USB连…

中考阅读理解深入逻辑分析-005 A Tale of Bears and Belonging 熊的故事与归属感

文章正文 Dear Mr. Henshaw, ​ I finished Beggar Bears in two nights. It is a really good book. At first, I was surprised because it wasn’t funny like your other books, but then I got to thinking (you said readers should think) and decided a book …

PCIe扫盲——BDF与配置空间

前面的文章中介绍过,每一个PCIe设备可以只有一个功能(Function),即Fun0。也可以拥有最多8个功能,即多功能设备(Multi-Fun)。不管这个PCIe设备拥有多少个功能,其每一个功能都有一个唯一独立的配置空间(Configuration Space)与之对应。 和PCI总线一样,PCIe总线中的每一…

路由器透明代理

​1、下载OpenWrt 找各自路由型号----下载地址:https://firmware-selector.openwrt.org/​ 下载完后我们通过以下步骤让路由器进入刷机模式并准备好刷机:关闭路由器电源 按住复位键并接入电源,此时你会看到电源LED灯变成橙色等闪烁,接着会变为白色灯闪烁,此时可以放开复位…

SpringBoot 集成RabbitMQ

springboot集成MQ 配置文件配置类 发送者 消费者 调用

前端文档生成框架

背景 有时候写项目难免要用到文档框架。VitePress/VuePress 熟悉的vue,不必多说。 https://vitepress.dev/zh/ docsify 28.2k stars https://docsify.js.org/#/ 知名度很高。 docusaurus facebook出品。 https://docusaurus.io/zh-CN/ StoryBook 85k stars https://storybook.…

【YashanDB知识库】通过触发器复制varchar(4000 char)列的数据导致乱码

本文内容来自YashanDB官网,原文内容请见 https://www.yashandb.com/newsinfo/7802969.html?templateId=1718516 问题现象 某客户在使用触发器将varchar(4000 char)列的数据从表A复制到表B时,表B上列的数据与A上对应列的数据不同且为乱码。 如下截图为A的数据及使用触发器复制…

CMFCToolTipCtrl的AddTool导致内存增加

多次调用CMFCToolTipCtrl的AddTool会导致程序内存不断增加,尤其在循环中,因此需要AddTool之前,先进行判断,如果新文本与旧的不相同,才添加,并且添加之前先删除。需要注意的是,提示文本支持最大长度是MAX_TIP_TEXT_LENGTH.该宏在tooltip.cpp line:22定义,值为1024,文本…

09. 数字选择控件

一、数字选择控件数字选择控件提供了一对上下箭头,用户可以单击箭头选择数字,也可以直接输入。PySide6 中提供的数据选择控件主要有 QSpinBox(整数数字选择控件)和 QDoubleSpinBox(小数数字选择控件)。我们可以在终端中使用 pip 安装 pyside6 模块。 pip install pyside6…

Fiddler v5.0.2专业网络抓包工具简体中文版

点击上方蓝字关注我 前言 Fiddler抓包工具一个非常专业且已经翻译成中文的网络调试帮手。它就像一个超级监视器,能帮你看到电脑和互联网之间所有通过HTTP协议传输的信息。比如,当你在网上冲浪时,它可以记录下你和网站之间交换的所有“小纸条”,比如网页上的小饼干(cookie)…

01Thingsboard中actor和RuleEngine的启动流程

一、基本关系 1、单体服务2、微服务3、创建流程Reference 二、DefaultActorService DefaultActorService是整个规则引擎的初始化入口。 DefaultActorService会初始化一个使用actor模型的规则引擎,共分为2步:①创建actorSystem; ②处理应用初始化完成事件1、创建actorSystem …