【编译原理】【实验】THOMPSON 算法的实现

一、实验目的

掌握将正规表达式转换为NFA的方法和过程。

二、实验要求、内容

1.输入一个正规表达式,输出一个接受同一语言的NFA;
2.采用任意语言,实现该算法;
3编制测试程序;
4.调试程序。

三、实验设备

计算机、Windows 7操作系统、Eclipse 2019-12 程序集成环境、JDK 12。

四、实验原理(或程序框图)及步骤

对于给定的以中缀表达式记录的正规表达式,需要将其转换为后缀表达式以便于程序处理,依次自左至右扫描整个中缀表达式,建立一个表用于存放转换后的后缀表达式,程序实现时为方便插入操作,可以使用链表。此外还需建立一个栈用于存储扫描时遇到的运算符。对于碰到的运算符,将其直接存入后缀表达式表。

在处理正规表达式时,对于所有的运算符,如果是左括号则压入栈操作符栈,如是右括号则依次在操作符栈中弹栈直到遇到左括号,将所有弹栈出的运算符依次加入后缀表达式表中,对于操作符,如果当前操作符的优先级低于操作服栈的栈顶元素,则弹栈并将栈顶元素加入结果列表,随后将当前操作符加入操作符栈。如果当前操作符的优先级高于栈顶元素,则压栈。扫描完成后如果操作符栈还有其他元素,则将所有元素依次弹栈后加入结果数组。

处理后得到的NFA可以用状态转换图和状态转换表描述,所以可以直接使用实验一中的State类。为方便构建State类,在扫描中缀表达式的时候,还需要构建字母表。即扫描中缀表达式前还需建立一个链表,如果扫描到的字符不在字母表中,则将其加入字母表。由于State类将ε也作为一个字符处理,所以如果给出的正规表达式中不包含ε,需要将其加入字母表中。

生成后缀表达式的流程图如图4-1所示,首先读入待扫描的正规表达式,然后判断是高优先级操作符还是操作数,存入对应的栈或链表中。读取完成后将操作符栈中的所有元素存入操作数链表。在得到后缀表达式后,即可以依据Thompson算法计算NFA。其中,由于Thompson算法中对ε和普通字符的处理相同,所以上述将ε视作字符的处理方式可行。分析Thompson算法可知,其所有运算符在处理时并非直接操作对应字符,而是根据对字符(包含ε)的规则将对应字符转换为一系列状态组后在进行操作。亦即所有运算符的操作对象都是基本字符转换得到的状态组,所以在处理得到后缀表达式后,并不能直接对后缀表达式中的字符进行计算,还需将其转换为相关的状态组后,再进行相关操作。所有状态组中的基本状态可复用实验一中的State类进行描述,因而可考虑设计基于State 的设计组类或数组对状态组加以描述。但相应方法在实现和操作过程中过于繁琐,故不采用上述方法,而依下述方法设置特殊的状态类用以区分不同的状态组。

图4-1 生成后缀表达式的流程图

分析Thompson算法的处理规则可知,所有运算在处理时只涉及状态组头尾的两个状态,处理后的结果是包含新的开始和结束状态的状态组,所以可以将后缀表达式中的所有字符转换为状态组,并将其按字符顺序存放在一个链表中,每个状态组的开始状态为start、结束状态为end,可以此区分不同字符串转换得到的状态组。则对于每一个字符串x转换得到的状态组X,所有X中均包含开始状态start、结束状态end和其他状态,如图4-1所示,其中白色部分是普通的状态组,灰色部分是用于标识不同状态组的开始和结束状态组。

图4-2 原始状态组示意图

在扫描原始后缀表达式时,如果遇到字符,则将一个状态组加入结果链表中。处理所有状态组时的顺序依据原始后缀表达式的顺序,生成一个空链表用于存放最终得到的结果,其链表头尾不可改变的START状态,用于标记最终得到的NFA的开始状态,其示意图如图4-2所示。

图4-3 包含两个状态组的结果链表示意图

对于生成和处理正确的后缀表达式,在遇到闭包运算时链表中应该仅能找到一组状态组,在遇到|运算时应仅有两组状态组。对于闭包运算和|运算,Thompson算法中的转换规则均在状态组的头尾加上新的开始状态start’和结束状态end’形成新的状态组X’,且在添加和处理时只涉及start、end、start’、end’几个状态。对此两种运算,均需要生成新的开始start’和结束状态end’分别加入链表的前端(START后一个位置)和尾端,找到原有的start和end修改其所记录的ε边信息,并改变其名称使其变成普通状态,即可完成操作。将字符状态组加入结果链表的示意图如图4-2所示,链表头为不可改变的最终开始状态START,其后的状态组以start和end区分。闭包和|运算的示意图如图4-3所示,加入新的开始和结束状态start’和end’后,找到待处理的状态组的头尾,将其修改并变为普通状态。

图4-4 闭包和|运算处理状态组的示意图

同样,在遇到连接运算时,结果链表中应仅包含两个状态组。对于连接运算,处理时只涉及其要连接的两个基本状态组X、Y中的开始和结束状态。修改第一个状态组的end状态和第二个状态组的start状态中记录的ε边信息并改变其名称,使其变成普通状态,即可完成操作。连接运算示意图如图4-4所示,不加入新的开始和结束状态,处理第一个状态组的end状态和第二个状态组的start状态,则第一个状态组的start状态和第二个状态组的end状态作为新的状态组的开始和结束状态。

图4-5 连接运算的示意图

上述相关处理的流程图如图4-6所示。

图4-6 后缀表达式计算流程图

五、程序源代码

生成后缀表达式的代码如下所示,其中,queue用于存放计算后得到的后缀表达式,stack用于存放过程中的操作符,list用于生成字母表。

		Queue<Character> queue = new LinkedList<>();
		Stack<Character> stack = new Stack<>();
		LinkedList<String> list = new LinkedList<>();
		for (int i = 0; i < normal.length(); i++) {
   
			char m = normal.charAt(i);
			if (m != '(' && m != ')' && m != '*' && m != '|' && m != '.' && m != '+') {
   
				if (!list.contains("" + m))
					list.add("" + normal.charAt(i));
				queue.add(m);
			}</
  • 7
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值