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?

Emiel Steerneman
  • 382
  • 1
  • 5
  • 12
  • 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. – Har Aug 26 '15 at 12:05
  • 2
    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. – Emiel Steerneman Aug 26 '15 at 12:14

1 Answers1

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()
Har
  • 3,727
  • 10
  • 41
  • 75
  • 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. – Emiel Steerneman Aug 26 '15 at 14:20
  • 1
    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! – Emiel Steerneman Aug 26 '15 at 14:29
  • 2
    Great answer! Instead of accessing parser's 'private' field, you can write: parser.addErrorListener(MyErrorListener()) – Tim Stewart Nov 17 '18 at 00:31