view Implab/JSON/JSONParser.cs @ 55:c0bf853aa04f

Added initial JSON support +JSONParser +JSONWriter
author cin
date Sun, 15 Jun 2014 19:39:11 +0400
parents
children 7759c80cad95
line wrap: on
line source

using Implab;
using Implab.Parsing;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Implab.JSON {
    /// <summary>
    /// internal
    /// </summary>
    public struct JSONParserContext {
        public string memberName;
        public JSONElementContext elementContext;
    }

    /// <summary>
    /// Pull парсер JSON данных.
    /// </summary>
    public class JSONParser : DFAutomaton<JSONParserContext> {

        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 jsonExpression = Token.New(JsonTokenType.BeginObject, JsonTokenType.BeginArray).Tag(0);

            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);

            _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;

        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());
        }

        public JSONElementType ElementType {
            get { return m_elementType; }
        }

        public string ElementName {
            get { return m_context.info.memberName; }
        }

        public object ElementValue {
            get { return m_elementValue; }
        }

        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;
                        } else {
                            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));
        }

    }

}