blob: c6c00e7657d127844f6e29a0133fb468b683dd10 [file] [log] [blame] [edit]
/* 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.Text;
using Antlr4.Runtime;
using Antlr4.Runtime.Misc;
using Antlr4.Runtime.Sharpen;
namespace Antlr4.Runtime
{
public class UnbufferedTokenStream : ITokenStream
{
private ITokenSource _tokenSource;
/// <summary>A moving window buffer of the data being scanned.</summary>
/// <remarks>
/// A moving window buffer of the data being scanned. While there's a marker,
/// we keep adding to buffer. Otherwise,
/// <see cref="Consume()">consume()</see>
/// resets so
/// we start filling at index 0 again.
/// </remarks>
protected internal IToken[] tokens;
/// <summary>
/// The number of tokens currently in
/// <see cref="tokens">tokens</see>
/// .
/// <p>This is not the buffer capacity, that's
/// <c>tokens.length</c>
/// .</p>
/// </summary>
protected internal int n;
/// <summary>
/// 0..n-1 index into
/// <see cref="tokens">tokens</see>
/// of next token.
/// <p>The
/// <c>LT(1)</c>
/// token is
/// <c>tokens[p]</c>
/// . If
/// <c>p == n</c>
/// , we are
/// out of buffered tokens.</p>
/// </summary>
protected internal int p = 0;
/// <summary>
/// Count up with
/// <see cref="Mark()">mark()</see>
/// and down with
/// <see cref="Release(int)">release()</see>
/// . When we
/// <c>release()</c>
/// the last mark,
/// <c>numMarkers</c>
/// reaches 0 and we reset the buffer. Copy
/// <c>tokens[p]..tokens[n-1]</c>
/// to
/// <c>tokens[0]..tokens[(n-1)-p]</c>
/// .
/// </summary>
protected internal int numMarkers = 0;
/// <summary>
/// This is the
/// <c>LT(-1)</c>
/// token for the current position.
/// </summary>
protected internal IToken lastToken;
/// <summary>
/// When
/// <c>numMarkers &gt; 0</c>
/// , this is the
/// <c>LT(-1)</c>
/// token for the
/// first token in
/// <see cref="tokens"/>
/// . Otherwise, this is
/// <see langword="null"/>
/// .
/// </summary>
protected internal IToken lastTokenBufferStart;
/// <summary>Absolute token index.</summary>
/// <remarks>
/// Absolute token index. It's the index of the token about to be read via
/// <c>LT(1)</c>
/// . Goes from 0 to the number of tokens in the entire stream,
/// although the stream size is unknown before the end is reached.
/// <p>This value is used to set the token indexes if the stream provides tokens
/// that implement
/// <see cref="IWritableToken"/>
/// .</p>
/// </remarks>
protected internal int currentTokenIndex = 0;
public UnbufferedTokenStream(ITokenSource tokenSource)
: this(tokenSource, 256)
{
}
public UnbufferedTokenStream(ITokenSource tokenSource, int bufferSize)
{
this.TokenSource = tokenSource;
this.tokens = new IToken[bufferSize];
n = 0;
Fill(1);
}
// prime the pump
public virtual IToken Get(int i)
{
int bufferStartIndex = GetBufferStartIndex();
if (i < bufferStartIndex || i >= bufferStartIndex + n)
{
throw new ArgumentOutOfRangeException("get(" + i + ") outside buffer: " + bufferStartIndex + ".." + (bufferStartIndex + n));
}
return tokens[i - bufferStartIndex];
}
public virtual IToken LT(int i)
{
if (i == -1)
{
return lastToken;
}
Sync(i);
int index = p + i - 1;
if (index < 0)
{
throw new ArgumentOutOfRangeException("LT(" + i + ") gives negative index");
}
if (index >= n)
{
System.Diagnostics.Debug.Assert(n > 0 && tokens[n - 1].Type == TokenConstants.EOF);
return tokens[n - 1];
}
return tokens[index];
}
public virtual int LA(int i)
{
return LT(i).Type;
}
public virtual ITokenSource TokenSource
{
get
{
return _tokenSource;
}
set
{
_tokenSource = value;
}
}
[return: NotNull]
public virtual string GetText()
{
return string.Empty;
}
[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));
}
throw new NotSupportedException("The specified start and stop symbols are not supported.");
}
public virtual void Consume()
{
if (LA(1) == TokenConstants.EOF)
{
throw new InvalidOperationException("cannot consume EOF");
}
// buf always has at least tokens[p==0] in this method due to ctor
lastToken = tokens[p];
// track last token for LT(-1)
// if we're at last token and no markers, opportunity to flush buffer
if (p == n - 1 && numMarkers == 0)
{
n = 0;
p = -1;
// p++ will leave this at 0
lastTokenBufferStart = lastToken;
}
p++;
currentTokenIndex++;
Sync(1);
}
/// <summary>
/// Make sure we have 'need' elements from current position
/// <see cref="p">p</see>
/// . Last valid
/// <c>p</c>
/// index is
/// <c>tokens.length-1</c>
/// .
/// <c>p+need-1</c>
/// is the tokens index 'need' elements
/// ahead. If we need 1 element,
/// <c>(p+1-1)==p</c>
/// must be less than
/// <c>tokens.length</c>
/// .
/// </summary>
protected internal virtual void Sync(int want)
{
int need = (p + want - 1) - n + 1;
// how many more elements we need?
if (need > 0)
{
Fill(need);
}
}
/// <summary>
/// Add
/// <paramref name="n"/>
/// elements to the buffer. Returns the number of tokens
/// actually added to the buffer. If the return value is less than
/// <paramref name="n"/>
/// ,
/// then EOF was reached before
/// <paramref name="n"/>
/// tokens could be added.
/// </summary>
protected internal virtual int Fill(int n)
{
for (int i = 0; i < n; i++)
{
if (this.n > 0 && tokens[this.n - 1].Type == TokenConstants.EOF)
{
return i;
}
IToken t = TokenSource.NextToken();
Add(t);
}
return n;
}
protected internal virtual void Add(IToken t)
{
if (n >= tokens.Length)
{
tokens = Arrays.CopyOf(tokens, tokens.Length * 2);
}
if (t is IWritableToken)
{
((IWritableToken)t).TokenIndex = GetBufferStartIndex() + n;
}
tokens[n++] = t;
}
/// <summary>Return a marker that we can release later.</summary>
/// <remarks>
/// Return a marker that we can release later.
/// <p>The specific marker value used for this class allows for some level of
/// protection against misuse where
/// <c>seek()</c>
/// is called on a mark or
/// <c>release()</c>
/// is called in the wrong order.</p>
/// </remarks>
public virtual int Mark()
{
if (numMarkers == 0)
{
lastTokenBufferStart = lastToken;
}
int mark = -numMarkers - 1;
numMarkers++;
return mark;
}
public virtual void Release(int marker)
{
int expectedMark = -numMarkers;
if (marker != expectedMark)
{
throw new InvalidOperationException("release() called with an invalid marker.");
}
numMarkers--;
if (numMarkers == 0)
{
// can we release buffer?
if (p > 0)
{
// Copy tokens[p]..tokens[n-1] to tokens[0]..tokens[(n-1)-p], reset ptrs
// p is last valid token; move nothing if p==n as we have no valid char
System.Array.Copy(tokens, p, tokens, 0, n - p);
// shift n-p tokens from p to 0
n = n - p;
p = 0;
}
lastTokenBufferStart = lastToken;
}
}
public virtual int Index
{
get
{
return currentTokenIndex;
}
}
public virtual void Seek(int index)
{
// seek to absolute index
if (index == currentTokenIndex)
{
return;
}
if (index > currentTokenIndex)
{
Sync(index - currentTokenIndex);
index = Math.Min(index, GetBufferStartIndex() + n - 1);
}
int bufferStartIndex = GetBufferStartIndex();
int i = index - bufferStartIndex;
if (i < 0)
{
throw new ArgumentException("cannot seek to negative index " + index);
}
else
{
if (i >= n)
{
throw new NotSupportedException("seek to index outside buffer: " + index + " not in " + bufferStartIndex + ".." + (bufferStartIndex + n));
}
}
p = i;
currentTokenIndex = index;
if (p == 0)
{
lastToken = lastTokenBufferStart;
}
else
{
lastToken = tokens[p - 1];
}
}
public virtual int Size
{
get
{
throw new NotSupportedException("Unbuffered stream cannot know its size");
}
}
public virtual string SourceName
{
get
{
return TokenSource.SourceName;
}
}
[return: NotNull]
public virtual string GetText(Interval interval)
{
int bufferStartIndex = GetBufferStartIndex();
int bufferStopIndex = bufferStartIndex + tokens.Length - 1;
int start = interval.a;
int stop = interval.b;
if (start < bufferStartIndex || stop > bufferStopIndex)
{
throw new NotSupportedException("interval " + interval + " not in token buffer window: " + bufferStartIndex + ".." + bufferStopIndex);
}
int a = start - bufferStartIndex;
int b = stop - bufferStartIndex;
StringBuilder buf = new StringBuilder();
for (int i = a; i <= b; i++)
{
IToken t = tokens[i];
buf.Append(t.Text);
}
return buf.ToString();
}
protected internal int GetBufferStartIndex()
{
return currentTokenIndex - p;
}
}
}