如何简化令牌预测DFA?

时间:2011-09-22 15:34:18

标签: java antlr antlr3 lexer dfa

Lexer DFA导致“代码太大”错误

我正在尝试使用ANTLR 3解析Java Server Pages。

Java对单个方法的字节代码有64k的限制,并且在编译ANTLR生成的Java源代码时,我一直遇到“代码太大”的错误。

在某些情况下,我已经能够通过妥协我的词法分析器来修复它。例如,JSP使用XML“Name”标记,该标记可以包含各种各样的字符。我决定只在我的“Name”标记中接受ASCII字符,这大大简化了lexer中的一些测试,允许它编译。<​​/ p>

然而,我已经达到了无法削减任何角落的程度,但DFA仍然过于复杂。

我该怎么办?

是否存在导致复杂DFA的常见错误?

有没有办法抑制DFA的生成,可能依赖于语义谓词或固定前瞻来帮助预测?

手工编写这个词法分析器很容易,但在我放弃ANTLR之前,我想确保我不会忽略一些明显的东西。

背景

ANTLR 3词法分析器使用DFA来决定如何标记输入。在生成的DFA中,有一个名为specialStateTransition()的方法。此方法包含switch语句,其中包含DFA中每个州的案例。在每种情况下,都有一系列if语句,每个语句从状态转换一次。每个if语句的条件测试输入字符以查看它是否与转换匹配。

这些角色测试条件可能非常复杂。它们通常具有以下形式:

int ch = … ; /* "ch" is the next character in the input stream. */
switch(s) { /* "s" is the current state. */
  …
  case 13 :
    if ((('a' <= ch) && (ch <= 'z')) || (('A' <= ch) && (ch <= 'Z')) || … )
      s = 24; /* If the character matches, move to the next state. */
    else if …

对我的词法分析器进行看似微小的改动可能会导致单个转换的几十个比较,每个状态的几个转换以及几十个状态。我认为由于我的语义谓词,一些正在考虑的状态是不可能达到的,但似乎DFA会忽略语义谓词。 (我可能会误读事情 - 这段代码肯定不是我能用手写的!)

我在Jsp2x工具中找到了一个ANTLR 2语法,但是我对它的解析树不满意,我想要刷新我的ANTLR技能,所以我想我会尝试自己编写。我正在使用ANTLRWorks,我尝试为DFA生成图形,但是ANTLRWorks中似乎存在阻止它的错误。

2 个答案:

答案 0 :(得分:4)

非常大的语法(许多不同的令牌)有这个问题,遗憾的是(SQL语法也受此影响)。

有时这可以通过使某些词法分析器规则fragments与“完整”词法分析器规则相对应来修复,这些规则产生令牌和/或重新排列字符在规则内的匹配方式,但是通过查看你的方式我已经尝试过了,我怀疑你的情况会有很多好处。但是,如果您愿意在此处发布您的词法分析器语法,我或其他人可能会看到可以更改的内容。

通常,通过将词法分类器语法分成两个或多个单独的词法分析器语法然后将它们导入一个“主”语法来解决此问题。在ANTLR术语中,这些称为复合语法。请参阅此ANTLR Wiki页面:http://www.antlr.org/wiki/display/ANTLR3/Composite+Grammars

修改

正如@Gunt在OP下面的评论中正确提到的那样,请参阅Q&amp; A:Why my antlr lexer java class is "code too large"?,其中一个小的更改(删除某个谓词)导致这个“代码太大” - 错误消失。

答案 1 :(得分:3)

嗯,实际上制作复合语法并不总是那么容易。在许多情况下this AntTask有助于解决这个问题(每次重新编译语法后都必须运行它,但这个过程并不那么无聊)。

不幸的是,即使这个神奇的脚本在一些复杂的情况下也无济于事。编译器可能会开始咒骂过多的 DFA转换(静态字符串[]字段)块。

通过移动(使用IDE重构功能)此类字段到另一个类并使用任意生成的名称,我找到了解决问题的简便方法。当以这种方式移动一个或多个场时,它总是有帮助的。