FOR循环语句的翻译程序设计递归下降法输出四元式表示.docx
《FOR循环语句的翻译程序设计递归下降法输出四元式表示.docx》由会员分享,可在线阅读,更多相关《FOR循环语句的翻译程序设计递归下降法输出四元式表示.docx(24页珍藏版)》请在冰豆网上搜索。
FOR循环语句的翻译程序设计递归下降法输出四元式表示
FOR循环语句的翻译程序设计(递归下降法、输出四元式表示)
1、系统描述
1.1、实验思想
通过设计、编制、调试一个FOR循环语句的语法及语义分析程序,加深对语法及语义分析原理的理解,实现词法分析程序对单词序列的词法检查和分析,并且实现对单词序列的语法分析、语义分析以及中间代码生成。
1.2、设计内容
本设计按照要求设计出for语句的简单文法,并使用递归下降分析法对用户输入的程序进行分析和翻译。
对下列正确的程序输入:
fori=1step1until10dok=j#
结果程序要对该输入进行词法分析,然后利用递归下降的分析法对词法分析得到的单词序列进行语法分析,经过语法制导翻译显示出等价的三地址表示的中间代码。
对于错误的程序输入,如:
Fori=1step1until10k=j#
结果程序要指出程序出错。
1.3、翻译过程
1.3.1、词法分析:
词法分析是计算机科学中将字符序列转换为单词(Token)序列的过程。
进行语法分析的程序或者函数叫作词法分析器(Lexicalanalyzer,简称Lexer),也叫扫描器(Scanner)。
词法分析器一般以函数的形式存在,供语法分析器调用。
词法分析是编译过程中的第一个阶段,在语法分析前进行。
也可以和语法分析结合在一起作为一遍,由语法分析程序调用词法分析程序来获得当前单词供语法分析使用。
简化设计、改进编译效率、增加编译系统的可移植性。
词法分析是编制一个读单词的过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。
并依次输出各个单词的内部编码及单词符号自身值。
单词的分类主要分为五类:
1.关键字:
由程序语言定义的具有固定意义的标识符。
也称为保留字或基本字。
2.标识符:
用来表示程序中各种名字的字符串。
3.常数:
常数的类型一般有整型、实型、布尔型、文字型。
4.运算符:
如+、-、*、/等。
5.界限符:
如逗号、分号、括号等。
词法分析器输出的单词符号常表示成如下的二元式:
(单词种别,单词符号的属性值)
1.3.2、语法分析:
语法分析是编译过程的一个逻辑阶段。
语法分析的任务是在的基础上将单词序列组合成各类语法短语,如“程序”,“语句”,“表达式”等等.语法分析程序判断源程序在结构上是否正确.源程序的结构由上下文无关文法描述.语法分析程序可以用YACC等工具自动生成。
语法分析是编译程序的核心部分,其主要任务是确定语法结构,检查语法错误,报告错误的性质和位置,并进行适当的纠错工作。
语法分析的主要工作:
是识别由词法分析给出的单词序列是否是给定的正确句子(程序)。
语法分析常用的方法:
自顶向下的语法分析和自底向上的语法分析两大类。
此次设计中语法分析中主要通过递归下降分析法对语法分析处理过程进行控制,使输出的三地址表示的翻译的工作有条不紊的进行,同时识别语法分析中的语法错误。
递归下降法主要采用自顶向下方法,即从文法的开始符号开始进行分析,逐渐推导的往下构造语法树,使其树叶正好构造出所给定的源程序串。
自顶向下方法的关键是确定在推导过程中选择候选式的问题。
当进行推导时,一个非终结符可能对应多个产生式,这样我们就无法事先知道应该用哪个产生式,因此实用都作了一些限制。
以便在任何情况下都能确定应该用的产生式。
自顶向下的主要思想是从开始符出发导出句型并一个符号一个符号地与给定终结符串进行匹配。
如果全部匹配成功,则表示开始符号可推导出给定的终结符串。
因此判定给定终结符号串是正确句子。
词法分析程序和语法分析程序的关系:
1.3.3、中间代码生成:
中间代码,也称中间语言,是复杂性介于源程序语言和机器语言的一种表示形式。
为了使编译程序有较高的目标程序质量,或要求从编译程序逻辑结构上把与机器无关和与机器有关的工作明显的分开来时,许多编译程序都采用了某种复杂性介于源程序语言和机器语言之间的中间语言。
中间代码(语言)是一种特殊结构的语言,编译程序所使用的中间代码有多种形式。
按其结构分常见的有逆波兰式(后缀式)、三地址代码(三元式、四元式)和树形表示(抽象语法树)、DAG表示。
本次课程设计要实现的是三地址表示。
1.3.4、属性文法:
对于文法的每个产生式都配备了一组属性的计算规则,称为语义规则。
所谓语法制导的翻译指的是在语法分析过程中,完成这些语义规则描述的动作,从而实现语义处理。
一个属性文法包含一个上下文无关文法和一系列语义规则,这些语义规则附在文法的每个产生式上。
形式上讲,属性文法是一个三元组:
A=(G,V,F),其中:
G:
是一个上下文无关文法;
V:
有穷的属性集,每个属性与文法的一个终结符或非终结符相连,这些属性代表与文法符号相关信息;
F:
关于属性的属性断言或一组属性的计算规则(称为语义规则)。
断言或语义规则与一个产生式相联,只引用该产生式左端或右端的终结符或非终结符相联的属性。
2、递归下降法
递归下降法又称递归子程序法。
在程序语言的语法定义中有许多采用递归定义。
我们在对它进行语法分析时,编制的处理程序也采取递归的方式,可使其结构简单易读。
但由于频繁地调用子程序大大地降低了分析速度。
2.1、递归下降法的主要思想:
对每个非终结符按其产生式结构写出相应语法分析子程序。
因为文法递归相应子程序也递归,子程序的结构与产生式结构几乎一致。
所以称此种方法称为递归子程序法或递归下降法。
2.2、用程序表示递归子程序的内部结构:
设A是一个非终结符:
A→β1
A→β2
┊
A→βn
则写ζ(A)⇔ifchar∈first(β1)thenζ(β1)
elseifchar∈first(β2)thenζ(β2)
else…
ifchar∈first(βn)thenζ(βn)
elseERROR
其中ζ(βi)表示调用处理符号串βi的子程序。
对A的任一右部i设为:
βi=y1y2…yn
则定义ζ(βi)⇔beginζ(y1);ζ(y2);…;ζ(yn)end
其中yj可分为下列两种情况(j=1,…,n):
1)yj∈VT,则
ζ(yj)⇔ifchar≠yjthenERRORelseREAD(char)
2)yj∈VN,则ζ(yj)表示调用关于yj的递归子程序。
2.3、递归下降法对文法的限制:
1、任一非终结符B都不是左递归的,否则会产生死循环。
2、对A的任意两个右部βi,βj,有:
first(βi)∩first(βj)=φ。
First(βi)表示βi所能导出串的第一个符号的集合。
显然,每个βi的first(βi)是互不相同的,否则则无法判断应执行哪个ζ(βi)。
3、语法制导翻译
3.1、翻译任务的处理过程
编译程序的整个任务就是把源程序翻译为目标程序。
实际上可以把每个编译阶段都看作是完成一定翻译任务的处理过程:
词法分析阶段把字符流翻译为单词流,语法分析阶段把单词流翻译为语法树,目标代码生成阶段把语法树翻译为汇编语言等等。
3.2、语法制导翻译:
在语法分析过程中,随着分析的步步进展,每当进行推导或归约时,同步的去执行每个产生式所附带的语义规则描述的语义动作(或语义子程序),这样进行翻译的办法称作语法制导翻译。
所谓属性依赖图是一个有向图,用于描述分析树中的属性和属性间的相互依赖关系。
3.3、基于属性文法的处理方法
输入串→语法树→依赖图→计算语义规则顺序→语法分析树遍历→执行语义规则
语义规则的计算可能产生代码,在符号表中存取信息,给出出错信息或执行其它动作。
对输入串的翻译也就是根据语义规则进行计算的结果。
4、中间代码形式的描述及中间代码序列的结构设计
本次设计,使用的中间代码为四元式四元式有四个组成成分:
算符op,第一和第二运算对象ARG1和ARG2,运算结果RESULT。
例如:
有中缀式a:
=b*-c+b*-c,求其等价的四元式。
四元元式
编号算符左对象右对象中间结果
oparg1arg2result
(1)minusct1
(2)*bt1t2
(3)minusct3
(4)*b(3)t4
(5)+(4)
(2)t5
(6)assigna
设计并生成的结果程序,最终需要将用户输入的程序经过词法分析和语法分析,生成如上所述的式表示的中间代码形式。
5、简要的分析与概要设计
程序由词法分析和语法分析两部分构成:
5.1、词法分析:
intbuffer()//载入
{
inti=0;
cout<<"输入程序,以#作为结束标志。
"<for(intn=0;n<=MAX;n++)
{
for(;i<=MAX;i++)
{
scanf("%c",&str[i]);
if(str[i]=='#')
break;//如果尾数为识别码#,则表示程序读完,跳出循环.
}
break;
}
return(i);
}
boolIsLetter(charch)//判断是否是字母
{
if(ch>=65&&ch<=90||ch>=97&&ch<=122)
return(true);
else
return(false);
}
boolIsDigit(charch)//判断是否是数字
{
if(ch>=48&&ch<=57)
return(true);
else
return(false);
}
charGetChar(inti)//读取字符
{
charch;
ch=str[i];
return(ch);
}
charGetBC(charch)//判断是不是空格或者换行,如果是,直接读取下一个字符直道不再空白为止
{
if(ch==32||ch==10)
{
turn++;
ch=GetChar(turn);
ch=GetBC(ch);//递归实现
return(ch);
}
else
return(ch);
}
voidConcat()//连接,即为strtoken[]赋值
{
strToken[n]=ch;
n++;
}
intReserve()//以单词为单位查找保留字,是则返回编码,不是则返回0,用来区分标志符和保留字
{
if(strcmp(strToken,"DIM\0")==0)//调用strcmp函数实现,
return
(1);
elseif(strcmp(strToken,"for\0")==0)
return
(2);
elseif(strcmp(strToken,"step\0")==0)
return(3);
elseif(strcmp(strToken,"until\0")==0)
return(4);
elseif(strcmp(strToken,"do\0")==0)
return(5);
else
return(6);
}
voidclear()
{
n=0;
}
ch=GetChar(++turn);
}
ch=NULL;
turn=turn-1;
kind=7;
cout<<"(";
for(inti=0;i{
record[x]->word[i]=strToken[i];
cout<word[i];
}
cout<<","<record[x]->word[i]='\0';
clear();x++;
}
elseif(ch=='=')
{
kind=8;
record[x]->word[0]='=';
record[x++]->sort=kind;
cout<<"(=,"<}
else
cout<<"errorinput!
"<
5.2源代码
#include
#include
#include
#include
chara[50],b[50],d[200],e[10];
charch;
intn1,i1=0,flag=1,n=5;
inttotal=0;/*步骤计数器*/
intE();
intE1();
intT();
intG();/*E’*/
intS();/*T’*/
intF();
voidinput();
voidinput1();
voidoutput();
voidmain()/*递归分析*/
{
intf,p,j=0;
charx;
d[0]='E';
d[1]='=';
d[2]='>';
d[3]='T';
d[4]='G';
d[5]='#';
printf("请输入字符串(长度<50,以#号结束)\n");
do{
scanf("%c",&ch);
a[j]=ch;
j++;
}while(ch!
='#');
n1=j;
ch=b[0]=a[0];
printf("步骤\t文法\t分析串\t\t分析字符\t剩余串\n");
f=E1();
if(f==0)return;
if(ch=='#')
{
printf("accept\n");
p=0;
x=d[p];
while(x!
='#'){
printf("%c",x);p=p+1;x=d[p];/*输出推导式*/
}
}else{
printf("error\n");
printf("回车返回\n");
getchar();
getchar();
return;
}
printf("\n");
printf("回车返回\n");
getchar();
getchar();
}
intE1()
{
intf,t;
printf("%d\tE-->TG\t",total);total++;
flag=1;
input();
input1();
f=T();
if(f==0)return(0);
t=G();
if(t==0)return(0);
elsereturn
(1);
}
intE()
{
intf,t;
printf("%d\tE-->TG\t",total);total++;
e[0]='E';e[1]='=';e[2]='>';e[3]='T';e[4]='G';e[5]='#';
output();
flag=1;
input();
input1();
f=T();
if(f==0)return(0);
t=G();
if(t==0)return(0);
elsereturn
(1);
}
intT()
{
intf,t;
printf("%d\tT-->FS\t",total);total++;
e[0]='T';e[1]='=';e[2]='>';e[3]='F';e[4]='S';e[5]='#';
output();
flag=1;
input();
input1();
f=F();
if(f==0)return(0);
t=S();
if(t==0)return(0);
elsereturn
(1);
}
intG()
{
intf;
if(ch=='+'){
b[i1]=ch;
printf("%d\tG-->+TG\t",total);total++;
e[0]='G';e[1]='=';e[2]='>';e[3]='+';e[4]='T';e[5]='G';e[6]='#';
output();
flag=0;
input();input1();
ch=a[++i1];
f=T();
if(f==0)return(0);
G();
return
(1);
}
printf("%d\tG-->^\t",total);total++;
e[0]='G';e[1]='=';e[2]='>';e[3]='^';e[4]='#';
output();
flag=1;
input();input1();
return
(1);
}
intS()
{
intf,t;
if(ch=='*'){
b[i1]=ch;printf("%d\tS-->*FS\t",total);total++;
e[0]='S';e[1]='=';e[2]='>';e[3]='*';e[4]='F';e[5]='S';e[6]='#';
output();
flag=0;
input();input1();
ch=a[++i1];
f=F();
if(f==0)return(0);
t=S();
if(t==0)return(0);
elsereturn
(1);}
printf("%d\tS-->^\t",total);total++;
e[0]='S';e[1]='=';e[2]='>';e[3]='^';e[4]='#';
output();
flag=1;
a[i1]=ch;
input();input1();
return
(1);
}
intF()
{
intf;
if(ch=='('){
b[i1]=ch;printf("%d\tF-->(E)\t",total);total++;
e[0]='F';e[1]='=';e[2]='>';e[3]='(';e[4]='E';e[5]=')';e[6]='#';
output();
flag=0;
input();input1();
ch=a[++i1];
f=E();
if(f==0)return(0);
if(ch==')'){
b[i1]=ch;printf("%d\tF-->(E)\t",total);total++;
flag=0;input();input1();
ch=a[++i1];
}
else{
printf("error\n");
return(0);
}
}
elseif(ch=='i'){
b[i1]=ch;printf("%d\tF-->i\t",total);total++;
e[0]='F';e[1]='=';e[2]='>';e[3]='i';e[4]='#';
output();
flag=0;input();input1();
ch=a[++i1];
}
else{printf("error\n");return(0);}
return
(1);
}
voidinput()
{
intj=0;
for(;j<=i1-flag;j++)
printf("%c",b[j]);/*输出分析串*/
printf("\t\t");
printf("%c\t\t",ch);/*输出分析字符*/
}
voidinput1()
{
intj;
for(j=i1+1-flag;jprintf("%c",a[j]);/*输出剩余字符*/
printf("\n");
}
voidoutput(){/*推导式计算*/
intm,k,j,q;
inti=0;
m=0;k=0;q=0;
i=n;
d[n]='=';d[n+1]='>';d[n+2]='#';n=n+2;i=n;
i=i-2;
while(d[i]!
='>'&&i!
=0)i=i-1;
i=i+1;
while(d[i]!
=e[0])i=i+1;
q=i;
m=q;k=q;
while(d[m]!
='>')m=m-1;
m=m+1;
while(m!
=q){
d[n]=d[m];m=m+1;n=n+1;
}
d[n]='#';
for(j=3;e[j]!
='#';j++){
d[n]=e[j];
n=n+1;
}
k=k+1;
while(d[k]!
='='){
d[n]=d[k];n=n+1;k=k+1;
}
d[n]='#';
system("pause");
}
5.3运行结果
1)在文件中输入:
i+i*i#运行结果如下图所示:
2)在文件中输入:
i+i-#运行结果如下图所示:
6、测试方法和测试结果
6.1测试过程
针对所设计的关于 for循环语句的翻译程序,分别用正确的程序和有错误的程序进行测试,测试出结果程序的可用性和健壮性。
测试中分别使用了一个合法程序和两个非法程序,对结果程序进行测试,具体的测试程序、测试过程和测试结果如下:
for循环语句语法分析过程:
输入程序:
for(d=3;d{
for(c=b;c<3;c++)
{
c=b/a+2*3;
a=c*d+b+6/2;
}
}
合法程序。
得到运行结果如图所示:
输入合法程序:
for(i=3;i{
a=a+i
}
得到运行结果如图所示:
6.2测试结论
经过测试,可以得知,结果程序能达到预计的要求:
对合法程序进行词法分析和简单优先的语法分析,并生四元式表示的中间代码;对于错误的程序输入,结果程序能够判断其出错。
本次设计的文法是:
S->for(A){G}
A->DidP
D->EidOF
E->id=F
F->id
P->++|--
O-><|>
G->GB
B->ID=S1
s1->TE1
T->FT1
E1->+TE1|&|-TE1
T1->*FT1|&|-TE1
X->B|&
*/
当输入的程序缺少do时,将会提示errordo,表明输入出错。
又因为该文法实现的设计是i=j这一赋值语句,当缺少了赋值符号“=”时,系统将会提醒赋值语句缺少赋值运算符这一错误现象。
存在问题:
对于含有非法输入符号的程序,结果程序没有很好地处理,程序健壮性不强。
例如当我输入的程序为:
Fori=1step-1until10dok=j#,该程序处理时把-1的负号给省略掉了,把这一语句和语句Fori=1step1until10dok=j#当做相同的进行处理。
这是程序设计考虑不周到的地方。
7、课程设计总结
在做本次试验之前我对递归下降原