编译原理课程设计报告11.docx
《编译原理课程设计报告11.docx》由会员分享,可在线阅读,更多相关《编译原理课程设计报告11.docx(44页珍藏版)》请在冰豆网上搜索。
![编译原理课程设计报告11.docx](https://file1.bdocx.com/fileroot1/2023-5/20/e188c683-9478-4ade-9074-5407acd4a413/e188c683-9478-4ade-9074-5407acd4a4131.gif)
编译原理课程设计报告11
课程设计报告
设计题目:
LL
(1)文法分析器
班级:
计算机科学与技术1201
组长学号:
20123826
组长姓名:
张小红
指导教师:
设计时间:
设计分工
摘要
选题要求:
根据某一文法编制调试LL
(1)文法语法分分析程序,以便对任意输入的符号串进行分析。
本次课程设计的目的主要是加深对预测分析LL
(1)文法语法分析法的理解。
具体如下:
1、对语法规则有明确的定义;
2、编写的分析程序能够对给定文法进行正确的语法分析;
3、对输入给定的文法,手工计算FIRST、FOLLOW集合,应能判断识别是否为给定文法的句子,并给出推导过程。
4、对输入给定的文法,由程序自动构造FIRST、FOLLOW集合。
5、对于遇到的语法错误,能够做出简单的错误处理,给出简单的错误提示,保证顺利完成语法分析过程。
关键词:
LL
(1)文法语法分析FIRSTFOLLOW。
1概述
根据某一文法编制调试LL
(1)文法语法分分析程序,以便对任意输入的符号串进行分析。
本次课程设计的目的主要是加深对预测分析LL
(1)文法语法分析法的理解。
预期目标
构造LL
(1)文法语法分析程序,任意输入一个文法符号串,并判断它是否为文法的一个句子。
程序要求为该文法构造预测分析表,并按照预测分析算法对输入串进行语法分析,判别程序是否符合已知的语法规则,如果不符合(编译出错),则输出错误信息。
对于任意输入的一个LL
(1)文法,构造其预测分析表。
首先实现集合FIRST(X)构造算法和集合FOLLOW(A)构造算法,再实现教材P.79给出的预测分析表构造算法。
程序显示输出预测分析表或输出到指定文件中。
2课程设计任务及要求
2.1设计任务
对于任意输入的一个LL
(1)文法,构造其预测分析表。
要求:
首先实现集合FIRST(X)构造算法和集合FOLLOW(A)构造算法,再实现教材P.79给出的预测分析表构造算法。
程序显示输出预测分析表或输出到指定文件中。
2.2设计要求
对文法:
G:
E→E+T|T
T→T*F|F
F→(E)|i
按教材P.76表4.1构造出G的预测分析程序,程序显示输出如P.78那样的匹配过程。
3算法及数据结构
3.1算法的总体思想(流程)
对存入文本文件(.txt文件)里存放的文法读入程序,然后首先要建立两个类,分别是grammer类和SeqStack类(Grammer类中有保存从文本文件读入的LL
(1)文法,SeqStack类主要的作用为在预测分析时作为符号栈)。
然后按照书上介绍的算法计算出相应的first集合和follow集合,根据已有的集合在用正确合理的算法计算出文法的预测分析表。
最后根据文法分析表对输入的字符串进行分析,输出相应的LL
(1)文法分析结果。
3.2文本文法读入程序模块
3.2.1功能
把文本文件中的LL
(1)文法读入到程序中grammer类中的数组成员g中。
3.2.2数据结构
首先要建立两个类,分别是grammer类和SeqStack类Grammer类中有保存从文本文件读入的LL
(1)文法,用一个二维字符数组g保存,保存非终结符的字符数组VN,保存终结符的字符数组VT,文法开始符号字符变量begin,元素对应first集合中的有空字符的非终结符数组emptychar,预测分析表为二维整形数组form.
SeqStack类主要的作用为在预测分析时作为符号栈.对于这个模块的设计,就是用输入流对文本文件中的文法进行读入当读到字符’|’或’/n’时意味着一个产生式已结束,那么相应数组中相应的下标变量+1;数组g[i][0]所存放着这个产生式对应的字符的数量.g[i][1]为这个产生式左边的非终结符。
数据结构框图如下:
3.3文法扫描模块
3.3.1功能
通过对已输入到数组g中的文法,进行扫描,把相应的非终结符和终结符输入到非终结符数组VN中和终结符数组VT中。
3.3.2数据结构
在已把文法读入到数组g中后,那么相应的g[i][1]为每个产生式的非终结符,若此非终结符不在数组VN中,那么把此字符加入到VN中对终结符的读入,则是从每个g[i][2]开始扫描,若此字符不在非终结符数组VN中,也不在VT中,那么就把此字符加入到VT中。
数据结构框图如下:
3.4first集合模块
3.4.1功能
生成所有的非终结符的FIRST集合。
3.4.2数据结构
生成FIRST集合这个算法基本上按照书上的原理,如对每个产生式进行扫描,当扫描到非终结符时,把此字符加入到相应非终结符的FIRST集合中,若遇到非终结符时,需把此非终结符的FIRST集合中除了空字符外全加入到对应的非终结符的first集合中,这就涉及到算法的一个递归算法,我是利用在函数的参数表中设置俩个参数,一个是FIRST数组,另外一个是非终结符,在用到递归时,我是利用把FIRST数组设为本产生式第一个非终结符的FIRST数组,而非终结符参数则设置为在扫描产生式右部过程中所遇到的非终结符.然后通过循环对这个文法中的所有非终结符进行函数的调用,这样来求到所有非终结符的FIRST集合。
数据结构框图如下:
3.4.3算法
构造FIRST(X),办法是,连续使用下面的规则,直至每个集合FIRST不再增大为止。
(1)若X∈VT,,则FIRST(X)={X}.
(2)若X∈Vn,且有产生式X->a……,则把a加入到FIRST(X)中,若X->ε,也是一条产生式,则把ε也加到FIRST(X)中.
(3)若X->Y……是一个产生式且Y∈Vn,则把FIRST(Y)中所有非ε-元素都加到FIRST(X)中,若X->Y1Y2……YK,是一个连续的产生式,Y1Y2……Yi-1都是非终结符,而且,对于任何j,1≤j≤i-1,FIRST(Yj)都含有ε(即Y1Y2……Yi-1=>ε),则把FIRST(Yi)中的所有非ε-元素都加到FIRST(X)中,特别是,若所有的FIRST(Yj)均含有ε,j=1,2,……,k,则把ε加到FIRST(X)中。
3.5follow集合模块
3.5.1功能
生成所有的非终结符的FOLLOW集合。
3.5.2数据结构
生成FOLLOW集合这个算法,关键就是求某非终结符A的FOLLOW集合,那么就是扫描到A后面的字符,若为终结符,则把此终结符加入到A的FOLLOW集合,那么若为非终结符,则把此非终结符的FIRST集合加入到A的FOLLOW集合中,若A为某个产生式最后一个字符,则把这个产生式的第一个字符的FOLLOW集合加入到A的FOLLOW集合中,在这个算法的过程中叶涉及到运用递归,此递归的方法同样与上述求FIRST集合中运用的递归方法类似。
数据结构框图如下:
3.5.3算法
对于文法G中每个非终结符A构造FOLLOW(A)的办法是,连续使用下面的规则,直到每个FOLLOW不在增大为止。
(1)对于文法的开始符号S,置#于FOLLOW(S)中;
(2)若A->aBb是一个产生式,则把FIRST(b)\{ε}加至FOLLOW(B)中;
(3)若A->aB是一个产生式,或A->aBb是一个产生式而b=>ε(即ε∈FIRST(b))则把FOLLOW(A)加至FOLLOW(B)中。
3.6分析表生成模块
3.6.1功能
生成文法的预测分析表。
3.6.2数据结构
求预测分析表的过程同样是对每个产生式进行扫描,对每个产生式的FIRST集合中的元素,都把相应的产生式的编号写入到对应的预测分析表中对应的位置。
数据结构框图如下:
3.6.3算法
构造分析表M的算法是:
(1)对文法G的每个产生式A->a执行第二步和第三步;
(2)对每个终结符a∈FIRST(a),把A->a加至M[A,a]中;
(3)若ε∈FIRST(a),则把任何b∈FOLLOW(A)把A->a加至M[A,b]中;
(4)把所有无定义的M[A,a]标上出错标志。
3.7预测分析程序模块
3.7.1功能
生成文法的预测分析程序。
3.7.2数据结构
生成预测分析程序,就是把’#’和文法开始符号进入到栈中,然后把相应的分析语句中当前的输入符号对应,在每一步过程中按照书上讲到的原理,在预测分析表中找到相应的产生式。
3.7.3算法
预测分析程序的总控程序在任何时候都是按STACK栈顶符号X和当前的输入符号行事的,对于任何(X,a),总控程序每次都执行下述三种可能的动作之一;
1.若X=a=”#”,则宣布分析成功,停止分析过程.
2.若X=a≠”#”,则把X从STACK栈顶逐出,让a指向下一个输入符号.
3.若X是一个非终结符,则查看分析表M,若M[A,a]中存放着关于X的一个产生式,那么,首先把X逐出STACK栈顶,然后,把产生式的右部符号串按反序一一推进STACK栈(若右部符号为ε,则意味着不推什么东西进栈).在把产生式的右部符号推进栈的同时应做这个产生式相应得语义动作,若M[A,a]中存放着”出错标志”,则调用出错诊察程序ERROR。
4程序设计与实现
4.1程序流程图
4.2程序说明
程序分为三部分:
1.文件的头文件准备,负责对类、栈的定义,2.功能函数的编写,负责所有功能函数的编写,3.主函数,负责对所有函数的调用与程序流程控制。
4.3实验结果
该程序是在VC++6.0以下编写的,里面没有用到C++的知识,纯C编写的代码.在运行之前,先将要分析的文法写入一个文本文档[*.txt]里,这里是:
G:
E→E+T|T
T→T*F|F
F→(E)|i
输入待分析的字符串是:
i*i+i
按教材P.76表4.1构造出G的预测分析程序,
程序显示输出如P.78那样的匹配过程。
注意:
只有这里用到两个符号"→"与"ε",其中"→"在特殊符号里、"ε"在希腊字母里,此处将上面这样的产生式组存放在eq.txt这个文本文档里作为示例。
下面显示语法分析进行个步骤。
运行结果如图:
5结论
通过读取任意的文本文件里的文法,合理的数据结构存储,正确的编译原理算法可以正确的分析输入字符串,达到预期效果。
6参考文献
1、陈火旺.《程序设计语言编译原理》(第3版).北京:
国防工业出版社.2000.
2、美AlfredV.AhoRaviSethiJeffreyD.Ullman著.李建中,姜守旭译.《编译原理》.北京:
机械工业出版社.2003.
3、美KennethC.Louden著.冯博琴等译.《编译原理及实践》.北京:
机械工业出版社.2002.
4、金成植著.《编译程序构造原理和实现技术》.北京:
高等教育出版社.2002.
7收获与体会
因为这是一个团体合作项目,刚开始我们组就分工问题就遇到了困难,数据结构与编译原理课程知识并不是很好,不知怎么正确分工,后来经过商讨,最后意见达成一致,开始各自的程序设计。
因为要考试的原因,所有想尽快把这个课程设计做完,可是越急越容易出错,做起来很不顺手,特别是编程的时候,虽然借鉴了几篇类似的试验或课程设计,但完成这一设计仍不是一件简单的事情。
在设计过程中主要遇到以下三个个问题:
1.数据结构问题。
在此程序中,用到队列,堆栈等形式的数据结构,各节点链接复杂,遇到不少问题。
在细心分析并逐步改正下,解决了此问题。
2.小组成员以前合作较少,各自完成自己的分工没有与其他人约定好接口的问题,在后期程序组装过程中遇到了很大麻烦,经过组内成员花费大量时间解决接口的问题。
3.ε、→符号问题。
一开始在visualstudio2005中没有找到这两个符号,苦恼半天。
后来想个办法在MicrosoftWord中,找到此符号后,直接粘贴到程序代码中。
但在粘贴之后,还出了问题。
后来把ε符号用@代替,→用->代替解决问题。
总的来说,此次编译原理课程设计加深了我对编译这门课的理解,发现自己还有很多的地方要复习,甚至还有知识点的理解偏差。
这些将直接影响期末考试,我将在以后的时间里,努力完善这些不足。
而且组员之间的合作默契程度还远远不够,这些都是我们要学习的地方。
8附录
程序第一部分
classSeqStack;//声明栈类
classgrammer//定义grammer类,此类的作用把文本文件中的文法保存,并且产生这个文法的非终结符集合,终结符集合,first集合,fellow集合,预测分析表等
{
public:
grammer();
~grammer();
voidopenfile(char*);
voidprepareform();
voidbuildform();
voidbuildProcess(SeqStack&ss);
private:
charbegin;
char*vt;
char**g;
char*vn;
char**first;//这时所有非终结符的first集合
char**fellow;//这是所有非终结符的fellow集合
char*emptychar;//这个集合中的元素为对应first集合中的有空字符的非终结符
int**form;//这是对应文法的预测分析表
intcount;
voidbuildVn();
voidbuildVt();
voidbuildemptychar();
voidbuildfirst();
voidbuildfellow();
voidfellowzh(char*,char);
voidfirstzh(char*,char);
intsearch(char*,char);
voidprintform();
voidoutputblank(int);
};
constintstackIncreament=20;
classSeqStack//定义SeqStack类,这个类的主要作用是在对语句的预测分析过程中作符号栈
{
public:
friendgrammer;
SeqStack(intsz=50);
~SeqStack();
voidPush(charx);
boolPop(char&x);
boolgetTop(char&x);
boolIsEmpty();
boolIsFull();
intgetSize();
voidMakeEmpty();
voidshowPlay();
private:
char*elements;
inttop;
intmaxSize;
voidoverflowProcess();
};
程序第二部分
#include
#include
#include
#include
#include
#include
#include"编译.h"
usingnamespacestd;
grammer:
:
grammer()
{
count=0;
}
grammer:
:
~grammer()
{
inti;
for(i=0;ideleteg[i];
deleteg;
deletevt;
deletevn;
}
voidgrammer:
:
openfile(char*file)//这个函数的主要功能在于对文本文件中的文法进行读入,并保存在对应的数组中,一并产生对应文法的终结符集合和非终结符集合
{
charch;
inti,j;
ifstreaminfile(file,ios:
:
in);//定义文件流
if(!
infile)
{
cout<<"openerror!
"<exit
(1);
}
while((ch=infile.get())!
=EOF)
{
if(ch=='-')
{
if((ch=infile.get())=='>')
{
count++;
}
else
{
infile.seekg(-1,ios:
:
cur);
}
}
elseif(ch=='|')
{
count++;
}
else
{
}
}
g=newchar*[count];
for(i=0;i{
g[i]=newchar[15];
g[i][0]=0;
}
infile.clear();//能重新激活文件流
infile.seekg(0,ios:
:
beg);//定位
i=0;
j=1;
while((ch=infile.get())!
=EOF)
{
if(ch=='-')
{
if(infile.get()!
='>')
{
g[i][j]=ch;
g[i][0]++;
j++;
if(ch==EOF)
break;
else
infile.seekg(-1,ios:
:
cur);
}
}
elseif(ch=='|')
{
i++;
g[i][1]=g[i-1][1];
g[i][0]=1;
j=2;
}
elseif(ch=='\n')
{
infile.seekg(-3,ios:
:
cur);
ch=infile.get();
if(ch!
='\n')
{
i++;
j=1;
}
infile.seekg(2,ios:
:
cur);
}
else
{
g[i][j]=ch;
g[i][0]++;
j++;
}
}
cout<for(i=0;i{
for(j=1;j<=g[i][0];j++)
{
cout<}
cout<}
buildVn();//建立非终结符集合
buildVt();//建立终结符集合
}
voidgrammer:
:
buildVn()
{
inti=1,j=0;
vn=newchar[count+1];
vn[i]=g[j++][1];
for(;jif(g[j][1]!
=g[j-1][1])
{
i++;
vn[i]=g[j][1];
}
vn[0]=i;
begin=vn[1];
cout<<"本文法开始符为:
"<cout<<"本文法非终结符为:
"<<"";
for(i=1;i<=vn[0];i++)
cout<cout<}
voidgrammer:
:
buildVt()
{
inti=1,j=0,t;
charch;
vt=newchar[100];
vt[0]=0;
for(;j{
t=2;
for(;t<=g[j][0];t++)
{
ch=g[j][t];
if(!
search(vt,ch)&&!
search(vn,ch))
{
vt[i++]=g[j][t];
vt[0]++;
}
}
}
cout<<"本文法终结符为:
"<<"";
for(i=1;i<=vt[0];i++)
cout<cout<}
intgrammer:
:
search(char*a,charch)//搜索函数,用于在指定数组中对指定字符进行搜寻,若存在输出对应的序号,若不在则输出0
{
for(inti=1;i<=a[0];i++)
{
if(ch==a[i])
returni;
}
return0;
}
voidgrammer:
:
buildemptychar()//建立对应first集合中含有空字符的的非终结符集合
{
inti=0,j=2,t=1;
boolflag=true,flag1;
emptychar=newchar[vn[0]+1];
emptychar[0]=0;
for(;i{
if(g[i][2]=='@'&&!
search(emptychar,g[i][1]))
{
emptychar[t++]=g[i][1];
emptychar[0]++;
}
}
while(flag)
{
flag=false;
for(i=0;i{
for(j=2;j<=g[i][0];j++)
{
if(search(emptychar,g[i][j]))
{
flag1=true;
}
else
{
flag1=false;
break;
}
}
if(flag1==true&&!
search(emptychar,g[i][1]))
{
emptychar[t++]=g[i][1];
emptychar[0]++;
flag=true;
}
}
}
cout<<"First集中含有空字符的有:
";
for(i=1;i<=emptychar[0];i++)
{
cout<}
cout<}
voidgrammer:
:
buildfirst()
{
inti;
first=newchar*[vn[0]];
for(i=0;i{
first[i]=newchar[vt[0]+1];
}
for(i=0;ifirst[i][0]=0;
for(i=1;i<=vn[0];i++)
{
firstzh(first[i-1],vn[i]);
}
}
voidgrammer:
:
buildfellow()
{
inti,j;
fellow=newchar*[vn[0]];
for(i=0;i{
fellow[i]=newchar[vt[0]+1];
}
for(i=0;ifellow[i][0]=0;
for(i=1;i<=vn[0];i++)
{
fellowzh(