231 lines
6.1 KiB
C#
231 lines
6.1 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
namespace Flee.Parsing
|
|
{
|
|
// * A look-ahead character stream reader. This class provides the
|
|
// * functionalities of a buffered line-number reader, but with the
|
|
// * additional possibility of peeking an unlimited number of
|
|
// * characters ahead. When looking further and further ahead in the
|
|
// * character stream, the buffer is continously enlarged to contain
|
|
// * all the required characters from the current position an
|
|
// * onwards. This means that looking more characters ahead requires
|
|
// * more memory, and thus becomes unviable in the end.
|
|
internal class LookAheadReader : TextReader
|
|
{
|
|
private const int StreamBlockSize = 4096;
|
|
private const int BufferBlockSize = 1024;
|
|
private char[] _buffer = new char[StreamBlockSize];
|
|
private int _pos;
|
|
private int _length;
|
|
private TextReader _input = null;
|
|
private int _line = 1;
|
|
private int _column = 1;
|
|
|
|
public LookAheadReader(TextReader input) : base()
|
|
{
|
|
this._input = input;
|
|
}
|
|
|
|
public int LineNumber => _line;
|
|
|
|
public int ColumnNumber => _column;
|
|
|
|
public override int Read()
|
|
{
|
|
ReadAhead(1);
|
|
if (_pos >= _length)
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
UpdateLineColumnNumbers(1);
|
|
return Convert.ToInt32(_buffer[System.Math.Max(System.Threading.Interlocked.Increment(ref _pos), _pos - 1)]);
|
|
}
|
|
}
|
|
|
|
public override int Read(char[] cbuf, int off, int len)
|
|
{
|
|
ReadAhead(len);
|
|
if (_pos >= _length)
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
var count = _length - _pos;
|
|
if (count > len)
|
|
{
|
|
count = len;
|
|
}
|
|
UpdateLineColumnNumbers(count);
|
|
Array.Copy(_buffer, _pos, cbuf, off, count);
|
|
_pos += count;
|
|
return count;
|
|
}
|
|
}
|
|
|
|
public string ReadString(int len)
|
|
{
|
|
ReadAhead(len);
|
|
if (_pos >= _length)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
var count = _length - _pos;
|
|
if (count > len)
|
|
{
|
|
count = len;
|
|
}
|
|
UpdateLineColumnNumbers(count);
|
|
var result = new string(_buffer, _pos, count);
|
|
_pos += count;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public override int Peek()
|
|
{
|
|
return Peek(0);
|
|
}
|
|
|
|
public int Peek(int off)
|
|
{
|
|
ReadAhead(off + 1);
|
|
if (_pos + off >= _length)
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return Convert.ToInt32(_buffer[_pos + off]);
|
|
}
|
|
}
|
|
|
|
public string PeekString(int off, int len)
|
|
{
|
|
ReadAhead(off + len + 1);
|
|
if (_pos + off >= _length)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
var count = _length - (_pos + off);
|
|
if (count > len)
|
|
{
|
|
count = len;
|
|
}
|
|
return new string(_buffer, _pos + off, count);
|
|
}
|
|
}
|
|
|
|
public override void Close()
|
|
{
|
|
_buffer = null;
|
|
_pos = 0;
|
|
_length = 0;
|
|
if (_input != null)
|
|
{
|
|
_input.Close();
|
|
_input = null;
|
|
}
|
|
}
|
|
|
|
private void ReadAhead(int offset)
|
|
{
|
|
int size = 0;
|
|
int readSize = 0;
|
|
|
|
// Check for end of stream or already read characters
|
|
if (_input == null || _pos + offset < _length)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Remove old characters from buffer
|
|
if (_pos > BufferBlockSize)
|
|
{
|
|
Array.Copy(_buffer, _pos, _buffer, 0, _length - _pos);
|
|
_length -= _pos;
|
|
_pos = 0;
|
|
}
|
|
|
|
// Calculate number of characters to read
|
|
size = _pos + offset - _length + 1;
|
|
if (size % StreamBlockSize != 0)
|
|
{
|
|
size = (size / StreamBlockSize) * StreamBlockSize;
|
|
size += StreamBlockSize;
|
|
}
|
|
EnsureBufferCapacity(_length + size);
|
|
|
|
// Read characters
|
|
try
|
|
{
|
|
readSize = _input.Read(_buffer, _length, size);
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
_input = null;
|
|
throw;
|
|
}
|
|
|
|
// Append characters to buffer
|
|
if (readSize > 0)
|
|
{
|
|
_length += readSize;
|
|
}
|
|
if (readSize < size)
|
|
{
|
|
try
|
|
{
|
|
_input.Close();
|
|
}
|
|
finally
|
|
{
|
|
_input = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void EnsureBufferCapacity(int size)
|
|
{
|
|
char[] newbuf = null;
|
|
|
|
if (_buffer.Length >= size)
|
|
{
|
|
return;
|
|
}
|
|
if (size % BufferBlockSize != 0)
|
|
{
|
|
size = (size / BufferBlockSize) * BufferBlockSize;
|
|
size += BufferBlockSize;
|
|
}
|
|
newbuf = new char[size];
|
|
Array.Copy(_buffer, 0, newbuf, 0, _length);
|
|
_buffer = newbuf;
|
|
}
|
|
|
|
private void UpdateLineColumnNumbers(int offset)
|
|
{
|
|
for (int i = 0; i <= offset - 1; i++)
|
|
{
|
|
if (_buffer.Contains(_buffer[_pos + i]))
|
|
{
|
|
_line += 1;
|
|
_column = 1;
|
|
}
|
|
else
|
|
{
|
|
_column += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|