编译原理实验二

jielahou大约 7 分钟

实验二要求依据我们在实验一建立起来的语法分析树,进行语义分析。

过程概览

  1. 词法、语法分析,得到一棵语法分析树
  2. 对语法分析树进行遍历,进行语义分析,(如果没有错误)得到语义分析表

上课讲的声明语句(即填表)例子,是将填表过程(SDT)融合在语法分析过程(Bison)中的。

而实验手册的意思是先有树,再单独分析;可是,分析的过程是需要用到SDT的哇!如果不把SDT融合在Bison里,那还得根据生成好的树看每一步推导用了啥产生式,进而确定具体的动作。

怎么根据生成好的树去看每一步推导用了啥产生式呢?==>仿照自顶向下的递归分析过程!

符号表

方案

链表(没能力的人是这样的呜呜)

符号表的表头

[等浏览完实验三要求再说]

  1. 名字

  2. 类型(变量?结构体声明?数组?函数?) 【注意补充类型】

  3. 特性信息(根据类型来哩!)

    对于变量至少要记录变量名及其类型

    对于函数至少要记录其返回类型、参数个数以及参数类型

  4. 相对地址

  5. [链表么]下一项的地址

  6. ...

什么时候填表?

分析到程序中的说明或定义语句时, 应将说明或定义的名字,以及与之有关的特性信息填入符号表中。

而在C--文法中,说明或定义语句恰是DefExtDef,所以——

分析到DefExtDef,就要填表

更具体地说,是在声明全局变量、声明本地变量、声明结构体、声明结构体型的变量、声明函数的时候填表。

  • 声明全局变量

    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 → DecDecList → Dec COMMA DecListDec → VarDec-->当这里导出来VarDec时,就可以知道变量名字和类型,就能填表了。 Dec → VarDec ASSIGNOP Exp-->当这里导出来VarDec时,就可以知道变量名字和类型,就能填表了。

    VarDec → ID-->这个能知道变量名字和基类型了! VarDec → VarDec LB INT RB-->这个出来之后,就能够构造数组类型了!需要把前一个VarDec的综合属性(基类型)

  • 声明结构体

    StructSpecifier → STRUCT OptTag LC DefList RC

    DefList → Def DefListDefList → 空

    Def → Specifier DecList SEMI

    DecList → DecDecList → Dec COMMA DecListDec → VarDec-->当这里导出来VarDec时,就可以知道变量名字和类型,就能填表了。 Dec → VarDec ASSIGNOP Exp-->当这里导出来VarDec时,就可以知道变量名字和类型,就能填表了。

    VarDec → ID-->这个能知道变量名字和基类型了! VarDec → VarDec LB INT RB-->这个出来之后,就能够构造数组类型了!需要把前一个VarDec的综合属性(基类型)

  • 声明结构体型变量

    同全局/局部变量

  • 声明函数

    ExtDef → Specifier FunDec CompSt--FunDec出来后就能搞了

    FunDec → ID LP VarList RPFunDec → ID LP RP

无论声明啥,都绕不过一个砍:Specifier,提供了基类型

无论声明啥,只有遇到了(树中最底层的)VarDec才能真·确定类型

什么时候查表?

正如实验1的难点在于和报错相关的error产生式书写,

实验2的难点在于什么时候进行查表(ww)

  • 填表前查表

    • 在定义的时候--重复定义或声明结构体时结构体类型未定义

      注:据假设4,所有变量的作用域都是全局的,即程序中声明的变量不能有重名的

      • 关键看VarDecDec,程序实现是在Dec插入表,故在插表前查表即可

      • FunDec → ID LP VarList RPFunDec → 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 RBExp DOT IDLP Exp RPID

    • 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 对于这一条关注Args

      Exp --> 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

      方案:看右部第一个ExpStruct的定义

  • 生成目标代码时,需要查表以取得所需要的地址或者寄存器编号

细节

DefList

DefList会出现在三个地方

  1. StructSpecifier → STRUCT OptTag LC DefList RC
  2. CompSt → LC DefList StmtList RC

对于(1),我们期望能够返回一个FieldList类型的综合属性,并且对于DefList中的每一个声明的变量,我们不把它加入符号表;

对于(2),则没有必要返回FieldList类型的综合属性,但是要把DefList中的每一个声明出来的变量写入符号表。

Exp

Exp的类型,可以套用Type,但是还需要加一个:是不是右值。因为右值的判断方法和Type是不说话的。

函数内的变量调用,要过一遍函数的参数list

Loading...