您的位置:首页 > 其它

编译原理实验——LR(0)分析法

2010-12-13 12:49 453 查看
/*本程序从文件读入文法,可以根据文法自动生成规范LR(0)项目族,并根据DFA自动构造ACTION和GOTO表,希望各位大虾批评指正*/

/*所用文法

SA
SB
AaAb
Ac
BaBb
Bd

*/

#include<iostream>
#include<stdio.h>
#include<fstream>
#include<malloc.h>
using namespace std;

#define OK 1
#define ERROR 0
#define N 50
#define Y 20

int vtnum,vnnum,pronum;//依次是终结符个数,非终结符个数,产生式个数
char vt
;
char vn
;//终结符和非终结符集
char old

={'/0'};//用于存储文法
char oldz

={'/0'};//用于存储增广文法
int ACTION

={0};
int GOTO

={0};

typedef struct SqE{
int t;//状态编号
char c1;
}SqE;//堆栈元素

typedef struct item{
int f;//项目前部,表示产生式编号
int l;//项目后部,表示停顿点在产生式的位置
}item;//定义项目

typedef struct link{
int f;//连接前部,表示所用符号的编号,非终结符编号=在vn[]中的下标+100
int l;//连接后部,即状态编号
}link;//定义状态之间的连接

typedef struct cd{
int item_num;//状态中的项目数
int link_num;//状态的连接数
item w
;//项目集
link u
;//连接集
}cd;//定义状态

typedef struct DFA{
int cd_num;//状态个数
cd s[N+1];//状态集
}DFA;//定义规范LR(0)项目族,D.s
用作状态转换函数go_switch()的存储空间

DFA D;

void dfa();
void closure(int);
void go_switch(int,int);
int test_go_switch();
void add_go_switch();
void del_go_switch();
void action();
void go_answer();
int control();
int length(int);
int test(char);
void printf_ag();
bool test_link(int i,int num);

void main()
{
int i,j;

ifstream in("input1.txt",ios_base::in);//读文件,从文件中读入pronum,vtnum,vnnum以及产生式
in>>pronum>>vnnum>>vtnum;
in>>vn;
in>>vt;
for(i=1;i<=pronum;i++)
in>>old[i];//将产生式存入old[][],old[1]为第一个产生式
for(i=1;i<=pronum;i++)
for(j=0;old[i][j]!='/0';j++)
oldz[i][j]=old[i][j];//将产生式从old[][]录入oldz[][]
oldz[0][0]='P';
oldz[0][1]=old[1][0];//加入P->S,将原文法扩充,使其变为增广文法
vt[vtnum]='$';//把结束符'$'加入终结符集
D.cd_num=0;
for(i=0;i<=N;i++)
{
D.s[i].item_num=0;
D.s[i].link_num=0;
}//初始化状态个数、连接个数、项目个数

dfa();
action();
go_answer();
printf_ag();
control();
}

//求一个状态的闭包
void closure(int i)
{
int j,k,m,x,flag;

do
{
j=D.s[i].item_num;//j是本轮循环开始前的项目数
for(k=0;k<D.s[i].item_num;k++)
{
for(m=0;m<=pronum;m++)
{
if(oldz[m][0]==oldz[D.s[i].w[k].f][D.s[i].w[k].l])//对当前状态i中的每个项目,查询每个产生式,例如:A->a.Ab应找到A->...
{
flag=0;//判断该项是否在当前状态i中,即检查(m,1)是否存在于状态i中,保证求闭包时加入的新项目和原项目集不重合

for(x=0;x<D.s[i].item_num;x++)
{
if(D.s[i].w[x].f==m&&D.s[i].w[x].l==1)
{
flag=1;
break;
}
}

if(flag==0)//如果该项不在当前状态i中,将其加入状态i
{
D.s[i].w[D.s[i].item_num].f=m;
D.s[i].w[D.s[i].item_num].l=1;
D.s[i].item_num++;
}
}
}

}
}while(j!=D.s[i].item_num);//当一轮没有新的项目加入i时,结束循环

}

