经过一系列算法摸索后,我们终于要进入C语言编译器开发的进程,这一节,我么你的目的是实现C语言的变量声明语句的解析算法,要解析的语句形式如下:
long int *x,y;
完成变量声明的解析后,我们便进入符号表和类型系统的研究。
自底向上的语法解析算法中,shift/reduce算法步骤解析
我们c语言编译器所采用的语法即系算法将依上一章的自底向上语法解析算法,在上一章算法中,我们通过形成语法解析状态机,进而构造语法解析跳转表, 从而实现语法解析的自动化,我们要实现c语言编译器基于上一章的代码,只不过把语法规则换成c语言的语法而已,在签名的算法拆解中,构造了状态机后,某些节点会存在shift/reduce矛盾,这个矛盾的处理办法,我再前面的讲解中有些问题,这里进一步澄清一下:
1 [S->a.rB,C]
2 r->r1.
r是一个非终结符,a,B代表0个或多个终结符或非终结符的集合。
对于上面的两个表达式,如果当前语法解析到表达式2,而且符号.处于表达式2 的末尾,那么当前解析器收到下一个字符时,采取shift操作还是reduce操作呢,如果要采取reduce操作,那么r->r1 reduce后,解析器进入到表达式1所代表的的节点,如果解析进程要能够顺利地进行下去的话,当前的输入字符必须符号B的规则,也就是当前输入字符必须属于First(B)。
具体来说,假定B对应的是一个终结符+,也就是:
S->a.r+
那么如果当前输入字符正好是“+”的话,那么当解析器处于表达式2时,做reduce操作是合理的,如果当前输入字符不是“+”,那么如果是reduce操作的话就无法继续进行下去了,因此做shift操作就是合理的选择。
因此表达式2的look ahead集合是First(B),如果B是空,那么2的look ahead集合就等于C,如果B是nullable,那么表达式2的look ahead集合就是First(B) 冰 C
C语言变量声明语句的解析语法:
1、program->ext_def_list
2、ext_def_list->ext_def_list ext_def
3、ext_def_list->ext_def
4、ext_def->opt_specifiers ext_dec_list SEMI
| opt_specifiers SEMI
5、ext_dec_list->ext_decl
| ext_decl_list COMMA ext_decl
6、ext_decl->var_decl
7、opt_specifiers->specifiers
| EMPTY
8、 specifiers->type_or_class
| specifiers type_or_class
9、type_or_class->type_specifier
9、type_specifier->TYPE
10、new_name->NAME
11、var_decl->new_name | star var_decl
上面的语法中,大写的表示终结符,TYPE是C语言数据类型的关键字例如long int float 等对应的token,NAME是所有Cyuyan变量名token,STAR代表的是符号*,接下来我们看语句:
long int *x,y;
首先执行一次shift操作,把对应的token TYPE压入解析栈:
TYPE
通过type_specifier->TYPE 进行reduce
type_specifier
通过type_or_class->type_specifier进行reduce
type_or_class
通过specifiers->type_or_class进行reduce
specifiers
接着把int对应的token TYPEshift进来
specifiers TYPE
通过type_or_class ->TYPE进行reduce
specifiers type_or_class
通过specifiers->specifiers type_or_class进行reduce
specifiers
通过opt_specifiers->specifiers进行reduce
opt_specifiers
接着分别把*和x对应的token shift到解析栈中
opt_specifiers STAR NAME
接着通过new_name ->NAME进行reduce
opt_specifiers STAR new_name
通过var-decl->STAR var_decl进行reduce
opt_specifiers var_decl
通过ext-decl->var_decl进行reduce
opt_specifiers ext_decl
再通过ext_decl_list->ext_decl
opt_specifiers ext_decl_list
接着再把逗号和y shift进去
opt_specifiers ext_decl_list COMMA NAME
然后经过new_name->NAME 以及var_decl->new_name 进行reduce
opt_specifiers ext_decl_list COMMA var_decl
再通过ext_decl_list=>ext_decl_list COMMA ext_decl 进行reduce
opt_specifiers ext_decl_list
接着把SEMI shift进去
opt_specifiers ext_decl_list SEMI
通过 ext_def -> opt_specifiers ext_list SEMI 进行reduce, 这样堆栈的头三个元素就出栈了:
ext_def
通过 ext_def_list -> ext_def 进行reduce:
ext_def_list
再通过 program -> ext_def_list 进行 reduce:
program
由于全句非终结符被压入堆栈,由此解析结束,语句能被我们的语法接受。
在解析过程中,reduce进行了之后,便是代码生成的适当时机,代码生成出来 将高级语言转换为低级语言之外,还有很重要的一部分是根据语言的类型系统创建符号表,符号表和类型系统是编译原理中,极具技术福鞥厚度,难度,和趣味的一部分。
走到这里,大家是否觉得,编译原理是一门博大精深的逻辑知识系统。随着学习和研究的不断推进,我越来越为编译原理各种算法的巧妙性,完备性所折服,不得不感慨,那些大牛前辈长得是什么大脑,怎么会构建出如此精妙逻辑系统,站在这些巨人的肩膀上,扩展了我们的知识视野,也体会到了“风物长宜放眼量”的愉悦