正规式转换为NFA代码实现

问题引入

正规式是一种编译原理课程中经常提到的描述文法的语法规则,与正则表达式有相同之处,但并不是同一个概念。有效的正规式的字母表∑ = { a − z , A − Z } 。

所有的正规式中的符号以及对应的规则如下:

‘*’:表示闭包;
‘|’:表示联合(或)——在有的版本中也用’+'来表示;

‘.’:表示连接(与);

NFA(non-deterministic finite automaton with ε-move,ε-NFA)带空移动的非确定的有穷状态自动机

从正规式到NFA的转换是编译原理课程中经典的问题,现在考虑使用C++代码来实现这一过程

解题分析

首先我们考虑如何表示NFA,博主这里采用的方法是将NFA的每一条边都显示并且输出出来,例如:边1 起始节点为:A1 转换过程为:a 结束节点为:A2,并以此方式展示NFA的所有的边。这里就需要构造一个结构体结构体Edge(由两个首尾节点和一个转换过程组成,表示NFA中的一条边)

在这里插入图片描述

1、用户输入正规式

2、对正规式进行合法性判断

3、对合法的正规式补全省略的连接符号 ’ . ’

4、将所得的中缀表达式转换为后缀表达式(逆波兰式)

5、“计算”后缀表达式。即将后缀表达式转换为NFA

​ (1)碰到字母表中的字符构造成一个结构体Edge(由两个首尾节点和一个转换过程组成,表示NFA中的一条边),进行压栈

​ (2)碰到运算符,从栈顶弹出一个(单目运算符)或两个字母(双目运算符),并进行对应的操作

函数Unite(Edge Left, Edge Right),处理 a|b的情况,并返回一个Edge类型

处理方法:新增两个空的节点,四条边**(红色所示)**,并将原有的a、b连起来,如图所示,返回的Edge的变量起始节点为新生成的A5,结束节点为新生成的A6,转换过程Thro置空 ‘ ’

在这里插入图片描述

函数Join(Edge Left, Edge Right),处理 a.b的情况,并返回一个Edge类型

处理方法:把右边Right的起始节点Begin 改成Left的结束节点End,并返回一个新的Edge的对象,起始节点为Left的开始节点,结束节点为Right的结束节点,转换过程Thro置空 ‘ ’

函数Self(Edge edge) ,处理 a*的情况,并返回一个Edge类型

处理方法:新增两条边**(红色所示)**构成闭环,返回传过来的参数edge,但需要把edge的转换过程变量Thro置空 ‘ ’

在这里插入图片描述

代码实现

RexpToNFA.h

#include <iostream>
#include <cmath>
#include <string>
#include <stack>
#include <queue>
using namespace std;

int Aflag = 0;//a用于标记NFA中的状态,例如:A0、A1

class Edge {
public:
	Edge() {
		this->begin = "";
		this->thro = ' ';
		this->end = "";
	};

	Edge(string begin, char thro, string end) {
		this->begin = begin;
		this->thro = thro;
		this->end = end;
	}

	void setThro(char ch) {
		this->thro = ch;
	}

	char getThro() {
		return this->thro;
	}

	void setBegin(string ch) {
		this->begin = ch;
	}

	string getBegin() {
		return this->begin;
	}

	void setEnd(string ch) {
		this->end = ch;
	}

	string getEnd() {
		return this->end;
	}


private:
	char thro;
	string begin;
	string end;
};

class RTN {
public:
	RTN() {};