//状态转换函数,i是状态编号,num是符号编号,状态转换函数的结果存于s

void go_switch(int i,int num)
{
int j;

for(j=0;j<D.s[i].item_num;j++)
{
if(test(oldz[D.s[i].w[j].f][D.s[i].w[j].l])==num)
{
D.s
.w[D.s
.item_num].f=D.s[i].w[j].f;
D.s
.w[D.s
.item_num].l=D.s[i].w[j].l+1;
D.s
.item_num++;
closure(N);
}
}

}

//返回终结符和非终结符的标号,判断符号的类型,0<=终结符返回值<100,100<=非终结符返回值<200,错误符号返回值=200
int test(char c)
{
int i,j;
for(i=0;i<vtnum;i++)
{
if(vt[i]==c)
break;
}

if(i<vtnum)
return i;

else
{
for(j=0;j<vnnum;j++)
{
if(vn[j]==c)
break;
}

if(j<vtnum)
return (100+j);

else
return 200;
}
}

//检验状态转换函数的结果
int test_go_switch()
{
int i,j,k,flag;

if(D.s
.item_num==0)//判断状态转换的结果是否为空,即当前状态可否接收该字符
return 0;

else
{
for(i=0;i<D.cd_num;i++)//选定一状态,对s
中的每个项目进行循环,如果存在一个项目在当前状态中未找到,即flag=0,立即跳至下一状态
{
flag=1;//判断状态转换函数的结果是否已经完全包含于某一现有状态中,例如go_switch(状态4,符号a)依然存在于状态4中,go_switch(状态4,符号c)已经存在于状态5中
for(j=0;j<D.s
.item_num;j++)//如果在当前状态i中找不到s
的当前项目j,就跳至下一状态
{
for(k=0;k<D.s[i].item_num;k++)
{
if(D.s[i].w[k].f==D.s
.w[j].f&&D.s[i].w[k].l==D.s
.w[j].l)
break;
}

if(k>=D.s[i].item_num)
{
flag=0;
break;
}
}

if(flag==1)
return 1000+i;
}

return 1;//状态转换函数的结果未被任何现有状态完全包含,完全满足建立新状态的条件
}
}

//把状态转换函数的结果加入DFA,即当建立新状态的条件符合时,建立新的状态s[D.cd_num]
void add_go_switch()
{
int i;
for(i=0;i<D.s
.item_num;i++)
{
D.s[D.cd_num].w[D.s[D.cd_num].item_num].f=D.s
.w[i].f;
D.s[D.cd_num].w[D.s[D.cd_num].item_num].l=D.s
.w[i].l;
D.s[D.cd_num].item_num++;
}
D.cd_num++;
}

//清空状态转换函数的存储空间
void del_go_switch()
{
D.s
.item_num=0;
}

