diff Implab/Parsing/Scanner.cs @ 55:c0bf853aa04f

Added initial JSON support +JSONParser +JSONWriter
author cin
date Sun, 15 Jun 2014 19:39:11 +0400
parents
children 7759c80cad95
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Parsing/Scanner.cs	Sun Jun 15 19:39:11 2014 +0400
@@ -0,0 +1,207 @@
+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>
+        /// Читает следующий токен, при этом <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]];
+        }
+    }
+}