view Implab/Formats/JSON/JSONScanner.cs @ 170:181119ef3b39 ref20160224

DFA refactoring, rx based dfa.
author cin
date Fri, 04 Mar 2016 01:56:31 +0300
parents e227e78d72e4
children 0c3c69fe225b
line wrap: on
line source

using System;
using System.Globalization;
using Implab.Automaton;

namespace Implab.Formats.JSON {
    /// <summary>
    /// Сканнер (лексер), разбивающий поток символов на токены JSON.
    /// </summary>
    public class JSONScanner : Scanner<object> {
        char[] m_stringBuffer;
        DFAStateDescriptior<>[] m_stringDFA;
        int[] m_stringAlphabet;

        /// <summary>
        /// Создает новый экземпляр сканнера
        /// </summary>
        public JSONScanner()
            : base(JSONGrammar.Instance.JsonDFA.GetTransitionTable(), JSONGrammar.Instance.JsonDFA.Alphabet.GetTranslationMap()) {
            m_stringBuffer = new char[1024];
            var dfa = JSONGrammar.Instance.JsonStringDFA;
            m_stringAlphabet = dfa.Alphabet.GetTranslationMap();
            m_stringDFA = dfa.States;
        }

        /// <summary>
        /// Читает следующий лексический элемент из входных данных.
        /// </summary>
        /// <param name="tokenValue">Возвращает значение прочитанного токена.</param>
        /// <param name="tokenType">Возвращает тип прочитанного токена.</param>
        /// <returns><c>true</c> - чтение произведено успешно. <c>false</c> - достигнут конец входных данных</returns>
        /// <remarks>В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е.
        /// в строках обрабатываются экранированные символы, числа становтся типа double.</remarks>
        public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) {
            if (ReadTokenInternal()) {
                switch ((JSONGrammar.TokenType)m_currentState.tag[0]) {
                    case JSONGrammar.TokenType.StringBound:
                        tokenValue = ReadString();
                        tokenType = JsonTokenType.String;
                        break;
                    case JSONGrammar.TokenType.Number:
                        tokenValue = Double.Parse(new String(m_buffer, m_tokenOffset, m_tokenLen), CultureInfo.InvariantCulture);
                        tokenType = JsonTokenType.Number;
                        break;
                    default:
                        tokenType = (JsonTokenType)m_currentState.tag[0];
                        tokenValue = new String(m_buffer, m_tokenOffset, m_tokenLen);
                        break;
                }
                return true;
            }
            tokenValue = null;
            tokenType = JsonTokenType.None;
            return false;
        }

        string ReadString() {
            int pos = 0;
            Switch(m_stringDFA, m_stringAlphabet);
            while (ReadTokenInternal()) {
                switch ((JSONGrammar.TokenType)m_currentState.tag[0]) {
                    case JSONGrammar.TokenType.StringBound:
                        Restore();
                        return new String(m_stringBuffer, 0, pos);
                    case JSONGrammar.TokenType.UnescapedChar:
                        EnsureStringBufferSize(pos + m_tokenLen);
                        Array.Copy(m_buffer, m_tokenOffset, m_stringBuffer, pos, m_tokenLen);
                        pos += m_tokenLen;
                        break;
                    case JSONGrammar.TokenType.EscapedUnicode:
                        EnsureStringBufferSize(pos + 1);
                        m_stringBuffer[pos] = StringTranslator.TranslateHexUnicode(m_buffer, m_tokenOffset + 2);
                        pos++;
                        break;
                    case JSONGrammar.TokenType.EscapedChar:
                        EnsureStringBufferSize(pos + 1);
                        m_stringBuffer[pos] = StringTranslator.TranslateEscapedChar(m_buffer[m_tokenOffset + 1]);
                        pos++;
                        break;
                    default:
                        break;
                }

            }

            throw new ParserException("Unexpected end of data");
        }

        void EnsureStringBufferSize(int size) {
            if (size > m_stringBuffer.Length) {
                var newBuffer = new char[size];
                m_stringBuffer.CopyTo(newBuffer, 0);
                m_stringBuffer = newBuffer;
            }
        }
    }
}