1、一.需求分析1问题的提出:语法分析是编译过程的核心部分。他的任务是在词法分析识别单词符号串的基础上,分析并判断程序的的语法结构是否符合语法规则。语言的语法结构是用上下文无关文法描述的。因此语法分析器的工作的本质上就是按文法的产生式,识别输入符号串是否为一个句子。对于一个文法,当给你一串符号是,如何知道它是不是该文法的一个句子,这是这个课程设计所要解决的一个问题。2问题解决:其实要知道一串符号是不是该文法的一个句子,只要判断是否能从文法的开始符号出发推导出这个输入串。语法分析可以分为两类,一类是自上而下的分析法,一类是自下而上的分析法。自上而下的主旨是,对任何输入串,试图用一切可能的办法,从文法
2、开始符号出发,自上而下的为输入串建立一棵语法树。或者说,为输入串寻找一个最左推倒,这种分析过程的本质是一种试探过程,是反复使用不同产生式谋求匹配输入串的过程我主要是自上而下的过程。3解决步骤:在自上而下的分析法中,主要是研究 LL(1)分析法。它的解决步骤是首先接收到用户输入的一个文法,对文法进行检测和处理,消除左递归,得到 LL(1)文法,这个文法应该满足:无二义性,无左递归,无左公因子。当文法满足条件后,再分别构造文法每个非终结符的 FIRST 和 FOLLOW 集合,然后根据 FIRST 和 FOLLOW 集合构造 LL(1)分析表,最后利用分析表,根据 LL(1)语法分析构造一个分析器
3、。LL(1 )的语法分析程序包含了三个部分,总控程序,预测分析表函数,先进先出的语法分析栈。二.概要设计1设计原理:所谓 LL(1 )分析法,就是指从左到右扫描输入串(源程序),同时采用最左推导,且对每次直接推导只需向前看一个输入符号,便可确定当前所应当选择的规则。实现 LL(1)分析的程序又称为LL(1 )分析程序或 LL1(1 )分析器。我们知道一个文法要能进行 LL(1)分析,那么这个文法应该满足:无二义性,无左递归,无左公因子。当文法满足条件后,再分别构造文法每个非终结符的 FIRST 和 FOLLOW 集合,然后根据 FIRST和 FOLLOW 集合构造 LL(1)分析表,最后利用分
4、析表,根据 LL(1)语法分析构造一个分析器。LL(1)的语法分析程序包含了三个部分,总控程序,预测分析表函数,先进先出的语法分析栈,本程序也是采用了同样的方法进行语法分析,该程序是采用了 C+语言来编写,其逻辑结构图如下:LL(1 )预测分析程序的总控程序在任何时候都是按 STACK 栈顶符号 X 和当前的输入符号 a 做哪种过程的。对于任何(X,a ),总控程序每次都执行下述三种可能的动作之一:()若 X = a =#,则宣布分析成功,停止分析过程。()若 X = a #,则把 X 从 STACK 栈顶弹出,让 a 指向下一个输入符号。()若 X 是一个非终结符,则查看预测分析表 M。若
5、MA,a中存放着关于 X 的一个产生式,那么,首先把 X 弹出 STACK 栈顶,然后,把产生式的右部符号串按反序一一弹出 STACK 栈(若右部符号为 ,则不推什么东西进 STACK 栈)。若 MA,a中存放着“出错标志” ,则调用出错诊断程序 ERROR。事实上,LL(1 )的分析是根据文法构造的,它反映了相应文法所定义的语言的固定特征,因此在LL(1 )分析器中,实际上是以 LL(1 )分析表代替相应方法来进行分析的。2构造 LL(1)分析表考查文法 GE:EE+T | TTT*F | FF( E ) | i | x | y我们容易看出此文法没有左公因子也没有二义性,但却存在两个直接左递
6、归,这里我们利用引入新非终结符的方法来消除它使方法满足要求,即:对形如:UUx|y 的产生式(其中 x,y V+ ,y 不以 U 开头),引入一个新的非终结符 U后,可以等价地改写成为:UyUUx U|显然改写后,U 和 U都不是左递归的非终结符。因此文法 GE按上述方法消去左递归后可等价地写成:ETPP+TP | TFQQ*FQ | F( E ) | i | x | y在构造 LL(1)预测分析表之前,首先要构造该文法的每个非终结符的 FIRST 和 FOLLOW 集合,按照下面描述的算法来构造这两个集合。FIRST 集合的构造算法:(1)若 XVT,则 FIRST(X )=X。(2)若 X
7、VN,且有产生式 Xa,则把 a 加入到 FIRST(X)中;若 X 也是一条产生式,则把 也加到 FIRST(X)中。(3)若 XY是一个产生式且 YVN,则把 FIRST(Y)中的所有非 -元素都加到 FIRST(X)中;若 XY1Y2Yk 是一个产生式,Y1 ,Yi-1 都是非终结符,而且,对于任何 j,1ji-1,FIRST(Yj)都含有 (即 Y1Yi-1* ),则把 FIRST(Yj)中的所有非 -元素都加到 FIRST(X)中;特别是,若所有的FIRST(Yj)均含有 ,j=1,2,,k,则把 加到 FIRST(X)中。连续使用上面的规则,直至每个集合 FIRST 不再增大为止。
8、FOLLOW 集合的构造算法:(1)对于文法的开始符号 S,置#于 FOLLOW(S)中;(2)若 AB 是一个产生式,则把 FIRST()| 加至 FOLLOW(B)中;(3)若 AB 是一个产生式,或 AB 是一个产生式而 (即 FIRST()),则把FOLLOW(A)加至 FOLLOW(B)中。连续使用上面的规则,直至每个集合 FOLLOW 不再增大为止。根据以上描述的算法,可以构造文法 GE的 FIRST 和 FOLLOW 集合如下:FIRST(E) = ( , i,x,y FOLLOW(E) = ) , # FIRST(P) = + , FOLLOW(P) = ) , # FIRST
9、(T) = ( , i,x,y FOLLOW(T) = + , ) , # FIRST(Q) = * , FOLLOW(Q) = + , ) , # FIRST(F) = ( , i,x,y FOLLOW(F) = * , + , ) , # 现在来构造 GE的 LL(1 )预测分析表。预测分析表 MA, a是如下形式的一个矩阵。A 为非终结符,a 是终结符或 #。矩阵元素 MA, a中存放这一条关于 A 的产生式,指出当 A 面临输入符号 a是所应采用的规则。MA, a也可能存放一条“出错标志” ,指出当 A 根本不该面临输入符号 a。文法GE 的 LL(1) 预测分析表如下: i + x
10、y * ( ) #E ETPERRORETPETPERRORETPERROR ERRORP ERRORE+TPERRORERRORERRORERRORP PT TFQERRORTFQTFQERRORTFQERROR ERRORQ ERRORQ ERRORERRORQ*FQERRORQ QF Fi ERRORFx Fy ERRORF(E)ERROR ERROR其中,E、P 、T、Q、F 为方法 GE的非终结符,i、+、x、y、*、(、) ,为方法 GE 的终结符,值得注意的是,“”不管有没有 产生式,我们在构造分析表时都不能省去。3.利用分析表进行预测分析的步骤对于这个文法,假设输入串为 i*i
11、+i,利用分析表进行预测分析的步骤为:步骤 符号栈 输入串 所用产生式0 #E i*i+i#1 #PT i*i+i# ETP2 #PQF i*i+i# TFQ3 #PQi i*i+i# Fi4 #PQ *i+i#5 #PQF* *i+i# Q*FQ6 #PQF i+i#7 #PQi i+i# Fi8 #PQ +i#9 #P +i# Q10 #PT+ +i# P+TP11 #PT i#12 #PQF i# TFQ13 #PQi i# Fi14 #PQ #15 #P # Q16 # # P三. 详细设计1程序流程图:在对程序各个模块分析之前。先给出整个程序的流程图。以便于在分析过程中更好的对各个模
12、块之间的联系进行了解。程序的流程图如下:2消除左递归1)消除左递归时主要经历以下步骤:a)对文法按推导字母顺序的顺序排列,且将开始符置于数组最前部,这里采用冒泡算法。b)查看文法是否含有左递归,如果没有,则终止。c)准备两个字符串数组:tweenStrArr 和 tmpGrammerArr,tweenStrArr 用以存放每一个非终止符作为左侧推导项时临时分析结果,tmpGrammerArr 则用以存放去除左递归后的文法。接下来即可进行消除左递归的过程,核心算法框架如下:FOR i: = 1 To n DoBEGINFOR j:=1 To i1 Do把形如 PiPj 的规则改写成: Pid1
13、|d2 |dk ,其中Pjd1|d2|dk 是关于 Pi 的所有规则; 消除关于 Pi 规则的直接左递归性END2)消除左递归关键代码void recur(char *point) /*完整的产生式在 point中*/int j,m=0,n=3,k;char temp20,ch;ch=c(); /*得到一个非终结符*/k=strlen(non_ter);non_terk=ch;non_terk+1=0;for(j=0;j pt-rCursor)/*注:此处因编程出错,使空产生式时rlength 同样是 1,故此处同样可处理空产生式*/AddFirst(U, pt-rCursor);break;
14、elseif(NULL = firstpt-rCursor - 100)First(pt-rCursor); AddFirst(U, pt-rCursor);if(!HaveEmpty(pt-rCursor)break;elsept = pt-next;j+;if(j = Pi.rLength) /*当产生式右部都能推出空时*/AddFirst(U, -1);/*加入 first 集*/void AddFirst(int U, int nCh) /*当数值小于 100 时 nCh 为 Vt*/*当处理非终结符时,AddFirst 不添加空项(-1)*/struct collectNode *p
15、t, *qt;int ch; /*用于处理 Vn*/pt = NULL;qt = NULL;if(nCh nVt = nCh)break;elseqt = pt;pt = pt-next;if(NULL = pt)pt = (struct collectNode *)malloc(sizeof(struct collectNode);pt-nVt = nCh;pt-next = NULL;if(NULL = firstU - 100)firstU - 100 = pt;elseqt-next = pt; /*qt 指向 first 集的最后一个元素*/pt = pt-next;elsept =
16、 firstnCh - 100;while(NULL != pt)ch = pt-nVt;if(-1 != ch)AddFirst(U, ch);pt = pt-next;/*判断 first 集中是否有空(-1)*/bool HaveEmpty(int nVn) if(nVn nVt)return true;pt = pt-next;return false;/*计算 follow 集,例:U-xVy,U-xV.(注:初始符必含# “-1“)*/void Follow(int V)int i;struct pRNode *pt ;if(100 = V) /*当为初始符时*/AddFollow
17、(V, -1, 0 );for(i = 0; i rCursor != V) /*注此不能处理:U-xVyVz 的情况*/pt = pt-next;if(NULL != pt)pt = pt-next; /*V 右侧的符号*/if(NULL = pt) /*当 V 后为空时 V-xV,将左符的 follow 集并入 V 的 follow 集中*/if(NULL = followPi.lCursor - 100 AddFollow(V, Pi.lCursor, 0);else /*不为空时 V-xVy,(注意:y-),调用 AddFollow 加入 Vt 或 y 的 first 集*/while
18、(NULL != pt /*y 的前缀中有空时,加如 first 集*/pt = pt-next;if(NULL = pt) /*当后面的字符可以推出空时*/if(NULL = followPi.lCursor - 100 AddFollow(V, Pi.lCursor, 0);else /*发现不为空的字符时*/AddFollow(V, pt-rCursor, 1); /*当数值小于 100 时 nCh 为 Vt*/*#用-1 表示 ,kind 用于区分是并入符号的 first 集,还是 follow 集kind = 0 表加入 follow 集,kind = 1 加入 first 集*/v
19、oid AddFollow(int V, int nCh, int kind)struct collectNode *pt, *qt;int ch; /*用于处理 Vn*/pt = NULL;qt = NULL;if(nCh nVt = nCh)break;elseqt = pt;pt = pt-next;if(NULL = pt)pt = (struct collectNode *)malloc(sizeof(struct collectNode);pt-nVt = nCh;pt-next = NULL;if(NULL = followV - 100)followV - 100 = pt;e
20、lseqt-next = pt; /*qt 指向 follow 集的最后一个元素*/pt = pt-next;else /*为非终结符时,要区分是加 first 还是 follow*/if(0 = kind)pt = follownCh - 100;while(NULL != pt)ch = pt-nVt;AddFollow(V, ch, 0);pt = pt-next;elsept = firstnCh - 100;while(NULL != pt)ch = pt-nVt;if(-1 != ch)AddFollow(V, ch, 1);pt = pt-next;2)构造预测分析表 的程序代码
21、如下:void CreateAT()int i;struct pRNode *pt;struct collectNode *ct;for(i = 0; i rCursor) /*处理非终结符,当为终结符时,定含空为假跳出*/ct = firstpt-rCursor - 100;while(NULL != ct)if(-1 != ct-nVt)analyseTablePi.lCursor - 100ct-nVt = i;ct = ct-next;pt = pt-next;if(NULL = pt)/*NULL = pt,说明 xyz-,用到 follow 中的符号*/ct = followPi.
22、lCursor - 100;while(NULL != ct)if(-1 != ct-nVt)analyseTablePi.lCursor - 100ct-nVt = i;else /*当含有#号时*/analyseTablePi.lCursor - 100vtNum = i;ct = ct-next;elseif(100 rCursor) /*不含空的非终结符*/ct = firstpt-rCursor - 100;while(NULL != ct)analyseTablePi.lCursor - 100ct-nVt = i;ct = ct-next;else /*终结符或者空*/if(-1
23、 = pt-rCursor) /*-1 为空产生式时*/ct = followPi.lCursor - 100;while(NULL != ct)if(-1 != ct-nVt)analyseTablePi.lCursor - 100ct-nVt = i;else /*当含有#号时*/analyseTablePi.lCursor - 100vtNum = i;ct = ct-next;else /*为终结符*/analyseTablePi.lCursor - 100pt-rCursor = i;4利用分析表进行预测分析1)总程序的算法描述如下:BEGIN首先把# 然后把文法开始符号推进 STA
24、CK 栈;把第一个输入符号读进 a;FLAG:=TRUE;WHILE FLAG DOBEGIN把栈顶符号出栈到 X 中; IF XVT THENIF X =a THEN 把下一输入符号读进 aELSE ERRORELSE IF X= # THEN IF X=a THEN FLAG:=FALSEELSE ERRORELSE IF MA, a=Xx1x2xk THEN把 xk, xk1, x1 依次进栈 /*若 x1, x2 xk=e,则不进栈*/ELSE ERROREND OF WHILE;STOP /*分析成功,过程结束*/END2)代码实现如下:void Identify(char *st)
25、int current,step,r; /*r 表使用的产生式的序号*/printf(“n%s 的分析过程:n“, st);printf(“步骤t 分析符号栈 t 当前指示字符t 使用产生式序号n“);step = 0;current = 0; /*符号串指示器*/printf(“%dt“,step);ShowStack();printf(“tt%ctt- -n“, stcurrent);while(# != stcurrent)if(100 analyseStacktopAnalyse) /*当为终结符时*/if(analyseStacktopAnalyse = IndexCh(stcurr
26、ent)/*匹配出栈,指示器后移*/Pop();current+;step+;printf(“%dt“, step);ShowStack();printf(“tt%ctt 出栈、后移n“, stcurrent);elseprintf(“%c-%c 不匹配!“, analyseStacktopAnalyse, stcurrent);printf(“此串不是此文法的句子!n“);return;else /*当为非终结符时*/r = analyseTableanalyseStacktopAnalyse - 100IndexCh(stcurrent);if(-1 != r)Push(r); /*产生式
27、右部代替左部,指示器不移动*/step+;printf(“%dt“, step);ShowStack();printf(“tt%ctt%dn“, stcurrent, r);elseprintf(“无可用产生式,此串不是此文法的句子!n“);return;if(# = stcurrent)if(0 = topAnalyse printf(“%dt“, step);ShowStack();printf(“tt%ctt 分析成功!n“, stcurrent);printf(“%s 是给定文法的句子!n“, st);elsewhile(topAnalyse 0)if(100 analyseStack
28、topAnalyse) /*当为终结符时*/printf(“无可用产生式,此串不是此文法的句子!n“);return;elser = analyseTableanalyseStacktopAnalyse - 100vtNum;if(-1 != r)Push(r); /*产生式右部代替左部,指示器不移动*/step+;printf(“%dt“, step);ShowStack();if(0 = topAnalyse printf(“%s 是给定文法的句子!n“, st);else printf(“tt%ctt%dn“, stcurrent, r);elseprintf(“无可用产生式,此串不是此
29、文法的句子!n“);return;四.运行结果以上两个试验从较大呈度上说明了程序运行的正确性及稳定性,当然,对程序本身还有待进行更进一步的严格测试。五.总结分析一.需求分析1问题的提出:语法分析是编译过程的核心部分。他的任务是在词法分析识别单词符号串的基础上,分析并判断程序的的语法结构是否符合语法规则。语言的语法结构是用上下文无关文法描述的。因此语法分析器的工作的本质上就是按文法的产生式,识别输入符号串是否为一个句子。对于一个文法,当给你一串符号是,如何知道它是不是该文法的一个句子,这是这个课程设计所要解决的一个问题。2问题解决:其实要知道一串符号是不是该文法的一个句子,只要判断是否能从文法的
30、开始符号出发推导出这个输入串。语法分析可以分为两类,一类是自上而下的分析法,一类是自下而上的分析法。自上而下的主旨是,对任何输入串,试图用一切可能的办法,从文法开始符号出发,自上而下的为输入串建立一棵语法树。或者说,为输入串寻找一个最左推倒,这种分析过程的本质是一种试探过程,是反复使用不同产生式谋求匹配输入串的过程我主要是自上而下的过程。3解决步骤:在自上而下的分析法中,主要是研究 LL(1)分析法。它的解决步骤是首先接收到用户输入的一个文法,对文法进行检测和处理,消除左递归,得到 LL(1)文法,这个文法应该满足:无二义性,无左递归,无左公因子。当文法满足条件后,再分别构造文法每个非终结符的
31、 FIRST 和 FOLLOW 集合,然后根据 FIRST 和 FOLLOW 集合构造 LL(1)分析表,最后利用分析表,根据 LL(1)语法分析构造一个分析器。LL(1 )的语法分析程序包含了三个部分,总控程序,预测分析表函数,先进先出的语法分析栈。二.概要设计1设计原理:所谓 LL(1 )分析法,就是指从左到右扫描输入串(源程序),同时采用最左推导,且对每次直接推导只需向前看一个输入符号,便可确定当前所应当选择的规则。实现 LL(1)分析的程序又称为LL(1 )分析程序或 LL1(1 )分析器。我们知道一个文法要能进行 LL(1)分析,那么这个文法应该满足:无二义性,无左递归,无左公因子。
32、当文法满足条件后,再分别构造文法每个非终结符的 FIRST 和 FOLLOW 集合,然后根据 FIRST和 FOLLOW 集合构造 LL(1)分析表,最后利用分析表,根据 LL(1)语法分析构造一个分析器。LL(1)的语法分析程序包含了三个部分,总控程序,预测分析表函数,先进先出的语法分析栈,本程序也是采用了同样的方法进行语法分析,该程序是采用了 C+语言来编写,其逻辑结构图如下:LL(1 )预测分析程序的总控程序在任何时候都是按 STACK 栈顶符号 X 和当前的输入符号 a 做哪种过程的。对于任何(X,a ),总控程序每次都执行下述三种可能的动作之一:()若 X = a =#,则宣布分析成
33、功,停止分析过程。()若 X = a #,则把 X 从 STACK 栈顶弹出,让 a 指向下一个输入符号。()若 X 是一个非终结符,则查看预测分析表 M。若 MA,a中存放着关于 X 的一个产生式,那么,首先把 X 弹出 STACK 栈顶,然后,把产生式的右部符号串按反序一一弹出 STACK 栈(若右部符号为 ,则不推什么东西进 STACK 栈)。若 MA,a中存放着“出错标志” ,则调用出错诊断程序 ERROR。事实上,LL(1 )的分析是根据文法构造的,它反映了相应文法所定义的语言的固定特征,因此在LL(1 )分析器中,实际上是以 LL(1 )分析表代替相应方法来进行分析的。2构造 LL
34、(1)分析表考查文法 GE:EE+T | TTT*F | FF( E ) | i | x | y我们容易看出此文法没有左公因子也没有二义性,但却存在两个直接左递归,这里我们利用引入新非终结符的方法来消除它使方法满足要求,即:对形如:UUx|y 的产生式(其中 x,y V+ ,y 不以 U 开头),引入一个新的非终结符 U后,可以等价地改写成为:UyUUx U|显然改写后,U 和 U都不是左递归的非终结符。因此文法 GE按上述方法消去左递归后可等价地写成:ETPP+TP | TFQQ*FQ | F( E ) | i | x | y在构造 LL(1)预测分析表之前,首先要构造该文法的每个非终结符的
35、 FIRST 和 FOLLOW 集合,按照下面描述的算法来构造这两个集合。FIRST 集合的构造算法:(1)若 XVT,则 FIRST(X )=X。(2)若 XVN,且有产生式 Xa,则把 a 加入到 FIRST(X)中;若 X 也是一条产生式,则把 也加到 FIRST(X)中。(3)若 XY是一个产生式且 YVN,则把 FIRST(Y)中的所有非 -元素都加到 FIRST(X)中;若 XY1Y2Yk 是一个产生式,Y1 ,Yi-1 都是非终结符,而且,对于任何 j,1ji-1,FIRST(Yj)都含有 (即 Y1Yi-1* ),则把 FIRST(Yj)中的所有非 -元素都加到 FIRST(X
36、)中;特别是,若所有的FIRST(Yj)均含有 ,j=1,2,,k,则把 加到 FIRST(X)中。连续使用上面的规则,直至每个集合 FIRST 不再增大为止。FOLLOW 集合的构造算法:(1)对于文法的开始符号 S,置#于 FOLLOW(S)中;(2)若 AB 是一个产生式,则把 FIRST()| 加至 FOLLOW(B)中;(3)若 AB 是一个产生式,或 AB 是一个产生式而 (即 FIRST()),则把FOLLOW(A)加至 FOLLOW(B)中。连续使用上面的规则,直至每个集合 FOLLOW 不再增大为止。根据以上描述的算法,可以构造文法 GE的 FIRST 和 FOLLOW 集合
37、如下:FIRST(E) = ( , i,x,y FOLLOW(E) = ) , # FIRST(P) = + , FOLLOW(P) = ) , # FIRST(T) = ( , i,x,y FOLLOW(T) = + , ) , # FIRST(Q) = * , FOLLOW(Q) = + , ) , # FIRST(F) = ( , i,x,y FOLLOW(F) = * , + , ) , # 现在来构造 GE的 LL(1 )预测分析表。预测分析表 MA, a是如下形式的一个矩阵。A 为非终结符,a 是终结符或 #。矩阵元素 MA, a中存放这一条关于 A 的产生式,指出当 A 面临输入
38、符号 a是所应采用的规则。MA, a也可能存放一条“出错标志” ,指出当 A 根本不该面临输入符号 a。文法GE 的 LL(1) 预测分析表如下: i + x y * ( ) #E ETPERRORETPETPERRORETPERROR ERRORP ERRORE+TPERRORERRORERRORERRORP PT TFQERRORTFQTFQERRORTFQERROR ERRORQ ERRORQ ERRORERRORQ*FQERRORQ QF Fi ERRORFx Fy ERRORF(E)ERROR ERROR其中,E、P 、T、Q、F 为方法 GE的非终结符,i、+、x、y、*、(、)
39、,为方法 GE 的终结符,值得注意的是,“”不管有没有 产生式,我们在构造分析表时都不能省去。3.利用分析表进行预测分析的步骤对于这个文法,假设输入串为 i*i+i,利用分析表进行预测分析的步骤为:步骤 符号栈 输入串 所用产生式0 #E i*i+i#1 #PT i*i+i# ETP2 #PQF i*i+i# TFQ3 #PQi i*i+i# Fi4 #PQ *i+i#5 #PQF* *i+i# Q*FQ6 #PQF i+i#7 #PQi i+i# Fi8 #PQ +i#9 #P +i# Q10 #PT+ +i# P+TP11 #PT i#12 #PQF i# TFQ13 #PQi i# Fi
40、14 #PQ #15 #P # Q16 # # P三. 详细设计1程序流程图:在对程序各个模块分析之前。先给出整个程序的流程图。以便于在分析过程中更好的对各个模块之间的联系进行了解。程序的流程图如下:2消除左递归1)消除左递归时主要经历以下步骤:a)对文法按推导字母顺序的顺序排列,且将开始符置于数组最前部,这里采用冒泡算法。b)查看文法是否含有左递归,如果没有,则终止。c)准备两个字符串数组:tweenStrArr 和 tmpGrammerArr,tweenStrArr 用以存放每一个非终止符作为左侧推导项时临时分析结果,tmpGrammerArr 则用以存放去除左递归后的文法。接下来即可进行
41、消除左递归的过程,核心算法框架如下:FOR i: = 1 To n DoBEGINFOR j:=1 To i1 Do把形如 PiPj 的规则改写成: Pid1 |d2 |dk ,其中Pjd1|d2|dk 是关于 Pi 的所有规则; 消除关于 Pi 规则的直接左递归性END2)消除左递归关键代码void recur(char *point) /*完整的产生式在 point中*/int j,m=0,n=3,k;char temp20,ch;ch=c(); /*得到一个非终结符*/k=strlen(non_ter);non_terk=ch;non_terk+1=0;for(j=0;j pt-rCur
42、sor)/*注:此处因编程出错,使空产生式时rlength 同样是 1,故此处同样可处理空产生式*/AddFirst(U, pt-rCursor);break;elseif(NULL = firstpt-rCursor - 100)First(pt-rCursor); AddFirst(U, pt-rCursor);if(!HaveEmpty(pt-rCursor)break;elsept = pt-next;j+;if(j = Pi.rLength) /*当产生式右部都能推出空时*/AddFirst(U, -1);/*加入 first 集*/void AddFirst(int U, int
43、nCh) /*当数值小于 100 时 nCh 为 Vt*/*当处理非终结符时,AddFirst 不添加空项(-1)*/struct collectNode *pt, *qt;int ch; /*用于处理 Vn*/pt = NULL;qt = NULL;if(nCh nVt = nCh)break;elseqt = pt;pt = pt-next;if(NULL = pt)pt = (struct collectNode *)malloc(sizeof(struct collectNode);pt-nVt = nCh;pt-next = NULL;if(NULL = firstU - 100)firstU - 100 = pt;elseqt-next = pt; /*qt 指向 first 集的最后一个元素*/