| /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. |
| * Use of this file is governed by the BSD 3-clause license that |
| * can be found in the LICENSE.txt file in the project root. |
| */ |
| using System; |
| using System.Collections.Generic; |
| using System.Text; |
| using Antlr4.Runtime; |
| using Antlr4.Runtime.Misc; |
| using Antlr4.Runtime.Sharpen; |
| |
| namespace Antlr4.Runtime |
| { |
| /// <summary> |
| /// This implementation of |
| /// <see cref="ITokenStream"/> |
| /// loads tokens from a |
| /// <see cref="ITokenSource"/> |
| /// on-demand, and places the tokens in a buffer to provide |
| /// access to any previous token by index. |
| /// <p> |
| /// This token stream ignores the value of |
| /// <see cref="IToken.Channel()"/> |
| /// . If your |
| /// parser requires the token stream filter tokens to only those on a particular |
| /// channel, such as |
| /// <see cref="TokenConstants.DefaultChannel"/> |
| /// or |
| /// <see cref="TokenConstants.HiddenChannel"/> |
| /// , use a filtering token stream such a |
| /// <see cref="CommonTokenStream"/> |
| /// .</p> |
| /// </summary> |
| public class BufferedTokenStream : ITokenStream |
| { |
| /// <summary> |
| /// The |
| /// <see cref="ITokenSource"/> |
| /// from which tokens for this stream are fetched. |
| /// </summary> |
| [NotNull] |
| private ITokenSource _tokenSource; |
| |
| /// <summary>A collection of all tokens fetched from the token source.</summary> |
| /// <remarks> |
| /// A collection of all tokens fetched from the token source. The list is |
| /// considered a complete view of the input once |
| /// <see cref="fetchedEOF"/> |
| /// is set |
| /// to |
| /// <see langword="true"/> |
| /// . |
| /// </remarks> |
| protected internal IList<IToken> tokens = new List<IToken>(100); |
| |
| /// <summary> |
| /// The index into |
| /// <see cref="tokens"/> |
| /// of the current token (next token to |
| /// <see cref="Consume()"/> |
| /// ). |
| /// <see cref="tokens"/> |
| /// <c>[</c> |
| /// <see cref="p"/> |
| /// <c>]</c> |
| /// should be |
| /// <see cref="LT(int)">LT(1)</see> |
| /// . |
| /// <p>This field is set to -1 when the stream is first constructed or when |
| /// <see cref="SetTokenSource(ITokenSource)"/> |
| /// is called, indicating that the first token has |
| /// not yet been fetched from the token source. For additional information, |
| /// see the documentation of |
| /// <see cref="IIntStream"/> |
| /// for a description of |
| /// Initializing Methods.</p> |
| /// </summary> |
| protected internal int p = -1; |
| |
| /// <summary> |
| /// Indicates whether the |
| /// <see cref="TokenConstants.EOF"/> |
| /// token has been fetched from |
| /// <see cref="_tokenSource"/> |
| /// and added to |
| /// <see cref="tokens"/> |
| /// . This field improves |
| /// performance for the following cases: |
| /// <ul> |
| /// <li> |
| /// <see cref="Consume()"/> |
| /// : The lookahead check in |
| /// <see cref="Consume()"/> |
| /// to prevent |
| /// consuming the EOF symbol is optimized by checking the values of |
| /// <see cref="fetchedEOF"/> |
| /// and |
| /// <see cref="p"/> |
| /// instead of calling |
| /// <see cref="LA(int)"/> |
| /// .</li> |
| /// <li> |
| /// <see cref="Fetch(int)"/> |
| /// : The check to prevent adding multiple EOF symbols into |
| /// <see cref="tokens"/> |
| /// is trivial with this field.</li> |
| /// </ul> |
| /// </summary> |
| protected internal bool fetchedEOF; |
| |
| public BufferedTokenStream(ITokenSource tokenSource) |
| { |
| if (tokenSource == null) |
| { |
| throw new ArgumentNullException("tokenSource cannot be null"); |
| } |
| this._tokenSource = tokenSource; |
| } |
| |
| public virtual ITokenSource TokenSource |
| { |
| get |
| { |
| return _tokenSource; |
| } |
| } |
| |
| public virtual int Index |
| { |
| get |
| { |
| return p; |
| } |
| } |
| |
| public virtual int Mark() |
| { |
| return 0; |
| } |
| |
| public virtual void Release(int marker) |
| { |
| } |
| |
| // no resources to release |
| public virtual void Reset() |
| { |
| Seek(0); |
| } |
| |
| public virtual void Seek(int index) |
| { |
| LazyInit(); |
| p = AdjustSeekIndex(index); |
| } |
| |
| public virtual int Size |
| { |
| get |
| { |
| return tokens.Count; |
| } |
| } |
| |
| public virtual void Consume() |
| { |
| bool skipEofCheck; |
| if (p >= 0) |
| { |
| if (fetchedEOF) |
| { |
| // the last token in tokens is EOF. skip check if p indexes any |
| // fetched token except the last. |
| skipEofCheck = p < tokens.Count - 1; |
| } |
| else |
| { |
| // no EOF token in tokens. skip check if p indexes a fetched token. |
| skipEofCheck = p < tokens.Count; |
| } |
| } |
| else |
| { |
| // not yet initialized |
| skipEofCheck = false; |
| } |
| if (!skipEofCheck && LA(1) == IntStreamConstants.EOF) |
| { |
| throw new InvalidOperationException("cannot consume EOF"); |
| } |
| if (Sync(p + 1)) |
| { |
| p = AdjustSeekIndex(p + 1); |
| } |
| } |
| |
| /// <summary> |
| /// Make sure index |
| /// <paramref name="i"/> |
| /// in tokens has a token. |
| /// </summary> |
| /// <returns> |
| /// |
| /// <see langword="true"/> |
| /// if a token is located at index |
| /// <paramref name="i"/> |
| /// , otherwise |
| /// <see langword="false"/> |
| /// . |
| /// </returns> |
| /// <seealso cref="Get(int)"/> |
| protected internal virtual bool Sync(int i) |
| { |
| System.Diagnostics.Debug.Assert(i >= 0); |
| int n = i - tokens.Count + 1; |
| // how many more elements we need? |
| //System.out.println("sync("+i+") needs "+n); |
| if (n > 0) |
| { |
| int fetched = Fetch(n); |
| return fetched >= n; |
| } |
| return true; |
| } |
| |
| /// <summary> |
| /// Add |
| /// <paramref name="n"/> |
| /// elements to buffer. |
| /// </summary> |
| /// <returns>The actual number of elements added to the buffer.</returns> |
| protected internal virtual int Fetch(int n) |
| { |
| if (fetchedEOF) |
| { |
| return 0; |
| } |
| for (int i = 0; i < n; i++) |
| { |
| IToken t = _tokenSource.NextToken(); |
| if (t is IWritableToken) |
| { |
| ((IWritableToken)t).TokenIndex = tokens.Count; |
| } |
| tokens.Add(t); |
| if (t.Type == TokenConstants.EOF) |
| { |
| fetchedEOF = true; |
| return i + 1; |
| } |
| } |
| return n; |
| } |
| |
| public virtual IToken Get(int i) |
| { |
| if (i < 0 || i >= tokens.Count) |
| { |
| throw new ArgumentOutOfRangeException("token index " + i + " out of range 0.." + (tokens.Count - 1)); |
| } |
| return tokens[i]; |
| } |
| |
| /// <summary>Get all tokens from start..stop inclusively.</summary> |
| /// <remarks>Get all tokens from start..stop inclusively.</remarks> |
| public virtual IList<IToken> Get(int start, int stop) |
| { |
| if (start < 0 || stop < 0) |
| { |
| return null; |
| } |
| LazyInit(); |
| IList<IToken> subset = new List<IToken>(); |
| if (stop >= tokens.Count) |
| { |
| stop = tokens.Count - 1; |
| } |
| for (int i = start; i <= stop; i++) |
| { |
| IToken t = tokens[i]; |
| if (t.Type == TokenConstants.EOF) |
| { |
| break; |
| } |
| subset.Add(t); |
| } |
| return subset; |
| } |
| |
| public virtual int LA(int i) |
| { |
| return LT(i).Type; |
| } |
| |
| protected internal virtual IToken Lb(int k) |
| { |
| if ((p - k) < 0) |
| { |
| return null; |
| } |
| return tokens[p - k]; |
| } |
| |
| [return: NotNull] |
| public virtual IToken LT(int k) |
| { |
| LazyInit(); |
| if (k == 0) |
| { |
| return null; |
| } |
| if (k < 0) |
| { |
| return Lb(-k); |
| } |
| int i = p + k - 1; |
| Sync(i); |
| if (i >= tokens.Count) |
| { |
| // return EOF token |
| // EOF must be last token |
| return tokens[tokens.Count - 1]; |
| } |
| // if ( i>range ) range = i; |
| return tokens[i]; |
| } |
| |
| /// <summary> |
| /// Allowed derived classes to modify the behavior of operations which change |
| /// the current stream position by adjusting the target token index of a seek |
| /// operation. |
| /// </summary> |
| /// <remarks> |
| /// Allowed derived classes to modify the behavior of operations which change |
| /// the current stream position by adjusting the target token index of a seek |
| /// operation. The default implementation simply returns |
| /// <paramref name="i"/> |
| /// . If an |
| /// exception is thrown in this method, the current stream index should not be |
| /// changed. |
| /// <p>For example, |
| /// <see cref="CommonTokenStream"/> |
| /// overrides this method to ensure that |
| /// the seek target is always an on-channel token.</p> |
| /// </remarks> |
| /// <param name="i">The target token index.</param> |
| /// <returns>The adjusted target token index.</returns> |
| protected internal virtual int AdjustSeekIndex(int i) |
| { |
| return i; |
| } |
| |
| protected internal void LazyInit() |
| { |
| if (p == -1) |
| { |
| Setup(); |
| } |
| } |
| |
| protected internal virtual void Setup() |
| { |
| Sync(0); |
| p = AdjustSeekIndex(0); |
| } |
| |
| /// <summary>Reset this token stream by setting its token source.</summary> |
| /// <remarks>Reset this token stream by setting its token source.</remarks> |
| public virtual void SetTokenSource(ITokenSource tokenSource) |
| { |
| this._tokenSource = tokenSource; |
| tokens.Clear(); |
| p = -1; |
| this.fetchedEOF = false; |
| } |
| |
| public virtual IList<IToken> GetTokens() |
| { |
| return tokens; |
| } |
| |
| public virtual IList<IToken> GetTokens(int start, int stop) |
| { |
| return GetTokens(start, stop, null); |
| } |
| |
| /// <summary> |
| /// Given a start and stop index, return a |
| /// <c>List</c> |
| /// of all tokens in |
| /// the token type |
| /// <c>BitSet</c> |
| /// . Return |
| /// <see langword="null"/> |
| /// if no tokens were found. This |
| /// method looks at both on and off channel tokens. |
| /// </summary> |
| public virtual IList<IToken> GetTokens(int start, int stop, BitSet types) |
| { |
| LazyInit(); |
| if (start < 0 || stop >= tokens.Count || stop < 0 || start >= tokens.Count) |
| { |
| throw new ArgumentOutOfRangeException("start " + start + " or stop " + stop + " not in 0.." + (tokens.Count - 1)); |
| } |
| if (start > stop) |
| { |
| return null; |
| } |
| // list = tokens[start:stop]:{T t, t.getType() in types} |
| IList<IToken> filteredTokens = new List<IToken>(); |
| for (int i = start; i <= stop; i++) |
| { |
| IToken t = tokens[i]; |
| if (types == null || types.Get(t.Type)) |
| { |
| filteredTokens.Add(t); |
| } |
| } |
| if (filteredTokens.Count == 0) |
| { |
| filteredTokens = null; |
| } |
| return filteredTokens; |
| } |
| |
| public virtual IList<IToken> GetTokens(int start, int stop, int ttype) |
| { |
| BitSet s = new BitSet(ttype); |
| s.Set(ttype); |
| return GetTokens(start, stop, s); |
| } |
| |
| /// <summary>Given a starting index, return the index of the next token on channel.</summary> |
| /// <remarks> |
| /// Given a starting index, return the index of the next token on channel. |
| /// Return |
| /// <paramref name="i"/> |
| /// if |
| /// <c>tokens[i]</c> |
| /// is on channel. Return the index of |
| /// the EOF token if there are no tokens on channel between |
| /// <paramref name="i"/> |
| /// and |
| /// EOF. |
| /// </remarks> |
| protected internal virtual int NextTokenOnChannel(int i, int channel) |
| { |
| Sync(i); |
| if (i >= Size) |
| { |
| return Size - 1; |
| } |
| IToken token = tokens[i]; |
| while (token.Channel != channel) |
| { |
| if (token.Type == TokenConstants.EOF) |
| { |
| return i; |
| } |
| i++; |
| Sync(i); |
| token = tokens[i]; |
| } |
| return i; |
| } |
| |
| /// <summary> |
| /// Given a starting index, return the index of the previous token on |
| /// channel. |
| /// </summary> |
| /// <remarks> |
| /// Given a starting index, return the index of the previous token on |
| /// channel. Return |
| /// <paramref name="i"/> |
| /// if |
| /// <c>tokens[i]</c> |
| /// is on channel. Return -1 |
| /// if there are no tokens on channel between |
| /// <paramref name="i"/> |
| /// and 0. |
| /// <p> |
| /// If |
| /// <paramref name="i"/> |
| /// specifies an index at or after the EOF token, the EOF token |
| /// index is returned. This is due to the fact that the EOF token is treated |
| /// as though it were on every channel.</p> |
| /// </remarks> |
| protected internal virtual int PreviousTokenOnChannel(int i, int channel) |
| { |
| Sync(i); |
| if (i >= Size) |
| { |
| // the EOF token is on every channel |
| return Size - 1; |
| } |
| while (i >= 0) |
| { |
| IToken token = tokens[i]; |
| if (token.Type == TokenConstants.EOF || token.Channel == channel) |
| { |
| return i; |
| } |
| i--; |
| } |
| return i; |
| } |
| |
| /// <summary> |
| /// Collect all tokens on specified channel to the right of |
| /// the current token up until we see a token on |
| /// <see cref="Lexer.DefaultTokenChannel"/> |
| /// or |
| /// EOF. If |
| /// <paramref name="channel"/> |
| /// is |
| /// <c>-1</c> |
| /// , find any non default channel token. |
| /// </summary> |
| public virtual IList<IToken> GetHiddenTokensToRight(int tokenIndex, int channel) |
| { |
| LazyInit(); |
| if (tokenIndex < 0 || tokenIndex >= tokens.Count) |
| { |
| throw new ArgumentOutOfRangeException(tokenIndex + " not in 0.." + (tokens.Count - 1)); |
| } |
| int nextOnChannel = NextTokenOnChannel(tokenIndex + 1, Lexer.DefaultTokenChannel); |
| int to; |
| int from = tokenIndex + 1; |
| // if none onchannel to right, nextOnChannel=-1 so set to = last token |
| if (nextOnChannel == -1) |
| { |
| to = Size - 1; |
| } |
| else |
| { |
| to = nextOnChannel; |
| } |
| return FilterForChannel(from, to, channel); |
| } |
| |
| /// <summary> |
| /// Collect all hidden tokens (any off-default channel) to the right of |
| /// the current token up until we see a token on |
| /// <see cref="Lexer.DefaultTokenChannel"/> |
| /// or EOF. |
| /// </summary> |
| public virtual IList<IToken> GetHiddenTokensToRight(int tokenIndex) |
| { |
| return GetHiddenTokensToRight(tokenIndex, -1); |
| } |
| |
| /// <summary> |
| /// Collect all tokens on specified channel to the left of |
| /// the current token up until we see a token on |
| /// <see cref="Lexer.DefaultTokenChannel"/> |
| /// . |
| /// If |
| /// <paramref name="channel"/> |
| /// is |
| /// <c>-1</c> |
| /// , find any non default channel token. |
| /// </summary> |
| public virtual IList<IToken> GetHiddenTokensToLeft(int tokenIndex, int channel) |
| { |
| LazyInit(); |
| if (tokenIndex < 0 || tokenIndex >= tokens.Count) |
| { |
| throw new ArgumentOutOfRangeException(tokenIndex + " not in 0.." + (tokens.Count - 1)); |
| } |
| if (tokenIndex == 0) |
| { |
| // obviously no tokens can appear before the first token |
| return null; |
| } |
| int prevOnChannel = PreviousTokenOnChannel(tokenIndex - 1, Lexer.DefaultTokenChannel); |
| if (prevOnChannel == tokenIndex - 1) |
| { |
| return null; |
| } |
| // if none onchannel to left, prevOnChannel=-1 then from=0 |
| int from = prevOnChannel + 1; |
| int to = tokenIndex - 1; |
| return FilterForChannel(from, to, channel); |
| } |
| |
| /// <summary> |
| /// Collect all hidden tokens (any off-default channel) to the left of |
| /// the current token up until we see a token on |
| /// <see cref="Lexer.DefaultTokenChannel"/> |
| /// . |
| /// </summary> |
| public virtual IList<IToken> GetHiddenTokensToLeft(int tokenIndex) |
| { |
| return GetHiddenTokensToLeft(tokenIndex, -1); |
| } |
| |
| protected internal virtual IList<IToken> FilterForChannel(int from, int to, int channel) |
| { |
| IList<IToken> hidden = new List<IToken>(); |
| for (int i = from; i <= to; i++) |
| { |
| IToken t = tokens[i]; |
| if (channel == -1) |
| { |
| if (t.Channel != Lexer.DefaultTokenChannel) |
| { |
| hidden.Add(t); |
| } |
| } |
| else |
| { |
| if (t.Channel == channel) |
| { |
| hidden.Add(t); |
| } |
| } |
| } |
| if (hidden.Count == 0) |
| { |
| return null; |
| } |
| return hidden; |
| } |
| |
| public virtual string SourceName |
| { |
| get |
| { |
| return _tokenSource.SourceName; |
| } |
| } |
| |
| /// <summary>Get the text of all tokens in this buffer.</summary> |
| /// <remarks>Get the text of all tokens in this buffer.</remarks> |
| [return: NotNull] |
| public virtual string GetText() |
| { |
| Fill(); |
| return GetText(Interval.Of(0, Size - 1)); |
| } |
| |
| [return: NotNull] |
| public virtual string GetText(Interval interval) |
| { |
| int start = interval.a; |
| int stop = interval.b; |
| if (start < 0 || stop < 0) |
| { |
| return string.Empty; |
| } |
| LazyInit(); |
| if (stop >= tokens.Count) |
| { |
| stop = tokens.Count - 1; |
| } |
| StringBuilder buf = new StringBuilder(); |
| for (int i = start; i <= stop; i++) |
| { |
| IToken t = tokens[i]; |
| if (t.Type == TokenConstants.EOF) |
| { |
| break; |
| } |
| buf.Append(t.Text); |
| } |
| return buf.ToString(); |
| } |
| |
| [return: NotNull] |
| public virtual string GetText(RuleContext ctx) |
| { |
| return GetText(ctx.SourceInterval); |
| } |
| |
| [return: NotNull] |
| public virtual string GetText(IToken start, IToken stop) |
| { |
| if (start != null && stop != null) |
| { |
| return GetText(Interval.Of(start.TokenIndex, stop.TokenIndex)); |
| } |
| return string.Empty; |
| } |
| |
| /// <summary>Get all tokens from lexer until EOF.</summary> |
| /// <remarks>Get all tokens from lexer until EOF.</remarks> |
| public virtual void Fill() |
| { |
| LazyInit(); |
| int blockSize = 1000; |
| while (true) |
| { |
| int fetched = Fetch(blockSize); |
| if (fetched < blockSize) |
| { |
| return; |
| } |
| } |
| } |
| } |
| } |