Mike75
Mike75

Reputation: 524

ANTLR4 grammar: evaluation by listener/visitor or better with recursions?

I would like to evaluate this grammar with ANTLR4:

    grammar GrammarStack;

    prog: sentence+;

    sentence:
            ID 'owns' carDef  
    ;   

    carDef:
            'a' car ( 'and' 'a' car)* '.'
    ;

    car:
        type = ('Toyota'  | 'Ford' | 'Hyundai' | 'Chevrolet' | 'Opel' | 'BMW')
    ;

    COLON: ':' ;
    HASH: '#';
    SEMI: ';';
    ID: [a-zA-Z][a-zA-z0-9]+;
    WS  :   [ \t\n\r]+ -> channel(HIDDEN);
    ANY_CHAR : . ; 

And the implementation of the listener:

    import org.antlr.v4.runtime.ParserRuleContext;
    import org.antlr.v4.runtime.tree.ErrorNode;
    import org.antlr.v4.runtime.tree.TerminalNode;
    import java.util.Stack;


    public class MyGrammarStackListener extends GrammarStackBaseListener {
        Stack lifo = new Stack();

        @Override public void enterCarDef(GrammarStackParser.CarDefContext ctx) { 

        }
        @Override public void exitCarDef(GrammarStackParser.CarDefContext ctx) {
            GrammarStackParser.SentenceContext  sctx = (GrammarStackParser.SentenceContext )ctx.parent;
            System.out.println("this is the carDef for : " + sctx.ID().getText());
            for (int i=0;i<ctx.car().size();i++) {
                if (ctx.car(i)!=null) System.out.println("car no. " + (ctx.car().size()-i)       +  ": " + lifo.pop());
            }
            // here I should definitely also find out, if there are AND options

        }

        @Override public void enterCar(GrammarStackParser.CarContext ctx) {
             lifo.push(ctx.type.getText());
        }
    }

In this example the implementation of the listener is straightforward, although I need a stack to collect the variables.

But if car would be even more complicated (say some cars would have definitions of depending inforamtions), I would prefer to use recursion instead of a listener.

like

Object exec(int ruletype, Context ctx) {
    switch (ruleType) ..
      case CARDEF_ :  {
        CarStruct cs = exec(ctx.car);
      }

To say this maybe clearer: I would like to use a recursive function for evualating the rules instead of writing separate functions for each rule. Instead of storing the relevant informations in each specific function I would like to call some eval-function, which goes down the tree (as long as necessary) and gives back the information to the point, where it is needed.

Could this be implemented in ANTLR4?

I found some code for this type of recursive execution-logic in the book "Language implementation patterns", but there an AST (abstract syntax tree) is used and to me it is not obvious how to apply this to the above example (e.g. from where (or: if) the exec function can be inherited or where the AST could be accessed).

Upvotes: 1

Views: 1948

Answers (1)

Mephy
Mephy

Reputation: 2986

To simplify these operations, Antlr implements a base class called YourGrammarNameBaseVisitor, using the visitor pattern to descend into nodes of the syntax tree. The BaseVisitor have a method called Visit, which implements more-or-less the switch you have for choosing which rule should be "visited" next. There are also a VisitRuleName method for each rule in the grammar. The base implementation of these methods will simply descend into the inner rules, but these should be overriden to take some actions during the descend or change the order the rules are visited.

Note the Visitor class includes a generic parameter that is the return of each Visit method. Sometimes it is useful to put a type like Integer if you are making a very specific visitor, like a calculator grammar, but you can always set the generic parameter to be Object or Void.

In your example grammar, we could have a code similar to this:

class MyVisitor extends GrammarStackBaseVisitor<Object> {
    @Override
    public Object visitCarDef(GrammarStackParser.CarDefContext ctx) {
        List<Car> cars = new ArrayList<Car>();
        // now for each car inside carDef
        for (GrammarStackParser.CarContext carCtx : ctx.car()) {
            Car car = (Car)visitCar(carCtx); // here is the recursion!
            cars.add(car);
        }
        return cars;
    }
    @Override
    public Object visitCar(GrammarStackParser.CarContext ctx) {
        String type = car.type().getText();
        return new Car(type);
    }
}

Upvotes: 1

Related Questions