0
votes

I am using ANTLR4 and have written lexer and parser grammar for a new language that I am designing. I'd like to create a Java POJO by parsing that language.

Language

{dept dept-name="human resources"}
   {emp name="john doe" age=23 address="123 Main St, Spring Field, CO 12345" /}
{/dept}

Java POJOs

public class Department {
  private final class name;
  private final List<Employee>
}

public class Employee {
  private final String name;
  private final int age;
  private final Address address;
}

public class Address {
  private final String streetAddress;
  private final String city;
  private final String state;
  private final int zip;
  private final int zipExt;
}

I've been able to define the grammar correctly. I was able to use the Antlr tool to generate the Visitor class. The Visitor class takes a generic type, T, and returns an instance of type T while visiting every node. I need to return an Address while visiting the Address section of the AST and an Employee while visiting the Employee section of the AST. So I am not sure what the type T should be for the Visitor implementation.

I am confused about how to go about construct the above Department POJO by implementing the Visitor given out by ANTLR.

PS: Please note that I cannot change the Department, Employee, Address classes. Also, please don't suggest using XML or JSON. I am just trying to learn how such a problem is solved in ANTLR.

Thanks!

1
If you don't have a common base object or interface, then isn't it just T=Object ?Hitobat
I think you go write your own visitor class. Thats most flexible. When I had the similar problem I wrote my own visitor class. To be exact, I used the generated visitor to check/validate the properties (for example, are the attribute names legal?). And then I wrote my own visitor class to convert the parse tree to AST. (in your case, the AST you mentioned above is the parse tree).Wang Sheng
The way to get the root of parse tree would be something like this: DLSParser.FileContext fileContext = parser.file(); Here the root rule in my project is called file so I am getting DLSParser.FileContext. Then you just recursively visit this parse tree from its root and generate your POJO along the way.Wang Sheng
@WangSheng Thanks for the reply. Do you have example that you can share? Will help me wrap my head around this.user544192
I will use an answer to post my example. its a bit difficult to format in the comments :)Wang Sheng

1 Answers

1
votes

Assuming this is the head of your parser.g4 grammar Your entire script is a file, file can be made by multiple elements. Elements can either be a page or a pageGroup. And page and pageGroup have other components.

parser grammar MyCustomParser;
file: element* EOF;
element: page | pageGroup;

Then once you run Antlr command you will get many generated files, most of them are probably named like MyCustomParser$XXXContext.class. These file represent Nodes in your parse tree. In our case we will have MyCustomParser.PageContext, MyCustomParser.PageGroupContext and so on.

One particular important node is the root node of your parse tree, MyCustomParser.FileContext. Its children will be element contexts, and element contexts' children will be page contexts or page group contexts mentioned above.

Now with this root node, all you need to do is to recursively visit all the nodes in the parse tree, starting from the root. And when you visit the nodes, you can generate your POJO based on the values stored in those nodes.

To get the root node

//....
MyCustomParser parser = new MyCustomParser(tokens);
MyCustomParser.FileContext fileContext = parser.file();

Notice we called the method file(). The method name is the same as your root rule name.

You don't have to use the generated tree visitor. It is only there to help your saves some boiler template code, because in most cases we travel this or that tree in the same way.

You mentioned about the generic type issue. Most of the time, your POJO may have a common parent class, or implement a common interface. Then you can just use the parent class or interface. But in your case, you cannot modify the POJO, so it may not be easy to make use of the generated tree.