根据文法规则,判断文法类型

本文介绍了一种通过编程实现的文法类型判别方法,详细解释了如何根据输入的文法规则来判断文法属于0型、1型、2型还是3型。并通过具体实例演示了整个判断过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

根据文法规则,判断文法类型

1、实验要求

输入:文法规则

输出:文法类型

2、实验原理

文法规则:
以四元组的形式展示出来:
文法G 定义为四元组G={Vn,Vt ,P,S}
Vn :非终结符集
Vt :终结符集
P :产生式集合(规则集合)
S :开始符号(识别符号)

文法类型:
文法类型分为四种:0型文法、1型文法、2型文法和3型文法

  • 0型文法:通俗的说,写出来的文法规则都输出0型文法
  • 1型文法:又称上下文有关文法。
    满足条件:0型文法的基础上,右边字符个数不小于左边字符个数
    例如:S->aBC
    a->Bb
    C->c
  • 2型文法:又称上下文无关文法。
    满足条件:在1型文法的基础上,左部只有一个非终结符
    例如:S->aBC
    B->b
    C->c
  • 3型文法:又称规范文法。
    满足条件:在2型文法的基础上,产生式的右边字符数不超过2个且所有产生式要么都是左递归,要么都是右递归
    例如:S->aB
    B->bC
    C->c
    注:
    S->aB
    B->Cb
    C->c
    不成立,因为字符串既有左递归,又有右递归

四种文法的关系可用如下图表示:
在这里插入图片描述

3、实验步骤

  1. 从键盘上输入文法规则(字符串)
  2. 对字符串进行按行比较,判断其所属的文法类型(先逐行判断文法类型1,然后再逐行判断文法类型2,最后在整个字符串判断文法类型3)
  3. 显示出结果(文法类型)

4、实验过程

  1. 首先是进行结构体的构建,并且为了方便实用定义了全局变量
//结构体 
struct Rule{
	char str[10];
	int k;//记录局部文法类型 
	int left;//记录产生式左边长度 
	int right;//记录产生式右边长度 
}r[10];

int temp;//记录字符串行数 
int level=0;//记录文法类型 

  1. 主函数运到了5个调用函数,分别是输入板块、文法类型1判断板块、文法类型2判断板块、文法类型3判断板块和输出板块
//主函数 
int main(){
	Input();//输入文法规则 
	Transform1();//类型1判断 
	Transform2();//文法类型2判断 
	Transform3();//文法类型3判断 
    Output();//输出文法类型 
	return 0;
} 
  1. 在输入板块进行了文法规则的输入,一字符串的形式从键盘输入,存储到结构体中
//输入文法规则 
void Input(){
	int i,j;
	printf("输入文法规则:\n");
	printf("大写字母为非终止符,小写字母为终止符,第一个产生式左边为开始符,结束用$表示\n");
	for(i=0;i<10;i++){
		scanf("%s",r[i].str);
		j=0;
		if(r[i].str[j]=='$')
		break;
	}
	temp=i; 
}
  1. 文法类型1板块时进行判断文法规则的类型是否符合类型1,符合之后才能进行类型2的判断
//判断文法类型1
void Transform1(){
	int i,j;
	int length;
	for(i=0;i<temp;i++){
		r[i].k=0;
		length=strlen(r[i].str);//字符串长度 
		r[i].left=0;
		for(j=0;r[i].str[j]!='-';j++)//左边长度 
		r[i].left++;
		r[i].right=length-r[i].left-2;//右边长度,减2表示减去字符'-'和'>' 
		if(r[i].right>=r[i].left)
		r[i].k=1;
	}
}
  1. 文法类型2板块时进行判断文法规则的类型是否符合类型2,符合之后才能进行类型3的判断
//判断文法类型2
void Transform2(){
	int i,j,ss=0;
	for(i=0;i<temp;i++){//判断是否字符串是否都为类型1 
		if(r[i].k!=1)
		ss=1;
	}
	if(ss==0)//字符串都为类型1,进行类型2的判断 
	for(i=0;i<temp;i++){
		j=0;
	    if(r[i].left==1)//左边只有一个且是非终结符的类型1,则为类型2 
	    if(r[i].str[j]>='A'&&r[i].str[j]<='Z')
	    r[i].k=2;
	}
	level=1;	
}
  1. 文法类型3板块时进行判断文法规则的类型是否符合类型3
