是否可以指示 ANTLR 不要将整个文件加载到内存中?它可以一一应用规则并按顺序生成最顶层的节点列表以及读取文件吗?还有可能以某种方式删除分析的节点吗?
2 回答
是的,您可以使用:
UnbufferedCharStream
为您的字符流(传递给词法分析器)UnbufferedTokenStream
对于您的令牌流(传递给解析器)- 此令牌流实现不会区分令牌通道,因此请确保在您的词法分析器规则中使用
->skip
而不是->channel(HIDDEN)
作为不应发送到解析器的命令。
- 此令牌流实现不会区分令牌通道,因此请确保在您的词法分析器规则中使用
- 确保调用
setBuildParseTree(false)
您的解析器,否则将为整个文件创建一个巨大的解析树。
编辑一些额外的评论:
- 我付出了相当多的工作来确保以最“理智”的方式进行操作,尤其是与
UnbufferedCharStream
、、和方法相关的方面。我的目标是在不影响流释放未使用内存的能力的情况下尽可能多地保留这些方法的功能。UnbufferedTokenStream
mark
release
seek
getText
- ANTLR 4 允许真正的无限前瞻。如果您的语法需要提前查看 EOF 来做出决定,那么您将无法避免将整个输入加载到内存中。在编写语法时,您必须非常小心以避免这种情况。
Antlr.org 上的某个地方有一个 Wiki 页面,可以解决您的问题;刚才好像找不到了。
实际上,词法分析器使用标准 InputStream 接口读取数据,特别是 ANTLRInputStream.java。典型的实现是ANTLRFileStream.java,它将整个输入数据文件抢先读入内存。您需要做的是编写自己的缓冲版本 - “ANTLRBufferedFileStream.java” - 根据需要从源文件中读取。或者,只需将标准 BufferedInputStream/FileInputStream 设置为 AntlrInputStream 的数据源。
需要注意的是,Antlr4 有可能进行无限前瞻。在正常操作中合理大小的缓冲区可能不是问题。当解析器尝试错误恢复时更有可能。Antlr4 允许定制错误恢复策略,因此问题是可控的。
附加细节:
实际上,Antlr 实现了一个拉解析器。当您调用第一个解析器规则时,解析器从词法分析器请求令牌,词法分析器从输入流中请求字符数据。解析器/词法分析器接口由缓冲令牌流实现,名义上是BufferedTokenStream。
解析树只不过是令牌的树数据结构。好吧,还有很多,但不是在数据大小方面。每个标记都是一个 INT 值,通常由与标记定义匹配的输入数据流的片段支持。词法分析器本身不需要将 lex 输入字符流的完整副本保存在内存中。并且,标记文本片段可能会被归零。给定缓冲文件输入流,词法分析器的关键内存要求是输入字符流前瞻扫描。
根据您的需要,即使输入文件超过 100GB,内存中的解析树也可以很小。
为了进一步提供帮助,您需要更多地解释您在 Antlr 中尝试做什么以及定义您的最低关键内存要求的内容。这将指导可以推荐哪些其他策略。例如,如果源数据适合,您可以使用多个词法分析器/解析器运行,每次在词法分析器中选择要处理的源数据的不同部分。与文件读取和数据库写入相比,即使使用快速磁盘,Antlr 的执行也可能几乎不会被注意到。