编译原理实习 2 语义分析

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

编译原理实习2 语义分析

编译原理课程的实习内容是为一个小型的类C语言(C-­‐-­‐)实现一个编译器。如果你顺利完成了本课程规定的实习任务,那么不仅你的编程能力将会得到大幅提高,而且你最终会得到一个比较完整的、能将C-­‐-­‐源代码转换成MIPS汇编代码的编译器,所得到的汇编代码可以在SPIM S imulator上运行。实习总共分为四个阶段:词法和语法分析、语义分析、中间代码生成和目标代码生成。每个阶段的输出是下一个阶段的输入,后一个阶段总是在前一个阶段的基础上完成,如下图所示:

实习2的任务是编写一个程序,该程序读入一个C-­‐-­‐源代码文件,在词法分析和语法分析之后,

对其进行语义分析和类型检查(C-­‐-­‐语言的词法和语法参见Grammar.pdf文件),并打印分析结果。与上一次实习不同的是,这次实习我们已经没有办法依靠任何工具了,所有的检查任务都必须要我们自己手写代码来完成。另外,虽说语义分析在整个编译器的实现中并不是难度最大的任务,但它却是最细致最琐碎的任务,你需要用心地设计符号表和变量类型的数据结构从而在多个方面完善你的编译器中的各种功能。

具体地说,你的程序需要对输入文件中的如下错误进行检查:

s♦变量在使用时未经定义(错误类型1)

s♦函数在调用时未经定义(错误类型2)

s♦变量经过重复定义(错误类型3)

s♦函数经过重复定义(错误类型4)

s♦赋值号两边表达式类型不匹配(错误类型5)。对于数组类型来说,同C语言一样,只要数组的基类型和维数相同我们即认为类型是匹配的,例如int a[10][2]和int b[5][3]即属于同一类型 s♦赋值号左边出现了一个只有右值的表达式(错误类型6)

s♦操作数类型不匹配(e.g. 整型变量与数组变量相加减)(错误类型7)

s♦return语句返回类型与函数本身的返回类型不匹配(错误类型8)

s♦函数调用时实参与形参的数目或者类型不匹配(错误类型9)

s♦对非数组型变量使用[](数组访问)操作符(错误类型10)

s♦对普通变量使用()(函数调用)操作符(错误类型11)

s♦数组访问操作符[]中出现非整数(例如a[1.5])(错误类型12)

另外,以下针对结构体的类型检查将作为可选内容(注意如果要引入结构体,仍需要保证上面所列的所有错误都能被检查出来):

s♦对非结构体型变量使用“.”操作符(错误类型13,选做内容1)

s♦访问结构体中未定义过的域(错误类型14,选做内容2)

s♦结构体中域名的重复定义,或者定义时对域进行了初始化(例如struct A { int a = 0;}就是非法的)(错误类型15,选做内容3)

s♦结构体名与前面定义过的某个结构体或者某个变量的名字重复(错误类型16,选做内容4)s♦直接使用未定义过的结构体来定义变量(错误类型17,选做内容5)

本次实验中,简单起见我们对C-­‐-­‐语言做如下假设,你可以认为这些就是C-­‐-­‐语言的特性:

s♦假设1:整型(int)变量不能与浮点型(float)变量相互赋值或者相互运算

s♦假设2:浮点型变量不能进行逻辑运算或者作为if和while语句的条件

s♦假设3:任何一个函数只能进行一次定义,无法进行声明

s♦假设4:所有语句的作用域都是全局的,即外层语句块中定义的变量不能在内层语句块中重复定义,内层语句块中定义的变量到了外层照样可以使用,不同函数体内定义的局部变量不能互相重名

s♦假设5:结构体类型间的等价机制采用名等价(name equivalence)的方式

s♦假设6:函数无法进行嵌套定义

其中假设5和6就是C语言本身的性质,还具备一定的合理性,不过前4条假设就完全是为了减少工作量而设置的了。作为选做内容,你可以尝试去掉假设3、4或者5并实现之: s♦函数除了在定义之外还可以进行声明。函数的定义不可以重复出现,但函数声明在相互一致的情况下可以重复出现。任一个函数无论声明与否,其定义必须在源文件中出现,否则编译器需要进行报错。因此一旦去掉假设3,你的程序需要额外检查如下错误:

n⏹函数进行了声明,但没有被定义(错误类型18,选做内容6)

n⏹函数的多次声明互相冲突(i.e. 函数名一致,但返回类型、形参数量或者形参类型不一致),或者声明与定义之间互相冲突(错误类型19,选做内容7)

由于C--语法本身并没有与函数声明相关的产生式,因此你在完成这个选做任务时,可能需要先对语法进行适当修改。修改的时候要特别留意:你的改动应该以不影响其他错误的检查为原则。

s♦变量的定义受可嵌套作用域的影响,外层语句块中定义的变量名可以在内层语句块中重复定义(但此时在内层语句块中就无法访问到外层语句块的同名变量),内层语句块中定义的变量到了外层语句块中就会消亡,不同函数体内定义的局部变量可以相互重名(选做内容8) s♦将类型等价机制更改为结构等价(structural equivalence)。例如,虽然名称不同,但两个结构体类型struct a{int x; float y;}和struct b{int y; float z;}仍然是等价的类型。注意在结构等价时不要将数组展开来判断,例如struct A {int a; struct {float f;int i;} b[10];}和struct B {struct { int i; float f;} b[10]; int b;}就不是等价的(选做内容9)

需要注意的是,由于在后面的实习中还会扩展甚至修改本次实习你已经写好的代码,因此保持一个良好的代码风格、系统地设计代码结构和各模块之间的接口等对于整个实习来讲可以说是相当重要的。

输入格式

程序的输入是一个文本文件,其中包含有C-­‐-­‐的源代码,该源代码中可能会有语义错误。你可以假设输入文件中已经不包含词法以及语法上的错误了。

本次实习要求你的程序能够接收一个输入文件名作为参数。例如,若你的程序名为cc、输入文件名为test1、程序和输入文件都位于当前目录下,那么在命令行下运行./cc t est1即可获得以test1作为输入文件的输出结果。

输出格式

本次实习要求你的程序打印到标准输出之上。

对于那些没有语义和类型错误的输入文件,要求你的程序不输出任何内容。

对于那些存在语义和类型错误的输入文件,你的程序应当输出相应的错误信息,这些信息包括出错的行号、错误类型以及说明文字,其格式为:

Error t ype [错误类型] a t l ine [行号]: [说明文字]

说明文字的内容没有具体要求,但是出错的行号和错误类型一定要写对,这是我们判断你输出的错误

相关文档
最新文档