1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2005-2008 Terence Parr
4 * All rights reserved.
5 *
6 * Conversion to C#:
7 * Copyright (c) 2008-2009 Sam Harwell, Pixel Mine, Inc.
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
33namespace Antlr.Runtime
34{
35    using Antlr.Runtime.Tree;
36
37    using ArgumentException = System.ArgumentException;
38    using ArgumentNullException = System.ArgumentNullException;
39    using Exception = System.Exception;
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>What is index of token/char were we looking at when the error occurred?</summary> */
82        private int _index;
83
84        /** <summary>
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         *  </summary>
89         */
90        private IToken _token;
91
92        /** <summary>
93         *  If this is a tree parser exception, node is set to the node with
94         *  the problem.
95         *  </summary>
96         */
97        private object _node;
98
99        /** <summary>The current char when an error occurred. For lexers.</summary> */
100        private int _c;
101
102        /** <summary>
103         *  Track the line (1-based) at which the error occurred in case this is
104         *  generated from a lexer.  We need to track this since the
105         *  unexpected char doesn't carry the line info.
106         *  </summary>
107         */
108        private int _line;
109
110        /// <summary>
111        /// The 0-based index into the line where the error occurred.
112        /// </summary>
113        private int _charPositionInLine;
114
115        /** <summary>
116         *  If you are parsing a tree node stream, you will encounter som
117         *  imaginary nodes w/o line/col info.  We now search backwards looking
118         *  for most recent token with line/col info, but notify getErrorHeader()
119         *  that info is approximate.
120         *  </summary>
121         */
122        private bool _approximateLineInfo;
123
124        /** <summary>Used for remote debugger deserialization</summary> */
125        public RecognitionException()
126            : this("A recognition error occurred.", null, null)
127        {
128        }
129
130        public RecognitionException( IIntStream input )
131            : this("A recognition error occurred.", input, null)
132        {
133        }
134
135        public RecognitionException(string message)
136            : this(message, null, null)
137        {
138        }
139
140        public RecognitionException(string message, IIntStream input)
141            : this(message, input, null)
142        {
143        }
144
145        public RecognitionException(string message, Exception innerException)
146            : this(message, null, innerException)
147        {
148        }
149
150        public RecognitionException(string message, IIntStream input, Exception innerException)
151            : base(message, innerException)
152        {
153            this._input = input;
154            if (input != null)
155            {
156                this._index = input.Index;
157                if (input is ITokenStream)
158                {
159                    this._token = ((ITokenStream)input).LT(1);
160                    this._line = _token.Line;
161                    this._charPositionInLine = _token.CharPositionInLine;
162                }
163
164                ITreeNodeStream tns = input as ITreeNodeStream;
165                if (tns != null)
166                {
167                    ExtractInformationFromTreeNodeStream(tns);
168                }
169                else
170                {
171                    ICharStream charStream = input as ICharStream;
172                    if (charStream != null)
173                    {
174                        this._c = input.LA(1);
175                        this._line = ((ICharStream)input).Line;
176                        this._charPositionInLine = ((ICharStream)input).CharPositionInLine;
177                    }
178                    else
179                    {
180                        this._c = input.LA(1);
181                    }
182                }
183            }
184        }
185
186        protected RecognitionException(SerializationInfo info, StreamingContext context)
187            : base(info, context)
188        {
189            if (info == null)
190                throw new ArgumentNullException("info");
191
192            _index = info.GetInt32("Index");
193            _c = info.GetInt32("C");
194            _line = info.GetInt32("Line");
195            _charPositionInLine = info.GetInt32("CharPositionInLine");
196            _approximateLineInfo = info.GetBoolean("ApproximateLineInfo");
197        }
198
199        /** <summary>Return the token type or char of the unexpected input element</summary> */
200        public virtual int UnexpectedType
201        {
202            get
203            {
204                if ( _input is ITokenStream )
205                {
206                    return _token.Type;
207                }
208
209                ITreeNodeStream treeNodeStream = _input as ITreeNodeStream;
210                if ( treeNodeStream != null )
211                {
212                    ITreeAdaptor adaptor = treeNodeStream.TreeAdaptor;
213                    return adaptor.GetType( _node );
214                }
215
216                return _c;
217            }
218        }
219
220        public bool ApproximateLineInfo
221        {
222            get
223            {
224                return _approximateLineInfo;
225            }
226            protected set
227            {
228                _approximateLineInfo = value;
229            }
230        }
231
232        public IIntStream Input
233        {
234            get
235            {
236                return _input;
237            }
238            protected set
239            {
240                _input = value;
241            }
242        }
243
244        public IToken Token
245        {
246            get
247            {
248                return _token;
249            }
250            set
251            {
252                _token = value;
253            }
254        }
255
256        public object Node
257        {
258            get
259            {
260                return _node;
261            }
262            protected set
263            {
264                _node = value;
265            }
266        }
267
268        public int Character
269        {
270            get
271            {
272                return _c;
273            }
274            protected set
275            {
276                _c = value;
277            }
278        }
279
280        public int Index
281        {
282            get
283            {
284                return _index;
285            }
286            protected set
287            {
288                _index = value;
289            }
290        }
291
292        public int Line
293        {
294            get
295            {
296                return _line;
297            }
298            set
299            {
300                _line = value;
301            }
302        }
303
304        public int CharPositionInLine
305        {
306            get
307            {
308                return _charPositionInLine;
309            }
310            set
311            {
312                _charPositionInLine = value;
313            }
314        }
315
316        public override void GetObjectData(SerializationInfo info, StreamingContext context)
317        {
318            if (info == null)
319                throw new ArgumentNullException("info");
320
321            base.GetObjectData(info, context);
322            info.AddValue("Index", _index);
323            info.AddValue("C", _c);
324            info.AddValue("Line", _line);
325            info.AddValue("CharPositionInLine", _charPositionInLine);
326            info.AddValue("ApproximateLineInfo", _approximateLineInfo);
327        }
328
329        protected virtual void ExtractInformationFromTreeNodeStream(ITreeNodeStream input)
330        {
331            this._node = input.LT(1);
332            ITokenStreamInformation streamInformation = input as ITokenStreamInformation;
333            if (streamInformation != null)
334            {
335                IToken lastToken = streamInformation.LastToken;
336                IToken lastRealToken = streamInformation.LastRealToken;
337                if (lastRealToken != null)
338                {
339                    this._token = lastRealToken;
340                    this._line = lastRealToken.Line;
341                    this._charPositionInLine = lastRealToken.CharPositionInLine;
342                    this._approximateLineInfo = lastRealToken.Equals(lastToken);
343                }
344            }
345            else
346            {
347                ITreeAdaptor adaptor = input.TreeAdaptor;
348                IToken payload = adaptor.GetToken(_node);
349                if (payload != null)
350                {
351                    this._token = payload;
352                    if (payload.Line <= 0)
353                    {
354                        // imaginary node; no line/pos info; scan backwards
355                        int i = -1;
356                        object priorNode = input.LT(i);
357                        while (priorNode != null)
358                        {
359                            IToken priorPayload = adaptor.GetToken(priorNode);
360                            if (priorPayload != null && priorPayload.Line > 0)
361                            {
362                                // we found the most recent real line / pos info
363                                this._line = priorPayload.Line;
364                                this._charPositionInLine = priorPayload.CharPositionInLine;
365                                this._approximateLineInfo = true;
366                                break;
367                            }
368                            --i;
369                            try
370                            {
371                                priorNode = input.LT(i);
372                            }
373                            catch (ArgumentException)
374                            {
375                                priorNode = null;
376                            }
377                        }
378                    }
379                    else
380                    {
381                        // node created from real token
382                        this._line = payload.Line;
383                        this._charPositionInLine = payload.CharPositionInLine;
384                    }
385                }
386                else if (this._node is Tree.ITree)
387                {
388                    this._line = ((Tree.ITree)this._node).Line;
389                    this._charPositionInLine = ((Tree.ITree)this._node).CharPositionInLine;
390                    if (this._node is CommonTree)
391                    {
392                        this._token = ((CommonTree)this._node).Token;
393                    }
394                }
395                else
396                {
397                    int type = adaptor.GetType(this._node);
398                    string text = adaptor.GetText(this._node);
399                    this._token = new CommonToken(type, text);
400                }
401            }
402        }
403    }
404}
405