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 System.Collections.Generic;
35    using ArgumentException = System.ArgumentException;
36    using ArgumentOutOfRangeException = System.ArgumentOutOfRangeException;
37    using ArgumentNullException = System.ArgumentNullException;
38
39    /** <summary>
40     *  A pretty quick CharStream that pulls all data from an array
41     *  directly.  Every method call counts in the lexer.  Java's
42     *  strings aren't very good so I'm avoiding.
43     *  </summary>
44     */
45    [System.Serializable]
46    public class ANTLRStringStream : ICharStream {
47        /** <summary>The data being scanned</summary> */
48        protected char[] data;
49
50        /** <summary>How many characters are actually in the buffer</summary> */
51        protected int n;
52
53        /** <summary>0..n-1 index into string of next char</summary> */
54        protected int p = 0;
55
56        /** <summary>line number 1..n within the input</summary> */
57        int line = 1;
58
59        /** <summary>The index of the character relative to the beginning of the line 0..n-1</summary> */
60        int charPositionInLine = 0;
61
62        /** <summary>tracks how deep mark() calls are nested</summary> */
63        protected int markDepth = 0;
64
65        /** <summary>
66         *  A list of CharStreamState objects that tracks the stream state
67         *  values line, charPositionInLine, and p that can change as you
68         *  move through the input stream.  Indexed from 1..markDepth.
69         *  A null is kept @ index 0.  Create upon first call to mark().
70         *  </summary>
71         */
72        protected IList<CharStreamState> markers;
73
74        /** <summary>Track the last mark() call result value for use in rewind().</summary> */
75        protected int lastMarker;
76
77        /** <summary>What is name or source of this char stream?</summary> */
78        public string name;
79
80        /** <summary>Copy data in string to a local char array</summary> */
81        public ANTLRStringStream(string input)
82            : this(input, null) {
83        }
84
85        public ANTLRStringStream(string input, string sourceName)
86            : this(input.ToCharArray(), input.Length, sourceName) {
87        }
88
89        /** <summary>This is the preferred constructor as no data is copied</summary> */
90        public ANTLRStringStream(char[] data, int numberOfActualCharsInArray)
91            : this(data, numberOfActualCharsInArray, null) {
92        }
93
94        public ANTLRStringStream(char[] data, int numberOfActualCharsInArray, string sourceName) {
95            if (data == null)
96                throw new ArgumentNullException("data");
97            if (numberOfActualCharsInArray < 0)
98                throw new ArgumentOutOfRangeException();
99            if (numberOfActualCharsInArray > data.Length)
100                throw new ArgumentException();
101
102            this.data = data;
103            this.n = numberOfActualCharsInArray;
104            this.name = sourceName;
105        }
106
107        protected ANTLRStringStream() {
108            this.data = new char[0];
109        }
110
111        /** <summary>
112         *  Return the current input symbol index 0..n where n indicates the
113         *  last symbol has been read.  The index is the index of char to
114         *  be returned from LA(1).
115         *  </summary>
116         */
117        public virtual int Index {
118            get {
119                return p;
120            }
121        }
122        public virtual int Line {
123            get {
124                return line;
125            }
126            set {
127                line = value;
128            }
129        }
130        public virtual int CharPositionInLine {
131            get {
132                return charPositionInLine;
133            }
134            set {
135                charPositionInLine = value;
136            }
137        }
138
139        /** <summary>
140         *  Reset the stream so that it's in the same state it was
141         *  when the object was created *except* the data array is not
142         *  touched.
143         *  </summary>
144         */
145        public virtual void Reset() {
146            p = 0;
147            line = 1;
148            charPositionInLine = 0;
149            markDepth = 0;
150        }
151
152        public virtual void Consume() {
153            //System.out.println("prev p="+p+", c="+(char)data[p]);
154            if (p < n) {
155                charPositionInLine++;
156                if (data[p] == '\n') {
157                    /*
158                    System.out.println("newline char found on line: "+line+
159                                       "@ pos="+charPositionInLine);
160                    */
161                    line++;
162                    charPositionInLine = 0;
163                }
164                p++;
165                //System.out.println("p moves to "+p+" (c='"+(char)data[p]+"')");
166            }
167        }
168
169        public virtual int LA(int i) {
170            if (i == 0) {
171                return 0; // undefined
172            }
173            if (i < 0) {
174                i++; // e.g., translate LA(-1) to use offset i=0; then data[p+0-1]
175                if ((p + i - 1) < 0) {
176                    return CharStreamConstants.EndOfFile; // invalid; no char before first char
177                }
178            }
179
180            if ((p + i - 1) >= n) {
181                //System.out.println("char LA("+i+")=EOF; p="+p);
182                return CharStreamConstants.EndOfFile;
183            }
184            //System.out.println("char LA("+i+")="+(char)data[p+i-1]+"; p="+p);
185            //System.out.println("LA("+i+"); p="+p+" n="+n+" data.length="+data.length);
186            return data[p + i - 1];
187        }
188
189        public virtual int LT(int i) {
190            return LA(i);
191        }
192
193        public virtual int Count {
194            get {
195                return n;
196            }
197        }
198
199        public virtual int Mark() {
200            if (markers == null) {
201                markers = new List<CharStreamState>();
202                markers.Add(null); // depth 0 means no backtracking, leave blank
203            }
204            markDepth++;
205            CharStreamState state = null;
206            if (markDepth >= markers.Count) {
207                state = new CharStreamState();
208                markers.Add(state);
209            } else {
210                state = markers[markDepth];
211            }
212            state.p = p;
213            state.line = line;
214            state.charPositionInLine = charPositionInLine;
215            lastMarker = markDepth;
216            return markDepth;
217        }
218
219        public virtual void Rewind(int m) {
220            if (m < 0)
221                throw new ArgumentOutOfRangeException();
222
223            //if (m > markDepth)
224            //    throw new ArgumentException();
225
226            CharStreamState state = markers[m];
227            // restore stream state
228            Seek(state.p);
229            line = state.line;
230            charPositionInLine = state.charPositionInLine;
231            Release(m);
232        }
233
234        public virtual void Rewind() {
235            Rewind(lastMarker);
236        }
237
238        public virtual void Release(int marker) {
239            // unwind any other markers made after m and release m
240            markDepth = marker;
241            // release this marker
242            markDepth--;
243        }
244
245        /** <summary>
246         *  consume() ahead until p==index; can't just set p=index as we must
247         *  update line and charPositionInLine.
248         *  </summary>
249         */
250        public virtual void Seek(int index) {
251            if (index <= p) {
252                p = index; // just jump; don't update stream state (line, ...)
253                return;
254            }
255            // seek forward, consume until p hits index
256            while (p < index) {
257                Consume();
258            }
259        }
260
261        public virtual string Substring(int start, int length) {
262            if (start < 0)
263                throw new ArgumentOutOfRangeException();
264            if (length < 0)
265                throw new ArgumentOutOfRangeException();
266            if (start + length > data.Length)
267                throw new ArgumentException();
268
269            if (length == 0)
270                return string.Empty;
271
272            return new string(data, start, length);
273        }
274
275        public virtual string SourceName {
276            get {
277                return name;
278            }
279        }
280
281        public override string ToString() {
282            return new string(data);
283        }
284    }
285}
286