using Implab; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Implab.Parsing { /// /// Базовый класс для разбора потока входных символов на токены. /// /// /// Сканнер имеет внутри буффер с симолами входного текста, по которому перемещаются два /// указателя, начала и конца токена, при перемещении искользуется ДКА для определения /// конца токена и допустимости текущего символа. /// public class Scanner { struct ScannerConfig { public DFAStateDescriptior[] states; public int[] alphabetMap; } Stack m_defs = new Stack(); DFAStateDescriptior[] m_states; int[] m_alphabetMap; protected DFAStateDescriptior m_currentState; int m_previewCode; protected int m_tokenLen = 0; protected int m_tokenOffset; protected char[] m_buffer; protected int m_bufferSize; protected int m_pointer; public Scanner(CDFADefinition definition, string text) { Safe.ArgumentNotNull(definition, "definition"); Safe.ArgumentNotEmpty(text, "text"); m_states = definition.States; m_alphabetMap = definition.Alphabet.GetTranslationMap(); Feed(text.ToCharArray()); } public Scanner(CDFADefinition definition) { Safe.ArgumentNotNull(definition, "definition"); m_states = definition.States; m_alphabetMap = definition.Alphabet.GetTranslationMap(); Feed(new char[0]); } /// /// Заполняет входными данными буффер. /// /// Данные для обработки. /// Копирование данных не происходит, переданный массив используется в /// качестве входного буффера. 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"); m_pointer = -1; m_buffer = data; m_bufferSize = length; Shift(); } /// /// Получает текущий токен в виде строки. /// /// public string GetTokenValue() { return new String(m_buffer, m_tokenOffset, m_tokenLen); } /// /// Метки текущего токена, которые были назначены в регулярном выражении. /// public int[] TokenTags { get { return m_currentState.tag; } } /// /// Читает следующий токен, при этом указывает на начало токена, /// на длину токена, - массив символов, в /// котором находится токен. /// /// false - достигнут конец данных, токен не прочитан. protected bool ReadTokenInternal() { if (m_pointer >= m_bufferSize) return false; m_currentState = m_states[CDFADefinition.INITIAL_STATE]; m_tokenLen = 0; m_tokenOffset = m_pointer; int nextState = CDFADefinition.UNREACHEBLE_STATE; do { nextState = m_currentState.transitions[m_previewCode]; if (nextState == CDFADefinition.UNREACHEBLE_STATE) { if (m_currentState.final) return true; else throw new ParserException( String.Format( "Unexpected symbol '{0}', at pos {1}", m_buffer[m_pointer], Position ) ); } else { m_currentState = m_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) { return ReadNextChunk(); } m_previewCode = m_alphabetMap[m_buffer[m_pointer]]; return true; } /// /// Вызывается по достижению конца входного буффера для получения /// новых данных. /// /// true - новые двнные получены, можно продолжать обработку. protected virtual bool ReadNextChunk() { return false; } /// /// Позиция сканнера во входном буфере /// public int Position { get { return m_pointer + 1; } } /// /// Преключает внутренний ДКА на указанный, позволяет реализовать подобие захватывающей /// группировки. /// /// Таблица состояний нового ДКА /// Таблица входных символов для нового ДКА protected void Switch(DFAStateDescriptior[] states, int[] alphabet) { Safe.ArgumentNotNull(states, "dfa"); m_defs.Push(new ScannerConfig { states = m_states, alphabetMap = m_alphabetMap }); m_states = states; m_alphabetMap = alphabet; m_previewCode = m_alphabetMap[m_buffer[m_pointer]]; } /// /// Восстанавливает предыдущей ДКА сканнера. /// protected void Restore() { if (m_defs.Count == 0) throw new InvalidOperationException(); var prev = m_defs.Pop(); m_states = prev.states; m_alphabetMap = prev.alphabetMap; m_previewCode = m_alphabetMap[m_buffer[m_pointer]]; } } }