First集

1.实验目的

深入理解“求解文法中非终结符的first集”算法的基本思想,通过程序设计,提高针对复杂算法问题的数据结构设计能力、编写程序的能力以及程序测试和调试等方面的能力。

2.实验原理和内容

  1. 结合程序设计的需要定义合适的数据结构存储文法、first集合以及一些中间数据的集合;
  2. 采用C语言或JAVA语言编程求解给定的文法中所有非终极符的first集;
  3. 对所编写的程序进行测试和调试。

 代码:

#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

#

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值