	bool isletter(char ch) {
		if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z') {
			return true;
		}
		else {
			return false;
		}
	}

	bool islegal(string s) {
		int Lbracket = 0;
		int Rbracket = 0;
		RTN r;
		for (int i = 0; i < s.length(); i++) {
			char ch = s[i];
			if (ch == '*' || ch == '|' || ch == '.') {
				continue;
			}
			//数字不考虑判断
			else if (ch >= 0 && ch <= 9) {
				continue;
			}
			else if (r.isletter(ch)) {
				continue;
			}
			else if (ch == '(') {
				Lbracket++;
			}
			else if (ch == ')') {
				Rbracket++;
			}
			else {
				return false;
			}
		}
		if (Lbracket != Rbracket) {
			return false;
		}
		else {
			return true;
		}
	}

	//将输入的表达式中的连接符号用'.'补全
	string turnToConnect(string s) {
		string ns = s.substr(0, 1);
		for (int i = 1; i < s.length(); i++) {
			char prech = s[i - 1];
			char ch = s[i];
			//连续的字母之间需要添加上连接符号'.'
			if (isletter(prech) && isletter(ch)) {
				ns = ns + '.' + ch;
				//cout << endl << ns;
				continue;
			}
			//判断当前是字母,并且前面是右括号')'的情况
			else if (isletter(ch) && prech == ')') {
				ns = ns + '.' + ch;
				continue;
			}
			//判断当前是左括号'(',并且前面是右括号')'或者前面是字母的情况
			else if (ch == '(' && prech == ')' || ch == '(' && isletter(prech)) {
				ns = ns + '.' + ch;
				continue;
			}
			else {
				ns += ch;
				//cout << endl << ns;
				continue;
			}
		}
		return ns;
	}

	string ReversePolishType(string s) {
		stack<char> st;
		string ns = "";

		for (int i = 0; i < s.length(); i++) {
			char ch = s[i];
			if (isletter(ch)) {
				ns = ns + ch;
			}
			else if (ch == '(') {
				st.push(ch);
			}
			else if (ch == ')') {
				while (st.top() != '(') {
					ns = ns + st.top();
					st.pop();
				}
				st.pop();//将栈顶剩余的左括号弹出
			}
			else if (ch == '*') {
				ns = ns + ch;
				//'*'是单目运算符,碰到的时候直接弹出
				/*if (st.empty() || st.top() == '.' || st.top() == '|' || st.top() == '(') {
					st.push(ch);
				}
				else {
					while (st.top() == '*') {
						ns = ns + st.top();
						st.pop();
						if (st.empty()) break;
					}
					if (st.empty() || st.top() == '.' || st.top() == '|' || st.top() == '(') {
						st.push(ch);
					}
				}*/
			}
			else if (ch == '.') {
				if (st.empty() || st.top() == '|' || st.top() == '(') {
					st.push(ch);
				}
				else {
					while (st.top() == '*' || st.top() == '.') {
						ns = ns + st.top();
						st.pop();
						if (st.empty()) break;
					}
					if (st.empty() || st.top() == '|' || st.top() == '(') {
						st.push(ch);
					}
				}
			}
			else if (ch == '|') {
				if (st.empty() || st.top() == '(') {
					st.push(ch);
				}
				else {
					while (st.top() == '*' || st.top() == '.' || st.top() == '|') {
						ns = ns + st.top();
						st.pop();
						if (st.empty()) break;
					}
					if (st.empty() || st.top() == '(') {
						st.push(ch);
					}
				}
			}
		}
		while (!st.empty()) {
			ns = ns + st.top();
			st.pop();
		}

		return ns;
	}

	//处理 a|b
	Edge Unite(Edge Left, Edge Right) {
		Edge ed1(to_string(Aflag), '#', Left.getBegin());
		Edge ed2(to_string(Aflag), '#', Right.getBegin());
		Edge ed3(Left.getEnd(), '#', to_string(Aflag + 1));
		Edge ed4(Right.getEnd(), '#', to_string(Aflag + 1));
		q.push(ed1);
		q.push(ed2);
		q.push(ed3);
		q.push(ed4);
		if (Left.getThro() != ' ') {
			this->q.push(Left);
		}
		if (Right.getThro() != ' ') {
			this->q.push(Right);
		}

		Edge ed(to_string(Aflag), ' ', to_string(Aflag + 1));
		Aflag = Aflag + 2;
		return ed;
	}

	//处理 a.b
	Edge Join(Edge Left, Edge Right) {
		Right.setBegin(Left.getEnd());
		if (Left.getThro() != ' ') {
			q.push(Left);
		}
		if (Right.getThro() != ' ') {
			q.push(Right);
		}
		
		
		Edge ed(Left.getBegin(), ' ', Right.getEnd());
		//Edge ed(Left.getBegin(), Left.getThro() + '.' + Right.getThro(), Right.getEnd());
		return ed;
	}

	//处理 a*
	Edge Self(Edge edge) {
		Edge ed1(edge.getBegin(), '#', edge.getEnd());
		Edge ed2(edge.getEnd(), '#', edge.getBegin());
		this->q.push(ed1);
		this->q.push(ed2);
		/*Edge ed1(to_string(Aflag), '#', edge.getBegin());
		Edge ed2(edge.getEnd(), '#', to_string(Aflag + 1));
		Edge ed3(edge.getEnd(), '#', edge.getBegin());
		Edge ed4(to_string(Aflag), '#', to_string(Aflag+1));
		this->q.push(ed1);
		this->q.push(ed2);
		this->q.push(ed3);
		this->q.push(ed4);*/

		if (edge.getThro()!=' ') {
			this->q.push(edge);
		}
		
		Aflag = Aflag + 2;
		Edge ed(to_string(Aflag), ' ', to_string(Aflag + 1));
		return ed;
	}

	void PolishTypeToNFA(string s) {
		stack<Edge> st;
		Aflag = 0;//a用于标记NFA中的状态,例如:A0、A1
		for (int i = 0; i < s.length(); i++) {
			char ch = s[i];

			if (this->isletter(ch)) {
				Edge ed(to_string(Aflag), ch, to_string(Aflag + 1));
				Aflag = Aflag + 2;
				st.push(ed);
			}
			else if (ch == '*') {
				Edge ed = st.top();
				st.pop();
				st.push(this->Self(ed));
			}
			else if (ch == '.') {
				Edge ed1 = st.top();
				st.pop();
				Edge ed2 = st.top();
				st.pop();
				Edge ed = Join(ed2, ed1);
				st.push(ed);
			}
			else if (ch == '|') {
				Edge ed1 = st.top();
				st.pop();
				Edge ed2 = st.top();
				st.pop();
				st.push(this->Unite(ed1, ed2));
			}
		}

		while (!this->q.empty())
		{
			Edge e = q.front();
			cout << endl << "开始结点为:" << e.getBegin() << "     转换过程为:" << e.getThro() << "     结束结点为:" << e.getEnd() ;
			q.pop();
		}

	}

private:
	queue<Edge> q;
};



RexpToNFA.cpp

#include <iostream>
#include <cmath>
#include <string>
#include <stack>
#include <queue>
#include "RexpToNFA.h"
using namespace std;
int main() {
	RTN rt;
	cout << "有效的正规式的字母表∑={a-z,A-Z},辅助字母表∑={'*','|','(',')','.'}" << endl;
	cout << "'*'   表示闭包"<< endl <<"'|'   表示联合" << endl << "'.'   表示连接" << endl;
	while (1) {
		
		cout << "\n请输入一个正规式:";
		string s = "";
		cin >> s;
		if (rt.islegal(s)) {
			cout << "输入的正规式合法!"<<endl;
			string ss = rt.turnToConnect(s);
			cout << "补缺省略的连接符号'.'之后为:" <<ss << endl;
			cout << "对应的后缀表达式(逆波兰式)为:" <<rt.ReversePolishType(ss);
			string rs = rt.ReversePolishType(ss);
			rt.PolishTypeToNFA(rs);
		}
		else {
			cout << "输入的正规式不合法!"<<endl<<"请重新输入:";
		}
	}

}
  • 10
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值