Home | History | Annotate | Download | only in antlr3
      1 """ANTLR3 exception hierarchy"""
      2 
      3 # begin[licence]
      4 #
      5 # [The "BSD licence"]
      6 # Copyright (c) 2005-2012 Terence Parr
      7 # All rights reserved.
      8 #
      9 # Redistribution and use in source and binary forms, with or without
     10 # modification, are permitted provided that the following conditions
     11 # are met:
     12 # 1. Redistributions of source code must retain the above copyright
     13 #    notice, this list of conditions and the following disclaimer.
     14 # 2. Redistributions in binary form must reproduce the above copyright
     15 #    notice, this list of conditions and the following disclaimer in the
     16 #    documentation and/or other materials provided with the distribution.
     17 # 3. The name of the author may not be used to endorse or promote products
     18 #    derived from this software without specific prior written permission.
     19 #
     20 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     21 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     22 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     23 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     24 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     25 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     29 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 #
     31 # end[licence]
     32 
     33 from .constants import INVALID_TOKEN_TYPE
     34 
     35 
     36 class BacktrackingFailed(Exception):
     37     """@brief Raised to signal failed backtrack attempt"""
     38 
     39     pass
     40 
     41 
     42 class RecognitionException(Exception):
     43     """@brief The root of the ANTLR exception hierarchy.
     44 
     45     To avoid English-only error messages and to generally make things
     46     as flexible as possible, these exceptions are not created with strings,
     47     but rather the information necessary to generate an error.  Then
     48     the various reporting methods in Parser and Lexer can be overridden
     49     to generate a localized error message.  For example, MismatchedToken
     50     exceptions are built with the expected token type.
     51     So, don't expect getMessage() to return anything.
     52 
     53     Note that as of Java 1.4, you can access the stack trace, which means
     54     that you can compute the complete trace of rules from the start symbol.
     55     This gives you considerable context information with which to generate
     56     useful error messages.
     57 
     58     ANTLR generates code that throws exceptions upon recognition error and
     59     also generates code to catch these exceptions in each rule.  If you
     60     want to quit upon first error, you can turn off the automatic error
     61     handling mechanism using rulecatch action, but you still need to
     62     override methods mismatch and recoverFromMismatchSet.
     63     
     64     In general, the recognition exceptions can track where in a grammar a
     65     problem occurred and/or what was the expected input.  While the parser
     66     knows its state (such as current input symbol and line info) that
     67     state can change before the exception is reported so current token index
     68     is computed and stored at exception time.  From this info, you can
     69     perhaps print an entire line of input not just a single token, for example.
     70     Better to just say the recognizer had a problem and then let the parser
     71     figure out a fancy report.
     72     
     73     """
     74 
     75     def __init__(self, input=None):
     76         super().__init__()
     77 
     78         # What input stream did the error occur in?
     79         self.input = None
     80 
     81         # What is index of token/char were we looking at when the error
     82         # occurred?
     83         self.index = None
     84 
     85         # The current Token when an error occurred.  Since not all streams
     86         # can retrieve the ith Token, we have to track the Token object.
     87         # For parsers.  Even when it's a tree parser, token might be set.
     88         self.token = None
     89 
     90         # If this is a tree parser exception, node is set to the node with
     91         # the problem.
     92         self.node = None
     93 
     94         # The current char when an error occurred. For lexers.
     95         self.c = None
     96 
     97         # Track the line at which the error occurred in case this is
     98         # generated from a lexer.  We need to track this since the
     99         # unexpected char doesn't carry the line info.
    100         self.line = None
    101 
    102         self.charPositionInLine = None
    103 
    104         # If you are parsing a tree node stream, you will encounter som
    105         # imaginary nodes w/o line/col info.  We now search backwards looking
    106         # for most recent token with line/col info, but notify getErrorHeader()
    107         # that info is approximate.
    108         self.approximateLineInfo = False
    109 
    110         
    111         if input:
    112             self.input = input
    113             self.index = input.index()
    114 
    115             # late import to avoid cyclic dependencies
    116             from .streams import TokenStream, CharStream
    117             from .tree import TreeNodeStream
    118 
    119             if isinstance(self.input, TokenStream):
    120                 self.token = self.input.LT(1)
    121                 self.line = self.token.line
    122                 self.charPositionInLine = self.token.charPositionInLine
    123 
    124             if isinstance(self.input, TreeNodeStream):
    125                 self.extractInformationFromTreeNodeStream(self.input)
    126 
    127             else:
    128                 if isinstance(self.input, CharStream):
    129                     self.c = self.input.LT(1)
    130                     self.line = self.input.line
    131                     self.charPositionInLine = self.input.charPositionInLine
    132 
    133                 else:
    134                     self.c = self.input.LA(1)
    135 
    136     def extractInformationFromTreeNodeStream(self, nodes):
    137         from .tree import Tree, CommonTree
    138         from .tokens import CommonToken
    139         
    140         self.node = nodes.LT(1)
    141         adaptor = nodes.adaptor
    142         payload = adaptor.getToken(self.node)
    143         if payload:
    144             self.token = payload
    145             if payload.line <= 0:
    146                 # imaginary node; no line/pos info; scan backwards
    147                 i = -1
    148                 priorNode = nodes.LT(i)
    149                 while priorNode:
    150                     priorPayload = adaptor.getToken(priorNode)
    151                     if priorPayload and priorPayload.line > 0:
    152                         # we found the most recent real line / pos info
    153                         self.line = priorPayload.line
    154                         self.charPositionInLine = priorPayload.charPositionInLine
    155                         self.approximateLineInfo = True
    156                         break
    157                     
    158                     i -= 1
    159                     priorNode = nodes.LT(i)
    160                     
    161             else: # node created from real token
    162                 self.line = payload.line
    163                 self.charPositionInLine = payload.charPositionInLine
    164                 
    165         elif isinstance(self.node, Tree):
    166             self.line = self.node.line
    167             self.charPositionInLine = self.node.charPositionInLine
    168             if isinstance(self.node, CommonTree):
    169                 self.token = self.node.token
    170 
    171         else:
    172             type = adaptor.getType(self.node)
    173             text = adaptor.getText(self.node)
    174             self.token = CommonToken(type=type, text=text)
    175 
    176      
    177     def getUnexpectedType(self):
    178         """Return the token type or char of the unexpected input element"""
    179 
    180         from .streams import TokenStream
    181         from .tree import TreeNodeStream
    182 
    183         if isinstance(self.input, TokenStream):
    184             return self.token.type
    185 
    186         elif isinstance(self.input, TreeNodeStream):
    187             adaptor = self.input.treeAdaptor
    188             return adaptor.getType(self.node)
    189 
    190         else:
    191             return self.c
    192 
    193     unexpectedType = property(getUnexpectedType)
    194     
    195 
    196 class MismatchedTokenException(RecognitionException):
    197     """@brief A mismatched char or Token or tree node."""
    198     
    199     def __init__(self, expecting, input):
    200         super().__init__(input)
    201         self.expecting = expecting
    202         
    203 
    204     def __str__(self):
    205         return "MismatchedTokenException({!r}!={!r})".format(
    206             self.getUnexpectedType(), self.expecting
    207             )
    208     __repr__ = __str__
    209 
    210 
    211 class UnwantedTokenException(MismatchedTokenException):
    212     """An extra token while parsing a TokenStream"""
    213 
    214     def getUnexpectedToken(self):
    215         return self.token
    216 
    217 
    218     def __str__(self):
    219         exp = ", expected {}".format(self.expecting)
    220         if self.expecting == INVALID_TOKEN_TYPE:
    221             exp = ""
    222 
    223         if not self.token:
    224             return "UnwantedTokenException(found={}{})".format(None, exp)
    225 
    226         return "UnwantedTokenException(found={}{})".format(self.token.text, exp)
    227     __repr__ = __str__
    228 
    229 
    230 class MissingTokenException(MismatchedTokenException):
    231     """
    232     We were expecting a token but it's not found.  The current token
    233     is actually what we wanted next.
    234     """
    235 
    236     def __init__(self, expecting, input, inserted):
    237         super().__init__(expecting, input)
    238 
    239         self.inserted = inserted
    240 
    241 
    242     def getMissingType(self):
    243         return self.expecting
    244 
    245 
    246     def __str__(self):
    247         if self.token:
    248             if self.inserted:
    249                 return "MissingTokenException(inserted {!r} at {!r})".format(
    250                     self.inserted, self.token.text)
    251 
    252             return "MissingTokenException(at {!r})".format(self.token.text)
    253 
    254         return "MissingTokenException"
    255     __repr__ = __str__
    256 
    257 
    258 class MismatchedRangeException(RecognitionException):
    259     """@brief The next token does not match a range of expected types."""
    260 
    261     def __init__(self, a, b, input):
    262         super().__init__(input)
    263 
    264         self.a = a
    265         self.b = b
    266         
    267 
    268     def __str__(self):
    269         return "MismatchedRangeException({!r} not in [{!r}..{!r}])".format(
    270             self.getUnexpectedType(), self.a, self.b
    271             )
    272     __repr__ = __str__
    273     
    274 
    275 class MismatchedSetException(RecognitionException):
    276     """@brief The next token does not match a set of expected types."""
    277 
    278     def __init__(self, expecting, input):
    279         super().__init__(input)
    280 
    281         self.expecting = expecting
    282         
    283 
    284     def __str__(self):
    285         return "MismatchedSetException({!r} not in {!r})".format(
    286             self.getUnexpectedType(), self.expecting
    287             )
    288     __repr__ = __str__
    289 
    290 
    291 class MismatchedNotSetException(MismatchedSetException):
    292     """@brief Used for remote debugger deserialization"""
    293     
    294     def __str__(self):
    295         return "MismatchedNotSetException({!r}!={!r})".format(
    296             self.getUnexpectedType(), self.expecting
    297             )
    298     __repr__ = __str__
    299 
    300 
    301 class NoViableAltException(RecognitionException):
    302     """@brief Unable to decide which alternative to choose."""
    303 
    304     def __init__(
    305         self, grammarDecisionDescription, decisionNumber, stateNumber, input
    306         ):
    307         super().__init__(input)
    308 
    309         self.grammarDecisionDescription = grammarDecisionDescription
    310         self.decisionNumber = decisionNumber
    311         self.stateNumber = stateNumber
    312 
    313 
    314     def __str__(self):
    315         return "NoViableAltException({!r}!=[{!r}])".format(
    316             self.unexpectedType, self.grammarDecisionDescription
    317             )
    318     __repr__ = __str__
    319     
    320 
    321 class EarlyExitException(RecognitionException):
    322     """@brief The recognizer did not match anything for a (..)+ loop."""
    323 
    324     def __init__(self, decisionNumber, input):
    325         super().__init__(input)
    326 
    327         self.decisionNumber = decisionNumber
    328 
    329 
    330 class FailedPredicateException(RecognitionException):
    331     """@brief A semantic predicate failed during validation.
    332 
    333     Validation of predicates
    334     occurs when normally parsing the alternative just like matching a token.
    335     Disambiguating predicate evaluation occurs when we hoist a predicate into
    336     a prediction decision.
    337     """
    338 
    339     def __init__(self, input, ruleName, predicateText):
    340         super().__init__(input)
    341         
    342         self.ruleName = ruleName
    343         self.predicateText = predicateText
    344 
    345 
    346     def __str__(self):
    347         return "FailedPredicateException({},{{{}}}?)".format(
    348             self.ruleName, self.predicateText)
    349     __repr__ = __str__
    350     
    351 
    352 class MismatchedTreeNodeException(RecognitionException):
    353     """@brief The next tree mode does not match the expected type."""
    354 
    355     def __init__(self, expecting, input):
    356         super().__init__(input)
    357         
    358         self.expecting = expecting
    359 
    360     def __str__(self):
    361         return "MismatchedTreeNodeException({!r}!={!r})".format(
    362             self.getUnexpectedType(), self.expecting
    363             )
    364     __repr__ = __str__
    365