LR0表构造讲解Word格式.docx
《LR0表构造讲解Word格式.docx》由会员分享,可在线阅读,更多相关《LR0表构造讲解Word格式.docx(17页珍藏版)》请在冰豆网上搜索。
A.xyz,Ax.yz,Axy.z,Axyz.。
为刻划分析过程中的文法的每一个产生式的右部符号已有多大一部分被识别(出现在栈顶),可以用这种标有圆点的产生式来确定。
(1)A→β.刻划产生式A→β的右部β已出现在栈顶。
(2)A→β1.β2刻划A→β1β2的右部子串β1已出现在栈顶,期待从输入串中看到β2推出的符号。
(3)A→.β刻划没有句柄的任何符号在栈顶,此时期望A→β的右部所推出的符号串。
(4)对于A→ε的LR(0)项目只有A→.。
设文法G=(VT,VN,S,P)是一个上下文无关文法,若存在一个规范推导S
Aw
12w(其中A12P),则称项目A12对活前缀=1是有效的,即LR(0)有效项目。
从直观意义上讲,一个LR(0)项目指明了在分析过程中的某一步我们看到产生式的多大部分被识别,LR(0)项目中的圆点可看成是分析栈栈顶与输入串的分界线,圆点左边为已进入分析栈的部分,右边是当前输入或继续扫描的符号串。
不同的LR(0)项目,反映了分析栈顶的不同情况。
我们根据LR(0)项目的作用不同,将其分为四类:
(1)归约项目:
表现形式:
A→a.
这类LR(0)项目表示句柄a恰好包含在栈中,即当前栈顶的部分内容构成了所期望的句柄,应按A→a进行归约。
(2)接受项目:
→a.
其中
是文法惟一的开始符号。
这类LR(0)项目实际是特殊的归约项目,表示分析栈中内容恰好为a,用
→a进行归约,则整个分析成功。
(3)移进项目:
(b
VT)
这类LR(0)项目表示分析栈中是不完全包含句柄的活前缀,为构成恰好有句柄的活前级,需将b移进分析栈。
(4)待约项目:
A→α.Bβ(B
VN)
这类LR(0)项目表示分析栈中是不完全包含句柄的活前缀,为构成恰好有句柄的活前缀,应把当前输入字符串中的相应内容先归约到B。
在给出LR(0)项目的定义和分类之后,我们从这些LR(0)项目出发,来构造能识别文法所有前缀的有限自动机。
其步骤是:
首先构造能识别文法所有活前缀的非确定的有限自动机,再将其确定化和最小化,最终得到所需的确定的有限自动机。
由文法G的LR(0)项目构造识别文法G的所有活前缀的非确定有限自动机的方法:
(1)规定含有文法开始符号的产生式(设
→A)的第一个LR(0)项目(即
→.A)为NFA的惟一初态。
(2)令所有LR(0)项目分别对应NFA的一个状态且LR(0)项目为归约项目的对应状态为终态。
(3)若状态i和状态j出自同一文法G的产生式且两个状态LR(0)项目的圆点只相差一个位置,即:
若i为X→X1X2·
…Xi-1·
Xi…Xn,j为X→X1X2…Xi·
Xi+1…Xn,则从状态i引一条标记为Xi的弧到状态j。
(4)若状态i为待约项目(设X→α·
Aβ),则从状态i引ε弧到所有A→·
r的状态。
为了使“接受”状态易于识别,我们通常将文法G进行拓广。
假定文法G是一个以S为开始符号的文法,我们构造一个
,它包含了整个G,但它引进了一个不出现在G中的非终结符
,并加进一个新产生式
→S,以
→S
为开始符号。
那么,我们称
是G的拓广文法。
这样,便会有一个仅含项目
→S的状态,这就是惟一的“接受”态。
如果I是文法G'的一个项目集,定义和构造I的闭包CLOSURE(I)如下:
(1)I的项目都在CLOSURE(I)中。
(2)若A→.B属于CLOSURE(I),则每一形如B→.的项目也属于CLOSURE(I)。
(3)重复
(2)直到CLOSURE(I)不再扩大。
定义转换函数如下:
GO(I,X)=CLOSURE(J)
其中:
I为包含某一项目集的状态,X为一文法符号,J={A→X.|A→.X∈I}。
圆点不在产生式右部最左边的项目称为核,惟一的例外是S′→.S,因此用GOTO(I,X)状态转换函数得到的J为转向后状态闭包项目集的核。
使用闭包函数(CLOSURE)和转换函数(GO(I,X))构造文法G’的LR(0)的项目集规范族,步骤如下:
(1)置项目S′→.S为初态集的核,然后对核求闭包CLOSURE({S′→.S})得到初态的闭包项目集。
(2)对初态集或其他所构造的项目集应用转换函数GO(I,X)=CLOSURE(J)求出新状态J的闭包项目集。
(3)重复
(2)直到不出现新的项目集为止。
计算LR(0)项目集规范族C={I0,I1,...In}的算法伪代码如下:
Procedureitemsets(G’);
BeginC:
={CLOSURE({S’.S})}
Repeat
ForC中每一项目集I和每一文法符号X
DoifGO(I,X)非空且不属于C
Then把GO(I,X)放入C中
UntilC不再增大
End;
一个项目集可能包含多种项目,若移进和归约项目同时存在,则称移进-归约冲突,若
归约和归约项目同时存在,则称归约-归约冲突。
下面看一个具体的例子:
我们希望能根据识别文法的活前缀的DFA建立LR分析器,因此,需要研究这个我们说项目A→β1.β2对活前缀αβ1是有效的,其条件是存在规范推导
。
一般而言,同一项目可能对几个活前缀都是有效的(当一个项目出现在几个不同的集合中时便是这种情形)。
若归约项目A→β1.对活前缀
是有效的,则它告诉我们应把符号串
归约为A,即把活前缀
变成αA。
若移进项目A→β1.β2对活前缀
是有效的,则它告诉我们,句柄尚未形成,因此,下一步动作应是移进。
但是,可能存在这样的情形,对同一活前缀,存在若干项目对它都是有效的。
而且它们告诉我们应做的事情各不相同,互相冲突。
这种冲突通过向前多看几个输入符号,或许能够获得解决。
对于每个活前缀,我们可以构造它的有效项目集。
实际上,一个活前缀γ的有效项目集正是从上述的DFA的初态出发,经读出γ后而到达的那个项目集(状态)。
换言之,在任何时候,分析栈中的活前缀X1X2…Xm的有效项目集正是栈顶状态Sm所代表的那个集合。
这是LR分析理论的一条基本定理。
实际上,栈顶的项目集(状态)体现了栈里的一切有用信息——历史。
前面我们已经对LR(0)文法进行了定义,下面我们来看一下LR(0)分析表是如何构造的。
对于LR(0)文法,我们可以直接从它的项目集规范族C和活前缀识别自动机的状态转换函数GO构造出LR分析表。
下面是构造LR(0)分析表的算法。
假定C={I0,I1,…,In},令每个项目集Ik的下标k为分析器的一个状态,因此,G'
的LR(0)分析表含有状态0,1,…,n。
令那个含有项目S'
→.S的Ik的下标k为初态。
ACTION子表和GOTO子表可按如下方法构造:
(1)若项目A→α.aβ属于Ik且GO(Ik,a)=Ij,a为终结符,则置ACTION[k,a]为“把状态j和符号a移进栈”,简记为“sj”;
(2)若项目A→α.属于Ik,那么,对任何终结符a,置ACTION[k,a]为“用产生式A→α进行规约”,简记为“rj”;
其中,假定A→α为文法G'
的第j个产生式;
(3)若项目S'
→S.属于Ik,则置ACTION[k,#]为“接受”,简记为“acc”;
(4)若GO(Ik,A)=Ij,A为非终结符,则置GOTO[k,A]=j;
(5)分析表中凡不能用上述1至4填入信息的空白格均置上“出错标志”。
按上述算法构造的含有ACTION和GOTO两部分的分析表,如果每个入口不含多重定义,则称它为文法G的一张LR(0)分析表。
具有LR(0)表的文法G称为一个LR(0)文法,LR(0)文法是无二义的。
DFA的每个项目集(状态)中的项目的不同作用。
三、实验内容
(1)建立一个项目集classProjectSet/
(2)建立stringactionTable[MAX_PRO_SET_NUM+1][MAX_VT_NUM];
//ACTION表
stringgotoTable[MAX_PRO_SET_NUM+1][MAX_VT_NUM];
//GOTO表
(3)输出分析表voidinput()
四、实验代码与结果
#include<
iostream>
string>
fstream>
usingnamespacestd;
#defineMAX_PRO_NUM50
#defineMAX_PRO_SET_NUM20
#defineMAX_P_NUM20
#defineMAX_VT_NUM27
stringvn;
//非终结符集
stringvt;
//终结符集,包括#
structProject
{//项目的数据结构
charleft;
stringinputed;
stringuninputed;
booloperator==(Projectcmp)
{
if(left==cmp.left&
&
inputed==cmp.inputed&
uninputed==cmp.uninputed)
returntrue;
else
returnfalse;
}
};
structGo
{
/*
*GO映射的数据结构
*go[i][j].input=a
*go[i][j].nextProjectSet=2
*表示第i状态的第j条箭弧受a激发到达第2状态
*/
charinput;
intnextProjectSet;
}go[MAX_PRO_SET_NUM][MAX_PRO_NUM];
intgoLength[MAX_PRO_SET_NUM]={0};
//goLength[i]是第i状态共有多少条箭弧
structPSet//产生式集合
//产生式左部
stringright;
//产生式右部
booloperator==(Projectt)//判断项目是否是由该产生式得到的
stringtemp;
if(left!
=t.left)
{
if(t.inputed=="
null"
)
temp=t.uninputed;
elseif(t.uninputed=="
temp=t.inputed;
else
temp=t.inputed+t.uninputed;
}
if(right==temp)
returnfalse;
}pSet[MAX_P_NUM];
intpSetLength=0;
//产生式个数
classProjectSet;
structDFA//识别活前缀的DFA的项目集集合
ProjectSet*state[MAX_PRO_SET_NUM];
intstateLength;
}aDFA;
classProjectSet//项目集
private:
intprojectNum;
Projectpro[MAX_PRO_NUM];
intproLength;
intinclude[MAX_P_NUM];
public:
ProjectSet(Projectin)//由一个项目构造项目集闭包
projectNum=aDFA.stateLength;
proLength=1;
for(inti=0;
i<
MAX_P_NUM;
i++)
include[i]=0;
pro[0].left=in.left;
pro[0].inputed=in.inputed;
pro[0].uninputed=in.uninputed;
for(intj=0;
j<
proLength;
j++)
{
charvn=pro[j].uninputed[0];
if(pro[j].uninputed=="
||vn>
='
a'
&
vn<
z'
continue;
for(inti=0;
i<
pSetLength;
i++)
{
if(pSet[i].left==vn&
include[i]==0)
{
include[i]=1;
pro[proLength].left=pSet[i].left;
pro[proLength].inputed="
;
pro[proLength].uninputed=pSet[i].right;
proLength++;
}
}
}
intgetProjectNum()
returnprojectNum;
intgetProLength()
returnproLength;
ProjectgetPro(inti)
returnpro[i];
stringactionTable[MAX_PRO_SET_NUM+1][MAX_VT_NUM];
voidinput();
intgetActionIndex(chart);
intgetGotoIndex(chart);
stringintToString(intt);
voiddisplay()//显示LR(0)分析表
cout<
<
"
\t\t\tACTION\t\t\t\tGOTO"
endl;
for(inti=0;
aDFA.stateLength+1;
if(i>
0)
cout<
i-1<
\t"
for(unsignedj=0;
j<
vt.length();
j++)
actionTable[i][j]<
'
\t'
for(j=0;
vn.length();
gotoTable[i][j]<
cout<
}
intmain()
input();
MAX_PRO_SET_NUM;
aDFA.state[i]=NULL;
aDFA.stateLength=0;
Projectstart;
start.left=pSet[0].left;
start.inputed="
start.uninputed=pSet[0].right;
aDFA.state[0]=newProjectSet(start);
aDFA.stateLength++;
intcurrState=0;
//当前分析的项目集
do{
for(inti=0;
aDFA.state[currState]->
getProLength();
stringtemp=aDFA.state[currState]->
getPro(i).uninputed;
if(temp=="
)//归约项目
continue;
go[currState][goLength[currState]].input=temp[0];
//当前状态currState的第goLength[currState]条箭弧受uninputed[0]激发
go[currState][goLength[currState]].nextProjectSet=aDFA.stateLength;
//默认到达一个新建的状态
ProjectaProject,t=aDFA.state[currState]->
getPro(i);
aProject.left=t.left;
if(t.inputed=="
aProject.inputed=t.uninputed[0];
else
aProject.inputed=t.inputed+t.uninputed[0];
if(t.uninputed.length()==1)
aProject.uninputed="
aProject.uninputed=t.uninputed.substr(1,t.uninputed.length());
boolflag=false;
if(aProject==aDFA.state[currState]->
getPro(0))//状态到达自身置一条到达自身的箭弧
go[currState][goLength[currState]].nextProjectSet=aDFA.state[currState]->
getProjectNum();
goLength[currState]++;
for(intiter=0;
iter<
aDFA.stateLength;
iter++)
if(aProject==aDFA.state[iter]->
getPro(0))//与其他某一状态吻合置一跳到达该状态的箭弧
{
go[currState][goLength[currState]].nextProjectSet=iter;
goLength[currState]++;
flag=true;
break;
}
if(flag==false)//均不满足时新建一个状态
go[currState][goLength[currState]].nextProjectSet=aDFA.stateLength;
aDFA.state[aDFA.stateLength]=newProjectSet(aProject);
aDFA.stateLength++;
}
currState++;
}while(currState!
=aDFA.stateLength);
//当前分析的项目集是项目集集合的最后一个项目集时退出循环
*以上,识别活前缀的DFA构造完毕
*以下,构造LR(0)分析表
//ACTION表和GOTO表初始化
for(i=0;
actionTable[0][i]=vt[i];
gotoTable[0][i]=vn[i];
//以状态号顺序填表
Projectt=aDFA.state[i]->
getPro(0);
if(t.uninputed=="
)//是归约项目
if(t.left=='
Z'
t.inputed[0]==vn[0]&
t.inputed.length()