第三步,进行有限自动机的搭建操作
我们要构造一个有限自动机,而这个自动机是用一个表达式来构建的,很容易可以想到,我们完全可以把表达式耳的操作数看做一个小号的自动机(或者说是状态图),而操作符的效果是对状态图进行修饰,或者连接两个状态图,当我们执行完表达式解析后,就可以得到一个完整的表达式的状态图了。
像是算术表达式这种给定输入只有一个结果的,表示成树的形式就可以满足要求了,而正则表达式对给定输入会有多个结果(虽然通常我们只要第一个结果),也确实只能用图表示。
操作数有三种,连续字符;包含;分组提取比较。都用如下状态图表示:
注意我把所有的操作都放在边上,状态节点处不做任何行为。
每一个操作数所对应的状态图,都各有唯一的进入节点和退出节点(我们不需要关系状态图内部的情况)
'?'操作符,也即是可选操作符,它的效果如下(从操作数修饰的来):
'??'的效果类似,但是ε边的优先级较高(图中靠上的边优先级高)
类似的,还有'*'、'*?':
输出节点为新建节点,'*?'的ε边的优先级较高
以及'+'、'+?':
输出节点为新建节点,'+?'的ε边的优先级较高
隐藏的“连接”操作符:
左俩操作数1,右俩操作数2
选择操作符,"|":
上面一排为操作数1,下面一排为操作数2
分组"( )":
黑色分别表示报告分组的起止点的字符串位置
一般不分组的括号"(: )",这个完全没啥操作,不用修饰,直接返回操作数就好了
限定次数的操作符(是的,看做操作符)"{a,b}":
如此,我们就得到了生成操作数的方法,以及所有操作符的操作效果
结合这些,我们就可以通过分析表达式来生成一个完整的有限自动机了。
分析表达式的方法么,自然是很多的,算术表达式分析器稍微改改就能拿来分析之前得到的列表了,无非是新的运算符,新的运算函数(函数指针真是棒啊真是棒)、新的操作数而已,整体架构变化极小。
当然,我是用的我之前写的一个可定制的表达式解析器,那是一个模板类,支持一元运算符(前置、后置)、二元运算符(左结合、右结合)、括号(普通括号、后缀括号),只要把这些运算符和它们对应的处理函数注册进去就行了。(还可以注册操作数生成函数、操作符生成函数、销毁函数等一系列函数)
所以具体写法就不多说了,拿个算术表达式解析器慢慢改就是。
下一帖,第四步。