第一篇:《编译原理课程设计》教学大纲
《编译原理课程设计》教学大纲
课程名称: 课程编号: 适用专业: 总 学 分: 总 周 时: 主 撰 人: 撰写日期:
一、目的与任务
通过程序设计上机调试程序实现算法,学习编译程序调试技巧和设计编译程序的一般原则,加深对词法分析、语法分析、语义分析和中间代码生成等编译阶段及实用编译系统的认识,初步掌握编译程序构造的基本原理与技术, 从形式语言理论的角度, 进一步认识与理解程序设计语言。通过编译程序的编写和调试能力的训练,激发学生进一步思考问题,培养学生的学习兴趣和创新能力。并进一步培养学生的抽象思维能力,进一步巩固《编译原理》课程所学知识。
本次课程设计的时间为2周,目的是通过实际的题目如:词法分析、语法分析、代码优化等,使学生了解和掌握编译程序的工作原理,同时培养学生用相关的程序设计语言进行程序设计,实现编译的功能,从而提高学生的综合能力。
二、教学基本要求
1.设计和调试过程要规范化
需求分析:将题目中要求的功能进行叙述分析,并且设计解决此问题的数据存储结构,(有些题目已经指定了数据存储的,按照指定的设计),设计或叙述解决此问题的算法,描述算法可以使用自然语言、伪代码、或函数的方式。
给出实现功能的一组或多组测试数据(测试文法),程序调试后,将按照此测试数据进行测试的结果列出来。
如果程序不能正常运行或运行过程中出现了不满足算法思想的情况,写出出现这一情况的原因或改进行的方法。
源程序要按照写程序的规则来编写。要结构清晰,重点函数的重点变量,重点功能部分要加上清晰的程序注释。
程序能够运行,要有基本的容错功能。尽量避免出现操作错误时出现死循环。2.课程设计实习报告的书写格式
编译原理 436105 软件工程 2W 2012.6
审 核 人:
① 设计题目
②运行环境(软、硬件环境)③算法设计的思想 ④算法设计分析 ⑤主要函数 ⑥源代码 ⑦运行结果分析 ⑧收获及体会 3.实施方式
本次课程设计分成9个题目,都有一定的工作量,涵盖本课程内容和实际应用相关的主要技术,学生可以自由组队选择其中一个实现。课程设计题目见“主要内容”。
根据老师给定的9个题目进行分析设计,本次课程设计采取分组的办法进行,3-4人为一组,要求每组学生在规定时间内独立完成。4.答辩:课题的论述、测试及问题回答
三、课程设计内容
1、词法分析器的构造:
人们理解一个程序,起码是在单词级别上来思考。同样,在编绎一个程序时,也是在单词级别上来分析和翻译源程序。词法分析是编绎的基础,执行词法分析的程序即为词法分析器,它的任务是对输入或给定的源程序,从左至右逐个字符进行扫描,产生一个个单词符号,把作为字符串的源程序改造成单词符号串的中间程序。设计目的与任务:
通过本课程设计教学所要求达到的目的是:对词法分析工作流程进行总体设计和详细设计,最终用C语言来设计一个简单词法分析器,实现对源程序的词法分析功能,对输入程序去除注释,并以二元式形式输出程序中所有单词。
2、正则表达式到NFA 在编译系统中,词法分析阶段是整个编译系统的基础。对于单词的识别,有限自动机FA是一种十分有效的工具。有限自动机由其映射f是否为单值而分为确定的有限自动机DFA和非确定的有限自动机NFA。在非确定的有限自动机NFA中,由于某些状态的转移需从若干个可能的后续状态中进行选择,故一个NFA对符号串的识别就必然是一个试探的过程。这种不确定性给识别过程带来的反复,无疑会影响到FA的工作效率。而DFA引擎在任意时刻必定处于某个确定的状态,它搜索是无需象NFA一样必须记录所有的可能路径(trace multiple possible routes through the NFA),这也是DFA运行效率高于NFA的原因。而已经证明DFA是NFA的一个特例,即对于每一个NFA M存在一个DFA M’’,使得L(M)=L(M’’)。
设计目的与任务
通过本课程设计教学所要求达到的目的是:充分理解和掌握NFA,DFA以及NFA确定化过程的相关概念和知识,编程实现对输入的任意正规式转换成NFA的形式输出。
3、NFA的确定化
有限自动机理论是描述词法规则的基本理论。一条词法规则表示一个正规表达式(又叫正规式),而一个正规式又可化为一个DFA(确定有穷自动机),这个有限自动机可用来识别词法规则所定义的所有单词符号。把程序设计语言的所有词法规则都构造出相应的有限自动机,就得到一个词法分析器。然后,再转换为计算机可识别的程序就能自动实现词法的分析和检查。在实际应用中,用NFA(不确定有穷自动机)识别词法存在不确定和状态的冗余,因而,就要将NFA(不确定有穷自动机)转换为DFA(确定有穷自动机),消除了不可到达和不确定。设计目的与任务
通过本课程设计教学所要求达到的目的是:掌握从NFA到DFA的转换,以及用子集法把NFA转换成DFA理论,编程实现将NFA(不确定有穷自动机)转换为DFA(确定有穷自动机)。
4、DFA的最小化
确定性有限自动机(DFA ,Deterministic Finite Automata)的最小化仍是有限自动机应用及实现方面的重要问题之一。DFA的最小化可以揭示状态之间的内在联系,便于其存储实现,便于建立用DFA描述的任务模型,一些理论问题也与最小化思想有关。DFA的最小化是指,构造一个与之等价且状态数最小的DFA,即等价最小DFA。许多文献给出了一个最小化算法,算法的思想是,构造状态集的一个划分,再将这个划分中的每个子集作为新的状态,从而得到等价最小DFA。
DFA的最小化可以揭示状态之间的内在联系,便于其存储实现,便于建立用DFA描述的任务模型,一些理论问题也与最小化思想有关。
5、语法分析之LL(1)文法
通过该课程设计了解了程序语言的自上而下的语法分析过程,提高了编程能力,能使我们了解编程语言更多的细节 设计目的与任务(1)读入文法(2)求出first(), follow()(3)判断是否为LL(1)文法
(4)若是,构造分析表;
(5)输入一个字符串看是否是文法的一个句子。
6、算符优先文法
一个文法,如果它的任一产生式的右边都不含有两个相继(并列)的非终结符,即不 含有如下形式的产生式的右部:
„QR„
则我们称该文法为算符文法。
假设文法中的任意两个终结符之间最多只有一个优先关系,则该文法称为算符优先文法。
该课程设计按照求,(P),(P)各两条规则,求出各非终结符的集。然后按照算符优先算法求出各终结符的算符优先关系,填写算符优先表,并将其输出。
7、LR(0)分析表的构造
LR分析技术是一种有效的自下而上分析技术,是一种规范归约,其中L表示从左到右扫描输入串,R表示构造一个最右推导的逆过程。这种方法可以适用于很大一类上下无关文法的语法分析。LR方法的基本思想是:在规范归约过程中,一方面记住已经移进和归约出的整个符号串,即记住“历史”;另一方面根据所用的产生式推测未来可能碰到的输入符号,即对未来进行“展望”。当一串貌似句柄的符号串呈现于分析栈的顶端时,我们希望能够根据所记载的“历史”和“展望”以及“现实”的输入符号等三方面的材料,来确定栈顶的符号传是否构成相对某一产生式的句柄。
LR分析器的核心部分是一张分析表。这张分析表包括两部分,一是“动作”(ACTION)表,另一是“状态转换”(GOTO)表。对于一个文法,如果能用一个每步顶多向前检查K个输入符号的LR分析器进行分析,则这个文法就称为LR(K)文法。本文研究的LR(0)文法即K=0时的文法。
设计目的与任务
本课程设计所设计目的与任务是:通过C语言程序实现LR(0)分析表的构造,熟练掌握LR(0)分析表的构造方法,即利用拓广文法和构造项目集规范族的方法。了解LR(0)分析器的工作原理,并能利用LR(0)分析表对输入串进行分析。
8、逆波兰表达式生成算法
虽然源程序可以直接翻译为目标语言代码,但许多编译程序采用了独立于机器的、复杂性介于源语言和机器翻译语言之间的中间语言:后缀式(逆波兰表达式)等。这样做的好处是:
(1)便于进行与机器无关的代码优化工作;(2)使编译程序改变目标机更容易;
(3)使编译程序的结构在逻辑上更为简单明确。以中间语言为界面,编译前端和后端的接口更清晰。设计目的与任务
将非后缀式用来表示的算术表达式转换为用逆波兰式来表示的算术表达式,并能运行查看结果。
9、表达式的中间代码生成
源程序可以直接翻译为目标语言代码,但是许多编译程序却采用了独立于机器的、复杂性介于源语言和机器语言之间的中间语言。这样我们可以做下面工作:
(1):便于进行与机器无关的代码优化工作;(2):使编译程序以改变目标机更容易;(3):使编译程序的结构在逻辑上更为简单明确;
而以中间语言为界面,编译前端和后端的接口更清晰,表达式可以用四个域分别称为OP、ORG1、ORG2及RESULT来表示。
四、时间安排
《编译原理课程设计》安排在第三学期进行,时间2周(17-18周)。
五、组织管理
1.由院、系指派经验丰富的专业教师担任指导教师。
2.课程设计实行指导教师负责制,由指导教师全面负责课程设计的指导与管理工作。
六、成绩考核与评定
学生课程设计结束后写出总结报告,对设计的内容和效果进行总结,按照学生在设计期间的表现,指导老师对每位学生写出评语和鉴定,系课程设计领导小组组织答辩,最后确定每位学生课程设计成绩,课程设计成绩分为优、良、中、及格和不及格五个等级。课程设计成绩为平时表现30%、设计报告50%、答辩20%。评分标准:
① 优秀:目的明确,态度端正,模范遵守学校的各项纪律。工作认真,积极 主动,吃苦耐劳,能出色的完成设计任务。撰写了高质量的总结报告。答辩准确流利。
② 良好:目的明确,态度端正,能遵守学校的各项纪律,工作比较积极主动。能较好地完成设计任务,成绩较突出,表现良好;撰写了质量比较高的实习报告。答辩较准确流利。
③ 及格:目的明确,态度基本端正,能遵守学校纪律,在督促下能开展工作 并完成一定的设计任务,无大的违纪违规现象;撰写了实习报告。通过了答辩。
④ 不及格:实习态度端正,不能遵守实习单位的纪律,不服从领导,自由散漫,工作消极被动,不能完成实习任务,实习期间有失职、旷工、打架、酗酒等大的过失。或无实习报告,没有通过答辩。
2.成绩评定
依据上述考核内容,最后采用优(>90分)、良(80~89分)、中(70~79分)及格(60~69分)、不及格(<60分)五级记分制评定学生课程设计成绩。
七、主要参考资料
教材:
《编译原理及实践》冯博琴等译,机械工业出版社 教学参考书
1、《程序设计语言与编译》龚天富、侯文永编,电子工业出版社。
2、《编译原理》吕映芝、张素琴、蒋维杜主编,清华大学出版社,1998年
3、《编译原理》胡伦骏、徐兰芳、刘建农编,电子工业出版社2002年
4、《编译原理》(第二版)蒋立源、康慕宁主编,西北工业大学出版社,2002年
5、《编译原理习题精选》陈意云、张昱著,中国科技大学出版社,2002年
6、《编译原理习题与解析》 伍春香著,清华大学出版社,2001年
7、《编译原理实验指导书》自编
第二篇:《编译原理》课程设计教学大纲
《编译原理》课程设计教学大纲
揭金良 2006.10.20 1 目的
通过课程设计,将《编译原理》的相关理论和技术运用到软件开发中,提高学生的应用程序设计能力,提高分析问题、解决问题的能力。内容
利用编译原理的某种思想或方法,设计一个应用程序,实现的具体内容自拟(见下面的选题指导)。要求
进行简单的需求分析、设计说明,写出程序结构框架,阐明设计思路、用到的原理和方法。程序规模适中,着重于内核功能。估计时间
总共时间2.5周(150学时),其中:1.讲课2学时;2.上机48学时调试;3.其余非上机时间由同学自行安排分析、检查问题、绘制流程图、写相关文档,最后集成设计(实验)报告并自行打印。过程指导
5.1 选题
通过平时积累,找到适合于自己的应用或某种软件功能,该应用能利用编译原理中的某些理论。题目大小适中。参考题目如下: 表达式计算器
表达式计算器:这是一款算术表达式计算程序,通过输入表达式达到计算的目的,可代替目前普遍使用的计算器。使用了编译原理中的词法分析、算符优先分析等。根据功能的不同可分为:
⑴无符号整数表达式计算器:输入无符号整数表达式,输出结果。难度:较难。工作量:中等。
整数表达式计算器:考虑负数。难度:较难。工作量:中等。 定点实数表达式计算器:难度:较难。工作量:中等。
通用表达式计算器:考虑1.23e-2的形式输入。难度:难。工作量:中等。⑵函数表达式计算程序:这是一款能计算函数值的实用程序,输入含有自变量x的函数表达式被接受后,可接着输入自变量x的值,输出函数值y的值。使用了词法分析、算符优先分析。根据功能的不同可以分为:
多项式函数计算程序:只处理多项式函数,如f(x)=x^3+x^2+5等。难度:较难。也可设计成其他专用函数计算程序,如幂函数计算、三角函数计算等。但要避免出现不同用,如不要设计成仅能处理f(x)=2^x的幂函数,因为这样的函数利用普通编程就能实现,无法体现编译原理。工作量:中等。
通用初等函数计算程序:能处理所有的初等函数的计算。如f(x)=3*sin(x/2+3.1415)+x^2等。难度:难(因为涉及到函数名称和参数的识别问题)。工作量:较大。
二元或多元函数的计算:如f(x,y)=x^2+y^2等。难度和工作量同一元函数的计算。⑶逻辑运算分析:输入逻辑表达式串,对表达式进行分析,并得出结果。难度:较难。2 字符串搜索程序
输入要查找的字符串的正规表达式,软件可在大量文本(要求不低于3000字符的文档资料文件)中找到符合描述的字符串。这个设计可参考微软.NET的正规表达式的功能。
⑴字符串搜索程序:使用到正规式、词法分析等,还需要有较大的设计技巧。根据功能的不同可以分为:
名称查找程序(类似于文件名):存有大量的名称,如abc,123,a1,ab1245等,输入要查找的规则,找出符合规则的名称。
方案1:通配符,把“*、?”当作通配符:如输入“a*”,显示“abc,a1,ab1245”;输入“a?”,输出“a1”。
方案2:正规式,把“*”当作“闭包”,把“|”当作“或”:如输入“(a|b)1”,输出“a1”;输入“(a|b)*c”,输出“abc”。
方案3:只要包含正规式即可,当名称中包含该正规式,就输出来:如输入“(a|b)1”,输出“a1,ab1245(含有b1)”;输入“(a|b)*”,输出“abc,a1,ab1245”。
方案4:含有通配符的正规式,设“@”表示任意字母,“$”表示任意数字(其他可再设多一点,如表示符号等,但会减少名称中的符号种数):如输入“a@*” 输出“abc”;输入“a(@|$)*”,输出“abc,a1,ab1245”。
(以上涉及到正规式的方案难度较大,其他难度一般。注:以上的@$等符号是随便设定的,与.NET中通用的符号不同且有冲突的,请在实际编程时修改它。)
文本搜索程序:在一连串文本中搜索所需的字符串。同“名称查找程序”中的有关正规式的方案,难度较大,具体正规式包含哪些符号和通配符可另定。
(关于正则表达式的介绍参见另一文档,详细信息见.NET框架联机文档)3 逻辑运算分析
可对关系表达式进行分析,并得出结果。这个设计结果可改变后用于电路分析、谓词演算等。源程序扫描程序
对某种高级语言的源程序进行分析,建立符号表,找出尽可能多的问题并输出相关的出错信息。转义符的识别
C语言的字符串常量书写时使用了大量的转义符,如“”表示单斜杠,而“n”表示换行(同“u000A”),“x20”表示十六进制表示形式(恰好两位)与 ASCII 字符匹配(这里代表值为32的ASCII字符,即空格)。请参照C语言教材,编制一个软件,输入包含转义符的字符串,输出没有转义符的真实字符串。难度:一般。工作量:一般。有限自动机的运行
设计一个确定的有限自动机,写出状态转换函数,或画出状态图,编制程序,输入一个字符串,程序能判别该字符串是否能被该有限自动机接受,可以考虑输出能否接受的同时,输出状态路径。难度:一般。
⑴整数的判断:写出整数的正规式,画出状态图,写出状态转换函数。编出程序。难度:简单。
⑵同上的过程还可以进行:正偶数的判断、自然数的判断、定点数的判断等。⑶实数的判断:过程同上。难度:一般。7 编写一个语法分析程序
编写一个语法分析程序,对输入到缓冲区的符号进行分析,建立相应的语法树,并输出相应的语法树。编写一个代码生成程序
编写一个代码生成程序,形成三地址程序并输出到文件中,打印三地址程序。9 学习使用LEX和YACC工具
LEX和YACC分别是生成词法分析程序和语法分析程序的工具,即编译程序的编译程序。其功能非常强大,可用于任何语言的分析。
学习使用LEX和YACC工具并编写相应程序。请参考有关文档资料。
5.2 分析
选好题目后,分析该题目的应用性,可用到编译原理的哪些理论?对它们进行简单阐述。同时对软件进行需求分析,通过回答下面问题得到:
1.软件提供哪些功能?软件有什么用?界面怎样?怎样使用该软件?对输入数据的格式有什么要求?用什么语言开发?怎样测试该软件?该软件开发的进度如何安排?
2.写出以上问题的答案,然后自问:你的分析材料别人能非常清楚地看懂吗?如果回答是肯定的,就可以搞设计了。
5.3 设计
对软件划分功能模块,将模块细化,设计出数据存放格式,写出各模块(函数)的功能、传递参数的格式和返回值的类型,画出模块结构图。最后画出较详细的程序流程图。
5.4上机
按课表的安排上机(修改设计/编码/调试/测试)。
1.根据流程图编制并输入代码,教师查看学生的设计,对设计提出修改意见。2.修改设计,继续完成输入代码,并作初步调试。3.调试,教师帮助解决学生设计和调试中出现的难题。4.修改设计中的缺陷,完善程序。„„
演示软件,教师根据实际情况提出测试用例,学生作最后的修改和完善,教师评分。继续完善程序,并完成设计说明书,上交成果。
5.5 注意事项
测试数据的设计:每组测试数据包括输入数据、预期的输出结果、实际的输出结果和预期的是否相吻合(如果不吻合,实际输出什么?可能错误的原因?检查源代码或设计进行查错,纪录结果)。上交文档
1.程序源代码(打印,文件名中包含姓名);
2.一组较完备的测试数据(存在一个文本文件中);
3.设计报告:有关的文档资料,包括:选题报告,简单的软件需求分析说明书,软件设计说明,设计经验总结。其中设计经验总结可以包括下列方面:你在编程过程中花时多少?多少时间在纸上设计?多少时间上机输入和调试?多少时间在思考问题?遇到了哪些难题?你是怎么克服的?你对你的软件如何评价?你的收获有哪些? 评分
教师对每个实验结果进行评分(优、良、中、及格、不及格),记入成绩。以下几方面可以提高分数: 7.1 软件实用性强;
7.2 软件使用了编译原理中的某些理论; 7.3 软件具有扩展性; 7.4 各类文档写的很规范;
7.5 设计时非常投入,设计后很有心得; 7.6 其他优点。
第三篇:编译原理课程设计
课 程 设 计 报 告
设计题目:一个简单文法的编译器前端的设计与实现
班
级: 计算机1206 组长学号:201239 组长姓名:闫智宣 指导教师:李晓华 设计时间:2014年12月
[在此处键入]
设计分工
组长学号及姓名: 20123974
闫智宣
分工:
语法分析,四元式生成,目标代码优化及生成 组员1学号及姓名:20123977
廖峭 分工:
词法分析,错误处理 组员2学号及姓名:20123959
郭天龙
分工:
符号表生成,语义动作插入,操作界面[在此处键入]
摘要
编译原理课程设计是通过C语言编译器相关子系统的设计,进一步加深对编译器构造的理解;第一部分词法分析,设计各单词的状态转换图,并为不同的单词设计种别码,制作扫描器识别一个个单词,返回值为识别码的序号,返回Token序列。将词法分析器设计成供语法分析器调用的子程序。词法分析器具备预处理功能。将不翻译的注释等符号先滤掉,只保留要翻译的符号串,即要求设计一个供词法分析调用的预处理子程序;第二部分,语法分析,用递归下降法,实现对表达式、各种说明语句、控制语句进行语法分析。若语法正确,则用语法制导翻译法进行语义翻译;生成并打印出语法树;若语法错误,要求指出出错性质和出错位置(行号)。
我们还做了附加功能,即编译后端,有中间代码优化,生成目标代码汇编语言。通过此次课程设计,提高了我们的独立分析问题、解决问题的能力,以及系统软件设计的能力; 提高程序设计能力、程序调试能力,团结协作能力
关键词:词法分析,语法分析,四元式生成,错误处理,符号表生成,语义动作插入,中间代码优化,生成目标代码 [在此处键入]
目录
摘要
1.概述
2.课程设计任务及要求
2.1 设计任务
2.2 设计要求
3.算法及数据结构
3.1算法的总体思想(流程)
3.2 词法分析模块
3.2.1 功能
3.2.2 数据结构
3.2.3 算法
3.3 语法分析模块
3.3.1功能
3.3.2 数据结构
3.3.3算法
3.4 符号表模块
3.4.1功能
3.4.2 数据结构
3.4.3算法
3.5 四元式模块
3.5.1功能
[在此处键入]
3.5.2 数据结构
3.5.3算法
3.6 语义动作分析模块
3.6.1功能 3.6.2 数据结构
3.6.3算法
3.7 错误处理模块
3.7.1功能
3.7.2 数据结构
3.7.3算法
3.8 目标代码模块
3.8.1功能
3.8.2 数据结构
3.8.3算法
4.程序设计与实现
4.1 程序流程图
4.2 程序说明
4.3 实验结果
5.结论 6.参考文献。7.收获、体会和建议。
[在此处键入]
1.概述
编译器是将C语言翻译为汇编语言代码的计算机程序。编译器将源程序(source language)编写的程序作为输入,翻译产生目标语言(target language)机器代码的等价程序。通常地,源程序为高级语言(high-level language),C语言程序,而目标则是 机器语言的目标代码(object code),也就是可以在计算机硬件中运行的机器代码软件程序。这一过程可以表示为:
源程序→编译器 →目标机器代码程序
2.课程设计任务及要求
2.1设计任务
学生在学习《编译原理》课程过程中,结合各章节的构造编译程序的基本理论,要求用C#语言描述及上机调试,实现一个 C编译程序(包括词法分析,语法分析等重要子程序),使学生将理论与实际应用结合起来,受到软件设计等开发过程的全面训练,从而提高学生软件开发的能力。
2.2设计要求 要求:
(1)设计词法分析器
设计各单词的状态转换图,并为不同的单词设计种别码。将词法分析器设计成供语法分析器调用的子程序。功能包括:
a.具备预处理功能。将不翻译的注释等符号先滤掉,只保留要翻译的符号串,即要求设计一个供词法分析调用的预处理子程序;
b.能够拼出语言中的各个单词; [在此处键入]
c.返回(种别码,属性值,行号)。
(2)语法分析
要求用学习过的自底向上或自顶向下的分析方法等,实现对表达式、各种说明语句、控制语句进行语法分析。若语法正确,则用语法制导翻译法进行语义翻译;生成并打印出语法树;若语法错误,要求指出出错性质和出错位置(行号)。
3.算法及数据结构
3.1算法的总体思想(流程)
本节主要分析程序的代码结构和代码工程文件的划分。(程序由几个类组成: Token类和Variable类SymbolTable类ObjectCode类Lexical类Grammar类Four_Yuan类Action类ErrorItem类,分别为词法分析和语法分析类。工程分为几个文件:Form1.cs,Token.cs,Variable.cs,SymbolTable.cs,ObjectCode.cs,Lexical.cs,Grammar.cs,Four_Yuan,cs,Action.cs,ErrorItem.cs分别对应Token类和Variable类SymbolTable类ObjectCode类Lexical类Grammar类Four_Yuan类Action类ErrorItem类的声明和实现文件)。本程序采用C#语言以面向对象的思想编写,程序分为几部分:词法分析(Lexical),语法分析(Grammer),目标代码生成(ObjectCode)。Lexical类主要的工作是词法分析获取Token。Grammer类的主要工作是根据Lexical类词法分析之后的Token进行语法分析,生成语法树,最后并输出语法树。在处理过程中,Token类的对象作为Lexical类的一个成员变量,配合Grammer类进行语法分析。
工程文件总体上是按照九个类的格局分为十个文件,分别是九个类的声明文件和实现文件。十个文件为Form1.cs,Token.cs,Variable.cs,SymbolTable.cs,ObjectCode.cs,Lexical.cs,Grammar.cs,Four_Yuan,cs,Action.cs,ErrorItem.cs,他们分别是Lexical类声明文件、Lexical类实现文件、Grammer类声明文件、Grammer类实现文件。[在此处键入]
程序流程
在程序中,Lexical类的对象(Token)作为Grammer类中的一个成员变量,配合Grammer类进行语法分析。它们的关系是这样的:Grammer类的一个成员变量temp首先对源程序删除注释,然后进行词法分析获取所有Token,并将获取的Token存储在Token对象的tokenList(List类型)中。然后Grammer类的语法分析程序就根据tokenList中的Token进行语法分析,生成语法树,最后打印语法树。同时,这也是程序的流程。[在此处键入]
3.2 词法分析模块 3.2.1功能
Lexical类主要的工作是词法分析获取Token序列。
3.2.2数据结构
词法分析阶段的代码被封装成一个类——Lexical,Token中主要是Lexical类的声明代码,Lexical.cs中主要是Lexical类的实现代码。Lexical类对外提供的函数主要有:
static public int RecogId(string str, int i),static public int RecogDig(string str,int i),static public int RecogOperator(string str, int i),static public int RecogBound(string str, int i),以上几个函数构成了词法分析的骨架,在Lexical类中还有其他成员变量和函数,主要作为这三个函数处理过程的中间步骤,为这三个函数服务。Lexical类的代码结构和主要的成员变量和函数及其含义如下图所示:
3.2.3算法
算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是[在此处键入]
根据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。
主程序示意图:
主程序示意图如图3-1所示。
⑴ 关键字表的初值。
关键字作为特殊标识符处理,把它们预先安排在一张表格中(称为关键字表),当扫描程序识别出标识符时,查关键字表。如能查到匹配的单词,则该单词为关键字,否则为一般标识符。
(2)程序中需要用到的主要变量为type和number 扫描子程序的算法思想:
首先设置3个变量: [在此处键入]
①token用来存放构成单词符号的字符串; ②number用来整型单词;
③type用来存放单词符号的种别码。
Token定义
Token定义:
Token类型(TokenType):
3.3 语法分析模块
3.3.1功能
语法分析是编译过程的一个逻辑阶段。语法分析的功能是在词法分析的基础上将单词序列组合成各类语法短语,如“程序”,“语句”,“表达式”等等.语法分析程序判断源程序在结构上是否正确.源程序的结构由上下文无关文法描述.3.3.2 数据结构
下图为实现语法分析的类Grammar,属性与方法的作用都已说明 在此处键入]
3.3.3算法
1.文法
下面终结符与非终结符意义
B程序开始
Z 数据类型,如int,char,float等
V 标识符
S 语句
P 语句块
E 加减算术表达式
D 逗号表达式
T 乘除算术表达式
C 关系表达式
L 逻辑表达式
Q 标识符或圆括号
e 表示空
i 表示标识符 a)函数文法
B----ZV()S
[
[在此处键入]
b)语句块文法
P----SP|e
S----{P} c)语句文法
表达式语句文法
S----V=E
goto语句文法
S----i:S
S----goto i
if语句文法
S----if(E)S[else S]
while语句文法
S----while(E)S
声明语句文法
S----ZVD
D----,VD|=ED|e d)表达式文法
E----T|E+T|E-T
T----F|T*F|T/F
C----C|C
L----Q|L&&Q|L||Q
Q----i|(E)|!Q
2.递归下降程序流程图
对应于每个文法编写如下递归下降子程序
主程序(B)[在此处键入] [在此处键入]
3.4 符号表模块
3.4.1功能
进行符号表的储存,添加,更新,查找,保存标识符活跃信息以及输出。3.4.2 数据结构
在此处键入]
3.4.3算法
3.5 四元式模块
3.5.1功能
四元式为中间代码,编译程序进行完语义分析后,先生成中间代码作为过渡,此时中间代码与目标代码已经比较相似
3.5.2 数据结构
[ 在此处键入]
3.5.3算法
3.6语义动作分析模块
3.6.1功能
在语法分析中嵌入相应的语义动作,生成四元式 3.6.2 数据结构
[
[在此处键入]
3.6.3算法 GEQ(+)(-)(*)(/)
(+,i1,i2,t)PUSH(i)ASSI(=)
(=,t,_,POP)LABER(i)
(lb,_,_,i)GOTO(i)
(gt,_,_,i)IF(if)
(if,a,_,_)EL(el)
(el,_,_,_)IE(ie)
(ie,_,_,_)WH()
(wh,_,_,_)DO()
(do,a,_,_)WE(we)
(we,_,_,_)
3.7 错误处理模块
3.7.1功能 保存运行时发现的错误,储存行号已经详细信息并输出。
3.7.2 数据结构
3.7.3算法 [在此处键入]
public static void AddErrorMessage(int lineno,string content)函数用作在发现错误时保存错误信息以及行号。
public static string PrintErrorList()把所有发现的错误格式化后统一输出。
错误信息在语法分析,语义分析,符号表检错中添加。3.8 目标代码模块
3.8.1功能
目标代码生成把优化后的中间代码变换成目标代码,此处的目标代码为汇编代码,采用单寄存器生成目标代码 3.8.2 数据结构[在此处键入]
3.8.3算法
对于一个基本块有如下流程图
W:操作符,B:第一操作数,C:第二操作数,R:寄存器
5.结论
网上找一段话抄上 [在此处键入]
6.测试
测试打开文件
测试保存文件
如果没打开文件,直接敲代码,点保存时会弹出另存为窗口[在此处键入]
测试错误检测,程序缺少main函数的类型,错误列表中显示第一行函数缺少错误类型。
测试错误检测,程序缺少分号,错误列表中显示该行缺少语句结束标志';' 单击错误列表,会自动选定错误行
编译成功,生成并显示token串、符号表、四元式与目标代码 [在此处键入]
测试if与while语句,而且while嵌套在if当中
测试goto语句,结果正确。[在此处键入]
测试优化,输入课件中的代码,结果与课件一样
6.参考文献。
1、陈火旺.《程序设计语言编译原理》(第3版).北京:国防工业出版社.2000.2、美 Alfred V.Aho Ravi Sethi Jeffrey D.Ullman著.李建中,姜守旭译.《编译原理》.24 [在此处键入]
北京:机械工业出版社.2003.3、美 Kenneth C.Louden著.冯博琴等译.《编译原理及实践》.北京:机械工业出版社.2002.4、金成植著.《编译程序构造原理和实现技术》.北京:高等教育出版社.2002.7.收获、体会和建议。
直接拷贝好歹也检查一下错误
对于编译原理的这次课程设计,自己经历了从刚开始的不懂明白任务的要求和内容理论知识的了解开始着手写代码完成基本功能根据DFA及自顶向下等理论修改完善代码等这些过程。
自己着手写词法分析的时候还不清楚词法分析的任务内容,还不知道词法分析的结果是什么,词法分析出错的情况和类型有哪些,也总是将词法分析和语法分析混在一起,不明白哪些错误在词法分析中报,哪些错误在语法分析中判断,后来经过查书、网上资料、请教同学等途径逐步清晰了词法分析的工作内容是从源代码文件中获取出Token,供语法分析使用。在充分了解了语法分析需要哪些信息时,我才真正了解了词法分析的工作内容和目标,才知道词法分析需要完成哪些任务获取到哪些信息。充分了解了词法分析的任务之后,就开始理论知识的学习。经过揣摩书上的例子,自己理解和掌握了怎么设计过滤注释和分析程序中Token的DFA,于是开始根据设计好的DFA进行编码,最后经过调试已经可以正确地完成词法阶段的任务了。这只是词法分析的原始代码,在之后还进行了两次彻底的改动。虽然之前写的词法分析的代码已经完成了词法分析的需求,也是根据DFA的原理编写的,但是在代码结构上却难以体现,在对书上的根据已知DFA写代码的例子进行了详细的研究之后,发现自己的代码并没有像书上那样完全按照所依据的DFA各状态转移的关系进行编写,所以对代码进行了重写,像书上一样严格按照状态之间转移的方式进行编写,将状态划分成11个状态,状态分别按1~11进行标注,程序也按照DFA来编写,也实现了词法分析的功能。再后来写报告的时候,发现分析出Token的那个DFA并不是最简的,有很多多余的状态,完全可以用一个flag标志来标识,从而简化代码结构,于是又重写了一次词法分析函数scan()的代码,将状态缩减为5个,且不再用1-5来表示,而是像书上那样分别取了名字(START、INNUM、INID、INDBSYM、DONE),同时为了简化代码将输出Token到文件的部分从scan()中剥离开来,而在Lexical类中加了一个printToken()的函数,使scan()函数逻辑更加清晰,使读者能够容易地将代码与DFA进行查看比照。
在写语法分析的时候,已经对编译器的语法分析的内容有了一定的了解,所以直接进行了理论的学习。首先自己对递归向下分析法进行了学习,将书上的几个递归向下分析的伪代码看过之后,自己对递归向下的分析方法的原理有了初步的认识,大概知道了根据文法怎么分析,但是对于如何编写代码却还在此处键入]
是难以下手,于是就对照TINY语言的文法看了几遍书后面的TINY语言的递归向下分析的语法分析程序,这样就基本知道了C-语言的语法分析程序怎么写。由于C-语言给出的文法有左递归存在,于是自己将存在左递归的文法改写成EBNF的形式,并据此进行代码编写。由于在编写代码的过程中需要确定分析是否正确或选择多个文法中的某一个文法进行分析,有时必须探测需要的或下一个Token的类型,在这种情况下需要求First集合,在推导中若存在empty,又需要求Follow集合,所以这样又需要我了解First集合和Follow集合,自己在程序中也根据求出的First集合和Follow集合进行判断,以确定程序的走向。在编写过程中,还有一类问题,就是存在公共左因子,如文法expression→ var = expression | simple-expression,左因子为ID,在分析过程中,由于已经取出了一个ID的Token,且生成了一个IdK的节点,但是在当前状态无法确定是哪一个推导,然而IdK节点已经生成,又无法回退,并且是使用自顶向下的分析方法,已经生成的IdK在程序上方无法使用,自己通过查阅资料等途径的学习确定了在这种情形下的处理方式:将已经生成的IdK节点传到下方的处理程序,所以TreeNode * simple_expression(TreeNode * k)、TreeNode * additive_expression(TreeNode * k)等函数都被设计成有节点类型参数的函数,目的就是将已经生成的节点传到下面的分析函数中去。
通过这次的编译原理课程的学习和实践,自己获益良多。首先最基本的成果是完成了课程设计的任务,实现了编译器的词法分析和语法分析阶段的功能,词法分析主要能过滤注释、分析出语法分析阶段需要的Token并满足语法阶段的所有要求,能够判别词法分析阶段是否出错和出错类型和位置。语法分析主要能根据递归向下的分析思想和C-文法对词法分析获取的Token进行语法分析,能够构造出语法树,能够判别语法分析过程中是否出错以及出错位置和错误类型。
由于在编写程序过程中,涉及到了正则表达式、DFA、提取公共左因子、消除左递归、EBNF、求First集合和Follow集合、递归向下分析方法以及编程语言方面的知识,所以,通过本次的课程设计的实践,使得自己对编译原理这门课的许多知识点有了更加深刻和具体的理解,而不再只限制于做题。此外,对以前那些已掌握的知识有了温习和动手锻炼的机会。如:以前在编译原理课上虽然知道First集合和Follow集合怎么求的,却不知道First集合和Follow集合到底是干什么的,通过编写程序自己明白了他们的实际作用,使得自己不仅知其然还知其所以然,从而使得自己加深了对知识点的理解和掌握。由于以前编写代码都是使用JAVA语言,所以C/C++很多内容都忘记了,通过本次的实践,自己又重新拾起了以前的知识。此外,由于在做报告的时候,需要描绘DFA和程序流程图,使得自己初步掌握了使用visio和word画图的能力。此外,对于文档的编写和美化自己也获得了许多有用的经验。[
第四篇:编译原理教学大纲(范文模版)
编译原理教学大纲
一、课程的性质、地位
本课程是计算机专业的重要专业课之一,是一门理论性和实践性较强的课程。主要介绍程序设计语言编译程序构造的基本原理和基本实现方法。本课程主要讲授形式语言、有限自动机、自上而下和自下而上的语法分析、LR分析方法、属性文法和语法制导翻译、语义分析的代码产生、存储器的动态分配与管理、符号表的组织与管理、优化问题、代码生成等内容。通过本课程学习,使学生对编译的基本概念、原理和方法有完整的和清楚的理解,并能正确地、熟练地运用。
二、课程的目的、任务和要求
该课程的目的是让学生掌握程序设计语言编译程序构造的一般原理、基本设计方法、主要实现技术和一些自动构造工具。通过本课程的学习,使学生较好地掌握编译原理的基本原理和基本技术、编译原理中涉及的基本算法、基本结构和主要实现技术,从而让学生了解将高级程序设计语言源程序翻译成计算机能处理的目标代码语言的整个过程,基本掌握计算机系统软件之一 编译程序的构造原理及相关技术,同时,还可提高学生计算机专业素质,培养学生的抽象思维能力。通过学习,学生可基本掌握计算机系统软件之一 编译程序的构造原理及相关技术,同时,还可提高学生计算机专业素质,培养学生的抽象思维能力。
三、与其它课程的关系
要求学生具有较好的计算机基础知识,对计算机的工作原理有一定了解,前导课程包括:高等数学、线性代数、计算机原理、离散数学、高级程序设计语言、数据结构等课程。
四、课程内容(建议理论课时:62 上机课时:18)第一章 编译程序概论
1、教学目的及要求:
本章介绍编译程序在计算机科学中的地位和作用,介绍编译技术的发展历史,讲解编译程序、解释程序的基本概念,概述编译过程,介绍编译程序的逻辑结构和编译程序的组织形式。要求理解编译程序、解释程序和遍的基本概念;掌握编译过程各阶段的任务和编译程序逻辑结构及其各部分的基本功能。
2、教学内容:
编译程序,编译过程概述,编译程序的结构,编译程序与程序设计环境,编译程序生成,学习构造编译程序。
3、教学重点:
重点:编译程序工作的基本过程及其各阶段的基本任务,编译程序总框。
4、教学难点:
编译的遍。
5、教学时间分配及进度安排:
建议本章教学时数2学时。
6、章节内容
1、什么是编译程序
2、编译过程概述
3、编译程序的结构
4、编译技术和软件工具 第二章 文法和语言
1、教学目的及要求:
本章是编译原理课程的理论基础,要求理解文法、语言、规范推导、规范归约和短语、简单短语、句炳的基本概念;掌握语言的求解方法、文法的二义性与递归性的判断方法及句型的分析方法。
2、教学内容:
形式语言的基本概念,包括符号串的基本概念和术语、文法和语言的形式定义、句型分析、文法和语言的Chomsky分类,二义性。
3、教学重点:
上下文无关文法,语言定义。
4、教学难点:
推导,文法与语言的相互转换。
5、教学时间分配及进度安排:
建议本章教学时数5学时。
6、章节内容
1、文法的直观概念
2、符号和符号串
3、文法和语言的形式定义
4、文法的类型
5、语法树和二义性
6、句型的分析
7、文法中的实用限制 第三章 词法分析
1、教学目的及要求:
本章介绍编译程序的第一个阶段词法分析的设计原理和设计方法,要求掌握正则文法、状态转换图、DFA、NFA、正规式和正规集的基本概念和词法分析设计与编写。
2、教学内容:
词法分析的设计原理和设计方法,源程序输入与词法分析程序输出、正则文法及其状态转换图、确定的有限自动机(DFA)不确定的有限自动机(NFA)正则表达式与正规集。
3、教学重点:
重点:词法分析器的任务与设计,状态转换图。
4、教学难点:
正则文法、正规集、DFA、NFA的相互转化。
5、教学时间分配及进度安排:
建议本章教学时数8学时。
6、章节内容
1、词法分析程序的设计
2、单词的描述工具
3、有穷自动机
4、正规式和有穷自动机的等价性
5、正规文法和有穷自动机间的转换 第四章 语法分析—自上而下分析
1、教学目的及要求:
本章介绍编译程序的第二个阶段语法分析的设计方法和实现原理,包括自上而下分析的无回朔的递归下降分析、LL(1)分析法。要求理解递归下降分析、LL(1)文法的基本概念;掌握无回朔的递归下降分析的设计和实现、LL(1)分析表的构造与分析方法。
2、教学内容:
语法分析器的功能,自上而下语法分析(递归下降分析法,预测分析程序),LL(1)分析法,递归下降分析程序构造,预测分析程序。
3、教学重点:
递归下降子程序,预测分析表构造,LL(1)文法。
4、教学难点:
LL(1)文法预测分析表构造。
5、教学时间分配及进度安排:
建议本章教学时数5学时。
6、章节内容
1、确定的自顶向下分析思想
2、LL(1)文法的判别
3、某些非LL(1)文法到LL(1)文法的等价变换
4、不确定的自顶向下分析思想
5、确定的自顶向下分析方法 第五章 语法分析—自下而上分析
1、教学目的及要求:
要求理解算符优先文法、最左素短语、有效项目的基本概念;掌握算符优先分析方法、LR(0)文法的判断及LR(0)分析表的构造与分析方法、SLR(1)文法的判断与SLR(1)分析方法和LR(1)文法的判断与LR(1)分析方法。
2、教学内容:
自下而上语法分析(算符优先分析法),算符优先分析,LR分析器,LR(0)项目集族和LR(0)分析表的构造,SLR分析表的构造,规范LR分析表的构造。
3、教学重点:
归约,算符优先表构造,LR分析法。
4、教学难点:
归约,LR分析法。
5、教学时间分配及进度安排:
建议本章教学时数12学时。
6、章节内容
1、自底向上分析思想
2、算符优先分析法
3、LR分析法 第六章 属性文法和语法制导翻译
1、教学目的及要求:
本章介绍编译程序的第三个阶段语义分析及中间代码生成的设计原理和实现方法,要求理解语法制导翻译、语义动作的基本概念;掌握算数表达式和赋值语句到中间代码的翻译、布尔表达式和几种控制语句的目标代码结构分析和到四元式的语法制导翻译;说明语句的语法制导翻译。
2、教学内容:
语法制导翻译的基本概念、中间代码的形式,可执行语句和说明语句的语法制导翻译方法。
3、教学重点:
语法制导翻译基本思想,语法制导翻译概述,基于属性文法的处理方法,自下而上分析制导翻译概述。
4、教学难点:
属性文法的处理方法
5、教学时间分配及进度安排:
建议本章教学时数9学时。
6、章节内容
1、属性文法
2、语法制导翻译概论
3、中间代码的形式
4、简单赋值语句的翻译
5、布尔表达式的翻译
6、控制语句的翻译 第七章 符号表
1、教学目的及要求:
本章介绍编译程序的组成部分之一符号表的管理,要求掌握符号表管理的基本方法。
2、教学内容:
符号表的作用、建立、符号表栏目的组织、符号表上的操作。
3、教学重点:
符号表的作用与内容。
4、教学难点:
符号表的内容。
5、教学时间分配及进度安排:
建议本章教学时数3学时。
6、章节内容
1、符号表的作用和地位
2、符号表的主要属性及作用
3、符号表的组织
4、符号表的管理 第八章 运行时存储空间组织
1、教学目的及要求:
本章介绍目标程序运行时的存储组织方式,包括静态存储分配和动态存储分配。要求掌握各种存储组织形式的基本方法。
2、教学内容:
目标程序运行时的活动,运行时存储器的划分,静态存储管理,简单的栈式存储分配的实现,嵌套过程语言的栈式实现,堆式动态存储分配。
3、教学重点:
静态分配策略和动态分配策略基本思想,嵌套过程语言栈式分配,活动记录、运行时栈的组织。
4、教学难点:
嵌套过程语言栈式分配,活动记录、运行时栈的组织。
5、教学时间分配及进度安排:
建议本章教学时数9学时。
6、章节内容
1、数据空间的三种不同使用方法
2、栈式存储分配的实现
3、参数传递
第九章 代码优化
1、教学目的及要求:
本章介绍优化的相关知识,要求掌握局部优化,基本块的DAG表示及其应用,控制流分析和循环查找算法,到达定值与引用定值链,循环优化。
2、教学内容:
主要内容:优化概述,局部优化,基本块的DAG表示及其应用,控制流分析和循环查找算法,到达定值与引用定值链,循环优化。
3、教学重点:
局部优化;DAG的构造与应用。
4、教学难点:
循环查找。
5、教学时间分配及进度安排:
建议本章教学时数6学时。
6、章节内容
1、优化技术简介
2、局部优化
3、控制流分析和循环优化 第十章 代码生成
1、教学目的及要求: 本章介绍编译程序的第五阶段目标代码的生成的设计原理和实现方法,要求掌握四元式到汇编语言的目标代码生成方法。
2、教学内容:
目标机器模型,一个简单代码生成器,寄存器分配,DAG目标代码,窥孔优化。
3、教学重点:
简单代码生成器,寄存器分配策略。
4、教学难点:
寄存器分配策略。
5、教学时间分配及进度安排:
建议本章教学时数3学时。
6、章节内容
1、代码生成概述
2、一个计算机模型
3、一个简单的代码生成器
4、代码生成研究现状
注:使用教材-编译原理(第二版).张素琴,吕映芝,蒋维杜,戴桂兰编著,清华大学出版社,2005.2。参考书:
1)编译原理, 何炎祥, 华中理工大学出版社, 2000.10 2)编译原理, 陈火旺等, 国防工业出版社, 2000.1 3)编译原理, 蒋立源, 西北工业大学出版社, 1999.9
第五篇:编译原理课程设计报告
武 汉 纺 织 大 学
编译原理课程设计实验报告
学院:数学与计算机 专业:计算机 姓名: 班级: 学号: 编译原理
编译原理课设报告
一、实验目的
加强对编译程序的整体认识和了解,巩固《编译原理》课程所学知识。通过本次课程设计掌握编译程序调试技巧和设计编译程序一般的原则,加深对词法分析、语法分析、语义分析等编译阶段及实用编译系统的认识。使学生能将编译理论与实际应用结合起来,提高学生软件开发的能力。
二、实验内容
1)仔细阅读PL/0编译程序文本(编译原理(第二版)张素琴 吕映芝 蒋维杜 戴桂兰 主编
清华大学出版社),并上机调试通过。
2)对PL/0语言进行下列扩充(1)扩充一维整型数组。
扩充var数组:VAR <数组标识名>(<下界>:<上界>)〈下界〉和〈上界〉可用常量标识名。
(2)扩充条件语句的功能使其为:IF<条件>THEN<语句>[ELSE<语句>](3)增加repeat重复语句: REPEAT<语句>{;<语句>}UNTIL<条件> 可根据自己具体情况从中选择2个以上题目进行扩充。
三、实验原理
PL/0语言可以看成PASCAL语言的子集,它的编译程序是一个编译解释执行系统。PL/0的目标程序为假想栈式计算机的汇编语言,与具体计算机无关。
PL/0的编译程序和目标程序的解释执行程序都是用PASCAL语言书写的,因此PL/0语言可在配备PASCAL语言的任何机器上实现。其编译过程采用一趟扫描方式,以语法分析程序为核心,词法分析和代码生成程序都作为一个独立的过程,当语法分析需要读单词时就调用词法分析程序,而当语法分析正确需要生成相应的目标代码时,则调用代码生成程序。
用表格管理程序建立变量、常量和过程表示符的说明与引用之间的信息联系。
当源程序编译正确时,PL/0编译程序自动调用解释执行程序,对目标代码进行解释执行,并按用户程序的要求输入数据和输出运行结果。
四、实验分析
PL/0语言编译程序采用以语法分析为核心、一遍扫描的编译方法。词法分析和代码生成作为独立的子程序供语法分析程序调用。语法分析的同时,提供了出错报告和出错恢复的功能。在源程序没有错误编译通过的情况下,调用类PCODE解释程序解释执行生成的类PCODE代码。
词法分析子程序分析:
词法分析子程序名为GETSYM,功能是从源程序中读出一个单词符号(TOTAKEN),把它的信息放入全局变量 SYM、ID和NUM中,字符变量放入CH中,语法分析器需要单词时,直接从这三个变量中获得。Getch过程通过反复调用Getch子过程从源程序过获取字符,并把它们拼成单词。GETCH过程中使用了行缓冲区技术以提高程序运行效率。
词法分析器的分析过程:调用GETSYM时,它通过GETCH过程从源程序中获得一个字符。如果这个字符是字母,则继续获取字符或数字,最终可以拼成一个单词,查保留字表,如果查到为保留字,则把SYM变量赋成相应的保留字类型值;如果没有查到,则这个单词应是一个用户自定义的标识符(可能是变量名、常量名或是过程的名字),把SYM置为IDENT,把这个单词存入ID变量。查保留字表时使用了二分法查找以提高效率。如果Getch获得的字符是数字,则继续用Getch获取数字,并把它们拼成一个整数或实数,然后把SYM置为 INTEGER或REAL,并把拼成的数值放入NUM变量。如果识别出其它合法的符号(比如:赋值号、大于号、小于等于号等),则把SYM则成相应的类型。如果遇到不合法的字符,把SYM置成NUL。
语法分析子程序分析:
语法分析子程序采用了自顶向下的递归子程序法,语法分析同时也根据程序的语义生成相应三元代码,并提供了出错处理的机制。语法分析主要由分程序分析过程(BLOCK)、参数变量分析过程(ParaDeclaration)、参数变量处理过程(ParaGetSub)、数组处理过程(ParaGetSub)、常量定义分析过程(ConstDeclaration)、变量定义分析过程(Vardeclaration)、语句分析过程(Statement)、表达式处理过程(Expression)、项处理过程(Term)、因子处理过程(Factor)和条件处理过程(Condition)构成。这些过程在结构上构成一个嵌套的层次结构。除此之外,还有出错报告过程(Error)、代码生成过程(Gen)、测试单词合法性及出错恢复过程(Test)、登录名字表过程(Enter)、查询名字表函数(Position)以及列出类 PCODE代码过程(Listcode)作过语法分析的辅助过程。
由PL/0的语法图可知:一个完整的PL/0程序是由分程序和句号构成的。因此,本编译程序在运行的时候,通过主程序中调用分程序处理过程block来分析分程序部分(分程序分析过程中还可能会递归调用block过程),然后,判断最后读入的符号是否为句号。如果是句号且分程序分析中未出错,则是一个合法的PL/0程序,可以运行生成的代码,否则就说明源PL/0程序是不合法的,输出出错提示即可。
if-then-else语句的处理:
按if语句的语法,首先调用逻辑表达式处理过程处理if语句的条件,把相应的真假值放到数据栈顶。接下去记录下代码段分配位置(即下面生成的jpc指令的位置),然后生成 条件转移jpc指令(遇0或遇假转移),转移地址未知暂时填0。然后调用语句处理过程处理 then语句后面的语句或语句块。then后的语句处理完后,如果遇到else,就调用语句处理过程处理else语句后面的语句或语句块,这时当前代码段分配指针的位置就应该是上面的jpc指令的转移位置。通过前面记录下的jpc指令的位置,把它的跳转位置改成当前的代码段指针位置,否则没遇到else,那么此时的当前代码段分配指针的位置也是上面jpc指令的转移位置,也是通过前面记录下的jpc位置指令的位置,把它的跳转到当前的代码段指针位置。
Repeat语句的处理:
首先用CX1变量记下当前代码段分配位置,作为循环的开始位置。然后通过递归调用语句分析过程分析,直到遇到until保留字,如果未对应until则出错。调用条件表达式处理过程生成相应代码把结果放在数据栈顶,再生成条件转移指令,转移位置为上面记录的CX1。
五、相关代码及运行结果
实验代码; PL0.h代码: #include
#ifndef WIRTH_ZYC_ #define WIRTH_ZYC_ using namespace std;
const int norw = 16;
// no.of reserved words 保留字的个数
const int txmax = 100;
// length of identifier table 标示符表的长度(容量)const int al = 10;
// length of identifiers 标示符的最大长度
const int nmax = 14;
// max.no.of digits in numbers 数字的最大长度 const int amax = 2047;
// maximum address 寻址空间
const int levmax = 3;
// maximum depth of block nesting 最大允许的块嵌套层数 const int cxmax = 200;
// size of code array 类PCODE目标代码数组长度(可容纳代码行数)
const int lineLength = 82;// 行缓冲区长度
typedef enum {NUL,IDENT,NUMBER,PLUS,MINUS,TIMES,SLASH,ODDSYM,EQL,NEQ,LSS,LEQ,GTR,GEQ,LPAREN,RPAREN,COMMA,SEMICOLON,PERIOD,BECOMES,BEGINSYM,ENDSYM,IFSYM,THENSYM,WHILESYM,WRITESYM,READSYM,DOSYM,CALLSYM,CONSTSYM,VARSYM,PROCSYM,ELSESYM,REPEATSYM,UNTILSYM} symbol;// symobl类型标识了不同类型的词汇
typedef char alfa[al+1];
// alfa类型用于标识符 typedef enum {CONSTANT,VARIABLE,PROCEDURE,ARRAY} obj0;
// 三种标识符的类型 typedef enum {LIT,OPR,LOD,STO,CAL,INT,JMP,JPC} fct;
// functions typedef set
struct instruction{ fct f;// function code int l;// level,cann't big than levmax
int a;// displacement address,cann't big than amax };
// 类PCODE指令类型,包含三个字段:指令f、层差l和另一个操作数a
/******************************************* * lit 0,a: load constant a
* * opr 0,a: execute operation a
* * lod l,a: load variable l,a
* * sto l,a: store variable l,a
* * cal l,a: call procedure a at level l
* * int 0,a: increment t-register by a
* * jmp 0,a: jump to a
* * jpc 0,a: jump conditional to a
* *******************************************/
typedef struct{ alfa name;obj0 kind;union {
struct{int level,adr,size;}inOther;
int val;}other;} Table;
class PL0 {
protected:
bool listswitch,sourceEnd;char ch;
// last character read symbol sym;
// last symbol read alfa id;
// last identifier read int num;
// last number read
int cc;
// character count int ll;
// line length int kk,err;int cx;
// code allocation index int codeNo;
// code line no.static string errStr[];
// error string
char line[lineLength];
// code line vector
// error array alfa a;
// 词法分析器中用于临时存放正在分析的词
instruction code[cxmax+1];
// destination code array
alfa word[norw+1];
// 保留字表
symbol wsym[norw+1];
// 保留字表中每一个保留字对应的symbol类型
symbol ssym[100];
// 一些符号对应的symbol类型表
合 char mnemonic[8][6];
// 类PCODE指令助记符表
symset declbegsys,statbegsys,facbegsys;// 声明开始、表达式开始和项开始符号集 Table table[txmax+1];
// 符号表
FILE* fin,*fout;
public:
PL0(char* source,char*destination);
~PL0(){fclose(fin),fclose(fout);}
void error(int n);
位置和出错代码
void getsym();
个单词
void getch();
个字符
void gen(fct x,int y,int z);
程序区
void test(symset s1,symset s2,int n);
合法
void block(int lev,int tx,symset fsys);
void enter(obj0 k,int &tx,int &dx,int lev);
int position(alfa id,int tx);的位置
void constdeclaration(int&tx,int&dx,int lev);
void vardeclaration(int&tx,int&dx,int lev);
void listcode(int cx0);
void statement(symset fsys,int tx,int lev);
void expression(symset fsys,int tx,int lev);
void term(symset fsys,int tx,int lev);
void factor(symset fsys,int tx,int lev);
void condition(symset fsys,int tx,int lev);
void arraydeclaration(int& tx,int& dx,int lev);
void interpret();
执行程序
int base(int l,int b,int s[]);
基地址
void SaveCode();
// 构造函数
// 析构函数
// 出错处理,打印出错
// 词法分析,读取一
// 漏掉空格,读取一// 生成目标代码,并送入目标
// 测试当前单词符号是否
// 分程序分析处理过程
// 登入名字表
// 查找标示符在名字表中
// 常量定义处理
// 变量说明处理
// 列出目标代码清单
// 语句部分处理
// 表达式处理
// 项处理
// 因子处理
// 条件处理
// 数组说明处理
// 对目标代码的解释
// 通过静态链求出数据区的 // 保存代码
};#endif PL0.cpp代码: #include “pl0.h”
// 错误字符串数组
string PL0::errStr[]={“",”error 0001: 常数说明中“=”写成“:=”“, ”error 0002: 常数说明中的“=”后应为数字“, ”error 0003: 常数说明中的标识符后应是“=”“, ”error 0004: const,var,procedure后应为标识符“, ”error 0005: 漏掉了‘,’或‘;’“, ”error 0006: 过程说明后的符号不正确(应是语句开始符或过程开始符)“, ”error 0007: 应是语句开始符“, ”error 0008: 过程体内语句部分的后跟符不正确“, ”error 0009: 程序皆为丢了句号‘.’“, ”error 0010: 语句之间漏了‘;’“, ”error 0011: 标识符没说明“, ”error 0012: 赋值语句中,赋值号左部标识符属性应是变量“, ”error 0013: 赋值语句左部标识符应是赋值号:=“, ”error 0014: call后应为标识符“, ”error 0015: call后标识符属性应为过程“, ”error 0016: 条件语句中丢了then“, ”error 0017: 丢了end或;“, ”error 0018: while型循环语句中丢了do“, ”error 0019: 语句后的标识符不正确“, ”error 0020: 应为关系运算符“, ”error 0021: 表达式内标识符属性不能是过程“, ”error 0022: 表达式中漏掉了右括号‘)’“, ”error 0023: 因子后的非法符号“, ”error 0024: 表达式开始符不能是此符号“, ”error 0025: 文件在不该结束的地方结束了“, ”error 0026: 结束符出现在不该结束的地方“, ”error 0027: “,”error 0028: “,”error 0029: “,”error 0030: “, ”error 0031: 数越界“, ”error 0032: read语句括号中标识符不是变量“, ”error 0033: else附近错误“ , ”error 0034: repeat附近错误“};
// PL0构造函数
PL0::PL0(char* source,char*destination){ listswitch=true,sourceEnd=false;
strcpy(word[1],”begin“);
// 初始化存储保留字
strcpy(word[2],”call“);strcpy(word[3],”const“);
strcpy(word[4],”do“);strcpy(word[5],”else“);strcpy(word[6],”end“);strcpy(word[7],”if“);strcpy(word[8],”odd“);strcpy(word[9],”procedure“);strcpy(word[10],”read“);
strcpy(word[11],”repeat“);strcpy(word[12],”then“);strcpy(word[13],”until“);strcpy(word[14],”var“);
strcpy(word[15],”while“);strcpy(word[16],”write“);
wsym[1]= BEGINSYM;
wsym[2]= CALLSYM;留字对应的symbol类型
wsym[3]= CONSTSYM;
wsym[4]= DOSYM;wsym[5]= ELSESYM;
wsym[6]= ENDSYM;wsym[7]= IFSYM;
wsym[8]= ODDSYM;wsym[9]= PROCSYM;
wsym[10]= READSYM;
wsym[11]= REPEATSYM;wsym[12]= THENSYM;wsym[13]= UNTILSYM;wsym[14]= VARSYM;
wsym[15]= WHILESYM;wsym[16]= WRITESYM;
memset(code,0,sizeof(code));memset(ssym,0,100*sizeof(symbol));memset(table,0,sizeof(table));memset(line,0,sizeof(line));
ssym['+']= PLUS;
类型表
ssym['-']= MINUS;ssym['*']= TIMES;ssym['/']= SLASH;ssym['(']= LPAREN;ssym[')']= RPAREN;ssym['=']= EQL;ssym[',']= COMMA;ssym['.']= PERIOD;
// 初始化保留字表中每一个保
// 初始化一些符号对应的symbol
ssym['#']= NEQ;ssym['<']= LSS;ssym['>']= GTR;ssym[';']= SEMICOLON;
strcpy(mnemonic[LIT],” lit “);
// 初始化类PCODE指令助记符表
strcpy(mnemonic[OPR],” opr “);strcpy(mnemonic[LOD],” lod “);strcpy(mnemonic[STO],” sto “);strcpy(mnemonic[CAL],” cal “);strcpy(mnemonic[INT],” int “);strcpy(mnemonic[JMP],” jmp “);strcpy(mnemonic[JPC],” jpc “);
declbegsys.insert(CONSTSYM),declbegsys.insert(VARSYM),declbegsys.insert(PROCSYM);// 初始化声明开始符号集合 statbegsys.insert(BEGINSYM),statbegsys.insert(CALLSYM),statbegsys.insert(IFSYM),statbegsys.insert(WHILESYM);// 初始化表达式开始符号集合facbegsys.insert(IDENT),facbegsys.insert(NUMBER),facbegsys.insert(LPAREN);// 初始化项开始符号集合
err= 0;cc= 0;
// 行缓冲区指针
cx= 0;
// 代码分配指针,代码生成模块总在cx所指位置生成新的代码
ll= 0;
// 行缓冲区长度
ch= ' ';
// last character read
}
kk= al;
// 引入此变量是出于程序性能考虑 codeNo=0;
// code line no.fin=fopen(source,”r“);fout=fopen(destination,”w“);// 出错处理,打印出错位置和出错代码 void PL0::error(int n){ char s[10];sprintf(s,”第 %d 行:“,codeNo);errorString.push_back(s+errStr[n]);err= err+1;//error count }//error end // 词法分析,读取一个单词
void PL0::getsym(){ if(sourceEnd)
return;int i,j,k;while(ch ==' '||ch==9)
getch();
// cls space and tab if(isalpha(ch))// id or reserved word {
k=0;
memset(a,0,al+1);
// 检测一个单词长度 do{ if(k < al){
a[k]= ch;
k= k+1;} getch();if(sourceEnd)
return;}while(isalpha(ch)||isdigit(ch));if(k >= kk)kk = k;else { do{
a[kk]= ' ';
kk= kk-1;}while(kk > k);} strcpy(id,a);i= 1;j= norw;// 判断是否是关键字(二分搜索)do{ k=(i+j)/ 2;if(strcmp(id, word[k])<=0)
j= k-1;if(strcmp(id,word[k])>=0)
i= k+1;}while(i<=j);if(i-1 > j)
sym= wsym[k];else
sym= IDENT;} else if(isdigit(ch))// number { k= 0;num= 0;sym= NUMBER;do{
num= 10 * num + ch-'0';
k= k+1;
getch();}while(isdigit(ch));if(k > nmax)
error(30);} else if(ch == ':'){ getch();if(ch == '='){
sym= BECOMES;
getch();} else
sym= NUL;} else if(ch == '<')
// extra stuff added to support <= { getch();if(ch== '='){
sym= LEQ;
getch();} else
sym= LSS;} else if(ch == '>'){ getch();if(ch == '='){
sym= GEQ;
getch();} else
sym= GTR;} else
// end of extra stuff { sym= ssym[ch];// 其它符号的赋值
getch();} }
// 漏掉空格,读取一个字符
void PL0::getch(){ if(cc == ll){
if(feof(fin))
{
if(sym!=PERIOD)
error(25);
sourceEnd=true;
return;
}
cc= 0;
fgets(line,lineLength,fin);
codeNo++;
ll=strlen(line);
if(line[ll-1]==10)ll--;} ch= line[cc];cc= cc+1;}
// 生成目标代码,并送入目标程序区void PL0::gen(fct x,int y,int z){ if(cx > cxmax){
cout<<”Program too longn“;
return;}
code[cx].f= x;code[cx].l= y;code[cx].a= z;
cx= cx+1;}//gen end
// 测试当前单词符号是否合法
void PL0::test(symset s1,symset s2,int n){ if(sourceEnd)
return;if(s1.find(sym)==s1.end()){
error(n);
symset::iterator it;
for(it=s2.begin();it!=s2.end();it++)
s1.insert(*it);//s1=s1+s2
while(s1.find(sym)==s1.end())
getsym();} }//test end
// 分程序分析处理过程
void PL0::block(int lev,int tx,symset fsys){ if(sourceEnd)
return;int dx;// data allocation index int tx0;// initial table index int cx0;// initial code index
dx= 3;
// 变量的个数 tx0= tx;// 表指针
table[tx].other.inOther.adr= cx;gen(JMP,0,0);if(lev>levmax)error(32);do{
if(sym == CONSTSYM)
// 处理常量声明
{
getsym();
do{
constdeclaration(tx,dx,lev);
while(sym == COMMA)
{
}
getsym();
constdeclaration(tx,dx,lev);} if(sym ==SEMICOLON)
getsym();else
error(5);}while(sym==IDENT);if(sym == VARSYM)
// 处理变量声明 { getsym();do{
vardeclaration(tx,dx,lev);
while(sym == COMMA){
getsym();
vardeclaration(tx,dx,lev);
}
if(sym ==SEMICOLON)
getsym();
else
error(5);}while(sym==IDENT);} while(sym ==PROCSYM)
// 处理过程的声明 { getsym();if(sym ==IDENT){
enter(PROCEDURE,tx,dx,lev);
getsym();} else
error(4);if(sym ==SEMICOLON)
getsym();else
error(5);symset tmp = fsys;tmp.insert(SEMICOLON);block(lev+1,tx,tmp);if(sym == SEMICOLON){
getsym();
symset tmp = statbegsys;
for(int i= IDENT;i<=PROCSYM;i++)
tmp.insert((symbol)i);
test(tmp,fsys,6);
}
else
error(5);
}
symset tmp=statbegsys;
tmp.insert(IDENT);
test(tmp,declbegsys,7);}while(declbegsys.find(sym)!=declbegsys.end());
code[table[tx0].other.inOther.adr].a= cx;table[tx0].other.inOther.adr= cx;// start adr of code table[tx0].other.inOther.size=dx;
cx0= cx;gen(INT,0,dx);symset tmp=statbegsys;for(int i=SEMICOLON;i <= ENDSYM;i++)
tmp.insert((symbol)i);statement(tmp,tx,lev);gen(OPR,0,0);// return symset s2;test(fsys,s2,8);listcode(cx0);}// block end
// 登入名字表
void PL0::enter(obj0 k,int &tx,int &dx,int lev){ tx= tx+1;strcpy(table[tx].name,id);table[tx].kind=k;switch(k){ case CONSTANT:
if(num>amax)
{
error(31);
num=0;
}
table[tx].other.val=num;
break;case VARIABLE:
table[tx].other.inOther.level=lev;
table[tx].other.inOther.adr=dx;
dx++;
break;case PROCEDURE:
table[tx].other.inOther.level=lev;
break;case ARRAY:
table[tx].other.inOther.size = lev;
break;} }//enter end
// 查找标示符在名字表中的位置
int PL0::position(alfa id,int tx)//find identifier id in table { int i;strcpy(table[0].name, id);i= tx;while(strcmp(table[i].name,id)!=0)i--;return i;}//position end
// 常量定义处理
void PL0::constdeclaration(int&tx,int&dx,int lev){ if(sym == IDENT){
getsym();
if(sym>=EQL&&sym<=BECOMES)
{
if(sym ==BECOMES)
error(1);
getsym();
if(sym == NUMBER)
{
enter(CONSTANT,tx,dx,lev);
getsym();
}
else
error(2);
}
else
error(3);} else
error(4);}// constdeclaration end
// 变量说明处理
void PL0::vardeclaration(int&tx,int&dx,int lev){ if(sym == IDENT){
enter(VARIABLE,tx,dx,lev);
getsym();} else
error(4);}//vardeclaration end
// 数组说明处理
void PL0::arraydeclaration(int&tx,int&dx,int lev){
int upscript=0,downscript=0;getsym();if(sym == NUMBER || sym == CONSTSYM){
if(num == 0)
{
upscript = num;
getsym();
}
else
error(32);} if(sym == COMMA)
getsym();else
error(32);if(sym == NUMBER || sym == CONSTSYM){
downscript = num;
getsym();} if(sym!= RPAREN)
} error(32);else { enter(ARRAY,tx,dx,downscript+1);getsym();} // 列出目标代码清单
void PL0::listcode(int cx0)//list code generated for this block { int i;if(listswitch)
for(i= cx0;i cout<<”“< <<”“< // 语句部分处理 void PL0::statement(symset fsys,int tx,int lev){ if(sourceEnd) return;int i,cx1,cx2;if(sym ==IDENT){ i= position(id,tx); if(i == 0) error(11); else if(table[i].kind!=VARIABLE) { error(12); i= 0; } getsym(); if(sym ==BECOMES) getsym(); else error(13); expression(fsys,tx,lev); if(sym!= SEMICOLON) error(10); if(i!= 0) gen(STO,lev-table[i].other.inOther.level,table[i].other.inOther.adr); } else if(sym == READSYM){ getsym();if(sym!=LPAREN) error(34);else do{ getsym(); if(sym==IDENT) i=position(id,tx); else i=0; if(i==0) error(35); else { gen(OPR,0,16); gen(STO,lev-table[i].other.inOther.level,table[i].other.inOther.adr); } getsym(); }while(sym == COMMA); if(sym!= RPAREN) { error(33); while(fsys.find(sym)!=fsys.end())getsym(); } else getsym();} else if(sym == WRITESYM){ getsym();if(sym==LPAREN){ do{ getsym(); symset tmp=fsys; for(int t=RPAREN;t<=COMMA;t++) tmp.insert((symbol)t); expression(tmp,tx,lev); gen(OPR,0,14); }while(sym==COMMA); if(sym!=RPAREN) error(33); else getsym();} gen(OPR,0,15);} else if(sym ==CALLSYM){ getsym();if(sym!=IDENT) error(14);else { i= position(id,tx); if(i == 0) error(11); else if(table[i].kind = PROCEDURE) gen(CAL,lev-table[i].other.inOther.level,table[i].other.inOther.adr); else error(15); getsym();} } else if(sym ==IFSYM){ getsym();symset tmp=fsys;for(int i = THENSYM;i<= DOSYM;i++) tmp.insert((symbol)i);condition(tmp,tx,lev);if(sym == THENSYM) getsym();else error(16);cx1= cx;gen(JPC,0,0);tmp.insert(ELSESYM);statement(tmp,tx,lev);getsym(); code[cx1].a= cx; if(sym == ELSESYM){ getsym(); cx2=cx; gen(JMP,0,0); code[cx1].a=cx; statement(fsys,tx,lev); code[cx2].a=cx;} } else if(sym ==BEGINSYM){ getsym();symset tmp=fsys;for(int i=SEMICOLON;i<=ENDSYM;i++) tmp.insert((symbol)i);statement(tmp,tx,lev);tmp=statbegsys;tmp.insert(SEMICOLON);while(tmp.find(sym)!=tmp.end()){ if(sourceEnd)return; if(sym ==SEMICOLON||sym ==ENDSYM) getsym(); else if(sym=PERIOD) { error(26); getsym(); } else error(10); tmp=fsys; for(i=SEMICOLON;i<=ENDSYM;i++) tmp.insert((symbol)i); if(sourceEnd)return; if(sym==ENDSYM) break; statement(tmp,tx,lev);} if(sym ==ENDSYM) getsym();else if(!sourceEnd) error(17);} else if(sym ==WHILESYM){ cx1= cx; // 记下当前代码分配位置,这是while循环的开始位置 getsym();symset tmp=fsys;tmp.insert(DOSYM);condition(tmp,tx,lev); cx2= cx; // 记下当前代码分配位置,这是while的do中的语句的开始位置 gen(JPC,0,0); if(sym ==DOSYM) getsym(); else error(18); statement(fsys,tx,lev); gen(JMP,0,cx1); code[cx2].a= cx;} else if(sym == REPEATSYM){ symset temp1, temp2; temp1= fsys,temp1.insert(SEMICOLON),temp1.insert(UNTILSYM); cx1= cx; getsym(); statement(temp1,tx,lev); temp2 = statbegsys; temp2.insert(SEMICOLON); while(temp2.find(sym)!= temp2.end()) { if(sym == SEMICOLON) getsym(); else error(34); statement(temp1,tx,lev); } if(sym == UNTILSYM) { getsym(); condition(fsys,tx,lev); gen(JPC,0,cx1); } else error(34); } symset setT;test(fsys,setT,19);}//statement end // 表达式处理 void PL0::expression(symset fsys,int tx,int lev){ symbol addop;symset tmp=fsys;for(int t=PLUS;t<=MINUS;t++) tmp.insert((symbol)t);if(sym>=PLUS&&sym<=MINUS){ addop= sym; getsym(); term(tmp,tx,lev); if(addop ==MINUS) gen(OPR,0,1);} else term(tmp,tx,lev);while(sym >=PLUS&&sym<=MINUS){ addop= sym; getsym(); term(tmp,tx,lev); if(addop ==PLUS) gen(OPR,0,2); else gen(OPR,0,3);} }// expression end // 项处理 void PL0::term(symset fsys,int tx,int lev){ if(sourceEnd) return;symbol mulop;symset tmp=fsys;for(int t=TIMES;t<=SLASH;t++) tmp.insert((symbol)t);factor(tmp,tx,lev);while(sym>=TIMES && sym<=SLASH){ mulop= sym; getsym(); factor(tmp,tx,lev); if(mulop ==TIMES) gen(OPR,0,4); else gen(OPR,0,5);} }// term end // 因子处理 void PL0:: factor(symset fsys,int tx,int lev){ int i;test(facbegsys,fsys,24);while(facbegsys.find(sym)!=facbegsys.end()){ if(sym ==IDENT) { i= position(id,tx); if(i == 0) error(11); else switch(table[i].kind) { case CONSTANT: gen(LIT,0,table[i].other.val); break; case VARIABLE: gen(LOD,lev-table[i].other.inOther.level,table[i].other.inOther.adr); break; case PROCEDURE: error(21); break; } getsym(); } else if(sym ==NUMBER) { if(num>amax) { error(31); num= 0; } gen(LIT,0,num); getsym(); } else if(sym ==LPAREN) { getsym(); symset tmp=fsys; tmp.insert(RPAREN); expression(tmp,tx,lev); if(sym == RPAREN) getsym(); else error(22); } test(fsys,facbegsys,23);} }//factor end // 条件处理 void PL0::condition(symset fsys,int tx,int lev){ symbol relop;symset tmp=fsys;tmp.insert(EQL),tmp.insert(NEQ),tmp.insert(LSS),tmp.insert(LEQ),tmp.insert(GTR),tmp.insert(GEQ); if(sym == ODDSYM){ getsym(); expression(fsys,tx,lev); gen(OPR,0,6);} else { expression(tmp,tx,lev); if(tmp.find(sym)==tmp.end()) error(20); else { relop= sym; getsym(); expression(fsys,tx,lev); switch(relop) { case EQL: gen(OPR,0,8); break; case NEQ: gen(OPR,0,9); break; case LSS: gen(OPR,0,10); break; case GEQ: gen(OPR,0,11); break; case GTR: gen(OPR,0,12); break; case LEQ: gen(OPR,0,13); break; } } } }//condition end // 对目标代码的解释执行程序 void PL0::interpret(){ int err1=errorString.size();if(err1>0){ cout<<”存在%d个错误:“< cout< t= t+1; s[t]= i.a; break;case OPR: switch(i.a)//operator { case 0:// return t= b-1; p= s[t+3]; b= s[t+2];break;case 1: s[t]=-s[t];break;case 2: t= t-1;s[t]= s[t]+s[t+1];break;case 3: t= t-1;s[t]= s[t]-s[t+1];break;case 4: t= t-1;s[t]= s[t]*s[t+1];break;case 5: t= t-1;s[t]= s[t] / s[t+1];break;case 6: if(s[t]%2) s[t]=1;else s[t]=0;break;case 8: t= t-1;if(s[t]==s[t+1]) s[t]=1;else s[t]=0;break;case 9: t= t-1;if(s[t]==s[t+1]) s[t]=0;else s[t]=1;break; case 10: t= t-1;if(s[t] s[t]=1;else s[t]=0;break;case 11: t= t-1;if(s[t]>=s[t+1]) s[t]= 1;else s[t]=0;break;case 12: t= t-1;if(s[t]>s[t+1]) s[t]= 1;else s[t]=0;break;case 13: t= t-1;if(s[t]<=s[t+1]) s[t]= 1;else s[t]=0;break;case 14: cout<<”“< t= t+1; s[t]= s[base(i.l,b,s)+i.a]; break; case STO: s[base(i.l,b,s)+i.a]= s[t]; t= t-1; break; case CAL:// generate new block mark s[t+1]= base(i.l,b,s); s[t+2]= b; s[t+3]= p; b= t+1; p=i.a; break; case INT: t= t+i.a; break; case JMP: p= i.a; break; case JPC: if(s[t] == 0) p= i.a; t= t-1; break; }//switch end }while(p!=0); cout<<” End PL/0n“;} // interpret end // 通过静态链求出数据区的基地址 int PL0::base(int l,int b,int s[]){ int b1;b1= b;//find base l levels down while(l>0){ b1= s[b1]; l= l-1;} return b1;} // 保存代码 void PL0::SaveCode(){ if(fout) for(int i=0;i fprintf(fout,”%d %s %d %dn “,i,mnemonic[code[i].f],code[i].l,code[i].a);} TestPL0.cpp代码: #include ”pl0.h“ void main(){ PL0 cp(”testPas2.txt“,”nasm.txt"); symset fsys; fsys.insert(PERIOD);fsys.insert(CONSTSYM),fsys.insert(VARSYM),fsys.insert(PROCSYM);fsys.insert(BEGINSYM),fsys.insert(CALLSYM),fsys.insert(IFSYM),fsys.insert(WHILESYM);cp.getsym(); // 词法分析,分析一个词 cp.block(0,0,fsys); // 分程序分析处理功能 cp.SaveCode(); // 保存代码 cp.interpret(); // 对目标代码的解释执行程序 } 实验运行结果: 运行的的文件见下图右侧:实验中我是固定了文件名的,可以是改写成动态输入,由于在测试中我把所有的测试语句都放在同一个文件中了,没有太多的必要。 六、心得体会 在编译程序实现的过程中反复使用了递归调用的思想,且也使用了模块化处理问题的思想,使用模块化的思想关键是在抽象阶段要抽象出对应的模块,且模块的层次必须是清晰的。 在实现此程序中,由于要实现关键字和符号表中字段的搜索,实现中就必须注意快速查找的方法,而在实现的过程中多次用到了二分搜索的方法,这是个比较快的搜索方法。 由于此程序的实现相对比较复杂,且不方便调试,改进时可以把此程序的词法分析,语法分析和执行原代码作为单独的测试程序来测试,这样也方便大家来调试。 通过本次的课设我知道了一个算法的设计是需要静下心来仔细的研究的,且实现中必须先了解程序的整个流程,也就是说在编程中首先必须看懂那些对应的UML图,只有在图的指导下,编程中才不会盲目,也有一定的方向性。同样在编程中必须注意代码的规范,多写一些对应的注释是很必要的,要时刻想这代码并不是给你自己看的,而是必须要给别人看,因此我觉得代码的规范是相当重要的。>s[t];break;};break;case LOD: