(编译原理)正规文法转正规式(原代码)

(编译原理)正规文法转正规式

一、实验要求

  • 输入:正规文法
  • 输出:正规式
    例:
    输入:S->aB B->b 输出:ab
    输入:S->aS S->b 输出:a*b
    输入:S->a S->b 输出:S->a|b

二、实验原理

1、正规文法

在编译原理当中,正规文法指的是3型文法,她所满足的条件为:文法左部只有一个非终结符,右部要么一个单终结符,要么一个终结符配一个非终结符(统一非终结符的位置,都在终结符左部或右部),但右部字符数不超过两个。
如:S->a S->aA A->b 满足3型文法条件
而S->a S->aA A->b S->Sd 不满足,须将S->Sd改为S->dS或S->aA改为S->Aa才满足要求

具体文法分类请看小编另一篇博客https://blog.csdn.net/ScottWei_007/article/details/88768019

2、正规式

正规式是用来描述语言单词的表达式。一般来说,正规式由操作符和字母组成,操作符包括: ( ) () ∗ * ∣ | ⋅ · 组成,字母是则是终结符的小写字母构成。
操作符的优先级由高到低为:()、 ∗ * ⋅ · ∣ |

a ⋅ b a·b ab
a ∗ b a*b ab
a b ab ab

有限次的上述三种操作的表达式即为正规式。

三、算法设计

注:小编采用的是右递归推导,即右部非终结符在终结符右侧

  1. 对所有正规文法进行如下操作:
  • 合并所有左部非终结符相同,右部只有一个终结符的正规文法(S->a S->b合并为S->a|b)
  • 合并所有左部非终结符相同,右部终结符相同且和左部非终结符相同的正规文法(S->aS S->bS 合并为S->aS|bS)
  • 合并所有左部非终结符相同,右部终结符相同但和左部非终结符不相同的正规文法(S->aB S->bB合并为S->aB|bB )
  • 进行文法整理(S->aB|bB S->aS|bS 整理为S->(a|b)B S->(a|b)S)
  1. 对右部只有一个终结符的正规文法L,遍历文法,若找到右部非终结符和左部非终结符不相等,且右部非终极符和L左部非终结符相同,则进行合并。(A->b S->aA 合并为S->ab)
  2. 对右部只有一个终结符的正规文法L,遍历文法,若找到右部非终结符和左部非终结符相等,且右部非终极符和L左部非终结符相同,则进行合并。(S->b S->aS 合并为S->a*b)
  3. 若所有文法右边都不含非终结符,进行合并输出,否则转至步骤1。

四、实验过程

1、定义

构建结构体进行存储字符串,定义全局变量和函数的调用

//定义全局变量 
struct Rule{
	char str[10];//存储正规文法 
	char right[100];//存储正规文法右边 
	char left;//存储正规文法左边 
}r[10];

int n;//全局变量,表示输入的正规文法行数 
//函数调用 
void Init();
//Max1~Max4为正规文法到正规式的转换函数 
void Mix1();
void Mix2();
void Mix3();
void Mix4();
void Mix();

2、初始化

输入字符串,获取字符串左右两部分内容便于操作

//初始化,输入正规文法 
void Init(){
	cout<<"输入正规文法:"<<endl;
	int i,j=0; 
    for(i=0;;i++){//输入正规文法,'#'停止 
    	cin>>r[i].str;
    	if(r[i].str[j]=='#')
    	break;
	}
	n=i;
	for(i=0;i<n;i++){//记录每行的文法的左部和右部 
		int k=0;
		for(j=3;j<strlen(r[i].str);j++)
		r[i].right[k++]=r[i].str[j];
		r[i].left=r[i].str[0];
	}
	cout<<"输出正规式:"<<endl;
}

3、转换

void mix1()进行算法设计步骤1的前三步操作
void mix2()进行算法设计步骤1的第四步操作
void mix3()进行算法设计步骤2的第操作
void mix4()进行算法设计步骤3的操作
void mix()进行算法设计步骤4的操作
代码段太长,请看源代码内容

4、主函数

进行函数调用并执行转换,输出最终结果。

//主函数 
int main(){
	Init();//初始化 
	Mix1();//进行转换	
} 

五、源代码和实验截图

代码已经过编译运行,可直接使用。

#include<iostream>
#include<iomanip>
#include<string.h>
using namespace std;

//定义全局变量 
struct Rule{
	char str[10];//存储正规文法 
	char right[100];//存储正规文法右边 
	char left;//存储正规文法左边 
}r[10];

int n;//全局变量,表示输入的正规文法行数 
//函数调用 
void Init();
//Max1~Max4为正规文法到正规式的转换函数 
void Mix1();
void Mix2();
void Mix3();
void Mix4();
void Mix();

//初始化,输入正规文法 
void Init(){
	cout<<"输入正规文法:"<<endl;
	int i,j=0; 
    for(i=0;;i++){//输入正规文法,'#'停止 
    	cin>>r[i].str;
    	if(r[i].str[j]=='#')
    	break;
	}
	n=i;
	for(i=0;i<n;i++){//记录每行的文法的左部和右部 
		int k=0;
		for(j=3;j<strlen(r[i].str);j++)
		r[i].right[k++]=r[i].str[j];
		r[i].left=r[i].str[0];
	}
	cout<<"输出正规式:"<<endl;
}

