收藏 分享(赏)

编译原理课程设计指导书.doc

上传人:dzzj200808 文档编号:2703015 上传时间:2018-09-25 格式:DOC 页数:75 大小:912.50KB
下载 相关 举报
编译原理课程设计指导书.doc_第1页
第1页 / 共75页
编译原理课程设计指导书.doc_第2页
第2页 / 共75页
编译原理课程设计指导书.doc_第3页
第3页 / 共75页
编译原理课程设计指导书.doc_第4页
第4页 / 共75页
编译原理课程设计指导书.doc_第5页
第5页 / 共75页
点击查看更多>>
资源描述

1、计 算 机 工 程 学 院编译原理课程设计指导书付永钢 集美大学计算机工程学院计算机科学与技术教研室二 O 一四年六月前 言编译原理是计算机科学与技术专业最重要的一门专业基础课程,内容庞大,涉及面广,知识点多。由于该课程教、学 难度都非常大,往往费了大量时间而达不到预期教学效果俗语说:学习的最好方法是实践。本课程设计正是基于此,力求为学生提供一个理论联系实际的机会,通过 布置一定难度的课题,要求学生独立完成。通 过实践,建立系统设计的整体思想,锻炼编 写程序、 调试程序的能力,学习文档编写规范,培养独立学习、吸取他人经验、探索前言知识的习惯,树立团队协 作精神。同时,课程设计可以充分弥补课堂教

2、学及普通实验中知识深度与广度有限的缺陷,更好地帮助学生从全局角度把握课程体系。本指导书在全面把握教学大纲及教材精神的基础上,共设计了 4 个题目,不是用一个独立的例子涵盖这些知识点,而是按层次逐步深入。为了使学生理解它们之间如何相互配合,设计要求使用接近 实际需要的方式编程。重点放在编译程序的基本特征上, 结合实际应用,通过详细的实 例,循序 渐进地启发学生完成设计。 课程设计比教学实验复杂,涉及的深度较广,并更加实用。目的是通 过课程 设计的综合训练,培养学生实际分析问题、 编程和动手能力。最终目标是通过课程设计 的形式,帮助学生系 统地掌握该门课程的主要内容,更好地完成教学任务。 书中给出

3、的 实例概念清楚,体系完整,内容丰富,采用循序渐进的方式,提高学生实际动手能力,完成“知识+实践=技能”的整个学习过程。目 录前 言 2选题一 LL(1)语法分析 4选题二 LR(1)语法分析 6选题三 SLR(1)语法分析 7选题四 PL/0 语言及其编译器的改进 8附录 A VISUAL C+6.0 简介 13附录 B C/C+常用函数 .28附录 C 课程设计操作规程 63附录 D 文档要求与规范 66选题一 LL( 1)语法分析 器的设计与开发一、设计内容及要求(1) 基于 PL/0 语言,通过编程判断该文法是否为 LL(1)文法; (2) 计算出文法的 First() 、Follow

4、()(3) 构造相应文法的预测分析表(4) 对某个输入句子进行语法分析二、实现原理1LL(1)文法LL(1)文法是一类可以进行确定的自顶向下语法分析的文法。就是要求描述语言的文法是无左递归的和无回溯的。根据 LL(1)文法的定义,对于同一非终结符 A 的任意两个产生式 A:=a 和A:=b,都要满足: SELECT(A:=a )SELECT(A:=b)= 。(1)文法的左递归当一个文法是左递归文法时,采用自顶向下分析法会使分析过程进入无穷循环之中。所以采用自顶向下语法分析需要消除文法的左递归性。文法的左递归是指若文法中对任一非终结符 A 有推导 AA,则称该文法是左递归的。左递归又可以分为直接

5、左递归和间接左递归。 直接左递归若文法中的某一产生式形如 AA ,V*,则称该文法是直接左递归的。消除直接左递归的方法:设有产生式是关于非终结符 A 的直接左递归:A A| (,V* ,且 不以 A 开头)对 A 引入一个新的非终结符 A,把上式改写为:A A AA| 间接左递归若文法中存在某一非终结符 A,使得 AA至少需要两步推导,则称该文法是间接左递归的。消除间接左递归的方法:【方法一】采用代入法把间接左递归变成直接左递归。【方法二】直接改写文法:设有文法 G10S:SA| AS 因为 SAS,所以 S 是一个间接递归的非终结符。为了消除这种间接左递归,将式代入式,即可得到与原文法等价的

