NFA/DFA算法

随着计算机语言的结构越来越复杂,为了开发优秀的编译器,人们已经渐渐感到将词 法分析独立出来做研究的重要性。不过词法分析器的作用却不限于此。回想一下我们的老师刚刚开始向我们讲述程序设计的时候,总是会出一道题目:给出一个填入 了四则运算式子的字符串,写程序计算该式子的结果。除此之外,我们有时候建立了比较复杂的配置文件,譬如XML的时候,分析器首先也要对该文件进行词法分 析,把整个字符串断成了一个一个比较短小的记号(指的是具有某种属性的字符串),之后才进行结构上的分析。再者,在实现某种控制台应用程序的时候,程序需 要分析用户打进屏幕的命令。如果该命令足够复杂的话,我们也首先要对这个命令进行词法分析,之后得到的结果会大大方便进行接下去的工作。

当然,这些问题大部分已经得到了解决,而且历史上也有人做出了各种各样专门的或 者通用的工具(Lex、正则表达式引擎等)来解决这一类问题。我们在使用这种工具的时候,为了更加高效地书写配置,或者我们在某种特殊情况下需要自己制作 类似的工具,就需要了解词法分析背后的原理。本文将给出一个构造通用词法分析工具所需要的原理。由于实现的代码过长,本文将不附带实现。

究竟什么是“把一个字符串断成一些记号”呢?我们先从四则运算式子入手。一个四 则运算式子是一个字符数列,可是我们关心的对象实际上是操作符、括号和数字。于是此法分析的作用就是把一个字符串断开成我们关心的带有属性的记号。举个例 子:(11+22)*(33+44)是一个合法的四则运算式子,如果输入是(左括号,”(“) (数字,”11”) (一级操作符,”+”) (数字,”22”) (右括号,”)”) (二级操作符,”*”) (左括号,”(“) (数字,”33”) (一级操作符,”+”) (数字,”44”) (右括号,”)”)的话,我们在检查结构的时候只需要关心这个记号的属性(也就是左括号、右括号、数字、操作符等)就行了,具体计算的时候才需要关心这个 记号实际上的内容。如果式子里边有空格的话,我们也仅仅需要把空格当成是一种记号类型,在词法分析得出结果之后,将具有空格属性的记号丢弃掉就可以了,接 下去的步骤不需变化。

但需要注意的是,词法分析得到的结果是没有层次结构的,所有的记号都是等价的对象。我们在计算表达式的时候把+和*看成了不同层次的操作符,类似的结构是具有嵌套的层次的。词法分析不能得出嵌套层次结构的信息,最多只能得到关于重复结构的信息。

2、正则表达式

我们现在需要寻找一种可以描述记号类型的工具,在此之前我们首先研究一下常见的记号的结构。为了表示出具有某种共性的字符串的集合,我们需要书写出一些能代表字符串集合的规则。这个集合中的所有成员都将被认为是一种特定类型的记号。

首先,规则可以把一个特定的字符或者是空字符串认为是一种类型的记号的全部。 上文所说到的四则运算式子的例子,“左括号”这种类型的记号就仅仅对应着字符”(“,其他的字符或者字符串都不可能是“左括号”这个类型的记号。

其次,规则可以进行串联。 串联的意思是这样 的,我们可以让一个字符串的前缀符合某一个指定的规则,剩下的部分的前缀符合第二个规则,剩下的部分的前缀符合第三个规则等等,一直到最后一个部分的全部 要符合最后一个规则。如果我们把”function”这个字符串作为一个记号类型来处理的话,我们可以把”function”这个字符串替换成8个串联的 规则:”f”,”u”,”n”,”c”,”t”,”i”,”o”,”n”。首先,字符串”function”的前缀”f”符合规则”f”,剩下的部 分”unction”的前缀”u”符合规则”u”,等等,一直到最后一个部分”n”的全部符合规则”n”。

第三,规则可以进行并联。 并联的意思就是, 如果一个字符串符合一系列规则中的其中一个的话,我们就说这个字符串符合这一些规则的并联。于是这些规则的并联就构成了一个新的规则。一个典型的例子就是 判断一个字符串是否关键字。关键字可以是”if”,可以是”else”,可以是”while”等等。当然,一个关键字是不可能同时符合这些规则的,不过只 要一个字符串符合这些规则的其中一个的话,我们就说这个字符串是关键字。于是,关键字这个规则就是”if”、”else”、”while”等规则的并联。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzppxj.html