//构造规范LR(0)项目族
void dfa()
{
int i,j,k;
D.s[0].w[0].f=0;
D.s[0].w[0].l=1;
D.cd_num++;
D.s[0].item_num++;
closure(0);//把P->S加入初状态,并求其闭包

do
{
i=D.cd_num;//本轮循环开始时状态数
for(j=0;j<D.cd_num;j++)//对每个状态进行循环
{
for(k=0;k<vnnum;k++)//对当前状态,每个非终结符
{
if(!test_link(j,k+100))//检验当前状态用当前非终结符构造的连接是否已经存在,若存在,则跳至下一非终结符,这样可以防止do_while()二次执行时,将连接集重新加入当前状态,比如s[0]
{
go_switch(j,k+100);//对当前状态,当前符号调用状态转换

if(test_go_switch()==1)//如果符合建立新状态的条件
{
add_go_switch();
D.s[j].u[D.s[j].link_num].f=k+100;//建立当前状态和新状态的连接
D.s[j].u[D.s[j].link_num].l=D.cd_num-1;
D.s[j].link_num++;
}

else
{
if(test_go_switch()>=1000)//如果状态转换的结果包含于某一现有状态
{
D.s[j].u[D.s[j].link_num].f=k+100;//建立当前状态和该现有状态的连接
D.s[j].u[D.s[j].link_num].l=test_go_switch()-1000;
D.s[j].link_num++;
}
}

del_go_switch();//清空
}
}

for(k=0;k<vtnum;k++)//对当前状态,每个终结符
{
if(!test_link(j,k))
{
go_switch(j,k);

if(test_go_switch()==1)
{
add_go_switch();
D.s[j].u[D.s[j].link_num].f=k;
D.s[j].u[D.s[j].link_num].l=D.cd_num-1;
D.s[j].link_num++;
}

else
{
if(test_go_switch()>=1000)
{
D.s[j].u[D.s[j].link_num].f=k;
D.s[j].u[D.s[j].link_num].l=test_go_switch()-1000;
D.s[j].link_num++;
}
}
del_go_switch();
}
}
}

}while(i!=D.cd_num);//当一轮没有新的状态产生时,结束
}

//构造ACTION表
void action()
{
int i,j,k;
for(i=0;i<D.cd_num;i++)//对每个状态循环
{
for(j=0;j<D.s[i].link_num;j++)//S,对每个状态的每个连接循环,如果找到当前状态用终结符建立的连接,则ACTION[当前状态号][对应终结符编号]=连接另一端状态号
{
if(D.s[i].u[j].f<100)
{
ACTION[i][D.s[i].u[j].f]=D.s[i].u[j].l;
}
}

for(j=0;j<D.s[i].item_num;j++)//r,对每个状态的每个项目循环,如果找到一个终结项目,例如A->c. 则ACTION[当前状态][所有vt和$]=产生式A->c的编号
{
if(oldz[D.s[i].w[j].f][D.s[i].w[j].l]=='/0')
{
for(k=0;k<=vtnum;k++)
{
ACTION[i][k]=D.s[i].w[j].f+100;
}
}
}

for(j=0;j<D.s[i].item_num;j++)//acc,对每个状态的每个项目循环,如果找到P->S. 所在状态,则ACTION[当前状态][$]=300,即acc
{
if(D.s[i].w[j].f==0&&D.s[i].w[j].l==2)
{
ACTION[i][vtnum]=300;
break;
}
}
}

}

//构造GOTO表
void go_answer()
{
int i,j;
for(i=0;i<D.cd_num;i++)
{
for(j=0;j<D.s[i].link_num;j++)//对每个状态的每个连接循环,如果找到当前状态用非终结符建立的连接,则GOTO[当前状态号][对应非终结符编号]=连接另一端状态号
{
if(D.s[i].u[j].f>=100)
{
GOTO[i][D.s[i].u[j].f-100]=D.s[i].u[j].l;
}
}
}
}

