Class GrammarParserInterpreter.BailButConsumeErrorStrategy

  • All Implemented Interfaces:
    ANTLRErrorStrategy
    Enclosing class:
    GrammarParserInterpreter

    public static class GrammarParserInterpreter.BailButConsumeErrorStrategy
    extends DefaultErrorStrategy
    We want to stop and track the first error but we cannot bail out like BailErrorStrategy as consume() constructs trees. We make sure to create an error node during recovery with this strategy. We consume() 1 token during the "bail out of rule" mechanism in recover() and let it fall out of the rule to finish constructing trees. For recovery in line, we throw InputMismatchException to engage recover().
    • Field Detail

      • firstErrorTokenIndex

        public int firstErrorTokenIndex
    • Constructor Detail

      • BailButConsumeErrorStrategy

        public BailButConsumeErrorStrategy()
    • Method Detail

      • recoverInline

        public Token recoverInline​(Parser recognizer)
                            throws RecognitionException
        Description copied from class: DefaultErrorStrategy
        This method is called when an unexpected symbol is encountered during an inline match operation, such as Parser.match(int). If the error strategy successfully recovers from the match failure, this method returns the Token instance which should be treated as the successful result of the match.

        This method handles the consumption of any tokens - the caller should not call Parser.consume() after a successful recovery.

        Note that the calling code will not report an error if this method returns successfully. The error strategy implementation is responsible for calling Parser.notifyErrorListeners(java.lang.String) as appropriate.

        The default implementation attempts to recover from the mismatched input by using single token insertion and deletion as described below. If the recovery attempt fails, this method throws an InputMismatchException.

        EXTRA TOKEN (single token deletion)

        LA(1) is not what we are looking for. If LA(2) has the right token, however, then assume LA(1) is some extra spurious token and delete it. Then consume and return the next token (which was the LA(2) token) as the successful result of the match operation.

        This recovery strategy is implemented by DefaultErrorStrategy.singleTokenDeletion(org.antlr.v4.runtime.Parser).

        MISSING TOKEN (single token insertion)

        If current token (at LA(1)) is consistent with what could come after the expected LA(1) token, then assume the token is missing and use the parser's TokenFactory to create it on the fly. The "insertion" is performed by returning the created token as the successful result of the match operation.

        This recovery strategy is implemented by DefaultErrorStrategy.singleTokenInsertion(org.antlr.v4.runtime.Parser).

        EXAMPLE

        For example, Input i=(3; is clearly missing the ')'. When the parser returns from the nested call to expr, it will have call chain:

         stat → expr → atom
         
        and it will be trying to match the ')' at this point in the derivation:
         => ID '=' '(' INT ')' ('+' atom)* ';'
                            ^
         
        The attempt to match ')' will fail when it sees ';' and call DefaultErrorStrategy.recoverInline(org.antlr.v4.runtime.Parser). To recover, it sees that LA(1)==';' is in the set of tokens that can follow the ')' token reference in rule atom. It can assume that you forgot the ')'.
        Specified by:
        recoverInline in interface ANTLRErrorStrategy
        Overrides:
        recoverInline in class DefaultErrorStrategy
        Parameters:
        recognizer - the parser instance
        Throws:
        RecognitionException - if the error strategy was not able to recover from the unexpected input symbol
      • sync

        public void sync​(Parser recognizer)
        Description copied from class: DefaultErrorStrategy
        The default implementation of ANTLRErrorStrategy.sync(org.antlr.v4.runtime.Parser) makes sure that the current lookahead symbol is consistent with what were expecting at this point in the ATN. You can call this anytime but ANTLR only generates code to check before subrules/loops and each iteration.

        Implements Jim Idle's magic sync mechanism in closures and optional subrules. E.g.,

         a : sync ( stuff sync )* ;
         sync : {consume to what can follow sync} ;
         
        At the start of a sub rule upon error, DefaultErrorStrategy.sync(org.antlr.v4.runtime.Parser) performs single token deletion, if possible. If it can't do that, it bails on the current rule and uses the default error recovery, which consumes until the resynchronization set of the current rule.

        If the sub rule is optional ((...)?, (...)*, or block with an empty alternative), then the expected set includes what follows the subrule.

        During loop iteration, it consumes until it sees a token that can start a sub rule or what follows loop. Yes, that is pretty aggressive. We opt to stay in the loop as long as possible.

        ORIGINS

        Previous versions of ANTLR did a poor job of their recovery within loops. A single mismatch token or missing token would force the parser to bail out of the entire rules surrounding the loop. So, for rule

         classDef : 'class' ID '{' member* '}'
         
        input with an extra token between members would force the parser to consume until it found the next class definition rather than the next member definition of the current class.

        This functionality cost a little bit of effort because the parser has to compare token set at the start of the loop and at each iteration. If for some reason speed is suffering for you, you can turn off this functionality by simply overriding this method as a blank { }.

        Specified by:
        sync in interface ANTLRErrorStrategy
        Overrides:
        sync in class DefaultErrorStrategy
        Parameters:
        recognizer - the parser instance
        See Also:
        DefaultErrorStrategy.sync(org.antlr.v4.runtime.Parser)