编译原理实验二
实验二要求依据我们在实验一建立起来的语法分析树,进行语义分析。
过程概览
- 词法、语法分析,先得到一棵语法分析树;
- 对语法分析树进行遍历,进行语义分析,(如果没有错误)得到语义分析表;
上课讲的声明语句(即填表)例子,是将填表过程(SDT)融合在语法分析过程(Bison)中的。
而实验手册的意思是先有树,再单独分析;可是,分析的过程是需要用到SDT的哇!如果不把SDT融合在Bison里,那还得根据生成好的树去看每一步推导用了啥产生式,进而确定具体的动作。
怎么根据生成好的树去看每一步推导用了啥产生式
呢?==>仿照自顶向下的递归分析过程!
符号表
方案
链表(没能力的人是这样的呜呜)
符号表的表头
[等浏览完实验三要求再说]
名字
类型(变量?结构体声明?数组?函数?) 【注意补充类型】
特性信息(根据类型来哩!)
对于变量至少要记录变量名及其类型
对于函数至少要记录其返回类型、参数个数以及参数类型
相对地址
[链表么]下一项的地址
...
什么时候填表?
当分析到程序中的说明或定义语句时, 应将说明或定义的名字,以及与之有关的特性信息填入符号表中。
而在C--
文法中,说明或定义语句
恰是Def
和ExtDef
,所以——
当分析到
Def
和ExtDef
时,就要填表
更具体地说,是在声明全局变量、声明本地变量、声明结构体、声明结构体型的变量、声明函数的时候填表。
声明全局变量
ExtDef → Specifier ExtDecList SEMI
ExtDecList → VarDec
-->当这里导出来VarDec
时,就可以知道变量名字和类型,就能填表了。ExtDecList → VarDec COMMA ExtDecList
-->当这里导出来VarDec
时,就可以知道变量名字和类型,就能填表了。VarDec → ID
-->这个能知道变量名字和基类型了!VarDec → VarDec LB INT RB
-->这个出来之后,就能够构造数组类型了!需要把前一个VarDec的综合属性(基类型)声明本地变量
CompSt → LC DefList StmtList RC
Def → Specifier DecList SEMI
DecList → Dec
DecList → Dec COMMA DecList
Dec → VarDec
-->当这里导出来VarDec
时,就可以知道变量名字和类型,就能填表了。Dec → VarDec ASSIGNOP Exp
-->当这里导出来VarDec
时,就可以知道变量名字和类型,就能填表了。VarDec → ID
-->这个能知道变量名字和基类型了!VarDec → VarDec LB INT RB
-->这个出来之后,就能够构造数组类型了!需要把前一个VarDec的综合属性(基类型)声明结构体
StructSpecifier → STRUCT OptTag LC DefList RC
DefList → Def DefList
DefList → 空
Def → Specifier DecList SEMI
DecList → Dec
DecList → Dec COMMA DecList
Dec → VarDec
-->当这里导出来VarDec
时,就可以知道变量名字和类型,就能填表了。Dec → VarDec ASSIGNOP Exp
-->当这里导出来VarDec
时,就可以知道变量名字和类型,就能填表了。VarDec → ID
-->这个能知道变量名字和基类型了!VarDec → VarDec LB INT RB
-->这个出来之后,就能够构造数组类型了!需要把前一个VarDec的综合属性(基类型)声明结构体型变量
同全局/局部变量
声明函数
ExtDef → Specifier FunDec CompSt
--FunDec
出来后就能搞了FunDec → ID LP VarList RP
FunDec → ID LP RP
无论声明啥,都绕不过一个砍:Specifier
,提供了基类型
无论声明啥,只有遇到了(树中最底层的)VarDec
才能真·确定类型
什么时候查表?
正如实验1的难点在于和报错相关的
error
产生式书写,实验2的难点在于什么时候进行查表(ww)
填表前查表
在定义的时候--重复定义或声明结构体时结构体类型未定义
注:据假设4,所有变量的作用域都是全局的,即程序中声明的变量不能有重名的
关键看
VarDec
和Dec
,程序实现是在Dec
插入表,故在插表前查表即可FunDec → ID LP VarList RP
FunDec → ID LP RP
关键看
FunDec
,因为设计的是完成FunDec
后插表StructSpecifier → STRUCT OptTag LC DefList RC
StructSpecifier → STRUCT Tag
方案:查
Tag
域名重复的检测采用“亡羊补牢”的策略,等到FieldList全部出来后,再使用双指针算法进行遍历,报错并去掉重复节点。
Dec → VarDec ASSIGNOP Exp
重复好说,初始化就看这个产生式啦!
检查名字的种类是否与说明一致
检查表达式中各变量的类型是否一致
遇到
Exp
成分每当遇到语法单元
Exp
,说明该结点及其子结点们会对变量或者函数进行使用,这个时候应当查符号表以确认这些变量或者函数是否存在以及它们的类型是什么Exp --> ID
Exp --> ID LP Args RP
Exp --> ID LP RP
Exp → Exp ASSIGNOP Exp
Dec → VarDec ASSIGNOP Exp
Exp → Exp ASSIGNOP Exp
Dec → VarDec ASSIGNOP Exp
方案:常数、表达式和函数调用一般只有右值而没有左值,反过来也就是只有
Exp LB Exp RB
、Exp DOT ID
、LP Exp RP
、ID
行Exp → Exp AND Exp
Exp → Exp OR Exp
Exp → Exp RELOP Exp
Exp → Exp PLUS Exp
Exp → Exp MINUS Exp
Exp → Exp STAR Exp
Exp → Exp DIV Exp
Exp → MINUS Exp
Exp → NOT Exp
Stmt → RETURN Exp SEMI
方案:得看当前
Stmt
所在的CompSt
对应的函数的声明...(目前的想法是整一个临时变量来记录)Exp --> ID LP Args RP
对于这一条关注ArgsExp --> ID LP RP
Exp → Exp LB Exp RB
方案:看右部第一个
Exp
是不是数组类型Exp --> ID LP Args RP
Exp --> ID LP RP
Exp → Exp LB Exp RB
Exp → Exp DOT ID
方案:看右部第一个
Exp
是不是结构体Exp → Exp DOT ID
方案:看右部第一个
Exp
的Struct
的定义
生成目标代码时,需要查表以取得所需要的地址或者寄存器编号
细节
DefList
DefList会出现在三个地方
StructSpecifier → STRUCT OptTag LC DefList RC
CompSt → LC DefList StmtList RC
对于(1),我们期望能够返回一个FieldList
类型的综合属性,并且对于DefList
中的每一个声明的变量,我们不把它加入符号表;
对于(2),则没有必要返回FieldList
类型的综合属性,但是要把DefList
中的每一个声明出来的变量写入符号表。
Exp
Exp的类型,可以套用Type
,但是还需要加一个:是不是右值。因为右值的判断方法和Type
是不说话的。
函数内的变量调用,要过一遍函数的参数list