《编译原理》实验指导书-2015

合集下载

民生—编译原理实验指导书

民生—编译原理实验指导书

编译原理实验指导书实验一词法分析一、实验目的通过实现PL/0语言(一种示例小语言)的词法分析器,理解词法分析过程,掌握程序各部分之间的接口安排。

二、实验要求1、要求每位同学单独完成词法分析器,并接受检查;2、撰写实验报告。

(1) 用有限自动机画出“三、实验内容”中的词法规则;(2) 实验报告不要摘抄全部代码,但需要流程图的形式描述程序结构;(3) 必须书写设计和实现的过程中出现的一些问题以及解决方法。

三、实验内容1、PL/0语言的单词结构关键字(10个):begin, end ,if ,then, while, do, const, var,call,procedure标识符:字母序列,最大长度10常数:整型常数算符和界符(17个):+,-,*,/,odd,=,<>,<,>,<=,>=,:=,(,) ,, ,.,;2、单词的种别划分标识符作为一种常数作为一种算符和界符每个单词作为一个单独种别3、PL/0的语言的词法分析器将要完成以下工作:(1)跳过分隔符(如空格,回车,制表符);(2)识别诸如begin,end,if,while等保留字;(3)识别非保留字的一般标识符。

(4)识别数字序列。

(5)识别:=,<=,>=之类的特殊符号。

4、词法分析器的实现方式:把词法分析器设计成一个独立子程序,以便于语法分析器调用。

词法分析器运行一次产生一个单词符号。

5、词法分析器的输出(单词种别编码,单词本身的值)说明:词法分析器的输出应该是(种别编码,属性值),但当种别不能唯一代表单词本身时,就需要组织符号表了。

为了降低实验难度,实验输出就改成了(种别编码,单词本身的值)。

6、词法分析器和语法分析器的接口设计要把词法分析器设计成独立的子程序,以便于被语法分析器调用,每调用语法分析器一次,就识别出一个单词符号。

实验二利用算符优先分析方法设计一个计算器一、实验目的在实验一的基础上,利用算符优先分析方法设计一个计算器,以加深对算符优先分析过程和语义分析过程的理解。

编译原理实验指导书

编译原理实验指导书

编译原理实验指导书《编译原理》实验指导书太原科技大学计算机学院 -3-1序《编译原理》是国内外各高等院校计算机科学技术类专业,特别是计算机软件专业的一门重要专业课程。

该课程系统地向学生介绍编译程序的结构、工作流程及编译程序各组成部分的设计原理和实现技术。

由于该课程理论性和实践性都比较强,内容较为抽象复杂,涉及到大量的软件设计和算法,因此,一直是一门比较难学的课程。

为了使学生更好地理解和掌握编译原理和技术的基本概念、基本原理和实现方法,实践环节非常重要,只有经过上机进行程序设计,才能使学生对比较抽象的教学内容产生具体的感性认识,增强学生综合分析问题、解决问题的能力,并对提高学生软件设计水平大有益处。

为了配合《编译原理》课程的教学,考虑到本课程的内容和特点,本指导书设置了七个综合性实验,分别侧重于词法分析、NFA的确定化、非递归预测分析、算符优先分析器的构造、LR分析、语义分析和中间代码的生成、基于DAG的基本块优化,以支持编译程序的各个阶段,基本涵盖了《编译原理》课程的主要内容。

本指导书可作为《编译原理》课程的实验或课程设计内容,在课程教学的同时,安排学生进行相关的实验。

实验平台可选择在MS-DOS或Windows操作系统环境,使用C/C++的任何版本作为开发工具。

学生在做完试验后,应认真撰写实验报告,内容应包括实验名称、实验目的、实验要求、实验内容、测试或运行结果等。

目录实验一词法分析 ........................................................... 错误!未定义书签。

实验二 NFA的确定化.................................................... 错误!未定义书签。

实验三非递归预测分析 ............................................... 错误!未定义书签。

编译原理实验指导书

编译原理实验指导书

编译原理实验指导书计算机学院实验1 词法分析程序一、实验目的构造simple语言的词法分析程序,程序要求能对输入的字符串流进行词法分析。

在实验的过程中,学会应用单词分析的方法——NFA(非确定有穷自动机)和DFA(确定有穷自动机),加深对词法分析原理的理解。

二、实验内容编写为任一正则文法(见实验参考(一)simple惯用的词法)构造非确定有穷自动机NFA并转换成确定有穷自动机DFA,并对任给的一个输入串(见实验参考(二)测试用输入串)进行词法分析的程序,程序的输出为单词的序列(见实验参考(三)程序输出形式)。

三、实验参考(一)simple 惯用的词法1. 下面是语言的关键字:Begin if then while do end所有的关键字都是保留字,并且必须是小写。

2. 下面是专用符号::= + * / < <= <> > >= = ; ( ) #3. 其他单词是标识符(ID)和整型常数(NUM),通过下列正规式定义:ID = letter(letter| digit)*NUM = digit digit*letter = a |…| z | A |…| Zdigit = 0 |…| 9小写和大写字母是有区别的。

思考:构造实数的正规表达式,力争实现对实数的识别及表示。

4. 空格由空白、换行符和制表符组成。

