逆波兰式.docx
《逆波兰式.docx》由会员分享,可在线阅读,更多相关《逆波兰式.docx(16页珍藏版)》请在冰豆网上搜索。
逆波兰式
塔里木大学信息工程学院论文
编译原理课程设计
课目:
编译原理
学生姓名:
\
学号:
学生姓名
学号:
所属学院:
信息工程学院
班级:
设计任务书
课题名称
逆波兰式的生成
设计
目的
通过一周的课程设计,对逆波兰式的生成有深刻的理解,达到了巩固理论知识、锻炼实践能力、构建合理知识结构的目的。
实验环境
Windows2000以上操作系统,VisualC++6.0以上编译环境
任务要求
1.对算术表达式进行词法、语法、逆波兰式的分析;
2.编写代码,实现逆波兰式的生成;
3.撰写课程设计报告;
4.参加答辩。
工作进度计划
序号
起止日期
工作内容
1
理论辅导,搜集资料
2
编写代码,上机调试
3
撰写课程设计报告
4
答辩
4
完善报告
指导教师(签章):
年月日
摘要:
编译原理是计算机科学与技术专业最重要的一门专业基础课程,内容庞大,涉及面广,知识点多。
编译原理旨在介绍编译程序构造的一般原理和基本方法。
内容包括语言和文法、词法分析、语法分析、语法制导翻译、中间代码生成、存储管理、代码优化和目标代码生成。
它是计算机科学与技术专业最重要的一门专业基础课程,内容庞大,涉及面广,知识点多。
由于该课程教、学难度都非常大,往往费了大量时间而达不到预期教学效果俗语说:
学习的最好方法是实践。
本课程设计正是基于此,力求为学生提供一个理论联系实际的机会,通过布置一定难度的课题,要求学生独立完成。
我们这次课程设计的主要任务是编程实现对输入合法的中缀表达式进行词法分析、语法分析,构造相应的逆波兰式,计算后缀表达式的值输出结果。
比如中缀表达式:
C*(E+F),其后缀表达式为:
CEF+*。
逆波兰式也叫后缀表达式,即将运算符写在操作数之后。
通过实践,建立系统设计的整体思想,锻炼编写程序、调试程序的能力,学习文档编写规范,培养独立学习、吸取他人经验、探索前言知识的习惯,树立团队协作精神。
同时,课程设计可以充分弥补课堂教学及普通实验中知识深度与广度有限的缺陷,更好地帮助学生从全局角度把握课程体系。
关键字:
逆波兰式;语法分析;中缀表达式
1课设综述
1.1课设来源
在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,所以,这种表示法也称为中缀表示。
对中缀表达式的计值,并非按运算符出现的自然顺序来执行其中的各个运算,而是根据算符间的优先关系来确定运算的次序,此外,还应顾及括号规则。
因此,要从中缀表达式直接产生目标代码一般比较麻烦。
相对的,逆波兰式在计算机看来却是比较简单易懂的结构。
因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。
1.2设计意义
对于实现逆波兰式算法,难度并不大,但为什么要将看似简单的中缀表达式转换为逆波兰式,原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中缀表达式是非常复杂的结构。
相对的,逆波兰式在计算机看来却是比较简单易懂的结构。
因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。
在逆波兰式中,不存在运算符的优先级问题,也不存在任何括号,计算的顺序完全按照运算符出现的先后次序进行。
比中缀表达式的求值要简单得多。
1.3设计目标
编写程序,实现逆波兰式的生成和计算。
首先对输入的表达式进行词法分析,然后进行语法分析,最后进行逆波兰式的输出和计算。
过实践,建立系统设计的整体思想,锻炼编写程序、调试程序的能力,学习文档编写规范,培养独立学习、吸取他人经验、探索知识的习惯。
1.4遇到的问题
如何通过递归下降方法分析表达式,并且输出词法分析、语法分析过程及结果。
如何实现把中缀表达式转换成后缀表达式,并计算表达式的结果。
1.5需解决的关键技术
本次课程设计中的关键是:
通过递归下降方法分析表达式,主要有词法分析和语法分析,输出分析结果,判断表达式是否合法。
如何确定操作符的优先顺序,确定数据的进栈及出栈顺序,根据后缀表达式计算表达式的结果。
以及如何编写、调试、修改代码。
还要了解一个题目有许多种解决方法。
锻炼我们的思维能力。
2系统分析
2.1基础知识
2.1.1词法分析基本原理
词法分析程序完成的是编译第一阶段的工作。
词法分析的作用是把符流的程序变为单词序列,输出在一个中间文件上,这个文件作为语法分析程序的输入而继续编译过程。
2.1.2递归下降的原理
由于时间和技术的限制,语法分析采用递归下降的方法。
递归下降法是语法分析中最易懂的一种方法。
它的主要原理是,对每个非终极符按其产生式结构构造相应语法分析子程序,其中终极符产生匹配命令,而非终极符则产生过程调用命令。
因为文法递归相应子程序也递归,所以称这种方法为递归子程序下降法或递归下降法。
其中子程序的结构与产生式结构几乎是一致的。
2.1.3递归下降分析的文法要求
递归下降法要满足的条件:
假设A的全部产生式为Aα1|α2|……|αn,则必须满足如下条件才能保证可以唯一的选择合适的产生式
predict(Aαi)∩predict(Aαj)=Φ,当i≠j。
2.1.4后缀表达式基础知识
有的编译程序直接生成目标代码,有的编译程序采用中间代码。
中间代码,也称中间语言,是复杂性介于源程序代码和机器语言的一种表示形式。
一般快速编译程序直接生成目标代码,没有中间代码翻译成目标代码的额外开销。
但是为了使编译程序在结构上更为简单明确,常采用中间代码,这样可以将与机器相关的某些实现细节置于代码生成阶段进行处理,并且可以在中间代码一级进行优化工作使得代码优化比较容易实现。
编译程序所使用的中间代码有多种形式。
我们采用的是逆波兰记号的形式。
逆波兰记号是最简单的一种中间代码表示形式,早在编译程序出现之前,它就用于表示算术表达式,是波兰逻辑学家卢卡西维奇发明的。
这种表示法将运算对象写在前面,把运算符写在后面,叫后缀表达式。
后缀表示法表示表达式,其最大优点是易于计算机处理表达式。
利用一个栈,自左向右扫描算术表达式。
每碰到运算对象,就把它推进栈,碰到运算符,若该运算符是二目的,则对栈顶部的两个运算对象实施运算,并将运算结果代替运算对象而进栈。
若是一目运算符,则对栈顶元素执行该运算,并以运算结果代替元素进栈。
最后的结果留在栈顶。
2.2解决问题的基本思路
根据课程设计的要求,程序应具有如下功能:
对输入的表达式进行分析并输出逆波兰式。
由于对算数表达式的分析过程比较简单,这里采用递归下降的分析方法。
首先对输入的表达式进行词法分析,分析的结果作为语法分析的输入。
然后进行语法分析,语法分析采用递归下降的分析方法。
最后输出生成的逆波兰式并进行运算。
2.3总体方案
启动VC++,新建源程序并进行编程,程序的功能模块图如图2-1所示。
图2-1程序功能模块图
3系统设计
3.1算法实现
将一个普通的中序表达式转换为逆波兰表达式的一般算法是:
1首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。
2读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。
3从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。
4如果不是数字,该字符则是运算符,此时需比较优先关系。
做法如下:
将该字符与运算符栈顶的运算符的优先关系相比较。
如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。
倘若不是的话,则将栈顶的运算符从栈中弹出,直到栈顶运算符的优先级低于当前运算符,将该字符入栈。
5重复上述操作1-2直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。
转换的基本思路:
1在表达式字符串的末尾加一个代表结束的辅助符,比如“#”。
2从头开始扫描表达式,并判断当前的每一个字符。
3取当前的一个字符,如果当前字符是代表数字,则进逆波兰式的栈,如果是运算符,则转入4,如果是“#”,则结束。
4比较当前运算符与临时栈中的栈顶运算符,如果栈顶运算符比当前运算符优先级高,则弹出一个运算符放进逆波兰式栈中,并继续4。
否则把当前运算符进临时栈,转入2。
3.2流程图
图3-1程序流程图
4代码编写
在这次课程设计过程中,我主要负责的是语法分析模块,在词法分析的基础上将词法分析的结果作为语法分析的输入,语法分析采用递归下降的分析方法,部分代码如下。
intE1()
{
intf,t;
printf("%d\tE-->TG\t",total);total++;
flag=1;
input();
input1();
f=T();
if(f==0)return(0);
t=G();
if(t==0)return(0);
elsereturn
(1);
}
intE()
{
intf,t;
printf("%d\tE-->TG\t",total);total++;
e[0]='E';e[1]='=';e[2]='>';e[3]='T';e[4]='G';e[5]='#';
output();
flag=1;
input();
input1();
f=T();
if(f==0)return(0);
t=G();
if(t==0)return(0);
elsereturn
(1);
}
intT()
{
intf,t;
printf("%d\tT-->FS\t",total);total++;
e[0]='T';e[1]='=';e[2]='>';e[3]='F';e[4]='S';e[5]='#';
output();
flag=1;
input();
input1();
f=F();
if(f==0)return(0);
t=S();
if(t==0)return(0);
elsereturn
(1);
}
intG()
{
intf;
if(ch=='+'){
b[i1]=ch;
printf("%d\tG-->+TG\t",total);total++;
e[0]='G';e[1]='=';e[2]='>';e[3]='+';e[4]='T';e[5]='G';e[6]='#';
output();
flag=0;
input();input1();
ch=a1[++i1];
f=T();
if(f==0)return(0);
G();
return
(1);
}
printf("%d\tG-->^\t",total);total++;
e[0]='G';e[1]='=';e[2]='>';e[3]='^';e[4]='#';
output();
flag=1;
input();input1();
return
(1);
}
intS()
{
intf,t;
if(ch=='*'){
b[i1]=ch;printf("%d\tS-->*FS\t",total);total++;
e[0]='S';e[1]='=';e[2]='>';e[3]='*';e[4]='F';e[5]='S';e[6]='#';
output();
flag=0;
input();input1();
ch=a1[++i1];
f=F();
if(f==0)return(0);
t=S();
if(t==0)return(0);
elsereturn
(1);}
printf("%d\tS-->^\t",total);total++;
e[0]='S';e[1]='=';e[2]='>';e[3]='^';e[4]='#';
output();
flag=1;
a1[i1]=ch;
input();input1();
return
(1);
}
intF()
{
intf;
if(ch=='('){
b[i1]=ch;printf("%d\tF-->(E)\t",total);total++;
e[0]='F';e[1]='=';e[2]='>';e[3]='(';e[4]='E';e[5]=')';e[6]='#';
output();
flag=0;
input();input1();
ch=a1[++i1];
f=E();
if(f==0)return(0);
if(ch==')'){
b[i1]=ch;printf("%d\tF-->(E)\t",total);total++;
flag=0;input();input1();
ch=a1[++i1];
}
else{
printf("error\n");
return(0);
}
}
elseif(ch=='i'){
b[i1]=ch;printf("%d\tF-->i\t",total);total++;
e[0]='F';e[1]='=';e[2]='>';e[3]='i';e[4]='#';
output();
flag=0;input();input1();
ch=a1[++i1];
}
else{printf("error\n");return(0);}
return
(1);
}
voidyufa()/*递归分析*/
{
intf,p,j=0;
charx;
d[0]='E';
d[1]='=';
d[2]='>';
d[3]='T';
d[4]='G';
d[5]='#';
j=strlen(a1);
a1[j]='#';
n1=j;
ch=b[0]=a1[0];
cout<"<printf("步骤\t文法\t分析串\t\t分析字符\t剩余串\n");
f=E1();
if(f==0)return;
if(ch=='#')
{
printf("accept\n");
p=0;
x=d[p];
while(x!
='#'){
printf("%c",x);p=p+1;x=d[p];/*输出推导式*/
}
}else{
printf("error\n");
getchar();
return;
}
printf("\n");
getchar();
}
5程序调试
程序中调用了许多函数,编写代码时会出现调用的错误,使在程序运行时无法正确判断以致程序运行出错。
在创建函数时会出现错误而无法得知,致使在程序运行时出错,需要很细心的去编写代码。
编程的时候一定要细心,对出现的错误要认真调整,反复修改,使函数前后相对应。
6运行与测试
运行界面如下:
图5-1运行结果
总结
在经过了一个星期的软件实习后,我获益匪浅。
编译原理课程设计是我们将所学理论知识形成系统的一个锻炼的好机会,也是学校和老师检验我们学习成果的一个方法。
经过一周的设计,实现逆波兰式的生成。
其功能能够分析算术表达式,显示词法分析和语法分析的结果,并显示算术表达式的后缀形式,计算后缀表达式的值,显示结果。
这次的课程设计已经结束了,通过编程实现了预期的目标,管如此,VC++不断地探索和研究,在程序实现的过程中出现过许多没有想到的功能,原本设计的思路和方法,到真正的程序中运行的时候就达不到预想的效果。
因此,我感觉编程序实际操作是非常重要的,不亲自实践就不会发现问题,我们只有不断的发现问题,不断的解决问题,才能使一个程序尽可能的完善。
由于经验不足,时间有限,虽然在一周的时间里顺利的完成了系统的分析、设计和调试的工作,但是仍然有许多不足之处,我会在将来的软件设计过程中引以为戒。
在做课程设计期间,有目的的去学习一些将要用到的东西,仔细的考虑工作流程的规律和步骤,充分的利用手中的开发工具,使自己的开发在代码上实现少而精确,能够尽量简单的进行操作。
当我即将完成这个设计的时候我终于认清楚了以前老师经常提起的一个问题,那就是:
一个软件开发的过程中编码不是重要的,重要的是对分析系统以及系统模型的建立。
有了一个好的系统模型之后,我们再将其划分成几个模块,那样做起来就会容易得多。
通过这个课程设计让我有了深刻的了解,增长了自己的动手能力,而且我认识到团队合作也十分重要。
通过这次课程设计不仅锻炼了我学习能力,在团队合作上也有很大提高。
致谢
在这次的课程设计中,让我深深地体现到编程不是一件简单的事情,它需要设计者具有全面的专业知识、缜密的思维、严谨的工作态度以及较高的分析问题、解决问题的能力,而我在很多方面还有欠缺。
首先我要感谢淮阴工学院、计算机工程系提供的实践机会,实验室人员提供的实验环境,以及指导老师的悉心教导。
因为课程设计不仅是学校和老师对我学习成果的一个统筹检测的方法,也是我将所学理论知识形成系统的一个锻炼的好机会。
经过一个星期的时间,我不仅对操作环境有了更新的了解,更是掌握了程序设计的基本步骤,同时也对VisualC++有了更深的了解和掌握。
其次我要感谢同学们的帮助,以及其他提供过帮助的所有人。
在课程设计的过程中,我查阅了大量的资料,也上网搜索了许多资料。
但还是有不会做的地方,于是我去请教同学,他们都很热情的讲给我听并且还帮我上机调试,我真的很感谢他们。
最后,我要向给予我悉心指导和帮助的各位老师表示衷心的感谢和诚挚的敬意。
因为在本次课程设计中,我从几位老师身上学到了很多东西,他们认真负责的工作态度,严谨的治学精神和深厚的理论水平都使我收益匪浅。
他们无论在理论上还是在实践中,都给与我很大的帮助,使我得到不少的提高。
这对于我以后的工作和学习都有一种巨大的帮助,感谢他们耐心的辅导。
同时也感谢其他老师对我技术上的指导,由于经验不足,时间有限,虽然在近一个星期时间里顺利的完成了程序的调研、分析、设计和调试的工作,但是仍然有许多不足之处,我会在将来的软件设计过程中引以为戒。
参考文献
1高丽.编译原理课程设计指导书.淮阴工学院计算机工程系,2008
2殷人昆,陶永雷等.面向对象方法与C++.清华大学出版社,1999
3BjarneStroustrup.C++程序设计语言(特别版).高等教育出版社,2006
4於春景.实用C++调试指南.华中理工大学出版社,2006
5于永彦,于长辉.程序设计指导书,2006
6黄迪明.C++程序设计基础.电子工业出版社,2003