Search code examples
parsingantlrantlr4

Skipping sub-expressions in listener


Let's say I have logical expressions in this form

((a OR b) AND c) OR (d AND e = (f OR g))

I'm interested in getting a, b, c, d and ignore e, f, g (everything that has to do with assignments).

My attempt was something like this

infixOp : OR | AND ;

assignment : Identifier EQUALS expression ;

expression  :
    Identifier |
    assignment |
    expression infixOp expression | 
    LEFTBRACKET expression RIGHTBRACKET ;

Then in my listener in enterExpression() I just printed expressionContext.getTokens(identifierType). Of course, this is too much and I got all of them.

How could "skip" over an assignment expression? Could this be done in the grammar? Or it can only be done programatically?


Solution

  • What you can do is create a small listener and keep track of the fact when you enter- and exit an assignment. Then inside the enterExpression method, check if you're NOT inside an assignment AND the Identifier token has a value.

    A quick demo for the grammar:

    grammar T;
    
    parse
     : expression EOF
     ;
    
    expression
     : '(' expression ')'
     | expression 'AND' expression
     | expression 'OR' expression
     | assignment
     | Identifier
     ;
    
    
    assignment
     : Identifier '=' expression
     ;
    
    Identifier : [a-z]+;
    Space      : [ \t\r\n] -> skip;
    

    and Java class:

    public class Main {
    
        public static void main(String[] args) {
    
            TLexer lexer = new TLexer(CharStreams.fromString("((a OR b) AND c) OR (d AND e = (f OR g))"));
            TParser parser = new TParser(new CommonTokenStream(lexer));
    
            MyListener listener = new MyListener();
    
            ParseTreeWalker.DEFAULT.walk(listener, parser.parse());
    
            System.out.println(listener.ids);
        }
    }
    
    class MyListener extends TBaseListener {
    
        public List<String> ids = new ArrayList<String>();
        private boolean inAssignment  = false;
    
        @Override
        public void enterExpression(TParser.ExpressionContext ctx) {
            if (!this.inAssignment && ctx.Identifier() != null) {
                this.ids.add(ctx.Identifier().getText());
            }
        }
    
        @Override
        public void enterAssignment(TParser.AssignmentContext ctx) {
            this.inAssignment = true;
        }
    
        @Override
        public void exitAssignment(TParser.AssignmentContext ctx) {
            this.inAssignment = false;
        }
    }
    

    will print:

    [a, b, c, d]