空格一般用来分隔ID、NUM、运算符和关键字,词法分析阶段通常被忽略。

5. 各种单词符号对应的种别码如下表所示:(二)词法分析程序的功能1. 输入为所给文法的源程序字符串。

2. 程序的输出形式为单词串的输出形式。

所输出的每一单词,均按形如(syn,token和sum)的二元式编码。

其中,syn 为单词种别码;token为存放的单词自身字符串;sum为整型常数。

3.测试源程序片断:begin x:=9; if x>0 then x:=2*x+1/3;end #。

编译原理实验指导书

编译原理实验指导书

陕西理工学院数学与计算机科学学院《编译原理》实验指导班级网络10级指导教师曹阳计算机工程教研室2012年8月25日编译原理实验指导书一、实验的目的《编译原理》是针对计算机专业的学生开设的一门专业基础课程,对引导学生进行科学思维和提高学生解决实际问题的能力有重要的作用。

开设“编译原理实验”的主要目的是让学生加深理解编译原理的基本理论、方法、词法分析、语法分析、中间代码的生成直到最后的代码生成,了解编译器原理,从而提高学生分析问、题解决问题的能力。

通过实验实现以下基本目标:1.深化已学的知识,完成从理论到实践的转化通过实验,进一步加深对编译原理基本思想、原理的了解。

2. 提高分析和解决实际问题的能力实验不仅是编译原理的一次模拟训练,同时通过实验,积累经验,提高分析和解决问题的能力。

3.培养“开拓创新”能力大力提倡和鼓励在程序中使用新方法,新技术。

激发学生实践的积极性与创造性,开拓思路,设计新算法,进行新创意,培养创造性能力。

二、参加实验的学生应具备的条件参加实验的学生应当具备计算机程序设计的一些基础的知识。

即学生应当熟练掌握和使用一种计算机操作系统(windows操作系统等)、一种程序设计语言(vb、vc、c、delphi等)。

三、实验要求实验的要求体现于整个工作的各个阶段中,可根据所选课题的特点而有所侧重,但应达到如下的基本要求:(1) 根据课题任务制定合理、可行的工作计划任务;(2) 制定适当的技术方案;(3) 学生在老师的指导下独立完成设计过程;(4) 撰写实验报告(包括实验内容中各个阶段所要求的文字材料);(5) 通过实验检查评定;四、实验项目与内容提要五、实验课程考核办法1、该实验课程考核成绩按百分制计算,满分为100分,60分为及格,60分以上者可获取该学分。

2、该实验课考核由三部分组成:实验课前预习(20%),实验操作(60%),实验报告(20%)。

3、各部分成绩由实验指导教师在每个实验项目完成后分别给出,在学期结束后或完成全部实验项目后综合给出该门实验课的成绩。

刘军_编译原理实验指导书_09软件1-2班

刘军_编译原理实验指导书_09软件1-2班

《编译原理》课程实验指导书一、使用说明《编译原理》课程实验指导书(以下简称:指导书)是针对计算机学院所开设的对应课程的上机实验而编写的教学文件,供学生上机实验时使用。

上机的工作环境要求:Windows 2000或以上操作系统、C++ 6.0或者其它高级程序设计语言。

学生应按指导教师的要求独立完成实验,并按要求撰写实验报告。

每一个实验,编程上机调试并且提交电子文档实验报告,以学号姓名作为文件名上传。

报告内容至少包含如下内容:1、学生基本情况:专业班级、学号、姓名2、实验题目、实验内容3、设计分析4、源程序代码5、测试用例(尽量覆盖所有分支)6、实验总结二、实验说明1、实验一:词法分析器设计实验类别:基础性实验实验学时:4分组人数:1人/组1、实验目的:(1)掌握词法分析器的构造过程以及基本方法。

(2)理解正规式、NFA、DFA及最小化DFA的转换过程和方法。

2、实验内容给定一个正规式R=XY*|YX*Y|XYX,请先在练习本上将此正规式转变为NFA、DFA、最小化DFA;对你所完成的最小化DFA进行编程,完成词法分析器工作。

2、实验二:算术表达式递归下降分析程序设计实验类别:设计性实验实验学时:4分组人数:1人/组1、实验目的:(1)掌握自上而下语法分析的要求与特点。

(2)掌握递归下降语法分析的基本原理和方法。

(3)掌握相应数据结构的设计方法。

2、实验内容:编程实现给定算术表达式的递归下降分析器。

算术表达式文法如下:E→E+T | TT→T*F | FF→(E) | i3、设计说明:首先改写文法为LL(1)文法;然后为每一个非终结符,构造相应的递归过程,过程的名字表示规则左部的非终结符;过程体按规则右部符号串的顺序编写。

编写者签字:刘军审阅者签字:张俊分管实验教学领导签字:王海晖。

编译原理实验指导书

编译原理实验指导书

编译原理实验指导书第1节概述1、本课程实践的目的和任务编译原理是一门实践性很强的课程,只有通过实践,才能真正掌握。

实际的编译程序是十分复杂的,有时由多达十几万条指令组成。

为此,编译原理的实践教学,采用简化编译过程的办法,选择最关键的3个环节──词法分析、语法分析(包括语义处理、产生无优化的目标指令)、连接调试,进行编程和调试训练。