//正规文法到正规式的转换1 
void Mix1(){//合并文法中形如S->aS S->bS为S->aS|bS;S->aB S->bB为S->aB|bB;S->a S->b为S->a|b ;即合并左部相同、右部相同的部分,以及右部单个字符 
	int i,j;
	for(i=0;i<n-1;i++){
		for(j=i+1;j<n;j++){
			if(r[i].left==r[j].left&&r[i].right[1]==r[j].right[1]&&r[i].left!='\0'){//并左部相同、右部相同的部分
				strcat(r[i].right,"|");
				strcat(r[i].right,r[j].right);
				r[j].left='\0';
				memset(r[j].right,0,sizeof(r[i].right));
			}
			if(r[i].left==r[j].left&&strlen(r[i].right)==1&&strlen(r[j].right)==1&&r[i].left!='\0'){//合并右部单个字符  
				strcat(r[i].right,"|");
				strcat(r[i].right,r[j].right);
				r[j].left='\0';	
				memset(r[j].right,0,sizeof(r[i].right));		
			}
		}
	}
	Mix2();	
}

//正规文法到正规式的转换2 
void Mix2()//合并Mix1中S->aS|bS为S->(a|b)S ;S->aB|bB为S->(a|b)B 
{
	int i,j,k;
	for(i=0;i<n;i++){
		if(strlen(r[i].right)>2&&r[i].right[1]>='A'&&r[i].right[1]<='Z'){
			k=0;
			char a[100]={};//使用中间数组,将需保留内容存入其中,最后再存入结构体中 
			a[k++]='(';
			for(j=0;j<strlen(r[i].right);j=j+3){//存储需保留的值 
     			a[k]=r[i].right[j];
     			a[k+1]='|';
     			k=k+2;
     		}
     		a[k-1]=')';
     		a[k++]=r[i].right[1];
     		memset(r[i].right,0,sizeof(r[i].right));
     		strcpy(r[i].right,a);//存入结构体 
		}
	}
	Mix3();
}

//正规文法到正规式的转换3 
void Mix3(){//合并类似S->a|b S->(a|b)S 为S->(a|b)*(a|b) ,采用右递归 
	int i,j,temp;
	for(i=0;i<n;i++){
		temp=0;
		for(j=0;j<strlen(r[i].right);j++){//先寻找S->a|b,即右边无非终结符 
			if(r[i].right[j]>='A'&&r[i].right[j]<='Z')
			temp=1;
		}
		if(temp==0){//然后在寻找与其左部有相同非终结符且右部也相同非终结符的文法 
			for(j=0;j<n;j++){
				int length=strlen(r[j].right);
				if(r[j].right[length-1]==r[j].left&&r[j].right[length-1]==r[i].left&&r[j].left!='\0'){//将两个文法进行合并 
					r[j].right[length-1]='\0';
					if(strlen(r[i].right)==1|strlen(r[i].right)==2){
	    				strcat(r[j].right,"*");
						strcat(r[j].right,r[i].right);
	    			}
	    			if(strlen(r[i].right)>2){
	    				strcat(r[j].right,"*(");
	    				strcat(r[j].right,r[i].right);
	    				strcat(r[j].right,")");
					}
					r[i].left='\0';
					memset(r[i].right,0,sizeof(r[i].right));
				}
			}
		}
	}
	Mix4();
}

//正规文法到正规式的转换4 
void Mix4(){//合并类型S->aA A->(b|a) 为S->a(b|a) 
	int i,j,temp,k;
	for(i=0;i<n;i++){
		temp=0;
		for(j=0;j<strlen(r[i].right);j++){//先寻找S->a|b,即右边无非终结符 
			if(r[i].right[j]>='A'&&r[i].right[j]<='Z')
			temp=1;
		}
		if(temp==0){//然后在寻找右部与其左部有相同非终结符但左部不相同非终结符的文法 
			for(j=0;j<n;j++){
				int length=strlen(r[j].right);
				if(r[j].right[length-1]==r[i].left&&r[j].left!='\0'){//进行合并 
					r[j].right[length-1]='\0';
					k=0;
					for(int m=0;m<strlen(r[i].right);m++)//判断操作符是否都为乘法 
					if(r[i].right[m]=='*'|r[i].right[m]=='|')
					k=1;
	    			if(strlen(r[i].right)>2&&k==1){//若都为乘法,则在合并时可以不用加括号 
	    				strcat(r[j].right,"(");
	    				strcat(r[j].right,r[i].right);
	    				strcat(r[j].right,")");
					}
					else
					strcat(r[j].right,r[i].right);
					r[i].left='\0';
					memset(r[i].right,0,sizeof(r[i].right));
				}
			}
		}
	}
	Mix();	
} 

//正规文法到正规式的转换5 
void Mix(){//进行最后的判断和循环 
	int i,j,temp=0;
	for(i=0;i<n;i++){//判断是否所有文法右边均不含非终结符 
		for(j=0;j<n;j++){
			if(r[i].right[j]>='A'&&r[i].right[j]<='Z')
			temp=1;
		}
	}
	if(!temp){//若都不含非终结符,进行合并输出 
		for(i=0;i<n-1;i++){
			for(j=i+1;j<n;j++){
				if(r[i].left==r[j].left&&r[i].left!='\0'){
					strcat(r[i].right,"|");
					strcat(r[i].right,r[j].right);
					r[j].left='\0';
					memset(r[j].right,0,sizeof(r[j].right));
				}
			}
		}
	}
	if(temp)//若含非终结符,调至Min1循环执行 
	Mix1(); 
	else{
	for(i=0;i<n;i++)//输出正规式 
	cout<<r[i].right<<endl;
    }
}

int main(){
	Init();//初始化 
	Mix1();//进行转换	
} 

结果展示:
输入:S->a S->b
输出:S->a|b
在这里插入图片描述
输入:S->aA A->b
输出:S->ab
在这里插入图片描述
输入:S->aS S->b
输出:S->a*b
在这里插入图片描述
输入:S->aA A->bB B->dD D->e
输出:abde
在这里插入图片描述

输入:S->a S->aA A->a A->d A->aA A->dD
输出:a|a((a|d)*(a|d))
在这里插入图片描述

  • 5
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值