Mercurial > pub > ImplabNet
view Implab/Parsing/Scanner.cs @ 57:7759c80cad95
minor changes
author | cin |
---|---|
date | Tue, 17 Jun 2014 03:30:49 +0400 |
parents | c0bf853aa04f |
children | 21611344d366 |
line wrap: on
line source
using Implab; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Implab.Parsing { /// <summary> /// Базовый класс для разбора потока входных символов на токены. /// </summary> /// <remarks> /// Сканнер имеет внутри буффер с симолами входного текста, по которому перемещаются два /// указателя, начала и конца токена, при перемещении искользуется ДКА для определения /// конца токена и допустимости текущего символа. /// </remarks> public class Scanner { struct ScannerConfig { public DFAStateDescriptior[] states; public int[] alphabetMap; } Stack<ScannerConfig> m_defs = new Stack<ScannerConfig>(); 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]); } /// <summary> /// Заполняет входными данными буффер. /// </summary> /// <param name="data">Данные для обработки.</param> /// <remarks>Копирование данных не происходит, переданный массив используется в /// качестве входного буффера.</remarks> public void Feed(char[] data) { Safe.ArgumentNotNull(data, "data"); Feed(data, data.Length); } /// <summary> /// Заполняет буффур чтения входными данными. /// </summary> /// <param name="data">Данные для обработки.</param> /// <param name="length">Длина данных для обработки.</param> /// <remarks>Копирование данных не происходит, переданный массив используется в /// качестве входного буффера.</remarks> 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(); } /// <summary> /// Получает текущий токен в виде строки. /// </summary> /// <returns></returns> public string GetTokenValue() { return new String(m_buffer, m_tokenOffset, m_tokenLen); } /// <summary> /// Метки текущего токена, которые были назначены в регулярном выражении. /// </summary> public int[] TokenTags { get { return m_currentState.tag; } } /// <summary> /// Признак конца данных /// </summary> public bool EOF { get { return m_pointer >= m_bufferSize; } } /// <summary> /// Читает следующий токен, при этом <see cref="m_tokenOffset"/> указывает на начало токена, /// <see cref="m_tokenLen"/> на длину токена, <see cref="m_buffer"/> - массив символов, в /// котором находится токен. /// </summary> /// <returns><c>false</c> - достигнут конец данных, токен не прочитан.</returns> 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; } /// <summary> /// Вызывается по достижению конца входного буффера для получения /// новых данных. /// </summary> /// <returns><c>true</c> - новые двнные получены, можно продолжать обработку.</returns> protected virtual bool ReadNextChunk() { return false; } /// <summary> /// Позиция сканнера во входном буфере /// </summary> public int Position { get { return m_pointer + 1; } } /// <summary> /// Преключает внутренний ДКА на указанный, позволяет реализовать подобие захватывающей /// группировки. /// </summary> /// <param name="states">Таблица состояний нового ДКА</param> /// <param name="alphabet">Таблица входных символов для нового ДКА</param> 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]]; } /// <summary> /// Восстанавливает предыдущей ДКА сканнера. /// </summary> 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]]; } } }