view Implab/Formats/JSON/JSONScanner.cs @ 187:dd4a3590f9c6 ref20160224

Reworked cancelation handling, if the cancel handler isn't specified the OperationCanceledException will be handled by the error handler Any unhandled OperationCanceledException will cause the promise cancelation
author cin
date Tue, 19 Apr 2016 17:35:20 +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);
        }
    }
}