每个环节作为一个实践课题。

2、实践方法任何一个实用的高级语言,其语法都比较复杂,如选其作为源语言,很难实践全过程。

故本实践将定义一个简化的语言──PASCAL语言的一个子集作为源语言,也可以自行定义一个简单的C语言子集,在3个题目中选择两个题目,也可以自行选择与编译技术相关的实验题目,设计调试出它的编译程序。

前后贯穿这一条主线进行实践。

每次都可利用课余时间编程,利用上机时间进行输入和调试。

建议使用C或C++或JAVA语言。

3、实践报告的规范和要求每个课题完成后写出实践报告。

实践报告包括程序设计时考虑的算法和方法;调试过程中出现的问题和解决的措施;提交电子版的程序清单和调试时所用的源程序。

4、简化的PASCAL语言子集的定义〈PASCAL子集程序〉→〈变量说明〉〈分程序〉。

〈变量说明〉→〈空〉|VAR〈变量表〉:INTEGER;〈变量表〉→〈变量〉|〈变量〉,〈变量表〉〈变量〉→〈标识符〉〈分程序〉→BEGIN〈语句组〉END〈语句组〉→〈语句〉|〈语句〉;〈语句组〉〈语句〉→〈赋值语句〉|〈条件语句〉|〈WHILE语句〉|〈分程序〉〈赋值语句〉→〈变量〉:=〈算术表达式〉〈条件语句〉→IF〈布尔表达式〉THEN〈语句〉ELSE〈语句〉〈WHILE语句〉→WHILE〈布尔表达式〉DO〈语句〉〈算术表达式〉→〈项〉|〈算术表达式〉+〈项〉|〈算术表达式〉-〈项〉〈项〉→〈初等量〉|〈项〉*〈初等量〉|〈项〉/〈初等量〉〈初等量〉→〈无符号数〉|〈变量〉|(〈算术表达式〉)〈关系表达式〉→〈算术表达式〉〈关系运算符〉〈算术表达式〉〈标识符〉→〈字母〉|〈标识符〉〈字母〉|〈标识符〉〈数字〉〈无符号数〉→〈数字〉|〈无符号数〉〈数字〉〈关系运算符〉→〈|〈=| =| 〉=| 〉|〈〉〈字母〉→ A│B│C│D│E│F│G│H│I│J│K│L│M│N│O│P│Q│R│S│T││U│V│W│X│Y│Z〈数字〉→ 1│2│3│4│5│6│7│8│9│0第2节词法分析本节进行词法分析程序的编程与调试。

编译原理实验指导书

编译原理实验指导书

《编译原理》实验指导书别小川于枫编写适用专业:计算机科学与应用江苏科技大学电子信息学院2005年2月前言《编译原理》是计算机专业的一门核心课程,在计算机本科教学中占有十分重要的地位。

由于《编译原理》课程兼有很强的理论性和实践性,并且编译程序构造的算法比较复杂,因而让学生在学习时普遍感到内容抽象、不易理解,难易掌握。

但是掌握编译原理的基本理论和设计思想是非常重要的,尤其是将本课程的理论知识与计算机应用中的许多领域紧密联系与广泛应用结合。

将有利于学生提高专业素质和适应社会多方面需要的能力。

因此,通过理论授课和上机实践,使学生对编译的基本概念、原理和方法有完整的和清楚的理解,并能正确地、熟练地加以运用。

通过实验逐步提高学生的编程能力和调试程序的能力以及解决实际问题的能力。

使学生培养出扎实的软件开发基本技能,并养成良好的编程风格,为进一步学习后续课程和将来从事应用软件开发奠定良好的基础。

实验课时具体内容安排如下:一、实验课的性质和目的(1)深刻理解程序语言编译系统的结构及各部分的功能。

(2)熟练掌握设计和构造程序语言编译系统的基本原理和技术。

(3)能独立编写清晰、工整、结论正确的编译原理的源程序。

(4)能学会上机进行正确调试,并进行程序修改。

即培养发现程序错误,排除错误的能力和经验。

二、实验课的基本要求:(1)掌握编译程序的功能和结构。

(2)掌握词法分析器的设计方法与实现步骤加深对讲授内容的理解,尤其是一些语法给定,通过上机实验帮助掌握。

(3)掌握语法分析器的设计方法与实现步骤。

(4)掌握符号表和存储空间的组织。

(5)掌握代码优化的作用与实现方法(6)掌握错误的诊断和校正方法。

三、主要实验教学方法实验前,由任课教师落实实验任务,每个学生必须事先独立完成好程序的设计的源程序编写工作。

实验课上对疑难点作集中辅导。

实验过程中随时针对不同的情况作个别启发式辅导。

实验后,学生撰写并提交实验报告。

最后,由实验教师根据每个学生的编程、上机调试能力、编程能力和实验结果及实验报告综合评定学生的实验成绩。

《编译原理》实验教学大纲

《编译原理》实验教学大纲

《编译原理》实验教学大纲一、实验目的和任务编译原理是计算机科学与技术专业的一门重要课程,它主要研究的是将高级语言程序翻译成机器语言程序的方法和技术。

通过本实验课程的学习,旨在使学生掌握编译原理的基本原理和方法,培养学生对编译器结构与构造技术的专门知识和技能,为学生今后进行编译器设计与实现打下基础。

