LALR语法分析器

✍ dations ◷ 2024-12-22 20:31:12 #编译原理,算法

在计算机科学中,LALR分析器是一种规范LR分析方法(英语:Canonical_LR_parser)的简化形式。它可以对上下无关文法进行语法分析。LALR即“Look-Ahead LR”。其中,Look-Ahead为“向前看”,L代表对输入进行从左到右的检查,R代表反向构造出最右推导序列。LALR分析器可以根据一种程序设计语言的正式语法的产生式(英语:Production_(computer_science))而对一段文本程序输入进行语法分析,从而在语法层面上判断输入程序是否合法。实际应用中的LALR分析器并不是由人手工写成的,而是由类似于yacc和GNU Bison之类的LALR语法分析器生成工具(英语:LALR_parser_generator)构成。由机器自动生成的代码相比较于程序员手工的代码,拥有更好的运行效率而且减少了程序员的工作量。

1965年,Donald Knuth发明了LR分析器。LR分析器可以在线性时间里分析一个确定的上下文无关文法的输入。但是,最右推导技术所需的分析表需要一个巨大的内存空间,所以在那个内存空间紧缺的年代,LR分析技术被认为是不可行的。为了解决这个缺点,在1969年,Frank DeRemer提出了两种LR分析方法的简化版,即LALR分析器和SLR分析器(英语:Simple_LR_parser)。这两种方法所需的内存空间较LR分析法减少了许多。即使在后来的1977年,LR分析器的空间优化方式被提出,但是其空间效率依然比不过LALR这种简化版本。1979年,Frank DeRemer和Tom Pennello宣布了对于LALR分析器的新的优化方法,这再一次提高了LALR分析器的空间使用效率 。

通常来说,LALR分析器是指LALR(1)分析器。其中(1)代表了向前看一个字符,分析器可以根据这前面的一个字符确定分析时使用的产生式。相类似的,还有向前看两个字符的LALR(2)分析器、甚至向前看k个字符的LALR()分析器,但是实际使用中很少使用这类分析器。LALR分析方法基于LR(0)分析法演化而来,因此对于一个LALR()分析法可以看成LA()LR(0)分析法。实际上考虑到LR分析法,有两种参数存在的LA()LR()分析法族。对于所有的和的组合,都可以由LR(+)分析法导出。但是这种观点没有任何的实际意义。相比较与其他的LR分析器,LALR分析器在一次简单的对输入流进行从左到右扫描时,可以更直接的根据向前看的那个字符确定一个从下至上的分析方法。这些是归功于LALR分析器不需要回溯。作为一个定位于向前看的语法分析器,LALR(1)即为最常用的形式。

由于LALR分析器采用了最右推导而不是采用最左推导,因此,理解LALR分析器的工作方法变得十分困难。而这导致了手动构造一个LALR分析器是一个消耗巨大而且费时的工作。而且当出现语法错误时,LALR分析器可能并不会马上报错,而是执行几次归约动作。无论如何,任意的LR()分析器中,由于要在出错时枚举每一个可能的字符, 让错误恢复这项工作变得十分繁琐。由于这个原因,在一些时候递归下降分析器比LALR分析器更实用。由于其较低的语法分析功能,一个递归下降分析器需要更多的手写代码。但是为一个递归下降分析器编写代码并不像LALR分析器那样的困难,这是因为递归下降分析器使用了最左推导。一个值得注意的例子就是Gnu Compiler Collection中的C和C++语言的语法分析器。其中语法分析器起始是LALR分析器,但是之后却被改写成递归下降分析器。

严格的来说,LALR(1)分析器的功能比LR(1)分析器要弱一些;但是却比SLR(1)分析器强。由LALR引入的对LR的简化在于存在相同核心的项集;但是在LR分析法中,下个字符是未知的。而这种简化导致了LALR的分析功能的下降:不知道下个字符导致了语法分析器不知道选择哪个产生式,从而产生了归约/归约冲突。而SLR(1)分析法采用了更多的合并,导致了相较于LALR(1)更多的额外冲突。下述是一个标准的LR(1)文法,但是并不能由LALR(1)分析器进行分析。

S -> a E c  -> a F d  -> b F c  -> b E dE -> eF -> e

在LALR分析表的构造中,有两个状态将会被合并成一个状态。而之后的下个字符将会出现歧义。这个状态如下

E -> e. {c,d}F -> e. {c,d}

对于一个LR(1)分析器,将产生两个不同的状态而不会产生冲突。但是一个LALR(1)分析器,只会产生一个状态,从而产生冲突(若下个输入字符为c或d,可以归约成E或F)。因此,上述文法对于LALR(1)是二义的。

LALR()分析器无法与LL()分析器进行比较。对于任意的和,都存在有某种LALR()文法,但该文法却不是LL()文法,反之亦然。实际上,一个给定的LL(1)文法是否能由一个LALR()分析器进行分析都是不确定的(其中 k 0 {\displaystyle k\geq 0} )。

称每一个LL(1)都是SLR(1)或者LALR(1)的说法经常是错误的;确实存在一些LL(1)文法不是LALR(1)的。但实际上,给一个LL(1)文法附加一系列的技术条件就可以是LALR(1)的;而这些条件仅仅是避免了一系列确实无用的产生式规则。所以,实际中用到的LL(1)文法通常都是LALR(1)的。

John C. Beatty. On the relationship between LL(1) and LR(1) grammars. Journal of the ACM (JACM). 1982-10-01, 29 (4): 1007–1022 . ISSN 0004-5411. doi:10.1145/322344.322350. 

相关