//总控程序
int control()
{
int i,j,num;
char c[Y]={'/0'};//初始化带分析字符串的存储空间
SqE stack
;
int p1=0,p2=0;//p1是待分析字符串指针,p2是栈顶下一位元素的指针,stack[0]是栈底
vtnum++;//把'$'加入终结符集
for(i=0;i<N;i++)
stack[i].c1='/0';//初始化符号栈
stack[p2].t=0;
stack[p2].c1='$';//把(0,$)压栈
p2++;//指针更新
printf("/n请输入要分析的字符串(以$结尾):");
cin>>c;
printf("/n栈中状态 栈中符号 输入串 分析动作/n");

while(ACTION[stack[p2-1].t][test(c[p1])]!=300)//ACTION[栈顶状态号][当前字符编号]
{
for(i=0,j=0;stack[i].c1!='/0';i++)
{
printf("%d",stack[i].t);
j++;
}

for(i=0;i<14-j;i++)
printf(" ");//输出栈中状态

for(i=0,j=0;stack[i].c1!='/0';i++)
{
printf("%c",stack[i].c1);
j++;
}

for(i=0;i<14-j;i++)
printf(" ");//输出栈中符号

for(i=p1,j=0;c[i]!='/0';i++)
{
printf("%c",c[i]);
j++;
}

for(i=0;i<14-j;i++)
printf(" ");//输出剩余字符串

if(ACTION[stack[p2-1].t][test(c[p1])]>0&&ACTION[stack[p2-1].t][test(c[p1])]<100)//S
{
printf("S%d/n",ACTION[stack[p2-1].t][test(c[p1])]);//输出分析动作
stack[p2].t=ACTION[stack[p2-1].t][test(c[p1])];//将状态压栈
stack[p2].c1=c[p1];//将当前字符压栈
p2++;
p1++;//更新两个指针
}

else if(ACTION[stack[p2-1].t][test(c[p1])]>100&&ACTION[stack[p2-1].t][test(c[p1])]<200)//r,归约
{
num=ACTION[stack[p2-1].t][test(c[p1])]-100;//num是归约所用的产生式编号
printf("r%d/n",num);
p2=p2-length(num);//弹出length(num)个元素
stack[p2].t=GOTO[stack[p2-1].t][test(oldz[num][0])-100];//若T是弹出后的栈顶元素中的状态号,把GOTO[T][oldz[num][0]]压栈,注意-100
stack[p2].c1=oldz[num][0];//把oldz[num][0]压栈
p2++;
stack[p2].c1='/0';//封顶
}

else
{
if(ACTION[stack[p2-1].t][test(c[p1])]==0)//Error
{
printf("Error/n/nError,您输入的句型和文法不符!/n");
return ERROR;
}
}
}

printf("01 $%c $ acc/n",oldz[1][0]);
printf("/nSucceed,您输入句型和文法匹配!/n");
return OK;

}

//返回增广文法产生式右部的长度
int length(int i)
{
int j;
for(j=1;oldz[i][j]!='/0';j++)
{
}
return (j-1);//j-1是产生式右部字符数
}

//输出两张表
void printf_ag()
{
int i,j;
printf("ACTION表为:/n");
printf(" ");
for(i=0;i<=vtnum;i++)
printf("%c ",vt[i]);
printf("/n");
for(i=0;i<D.cd_num;i++)
{
if(i<10)
printf("%d ",i);
else
printf("%d ",i);
for(j=0;j<=vtnum;j++)
{
if(ACTION[i][j]<100&&ACTION[i][j]>0)
{
if(ACTION[i][j]<10&&ACTION[i][j]>0)
printf("s%d ",ACTION[i][j]);
else
printf("s%d ",ACTION[i][j]);
}

else if(ACTION[i][j]>100&&ACTION[i][j]<200)
{
if((ACTION[i][j]-100)<10&&(ACTION[i][j]-100)>0)
printf("r%d ",ACTION[i][j]-100);
else
printf("r%d ",ACTION[i][j]-100);
}

else if(ACTION[i][j]==300)
printf("acc ");

else
printf(" ");
}
printf("/n");
}

printf("/nGOTO表为:/n");
printf(" ");
for(i=0;i<vnnum;i++)
printf("%c ",vn[i]);
printf("/n");
for(i=0;i<D.cd_num;i++)
{
if(i<10)
printf("%d ",i);
else
printf("%d ",i);
for(j=0;j<vnnum;j++)
{
if(GOTO[i][j]<10)
printf("%d ",GOTO[i][j]);
else
printf("%d ",GOTO[i][j]);
}
printf("/n");
}

}

//检验在当前状态下是否已存在用当前字符建立的连接
bool test_link(int i,int num)
{
int j;
for(j=0;j<D.s[i].link_num;j++)
{
if(D.s[i].u[j].f==num)
return true;
}

return false;
}

/*txt文件的格局

6 3 4
SAB
abcd
SA
SB
AaAb
Ac
BaBb
Bd
示例:aacbb$

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: