【问题标题】:How can I simplify token prediction DFA?如何简化令牌预测 DFA?
【发布时间】:2011-11-22 23:09:02
【问题描述】:

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

我正在尝试使用 ANTLR 3 解析 Java 服务器页面。

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

在某些情况下,我可以通过破坏我的词法分析器来修复它。例如,JSP 使用 XML“名称”标记,它可以包含多种字符。我决定在我的“名称”标记中只接受 ASCII 字符,这极大地简化了一些测试,并且词法分析器允许它编译。

但是,我已经到了不能再走捷径的地步了,但 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 中似乎存在阻止它的错误。

【问题讨论】:

  • 请参阅this question 了解“代码太大”的情况,其中语法更改会完全删除specialStateTransition
  • @Gunther,啊,是的,语法中的谓词导致了这个!我完全忘记了那个问答!我会收藏的。

标签: java antlr antlr3 lexer dfa


【解决方案1】:

非常大的语法(许多不同的标记)有这个问题,不幸的是(SQL 语法也有这个问题)。

有时可以通过制定某些词法分析器规则fragments 来解决这个问题你自己,我怀疑你的情况会有所收获。但是,如果您愿意在 SO 上发布您的词法分析器语法,我或其他人可能会看到可以更改的内容。

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

编辑

正如@Gunther 在 OP 下方的评论中正确提到的,请参阅问答:Why my antlr lexer java class is "code too large"?,其中一个小更改(删除某个谓词)导致此“代码太大”错误消失。

【讨论】:

    【解决方案2】:

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

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

    我找到了一种简单的解决方法,即移动(使用 IDE 重构功能)这些字段到另一个类任意生成的名称。以这种方式仅移动一个或多个字段时总是有帮助的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-26
      • 2021-06-05
      • 1970-01-01
      • 2016-11-09
      相关资源
      最近更新 更多