//判断文法类型3
void Transform3(){
	int i,j,t,m,n=0,sum=0,ss=0;
	for(i=0;i<temp;i++){//判断是否字符串是否都为类型2 
		if(r[i].k!=2)
		ss=1; 
	} 
	if(ss==0){//字符串都为类型2,进行类型3的判断 
		level=2;
    	for(i=0;i<temp;i++){
	    	m=r[i].left+2;//产生式右边第一个字符 
     	    if(r[i].str[m]>='a'&&r[i].str[m]<='z')
	    	t=1;
	    	if(r[i].str[m]>='A'&&r[i].str[m]<='Z')
	    	t=0;
	    	sum+=t;
	    	if(r[i].right>2)
	    	n=1;
    	}
     	if(n==0)//保证产生式右边不超过2个字符 
    	if(sum==temp|sum==0)//保证产生式右边是左递归或右递归 
    	level=3;
    }
}

7.在输出板块需要注意的是,不仅要将文法的类型输出,小编也将该四元组进行显示出来

//输出文法类型
void Output(){
	int i,j,m=0,n=0;
	int t,p,ss;
	char S,V1[100],V2[100],Vn[10]={},Vt[10]={};
	S=r[0].str[0];//开始符 
	for(i=0;i<temp;i++){
		for(j=0;j<strlen(r[i].str);j++){
			if(r[i].str[j]>='a'&&r[i].str[j]<='z')
			V1[m++]=r[i].str[j];
			if(r[i].str[j]>='A'&&r[i].str[j]<='Z')
			V2[n++]=r[i].str[j]; 
		}
	}
	t=0;
	for(i=0;i<m;i++){//终止符 
		ss=1;
		for(j=0;j<t;j++){
     		if(V1[i]==Vt[j])
	    	ss=0;
	    }
	    if(ss==1)
	    Vt[t++]=V1[i];
	}
	p=0;
	for(i=0;i<n;i++){//非终止符 
		ss=1;
		for(j=0;j<p;j++){
     		if(V2[i]==Vn[j])
	    	ss=0;
	    }
	    if(ss==1)
	    Vn[p++]=V2[i];
	}
	printf("该文法为第%d类型\n",level);//输出 
	printf("该文法的四元组为:\n");
	printf("S:%c\n",S); 
	printf("Vn=");
	for(i=0;i<p;i++)
	printf("%2c",Vn[i]);
	printf("\nVt=");
	for(i=0;i<t;i++)
	printf("%2c",Vt[i]);
	printf("\nP:\n");
	for(i=0;i<temp;i++)
	printf("%s\n",r[i].str);	
} 

5、源代码和截图

代码已经过调试,无编译错误

#include<stdio.h>
#include<string.h>

//结构体 
struct Rule{
	char str[10];
	int k;//记录局部文法类型 
	int left;//记录产生式左边长度 
	int right;//记录产生式右边长度 
}r[10];

int temp;//记录字符串行数 
int level=0;//记录文法类型 

//输入文法规则 
void Input(){
	int i,j;
	printf("输入文法规则:\n");
	printf("大写字母为非终止符,小写字母为终止符,第一个产生式左边为开始符,结束用$表示\n");
	for(i=0;i<10;i++){
		scanf("%s",r[i].str);
		j=0;
		if(r[i].str[j]=='$')
		break;
	}
	temp=i; 
}

//输出文法类型
void Output(){
	int i,j,m=0,n=0;
	int t,p,ss;
	char S,V1[100],V2[100],Vn[10]={},Vt[10]={};
	S=r[0].str[0];//开始符 
	for(i=0;i<temp;i++){
		for(j=0;j<strlen(r[i].str);j++){
			if(r[i].str[j]>='a'&&r[i].str[j]<='z')
			V1[m++]=r[i].str[j];
			if(r[i].str[j]>='A'&&r[i].str[j]<='Z')
			V2[n++]=r[i].str[j]; 
		}
	}
	t=0;
	for(i=0;i<m;i++){//终止符 
		ss=1;
		for(j=0;j<t;j++){
     		if(V1[i]==Vt[j])
	    	ss=0;
	    }
	    if(ss==1)
	    Vt[t++]=V1[i];
	}
	p=0;
	for(i=0;i<n;i++){//非终止符 
		ss=1;
		for(j=0;j<p;j++){
     		if(V2[i]==Vn[j])
	    	ss=0;
	    }
	    if(ss==1)
	    Vn[p++]=V2[i];
	}
	printf("该文法为第%d类型\n",level);//输出 
	printf("该文法的四元组为:\n");
	printf("S:%c\n",S); 
	printf("Vn=");
	for(i=0;i<p;i++)
	printf("%2c",Vn[i]);
	printf("\nVt=");
	for(i=0;i<t;i++)
	printf("%2c",Vt[i]);
	printf("\nP:\n");
	for(i=0;i<temp;i++)
	printf("%s\n",r[i].str);	
} 

