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, 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 System.Collections.Generic;
36    using System;
37    using System.Text;
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 FastTokenStream
47        : ITokenStream<SlimToken>
48    {
49        [System.NonSerialized]
50        ITokenSource<SlimToken> _tokenSource;
51
52        /** <summary>
53         *  Record every single token pulled from the source so we can reproduce
54         *  chunks of it later.
55         *  </summary>
56         */
57        protected List<SlimToken> tokens;
58
59        /** <summary>Skip tokens on any channel but this one; this is how we skip whitespace...</summary> */
60        protected int channel = TokenChannels.Default;
61
62        /** <summary>Track the last mark() call result value for use in rewind().</summary> */
63        protected int lastMarker;
64
65        /** <summary>
66         *  The index into the tokens list of the current token (next token
67         *  to consume).  p==-1 indicates that the tokens list is empty
68         *  </summary>
69         */
70        protected int p = -1;
71
72        public FastTokenStream()
73        {
74            tokens = new List<SlimToken>( 500 );
75        }
76
77        public FastTokenStream( ITokenSource<SlimToken> tokenSource )
78            : this()
79        {
80            this._tokenSource = tokenSource;
81        }
82
83        public FastTokenStream( ITokenSource<SlimToken> tokenSource, int channel )
84            : this( tokenSource )
85        {
86            this.channel = channel;
87        }
88
89        public int Index
90        {
91            get
92            {
93                return p;
94            }
95        }
96
97        /** <summary>Reset this token stream by setting its token source.</summary> */
98        public void SetTokenSource( ITokenSource<SlimToken> tokenSource )
99        {
100            this._tokenSource = tokenSource;
101            tokens.Clear();
102            p = -1;
103            channel = TokenChannels.Default;
104        }
105
106        /** <summary>
107         *  Load all tokens from the token source and put in tokens.
108         *  This is done upon first LT request because you might want to
109         *  set some token type / channel overrides before filling buffer.
110         *  </summary>
111         */
112        public void FillBuffer()
113        {
114            // fast return if the buffer is already full
115            if ( p != -1 )
116                return;
117
118            int index = 0;
119            SlimToken t = _tokenSource.NextToken();
120            while ( t.Type != CharStreamConstants.EndOfFile )
121            {
122                //t.TokenIndex = index;
123                tokens.Add( t );
124                index++;
125
126                t = _tokenSource.NextToken();
127            }
128            // leave p pointing at first token on channel
129            p = 0;
130            p = SkipOffTokenChannels( p );
131        }
132
133        /** <summary>
134         *  Move the input pointer to the next incoming token.  The stream
135         *  must become active with LT(1) available.  consume() simply
136         *  moves the input pointer so that LT(1) points at the next
137         *  input symbol. Consume at least one token.
138         *  </summary>
139         *
140         *  <remarks>
141         *  Walk past any token not on the channel the parser is listening to.
142         *  </remarks>
143         */
144        public void Consume()
145        {
146            if ( p < tokens.Count )
147            {
148                p++;
149                p = SkipOffTokenChannels( p ); // leave p on valid token
150            }
151        }
152
153        /** <summary>Given a starting index, return the index of the first on-channel token.</summary> */
154        protected int SkipOffTokenChannels( int i )
155        {
156            int n = tokens.Count;
157            while ( i < n && tokens[i].Channel != channel )
158            {
159                i++;
160            }
161            return i;
162        }
163
164        protected int SkipOffTokenChannelsReverse( int i )
165        {
166            while ( i >= 0 && tokens[i].Channel != channel )
167            {
168                i--;
169            }
170            return i;
171        }
172
173        public IList<SlimToken> GetTokens()
174        {
175            if ( p == -1 )
176            {
177                FillBuffer();
178            }
179            return tokens;
180        }
181
182        public IList<SlimToken> GetTokens( int start, int stop )
183        {
184            return GetTokens( start, stop, (BitSet)null );
185        }
186
187        /** <summary>
188         *  Given a start and stop index, return a List of all tokens in
189         *  the token type BitSet.  Return null if no tokens were found.  This
190         *  method looks at both on and off channel tokens.
191         *  </summary>
192         */
193        public IList<SlimToken> GetTokens( int start, int stop, BitSet types )
194        {
195            if ( p == -1 )
196            {
197                FillBuffer();
198            }
199            if ( stop >= tokens.Count )
200            {
201                stop = tokens.Count - 1;
202            }
203            if ( start < 0 )
204            {
205                start = 0;
206            }
207            if ( start > stop )
208            {
209                return null;
210            }
211
212            // list = tokens[start:stop]:{Token t, t.getType() in types}
213            List<SlimToken> filteredTokens = new List<SlimToken>();
214            for ( int i = start; i <= stop; i++ )
215            {
216                SlimToken t = tokens[i];
217                if ( types == null || types.Member( t.Type ) )
218                {
219                    filteredTokens.Add( t );
220                }
221            }
222            if ( filteredTokens.Count == 0 )
223            {
224                filteredTokens = null;
225            }
226            return filteredTokens;
227        }
228
229        public IList<SlimToken> GetTokens( int start, int stop, IList<int> types )
230        {
231            return GetTokens( start, stop, new BitSet( types ) );
232        }
233
234        public IList<SlimToken> GetTokens( int start, int stop, int ttype )
235        {
236            return GetTokens( start, stop, BitSet.Of( ttype ) );
237        }
238
239        /** <summary>
240         *  Get the ith token from the current position 1..n where k=1 is the
241         *  first symbol of lookahead.
242         *  </summary>
243         */
244        public SlimToken LT( int k )
245        {
246            if ( p == -1 )
247            {
248                FillBuffer();
249            }
250            if ( k == 0 )
251            {
252                return default( SlimToken );
253            }
254            if ( k < 0 )
255            {
256                return LB( -k );
257            }
258            //System.out.print("LT(p="+p+","+k+")=");
259            if ( ( p + k - 1 ) >= tokens.Count )
260            {
261                return new SlimToken(TokenTypes.EndOfFile);
262            }
263            //System.out.println(tokens.get(p+k-1));
264            int i = p;
265            int n = 1;
266            // find k good tokens
267            while ( n < k )
268            {
269                // skip off-channel tokens
270                i = SkipOffTokenChannels( i + 1 ); // leave p on valid token
271                n++;
272            }
273            if ( i >= tokens.Count )
274            {
275                return new SlimToken(TokenTypes.EndOfFile);
276            }
277            return tokens[i];
278        }
279
280        /** <summary>Look backwards k tokens on-channel tokens</summary> */
281        protected SlimToken LB( int k )
282        {
283            //System.out.print("LB(p="+p+","+k+") ");
284            if ( p == -1 )
285            {
286                FillBuffer();
287            }
288            if ( k == 0 )
289            {
290                return default( SlimToken );
291            }
292            if ( ( p - k ) < 0 )
293            {
294                return default( SlimToken );
295            }
296
297            int i = p;
298            int n = 1;
299            // find k good tokens looking backwards
300            while ( n <= k )
301            {
302                // skip off-channel tokens
303                i = SkipOffTokenChannelsReverse( i - 1 ); // leave p on valid token
304                n++;
305            }
306            if ( i < 0 )
307            {
308                return default( SlimToken );
309            }
310            return tokens[i];
311        }
312
313        /** <summary>
314         *  Return absolute token i; ignore which channel the tokens are on;
315         *  that is, count all tokens not just on-channel tokens.
316         *  </summary>
317         */
318        public SlimToken Get( int i )
319        {
320            return tokens[i];
321        }
322
323        public int LA( int i )
324        {
325            return LT( i ).Type;
326        }
327
328        public int Mark()
329        {
330            if ( p == -1 )
331            {
332                FillBuffer();
333            }
334            lastMarker = Index;
335            return lastMarker;
336        }
337
338        public void Release( int marker )
339        {
340            // no resources to release
341        }
342
343        public int Count
344        {
345            get
346            {
347                return tokens.Count;
348            }
349        }
350
351        public void Rewind( int marker )
352        {
353            Seek( marker );
354        }
355
356        public void Rewind()
357        {
358            Seek( lastMarker );
359        }
360
361        public void Reset()
362        {
363            p = 0;
364            lastMarker = 0;
365        }
366
367        public void Seek( int index )
368        {
369            p = index;
370        }
371
372        public ITokenSource<SlimToken> TokenSource
373        {
374            get
375            {
376                return _tokenSource;
377            }
378        }
379
380        public string SourceName
381        {
382            get
383            {
384                return TokenSource.SourceName;
385            }
386        }
387
388        public override string ToString()
389        {
390            if ( p == -1 )
391            {
392                throw new InvalidOperationException( "Buffer is not yet filled." );
393            }
394            return ToString( 0, tokens.Count - 1 );
395        }
396
397        public virtual string ToString( int start, int stop )
398        {
399            if ( start < 0 || stop < 0 )
400            {
401                return null;
402            }
403            if ( p == -1 )
404            {
405                throw new InvalidOperationException( "Buffer is not yet filled." );
406            }
407            if ( stop >= tokens.Count )
408            {
409                stop = tokens.Count - 1;
410            }
411            StringBuilder buf = new StringBuilder();
412            for ( int i = start; i <= stop; i++ )
413            {
414                SlimToken t = tokens[i];
415                SlimLexer lexer = _tokenSource as SlimLexer;
416                if ( lexer != null )
417                {
418                    SlimStringStream input = lexer.CharStream as SlimStringStream;
419                    if ( input != null )
420                    {
421                        string text = input.Substring( t.StartIndex, t.StopIndex - t.StartIndex + 1 );
422                        buf.Append( text );
423                    }
424                }
425            }
426            return buf.ToString();
427        }
428
429        public virtual string ToString( IToken start, IToken stop )
430        {
431            if ( start != null && stop != null )
432            {
433                return ToString( start.TokenIndex, stop.TokenIndex );
434            }
435            return null;
436        }
437    }
438}
439