using System;
using Implab.Components;
using System.Diagnostics;
using Implab.Automaton;
using System.Text;
namespace Implab.Formats {
public abstract class TextScanner : Disposable {
readonly int m_bufferMax;
readonly int m_chunkSize;
char[] m_buffer;
int m_bufferOffset;
int m_bufferSize;
int m_tokenOffset;
int m_tokenLength;
///
/// Initializes a new instance of the class.
///
/// Buffer max.
/// Chunk size.
protected TextScanner(int bufferMax, int chunkSize) {
Debug.Assert(m_chunkSize <= m_bufferMax);
m_bufferMax = bufferMax;
m_chunkSize = chunkSize;
}
///
/// Initializes a new instance of the class.
///
/// Buffer.
protected TextScanner(char[] buffer) {
if (buffer != null) {
m_buffer = buffer;
m_bufferSize = buffer.Length;
}
}
///
/// (hungry) Reads the next token.
///
/// true, if token internal was read, false if there is no more tokens in the stream.
/// The transition map for the automaton
/// Final states of the automaton.
/// Tags.
/// The initial state for the automaton.
///
///
internal bool ReadToken(int[,] dfa, bool[] final, TTag[][] tags, int state, int[] alphabet, out TTag[] tag) {
m_tokenLength = 0;
tag = null;
var maxSymbol = alphabet.Length - 1;
int next;
do {
// after the next chunk is read the offset in the buffer may change
int pos = m_bufferOffset + m_tokenLength;
next = state;
while (pos < m_bufferSize) {
var ch = m_buffer[pos];
next = dfa[next, ch > maxSymbol ? AutomatonConst.UNCLASSIFIED_INPUT : alphabet[ch]];
if (next == AutomatonConst.UNREACHABLE_STATE)
break;
state = next;
pos++;
}
m_tokenLength = pos - m_bufferOffset;
} while (next != AutomatonConst.UNREACHABLE_STATE && Feed());
m_tokenOffset = m_bufferOffset;
m_bufferOffset += m_tokenLength;
if (final[state]) {
tag = tags[state];
return true;
}
if (m_bufferOffset == m_bufferSize) {
if (m_tokenLength == 0) //EOF
return false;
throw new ParserException();
}
throw new ParserException(String.Format("Unexpected symbol '{0}'", m_buffer[m_bufferOffset]));
}
protected void Feed(char[] buffer, int offset, int length) {
m_buffer = buffer;
m_bufferOffset = offset;
m_bufferSize = offset + length;
}
protected bool Feed() {
if (m_chunkSize <= 0)
return false;
if (m_buffer != null) {
var free = m_buffer.Length - m_bufferSize;
if (free < m_chunkSize) {
free += m_chunkSize;
var used = m_bufferSize - m_bufferOffset;
var size = used + free;
if (size > m_bufferMax)
throw new ParserException(String.Format("The buffer limit ({0} Kb) is reached", m_bufferMax / 1024));
var temp = new char[size];
var read = Read(temp, used, m_chunkSize);
if (read == 0)
return false;
Array.Copy(m_buffer, m_bufferOffset, temp, 0, used);
m_bufferOffset = 0;
m_bufferSize = used + read;
m_buffer = temp;
} else {
var read = Read(m_buffer, m_bufferSize, m_chunkSize);
if (read == 0)
return false;
m_bufferSize += m_chunkSize;
}
return true;
} else {
Debug.Assert(m_bufferOffset == 0);
m_buffer = new char[m_chunkSize];
m_bufferSize = Read(m_buffer, 0, m_chunkSize);
return (m_bufferSize != 0);
}
}
protected abstract int Read(char[] buffer, int offset, int size);
public string GetTokenValue() {
return new String(m_buffer, m_tokenOffset, m_tokenLength);
}
public void CopyTokenTo(char[] buffer, int offset) {
Array.Copy(m_buffer, m_tokenOffset,buffer, offset, m_tokenLength);
}
public void CopyTokenTo(StringBuilder sb) {
sb.Append(m_buffer, m_tokenOffset, m_tokenLength);
}
}
}