编译原理课程设计编译器Word格式文档下载.docx
《编译原理课程设计编译器Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《编译原理课程设计编译器Word格式文档下载.docx(23页珍藏版)》请在冰豆网上搜索。
2报告主体1
2.1设计目的:
1
2.2设计内容及要求:
2.2.1设计符号表1
2.2.2设计词法分析器1
2.2.3语法分析与中间代码产生器2
2.2.4优化器(选做)2
2.2.5目标代码生成器(选做)2
2.2.6测试范例:
2
2.3实现原理3
2.3.1符号表的设计3
2.3.2词法分析器的设计4
2.3.3语法/语义分析5
2.5测试数据17
2.6结果输出及分析19
2.7软件运行环境及限制28
2.8心得体会29
2.9参考文献30
3结论30
1问题的提出
在网络世界中,我们往往对功能强大的程序叹为观止。
而这些强大程序的背后是编译这些程序的编译软件,是这些编译软件承托起了这些功能强大的运行程序。
我们有不少的同志致其自身于无尽的运行程序上。
而只有很少的人搞编译程序。
这就是为什么中国的可运行程序满天飞,而编译程序却很少。
本课程设计就是在这方面的探索,为你解读编译程序的奥秘。
课程设计题目选择9个题目中的一个,要求根据自己的兴趣和能力,选择一个对自己意义甚大的题目。
课程设计基本原理是在实践上,实现我们在课程上学习到的理论知识。
通过理论联系实践,更好的掌握课本上的理论知识。
2报告主体
本次课程设计是作一个集词法分析、语法分析、语义分析和中间代码生成于一体的编译器,它集中了《编译原理》里的几乎所有的思想。
不但加深了学生对编译方法的理解,而且能对学生的编程能力起到进一步提高的作用,培养学生的程序设计风格。
通过某种可视化编程语言的应用,具备初步的Windows环境下的编程思想。
2.2.1设计符号表
确定符号表的组织方式,一般应包括名字栏和信息栏,其中名字栏作为关键字。
要考虑能够存储有关名字的信息,并可以高效地完成如下操作:
1.查找:
根据给定的名字,在符号表中查找其信息。
如果该名字在符号表中不存在,则将其加入到符号表中,否则返回指向该名字的指针;
2.删除:
从符号表中删除给定名字的表项。
2.2.2设计词法分析器
设计各单词的状态转换图,并为不同的单词设计种别码。
将词法分析器设计成供语法分析器调用的子程序。
功能包括:
1.
具备预处理功能。
将不翻译的注释等符号先滤掉,只保留要翻译的符号串,即要求设计一个供词法分析调用的预处理子程序;
2.
能够拼出语言中的各个单词;
3.
将拼出的标识符填入符号表;
4.
返回(种别码,属性值)。
2.2.3语法分析与中间代码产生器
要求用LL1、递归下降、算符优先分析法、SLR分析法等方法之一或若干方法,实现对表达式、各种说明语句、控制语句进行语法分析。
若语法正确,则用语法制导翻译法进行语义翻译:
对说明语句,要求将说明的各符号记录到相应符号表中;
对可执行语句,应产生出四元式中间代码并填写到三地址码表中;
若语法错误,要求指出出错性质和出错位置(行号)。
出错处理应设计成一个出错处理子程序
2.2.4优化器
局部优化:
设计出划分基本块的算法,在每一个基本块中实现:
合并已知量、删除多余运算和删除无用赋值三种局部优化。
设计构造基本块的DAG图的算法,以及将DAG图还原实现基本块的优化的算法。
循环优化:
只做一重循环优化,完成代码外提,强度削弱和删除归纳变量等三种优化。
要求实现while循环和for循环语句的优化。
2.2.5目标代码生成器
能完成指定寄存器个数的情况下将一中间代码程序段翻译成汇编语言目标代码(汇编指令应包括加、减、乘、除),要求指令条数最少的情况下,尽量使用寄存器,尽量少访问内存,这样才能做到运行效率高。
proceduretest;
varb,c,i:
integer;
begin
b:
=1;
ifa>
bthen
c:
=a+b
else
=a-b;
fori:
=0to3do
begin
c:
=i;
end;
end;
2.3实现原理
2.3.1符号表的设计
1.符号表的结构
classSymbol
{
publicWordNamename;
publicStringBuffertype;
publicStringBufferkind;
publicStringBufferval;
publicintaddr;
publicSymbol()
{name=newWordName();
type=newStringBuffer();
kind=newStringBuffer();
val=newStringBuffer();
}
}
符号表的每一项是symbol类的一个对象,即符号表是symbol类的对象数组。
其中WordName又是一个类,它对应名字栏,它有两个属性start和length,分别存放标志符在字符串中的起始地址和长度。
type、kind、val和addr分别对应符号表中的其他四项,它们共同组成信息栏。
classWordName
{
publicintstart;
publicintlength;
2.查找
当前字符串的长度就是将要入字符串的标志符的起始位置,即名字栏中的start,标志符的长度对应名字栏中的length,varCount是符号表的即将生成的符号表项的下标,碰到一个标志符,如test,我们就在符号表中从头到尾循环,利用名字栏中的start和length从字符串(StringBuffer类型的一个对象alphabet)中取出其对应得标志符,看它是否为test,如果符号表中有test,就返回循环是所用的计数器i,没有就返回-1;
从字符串中取自字符串的方法是
alphabet.substring(symbolList[i].name.start,symbolList[i].name.start+symbolList[i].name.length,用方法equals()来判断其是否一样。
实现此功能的是wordExist()。
程序中出现的数字,也要查符号表,看其是否在符号表中存在,在符号表中从头到尾查找,如果start和length都为零,而且val值和当前数字相同,就返回循环计数器i,如果没有找到满足条件的,就返回-1;
实现此功能的方法(即函数)是numberExist().
2.3.2词法分析器的设计
1.处理标志符或关键字:
把刚才的字母保存在currentToken的name变量中,接着读入下一个字符,这个字符如果是字母或下划线,则将其继续追加在name变量中,如此循环直到碰到其他字符。
这样就把程序中一个连续的字符串保存在一个变量中,接着判断这个字符串是不是关键字,依次与keyWord数组的关键字比较,循环计数器i就是keyWord[i]的种别码。
如果是关键字则将currentToken的code置为i,address置为-1。
如果不是关键字,则查找符号表,如果wordExist()函数返回的是-1,则表示符号表中还没有此标志符,将address置为addrCount,否则将address置为wordExist()的返回值。
然后调用outPut()函数。
outPut()函数的功能是输出token串,并将token串填入数组,如果它在符号表中不存在,则还要将其填在符号表中。
清空currentToken中各个属性的值。
2.处理数字:
用循环将数字保存在currentToken的name变量中,直到碰到小数点或其他字符,如果碰到了小数点,我们将标志flag置成1,表明这可能是一个小数。
这时需要判断下一个是否是字母,如果是字母,则丢弃接下来的字母和数字,只保留前面的数字。
如果是小数点造成的循环结束,则再读入一个字符,如果这个字符是数字,则将刚才的小数点保存起来,否则给出小数点后出错的提示。
在这个字符是数字的情况下,将这个数字以及后面的数字保存起来。
循环结束后,我们将其编码为36(实数),造成这次循环结束的仍然可能是小数点或字母,不管是哪一种情况,都要丢掉后面的字母和数字。
如果第一次循环不是小数点造成的,我们将其编码35(整数)。
接下来考虑的入口地址,为了防止造成符号表的空间的浪费,我们从符号表中查找有没有这个数字的记录。
如果有,返回的入口地址就是这个数字的属性值。
此时,这个数字的token串已经完成。
最后调用outPut()函数输出并保存token串,并根据是否在已符号表中而填入符号表。
3.处理注释和除号:
在字符为/时,我们调用此程序。
我们在读入下一个字符,如果这个字符是*,这表示后面是注释,用循环读入后面的字符,如果碰到*,表示注释将要结束,下一个字符应该是/,如果不是/,则注释出错。
而如果/后面的字符不是*,则/当作除号处理,将/保存到currentToken的name中,属性值设置为48,调用outPut()函数。
4.处理字符常数:
在字符为‘时我们调用此程序,用循环继续读入后面的字符,碰到’时结束。
设置种别码为37,查找符号表是否已经有了该字符常数的记录,如果有,将其属性值设置为返回的入口地址。
如果没有,将其属性值设置为addrCount(符号表的记录的计数器),同时将addrCount加一。
5.处理其他字符:
用switch()对每一种字符进行匹配,不同的字符给于不同的编码,入口地址均为-1。
调用outPut()输出和保存token串,然后获得下一个字符。
2.3.3语法/语义分析
此处是将语法分析和语义分析和在一起进行的。
由于词法分析已经将符号表和token串保存在数组中了,语法和语义分析时将tokenList数组中一个一个的调出token来分析,并根据需要修改存在symbolList数组的符号表。
首先第一个token应该是procedure(21,_)或program(22,_),第二个token应该是标志符(34,0),第三个token应该是分号(52,_),此时应该将行计数器lineOfPro加一,下一个token应该是var(31,_),然后进入说明语句分析的循环,declare()方法(函数)将会分析一行说明语句,因为说明语句可能多行,所以采用循环调用的declare()的方法。
说明语句分析完,然后进行可执行语句的分析,beginAnalyse()将完成此功能,遇到错误它将返回–1。
1.说明语句的分析:
首先,将说明语句的符号按照出现的先后顺序入栈,如a,b,c:
将三个字母、两个逗号、一个分号、integer和一个分号按照a、,、b、,、c、:
、integer、;
的顺序压入堆栈,然后从左向右扫描,碰到标识符跳过,碰到逗号跳过,碰到冒号表明说明语句即将结束,用switch()语句对冒号之后的那个字符进行匹配,根据这个字符是char、integer、bool、real中的哪一个来将前面变量再符号表中信息栏的kind和type置成不同的值。
完成此项任务后,移动指针,此时指针应该指向分号,如果是分号则成功返回,不是分号给出错误。
说明语句后紧接的是以begin开头的赋值语句、布尔表达式语句、if语句、while语句、repeat语句以及for语句,下面将一一加以介绍。
2.赋值语句(