diff --git a/Implab/Automaton/Scanner.cs b/Implab/Automaton/Scanner.cs --- a/Implab/Automaton/Scanner.cs +++ b/Implab/Automaton/Scanner.cs @@ -1,255 +1,255 @@ -using Implab; -using System; -using System.Collections.Generic; -using System.IO; -using Implab.Components; -using Implab.Automaton.RegularExpressions; - -namespace Implab.Automaton { - /// - /// Базовый класс для разбора потока входных символов на токены. - /// - /// - /// Сканнер имеет внутри буффер с симолами входного текста, по которому перемещаются два - /// указателя, начала и конца токена, при перемещении искользуется ДКА для определения - /// конца токена и допустимости текущего символа. - /// - public abstract class Scanner : Disposable { - protected struct ScannerConfig { - public readonly DFAStateDescriptor[] states; - public readonly int[] alphabet; - public readonly int initialState; - - public ScannerConfig(DFAStateDescriptor[] states, int[] alphabet, int initialState) { - this.initialState = initialState; - this.alphabet = alphabet; - this.states = states; - } - } - - Stack m_defs = new Stack(); - - ScannerConfig m_config; - - protected DFAStateDescriptor m_currentState; - int m_previewCode; - - protected int m_tokenLen; - protected int m_tokenOffset; - - protected char[] m_buffer; - protected int m_bufferSize; - protected int m_pointer; - - TextReader m_reader; - bool m_disposeReader; - int m_chunkSize = 1024; // 1k - int m_limit = 10 * 1024 * 1024; // 10Mb - - protected Scanner(ScannerConfig config) { - Safe.ArgumentNotEmpty(config.states, "config.states"); - Safe.ArgumentNotNull(config.alphabet, "config.alphabet"); - - m_config = config; - } - - /// - /// Заполняет входными данными буффер. - /// - /// Данные для обработки. - /// Копирование данных не происходит, переданный массив используется в - /// качестве входного буффера. - public void Feed(char[] data) { - Safe.ArgumentNotNull(data, "data"); - - Feed(data, data.Length); - } - - /// - /// Заполняет буффур чтения входными данными. - /// - /// Данные для обработки. - /// Длина данных для обработки. - /// Копирование данных не происходит, переданный массив используется в - /// качестве входного буффера. - public void Feed(char[] data, int length) { - Safe.ArgumentNotNull(data, "data"); - Safe.ArgumentInRange(length, 0, data.Length, "length"); - AssertNotDisposed(); - - m_pointer = -1; - m_buffer = data; - m_bufferSize = length; - Shift(); - } - - public void Feed(TextReader reader, bool dispose) { - Safe.ArgumentNotNull(reader, "reader"); - AssertNotDisposed(); - - if (m_reader != null && m_disposeReader) - m_reader.Dispose(); - - m_reader = reader; - m_disposeReader = dispose; - m_pointer = -1; - m_buffer = new char[m_chunkSize]; - m_bufferSize = 0; - Shift(); - } - - /// - /// Получает текущий токен в виде строки. - /// - /// - protected string GetTokenValue() { - return new String(m_buffer, m_tokenOffset, m_tokenLen); - } - - /// - /// Метки текущего токена, которые были назначены в регулярном выражении. - /// - protected TTag[] TokenTags { - get { - return m_currentState.tags; - } - } - - /// - /// Признак конца данных - /// - public bool EOF { - get { - return m_pointer >= m_bufferSize; - } - } - - /// - /// Читает следующий токен, при этом указывает на начало токена, - /// на длину токена, - массив символов, в - /// котором находится токен. - /// - /// false - достигнут конец данных, токен не прочитан. - protected bool ReadTokenInternal() { - if (m_pointer >= m_bufferSize) - return false; - - m_currentState = m_config.states[m_config.initialState]; - m_tokenLen = 0; - m_tokenOffset = m_pointer; - int nextState; - do { - nextState = m_currentState.transitions[m_previewCode]; - if (nextState == DFAConst.UNREACHABLE_STATE) { - if (m_currentState.final) - return true; - - throw new ParserException( - String.Format( - "Unexpected symbol '{0}', at pos {1}", - m_buffer[m_pointer], - Position - ) - ); - } - m_currentState = m_config.states[nextState]; - m_tokenLen++; - - } while (Shift()); - - // END OF DATA - if (!m_currentState.final) - throw new ParserException("Unexpected end of data"); - - return true; - } - - - bool Shift() { - m_pointer++; - - if (m_pointer >= m_bufferSize) { - if (!ReadNextChunk()) - return false; - } - - m_previewCode = m_config.alphabet[m_buffer[m_pointer]]; - - return true; - } - - bool ReadNextChunk() { - if (m_reader == null) - return false; - - // extend buffer if nesessary - if (m_pointer + m_chunkSize > m_buffer.Length) { - // trim unused buffer head - var size = m_tokenLen + m_chunkSize; - if (size >= m_limit) - throw new ParserException(String.Format("Input buffer {0} bytes limit exceeded", m_limit)); - var temp = new char[size]; - Array.Copy(m_buffer, m_tokenOffset, temp, 0, m_tokenLen); - m_pointer -= m_tokenOffset; - m_bufferSize -= m_tokenOffset; - m_tokenOffset = 0; - m_buffer = temp; - } - - var read = m_reader.Read(m_buffer, m_tokenLen, m_chunkSize); - if (read == 0) - return false; - - m_bufferSize += read; - - return true; - } - - /// - /// Позиция сканнера во входном буфере - /// - public int Position { - get { - return m_pointer + 1; - } - } - - /// - /// Преключает внутренний ДКА на указанный, позволяет реализовать подобие захватывающей - /// группировки. - /// - /// - protected void Switch(ScannerConfig config) { - Safe.ArgumentNotNull(config.states, "config.states"); - - m_defs.Push(m_config); - m_config = config; - - m_previewCode = m_config.alphabet[m_buffer[m_pointer]]; - } - - /// - /// Восстанавливает предыдущей ДКА сканнера. - /// - protected void Restore() { - if (m_defs.Count == 0) - throw new InvalidOperationException(); - m_config = m_defs.Pop(); - - m_previewCode = m_config.alphabet[m_buffer[m_pointer]]; - } - - protected override void Dispose(bool disposing) { - if (disposing) { - if (m_reader != null && m_disposeReader) - m_reader.Dispose(); - m_buffer = null; - m_bufferSize = 0; - m_pointer = 0; - m_tokenLen = 0; - m_tokenOffset = 0; - } - base.Dispose(disposing); - } - } -} +using Implab; +using System; +using System.Collections.Generic; +using System.IO; +using Implab.Components; +using Implab.Automaton.RegularExpressions; + +namespace Implab.Automaton { + /// + /// Базовый класс для разбора потока входных символов на токены. + /// + /// + /// Сканнер имеет внутри буффер с симолами входного текста, по которому перемещаются два + /// указателя, начала и конца токена, при перемещении искользуется ДКА для определения + /// конца токена и допустимости текущего символа. + /// + public abstract class Scanner : Disposable { + protected struct ScannerConfig { + public readonly DFAStateDescriptor[] states; + public readonly int[] alphabet; + public readonly int initialState; + + public ScannerConfig(DFAStateDescriptor[] states, int[] alphabet, int initialState) { + this.initialState = initialState; + this.alphabet = alphabet; + this.states = states; + } + } + + Stack m_defs = new Stack(); + + ScannerConfig m_config; + + protected DFAStateDescriptor m_currentState; + int m_previewCode; + + protected int m_tokenLen; + protected int m_tokenOffset; + + protected char[] m_buffer; + protected int m_bufferSize; + protected int m_pointer; + + TextReader m_reader; + bool m_disposeReader; + int m_chunkSize = 1024; // 1k + int m_limit = 10 * 1024 * 1024; // 10Mb + + protected Scanner(ScannerConfig config) { + Safe.ArgumentNotEmpty(config.states, "config.states"); + Safe.ArgumentNotNull(config.alphabet, "config.alphabet"); + + m_config = config; + } + + /// + /// Заполняет входными данными буффер. + /// + /// Данные для обработки. + /// Копирование данных не происходит, переданный массив используется в + /// качестве входного буффера. + public void Feed(char[] data) { + Safe.ArgumentNotNull(data, "data"); + + Feed(data, data.Length); + } + + /// + /// Заполняет буффур чтения входными данными. + /// + /// Данные для обработки. + /// Длина данных для обработки. + /// Копирование данных не происходит, переданный массив используется в + /// качестве входного буффера. + public void Feed(char[] data, int length) { + Safe.ArgumentNotNull(data, "data"); + Safe.ArgumentInRange(length, 0, data.Length, "length"); + AssertNotDisposed(); + + m_pointer = -1; + m_buffer = data; + m_bufferSize = length; + Shift(); + } + + public void Feed(TextReader reader, bool dispose) { + Safe.ArgumentNotNull(reader, "reader"); + AssertNotDisposed(); + + if (m_reader != null && m_disposeReader) + m_reader.Dispose(); + + m_reader = reader; + m_disposeReader = dispose; + m_pointer = -1; + m_buffer = new char[m_chunkSize]; + m_bufferSize = 0; + Shift(); + } + + /// + /// Получает текущий токен в виде строки. + /// + /// + protected string GetTokenValue() { + return new String(m_buffer, m_tokenOffset, m_tokenLen); + } + + /// + /// Метки текущего токена, которые были назначены в регулярном выражении. + /// + protected TTag[] TokenTags { + get { + return m_currentState.tags; + } + } + + /// + /// Признак конца данных + /// + public bool EOF { + get { + return m_pointer >= m_bufferSize; + } + } + + /// + /// Читает следующий токен, при этом указывает на начало токена, + /// на длину токена, - массив символов, в + /// котором находится токен. + /// + /// false - достигнут конец данных, токен не прочитан. + protected bool ReadTokenInternal() { + if (m_pointer >= m_bufferSize) + return false; + + m_currentState = m_config.states[m_config.initialState]; + m_tokenLen = 0; + m_tokenOffset = m_pointer; + int nextState; + do { + nextState = m_currentState.transitions[m_previewCode]; + if (nextState == DFAConst.UNREACHABLE_STATE) { + if (m_currentState.final) + return true; + + throw new ParserException( + String.Format( + "Unexpected symbol '{0}', at pos {1}", + m_buffer[m_pointer], + Position + ) + ); + } + m_currentState = m_config.states[nextState]; + m_tokenLen++; + + } while (Shift()); + + // END OF DATA + if (!m_currentState.final) + throw new ParserException("Unexpected end of data"); + + return true; + } + + + bool Shift() { + m_pointer++; + + if (m_pointer >= m_bufferSize) { + if (!ReadNextChunk()) + return false; + } + + m_previewCode = m_config.alphabet[m_buffer[m_pointer]]; + + return true; + } + + bool ReadNextChunk() { + if (m_reader == null) + return false; + + // extend buffer if nesessary + if (m_pointer + m_chunkSize > m_buffer.Length) { + // trim unused buffer head + var size = m_tokenLen + m_chunkSize; + if (size >= m_limit) + throw new ParserException(String.Format("Input buffer {0} bytes limit exceeded", m_limit)); + var temp = new char[size]; + Array.Copy(m_buffer, m_tokenOffset, temp, 0, m_tokenLen); + m_pointer -= m_tokenOffset; + m_bufferSize -= m_tokenOffset; + m_tokenOffset = 0; + m_buffer = temp; + } + + var read = m_reader.Read(m_buffer, m_tokenLen, m_chunkSize); + if (read == 0) + return false; + + m_bufferSize += read; + + return true; + } + + /// + /// Позиция сканнера во входном буфере + /// + public int Position { + get { + return m_pointer + 1; + } + } + + /// + /// Преключает внутренний ДКА на указанный, позволяет реализовать подобие захватывающей + /// группировки. + /// + /// + protected void Switch(ScannerConfig config) { + Safe.ArgumentNotNull(config.states, "config.states"); + + m_defs.Push(m_config); + m_config = config; + + m_previewCode = m_config.alphabet[m_buffer[m_pointer]]; + } + + /// + /// Восстанавливает предыдущей ДКА сканнера. + /// + protected void Restore() { + if (m_defs.Count == 0) + throw new InvalidOperationException(); + m_config = m_defs.Pop(); + + m_previewCode = m_config.alphabet[m_buffer[m_pointer]]; + } + + protected override void Dispose(bool disposing) { + if (disposing) { + if (m_reader != null && m_disposeReader) + m_reader.Dispose(); + m_buffer = null; + m_bufferSize = 0; + m_pointer = 0; + m_tokenLen = 0; + m_tokenOffset = 0; + } + base.Dispose(disposing); + } + } +} diff --git a/Implab/Formats/BufferScanner.cs b/Implab/Formats/BufferScanner.cs --- a/Implab/Formats/BufferScanner.cs +++ b/Implab/Formats/BufferScanner.cs @@ -1,125 +1,45 @@ using System; using Implab.Automaton.RegularExpressions; using Implab.Automaton; +using System.Diagnostics; namespace Implab.Formats { public struct BufferScanner { - char[] m_buffer; - int m_offset; - int m_position; - int m_hi; - - readonly int m_chunk; - readonly int m_limit; - readonly DFAStateDescriptor[] m_dfa; int m_state; + int m_pos; - public BufferScanner(DFAStateDescriptor[] dfa, int initialState, int chunk, int limit) { + public BufferScanner(DFAStateDescriptor[] dfa, int initialState) { m_dfa = dfa; m_state = initialState; - m_chunk = chunk; - m_limit = limit; - m_buffer = null; - m_offset = 0; - m_position = 0; - m_hi = 0; - } - - public char[] Buffer { - get { - return m_buffer; - } - } - - public int HiMark { - get { - return m_hi; - } } public int Position { - get { - return m_position; - } - } - - public int Length { - get { - return m_hi - m_position; - } - } - - public int TokenOffset { - get { - return m_offset; - } - } - - public int TokenLength { - get { - return m_position - m_offset; - } - } - - public void Init(char[] buffer, int position, int length) { - m_buffer = buffer; - m_position = position; - m_offset = position; - m_hi = position + length; - } - - public int Extend() { - // free space - var free = m_buffer.Length - m_hi; - - // if the buffer have enough free space - if (free > 0) - return free; - - // effective size of the buffer - var size = m_buffer.Length - m_offset; - - // calculate the new size - int grow = Math.Min(m_limit - size, m_chunk); - if (grow <= 0) - throw new ParserException(String.Format("Input buffer {0} bytes limit exceeded", m_limit)); - - var temp = new char[size + grow]; - Array.Copy(m_buffer, m_offset, temp, 0, m_hi - m_offset); - m_position -= m_offset; - m_hi -= m_offset; - m_offset = 0; - m_buffer = temp; - - return free + grow; - } - - public void RaiseMark(int size) { - m_hi += size; + get { return m_pos; } } /// /// Scan this instance. /// /// true - additional data required - public bool Scan() { - while (m_position < m_hi) { - var ch = m_buffer[m_position]; - var next = m_dfa[m_state].transitions[(int)ch]; + public bool Scan(int[] buffer, int position, int length) { + var hi = position + length; + m_pos = position; + + while (position < hi) { + var next = m_dfa[m_state].transitions[buffer[position]]; if (next == DFAConst.UNREACHABLE_STATE) { if (m_dfa[m_state].final) return false; throw new ParserException( String.Format( - "Unexpected token '{0}'", - new string(m_buffer, m_offset, m_position - m_offset) + "Unexpected symbol" ) ); } + m_pos++; m_state = next; - m_position++; } return true; @@ -129,8 +49,7 @@ namespace Implab.Formats { if (!m_dfa[m_state].final) throw new ParserException( String.Format( - "Unexpected token '{0}'", - new string(m_buffer, m_offset, m_position - m_offset) + "Unexpected EOF" ) ); } diff --git a/Implab/Formats/TextScanner.cs b/Implab/Formats/TextScanner.cs --- a/Implab/Formats/TextScanner.cs +++ b/Implab/Formats/TextScanner.cs @@ -4,11 +4,11 @@ using Implab.Components; namespace Implab.Formats { public abstract class TextScanner : Disposable { - char[] m_buffer; - int m_offset; - int m_length; - int m_tokenOffset; + readonly int[] m_buffer; + int m_bufferOffset; + int m_dataLength; int m_tokenLength; + TTag[] m_tags; BufferScanner m_scanner; @@ -17,29 +17,36 @@ namespace Implab.Formats { if (EOF) return false; - // create a new scanner from template (scanners are structs) + // create a new scanner from template (scanners are value types) var inst = m_scanner; - // initialize the scanner - inst.Init(m_buffer, m_offset, m_length); + m_tokenLength = 0; + + while (inst.Scan(m_buffer, m_bufferOffset, m_dataLength)) { + m_tokenLength += m_dataLength; + + var actual = Read(m_buffer, 0, m_buffer.Length); + + m_bufferOffset = 0; + m_dataLength = actual; - // do work - while (inst.Scan()) - Feed(ref inst); + if (actual == 0) { + inst.Eof(); + break; + } + } + + var len = inst.Position - m_bufferOffset; + m_tokenLength += len; + m_dataLength -= len; + m_bufferOffset = inst.Position; // save result; - m_buffer = inst.Buffer; - m_length = inst.Length; - m_offset = inst.Position; - m_tokenOffset = inst.TokenOffset; - m_tokenLength = inst.TokenLength; - + m_tags = inst.GetTokenTags(); } - protected string GetToken() { - return new String(m_buffer, m_tokenOffset, m_tokenLength); - } + protected abstract int Read(int[] buffer, int offset, int size); protected TTag[] Tags { get { @@ -47,26 +54,8 @@ namespace Implab.Formats { } } - /// - /// Feed the specified scanner. - /// - /// Scanner. - /// - /// protected override void Feed(ref BufferScanner scanner) { - /// var size = scanner.Extend(); - /// var actual = m_reader.Read(scanner.Buffer, scanner.HiMark, size); - /// if (actual == 0) { - /// m_eof = true; - /// scanner.Eof(); - /// } else { - /// scanner.RaiseHiMark(actual); - /// } - /// } - /// - protected abstract void Feed(ref BufferScanner scanner); - public abstract bool EOF { get; } - + } }