//判断文法类型3
void Transform3(){
	int i,j,t,m,n=0,sum=0,ss=0;
	for(i=0;i<temp;i++){//判断是否字符串是否都为类型2 
		if(r[i].k!=2)
		ss=1; 
	} 
	if(ss==0){//字符串都为类型2,进行类型3的判断 
		level=2;
    	for(i=0;i<temp;i++){
	    	m=r[i].left+2;//产生式右边第一个字符 
     	    if(r[i].str[m]>='a'&&r[i].str[m]<='z')
	    	t=1;
	    	if(r[i].str[m]>='A'&&r[i].str[m]<='Z')
	    	t=0;
	    	sum+=t;
	    	if(r[i].right>2)
	    	n=1;
    	}
     	if(n==0)//保证产生式右边不超过2个字符 
    	if(sum==temp|sum==0)//保证产生式右边是左递归或右递归 
    	level=3;
    }
} 

//判断文法类型2
void Transform2(){
	int i,j,ss=0;
	for(i=0;i<temp;i++){//判断是否字符串是否都为类型1 
		if(r[i].k!=1)
		ss=1;
	}
	if(ss==0)//字符串都为类型1,进行类型2的判断 
	for(i=0;i<temp;i++){
		j=0;
	    if(r[i].left==1)//左边只有一个且是非终结符的类型1,则为类型2 
	    if(r[i].str[j]>='A'&&r[i].str[j]<='Z')
	    r[i].k=2;
	}
	level=1;	
}

//判断文法类型1
void Transform1(){
	int i,j;
	int length;
	for(i=0;i<temp;i++){
		r[i].k=0;
		length=strlen(r[i].str);//字符串长度 
		r[i].left=0;
		for(j=0;r[i].str[j]!='-';j++)//左边长度 
		r[i].left++;
		r[i].right=length-r[i].left-2;//右边长度,减2表示减去字符'-'和'>' 
		if(r[i].right>=r[i].left)
		r[i].k=1;
	}
}	

//主函数 
int main(){
	Input();//输入文法规则 
	Transform1();//类型1判断 
	Transform2();//文法类型2判断 
	Transform3();//文法类型3判断 
    Output();//输出文法类型 
	return 0;
} 





在这里插入图片描述

在这里插入图片描述

本程序的所用的存储结构都是string类型的,最主要的存储文法的数据结构为自定义结构,里面包括一个产生式的左部,右部以及select集合,至于非终结符的first和follow集合,则是定义了一个string类型的数组进行存储。 本程序的求first,follow,select集合的算法即为书上所介绍的方法,即求first的集合时,只看本产生式,求follow集合时,要进行递归查找一个非终结符的所有后跟字符,求select其实就是对first与follow集合的运算,最终根据所有的select集合,便可以判断文法是否为LL(1文法。 对于不是LL(1文法的产生式,本程序在判断后进行转换,先进行消除左递归,然后提取左公因子,在这两步的每一步结束之后,都要对产生式进行整合,去掉空存储,去掉无法到达的产生式,将select全部置空。 每进行一次非LL(1)到LL(1)的转换之后,都要对其文法性质进行判断,如果是LL(1),则跳出,不是则继续,但是当循环一定次数之后仍不是,程序判定其无法转换,也要跳出。 其中还有对第一个非终结字符的右部替换与否进行选择,原因是,有些通过替换就可以很方便的进行转换,这个要通过人为进行输入。 提取公因子中也有上一段所说的类似的判断机制,目的是为了防止文法的左公因子无法提取完的情况出现。 最终有三种结果,一种是是LL(1文法,一种是不是LL(1),但是经过转换变成了LL(1),还有一种是经过转换也无法变成LL(1)。 输入文本格式样例: A A->ad A->Bc B->aA B->bB
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值