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