二、实验设备和工具1.计算机和相关硬件设备2. 编程语言的开发环境,如C/C++或Java三、实验内容1.实验一:词法分析器设计与实现a)实验目的:学习词法分析器的原理和设计方法,掌握正则表达式、DFA和NFA的转换方法。

b)实验任务:i.设计并实现一个词法分析器的原型,能够正确地识别出给定的程序中的词法单元。

ii. 使用给定的正则表达式设计并实现识别给定程序中的关键字、标识符、常量等的词法分析器。

2.实验二:语法分析器设计与实现a)实验目的:学习语法分析器的原理和设计方法,掌握上下文无关文法和LR分析表的构造方法。

b)实验任务:i.学习并理解上下文无关文法和LR分析表的构造方法。

ii. 设计并实现一个简单的递归下降语法分析器。

3.实验三:语义分析器设计与实现a)实验目的:学习语义分析器的原理和设计方法,掌握语义动作的定义和处理方法。

b)实验任务:i.学习并理解语义分析器的原理和设计方法。

ii. 设计并实现一个简单的语义分析器,能够对给定的程序进行语义分析和语义动作的处理。

4.实验四:中间代码生成器设计与实现a)实验目的:学习中间代码生成器的原理和设计方法,掌握中间代码的生成和优化方法。

b)实验任务:i.学习并理解中间代码生成器的原理和设计方法。

ii. 设计并实现一个简单的中间代码生成器,能够将给定的程序翻译成中间代码。

5.实验五:目标代码生成器设计与实现a)实验目的:学习目标代码生成器的原理和设计方法,掌握目标代码的生成和优化方法。

b)实验任务:i.学习并理解目标代码生成器的原理和设计方法。

ii. 设计并实现一个简单的目标代码生成器,能够将中间代码翻译成目标代码。

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

武汉科技大学计算机科学与技术学院编译原理实验指导书实验一词法分析器设计【实验目的】1.熟悉词法分析的基本原理,词法分析的过程以及词法分析中要注意的问题。

2.复习高级语言,进一步加强用高级语言来解决实际问题的能力。

3.通过完成词法分析程序,了解词法分析的过程。

【实验内容】用C语言编写一个PL/0词法分析器,为语法语义分析提供单词,使之能把输入的字符串形式的源程序分割成一个个单词符号传递给语法语义分析,并把分析结果(基本字,运算符,标识符,常数以及界符)输出。

【实验要求】1.要求绘出词法分析过程的流程图。

2.根据词法分析的目的以及内容,确定完成分析过程所需模块。

3.写出每个模块的源代码,并给出注释。

4.整理程序清单及所得结果。

【说明】运行成功以后,检查程序,并将运行结果截图打印粘贴到实验报告上。

辅助库函数scanerLib设计以及使用说明:下面内容给出了一个辅助库函数的接口说明以及具体实现。

接口设计//字符类class Token{TokenType type;String str;Int line;}//词法分析结果输出操作类class TokenWriter{ArrayList tokens; //用来记录所识别出来的tokenTokenWriter(); //构造函数指定输入文件名,创建文件输出流V oid Add(Token); //将词法分析器中分析得到的Token添加到tokens中WriteXML(); //将tokens写出到目标文件.xml中}//词法分析操作词法分析生成文件接口<暂时不需要对该类的操作;下一步做语法分析的时候使用>class TokenReader{ArrayList tokens;TokenReader(); //构造函数输入文件名;初始化Token Next(); //返回下一个Token;Token Back(); //回退一个Token,并返回回退后的当前Token ReadXML(); //从文件中读出所有Token到tokens}使用:TokenWriter tw = new TokenWriter();While() {Token tok = getToken();Tw.Add(tok);}tw.WriteXML();TokenReader tr = new TokenReader();Tok,ReadXML();Token tok = new Token();While() {Tok = tr.Next();Process(tok);}类图:TokenWriter-tokens : ArrayList-scanfile : string+TokenWriter(string)+Add(Token) : void+WriteXML() : voidTokenReader-tokens : ArrayList-scanfile : string-pos : int+TokenWriter(string)+Next() : Token+Back() : Token+WriteXML() : void操作类库源码Token.cs //字符类ReservedWord.cs //保留字类TokenWriter.cs //结果输出TokenReader.cs //操作词法分析生成文件,进行下一步的工作使用时注意事项:1)定义文法中涉及到的所有字符类型,修改Token.cs文件中public enum TokenType{//自己定义的所有字符类型};2)对于保留字,在词法分析的过程中进行定义,例如:Hashtable reservedWords = new Hashtable();reservedWords.Add("if",TokenType.IF);reservedWords.Add("else",TokenType.ELSE);reservedWords.Add("while",TokenType.WHILE);reservedWords.Add("return",TokenType.RETURN);reservedWords.Add("void",TokenType.VOID);reservedWords.Add("int",TokenType.INT);可以继续添加其它的保留字;也可以将保留字表定义为其它的类型结构进行存储。

3)进行词法分析,词法分析的过程中,可以使用Token、ReservedWord的类型定义以及TokenWriter类中给出的接口函数进行操作。

4)同时还给出了一个较复杂的语言文法的词法分析程序scanner.cs。

