1 /*
2  * [The "BSD license"]
3  * Copyright (c) 2011 Terence Parr
4  * All rights reserved.
5  *
6  * Conversion to C#:
7  * Copyright (c) 2011 Sam Harwell, Tunnel Vision Laboratories, LLC
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 namespace Antlr.Runtime
34 {
35     using Antlr.Runtime.Tree;
36 
37     using ArgumentNullException = System.ArgumentNullException;
38     using Exception = System.Exception;
39     using NotSupportedException = System.NotSupportedException;
40     using SerializationInfo = System.Runtime.Serialization.SerializationInfo;
41     using StreamingContext = System.Runtime.Serialization.StreamingContext;
42 
43     /** <summary>The root of the ANTLR exception hierarchy.</summary>
44      *
45      *  <remarks>
46      *  To avoid English-only error messages and to generally make things
47      *  as flexible as possible, these exceptions are not created with strings,
48      *  but rather the information necessary to generate an error.  Then
49      *  the various reporting methods in Parser and Lexer can be overridden
50      *  to generate a localized error message.  For example, MismatchedToken
51      *  exceptions are built with the expected token type.
52      *  So, don't expect getMessage() to return anything.
53      *
54      *  Note that as of Java 1.4, you can access the stack trace, which means
55      *  that you can compute the complete trace of rules from the start symbol.
56      *  This gives you considerable context information with which to generate
57      *  useful error messages.
58      *
59      *  ANTLR generates code that throws exceptions upon recognition error and
60      *  also generates code to catch these exceptions in each rule.  If you
61      *  want to quit upon first error, you can turn off the automatic error
62      *  handling mechanism using rulecatch action, but you still need to
63      *  override methods mismatch and recoverFromMismatchSet.
64      *
65      *  In general, the recognition exceptions can track where in a grammar a
66      *  problem occurred and/or what was the expected input.  While the parser
67      *  knows its state (such as current input symbol and line info) that
68      *  state can change before the exception is reported so current token index
69      *  is computed and stored at exception time.  From this info, you can
70      *  perhaps print an entire line of input not just a single token, for example.
71      *  Better to just say the recognizer had a problem and then let the parser
72      *  figure out a fancy report.
73      *  </remarks>
74      */
75     [System.Serializable]
76     public class RecognitionException : Exception
77     {
78         /** <summary>What input stream did the error occur in?</summary> */
79         private IIntStream _input;
80 
81         /// <summary>
82         /// What was the lookahead index when this exception was thrown?
83         /// </summary>
84         private int _k;
85 
86         /** <summary>What is index of token/char were we looking at when the error occurred?</summary> */
87         private int _index;
88 
89         /** <summary>
90          *  The current Token when an error occurred.  Since not all streams
91          *  can retrieve the ith Token, we have to track the Token object.
92          *  For parsers.  Even when it's a tree parser, token might be set.
93          *  </summary>
94          */
95         private IToken _token;
96 
97         /** <summary>
98          *  If this is a tree parser exception, node is set to the node with
99          *  the problem.
100          *  </summary>
101          */
102         private object _node;
103 
104         /** <summary>The current char when an error occurred. For lexers.</summary> */
105         private int _c;
106 
107         /** <summary>
108          *  Track the line (1-based) at which the error occurred in case this is
109          *  generated from a lexer.  We need to track this since the
110          *  unexpected char doesn't carry the line info.
111          *  </summary>
112          */
113         private int _line;
114 
115         /// <summary>
116         /// The 0-based index into the line where the error occurred.
117         /// </summary>
118         private int _charPositionInLine;
119 
120         /** <summary>
121          *  If you are parsing a tree node stream, you will encounter som
122          *  imaginary nodes w/o line/col info.  We now search backwards looking
123          *  for most recent token with line/col info, but notify getErrorHeader()
124          *  that info is approximate.
125          *  </summary>
126          */
127         private bool _approximateLineInfo;
128 
129         /** <summary>Used for remote debugger deserialization</summary> */
RecognitionException()130         public RecognitionException()
131             : this("A recognition error occurred.", null, null)
132         {
133         }
134 
RecognitionException(IIntStream input)135         public RecognitionException(IIntStream input)
136             : this("A recognition error occurred.", input, 1, null)
137         {
138         }
139 
RecognitionException(IIntStream input, int k)140         public RecognitionException(IIntStream input, int k)
141             : this("A recognition error occurred.", input, k, null)
142         {
143         }
144 
RecognitionException(string message)145         public RecognitionException(string message)
146             : this(message, null, null)
147         {
148         }
149 
RecognitionException(string message, IIntStream input)150         public RecognitionException(string message, IIntStream input)
151             : this(message, input, 1, null)
152         {
153         }
154 
RecognitionException(string message, IIntStream input, int k)155         public RecognitionException(string message, IIntStream input, int k)
156             : this(message, input, k, null)
157         {
158         }
159 
RecognitionException(string message, Exception innerException)160         public RecognitionException(string message, Exception innerException)
161             : this(message, null, innerException)
162         {
163         }
164 
RecognitionException(string message, IIntStream input, Exception innerException)165         public RecognitionException(string message, IIntStream input, Exception innerException)
166             : this(message, input, 1, innerException)
167         {
168         }
169 
RecognitionException(string message, IIntStream input, int k, Exception innerException)170         public RecognitionException(string message, IIntStream input, int k, Exception innerException)
171             : base(message, innerException)
172         {
173             this._input = input;
174             this._k = k;
175             if (input != null)
176             {
177                 this._index = input.Index + k - 1;
178                 if (input is ITokenStream)
179                 {
180                     this._token = ((ITokenStream)input).LT(k);
181                     this._line = _token.Line;
182                     this._charPositionInLine = _token.CharPositionInLine;
183                 }
184 
185                 ITreeNodeStream tns = input as ITreeNodeStream;
186                 if (tns != null)
187                 {
188                     ExtractInformationFromTreeNodeStream(tns, k);
189                 }
190                 else
191                 {
192                     ICharStream charStream = input as ICharStream;
193                     if (charStream != null)
194                     {
195                         int mark = input.Mark();
196                         try
197                         {
198                             for (int i = 0; i < k - 1; i++)
199                                 input.Consume();
200 
201                             this._c = input.LA(1);
202                             this._line = ((ICharStream)input).Line;
203                             this._charPositionInLine = ((ICharStream)input).CharPositionInLine;
204                         }
205                         finally
206                         {
207                             input.Rewind(mark);
208                         }
209                     }
210                     else
211                     {
212                         this._c = input.LA(k);
213                     }
214                 }
215             }
216         }
217 
RecognitionException(SerializationInfo info, StreamingContext context)218         protected RecognitionException(SerializationInfo info, StreamingContext context)
219             : base(info, context)
220         {
221             if (info == null)
222                 throw new ArgumentNullException("info");
223 
224             _index = info.GetInt32("Index");
225             _c = info.GetInt32("C");
226             _line = info.GetInt32("Line");
227             _charPositionInLine = info.GetInt32("CharPositionInLine");
228             _approximateLineInfo = info.GetBoolean("ApproximateLineInfo");
229         }
230 
231         /** <summary>Return the token type or char of the unexpected input element</summary> */
232         public virtual int UnexpectedType
233         {
234             get
235             {
236                 if ( _input is ITokenStream )
237                 {
238                     return _token.Type;
239                 }
240 
241                 ITreeNodeStream treeNodeStream = _input as ITreeNodeStream;
242                 if ( treeNodeStream != null )
243                 {
244                     ITreeAdaptor adaptor = treeNodeStream.TreeAdaptor;
245                     return adaptor.GetType( _node );
246                 }
247 
248                 return _c;
249             }
250         }
251 
252         public bool ApproximateLineInfo
253         {
254             get
255             {
256                 return _approximateLineInfo;
257             }
258             protected set
259             {
260                 _approximateLineInfo = value;
261             }
262         }
263 
264         public IIntStream Input
265         {
266             get
267             {
268                 return _input;
269             }
270             protected set
271             {
272                 _input = value;
273             }
274         }
275 
276         public int Lookahead
277         {
278             get
279             {
280                 return _k;
281             }
282         }
283 
284         public IToken Token
285         {
286             get
287             {
288                 return _token;
289             }
290             set
291             {
292                 _token = value;
293             }
294         }
295 
296         public object Node
297         {
298             get
299             {
300                 return _node;
301             }
302             protected set
303             {
304                 _node = value;
305             }
306         }
307 
308         public int Character
309         {
310             get
311             {
312                 return _c;
313             }
314             protected set
315             {
316                 _c = value;
317             }
318         }
319 
320         public int Index
321         {
322             get
323             {
324                 return _index;
325             }
326             protected set
327             {
328                 _index = value;
329             }
330         }
331 
332         public int Line
333         {
334             get
335             {
336                 return _line;
337             }
338             set
339             {
340                 _line = value;
341             }
342         }
343 
344         public int CharPositionInLine
345         {
346             get
347             {
348                 return _charPositionInLine;
349             }
350             set
351             {
352                 _charPositionInLine = value;
353             }
354         }
355 
GetObjectData(SerializationInfo info, StreamingContext context)356         public override void GetObjectData(SerializationInfo info, StreamingContext context)
357         {
358             if (info == null)
359                 throw new ArgumentNullException("info");
360 
361             base.GetObjectData(info, context);
362             info.AddValue("Index", _index);
363             info.AddValue("C", _c);
364             info.AddValue("Line", _line);
365             info.AddValue("CharPositionInLine", _charPositionInLine);
366             info.AddValue("ApproximateLineInfo", _approximateLineInfo);
367         }
368 
ExtractInformationFromTreeNodeStream(ITreeNodeStream input)369         protected virtual void ExtractInformationFromTreeNodeStream(ITreeNodeStream input)
370         {
371             this._node = input.LT(1);
372 
373             object positionNode = null;
374             IPositionTrackingStream positionTrackingStream = input as IPositionTrackingStream;
375             if (positionTrackingStream != null)
376             {
377                 positionNode = positionTrackingStream.GetKnownPositionElement(false);
378                 if (positionNode == null)
379                 {
380                     positionNode = positionTrackingStream.GetKnownPositionElement(true);
381                     this._approximateLineInfo = positionNode != null;
382                 }
383             }
384 
385             ITokenStreamInformation streamInformation = input as ITokenStreamInformation;
386             if (streamInformation != null)
387             {
388                 IToken lastToken = streamInformation.LastToken;
389                 IToken lastRealToken = streamInformation.LastRealToken;
390                 if (lastRealToken != null)
391                 {
392                     this._token = lastRealToken;
393                     this._line = lastRealToken.Line;
394                     this._charPositionInLine = lastRealToken.CharPositionInLine;
395                     this._approximateLineInfo = lastRealToken.Equals(lastToken);
396                 }
397             }
398             else
399             {
400                 ITreeAdaptor adaptor = input.TreeAdaptor;
401                 IToken payload = adaptor.GetToken(positionNode ?? _node);
402                 if (payload != null)
403                 {
404                     this._token = payload;
405                     if (payload.Line <= 0)
406                     {
407                         // imaginary node; no line/pos info; scan backwards
408                         int i = -1;
409                         object priorNode = input.LT(i);
410                         while (priorNode != null)
411                         {
412                             IToken priorPayload = adaptor.GetToken(priorNode);
413                             if (priorPayload != null && priorPayload.Line > 0)
414                             {
415                                 // we found the most recent real line / pos info
416                                 this._line = priorPayload.Line;
417                                 this._charPositionInLine = priorPayload.CharPositionInLine;
418                                 this._approximateLineInfo = true;
419                                 break;
420                             }
421 
422                             --i;
423                             try
424                             {
425                                 priorNode = input.LT(i);
426                             }
427                             catch (NotSupportedException)
428                             {
429                                 priorNode = null;
430                             }
431                         }
432                     }
433                     else
434                     {
435                         // node created from real token
436                         this._line = payload.Line;
437                         this._charPositionInLine = payload.CharPositionInLine;
438                     }
439                 }
440                 else if (this._node is Tree.ITree)
441                 {
442                     this._line = ((Tree.ITree)this._node).Line;
443                     this._charPositionInLine = ((Tree.ITree)this._node).CharPositionInLine;
444                     if (this._node is CommonTree)
445                     {
446                         this._token = ((CommonTree)this._node).Token;
447                     }
448                 }
449                 else
450                 {
451                     int type = adaptor.GetType(this._node);
452                     string text = adaptor.GetText(this._node);
453                     this._token = new CommonToken(type, text);
454                 }
455             }
456         }
457 
ExtractInformationFromTreeNodeStream(ITreeNodeStream input, int k)458         protected virtual void ExtractInformationFromTreeNodeStream(ITreeNodeStream input, int k)
459         {
460             int mark = input.Mark();
461             try
462             {
463                 for (int i = 0; i < k - 1; i++)
464                     input.Consume();
465 
466                 ExtractInformationFromTreeNodeStream(input);
467             }
468             finally
469             {
470                 input.Rewind(mark);
471             }
472         }
473     }
474 }
475