view Implab/Formats/JSON/JSONScanner.cs @ 186:75103928da09 ref20160224

working on cancelation and error handling
author cin
date Tue, 19 Apr 2016 00:50:14 +0300
parents 4f82e0f161c3
children 7d07503621fe
line wrap: on
line source

using System;
using System.Globalization;
using Implab.Automaton;
using System.Text;
using Implab.Components;
using System.IO;

namespace Implab.Formats.JSON {
    /// <summary>
    /// Сканнер (лексер), разбивающий поток символов на токены JSON.
    /// </summary>
    public class JSONScanner : Disposable {
        readonly StringBuilder m_builder = new StringBuilder();

        readonly ScannerContext<JSONGrammar.TokenType> m_jsonContext = JSONGrammar.Instance.JsonExpression;
        readonly ScannerContext<JSONGrammar.TokenType> m_stringContext = JSONGrammar.Instance.JsonStringExpression;


        readonly TextScanner m_scanner;

        /// <summary>
        /// Создает новый экземпляр сканнера
        /// </summary>
        public JSONScanner(string text) {
            Safe.ArgumentNotEmpty(text, "text");

            m_scanner = new StringScanner(text);
        }

        public JSONScanner(TextReader reader, int bufferMax, int chunkSize) {
            Safe.ArgumentNotNull(reader, "reader");

            m_scanner = new ReaderScanner(reader, bufferMax, chunkSize);
        }

        public JSONScanner(TextReader reader) : this(reader, 1024*1024, 1024){
        }

        /// <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) {
            JSONGrammar.TokenType[] tag;
            while (m_jsonContext.Execute(m_scanner, out tag)) {
                switch (tag[0]) {
                    case JSONGrammar.TokenType.StringBound:
                        tokenValue = ReadString();
                        tokenType = JsonTokenType.String;
                        break;
                    case JSONGrammar.TokenType.Number:
                        tokenValue = Double.Parse(m_scanner.GetTokenValue(), CultureInfo.InvariantCulture);
                        tokenType = JsonTokenType.Number;
                        break;
                    case JSONGrammar.TokenType.Whitespace:
                        continue;
                    default:
                        tokenType = (JsonTokenType)tag[0];
                        tokenValue = m_scanner.GetTokenValue();
                        break;
                }
                return true;
            }
            tokenValue = null;
            tokenType = JsonTokenType.None;
            return false;
        }

        string ReadString() {
            int pos = 0;
            var buf = new char[6]; // the buffer for unescaping chars

            JSONGrammar.TokenType[] tag;
            m_builder.Clear();

            while (m_stringContext.Execute(m_scanner, out tag)) {
                switch (tag[0]) {
                    case JSONGrammar.TokenType.StringBound:
                        return m_builder.ToString();
                    case JSONGrammar.TokenType.UnescapedChar:
                        m_scanner.CopyTokenTo(m_builder);
                        break;
                    case JSONGrammar.TokenType.EscapedUnicode: // \xXXXX - unicode escape sequence
                        m_scanner.CopyTokenTo(buf, 0); 
                        m_builder.Append(StringTranslator.TranslateHexUnicode(buf, 2));
                        pos++;
                        break;
                    case JSONGrammar.TokenType.EscapedChar:  // \t - escape sequence
                        m_scanner.CopyTokenTo(buf, 0);
                        m_builder.Append(StringTranslator.TranslateEscapedChar(buf[1]));
                        break;
                }

            }

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

        protected override void Dispose(bool disposing) {
            if (disposing)
                Safe.Dispose(m_scanner);
            base.Dispose(disposing);
        }
    }
}