示例程序举例:设置输入:prime.cm输出:prime.xml实验二 LL(1)语法分析程序设计【实验目的】1.熟悉判断LL(1)文法的方法及对某一输入串的分析过程。

2.学会构造表达式文法的预测分析表。

【实验内容】编写一个语法分析程序,对于给定的输入串,能够判断识别该串是否为给定文法的句型。

【实验要求】1.输入一个LL(1)文法,构造相应的LL(1)预测分析表。

2.从键盘读入输入串,由算法判断该输入串是否为该文法的句子,若正确,就通过,若错误就报错。

(可参考教材96页的例题1)【说明】语法分析主要是将从词法分析那里得来的记号构成一棵语法树。

例:SHMA#adbe#S->aHH->aMdH->dM->AbM->A->aMA->e分析例句:aaabd#/*-------------------LL(1)语法分析--------------------*/#include "stdio.h"#include "stdlib.h"#define MaxRuleNum 8#define MaxVnNum 5#define MaxVtNum 5#define MaxStackDepth 20#define MaxPLength 20#define MaxStLength 50struct pRNode/*产生式右部结构*/{int rCursor;/*右部序号*/struct pRNode *next;};struct pNode/*产生式结点结构*/{int lCursor;/*左部符号序号*/int rLength;/*右部长度*//*注当rLength = 1 时,rCursor = -1为空产生式*/struct pRNode *rHead;/*右部结点头指针*/};char Vn[MaxVnNum + 1];/*非终结符集*/int vnNum;char Vt[MaxVtNum + 1];/*终结符集*/int vtNum;struct pNode P[MaxRuleNum];/*产生式*/int PNum;/*产生式实际个数*/char buffer[MaxPLength + 1];char ch;/*符号或string ch;*/char st[MaxStLength]; /*要分析的符号串*/struct collectNode/*集合元素结点结构*/{int nVt;/*在终结符集中的下标*/struct collectNode *next;};struct collectNode* first[MaxVnNum + 1];/*first集*/struct collectNode* follow[MaxVnNum + 1];/*follow集*/int analyseTable[MaxVnNum + 1][MaxVtNum + 1 + 1];/*预测分析表存放为产生式的编号,+1用于存放结束符,多+1用于存放#(-1)*/ int analyseStack[MaxStackDepth + 1];/*分析栈*/int topAnalyse;/*分析栈顶*//*int reverseStack[MaxStackDepth + 1];/*颠倒顺序栈*//*int topReverse;/*倒叙栈顶*/void Init();/*初始化*/int IndexCh(char ch);/*返回Vn在Vn表中的位置+100、Vt在Vt表中的位置,-1表示未找到*/void InputVt();/*输入终结符*/void InputVn();/*输入非终结符*/void ShowChArray(char* collect, int num);/*输出Vn或Vt的内容*/void InputP();/*产生式输入*/bool CheckP(char * st);/*判断产生式正确性*/void First(int U);/*计算first集,U->xx...*/void AddFirst(int U, int nCh);/*加入first集*/bool HaveEmpty(int nVn); /*判断first集中是否有空(-1)*/void Follow(int V);/*计算follow集*/void AddFollow(int V, int nCh, int kind);/*加入follow集,kind = 0表加入follow集,kind = 1加入first集*/void ShowCollect(struct collectNode **collect);/*输出first或follow集*/void FirstFollow();/*计算first和follow*/void CreateAT();/*构造预测分析表*/void ShowAT();/*输出分析表*/void Identify(char *st);/*主控程序,为操作方便*//*分析过程显示操作为本行变换所用,与教程的显示方式不同*/ void InitStack();/*初始化栈及符号串*/void ShowStack();/*显示符号栈中内容*/void Pop();/*栈顶出栈*/void Push(int r);/*使用产生式入栈操作*/#include "LL1.h"void main(void){char todo,ch;Init();InputVn();InputVt();InputP();getchar();FirstFollow();printf("所得first集为:");ShowCollect(first);printf("所得follow集为:");ShowCollect(follow);CreateAT();ShowAT();todo = 'y';while('y' == todo){printf("\n是否继续进行句型分析?(y / n):");todo = getchar();while('y' != todo && 'n' != todo){printf("\n(y / n)? ");todo = getchar();}if('y' == todo){int i;InitStack();printf("请输入符号串(以#结束) : ");ch = getchar();i = 0;while('#' != ch && i < MaxStLength) {if(' ' != ch && '\n' != ch){st[i++] = ch;}ch = getchar();}if('#' == ch && i < MaxStLength){st[i] = ch;Identify(st);}elseprintf("输入出错!\n");}}getchar();}void Init(){int i,j;vnNum = 0;vtNum = 0;PNum = 0;for(i = 0; i <= MaxVnNum; i++)Vn[i] = '\0';for(i = 0; i <= MaxVtNum; i++)Vt[i] = '\0';for(i = 0; i < MaxRuleNum; i++){P[i].lCursor = NULL;P[i].rHead = NULL;P[i].rLength = 0;}PNum = 0;for(i = 0; i <= MaxPLength; i++) buffer[i] = '\0';for(i = 0; i < MaxVnNum; i++){first[i] = NULL;follow[i] = NULL;}for(i = 0; i <= MaxVnNum; i++){for(j = 0; j <= MaxVnNum + 1; j++)analyseTable[i][j] = -1;}}/*返回Vn在Vn表中的位置+100、Vt在Vt表中的位置,-1表示未找到*/ int IndexCh(char ch){int n;n = 0;/*is Vn?*/while(ch != Vn[n] && '\0' != Vn[n])n++;if('\0' != Vn[n])return 100 + n;n = 0;/*is Vt?*/while(ch != Vt[n] && '\0' != Vt[n])n++;if('\0' != Vt[n])return n;return -1;}/*输出Vn或Vt的内容*/void ShowChArray(char* collect){int k = 0;while('\0' != collect[k]){printf(" %c ", collect[k++]);}printf("\n");}/*输入非终结符*/void InputVn(){int inErr = 1;int n,k;char ch;while(inErr){printf("\n请输入所有的非终结符,注意:");printf("请将开始符放在第一位,并以#号结束:\n");ch = ' ';n = 0;/*初始化数组*/while(n < MaxVnNum){Vn[n++] = '\0';}n = 0;while(('#' != ch) && (n < MaxVnNum)){if(' ' != ch && '\n' != ch && -1 == IndexCh(ch)) {Vn[n++] = ch;vnNum++;}ch = getchar();}Vn[n] = '#';/*以“#”标志结束用于判断长度是否合法*/ k = n;/*k用于记录n以便改Vn[n]='\0'*/if('#' != ch){if( '#' != (ch = getchar())){while('#' != (ch = getchar()));printf("\n符号数目超过限制!\n");inErr = 1;continue;}}/*正确性确认,正确则,执行下下面,否则重新输入*/Vn[k] = '\0';ShowChArray(Vn);ch = ' ';while('y' != ch && 'n' != ch){if('\n' != ch){printf("输入正确确认?(y/n):");}scanf("%c", &ch);}if('n' == ch){printf("录入错误重新输入!\n");inErr = 1;}else{inErr = 0;}}}/*输入终结符*/void InputVt(){int inErr = 1;int n,k;char ch;while(inErr){printf("\n请输入所有的终结符,注意:");printf("以#号结束:\n");ch = ' ';n = 0;/*初始化数组*/while(n < MaxVtNum){Vt[n++] = '\0';}n = 0;while(('#' != ch) && (n < MaxVtNum)){if(' '!= ch && '\n' != ch && -1 == IndexCh(ch)) {Vt[n++] = ch;vtNum++;}ch = getchar();}Vt[n] = '#';/*以“#”标志结束*/k = n;/*k用于记录n以便改Vt[n]='\0'*/if('#' != ch){if( '#' != (ch = getchar())){while('#' != (ch = getchar()))printf("\n符号数目超过限制!\n");inErr = 1;continue;}}/*正确性确认,正确则,执行下下面,否则重新输入*/Vt[k] = '\0';ShowChArray(Vt);ch =' ';while('y' != ch && 'n' != ch){if('\n' != ch){printf("输入正确确认?(y/n):");}scanf("%c", &ch);}if('n' == ch){printf("录入错误重新输入!\n");inErr = 1;}else{inErr = 0;}}}/*产生式输入*/void InputP(){char ch;int i = 0, n,num;printf("请输入文法产生式的个数:");scanf("%d", &num);PNum = num;getchar();/*消除回车符*/printf("\n请输入文法的%d个产生式,并以回车分隔每个产生式:", num); printf("\n");while(i < num){printf("第%d个:", i);/*初始化*/for(n =0; n < MaxPLength; n++)buffer[n] = '\0';/*输入产生式串*/ch = ' ';n = 0;while('\n' != (ch = getchar()) && n < MaxPLength) {if(' ' != ch)buffer[n++] = ch;}buffer[n] = '\0';/*printf("%s", buffer);*/if(CheckP(buffer)){/*填写入产生式结构体*/pRNode *pt, *qt;P[i].lCursor = IndexCh(buffer[0]);pt = (pRNode*)malloc(sizeof(pRNode));pt->rCursor = IndexCh(buffer[3]);pt->next = NULL;P[i].rHead = pt;n = 4;while('\0' != buffer[n]){qt = (pRNode*)malloc(sizeof(pRNode));qt->rCursor = IndexCh(buffer[n]);qt->next = NULL;pt->next = qt;pt = qt;n++;}P[i].rLength = n - 3;i++;/*调试时使用*/}elseprintf("输入符号含非法在成分,请重新输入!\n"); }}/*判断产生式正确性*/bool CheckP(char * st){int n;if(100 > IndexCh(st[0]))return false;if('-' != st[1])return false;if('>' != st[2])return false;for(n = 3; '\0' != st[n]; n ++){if(-1 == IndexCh(st[n]))return false;}return true;}/*====================first & follow======================*/ /*计算first集,U->xx...*/void First(int U){int i,j;for(i = 0; i < PNum; i++){if(P[i].lCursor == U){struct pRNode* pt;pt = P[i].rHead;j = 0;while(j < P[i].rLength){if(100 > pt->rCursor){/*注:此处因编程出错,使空产生式时rlength同样是1,故此处同样可处理空产生式*/AddFirst(U, pt->rCursor);break;}else{if(NULL == first[pt->rCursor - 100]){First(pt->rCursor);}AddFirst(U, pt->rCursor);if(!HaveEmpty(pt->rCursor)){break;}else{pt = pt->next;}}j++;}if(j >= P[i].rLength)/*当产生式右部都能推出空时*/AddFirst(U, -1);}}}/*加入first集*/void AddFirst(int U, int nCh)/*当数值小于100时nCh为Vt*//*当处理非终结符时,AddFirst不添加空项(-1)*/{struct collectNode *pt, *qt;int ch;/*用于处理Vn*/pt = NULL;qt = NULL;if(nCh < 100){pt = first[U - 100];while(NULL != pt){if(pt->nVt == nCh)break;else{qt = pt;pt = pt->next;}}if(NULL == pt){pt = (struct collectNode *)malloc(sizeof(struct collectNode));pt->nVt = nCh;pt->next = NULL;if(NULL == first[U - 100]){first[U - 100] = pt;}else{qt->next = pt;/*qt指向first集的最后一个元素*/}pt = pt->next;}}else{pt = first[nCh - 100];while(NULL != pt){ch = pt->nVt;if(-1 != ch){AddFirst(U, ch);}pt = pt->next;}}}/*判断first集中是否有空(-1)*/bool HaveEmpty(int nVn){if(nVn < 100)/*为终结符时(含-1),在follow集中用到*/return false;struct collectNode *pt;pt = first[nVn - 100];while(NULL != pt){if(-1 == pt->nVt)return true;pt = pt->next;}return false;}/*计算follow集,例:U->xVy,U->xV.(注:初始符必含#——"-1")*/void Follow(int V){int i;struct pRNode *pt;if(100 == V)/*当为初始符时*/AddFollow(V, -1, 0 );for(i = 0; i < PNum; i++){pt = P[i].rHead;while(NULL != pt && pt->rCursor != V) /*注此不能处理:U->xVyVz的情况*/ pt = pt->next;if(NULL != pt){pt = pt->next;/*V右侧的符号*/if(NULL == pt)/*当V后为空时V->xV,将左符的follow集并入V的follow集中*/ {if(NULL == follow[P[i].lCursor - 100] && P[i].lCursor != V){Follow(P[i].lCursor);}AddFollow(V, P[i].lCursor, 0);}else/*不为空时V->xVy,(注意:y->),调用AddFollow加入Vt或y的first集*/ {while(NULL != pt && HaveEmpty(pt->rCursor)){AddFollow(V, pt->rCursor, 1);/*y的前缀中有空时,加如first集*/pt = pt->next;}if(NULL == pt)/*当后面的字符可以推出空时*/{if(NULL == follow[P[i].lCursor - 100] && P[i].lCursor != V){Follow(P[i].lCursor);}AddFollow(V, P[i].lCursor, 0);}else/*发现不为空的字符时*/{AddFollow(V, pt->rCursor, 1);}}}}}/*当数值小于100时nCh为Vt*//*#用-1表示,kind用于区分是并入符号的first集,还是follow集kind = 0表加入follow集,kind = 1加入first集*/void AddFollow(int V, int nCh, int kind){struct collectNode *pt, *qt;int ch;/*用于处理Vn*/pt = NULL;qt = NULL;if(nCh < 100)/*为终结符时*/{pt = follow[V - 100];while(NULL != pt)if(pt->nVt == nCh)break;else{qt = pt;pt = pt->next;}}if(NULL == pt){pt = (struct collectNode *)malloc(sizeof(struct collectNode)); pt->nVt = nCh;pt->next = NULL;if(NULL == follow[V - 100]){follow[V - 100] = pt;}else{qt->next = pt;/*qt指向follow集的最后一个元素*/}pt = pt->next;}}else/*为非终结符时,要区分是加first还是follow*/{if(0 == kind){pt = follow[nCh - 100];while(NULL != pt){ch = pt->nVt;AddFollow(V, ch, 0);pt = pt->next;}}else{pt = first[nCh - 100];while(NULL != pt){ch = pt->nVt;if(-1 != ch)AddFollow(V, ch, 1);}pt = pt->next;}}}}/*输出first或follow集*/void ShowCollect(struct collectNode **collect) {int i;struct collectNode *pt;i = 0;while(NULL != collect[i]){pt = collect[i];printf("\n%c:\t", Vn[i]);while(NULL != pt){if(-1 != pt->nVt){printf(" %c", Vt[pt->nVt]);}elseprintf(" #");pt = pt->next;}i++;}printf("\n");}/*计算first和follow*/void FirstFollow(){int i;i = 0;while('\0' != Vn[i]){if(NULL == first[i])First(100 + i);i++;}i = 0;while('\0' != Vn[i]){if(NULL == follow[i])Follow(100 + i);i++;}}/*=================构造预测分析表,例:U::xyz=============*/ void CreateAT(){int i;struct pRNode *pt;struct collectNode *ct;for(i = 0; i < PNum; i++){pt = P[i].rHead;while(NULL != pt && HaveEmpty(pt->rCursor)){/*处理非终结符,当为终结符时,定含空为假跳出*/ct = first[pt->rCursor - 100];while(NULL != ct){if(-1 != ct->nVt)analyseTable[P[i].lCursor - 100][ct->nVt] = i;ct = ct->next;}pt = pt->next;}if(NULL == pt){/*NULL == pt,说明xyz->,用到follow中的符号*/ct = follow[P[i].lCursor - 100];while(NULL != ct){if(-1 != ct->nVt)analyseTable[P[i].lCursor - 100][ct->nVt] = i;else/*当含有#号时*/analyseTable[P[i].lCursor - 100][vtNum] = i;ct = ct->next;}}else{if(100 <= pt->rCursor)/*不含空的非终结符*/{ct = first[pt->rCursor - 100];while(NULL != ct){analyseTable[P[i].lCursor - 100][ct->nVt] = i;ct = ct->next;}}else/*终结符或者空*/{if(-1 == pt->rCursor)/*-1为空产生式时*/{ct = follow[P[i].lCursor - 100];while(NULL != ct){if(-1 != ct->nVt)analyseTable[P[i].lCursor - 100][ct->nVt] = i;else/*当含有#号时*/analyseTable[P[i].lCursor - 100][vtNum] = i;ct = ct->next;}}else/*为终结符*/{analyseTable[P[i].lCursor - 100][pt->rCursor] = i;}}}}}/*输出分析表*/void ShowAT(){int i,j;printf("构造预测分析表如下:\n");printf("\t|\t");for(i = 0; i < vtNum; i++){printf("%c\t", Vt[i]);}printf("#\t\n");printf("- - -\t|- - -\t");for(i = 0; i <= vtNum; i++)printf("- - -\t");printf("\n");for(i = 0; i < vnNum; i++){printf("%c\t|\t", Vn[i]);for(j = 0; j <= vtNum; j++){if(-1 != analyseTable[i][j])printf("R(%d)\t", analyseTable[i][j]);elseprintf("error\t");}printf("\n");}}/*=================主控程序=====================*/void Identify(char *st){int current,step,r;/*r表使用的产生式的序号*/printf("\n%s的分析过程:\n", st);printf("步骤\t分析符号栈\t当前指示字符\t使用产生式序号\n");step = 0;current = 0;/*符号串指示器*/printf("%d\t",step);ShowStack();printf("\t\t%c\t\t- -\n", st[current]);while('#' != st[current]){if(100 > analyseStack[topAnalyse])/*当为终结符时*/{if(analyseStack[topAnalyse] == IndexCh(st[current])){/*匹配出栈,指示器后移*/Pop();current++;step++;printf("%d\t", step);ShowStack();printf("\t\t%c\t\t出栈、后移\n", st[current]);}else{printf("%c-%c不匹配!", analyseStack[topAnalyse], st[current]);printf("此串不是此文法的句子!\n");return;}}else/*当为非终结符时*/{r = analyseTable[analyseStack[topAnalyse] - 100][IndexCh(st[current])]; if(-1 != r){Push(r);/*产生式右部代替左部,指示器不移动*/step++;printf("%d\t", step);ShowStack();printf("\t\t%c\t\t%d\n", st[current], r);}else{printf("无可用产生式,此串不是此文法的句子!\n");return;}}}if('#' == st[current]){if(0 == topAnalyse && '#' == st[current]){step++;printf("%d\t", step);ShowStack();printf("\t\t%c\t\t分析成功!\n", st[current]);printf("%s是给定文法的句子!\n", st);}else{while(topAnalyse > 0){if(100 > analyseStack[topAnalyse])/*当为终结符时*/{printf("无可用产生式,此串不是此文法的句子!\n");return;}else{r = analyseTable[analyseStack[topAnalyse] - 100][vtNum];if(-1 != r){Push(r);/*产生式右部代替左部,指示器不移动*/step++;printf("%d\t", step);ShowStack();if(0 == topAnalyse && '#' == st[current]){printf("\t\t%c\t\t分析成功!\n", st[current]);printf("%s是给定文法的句子!\n", st);}elseprintf("\t\t%c\t\t%d\n", st[current], r);}else{printf("无可用产生式,此串不是此文法的句子!\n");return;}}}}}}/*初始化栈及符号串*/void InitStack(){int i;/*分析栈的初始化*/for(i = 0; i < MaxStLength; i++)st[i] = '\0';analyseStack[0] = -1;/*#(-1)入栈*/analyseStack[1] = 100;/*初始符入栈*/topAnalyse = 1;}/*显示符号栈中内容*/void ShowStack(){int i;for(i = 0; i <= topAnalyse; i++){if(100 <= analyseStack[i])printf("%c", Vn[analyseStack[i] - 100]);else{if(-1 != analyseStack[i])printf("%c", Vt[analyseStack[i]]);elseprintf("#");}}}/*栈顶出栈*/void Pop(){topAnalyse--;}/*使用产生式入栈操作*/void Push(int r){int i;struct pRNode *pt;Pop();pt = P[r].rHead;if(-1 == pt->rCursor)/*为空产生式时*/return;topAnalyse += P[r].rLength;for(i = 0; i < P[r].rLength; i++){/*不为空产生式时*/analyseStack[topAnalyse - i] = pt->rCursor;/*逆序入栈*/ pt = pt->next;}/*循环未完时pt为空,则说明rLength记录等出错*/}。

相关文档
最新文档