我正在阅读“The Definitive Antlr 4 Reference”,并了解听众和访客的工作方式。本书特别详细地解释了监听器如何与SAX解析器相关,并且在每个解析器的实现过程中调用方法时都很明显。我还可以看到,监听器非常适合将输入转换为输出,但我很欣赏一个简短的解释/示例,关于何时使用监听器以及何时使用访问者(或者它们是否应该在某些情况下使用?)。 / p>
我的目的是创建一个解释器(带有一些自定义调用的Cucumber-style / TinyBasic Interpreter),它将检查语法错误并停止执行自定义函数的错误而不恢复 - 很想看到完整的实现在antlr这样的事情 - 如果有人碰巧知道一个。
提前感谢任何建议。
答案 0 :(得分:45)
以下是我认为相关的书的引用:
侦听器和访问者机制之间的最大区别在于侦听器方法由ANTLR提供的walker对象调用,而访问者方法必须通过显式访问调用来访问其子节点。忘记在节点的子节点上调用visit()意味着不会访问这些子树。
在访客模式中,您可以指导树木行走,而在听众中,您只对树木行走者做出反应。
答案 1 :(得分:28)
如果您打算直接使用解析器输出进行解释,那么访问者是个不错的选择。您可以完全控制遍历,因此在条件中只访问一个分支,循环可以访问n次,依此类推。
如果您将输入转换为较低级别,例如虚拟机指令,这两种模式都可能有用。
您可以查看“语言实现模式”,其中介绍了基本的解释器实现。
我主要使用访客模式,因为它更灵活。
答案 2 :(得分:6)
这两种模式之间存在另一个重要区别:访问者使用调用堆栈来管理树遍历,而侦听器使用分配的显式堆栈堆由walker管理。这意味着访问者的大量输入可能会使堆栈爆炸,而听众则没有任何麻烦。
如果您的输入可能无限制,或者您可能会在调用树中深入调用访问者,则应使用监听器而不是访问者,或者至少验证解析树不是太深。一些公司'由于这个原因,编码实践不鼓励甚至完全禁止非尾递归。
答案 3 :(得分:1)
摘自本书,第120页。
如果我们需要特定于应用程序的返回值,则访问者会很好地工作,因为我们可以使用内置的Java返回值机制。如果我们不想显式调用访问者方法来访问子级,则可以切换到侦听器机制。不幸的是,这意味着放弃了使用Java方法返回值的简洁性。
这就是我使用访客的原因。