我目前正在规划语言解释器的结构,我意识到我不喜欢专门使用访问者或侦听器树遍历方法的想法。
由于这两种树遍历方法都有其优点,理想情况下,我想混合使用两种方法:
两种遍历方式之间的切换最“正确”的方法是什么?
到目前为止,我的想法如下:
也就是说,当侦听器到达节点“Foo”时,我想使用访问者更明确地处理其子节点。我能想到的一种方法是:
enterFoo(ctx)
ctx.children = []
(或同等内容)enterFoo()
返回时,解析树遍历器会发现该节点不再有子节点,并且不会不必要地遍历 Foo 的所有子节点这对我来说更明显一点。由于使用访问者时显式控制树遍历,因此切换似乎微不足道。
visitFoo()
被叫到
您似乎已经知道听众和访客是模式,有点可以切换。这是错误的。
侦听器和访问者都是允许您对规则遍历进行操作的类。当规则被“输入”或“离开”时,侦听器通过在解析过程中从解析器调用来实现此目的。不涉及解析树。
然而,访问者使用解析树来遍历每个节点并为它们调用方法。您可以重写这些方法中的任何一个来执行任何关联的工作。这与评估结果没有必然关系。您可以独立使用它。ANTLR4 为每个规则(在侦听器和访问者中)生成方法体,这使您可以轻松地仅实现您感兴趣的规则。
既然您知道监听器是在解析期间使用的,而访问者是在解析之后使用的,那么很明显您不能在它们之间切换。事实上,切换并没有多大帮助,因为两个类的作用本质上是相同的(调用遇到的规则的方法)。
如果您的问题实际上包含您想要实现的
什么,而不是如何实现,我可能可以为您提供更多信息。
如果您使用
ParseTreeWalker
,这是一个非常简单的类,它只是递归地迭代树中的节点。您可以制作自己的节点,或者扩展它以覆盖
Walk
,从而对不需要迭代的特定节点禁用它。您唯一需要决定的是如何将该信息提供给步行者,例如通过显式列出所有此类节点类型,通过将侦听器耦合到它并仅切换其状态,或者通过以某种方式从侦听器标记节点等。在任何情况下,您的侦听器都可以自由地在
Enter
中调用访问者或此类节点的
Exit
。如果在解析期间使用
AddParseListener
来调用侦听器,您仍然需要处理解析树,但您可以以相同的方式调用访问者,只需在创建后从
Exit
开始即可。