diff Implab/Formats/JSON/JSONParser.cs @ 163:419aa51b04fd ref20160224

JSON moved to Formats namespace Working in RegularDFA
author cin
date Wed, 24 Feb 2016 20:12:52 +0300
parents
children e227e78d72e4
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Formats/JSON/JSONParser.cs	Wed Feb 24 20:12:52 2016 +0300
@@ -0,0 +1,277 @@
+using Implab.Parsing;
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace Implab.JSON {
+    /// <summary>
+    /// internal
+    /// </summary>
+    public struct JSONParserContext {
+        public string memberName;
+        public JSONElementContext elementContext;
+    }
+
+    /// <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 JSONParser : DFAutomaton<JSONParserContext>, IDisposable {
+
+        enum MemberContext {
+            MemberName,
+            MemberValue
+        }
+
+        static readonly EnumAlphabet<JsonTokenType> _alphabet = EnumAlphabet<JsonTokenType>.FullAlphabet;
+        static readonly DFAStateDescriptior[] _jsonDFA;
+        static readonly DFAStateDescriptior[] _objectDFA;
+        static readonly DFAStateDescriptior[] _arrayDFA;
+
+        static JSONParser() {
+
+
+            var valueExpression = Token.New(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String);
+            var memberExpression = Token.New(JsonTokenType.String).Cat(Token.New(JsonTokenType.NameSeparator)).Cat(valueExpression);
+
+            var objectExpression = memberExpression
+                .Cat(
+                    Token.New(JsonTokenType.ValueSeparator)
+                    .Cat(memberExpression)
+                    .EClosure()
+                )
+                .Optional()
+                .Cat(Token.New(JsonTokenType.EndObject))
+                .Tag(0);
+            var arrayExpression = valueExpression
+                .Cat(
+                    Token.New(JsonTokenType.ValueSeparator)
+                    .Cat(valueExpression)
+                    .EClosure()
+                )
+                .Optional()
+                .Cat(Token.New(JsonTokenType.EndArray))
+                .Tag(0);
+
+            var jsonExpression = valueExpression.Tag(0);
+
+            _jsonDFA = BuildDFA(jsonExpression).States;
+            _objectDFA = BuildDFA(objectExpression).States;
+            _arrayDFA = BuildDFA(arrayExpression).States;
+        }
+
+        static EDFADefinition<JsonTokenType> BuildDFA(Token expr) {
+            var builder = new DFABuilder();
+            var dfa = new EDFADefinition<JsonTokenType>(_alphabet);
+            expr.Accept(builder);
+
+            builder.BuildDFA(dfa);
+            return dfa;
+        }
+
+        JSONScanner m_scanner;
+        MemberContext m_memberContext;
+
+        JSONElementType m_elementType;
+        object m_elementValue;
+
+        /// <summary>
+        /// Создает новый парсер на основе строки, содержащей JSON
+        /// </summary>
+        /// <param name="text"></param>
+        public JSONParser(string text)
+            : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) {
+            Safe.ArgumentNotEmpty(text, "text");
+            m_scanner = new JSONScanner();
+            m_scanner.Feed(text.ToCharArray());
+        }
+
+        /// <summary>
+        /// Создает новый экземпляр парсера, на основе текстового потока.
+        /// </summary>
+        /// <param name="reader">Текстовый поток.</param>
+        /// <param name="dispose">Признак того, что парсер должен конролировать время жизни входного потока.</param>
+        public JSONParser(TextReader reader, bool dispose)
+            : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) {
+            Safe.ArgumentNotNull(reader, "reader");
+            m_scanner = new JSONScanner();
+            m_scanner.Feed(reader, dispose);
+        }
+
+        /// <summary>
+        /// Тип текущего элемента на котором стоит парсер.
+        /// </summary>
+        public JSONElementType ElementType {
+            get { return m_elementType; }
+        }
+
+        /// <summary>
+        /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
+        /// пустая строка.
+        /// </summary>
+        public string ElementName {
+            get { return m_context.info.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() {
+            if (m_context.current == UNREACHEBLE_STATE)
+                throw new InvalidOperationException("The parser is in invalid state");
+            object tokenValue;
+            JsonTokenType tokenType;
+            m_context.info.memberName = String.Empty;
+            while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
+                Move((int)tokenType);
+                if (m_context.current == UNREACHEBLE_STATE)
+                    UnexpectedToken(tokenValue, tokenType);
+                switch (tokenType) {
+                    case JsonTokenType.BeginObject:
+                        Switch(
+                            _objectDFA,
+                            INITIAL_STATE,
+                            new JSONParserContext {
+                                memberName = m_context.info.memberName,
+                                elementContext = JSONElementContext.Object
+                            }
+                        );
+                        m_elementValue = null;
+                        m_memberContext = MemberContext.MemberName;
+                        m_elementType = JSONElementType.BeginObject;
+                        return true;
+                    case JsonTokenType.EndObject:
+                        Restore();
+                        m_elementValue = null;
+                        m_elementType = JSONElementType.EndObject;
+                        return true;
+                    case JsonTokenType.BeginArray:
+                        Switch(
+                            _arrayDFA,
+                            INITIAL_STATE,
+                            new JSONParserContext {
+                                memberName = m_context.info.memberName,
+                                elementContext = JSONElementContext.Array
+                            }
+                        );
+                        m_elementValue = null;
+                        m_memberContext = MemberContext.MemberValue;
+                        m_elementType = JSONElementType.BeginArray;
+                        return true;
+                    case JsonTokenType.EndArray:
+                        Restore();
+                        m_elementValue = null;
+                        m_elementType = JSONElementType.EndArray;
+                        return true;
+                    case JsonTokenType.String:
+                        if (m_memberContext == MemberContext.MemberName) {
+                            m_context.info.memberName = (string)tokenValue;
+                            break;
+                        }
+                        m_elementType = JSONElementType.Value;
+                        m_elementValue = tokenValue;
+                        return true;
+                    case JsonTokenType.Number:
+                        m_elementType = JSONElementType.Value;
+                        m_elementValue = tokenValue;
+                        return true;
+                    case JsonTokenType.Literal:
+                        m_elementType = JSONElementType.Value;
+                        m_elementValue = ParseLiteral((string)tokenValue);
+                        return true;
+                    case JsonTokenType.NameSeparator:
+                        m_memberContext = MemberContext.MemberValue;
+                        break;
+                    case JsonTokenType.ValueSeparator:
+                        m_memberContext = m_context.info.elementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
+                        break;
+                    default:
+                        UnexpectedToken(tokenValue, tokenType);
+                        break;
+                }
+            }
+            if (m_context.info.elementContext != JSONElementContext.None)
+                throw new ParserException("Unexpedted end of data");
+            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 {
+                return m_scanner.EOF;
+            }
+        }
+
+        protected virtual void Dispose(bool disposing) {
+            if (disposing) {
+                m_scanner.Dispose();
+            }
+        }
+
+        /// <summary>
+        /// Освобождает парсер и связанный с ним сканнер.
+        /// </summary>
+        public void Dispose() {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        ~JSONParser() {
+            Dispose(false);
+        }
+
+        /// <summary>
+        /// Переходит в конец текущего объекта.
+        /// </summary>
+        public void SeekElementEnd() {
+            var level = Level - 1;
+
+            Debug.Assert(level >= 0);
+
+            while (Level != level)
+                Read();
+        }
+    }
+
+}