Answers:
一个主要区别是ANTLR生成LL(*)解析器,而YACC和Bison都生成LALR解析器。这是许多应用程序的重要区别,最明显的是运算符:
expr ::= expr '+' expr
| expr '-' expr
| '(' expr ')'
| NUM ;
ANTLR完全无法按原样处理此语法。要使用ANTLR(或任何其他LL解析器生成器),您需要将此语法转换为非左递归形式。但是,Bison对此语法没有任何问题。您需要将'+'和'-'声明为左关联运算符,但这并不是左递归严格要求的。一个更好的例子可能是调度:
expr ::= expr '.' ID '(' actuals ')' ;
actuals ::= actuals ',' expr | expr ;
请注意,expr
和actuals
规则都是左递归的。生成代码时,这会产生效率更高的AST,因为它避免了对多个寄存器的需要和不必要的溢出(左倾斜的树可以折叠而右倾斜的树不能折叠)。
就个人喜好而言,我认为LALR语法更容易构建和调试。缺点是您必须处理一些隐秘的错误,例如移位减少和(可怕的)减少减少。这些是Bison在生成解析器时捕获的错误,因此它不会影响最终用户的体验,但是可以使开发过程更加有趣。正是由于这个原因,通常认为ANTLR比YACC / Bison更易于使用。
YACC / Bison和ANTLR之间最大的区别是这些工具可以处理的语法类型。YACC / Bison处理LALR语法,ANTLR处理LL语法。
通常,长期使用LALR语法的人会发现处理LL语法更加困难,反之亦然。这并不意味着语法或工具天生就更难使用。您发现更易于使用的工具主要取决于对语法类型的了解。
就优势而言,在某些方面LALR语法比LL语法具有优势,而在其他方面LL语法比LALR语法具有优势。
YACC / Bison生成表驱动的解析器,这意味着“处理逻辑”包含在解析器程序的数据中,而不是包含在解析器的代码中。值得一提的是,即使是一种非常复杂的语言的解析器,其代码足迹也相对较小。在1960年代和1970年代,硬件非常有限,这一点更为重要。表驱动的解析器生成器可以追溯到这个时代,那时的主要代码占用空间很小。
ANTLR生成递归下降解析器,这意味着“处理逻辑”包含在解析器的代码中,因为语法的每个生成规则都由解析器的代码中的一个函数表示。值得一提的是,通过阅读其代码,可以更轻松地了解解析器的功能。同样,递归下降解析器通常比表驱动解析器更快。但是,对于非常复杂的语言,代码占用空间将更大。这在1960年代和1970年代是一个问题。那时,由于硬件限制,仅以这种方式实现了相对较小的语言(例如Pascal)。
ANTLR生成的解析器通常在10.000行代码及附近。手写递归下降解析器通常位于同一球场。沃思的Oberon编译器也许是最紧凑的编译器,它包含大约4000行代码,包括代码生成,但是Oberon是一种非常紧凑的语言,只有大约40条生产规则。
就像有人已经指出的那样,ANTLR的一大优势是图形化IDE工具,称为ANTLRworks。这是一个完整的语法和语言设计实验室。当您键入语法规则时,它将可视化您的语法规则,如果发现任何冲突,它将以图形方式向您显示什么是冲突以及导致冲突的原因。它甚至可以自动重构和解决冲突,例如左递归。一旦有了无冲突的语法,就可以让ANTLRworks解析您的语言的输入文件,并为您构建一个解析树和AST,并在IDE中以图形方式显示该树。这是一个很大的优势,因为它可以节省您许多时间:在开始编码之前,您将在语言设计中发现概念错误!我没有找到用于LALR语法的任何此类工具,似乎没有任何此类工具。
即使对于不希望生成解析器但要手动编码的人来说,ANTLRworks也是用于语言设计/原型制作的出色工具。可能是最好的此类工具。不幸的是,如果您要构建LALR解析器,那对您没有帮助。仅仅为了利用ANTLRworks而从LALR切换到LL可能是值得的,但是对于某些人来说,切换语法类型可能是非常痛苦的经历。换句话说:YMMV。
ANTLR有几个优点:
我的.02 $
ANTRL的另一个优点是您可以使用ANTLRWORKS,尽管我不能说这是严格的优点,因为其他生成器也可能有类似的工具。
Bison / Flex内存使用量通常约为1 MB。与antlr对比,假设它要解析的文件中的每个令牌都使用512字节的内存。如果有400万个令牌,则您在32位系统上的虚拟内存不足。
如果您要解析的文件很大,则antlr可能会用完内存,因此,如果您只想解析配置文件,那将是一个可行的解决方案。否则,如果要分析包含大量数据的文件,请尝试使用Bison。