北方工业大学计算机专业编译原理实验报告语法分析器详细代码报告.docx
《北方工业大学计算机专业编译原理实验报告语法分析器详细代码报告.docx》由会员分享,可在线阅读,更多相关《北方工业大学计算机专业编译原理实验报告语法分析器详细代码报告.docx(23页珍藏版)》请在冰豆网上搜索。
北方工业大学计算机专业编译原理实验报告语法分析器详细代码报告
编译原理实验报告
实验题目:
语法分析器构造
指导教师:
杨健
姓名:
班级:
学号:
实验成绩:
实验题目
语法分析器构造
实验目的和要求
编译器实现技术是一大宝库,一方面以编译器的实现为背景可以实践几乎全部在数据结构与算法分析课程中学到的主要数据结构与算法;另一方面,编译器设计中使用的问题求解方法、处理问题的思路被广泛地用于自动数据处理(转换)及其它一些新的研究领域。
没有编译器的出现就没有现代数字计算机的发展。
本次课设即以“语法规则的存储与显示”、“句子的生成”、“语法(分析)树的建立”等等这些编译器中的一些基本功能的实现为题,对高级程序设计语言在计算机中的表达和相关的处理有一个初步认识,提前领略“数据的自动转换与处理”这一计算机问题求解的核心技术。
尽管这些功能的实现并不涉及较深入的编译技术,但也需要带着问题预先学习、掌握有关形式语言、编译原理与技术的若干基本概念。
借助于词法分析程序提供的分析结果,编写一个算符优先语法分析程序,程序能进行语法结构分析和错误检查并产生相应的归约信息。
同时给出出错信息和错误类型,从而加深对语法分析的理解。
设计思想与框架
1.算符优先文法
定义:
设有不含空串的一文法G,如果G中没有形如G>……BC……的产生式,其中B和C为非终结符,且对任意两个终结符a,b之间之多只有<,>,=,三种关系的一种成立,则称G是一个算符优先文法。
非终结符的FIRSTVT集合和LASTVT集合
FIRSTVT(B)={b|B→b…或B→Cb…}
LASTVT(B)={b|B→…a或B→…aC}
2.算符优先矩阵
算符优先关系矩阵,判断输入是否满足已知文法的依据。
根据非终结符的FIRSTVT集合和LASTVT集合产生。
1.“=”关系
若A→…ab…或A→…aBb…,则a=b;
2.“
”关系
若A→…aB…,对每一个b属于FIRSTVT(B),有a
b;
3.“
”关系
若A→…Bb…,对每一个a属于LASTVT(B),有a
b。
3.如何归约
在分析过程中,利用分析栈存放已识别的那部分句型,而句型的其余部分由剩余输入串组成,通过比较栈顶符号和下一个输入符号之间的关系,可以判别栈顶符号是否为句柄尾符号。
如果是句柄尾,则沿栈顶向下,在栈内寻找句柄头。
关系和关系之间包括的符号串就是句柄,然后把它们弹出栈,并代之以归约后的非终结符。
这样就完成了一次归约过程。
4.算符优先分析方法的局限性
由于算符优先分析法去掉了单非终结符之间的归约,尽管在分析过程中,当决定是否为句柄时采取一些检查措施,但仍难完全避免把错误的句子得到正确的归约。
此外,通常一个适用语言的文法也很难满足算符优先文法的条件,因而致使算符优先分析法仅适用于表达式的语法分析。
输入:
所给文法的源程序字符串。
输出:
二元组构成的序列。
实现过程:
算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。
算符优先文法的执行过程为:
输入已知文法,分析其正确性,提取非终结符和终结符,构造非终结符的FIRSTVT集和LASTVT集,再次基础上构造算符优先关系矩阵,并用来判断表达式是否符合该文法。
(1)输入已知文法,生成文法矩阵,判断该文法是否是算符优先文法。
(2)用程序自动生成该文法的算符优先关系矩阵。
(3)对人工输入的句型或句子,分析该句型或句子是否合法,能否用已知文法推出。
(4)具有通用性。
所开发的程序可适用于不同的文法和任意输入串,且能判断该文法是否为算符优先文法。
(5)有运行实例。
对于输入的文法和符号串,所编制的语法分析程序应能正确判断此串是否为文法的句子,并要求输出分析过程。
核心算法
voidprior_analysis()
{inti,j,m;
charq,str,ch[20];
push('#');
print(0,-1);
u:
readvt(&a);
if(IsVT(stack[top]))
j=top;
else
j=top-1;
do
{while(big(j,a)&&strcmp(stack,"#N")!
=0)
{do
{q=stack[j];
if(IsVT(stack[j-1]))
j=j-1;
else
j=j-2;
}while(!
less(j,q));
i=-1;
while((top-j)!
=0)
{
ch[++i]=pop();
}
ch[i+1]='\0';
for(m=0;m<=5;m++)
{
if(strcmp(word[m],ch)==0)
str='N';
}
push(str);
print(1,1);
}
if(less(j,a))
{
push(a);
print(0,-1);
if(stack[top]!
='#')
gotou;
}
else
{if(equal(j,a))
{push(a);
print(0,0);
if(stack[top]!
='#')
gotou;
}
else
{error(j);
a='#';
}
}
}while(a!
='#');
}
源程序
及注释
//实验二语法分析
//算术表达式->项|算术表达式+项|算术表达式-项
//项->因子|项*因子|项/因子
//因子->变量|常数|(算术表达式)
//算符优先关系表
//----------------------------------
//+-*/()i#
//+>><<<><>
//->><<<><>
//*>>>><><>
///>>>><><>
//(<<<<<=
//)>>>>?
>?
>
//i>>>>?
>?
>
//#<<<<
<=
//----------------------------------
#include
#include
#include
usingnamespacestd;
#defineN100
#defineNULL0
FILE*fp;//预处理文件指针
//定义符号栈的大小与输入字符串的大小以及算术表达式字符串的大小
charstack[N],strings[N],oldstrings[N];
chara;
inttop=-1,k=0,step=1,n=0,No[N],id=1;
//二维数组定义字符之间的优先关系(1表示>,-1表示<,0表示=,-2表示错误)
intM[N][N]={{1,1,-1,-1,-1,1,-1,1},{1,1,-1,-1,-1,1,-1,1},{1,1,1,1,-1,1,-1,1},{1,1,1,1,-1,1,-1,1},
{-1,-1,-1,-1,-1,0,-1,-2},{1,1,1,1,-2,1,-2,1},{1,1,1,1,-2,1,-2,1},{-1,-1,-1,-1,-1,-2,-1,0}};
char*word[6]={"N+N","N-N","N*N","N/N",")N(","i"};//可归约字符串
intprint(intt,intm)
{
cout<<"\n"<cout<if(m==1)
cout<<">"<elseif(m==0)
cout<<"="<else
cout<<"<"<cout<cout<<&strings[k]<if(t)
{
cout<<"归约"<No[n++]=step-1;
}
else
cout<<"移进"<return0;
}
voidpush(charch)
{
stack[++top]=ch;
}
charpop()
{
chara;
a=stack[top--];
stack[top+1]='\0';
returna;
}
intch_di(charch)
{
switch(ch)
{
case'+':
return1;
case'-':
return2;
case'*':
return3;
case'/':
return4;
case'(':
return5;
case')':
return6;
case'i':
return7;
case'#':
return8;
default:
return0;
}
}
intIsVT(charch)
{
if(ch=='N')
return0;
else
return1;
}
intreadvt(char*a)
{
if(IsVT(strings[k]))
{
(*a)=strings[k];
k++;
return1;
}
else
{
k++;
return0;
}
}
intbig(intt,chara)
{
if(M[ch_di(stack[t])-1][ch_di(a)-1]==1)
return1;
else
return0;
}
intless(intt,chara)
{
if(M[ch_di(stack[t])-1][ch_di(a)-1]==-1)
return1;
else
return0;
}
intequal(intt,chara)
{
if(M[ch_di(stack[t])-1][ch_di(a)-1]==0)
return1;
else
return0;
}
voiderror(intt)
{
if(ch_di(stack[t])==6||ch_di(stack[t])==7)
{
printf("\n错误e2:
缺少运算符!
");
}
elseif(ch_di(stack[t])==5)
{
printf("\n错误e1:
非法左括号!
");
}
else
printf("\n错误e3:
非法右括号!
");
}
voidprior_analysis()
{
inti,j,m;
charq,str,ch[20];
push('#');
print(0,-1);
u:
readvt(&a);
if(IsVT(stack[top]))
j=top;
else
j=top-1;
do
{
while(big(j,a)&&strcmp(stack,"#N")!
=0)
{
do
{
q=stack[j];
if(IsVT(stack[j-1]))
j=j-1;
else
j=j-2;
}while(!
less(j,q));
i=-1;
while((top-j)!
=0)
{
ch[++i]=pop();
}
ch[i+1]='\0';
for(m=0;m<=5;m++)
{
if(strcmp(word[m],ch)==0)
str='N';
}
push(str);
print(1,1);
}
if(less(j,a))
{
push(a);
print(0,-1);
if(stack[top]!
='#')
gotou;
}
else
{
if(equal(j,a))
{
push(a);
print(0,0);
if(stack[top]!
='#')
gotou;
}
else
{
error(j);
a='#';
}
}
}while(a!
='#');
}
voidmain()
{
cout<<"*********算符优先语法分析程序*********"<cout<<"E->E+T|E-T|T"<cout<<"T->T*F|T/F|F"<cout<<"F->(E)|i"<cout<<"E表示算术表达式.T表示项.F表示因子.i表示变量或常数."<cout<<"优先表"<cout<<"+-*/()i#"<cout<<"+>><<<><>"<cout<<"->><<<><>"<cout<<"*>>>><><>"<cout<<"/>>>><><>"<cout<<"(<<<<<=cout<<")>>>>e2>e2>"<cout<<"i>>>>e2>e2>"<cout<<"#<<<<if((fp=fopen("预处理.txt","r"))==NULL)//如果预处理文件中是空的
{
cout<<"请先将实验1文件夹中的预处理.txt文件复制到实验2文件夹中!
"<system("pause");
exit(0);
}
charch=fgetc(fp);
charch1;
while(ch!
='#')
{
inti=-1,j=0,m=-1;
while(ch!
='='&&ch!
='#')
{
ch1=ch;
ch=fgetc(fp);
if((ch1=='>'||ch1=='<')&&ch=='=')
ch=fgetc(fp);
}
if(ch=='#')
{
cout<<"算符优先语法分析结束!
"<fclose(fp);
system("pause");
exit(0);
}
while(ch!
=''&&ch!
='#')
{
ch=fgetc(fp);
oldstrings[++i]=ch;
}
oldstrings[i]='\0';
if(isalnum(oldstrings[j]))
strings[++m]='i';
else
strings[++m]=oldstrings[j];
j++;
while(oldstrings[j]!
='\0')
{
if(isalnum(oldstrings[j]))
{
if(isalnum(oldstrings[j-1])==0)
strings[++m]='i';
}
else
strings[++m]=oldstrings[j];
j++;
}
strings[m+1]='#';
strings[m+2]='\0';
cout<cout<<"算术表达式"<"<cout<<"转换为输入串:
"<cout<<"步骤号符号栈优先关系当前分析符剩余输入串动作";
prior_analysis();
n=0;
cout<cout<<"算术表达式"<";
while(No[n])
{
cout<<""<n++;
}
cout<while(stack[0]!
='\0')
pop();
while(No[--n])
{
No[n]='\0';
}
top=-1;
a='\0';
k=0;
step=1;
n=0;
}
cout<<"算符优先语法分析结束!
"<fclose(fp);
system("pause");
exit(0);
}
问题及处理
问题1:
预处理文件不存在
当预处理文件不存在时,程序执行时会做出相应的提醒,并且预处理文件是我们此次实验的重要文件,必须在它存在的前提下才能正常运行程序,得到语法分析。
问题2:
分析的时候把+判断成非法运算符
判断标志flag为0,导致在数据第一个元素返回值也是0,使得判断出错。
把flag判断改为-1,使得与数组各下标位不冲突。
实验结果
程序设计语言和自然语言不一样,都是用符号来描述,每个特定的符号表示特定的意思,而且程序设计语言是上下文无关的。
上下文无关就是某一个特定语句所要表达的意思和它所处的上下文没有关系,只有它自身决定。
词法分析器的功能输入源程序,按照构词规则分解成一系列单词符号。
单词是语言中具有独立意义的最小单位,包括关键字、标识符、运算符、界符和常量等。
(1)关键字是由程序语言定义的具有固定意义的标识符。
例如,Pascal中的begin,end,if,while都是保留字。
这些字通常不用作一般标识符。
(2)标识符用来表示各种名字,如变量名,数组名,过程名等等。
(3)常数常数的类型一般有整型、实型、布尔型、文字型等。
(4)运算符如+、-、*、/等等。
(5)界符如逗号、分号、括号、等等。
输出:
词法分析器所输出单词符号常常表示成如下的二元式:
(单词种别,单词符号的属性值)单词种别通常用整数编码。
标识符一般统归为一种。
常数则宜按类型(整、实、布尔等)分种。
关键字可将其全体视为一种。
运算符可采用一符一种的方法。
对于每个单词符号,除了给出了种别编码之外,还应给出有关单词符号的属性信息。
单词符号的属性是指单词符号的特性或特征。
截图一:
截图二:
截图三:
截图四:
实验心得
在算符优先文法的程序设计过程中,程序比较复杂,光数据结构设计阶段就占据了很长的时间,几种数据结构很难确定那种能满足程序要求。
一旦确定了数据结构,程序编写还是比较顺利。
很快程序就可以对正确的输入做出正确的处理,不过没有查错纠错的能力,之后加入查错纠错的功能花费了很大的力气。
对整个编程的过程把握的不是很好,以致调试很艰难。
本程序虽然还不能尽如人意,但本人也已经尽力把所有已知的错误做出修正,这次编程其实收获还是很大的。
首先,再一次锻炼了自己独立编程的能力。
从整个要求分析,到数据设计和一些算法的实现都使我对程序设计有了新的设计体会。
其次,通过该课程设计,全面系统的理解了编译原理程序构造的一般原理和基本实现方法。
把学过的计算机编译原理的知识强化,能够把课堂上学的知识通过自己设计的程序表示出来,加深了对理论知识的理解,通过实际动手做实验,从实践上认识了操作系统是如何处理命令的,对计算机编译原理的认识更加深刻。
最后,感谢实验杨健老师的辛勤指导,和同学们的帮助,谢谢你们。