diff Implab/Formats/Json/JsonReader.cs @ 230:3e26338eb977 v2

slowly cutting off mono specific settings
author cin
date Wed, 13 Sep 2017 16:55:13 +0300
parents Implab/Formats/JSON/JsonReader.cs@5f7a3e1d32b9
children 302ca905c19e
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Formats/Json/JsonReader.cs	Wed Sep 13 16:55:13 2017 +0300
@@ -0,0 +1,318 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using Implab.Automaton;
+using Implab.Automaton.RegularExpressions;
+using System.Linq;
+using Implab.Components;
+using System.Collections.Generic;
+using System.Text;
+using System.Globalization;
+
+namespace Implab.Formats.Json {
+    /// <summary>
+    /// Pull парсер JSON данных.
+    /// </summary>
+    /// <remarks>
+    /// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>,
+    /// оно означает текущий уровень вложенности объектов, однако закрывающий
+    /// элемент объекта и массива имеет уровень меньше, чем сам объект.
+    /// <code>
+    /// { // Level = 1
+    ///     "name" : "Peter", // Level = 1
+    ///     "address" : { // Level = 2
+    ///         city : "Stern" // Level = 2
+    ///     } // Level = 1
+    /// } // Level = 0
+    /// </code>
+    /// </remarks>
+    public class JsonReader : Disposable {
+
+        enum MemberContext {
+            MemberName,
+            MemberValue
+        }
+
+        #region Parser rules
+        struct ParserContext {
+            readonly int[,] m_dfa;
+            int m_state;
+
+            readonly JsonElementContext m_elementContext;
+
+            public ParserContext(int[,] dfa, int state, JsonElementContext context) {
+                m_dfa = dfa;
+                m_state = state;
+                m_elementContext = context;
+            }
+
+            public bool Move(JsonTokenType token) {
+                var next = m_dfa[m_state, (int)token];
+                if (next == AutomatonConst.UNREACHABLE_STATE)
+                    return false;
+                m_state = next;
+                return true;
+            }
+
+            public JsonElementContext ElementContext {
+                get { return m_elementContext; }
+            }
+        }
+
+        static readonly ParserContext _jsonContext;
+        static readonly ParserContext _objectContext;
+        static readonly ParserContext _arrayContext;
+
+        static JsonReader() {
+
+            var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String);
+            var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression);
+
+            var objectExpression = memberExpression
+                .Cat(
+                    MakeToken(JsonTokenType.ValueSeparator)
+                    .Cat(memberExpression)
+                    .EClosure()
+                )
+                .Optional()
+                .Cat(MakeToken(JsonTokenType.EndObject))
+                .End();
+            
+            var arrayExpression = valueExpression
+                .Cat(
+                    MakeToken(JsonTokenType.ValueSeparator)
+                    .Cat(valueExpression)
+                    .EClosure()
+                )
+                .Optional()
+                .Cat(MakeToken(JsonTokenType.EndArray))
+                .End();
+
+            var jsonExpression = valueExpression.End();
+
+            _jsonContext = CreateParserContext(jsonExpression, JsonElementContext.None);
+            _objectContext = CreateParserContext(objectExpression, JsonElementContext.Object);
+            _arrayContext = CreateParserContext(arrayExpression, JsonElementContext.Array);
+        }
+
+        static Token MakeToken(params JsonTokenType[] input) {
+            return Token.New( input.Select(t => (int)t).ToArray() );
+        }
+
+        static ParserContext CreateParserContext(Token expr, JsonElementContext context) {
+            
+            var dfa = new DFATable();
+            var builder = new RegularExpressionVisitor(dfa);
+            expr.Accept(builder);
+            builder.BuildDFA();
+
+            return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context);
+        }
+
+        #endregion
+
+        readonly JsonScanner m_scanner;
+        // json starts from the value context and may content even a single literal
+        MemberContext m_memberContext = MemberContext.MemberValue;
+
+        JsonElementType m_elementType;
+        object m_elementValue;
+        string m_memberName = String.Empty;
+
+        Stack<ParserContext> m_stack = new Stack<ParserContext>();
+        ParserContext m_context = _jsonContext;
+
+        /// <summary>
+        /// Создает новый парсер на основе строки, содержащей JSON
+        /// </summary>
+        /// <param name="text"></param>
+        JsonReader(JsonScanner scanner) {
+            m_scanner = scanner;
+        }
+        
+        public int Level {
+            get { return m_stack.Count; }
+        }
+
+        /// <summary>
+        /// Тип текущего элемента на котором стоит парсер.
+        /// </summary>
+        public JsonElementType ElementType {
+            get { return m_elementType; }
+        }
+
+        /// <summary>
+        /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
+        /// пустая строка.
+        /// </summary>
+        public string ElementName {
+            get { return m_memberName; }
+        }
+
+        /// <summary>
+        /// Значение элемента. Только для элементов типа <see cref="JsonElementType.Value"/>, для остальных <c>null</c>
+        /// </summary>
+        public object ElementValue {
+            get { return m_elementValue; }
+        }
+
+        /// <summary>
+        /// Читает слеюудущий объект из потока
+        /// </summary>
+        /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns>
+        public bool Read() {
+            string tokenValue;
+            JsonTokenType tokenType;
+
+            m_memberName = String.Empty;
+
+            while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
+                if(!m_context.Move(tokenType))
+                    UnexpectedToken(tokenValue, tokenType);
+
+                switch (tokenType) {
+                    case JsonTokenType.BeginObject:
+                        m_stack.Push(m_context);
+                        m_context = _objectContext;
+
+                        m_elementValue = null;
+                        m_memberContext = MemberContext.MemberName;
+                        m_elementType = JsonElementType.BeginObject;
+                        return true;
+                    case JsonTokenType.EndObject:
+                        if (m_stack.Count == 0)
+                            UnexpectedToken(tokenValue, tokenType);
+                        m_context = m_stack.Pop();
+
+                        m_elementValue = null;
+                        m_elementType = JsonElementType.EndObject;
+                        return true;
+                    case JsonTokenType.BeginArray:
+                        m_stack.Push(m_context);
+                        m_context = _arrayContext;
+
+                        m_elementValue = null;
+                        m_memberContext = MemberContext.MemberValue;
+                        m_elementType = JsonElementType.BeginArray;
+                        return true;
+                    case JsonTokenType.EndArray:
+                        if (m_stack.Count == 0)
+                            UnexpectedToken(tokenValue, tokenType);
+                        m_context = m_stack.Pop();
+
+                        m_elementValue = null;
+                        m_elementType = JsonElementType.EndArray;
+                        return true;
+                    case JsonTokenType.String:
+                        if (m_memberContext == MemberContext.MemberName) {
+                            m_memberName = tokenValue;
+                            break;
+                        }
+                        m_elementType = JsonElementType.Value;
+                        m_elementValue = tokenValue;
+                        return true;
+                    case JsonTokenType.Number:
+                        m_elementType = JsonElementType.Value;
+                        m_elementValue = double.Parse(tokenValue, CultureInfo.InvariantCulture);
+                        return true;
+                    case JsonTokenType.Literal:
+                        m_elementType = JsonElementType.Value;
+                        m_elementValue = ParseLiteral(tokenValue);
+                        return true;
+                    case JsonTokenType.NameSeparator:
+                        m_memberContext = MemberContext.MemberValue;
+                        break;
+                    case JsonTokenType.ValueSeparator:
+                        m_memberContext = m_context.ElementContext == JsonElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
+                        break;
+                    default:
+                        UnexpectedToken(tokenValue, tokenType);
+                        break;
+                }
+            }
+            if (m_context.ElementContext != JsonElementContext.None)
+                throw new ParserException("Unexpedted end of data");
+
+            Eof = true;
+
+            return false;
+        }
+
+        object ParseLiteral(string literal) {
+            switch (literal) {
+                case "null":
+                    return null;
+                case "false":
+                    return false;
+                case "true":
+                    return true;
+                default:
+                    UnexpectedToken(literal, JsonTokenType.Literal);
+                    return null; // avoid compliler error
+            }
+        }
+
+        void UnexpectedToken(object value, JsonTokenType tokenType) {
+            throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value));
+        }
+
+
+        /// <summary>
+        /// Признак конца потока
+        /// </summary>
+        public bool Eof {
+            get;
+            private set;
+        }
+
+        protected override void Dispose(bool disposing) {
+            if (disposing)
+                m_scanner.Dispose();
+        }
+
+        /// <summary>
+        /// Переходит в конец текущего объекта.
+        /// </summary>
+        public void SeekElementEnd() {
+            var level = Level - 1;
+
+            Debug.Assert(level >= 0);
+
+            while (Level != level)
+                Read();
+        }
+
+        public static JsonReader Create(string file, Encoding encoding) {
+            return new JsonReader(JsonTextScanner.Create(file, encoding));
+        }
+
+        public static JsonReader Create(string file) {
+            return new JsonReader(JsonTextScanner.Create(file));
+        }
+
+        public static JsonReader Create(Stream stream, Encoding encoding) {
+            return new JsonReader(JsonTextScanner.Create(stream, encoding));
+        }
+
+        public static JsonReader Create(Stream stream) {
+            return new JsonReader(JsonTextScanner.Create(stream));
+        }
+
+        public static JsonReader Create(TextReader reader) {
+            return new JsonReader(JsonTextScanner.Create(reader));
+        }
+
+        public static JsonReader ParseString(string data) {
+            return new JsonReader(JsonStringScanner.Create(data));
+        }
+
+        public static JsonReader ParseString(string data, int offset, int length) {
+            return new JsonReader(JsonStringScanner.Create(data, offset, length));
+        }
+
+        public static JsonReader ParseString(char[] data, int offset, int lenght) {
+            return new JsonReader(JsonStringScanner.Create(data, offset, lenght));
+        }
+    }
+
+}