Mercurial > pub > ImplabNet
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/JSON/JSONParser.cs Sun Jun 15 19:39:11 2014 +0400 @@ -0,0 +1,197 @@ +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)); + } + + } + +}