Search / Analytics / Distributed Systems / Machine Learning / DSLs

Antlr4 - Visitor vs Listener Pattern

Oct19th, 201712:00 am

In previous post, we saw how to create and parse DSL with antlr4. In this post we will compare the two tree walking mechanism provided by the library - Listener vs Visitor. Both approaches have their own advantages, and choice of preferred method depends on what you are using antlr for.

Lets take a look at generated methods in Listener and Visitor interfaces, for grammar from previous post

Listener methods are called automatically by the ANTLR provided walker object, whereas visitor methods must walk their children with explicit visit calls. Forgetting to invoke visit() on a node’s children means those subtrees don’t get visited

Listener methods can’t return a value, whereas visitor methods can return any custom type. With listener, you will have to use mutable variables to store values, whereas with visitor there is no such need.

Listener uses an explicit stack allocated on the heap, whereas visitor uses call stack to manage tree traversals. This might lead to StackOverFlow exceptions while using visitor on deeply nested ASTs

In previous post we used listener. Lets see a equivalent visitor implementation for same usecase:

First thing which we need to do is identify and define types which will be returned by each vistor method which we will override.

At line 10, We are explicitly calling visit on expr rule and it returns back the result of expression evaluation.

Both approaces have their advantages depending on the usecase. If you want to have full control over the traversal, dont want any mutability and your grammar rules won’t lead to deeply nested trees, then its preferred to go with visitor.

Here’s a figure showing call sequence for listener vs visitor

Listener

Visitor

Good thing about listener pattern is that its all automatic and even if you don’t write any parse tree walker, then also it will figure out and trigger the enter and exit method for each rule. This is a huge benefit for translation type of usecases.

I hope this post gave you some clarity in terms of which pattern to choose while creating your DSL.