收藏 分销(赏)

WHILE循环语句的翻译程序设计(LL(1)法、输出三地址表示).doc

上传人:丰**** 文档编号:4315717 上传时间:2024-09-05 格式:DOC 页数:23 大小:284.51KB 下载积分:10 金币
下载 相关 举报
WHILE循环语句的翻译程序设计(LL(1)法、输出三地址表示).doc_第1页
第1页 / 共23页
WHILE循环语句的翻译程序设计(LL(1)法、输出三地址表示).doc_第2页
第2页 / 共23页


点击查看更多>>
资源描述
武汉理工大学〈〈WHILE语句的翻译分析LL(1)法、输出三地址表示〉〉课程设计说明书 课程设计任务书 题 目: WHILE语句的翻译分析程序设计(LL(1)法、输出三地址表示) 初始条件: 理论:学完编译课程,掌握一种计算机高级语言的使用。 实践:计算机实验室提供计算机及软件软件环境。 要求完成的主要任务(包括课程设计工作量及其技术要求,以及说明书撰写等具体要求) 1) 写出符合LL(1)法的文法及属性文法。 2) 完成题目要求的中间代码三地址表示的描述。 3) 写出LL(1)法的思想,完成语法分析和语义分析程序设计。 4) 编制好分析程序后,设计若干用例,上机测试并通过所设计的分析程序。 5) 设计报告格式按附件要求书写。课程设计报告书正文的内容应包括: 1 问题描述; 2文法及属性文法的描述; 3语法分析方法及中间代码形式的描述; 4简要的分析与概要设计; 5详细的算法描述; 6给出软件的测试方法和测试结果; 7设计的特点、不足、收获与体会。 时间安排: 设计安排一周:周1、周2:完成程序分析及设计。 周3、周4:完成程序调试及测试。 周5:程序验收和撰写课程设计报告。 设计验收安排:18周的星期五第3节课到实验室进行上机验收。 设计报告书收取时间:19周的星期四下午。 指导教师签名: 年 月 日 系主任(或责任教师)签名: 年 月 日 目 录 1 问题描述…………………………………3 2 文法及属性文法的描述…………………3 3 语法分析方法及中间代码形式的描述…4 4 简要的分析与概要设计…………………7 5 详细的算法描述…………………………8 6 软件的测试方法和测试结果…………13 7设计的特点、不足、收获与体会………16 WHILE语句的翻译分析程序设计 ----LL(1)法、输出三地址表示 1 问题描述: 1.1 能够写出一个while-do语句,此语句符合LL(1)的文法。 1.2 构造词法分析程序对while-do语句进行词法分析。 1.3构造语法分析程序对while-do语句进行语法分析,判断语法正确性。 1.4 运行程序,要求有正确的语义输出(3地址码)。 2 文法及属性文法的描述: 2.1 文法描述: 2.1.1 基本概念: 文法是对语言结构的定义与描述。即从形式上用于描述和规定语言 构的称为“文法”。 定义:文法G=(VN,VT,P,Z) VN :非终结符号集 VT :终结符号集 P:产生式或规则的集合 Z:开始符号(识别符号) Z∈VN 其中: A.产生式:产生式是一个有序对(U, x), 通常写为: U ::= x 或U ® x; | U| = 1 |x| ³ 0 B.非终结符号:出现在产生式的左部,且能推出符号或符号串的 那些符号。其全体构成非终结符号集,记为VN 。 C.终结符号:不出现在产生式的左部,且不能推出符号或符号串 的那些符号。其全体构成终结符号集,记为VT 。 2.1.2 此设计针对的文法为: S->while E do A E->id1>id2| id1=id2| id1<id2 A-> id1= id2 (id1,id2代表标识符) 2.2 属性文法的描述: 2.2.1 属性文法的定义形式: 每个文法符号有一组属性,每个文法产生式A->α有一组产生式b:=f(c1,c2,……,ck)的语义规则,其中f式函数,b和c1,c2,……,ck式该产生式文法符号的属性。 2.2.2 此设计题目的属性文法为: 产 生 式 属 性 文 法( 语 义 规 则 ) S->while E do A S.begin:=newlable; E.true:=newlable; E.false:=S.next; A.next:=S.begin; S.code:=gen(S.begin‘:’||E.code|| Gen(S.ture‘:’)||A.code||gen(‘goto’S.begin); E->id1>id2| id1=id2| id1<id2 E.place:=newtemp; E.code:=B1.code||B2.code|| gen(E.place':='id1.place'>'id2.place) ||gen(E.place':='id1.place'='id2.place) ||gen(E.place':='id1.place'<'id2.place) A-> id1= id2 A.place:=newtemp; A. code:= id1.code|| id2.code|| gen(A.place':=' id1.place'=' id2.place 3语法分析方法及中间代码形式的描述; 3.1 语法分析方法: 3.1.1本次设计采用LL(1)分析 : 预测分析方法概述: 所谓的LL(1)方法是在实现时用到一个LL(1)分析钜阵和一个分析栈以及预测分析程序。 分析钜阵的元素M[A,a]中的下标A为非终结符,a为终结符或句子的结束标记“#”,钜阵元素M[A,a]的内容为一条关于A的产生式。它表明当用非终结符A向下推而当输入符a时,所应该采用的后选式。当钜阵元素为空时,则表示用A往下推导时遇到了不应该出现的符号,即A与a不能匹配,因此应该出错处理。 在构造预测分析表时,对每个终结符或“#”号用a表示,则若a∈SELECT(A->a)。令M[A,a]= A->a(一般为了简化,取M[A,a]= a)把所有的无定义的M[A,a]标上ERROR(一般在表中用空白表示)。 求SELECT (A->a)的算法: (1) 若FIRST(a);() (2)若ε∈FIRST(a),则令SELECT(A->a)=FIRST(a),否则求FOLLOW(A),并令SELECT (A->a)= FIRST(a)∪FOLLOW(A) 分析模型: 3.1.2 此程序预测分析方法: 此设计为非左递归while-do文法,应采用自上而下的预测分析方法。在此设计中,产生式只到E->id1>id2| id1=id2| id1<id2,A-> id1= id2,对A只有一种产生式而在输入串中的终结符a,b……等在程序中用id代替了,正好做到了输入串和符号栈的匹配抵消,因此简化了预测分析表的构造,对于E来说有3个侯选式,在本程序中通过函数firstset()来判断应该选哪个产生式,但是firstset()是依赖Token2数组来判断的,没有完全摆脱掉数组的限制,因此也是本程序的不足之处。 3.2 中间代码的描述: 3.2.1中间代码概述: 本此设计要求使用的是3地址的中间代码,3地址代码是由下面一般形式构成的语句序列: x:=y op z; 其中x,y,z为名字,常数或编译时产生的临时变量;op代表运算符号如标点,浮点运算符号等等。每个语句的右边只能有一个运算符。 表达式x + y * z翻译成的三地址语句序列是: t1 := y * z t2 := x + t1 常用的三地址语句有: 赋值语句x := y op z, x := op y, x := y 无条件转移goto L 条件转移if x relop y goto L 过程调用param x 和call p , n 过程返回 return y 索引赋值x := y[i]和 x[i] := y 地址和指针赋值x := &y,x := *y和*x := y 3.2.1本次设计的中间代码: 本次程序的中间代码是根据一定的语义规则写出的: T while A do E F 代码结构: S.begin : E.code to E.ture E.ture : A.code to E.false goto S.begin to E.false : …… 本次设计最终应产生的3地址形如: L0: if Token2[2].b >Token2[4].b goto L2 L1: if not goto L4 L2: Token2[6].b:= Token2[8].b L3: goto L0 L4: 4 简要的分析与概要设计: 4.1 简要的过程分析: 在词法分析部分,本次设计主要是通过Save(char* p,int x)将a [ProgramLength]中通过函数GetChr()获取的单词符号提压入 Token栈,用函数Getcode()来识别Token栈中的单词符号。 在语法分析部分,用Ecode来放代码,当Ecode栈顶有非终结符号的时候,将非终结符号用pop()函数出栈,并依据函数pabduanESA()判别Token栈顶的非终结符号,对非终结符号进行推导,将推导出的符号分别用函数InputG(),InputA(),InputE1(),InputE2(),InputE3(),逆序压入Ecode栈中,再对栈顶元素进行判断,并通过函数EStrcmp()识别关键字while和do,识别的同时将关键字放入ADDL L结构体,并且此函数可以判断语法分析是否结束。在进行非终结符识别的时候,函数体内有相应的对应不同非终结符的产生式的输出,即产生式右部的符号推进栈的同时做产生式相应的语义动作,从而实现语法制导翻译的目的,最后将语法的分析结果输出,最终以3地址表示出来。 在语义输出部分,因为在程序中已经将标识符替换成id,所以应该建立一个数组Token2[i]把Token[i]中的标识符保存下来,再通过cout语句将3地址输出。 4.2 设计流程图: 4.2.1词法分析部分简要图: 4.2.2语法分析部分简要图: 其中I代表是,T代表否 5 详细的算法描述: 5.1预定义声明部分: 包括文件的包含和常数的定义 5.2 词法分析部分: 思想概述:通过GetProgram() 函数将源程序中的逐个字符放到数组a中,并且通过函数GetChr() 将数组a中的字符逐个读出,并且在此过程中识别出单词符号,并调用函数save(),将单词符号放入Token中,再调用函数Getcode()为Token中的不同的符号赋予不同的种别编码,最后将单词符号和种别编码输出。词法分析成功! ……………………………………词法分析函数声明…………………………………… typedef struct {……次结构体用于识别关键字while,do………}ADDL; typedef struct { ……结构体用于存放单词符号…………}link; link Token[MaxiTokenNumer]; / /存放单词符号 link Token[MaxiTokenNumer]; //拷贝单词符号 void GetProgram(); //把源程序装入数组a char GetChr(); //将数组a中的元素读出 int Judge(char& chr); //用于判断'\0' int IsLetter(char c); //用于判断是否为字母 void Input(char* p,char c,int& nx); //标识符关键字入指针p指向的数组第nx+1个元素 void Save(char* word,int x); //将关键字或标志符或算符装入Token void Getcode(); //为不同的输入符号赋予不同的种别编码 char word[MaxiWordLength]; //声明临时数组 ………………………………词法分析函数体…………………………………… int Wordanalyze() {……GetProgram() { …… 把源程序装入数组a………} while((chr=GetChr())!='\0') {if(!(Judge(chr))) {break;} //跳过空格和回车取元素 if(IsLetter(chr)) { jieshu=1; while(IsLetter(chr)) {…………将关键字,标识符装入数组word………… } if(chr=='>' || chr=='='||chr=='<') nLength=nLength-1; //指向算符 word[x]='\0'; Save(word,x); } //将算符装入Token else if(chr=='>') //将'>'装入Token {Input(word,chr,x); word[x]='\0'; Save(word,x);} else if(chr=='=') //将'='装入Token { Input(word,chr,x); word[x]='\0'; Save(word,x);} else if(chr=='<') //将'<'装入Token {Input(word,chr,x); word[x]='\0'; Save(word,x);} else {printf("输入出错!\n"); / /出错处理 jieshu=0; return 0;}} //这个函数的作用是将输入符号放入Token if(jieshu==1) //词法分析结束 { Getcode(); ………… 为存放在Token中的不同的单词符号配置不同的种别编码并输出,并且将单词符号拷贝到Token2数组中,以便在语法分析结束后仍能输出具体的标识符……………… } 5.3 语法分析部分: 思想概述:建立两个栈Token和Ecode,Token相当于输入串,Ecode相当于符号栈。将Ecode栈顶元素通过函数Pop()出栈,并且根据Token栈顶的符号,调用函数firstset()选择恰当的产生式,将推倒出的符号再逆着压入Ecode,并同时输出对应的产生式。当Ecode栈顶出现#符号时,语法分析成功! ………………………………语法分析部分函数声明………………………… void Pop(); //出栈 void InputS(); //识别出S时,将其推倒出的单词符号逆着压栈 void InputE1(); //识别出E1时,将其推倒出的单词符号逆着压栈 void InputE2(); //识别出E2时,将其推倒出的单词符号逆着压栈 void InputE3(); //识别出E3时,将其推倒出的单词符号逆着压栈 void InputA(); //识别出A时,将其推倒出的单词符号逆着压栈 int firstset(); //求初始符 int panduanESA(); //识别非终结符 int EStrcmp(); //识别关键字 ………………………………语法分析部分函数体………………………… int ExpressionAnalyse() //语法分析 {printf("\n语法分析得出:\n"); strcpy(Ecode[Top++],"#"); //将#压栈 strcpy(Ecode[Top++],"S"); //将S压栈 int FLAG=1; //语法分析未结束标志 while(FLAG) {int f1=0; f1= panduanESA (); if(f1==1) {……输出S->while E do A,并将A do E while压栈 …… } else if(f1==2) {……当栈顶元素为E时…… if(f3==1) { ……输出 E->id>id,将id > id压栈…… } else if(f3==2) {…………输出E->id=id,将id = id压栈…… } else if(f3==3) {…………输出E->id<id,将id < id压栈… } } else if(f1==3) //Ecode栈顶元素为A时 { …………输出A->id<id,将id < id压栈… } else {int f2=0; f2=EStrcmp(); if(f2==1) //识别出关键字 { Pop(); nID=nID+1; } else if(f2==3) //识别出#,分析结束 { cout<<endl<<"语法正确!"<<endl; FLAG=0; return 1; } else { cout<<endl<<"语法错误啦!"<<endl; FLAG=0; return 0; } } } getch();} 5.4 语义输出部分: 思想概述:输出文法,在词法和语法分析都成功的基础上,再根据识别的是while或是do,输出3地址。 void main(){…………输出所用的文法………… if(Wordanalyze()) //词法分析成功 {if(ExpressionAnalyse()) //语法分析也成功 {int i; L[nL].add=nadd; for(i=0;i<nL;i++) {if(strcmp(L[i].str,"while")==0) //输出3地址 { cout<<endl; cout<<"正确的语义输出为:"<<endl; cout<<"L0: if "<<Token2[2].b<<" > "<<Token2[4].b<<" goto L2"<<endl; cout<<"L1: if not goto L4"<<endl; } else {cout<<"L2: "<<Token2[6].b<<":="<<Token2[8].b<<endl; cout<<"L3: "<<"goto L0"<<endl; cout<<"L4: "<<endl; } } } } } 6 软件的测试方法和测试结果: 6.1软件的测试方法: 本次课程设计在运行时,按照while语句的语法要求输入符合条件的语法,G->while E do A,其中G为初始符号,E应该是两个符号的ASCII码值的大小的判断,而A应该是一个赋值语句。最后语句应以#结束。 6.2 测试结果: 6.2.1测试用例1(词法,语法都正确): 测试用例1分析: 词法部分:因为本此输入的单词符号都是符合词法分析程序定义的符号类别(ID从1到6),所以能够得到正确的词法分析结果。 语法部分:因为次此的输入a>b是E的正确的推导,并且A推导出的是一个赋值语句a=d,符合本程序定义的while-do语法规则,所以语法正确。 语义输出:在识别非终结符号的时候将与其相对应的产生式输出(相应的语义动作),实现了语法制导翻译,最后将其以3地址的形式输出。 6.2.2 测试用例2(词法错误): 测试用例2分析: 因为符号%和@不属于本程序词法分析部分定义的正确的输入形式,即Wordanalyze()中将@和%定义为错误的输入,所以在词法分析的时候输入的语句没有通过词法分析。 本次的设计主要是针对字母,算符,关键字while和do。 6.2.3 测试用例3(语法错误): 见下页 测试用例3分析: 词法分析部分:此次的输入将while误输入为whill,因此whill只能作为标识符被识别,而不能够作为关键字被识别,因此可以通过词法分析。 语法分析部分:在程序中一开始就将开始符号S压入栈中,因此能够识别S,并在识别S的时候把S所对应的产生式输出,因此运行结果中有S->while E do A,但是当识别出S后把S出栈,把A,do,E,whill依次压入栈中以后,在识别栈顶元素whill时, 因为whill不是关键字,所以语法错误,最终不能通过语法分析。 7 设计的特点、不足、收获与体会: 7.1 设计的特点: 此次设计的特点是在语法分析的时候当识别出一个非终结符,就将其对应的产生式输出,可将其看做为相应语义动作,并且在3地址码输出的时候,通过Token2这个数组把输入的标识符保留,这样可以避免在把标识符替换为id的时候导致的标识符的丢失。 7.2 设计的不足与改进: 7.2.1(调试的程序为改进后的)没改进之前不能对“<”条件进行判断 改进前的代码与运行界面: 代码: if(chr=='>' || chr=='=') nLength=nLength-1; //指向算符 word[x]='\0'; Save(word,x); } //将关键字或标志符或算符装入Token else if(chr=='>') //将'>'装入Token { Input(word,chr,x); word[x]='\0'; Save(word,x); } else if(chr=='=') //将'='装入Token { Input(word,chr,x); word[x]='\0'; Save(word,x); } else { printf("输入出错!\n"); //出错处理 jieshu=0; return 0;} 界面: 改进后的代码与运行界面: 代码:(带下划线的为改动后的不同之处) if(chr=='>' || chr=='='||chr=='<') nLength=nLength-1; //指向算符 word[x]='\0'; Save(word,x); //将关键字或标志符或算符装入Token else if(chr=='>') //将'>'装入Token { Input(word,chr,x); word[x]='\0'; Save(word,x); } else if(chr=='=') //将'='装入Token { Input(word,chr,x); word[x]='\0'; Save(word,x); } else if(chr=='<') //将'<'装入Token { Input(word,chr,x); word[x]='\0'; Save(word,x); } else { printf("输入出错!\n"); //出错处理 jieshu=0; return 0; } 运行界面: 7.2.2(调试的程序为改进后的)没改进之前不能将具体的标识符输出 改进前的代码与运行界面:(带下划线的为改动后的不同之处) 代码:for(i=0;i<nL;i++) {if(strcmp(L[i].str,"while")==0) { cout<<endl; cout<<"正确的语义输出为:"<<endl; cout<<"L0: if "<<Token [2].b<<" > "<<Token [4].b<<" goto L2"<<endl; cout<<"L1: if not goto L4"<<endl; } else { cout<<"L2: "<<Token[6].b<<":="<<Token[8].b<<endl; cout<<"L3: "<<"goto L0"<<endl; cout<<"L4: "<<endl; } } } } } 界面如下: 改进后的代码与运行界面: 说明:改进是在把具体的标识符替换成id之前将其拷贝到Token2中,这样就可以将具体的标识符输出。 代码:for(i=0;i<nL;i++) {if(strcmp(L[i].str,"while")==0) { cout<<endl; cout<<"正确的语义输出为:"<<endl; cout<<"L0: if "<<Token2 [2].b<<" > "<<Token 2[4].b<<" goto L2"<<endl; cout<<"L1: if not goto L4"<<endl; } else { cout<<"L2: "<<Token2[6].b<<":="<<Token2[8].b<<endl; cout<<"L3: "<<"goto L0"<<endl; cout<<"L4: "<<endl; } } } } } 界面: 7.2.3(调试的程序为改进后的)不能识别算符: 改进前的代码与运行界面: 代码: if(IsLetter(chr)) { jieshu=1; while(IsLetter(chr)) {Input(word,chr,x); //是字符就将其装入数组word chr=GetChr(); } word[x]='\0'; Save(word,x); }//将关键字或标志符或算符装入Token 运行界面: 改进后的代码与运行界面:(带下划线的为改动后的不同之处) 说明:因为改进前没有if(chr=='>' || chr=='='||chr=='<')nLength=nLength-1; 每次执行getchr()函数后指针都会向后跳一个位置,如果不将nlength减1,那么算符就会被忽略。因此改动前当识别出E,并将E对应的产生式输出以后,在识别a>b时,因为识别不出>,所以会出现语法的错误。 代码:if(IsLetter(chr)) { jieshu=1; while(IsLetter(chr)) {Input(word,chr,x); //是字符就将其装入数组word chr=GetChr(); } if(chr=='>' || chr=='='||chr=='<') nLength=nLength-1; //指向算符 word[x]='\0'; Save(word,x); //将关键字或标志符或算符装入Token } 界面: 7.3心得体会: 这次的课程设计使我无论在理论基础知识上,动手实践方面,还是在心理素质方面都得到了锻炼和提高。 首先是基础知识方面,由于授课学时的限制和自己学习中的疏忽,遗漏了一些比较细小的知识点,例如:数组下标的问题,和栈使用的时候的栈顶指针指向问题,问题虽然小,但是在运行程序时,这种错误的确是比较具有隐蔽性的,也比较捆饶人的。 在本次的程序中,开始时候不能识别算符,即上述的7.2.3问题,这就属于数组下标指向问题,后来在程序中添加了代码if(chr=='>' || chr=='='||chr=='<')nLength=nLength-1; 使指针前移一个位置,运行结果就可输出算符了。 其次是在上机实验方面,在平时的学习中,学习的是理论知识,所以在进行课程设计的时候,发现一定的难度,首先是文件的预定义包含,在程序中用到的函数不清楚是在哪个预定义的函数中,因此在编译的时候总会出现“未定义的函数”的错误提示。 其次是设计的思想,由于语句相对比较多,很多函数的组合没有处理好,经常会出现函数类型不匹配的错误。由于程序比较长,语句的包含关系的搞错和匹配问题也是编译时的瓶颈。 在心理素质方面也使自己得到了锻炼,在面对困难的时候,学会要把问题逐步的细小化,给自己拟定在不同时期要功克不同的难点的计划,在困难面前不能轻言放弃。 这段时间的设计,我深刻的体会到编程的辛苦,一个优秀的程序在设计的过程中,不仅要求程序员要有清晰的思路更要有完成枯燥工作的毅力。 要结合面对的问题去图书馆找资料、上网去搜索。要静下心来读书,书读百遍,其义自现,遇到看不懂的地方,不要在那儿阻塞太久,实在看不懂就咕噜吞下去,等到看完这一章或者这一节再回过头来学习,学习就是一个往返的曲线! 心要细,天下大事,必作于细,要耐心对逐条语句进行分析,要勤动脑。要把每次的收获的点点滴滴都牢记于心,不可以不屑于小的收获,懂得不积跬步,无以至千里;不积小流,无以成江海的道理。即使再小的问题,再小的收获都要牢记,否则会对日后的编程造成阻碍。 要有真理必胜的科学理念,坚持真理,要学会承认和改进自己的错误与不足,学会否定自己,只有这样我们才能不断的提高。 要学会帮助他人,勤与与别人交流经验,无论是对方还是自己都能得到提高。 总之,在这次课程设计后我的理论知识,动手动脑能力,和心理素质都有了一定的提高,经过这次实验,我基本明白了自己做一个课题的实验过程,从中也学到很多东西,这是以前按教科书上照搬照套的实验得不到的。我明白了自己以后还要继续努力,在这里,我也要感谢我的同学和老师,正是他们帮助我解决了课程设计中遇到的一个又一个难题。 最后感谢指导老师抽出时间为我们验收,我期待下次课程设计的到来! 第 23 页 共 23 页
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服