6、文法(可以证明):SS| 式是直接左递归的,可以采用前面介绍的消除直接左递归的方法,对文法进行改写后可得文法:SSSS|2. 计算 First 集(1) 若 XV T ,则 First(X)=X(2) 若 XV N ,且有产生式 Xa, aV T 则 First(X)=X(3) 若 XV N ,且有产生式 X,则 First(X)=X(4) 若 X,Y 1 ,Y 2 ,Y n 都V N,而由产生式 XY 1 Y2 Yn 。当 Y1 ,Y 2 ,Y i-1都能推导出 时, (其中 1i n) ,则 First(Y1)-, First(Y2)-, , First(Yi)都包含在 First(X)中

7、(5)当(4)中所有 Yi 都能推导出 , (i=1 ,2,n) ,则 First(X)=First(Y1)First(Y 2)First(Y n)反复使用上述步骤直到每个符合的 First 集合不再增大为止。3. 计算 Follow 集对文法中的每个 AV N,计算 Follw(A):(1) 设 S 为文法的开始符合,把 #加入 Follow(S)中;(2) 若 AB 是一个产生式,则把 First() 的非空元素加入 Follow(B)中,如果 能推导出 ,则把 Follow(A)也加入 (B)中;(3) 反复使用以上步骤直到每个非终结符号的 Follow 集不再增大为止。4. 预测分析方

8、法预测分析方法是自顶向下分析的另一种方法,一个预测分析器是由三个部分组成:预测分析程序;先进后出栈;预测分析表。预测分析程序的框图如下:选题二 LR(1)语法分析器设计与实现一、实验内容与要求(1)给定 PL/0 语言的描述文法,通过程序分析的方式判断该文法是否为 LR(1).(2)计算该文法的 LR(1)分析表;(3)可以对输入的句子进行语法分析。二、实验原理1. LR 分析表的构造(1)若A a,bIk ,且 GO(Ik,a)= Ij(aVT),则置 ACTIONk,a=sj;(2)若A ,aIk ,则对终结符 a(包括#)置 ACTIONk,a= rj(j 为产生式 A 的编号) ;(3

9、)若项目SS,#Ik,则置 ACTIONk,#=acc;(4)若 GO(Ik,A)=Ij(AVN),则置 GOTOk,A=j; (5)不能用上述方法填入内容的单元均置为“出错标志”(用空白表示) 。 2. LR(1)分析算法描述将 S0 移进状态栈,# 移进符号栈,S 为状态栈栈顶状态a=getsym( ) /读入第一个符号给 awhile(ACTIONS,a!=acc )if ACTIONS,a=SiPUSH i,a(分别进栈); a=getsym( ) ;/读入下一个符号给 aelse if ACTIONS,a=rj (第 j 条产生式为 A) 将状态栈和符号栈分别弹出| 项;push(A

10、);将 GOTOS,A 移进状态栈(S为当前栈顶状态);else error( ); 选题三 SLR(1)语法分析一、实验内容与要求已知控制语句、算术表达式、布尔表达式的文法如下:1. 控制语句1)S if e S else S2)S while e S3)S L 4)S a;5)L S6)L SL2. 算术表达式1)E E+E2)E E*E3)E (E)4)E i3.布尔表达式的 SLR 分析表 3 设计如下:(过程略)1) B i2) B i rop i3) B ( B )4) B ! B5) A B 另一种仍是程序设计上的错误, 但是躲过了编译程序和连接程序的检查, 通常表现为突然死机、

11、自行热启动或者输出信息混乱。相对于编译和连接错误来说, 运行错误的查找和判断更为困难。编译和连接错误可以由编译程序和连接程序检查, 而运行错误就不同了 , 很少或根本没有提示信息 , 只能靠程序员的经验来判断错误的性质和位置。下面简单地介绍一些常见运行错误的调试方法。逻辑错误: 一种逻辑错误是由于在设计程序的算法时考虑欠周引起的, 例如对边界和极端条件未作处理等。例如以下循环:while(count) count = count1;程序员的构思是进行 count 次循环。但是, 如果 count 中原来的值为负数时, 此循环就成了一个“死循环”而导致无法停机, 显然是错误的。但是编译程序无法查

