1.实验目的
深入理解“求解文法中非终结符的first集”算法的基本思想,通过程序设计,提高针对复杂算法问题的数据结构设计能力、编写程序的能力以及程序测试和调试等方面的能力。
2.实验原理和内容
- 结合程序设计的需要定义合适的数据结构存储文法、first集合以及一些中间数据的集合;
- 采用C语言或JAVA语言编程求解给定的文法中所有非终极符的first集;
- 对所编写的程序进行测试和调试。
代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int N=100,M=1000;
/*
文法
S->AB|bC
A->$|b
B->aD|$
C->AD|b
D->aS|c
#
epsilon集
Epsilion={A,B,S}
first集合
First(S)={b,$,a}
First(A)={$,b}
First(B)={a,$}
First(C)={b,a,c}
First(D)={a,c}
*/
/****************************************************/
/* 结构体 */
/****************************************************/
typedef struct{
char left;//左边字符
char right[20];//右边字符集合
int rightlen;//右边字符集合长度
}jgt;
/***************************************************/
/* 定义 /
/***************************************************/
jgt Total[N];//记录所有文法
int Total_len;//记录文法的个数
jgt first[N];//记录Fist集
int first_len;//记录first集的个数
//存放文法的缓冲数组
char buffer[M];//存放文法
int buffer_len;//记录文法的长度
//存放导空集
char epsilion[N];//存放可导空非终结符
int epsilion_len;//记录导空集的长度
/***************************************************/
/* 函数声明 /
/***************************************************/
void read_file();//读文件函数
void scaner();//扫描代码
void printWF();//打印文法
bool is_in(char a[],int len,char c);//判断是否在数组里
void Epsilion();//求导空集
void printEpsilion();//打印导空集
bool isNTerminal(char c);//判断是否为终结符
int pos_inFirst(char c);//返回非终结符的应该在的位置
void First();//求First集
void printFirst();//打印First集
/***************************************************/
/* 主函数 /
/***************************************************/
int main()
{
read_file();//读文件
scaner();//扫描文法,把每个文法分离出来
printWF();//打印文法
Epsilion();//求导空集
printEpsilion();//打印导空集
First();//求First集
printFirst();//打印First集
return 0;
}
/****************************************************************/
/* 读文件函数 /
/****************************************************************/
void read_file()
{
FILE *p;
char c;
p=fopen("First.txt","r");
c=fgetc(p);
while(c!='#')//以"#"结束
{
buffer[buffer_len++]=c;
c=fgetc(p);
}
fclose(p);
buffer[buffer_len++]='#';
}
/****************************************************************/
/* 扫描代码 */
/****************************************************************/
void scaner()
{
int pos=0;//扫描的位置
char ch=buffer[pos];
bool left=true;//标记文法的左部,若为true,则识别到的非终结符加入左部,否则,加入右部
while(ch!='#')
{
if(ch=='\n')//换行,将读下一个文法
{
ch=buffer[++pos];//读下一个字符
Total_len++;//将记录下一个文法
left=true;//接下来加入文法左部
continue;
}
else if(left==true&&isNTerminal(ch))//遇见左边部分
{
Total[Total_len].left=ch;
ch=buffer[++pos];//读下一个字符
}
else if(ch=='-')//进入文法右边部分
{
ch=buffer[++pos];//读下一个字符
if(ch=='>')
{
ch=buffer[++pos];//读下一个字符
left=false;//将读取文法的右边部分
}
}
else if(ch=='|')//分隔符
{
Total[Total_len+1].left=Total[Total_len++].left;//让该文法的左部赋值给下一个文法的右部
ch=buffer[++pos];//读下一个字符
}
else if(left==false)//读取到的是右边
{
Total[Total_len].right[Total[Total_len].rightlen++]=ch;//读入右部分
ch=buffer[++pos];//读下一个字符
}
}
}
/****************************************************************/
/* 打印文法 /
/****************************************************************/
void printWF()
{
cout<<"-------------文法----------"<<endl;
for(int i=0;i<Total_len;i++)
{
cout<<"\t "<<Total[i].left<<"->";
for(int j=0;j<Total[i].rightlen;j++)
cout<<Total[i].right[j];
cout<<endl;
}
cout<<endl;
}
/****************************************************************/
/* 判断是否在数组里 /
/****************************************************************/
bool is_in(char a[],int len,char c)
{
for(int i=0;i<len;i++)
{
if(a[i]==c)
return true;
}
return false;
}
/****************************************************************/
/* 求导空的非终结符集 /
/****************************************************************/
void Epsilion()
{
int tmp=-1;//tmp标记导空集合的长度,
while(tmp!=epsilion_len)//若循环出来,可导空集合不变,即代表没有可导空的非终结符了,可退出
{
tmp=epsilion_len;//标记当前可导空的非终结符的长度
for(int i=0;i<Total_len;i++)//遍历文法
{
if(is_in(epsilion,epsilion_len,Total[i].left))//如果该非终结符已经在导空集了,可以跳该文法
continue;
int isEp=1;//标记是否可导空,初始为可导空
for(int j=0;j<Total[i].rightlen;j++)//遍历文法的右边部分,观察右边部分是否能全导空
{
if(Total[i].right[j]=='$')//如果右边为空,即为导空
break;//退出该文法
else if(!is_in(epsilion,epsilion_len,Total[i].right[j]))//右边部分有符号不能导空,退出该文法
{
isEp=0;//标记该文法不能导空
break; //退出该文法
}
}
if(isEp==1)//遍历结束,仍然没有遇见不能导空的符号,即说明左边部分的非终结符可导空
epsilion[epsilion_len++]=Total[i].left;//将左边部分非终结符加入导空集合
}
}
}
/****************************************************************/
/* 打印导空集 /
/****************************************************************/
void printEpsilion()
{
cout<<"-------------导空集----------"<<endl;
cout<<"\tEpsilion={";
for(int i=0;i<epsilion_len-1;i++)
{
cout<<epsilion[i]<<",";
}
cout<<epsilion[epsilion_len-1]<<"}"<<endl<<endl;
}
/****************************************************************/
/* 判断是否为非终结符 /
/****************************************************************/
bool isNTerminal(char c)
{
if(c>='A'&&c<='Z')//大写字母为非终结符
return true;
return false;
}
int pos_inFirst(char c)//返回非终结符的应该在的位置
{
for(int i=0;i<first_len;i++)
{
if(c==first[i].left)
return i;
}
first[first_len].left=c;//将非终结符加入first集中
return first_len++;
}
/****************************************************************/
/* 求First集 /
/****************************************************************/
void First()
{
int tmp=1;//记录first集有无变化,0代表无更新,1代表有更新
while(tmp)
{
tmp=0;//无更新
for(int i=0;i<Total_len;i++)//遍历文法
{
int pos=pos_inFirst(Total[i].left);//记录左边部分非终结符在first下标
for(int j=0;j<Total[i].rightlen;j++)//遍历右边
{
char c=Total[i].right[j];//c记录右边字符
//c为终结符
if(!isNTerminal(c))
{
//c还未加入其first集
if(!is_in(first[pos].right,first[pos].rightlen,c))
{
first[pos].right[first[pos].rightlen++]=c;//加入
tmp=1; //更新,标记位 置为1
}
break; //退出该文法
}
//c为非终结符
else
{
int pos1=pos_inFirst(c);//pos1记录该非终结符c的first下标
//把c的first集串传给左边,遍历右边非终结符的first所有元素
for(int k=0;k<first[pos1].rightlen;k++)
{
char c1=first[pos1].right[k];//记录c的first集的元素
if(c1=='$'&&!is_in(epsilion,epsilion_len,Total[i].left))//如果Total[i].left不能导空,则不能把$加入
continue;
if(!is_in(first[pos].right,first[pos].rightlen,c1))//如果该终结符还未加入其first集
{
first[pos].right[first[pos].rightlen++]=c1;
tmp=1; //更新,标记位 置为1
}
}
if(!is_in(epsilion,epsilion_len,c))//若遇到不能导空的非终结符
break;
}
}
}
}
return;
}
/****************************************************************/
/* 打印First集 /
/****************************************************************/
void printFirst()
{
cout<<"-------------First集---------"<<endl;
for(int i=0;i<first_len;i++)
{
cout<<"\tFirst("<<first[i].left<<")={";
for(int j=0;j<first[i].rightlen-1;j++)
cout<<first[i].right[j]<<",";
if(first[i].rightlen>0);
cout<<first[i].right[first[i].rightlen-1]<<"}"<<endl;
}
}
运行结果:
文法1:
A->BC
A->AaB
B->bc|$
C->c|$
#
文法2:
S->AB|bC
A->$|b
B->aD|$
C->AD|b
D->aS|c
#