Python 2.7 & ANTLR4 : Make ANTLR throw exceptions on invalid input
Asked Answered
S

1

7

I want to catch errors like

line 1:1 extraneous input '\r\n' expecting {':', '/',}

line 1:1 mismatched input 'Vaasje' expecting 'Tafel'

I tried wrapping my functions in try-catch but, as expected, these errors are just print statement and not exceptions. I've seen some examples of switching on errors in the .g4 file, but all the examples are for Java, and I can't seem to get it working.

Is it possible for ANTLR4 in Python to throw exceptions which I can catch?

Siouxie answered 26/8, 2015 at 11:6 Comment(2)
Hi Emiel, these are not exceptions as such, they are messages directlty from the parser which is saying that the input you have given is not valid due to 1. new lines \r\n not being accepted and 2. a keyword Tafel was not present. It is possible to write your own messages if you find them to be unfriendly but they are not generated by exceptions but by an ErrorListener.Odelle
Hi Har. I know these are not exceptions. I stated that "these errors are just print statement and not exceptions". I need the parser to change from printing errors to actually throwing exceptions, so that I can catch them.Siouxie
O
12

I have looked through the python classes and noticed that they dont have the methods that the java one has for adding and removing a error listener, this maybe a bug in ANTLR, however python being python you are allowed to modify the members without requiring a setter as such like in the following example:

I run the example by performing a : antlr4 -Dlanguage=Python2 AlmostEmpty.g4 and then by typing in main.py


AlmostEmpty.g4

grammar AlmostEmpty;

animals: (CAT | DOG | SHEEP ) EOF;

WS: [ \n\r]+ -> skip;
CAT: [cC] [aA] [tT];
DOG: [dD] [oO] [gG];
SHEEP: [sS] [hH] [eE] [pP];

main.py

from antlr4 import *
import sys
from AlmostEmptyLexer import AlmostEmptyLexer
from AlmostEmptyParser import AlmostEmptyParser
from antlr4.error.ErrorListener import ErrorListener

class MyErrorListener( ErrorListener ):

    def __init__(self):
        super(MyErrorListener, self).__init__()

    def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e):
        raise Exception("Oh no!!")

    def reportAmbiguity(self, recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs):
        raise Exception("Oh no!!")

    def reportAttemptingFullContext(self, recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs):
        raise Exception("Oh no!!")

    def reportContextSensitivity(self, recognizer, dfa, startIndex, stopIndex, prediction, configs):
        raise Exception("Oh no!!")

if __name__ == "__main__":
    inputStream = StdinStream( )
    lexer = AlmostEmptyLexer(inputStream)
    # Add your error listener to the lexer if required
    #lexer.removeErrorListeners()
    #lexer._listeners = [ MyErrorListener() ]
    stream = CommonTokenStream(lexer)
    parser = AlmostEmptyParser(stream)
    # As mentioned in the comments by @Tim Stewart instead of doing this:
    # parser._listeners = [ MyErrorListener() ]
    # you can do this:
    parser.addErrorListener( MyErrorListener() )
    tree = parser.animals()
Odelle answered 26/8, 2015 at 13:52 Comment(3)
Hi Har. Thanks for your answer, this works great! I checked the ErrorListener, and unfortunately I can't find a way to catch "mismatched input" and "extraneous input". Do you maybe have the answer to that as well? If so, I can accept your question.Siouxie
I think I found a way. I tried your approach, and I can create MyErrorStrategy(ErrorStrategy) which throws an exception on reportError(). Thanks for your help!Siouxie
Great answer! Instead of accessing parser's 'private' field, you can write: parser.addErrorListener(MyErrorListener())Cinquain

© 2022 - 2024 — McMap. All rights reserved.