12、出这类错误, 只有到了程序运行之后才有可能发现。再如, 在利用海伦公式计算三角型面积时, 首先应该确认给出的三条边长确实可以构成一个三角形, 否则计算结果是没有意义的; 而在编写求解一般实系数一元二次方程的程序时, 必须在程序中设计处理复根情况的程序段, 以免对负数求平方根。另一种常见的逻辑错误是由于程序输入时的打字错误造成的, 例如将判断条件中的“=”误输入为“”, 将相等判断“=”误输入为赋值号“=”等。含有这类错误的程序在运行时出现的现象多种多样, 而且通常很难与错误的原因联系起来。数组下标越界错误:即使用了并不存在的数组元素。例如有程序段int a5;for(int i=1; i 10

13、0“ a b c;. .这样的程序时,为了突出测试该段程序对某组输入数据(如 a = 1, b = 2, c= 3)的响应情况, 同时避免每次输入数据的麻烦, 可以利用注解号将上述程序段落改为:/ cin a b c;a = 1;b = 2;c = 3;. .这样在调试程序时就不必每次停下来等待输入数据了, 可以直接使用步进、跟踪或设置断点等手段调试这段程序。等到程序中的所有错误全部修正以后, 再恢复被修改的内容(即注解中的内容)即可。A.10 条件编译条件编译是编译预处理命令的一种,用于对源程序的内容进行选择性编译。例如, 在调试程序期间, 常常希望记录输出一些调试用的信息 , 而在调试完成

14、后 , 就不再需要这些输出信息了。要解决这个问题,一种办法是逐一从源程序中删去这些输出这些调试信息的程序段落, 或者将这些程序段落用注释标记括起来。显然这样很不方便。另一种方法就是使用编译预处理中的条件编译命令。条件编译命令的格式为:#if #elif . .#elif #else#endif条件编译中所使用的条件只能是由常数构成的表达式。如果该常数表达式的值不为 0, 就表示条件成立, 否则表示条件不成立。一般情况下, 在#if 中都是使用由#define 指令产生的符号常数进行测试。编译预处理命令的一个常见的用途是将调试代码插入应用程序中。程序员可以定义一个叫做DEBUG 的符号常数, 值

15、可以定义为 1 或者 0。在程序中的任何地方 ,都可用以下方式插入调试信息:#if DEBUG = 1#endif在开发程序时, 将 DEBUG 定义为 1, 则插入的调试代码被编译进目标程序 , 可输出帮助跟踪错误的信息。一旦程序工作正常, 就可将 DEBUG 重新定义为 0 后再次编译程序, 即可从目标程序中去掉调试代码。+中还有两个和#if 类似的条件编译命令: #ifdef 和#ifndef 。例如#ifdef DEBUG则只要定义了 DEBUG(无论将 DEBUG 定义为什么值,包括 0), 甚至什么具体值也不是:#define DEBUG则上述条件成立。而在前面没有定义 DEBUG

16、 时#ifndef DEBUG成立。A.11 Developer Studio 的跟踪调试功能调试器是 Developer Studio 中最出色的部件之一,可以帮助找到在软件开发中可能遇到的几乎每个错误。调试器的主要调试手段有设置断点、跟踪和观察。所谓断点,即程序中的某处。在调试时使程序执行到断点处停下来,通过观察程序变量、表达式、调试输出信息、内存、寄存器和堆栈的值来了解程序的运行情况,或进一步跟踪程序的运行。在当前编辑位置设置一个断点最直接的方式是使用快捷键 F9,或用鼠标点击 Build MiniBar 工具条上的手形图标。断点用编辑窗口左边框上的大红圆点表示,非常醒目。取消一个断点的

17、方法类似,只要在有断点的语句上重新使用快捷键 F9 即可取消已设置的断点。如果已设置好了断点,则可通过子菜单 Build/Start Debug 调用调试器。该子菜单有 4 个选项,分别为:A. Go(快捷键为 F5):从当前语句开始执行程序,直到遇到一个断点或程序结束。用Go 命令启动调试器时,从头开始执行程序。B. Step Into(快捷键为 F11):单步执行每一程序行,遇到函数时进入函数体内单步执行。C. Run To Cursor(快捷键为 Ctrl+F10):运行程序至当前编辑位置。D. Attach To Process:将调试器与当前运行的其中某个进程联系起来,这样就可以跟踪

18、进入进程内部,就象调试项目工作区中当前打开的应用程序一样调试运行中的进程。当被调试的程序停在某个断点上时,编辑器左边框上的对应位置会出现一个黄色箭头指示被中断的语句。此时 Developer Studio 的版面布置会一些发生变化,如菜单栏中以调试( Debug)菜单项代替了建立(Build)菜单项并出现了一 Debug 工具栏。调试工具栏如图 A.9 所示。图 A.9 Developer Studio 的 Debug 工具栏Debug 工具栏可分为 4 个区,第 1,2 两个区中是常用的调试命令,包括:(1)Restart (快捷键:Ctrl+Shift+F5):终止当前的调试过程,重新开始

19、执行程序,停在程序的第 1 条语句处。(2)Stop Debugging(快捷键: Shift+F5):退出调试器,同时结束调试过程和程序运行过程。(3)Break Execution:终止程序运行,进入调试状态。多用于终止一个进入死循环的程序。(4)Apply Code Changes(快捷键: Alt+F10):当源程序在调试过程中发生改变,重新进行编译。(5)Show Next Statement (快捷键: Alt+Num *):显示下一语句。(6)Step Into(快捷键:F11):跟踪。如果是一语句,则单步执行;如果是一函数调用,则跟踪到函数第一条可执行语句。(7)Step Ov

20、er(快捷键 F10):单步执行。如果是一语句,则单步执行;如果是一函数调用,将此函数一次执行完毕,运行到下一条可执行语句。(8)Step Out(快捷键:Shift+F11):从函数体内运行到外,即从当前位置运行到调用该函数语句的下一条语句。(9)Run To Cursor(快捷键:Ctrl+F10 ):从当前位置运行到编辑光标。第 3 区有一个眼镜图标(Quick Watch,快捷键为 Shift+F9)用于弹出一个对话框,观察当前编辑位置的变量的值。第 4 区有 6 个图标,分别用于激活 6 个调试器窗口:(1)观察窗口(Watch)用于观察指定变量或表达式的值。可任意添加要观察的变量或

21、表达式,并可用标签的形式(Watch1,Watch2 ,Watch3 等)增加多组观察对象。(2)变量窗口(Variables)用于观察断点处或其附近的变量的当前值。 Variables 有 3 个标签,Auto 标签显示变量和函数返回值,Locals 标签显示当前函数的局部变量,this 标签显示 this 指针对象。(3)寄存器窗口(Register)用于观察在当前运行点的寄存器的内容。(4)内存窗口(Memory)用于观察指定内存地址内容。(5)调用栈窗口(Call Stack)用于观察调用栈中还未返回的被调用函数列表。调用栈给出从嵌套函数调用一直到断点位置的执行路径。(6)汇编代码窗口

22、(Disassmbly )用于显示被编译代码的汇编语言形式。A.12 如何在程序中使用 MFC 类库如果要在程序中使用 CString、CTime 和 CTimeSpan 等 MFC 类,要在程序首部加上文件包含命令:#include 此外,还需在 Developer Studio 的菜单选项 project/Settings的 General 选项卡设置 Microsoft Foundation Classes 项。可选项有三种,分别为 Not Using MFC(不使用 MFC) 、Use MFC in a Shared DLL(以动态链接库方式用 MFC)和 Use MFC in a S

23、tatic Library(以静态库方法使用MFC) ,后两种选项均可使用。A.13 使用 FileView 标签FileView 标签用于显示当前项目中各项目之间的包含关系和项目中包含的所有文件。扩展顶层文件夹就可以显示项目的所有文件,如图 A.10 所示。图 A.10 FileView 标签FileView 的操作功能:1定位:双击某个文件名或图标就可以打开相应的源程序编辑窗口。2添加文件:在项目中增加一个文件有多种方法,可以通过 VC+的菜单选项 Project/Add Project,也可以在文件列表上单击鼠标右键调出快捷菜单来增加一个文件。3删除文件:用鼠标在 FileView 列表

24、中选择要去掉的文件,按删除键(Del)就会把这个文件从项目列表中去掉。A.14 使用 ClassView 标签默认情况下,当用户打开个包含有 C+类定义的工程时,ClassView 窗口将作为工程工作空间的一部分出现,如图 A.11 所示。C1assView 显示代表类和类成员的图标和名称。通过ClassView 标签可以使成员函数、变量的定位和增加类、类的成员的工作变得较为容易。图 A.11 ClassView 标签单击 ClassView 标签,就会在列表框中列出当前开发项目中所包含的类,用鼠标单击“+”会打开各子项目,单击“-”会关闭打开的子项。通过该标签可以浏览类的成员。每个成员的左边

25、都有一个或多个图标,这些图标表示该成员是数据成员还是成员函数,以及成员的访问类型等。如果成员是保护成员,那么它的左边会有一个钥匙图标;如果它是私有成员,那么它的左边会有一个锁头图标。ClassView 的操作功能:1直接定位到代码处:通过双击某个类或成员,可以在源代码窗口查看相应的源代码。2创建新的类:在最高一级的列表项目标题上(如图 A.11 中 vc0805 Classes 处)单击鼠标右键,调出操作菜单,在菜单中选择 New Class就会进入图 A.12 所示的对话框。增加的类可以分为两种,种是 MFC 继承类,它从 VC+中已有的一些 MFC 类继承而来;另一种是般的 C+类。图 A

26、.12 增加一般类对话框在图 A.12 增加类的对话框中从 Class Type 中选择 Generic Class 选项。单击 Change按钮可以修改输入类的头文件和源文件。在 Base Class(es)组合框中输入继承的基类的名称和继承时的类型。在 Name 编辑框中输人类的名称。按“OK”按钮就可以实现增加一般类的工作。3添加类的成员:这些成员包括数据成员、成员函数,以及可重载的虚函数等。(1)添加数据成员:在需要增加数据成员的类上单击鼠标右键,就会弹出一个选择菜单,选择 Add Member Variable,弹出图 A.13 所示的对话框来增加数据成员。图 A.13 增加类的数据

27、成员对话框在 Variable Type 编辑框中输入变量的类型(如 int, float, char, 等) ,在 Variable Declaration 编辑框中输人变量的名称,通过按钮选择变量的访问属性即(公有类型、保护类型、私有类型) ,按OK 按钮就会完成增加数据成员的工作。(2)添加成员函数:在需要增加成员函数的类上单击鼠标右键,在弹出的选择菜单上选择Add Member Function,就会进入图 A.14 所示的对话框来增加成员函数。成员函数的增加方法与成员变量类似,增加完毕后,会在类的定义中增加函数的定义,在源文件中增加一个空函数。图 A.14 增加类的成员函数对话框附录

28、 B C/C+常用函数在使用 C+语言进行编程时,许多很基本和很重要的功能都由系统的库函数和类库来实现。为便于学习和使用,本附录列出了一些常用的库函数,供读者查阅。B.1 缓冲区操作函数函数名:_memccpy函数原型:void *_memccpy(void *dest,const void *src,int c,unsigned int count);参数:dest 目的指针;sro 源指针; c 拷贝的最后字符;count 字符个数。所需头文件:或 功能:从 src 所指向的地址开始拷贝。个或多个字节到 dest 中。当指定字符 c 已拷贝或已拷贝count 个字节后停止。返回值:如果字符

29、 c 被拷贝,返回 dest 中该字符直接后继字节的指针;如果 c 没有被拷贝则返回NULL。函数名:memchr函数原型:void *memchr(const void *buf,int c ,sizet count);参数:buf 缓冲区的指针;c 查找的字符;count 检查的字符个数。所需头文件:或 功能:查找 buf 的前 count 个字节中 c 的第一次出现,当找到 c 或已检查完 count 个字节时停止。返回值:如果成功,返回 buf 中 c 首次出现的位置的指针;否则返回 NULL函数名:memcpy函数原型:void *memcpy(void *dest,const vo

30、id *src,sizet count);参数:dest 目的缓冲区;src 源缓冲区;count 拷贝的字符个数。所需头文件:或 功能:从 sro 拷贝 count 个字节到 dest。如果源缓冲区和目的缓冲区重叠,这个函数不能保证正确拷贝;对于这种情况可使用 memmove 处理。返回值:返回 dest 的值。函数名:_memlcmp函数原型:int _memicmp(const void *buf1,const void *buf2,unsigned int count);参数:bufl 第一个缓冲区;buf2 第二个缓冲区;count 字符个数。所需头文件:或 功能:比较两个缓冲区 b

31、ufl 和 buf2 的前 count 个字符,比较过程是大小写无关的。返回值:bufl 和 buf2 的前 count 个字节之间的关系:0:bufl 大于 bur2函数名:memmove函数原型:void *memmove(void *dest,const void *src,sizet count);参数:dest 目的对象;src 源对象;count 拷贝的字符字节个数。所需头文件;功能:从 src 拷贝 count 个字节到 dest。如果源区域与目的区域有重叠,memmove 也能确保正确拷贝。返回值:返回 dest 的值。函数名:memset函数原型:void *memset(v

32、oid *dest,int c ,sizet count);参数:dest 目的指针;c 设置的字符;count 字符个数。所需头文件:或 功能:设置 dest 的前 count 个字节为字符 c。返回值:返回 dest 的值。函数名:_swab函数原型:void _swab(char *src,char *dest,int n) ;参数:src 需拷贝和交换的数据;dest 交换结果数据的存储位置; n 拷贝和交换的字节个数。所需头文件:功能:从 src 拷贝 n 个字节,交换每对相邻的字节,并把结果存储在 dest 中。一般用于为转换到使用不同字节次序的机器上而准备二进制数据。返回值:无B

33、.2 字符分类函数本类函数都只有一个 int 型参数,即要测试的整数。函数名:isalnum函数原型:int isalnum(int c);所需头文件:功能:测试 c 是否字母或数字。返回值:如果 c 在 AZ、az 或 09 的范围内,则返回一个非 0 值;否则返回 0。函数名:isalpha函数原型:int isalpha(int c) ;所需头文件:功能:测试 c 是否字母。返回值:如果 c 在 AZ 或 az 的范围内,则返回一个非 0 值;否则返回 0。函数名:_isascll函数原型:int _isascll (int c);所需头文件:功能:测试 c 是否 ASCII 字符。返回

34、值:如果 c 为一个 0x000x7F 之间的 ASCII 字符,则返回一个非 0 值:否则返回 0。函数名:iscntrl函数原型:int iscntrl(int c);所需头文件:功能:测试 c 是否控制字符,返回值:如果 c 是一个控制字符(0x000xlF 或 0x7F),则返回一个非 0 值,否则返回 0。函数名:_iscsym函数原型:int _iscsym (int c);所需头文件:功能:测试 c 是否字母、下划线或数字。返回值:如果 c 是一个字母、下划线或数字,则返回一个非 0 值;否则返回 0。函数名:_iscsymf函数原型:int _iscsymf (int c) ;

35、所需头文件:功能:测试是否字母或下划线。返回值:如果 c 是一个字母或下划线,则返回一个非 0 值;否则返回 0。函数名:isdigit函数原型:int isdigit(int c) ;所需头文件:功能:测试是否十进制数字。返回值:如果 c 是一个十进制数字(09) ,则返回一个非 0 值;否则返回 0。函数名:isgraph函数原型:int isgraph(int c) ;所需头文件:功能:测试是否空格外的可打印字符。返回值:如果 c 是一个非空格的其它可打印字符,则返回一个非 0 值;否则返回 0。函数名:islower函数原型:int islower(int c) ;所需头文件:功能:测试是否小写字母。返回值:如果 c 是一个小写字母(az)函数名:isprint函数原型:int isprint(int c);所需头文件:功能:测试是否可打印字符。返回值:如果 c 是一个可打印字符包括空格字符(0x200x7E),则返回一个非 0 值;否则返回 0。函数名:ispunct函数原型:int ispunct(int c);所需头文件:功能:测试是否标点符号。返回值:如果 c 是一个非空格字符并且是 isalnum 不为真的字符,则返回一个非 0 值;否则返回0。函数名:isspace函数原型:int isspace(int c);所需头文件:功能:测试是否空白。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 高等教育 > 大学课件

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报