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    using Antlr.Runtime.Tree;
35
36    using ArgumentNullException = System.ArgumentNullException;
37    using Exception = System.Exception;
38    using NonSerialized = System.NonSerializedAttribute;
39    using SerializationInfo = System.Runtime.Serialization.SerializationInfo;
40    using StreamingContext = System.Runtime.Serialization.StreamingContext;
41
42    /** <summary>The root of the ANTLR exception hierarchy.</summary>
43     *
44     *  <remarks>
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     *  </remarks>
73     */
74    [System.Serializable]
75    public class RecognitionException : Exception {
76        /** <summary>What input stream did the error occur in?</summary> */
77        private IIntStream _input;
78
79        /** <summary>What is index of token/char were we looking at when the error occurred?</summary> */
80        private int _index;
81
82        /** <summary>
83         *  The current Token when an error occurred.  Since not all streams
84         *  can retrieve the ith Token, we have to track the Token object.
85         *  For parsers.  Even when it's a tree parser, token might be set.
86         *  </summary>
87         */
88        private IToken _token;
89
90        /** <summary>
91         *  If this is a tree parser exception, node is set to the node with
92         *  the problem.
93         *  </summary>
94         */
95        private object _node;
96
97        /** <summary>The current char when an error occurred. For lexers.</summary> */
98        private int _c;
99
100        /** <summary>
101         *  Track the line (1-based) at which the error occurred in case this is
102         *  generated from a lexer.  We need to track this since the
103         *  unexpected char doesn't carry the line info.
104         *  </summary>
105         */
106        private int _line;
107
108        /// <summary>
109        /// The 0-based index into the line where the error occurred.
110        /// </summary>
111        private int _charPositionInLine;
112
113        /** <summary>
114         *  If you are parsing a tree node stream, you will encounter som
115         *  imaginary nodes w/o line/col info.  We now search backwards looking
116         *  for most recent token with line/col info, but notify getErrorHeader()
117         *  that info is approximate.
118         *  </summary>
119         */
120        private bool _approximateLineInfo;
121
122        /** <summary>Used for remote debugger deserialization</summary> */
123        public RecognitionException()
124            : this("A recognition error occurred.", null, null) {
125        }
126
127        public RecognitionException(IIntStream input)
128            : this("A recognition error occurred.", input, null) {
129        }
130
131        public RecognitionException(string message)
132            : this(message, null, null) {
133        }
134
135        public RecognitionException(string message, IIntStream input)
136            : this(message, input, null) {
137        }
138
139        public RecognitionException(string message, Exception innerException)
140            : this(message, null, innerException) {
141        }
142
143        public RecognitionException(string message, IIntStream input, Exception innerException)
144            : base(message, innerException) {
145            this._input = input;
146            if (input != null) {
147                this._index = input.Index;
148                if (input is ITokenStream) {
149                    this._token = ((ITokenStream)input).LT(1);
150                    this._line = _token.Line;
151                    this._charPositionInLine = _token.CharPositionInLine;
152                }
153                if (input is ITreeNodeStream) {
154                    ExtractInformationFromTreeNodeStream(input);
155                } else if (input is ICharStream) {
156                    this._c = input.LA(1);
157                    this._line = ((ICharStream)input).Line;
158                    this._charPositionInLine = ((ICharStream)input).CharPositionInLine;
159                } else {
160                    this._c = input.LA(1);
161                }
162            }
163        }
164
165        protected RecognitionException(SerializationInfo info, StreamingContext context)
166            : base(info, context) {
167            if (info == null)
168                throw new ArgumentNullException("info");
169
170            _index = info.GetInt32("Index");
171            _c = info.GetInt32("C");
172            _line = info.GetInt32("Line");
173            _charPositionInLine = info.GetInt32("CharPositionInLine");
174            _approximateLineInfo = info.GetBoolean("ApproximateLineInfo");
175        }
176
177        /** <summary>Return the token type or char of the unexpected input element</summary> */
178        public virtual int UnexpectedType {
179            get {
180                if (_input is ITokenStream) {
181                    return _token.Type;
182                }
183
184                ITreeNodeStream treeNodeStream = _input as ITreeNodeStream;
185                if (treeNodeStream != null) {
186                    ITreeAdaptor adaptor = treeNodeStream.TreeAdaptor;
187                    return adaptor.GetType(_node);
188                }
189
190                return _c;
191            }
192        }
193
194        public bool ApproximateLineInfo {
195            get {
196                return _approximateLineInfo;
197            }
198            protected set {
199                _approximateLineInfo = value;
200            }
201        }
202
203        public IIntStream Input {
204            get {
205                return _input;
206            }
207            protected set {
208                _input = value;
209            }
210        }
211
212        public IToken Token {
213            get {
214                return _token;
215            }
216            set {
217                _token = value;
218            }
219        }
220
221        public object Node {
222            get {
223                return _node;
224            }
225            protected set {
226                _node = value;
227            }
228        }
229
230        public int Character {
231            get {
232                return _c;
233            }
234            protected set {
235                _c = value;
236            }
237        }
238
239        public int Index {
240            get {
241                return _index;
242            }
243            protected set {
244                _index = value;
245            }
246        }
247
248        public int Line {
249            get {
250                return _line;
251            }
252            set {
253                _line = value;
254            }
255        }
256
257        public int CharPositionInLine {
258            get {
259                return _charPositionInLine;
260            }
261            set {
262                _charPositionInLine = value;
263            }
264        }
265
266        public override void GetObjectData(SerializationInfo info, StreamingContext context) {
267            if (info == null)
268                throw new ArgumentNullException("info");
269
270            base.GetObjectData(info, context);
271            info.AddValue("Index", _index);
272            info.AddValue("C", _c);
273            info.AddValue("Line", _line);
274            info.AddValue("CharPositionInLine", _charPositionInLine);
275            info.AddValue("ApproximateLineInfo", _approximateLineInfo);
276        }
277
278        protected virtual void ExtractInformationFromTreeNodeStream(IIntStream input) {
279            ITokenStreamInformation streamInformation = input as ITokenStreamInformation;
280            if (streamInformation != null) {
281                IToken lastToken = streamInformation.LastToken;
282                IToken lastRealToken = streamInformation.LastRealToken;
283                if (lastRealToken != null) {
284                    this._token = lastRealToken;
285                    this._line = lastRealToken.Line;
286                    this._charPositionInLine = lastRealToken.CharPositionInLine;
287                    this._approximateLineInfo = lastRealToken.Equals(lastToken);
288                }
289            } else {
290                ITreeNodeStream nodes = (ITreeNodeStream)input;
291                this._node = nodes.LT(1);
292                ITreeAdaptor adaptor = nodes.TreeAdaptor;
293                IToken payload = adaptor.GetToken(_node);
294                if (payload != null) {
295                    this._token = payload;
296                    if (payload.Line <= 0) {
297                        // imaginary node; no line/pos info; scan backwards
298                        int i = -1;
299                        object priorNode = nodes.LT(i);
300                        while (priorNode != null) {
301                            IToken priorPayload = adaptor.GetToken(priorNode);
302                            if (priorPayload != null && priorPayload.Line > 0) {
303                                // we found the most recent real line / pos info
304                                this._line = priorPayload.Line;
305                                this._charPositionInLine = priorPayload.CharPositionInLine;
306                                this._approximateLineInfo = true;
307                                break;
308                            }
309                            --i;
310                            priorNode = nodes.LT(i);
311                        }
312                    } else { // node created from real token
313                        this._line = payload.Line;
314                        this._charPositionInLine = payload.CharPositionInLine;
315                    }
316                } else if (this._node is Tree.ITree) {
317                    this._line = ((Tree.ITree)this._node).Line;
318                    this._charPositionInLine = ((Tree.ITree)this._node).CharPositionInLine;
319                    if (this._node is CommonTree) {
320                        this._token = ((CommonTree)this._node).Token;
321                    }
322                } else {
323                    int type = adaptor.GetType(this._node);
324                    string text = adaptor.GetText(this._node);
325                    this._token = new CommonToken(type, text);
326                }
327            }
328        }
329    }
330}
331