条件语句的翻译分析程序设计

条件语句的翻译分析程序设计
--LR方法、输出四元式
1.问题描述
对条件语句: if 〈布尔表达式〉〈赋值语句〉 else 〈赋值语句〉, 进行词法,语法分析,并根据语法制导翻译方法将条件语句翻译成四元式中间代码形式,最后输出翻译后的四元式代码。
2.文法及属性文法描述
2.1 if-else语句的LR文法:
采用LR方法,因此可以得到如下文法:
G[S]: S→if(B) S else S
S→if(B)S
S→i=E
B→B&&B
B→B||B
B→!B
B→E relop E
E→E+E
E→E*E
E→-E
E→(E)
E→i
其中relop是终结符号,代表关系运算符<,<=,>,>=,!=等。
通过一遍扫描来产生布尔表达式和控制语句的代码的主要问题在于,当生成某些转移语句时还不知道该语句将要转移到的目标是什么。为了解决这个问题,可以在生成形式分之的跳转指令时暂时不确定跳转目标,而建立一个链表,把转向这个目标的跳转指令的标号键入这个链表。一旦目标确定后再把它填入有关的跳转指令中,因此将上述文法扩展为:
P→SM
S→if(B)M1S1NelseM2S2
S→if(B)MS1
S→i=E
B→B1&&MB2
B→B1||MB2
B→!B1
B→E1 relop E2
E→E1+E2
E→E1*E2
E→-E1
E→(E1)
E→i
M→¢ ;代表的是空
N→¢ ;代表的是空
2.2 if-else语句的LR属性文法:
由以上扩展的文法可得到如下的属性文法:
P→SM
{if(S.nextlist!=¢) then backpatch(S.nextlist, M.pos);
}
S→if(B)M1S1NelseM2S2
{backpatch(B.truelist,M1.pos);
backpatch(B.falselist,M2.pos);
S.nextlist=merge(S1.nextlist,S2.nextlist);
S.nextlist=merge(S.nextlist,N.nextlist);
}
S→if(B)MS1
{backpatch(B.truelist,M.pos);
S.nextlist=merge(B.falselist,S1.nextlist);
}
S→i=E
{p=lookup(https://www.360docs.net/doc/5117552052.html,);
if(p!=Null then genquad("=",E.place,"",p.place);
}
B→B1&&MB2
{ backpatch(B1.truelist, M1.pos);
B.truelist=B2 .truelist;
B.falselist=merge(B1.falselist, B2.falselist);
}
B→B1||MB2
{backpatch(B1.falselist, M1.pos);
B.truelist=B2 .truelist;
B.falselist=merge(B1.truelist,B2.truelist);
B.falselist=B2.falselist;
}
B→!B1
{B.truelist=B1.falselist;
B.falselist= B2.falselist;
}
B→E1 relop E2
{ B.truelist=makelist(nexpos+1);
B.false=makelist(nextpos+2);
genquad(relop, E1.place, E2.pl

ace,"_");
genquad("J","","","_");
}
E→E1+E2
{ e=newtemp;
e.place=E1.place+E2.place;
genquad("+", E1.place, E2.place,e.place);
}
E→E1*E2
{ e=newtemp;
e.place=E1.place*E2.place;
genquad("*", E1.place, E2.place,e.place);
}
E→-E1
{ e=newtemp;
e.place=-E.place;
genquad("uminus", E.place, "",e.place);
}
E→(E1)
{E.place=E1.place;
}
E→i
{E.place=i.value;
}
M→¢
{M.pos=nextpos+1;
}
N→¢
{ N.next=makelist(nextpos+1);
genquad("J","","","_");
}

其中relop可为任意的关系运算符,i可以为标识符,也可以为常数。M以及N是为了便于分析生成四元式而设的。
3. 语法分析方法及中间代码形式的描述
3.1 LR方法的基本思想
一个LR分析器实质上是一个带先进后出存储器的确定有限状态自动机。我们将把"历史"和"展望"材料综合地抽象成某些"状态"。分析栈用来存放状态。栈里的每个状态概括了从分析开始直到某一归约阶段的全部"历史"和"展望"资料。任何时候,栈顶的状态都代表了整个的历史和已推测出的展望。因此,在任何时候都可从栈顶状态得知所想了解的一切,而绝对没有必要从称底而上翻阅整个栈。LR分析器的每一步工作都是由栈顶
状态和现行输入符号所唯一决定的。为了有助于明确归约手续,我们把已归约出的文法符号串也同时放在栈里。于是,我们可以把栈的结构看成是:
栈的每一项内容包括状态S和文法符号X两部分。(S0,#)为分析开始前预先放到栈里的初始状态和句子括号。栈顶状态为SM,符号串X1X2....XM是至今已移进归约出的部分。
3.2 LR分析器模型
LR分析器模型如下图:









LR分析器的核心部分是一张分析表。这张分析表包括两部分,一是"动作"(ACTION)表,另一个是"状态转换表"(GOTO)表。它们都是二维数组。ACTION[s,a]规定了当状态s面临输入符号a时应采取什么动作。GOTO[s,a]规定了状态s面对文法符号X(终结符或非终结符)时下一个状态是什么。显然GOTO[S,x]定义了一个以文法符号为字母表的DFA。
每一项ACTION[s,a]所规定的动作不外是下述四种可能之一:
1. 移进 把(S,A)的下一状态S=GOTO[S,A]和输入符号A推进栈,下一输入符号变成现行输入状态。
2. 规约 指用某一产生式A-> 进行规约。假若 的长度为r,归约动作是A,去除栈顶的r个项,使状态Sm-r变成栈顶状态,然后把(Sm-r,A)的下一状态S1=GOTO[Sm-r,A]和文法符号A推进栈。归约动作不改变现行输

入符号。执行归约动作意味着(=Xm-r+1....Xm)已呈现于栈顶而且是一个相对于A的句柄。
3. 接受 宣布分析成功,停止分析器的工作。
4. 报错 发现源程序含有错误,调用出错处理程序。
LR分析器的总控程序本身的工作是非常简单。它的任何一步只需要按栈顶状态和现行输入符号a执行ACTION[S,a]所规定的动作。不管什么分析表,总控程序都是一样地工作。
一个LR分析器的工作过程可看成是栈里的状态序列,已归约串和输入串所构成的三元式的变化过程。分析地的初始三元式(S0,#,a1a2...an#)其中,S0为分析器的初态;#为句子的左括号;a1a2...an为输入串;其后的#为结束符。分析过程每步的结果可表示为(s0s1...sm,# X1X2...,ai....an#)分析器的下一步动作是由栈顶状态Sm和现行输入符号ai所唯一决定。即,执行ACTION[Sm,ai]所规定的动作。经执行每种可能的动作之后,三元式的变化的情形是:
(1) 若ACTION[Sm,ai]为移进,且S=GOTO[Sm,ai],则三元式变成:
(S0S1...Sm,#X1X2...Xmaian#)
(2) 若ACTION[Sm,ai]={A-> },则按产生式A-> 进行归约。此时三元式变为
(S0S1...Sm-rS,#X1...Xm-rA,aiai+1...an#)
此处S =GOTO[Sm-r,A],r为 的长度, =Xm-r+1...Xm。
(3) 若ACTION[Sm,ai]为:接受,则三元式不再变化,变化过程终止,宣布分析成功。
(4) 若ACTION[Sm,ai]为"报错",则三元式的变化过程终止,报告错误。
一个LR分析器的工作过程就是一步一步地变换三元式,直至执行"接受"或"报错"为止。
3.3对中间代码四元式的描述
一个四元式是一个带有四个域的记录结构,这四个域分别称为op、arg1、arg2及result。域op包含一个代表运算符的内部码。三地址语句x=y op z可以表示为:将y 置于arg1域,z置于 arg2域, x置于 result域, =为运算符。带有一元运算符的语句如 x=-y或者 x=y的表示中不用 arg2。条件和无条件转移语句将目标标号置于 result域中。通常,四元式中的arg1,arg2和result的内容都是一个指针,此指针指向有关名字的符号表示入口。这样,临时变量名也要填入符号表。
例如对于条件语句if(a>b) c=d+f;对应的一串四元式为:
Op arg1 arg2 result (0)
(1)
(2)
(3)
(4) J>
J
+
=
a

d
T1
b

f

(2)
(4)
T1
c
表一 四元式
4.简要的分析与概要设计
本课程设计需要写一个条件语句的LR文法及其属性文法,运用LR分析方法对此文法进行语法和语义分析,中间代码采用四元式输出。在这个条件语句的翻译分析程序设计中,主要通过以下四个过程来完成:
1.词法分析。由于编译程序是在单词的级别上来分析和翻译源程序的,那么在这里,词法分析的任务是:从左至右逐

个字符地对源程序进行扫描,产生一个一个的单词符号,把作为字符串的源程序改造成为单词符号串的中间程序。所以词法分析是编译的基础。在此程序中是将词法分析作为一遍处理的,通过一次分析把全部的字符串都分析完成,并将其保存在数组中便于下一步进行语法分析。
2.语法分析。在完成词法分析的基础上对条件语句进行语法分析,在这里我采用了自下而上分析法LR(1)分析方法,来分析判定程序的语法结构是否符合语法规则,在分析前首先要构造LR(1)分析表,然后在进行语法分析,在此程序中,以';'为结束符号来判断一条条的条件语句,并且独立的对每条语句进行语法分析,若分析正确便在DOS环境下显示"一个条件语句的分析完成!!!",若语法分析错误,便会输出"ERROR"。
3.语义分析、输出四元式。在进行语法分析的同时进行语义分析,在此次设计中式将二者结合起来作为一遍进行处理的。在进行语义时同时生成中间语言四元式。
4.出错处理。如果在词法分析时遇到非法字符就会输出出错信息,同时输出从出错点开始往后的一串字符,但是它仍然能跳过该非法字符继续分析;如果在语法分析中有错误的话,就会显示在DOS环境下输出"ERROR",但是它能跳过出错的地方继续往后执行,分析出一部分结果并保存在文件中。
5.详细的算法描述
5.1词法分析:
本程序中,选用C程序设计语言的部分常用的单词作为词法分析的对象,词法分析后,将识别的所有单词符号以及相关信息保存在数组中,以便后面语法分析和语意分析及中间代码生成使用,同时将识别出的单词符号输出到文件中,一便进行查看。
词法分析过程过程中采用了三个数组保存分析的结果,定义如下:
struct mymap
{ int code,value;
};
string *var=new string[20];//保存标识符
float *myconst=new float[20];//保存常数
mymap *result=new mymap[40];//code用来保存所识别的单词的机内码,当该单词为标识符或常数时,value保存该标识符或常数在标识符数组或常数数组中的下标,而当该单词为其它的关键字时,value的值为0。
该词法分析程序从文件进行输入,词法分析中各个函数描述如下:
void renewresult();
当result数组空间满时,对其重新分配,并保存一以前的值。
void renewvar();
当var数组空间满时,对其重新分配,并保存一以前的值。
void renewconst();
当myconst数组空间满时,对其重新分配,并保存一以前的值。
bool isletter(char c);
判断当前输入的字符是否为字母。
bool isdigital(char c);
判断当前输入的字符是否为数字。
int reserve(char c[],int i);

查找保留字表,并返回它的编码,否则返回0。
void insertresult(int code,int value);
将当前识别出的单词的种别编码和其值保存到result数组中。如果该单词为标识符或常数,则其value值为其在var或myconst数组中的下标,否则其值为0。
void insertid(char c[],int i);
将当前识别出的标识符保存到数组var中,并将其下标保存到数组result的value中。
void insertconst(char c[],int i);
将当前识别出的常数保存到数组myconst中,并将其下标保存到数组result的value中。
void wordanalyse();
词法分析的总控函数,控制整个词法分析过程。
5.2语法分析:
在做语法分析时首先要构造条件语句的LR(1)分析表,此表为:
ACTION GOTO if ( ) else = && || ! relop + * - i ; P S B E M N 0 S3 S4 1 2 1 acc 2 r14 r14 r14 5 3 S6 4 S7 5 r1 6 S12 S9 S11 S13 8 10 7 S12 S11 S13 14 8 S15 S16 S17 18 10 9 S12 S9 S11 S13 10 S19 S20 S21 11 S12 S11 S13 22 12 S12 S11 S13 23 13 r13 r13 r13 r13 r13 r13 r13 r13 r13 14 r4 S20 S21 r4 15 r14 r14 r14 24 16 r14 r14 r14 25 17 r14 r14 r14 26 18 r7 r7 19 S12 S11 S13 27 20 S12 S11 S13 28 21 S12 S11 S13 29 22 r11 r11 r11 r11 r11 r11 r11 r11 23 S30 S20 S1 24 S3 S4 31 25 S12 S9 S11 S13 32 10 26 S12 S9 S11 S13 33 10 27 r8 r8 r8 S20 S21 28 r9 r9 r9 r9 r9 r9 S21 29 r10 r10 r10 r10 r10 r10 r10 r10 30 r12 r12 r12 r12 r12 r12 r12 r12 31 r15 r3 34 32 r5 r5 r5 33 r6 S16 r6 34 S35 35 r14 r14 r14 36 36 S3 S3 37 37 r2 r2 此表中引用记号的意义是:
(1)sj 把下一状态j和现行输入符号a移进栈;
(2)rj 按第j个产生式进行规约;
(3)acc 接受;
(4)空白格 出错标志,报错;
在做语法分析时,需对此表进行编码并用数组table[38][20]保存,其中将错误即空白格设为100,而将接受设为110,对于状态就按表中的数字表示,而规约所需的产生式编码为从61到75分别对应15条产生式。
语法分析中的输入为词法分析的结果,即上述的三个数组,现将语法分析中所用到的数据结构和函数描述如下:
struct analyse
{ int state;
char sign;
};//该结构体即为分析栈的结构,state表示当前的状态,sign表示当前的单词的种别编码。
int tindex2(int c);

该函数接受单词的种别编码,返回该单词在tabel数组中的第二维下标。
void tiaojian();
该函数为条件语句的总控函数,识别所有的单词利用分析表,应用LR分析法对输入的单词进行移入和规约,以及错误处理。
void phraseanalyse();
该函数为语法分析函数,函数tiaojian()只能分析一条条件语句,而利用此函数调用函数tiaojian()可以实现多条条件语句的语法分析。
5.3语义分析
语义分析是和语法分析同时进行的,在进行语法分析遇到规约项目时就调用对应的语义分析函数进行语义分析并同时生成四元式。
现将语义分析中所用到的数据结构和函数描述如下:
struct list
{ int value;
struct list *next;
};//该结构体用于定义以下的栈,做回填时用。
stack stknext;//保存当前还未回填的所有链表的头指针。
stack stktrue;//保存当前还未回填的所有链表的头指针。
stack stkfalse;//保存当前还未回填的所有链表的头指针。
stack stkpos;//保存当前还未回填的M的值。
stack stktemp;//保存常量,变量以及临时变量
mymap gen[100][4];//保存生成的四元式。
使用栈结构可以保证当前进行规约的所有的信息都在栈的顶部,然后将规约后的信息再保存到栈顶,以便下一次进行规约。
void backpatch(list *list1,int t);
该函数的功能完成"回填",把list1所链接的每个四元式的第四区段都填为t.
list* mymerge(list *list1,list *list2);
该函数的功能把以list1和list2为链首的两条链合并为一,作为函数值,回送合并后的链首。
void tiaojian_1();
该函数实现产生式P->SM的语义分析及四元式的生成。
void tiaojian_2();
该函数实现产生式S->if ( B ) M1 S1 N else M2 S2的语义分析及四元式的生成。
void tiaojian_3();
该函数实现产生式S->if ( B ) M S1的语义分析及四元式的生成。
void tiaojian_4();
该函数实现产生式S->i=E的语义分析及四元式的生成。
void tiaojian_5();
该函数实现产生式B->B1 && M B2的语义分析及四元式的生成。
void tiaojian_6();
该函数实现产生式B->B1 || M B2的语义分析及四元式的生成。
void tiaojian_7();
该函数实现产生式B->!B1的语义分析及四元式的生成。
void tiaojian_8();
该函数实现产生式B->E1 relop E2的语义分析及四元式的生成。
void tiaojian_9();
该函数实现产生式E->E1+E2的语义分析及四元式的生成。
void tiaojian_10();
该函数实现产生式E->E1*E2的语义分析及四元式的生成。
void ti

aojian_11();
该函数实现产生式E->-E1的语义分析及四元式的生成。
void tiaojian_14();
该函数实现产生式M->0的语义分析及四元式的生成。
void tiaojian_15();
该函数实现产生式N->0的语义分析及四元式的生成。
int main();
该函数为该程序的总控函数,控制词法分析,语法分析以及语义分析。
语义分析的结果四元式输出保存在文件genquad.txt中。

6.给出软件的测试方法和测试结果
该程序的编写语言使用的是C++,编写、调试和测试都是使用的microsoft visual studio 6.0。在程序中用到的输入与输出都是用文件方式实现的,其中,源条件语句是在与程序代码在同一个目录下人为建立的一个"sourcefile.txt"的文本里输入的,按文法要求输入条件语句后要以";"结束,例如在输入文件中输入一下的字符串:








对于以上的输入输出的四元式为:










由以上结果可知,该程序能够实现对if-else嵌套语句的翻译。
在输入文件中输入一下的字符串:





对于以上的输入输出的四元式为:










由以上结果可知,该程序能实现多条条件语句的翻译,并且在条件语句的布尔表达式中可以由&&,||,!等逻辑运算符以及>,<>=,<=,等关系运算符。
7.设计的特点、不足、收获与体会
7.1特点与不足
在此次课程设计中,我运用C++语言实现了用LR分析方法对条件语句的翻译分析程序设计,最后输出中间代码形式为四元式。在此次课程设计中,对于词法分析这一块,由于以前在做实验时就做过,所以此次的语法分析只是在以前的基础上添加了一些关键字,没有做太多的改变,因此很快就完成了。
对于语法分析这一块,以前也做过,其基本模块没有做什么改变,主要时此次的条件语句的产生式很多,构造它的LR(1)分析表非常繁琐,因此我上图书馆查到了一个条件语句的LR(1)分析表,在进行语法分析控制时,采用了使用识别条件语句的第一个关键字的方法,并且使用分号做为每条语句的结束,因而可以同时对多条条件语句进行分析。
对于语义分析这一块,它是结合语法分析同时进行的,这是此次课程设计的最大的特点,在进行规约时我想出了用栈来保存规约出来的信息,使用栈结构可以保证当前进行规约的所有的信息都在栈的顶部,然后将规约后的信息再保存到栈顶,以便进行下一次规约。
这次课程设计的过程中不足主要是在语法分析时,当遇到语法错误时,只能输出出错信息,但是不能指出出错的位置。而做词法分

析时,如果遇到非法字符,就能够输出从出错点往后的一串字符。
7.2收获与体会
通过这次的课程设计使我在各个方面都得到了很大的提高。首先,在此次课程设计的初期,查阅了很多的书籍,进而编译原理这门课有了更深的理解,并且对以前遗漏的知识点进行了补充。并且对LR分析法有了更加深刻的理解,同时也学会了构造LR(1)分析表。
其次,在此次课程设计的中期,自己对语义分析这一块进行了全面的分析,使我掌握了语法制导翻译的方法。同时对于使用栈式存储结构来保存做语义分析时的中间结果是我此次课程设计中最值得高兴的一件事,它使我解决了所有的规约问题,并且在生成四元式时使用了回填技术,使得能够产生正确的结果。
总之,在这次课程设计后我的理论知识,动手动脑能力,有了一定的提高,经过这次课程设计,我从中学到了很多东西,这是以前在书上学不到的,同时也使我认识到同学之间的相互帮助是多么的重要。



1



相关文档
最新文档