JSONParser.cs
293 lines
| 11.0 KiB
| text/x-csharp
|
CSharpLexer
|
|
r165 | using System; | ||
|
|
r163 | using System.Diagnostics; | ||
| using System.IO; | ||||
|
|
r165 | using Implab.Automaton; | ||
| using Implab.Automaton.RegularExpressions; | ||||
| using System.Linq; | ||||
| using Implab.Components; | ||||
|
|
r180 | using System.Collections.Generic; | ||
|
|
r163 | |||
|
|
r165 | namespace Implab.Formats.JSON { | ||
|
|
r163 | /// <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> | ||||
|
|
r165 | public class JSONParser : Disposable { | ||
|
|
r163 | |||
| enum MemberContext { | ||||
| MemberName, | ||||
| MemberValue | ||||
| } | ||||
|
|
r178 | #region Parser rules | ||
|
|
r165 | struct ParserContext { | ||
|
|
r178 | readonly int[,] m_dfa; | ||
| int m_state; | ||||
| readonly JSONElementContext m_elementContext; | ||||
|
|
r165 | |||
|
|
r178 | public ParserContext(int[,] dfa, int state, JSONElementContext context) { | ||
| m_dfa = dfa; | ||||
| m_state = state; | ||||
| m_elementContext = context; | ||||
| } | ||||
| public bool Move(JsonTokenType token) { | ||||
|
|
r180 | var next = m_dfa[m_state, (int)token]; | ||
|
|
r178 | if (next == AutomatonConst.UNREACHABLE_STATE) | ||
| return false; | ||||
| m_state = next; | ||||
|
|
r180 | return true; | ||
|
|
r178 | } | ||
| public JSONElementContext ElementContext { | ||||
| get { return m_elementContext; } | ||||
| } | ||||
| } | ||||
|
|
r163 | |||
|
|
r180 | static readonly ParserContext _jsonContext; | ||
| static readonly ParserContext _objectContext; | ||||
| static readonly ParserContext _arrayContext; | ||||
|
|
r163 | static JSONParser() { | ||
|
|
r180 | var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String); | ||
| var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression); | ||||
|
|
r163 | |||
| var objectExpression = memberExpression | ||||
| .Cat( | ||||
|
|
r180 | MakeToken(JsonTokenType.ValueSeparator) | ||
|
|
r163 | .Cat(memberExpression) | ||
| .EClosure() | ||||
| ) | ||||
| .Optional() | ||||
|
|
r180 | .Cat(MakeToken(JsonTokenType.EndObject)) | ||
|
|
r178 | .End(); | ||
|
|
r163 | var arrayExpression = valueExpression | ||
| .Cat( | ||||
|
|
r180 | MakeToken(JsonTokenType.ValueSeparator) | ||
|
|
r163 | .Cat(valueExpression) | ||
| .EClosure() | ||||
| ) | ||||
| .Optional() | ||||
|
|
r180 | .Cat(MakeToken(JsonTokenType.EndArray)) | ||
|
|
r178 | .End(); | ||
|
|
r163 | |||
|
|
r178 | var jsonExpression = valueExpression.End(); | ||
|
|
r163 | |||
|
|
r180 | _jsonContext = CreateParserContext(jsonExpression, JSONElementContext.None); | ||
| _objectContext = CreateParserContext(objectExpression, JSONElementContext.Object); | ||||
| _arrayContext = CreateParserContext(arrayExpression, JSONElementContext.Array); | ||||
|
|
r163 | } | ||
|
|
r180 | static Token MakeToken(params JsonTokenType[] input) { | ||
|
|
r178 | return Token.New( input.Select(t => (int)t).ToArray() ); | ||
|
|
r165 | } | ||
|
|
r178 | static ParserContext CreateParserContext(Token expr, JSONElementContext context) { | ||
| var dfa = new DFATable(); | ||||
| var builder = new RegularExpressionVisitor(dfa); | ||||
|
|
r163 | expr.Accept(builder); | ||
|
|
r178 | builder.BuildDFA(); | ||
|
|
r163 | |||
|
|
r178 | return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context); | ||
|
|
r163 | } | ||
|
|
r178 | #endregion | ||
|
|
r180 | readonly JSONScanner m_scanner; | ||
|
|
r163 | MemberContext m_memberContext; | ||
| JSONElementType m_elementType; | ||||
| object m_elementValue; | ||||
|
|
r180 | string m_memberName = String.Empty; | ||
| Stack<ParserContext> m_stack = new Stack<ParserContext>(); | ||||
| ParserContext m_context = _jsonContext; | ||||
|
|
r163 | |||
| /// <summary> | ||||
| /// Создает новый парсер на основе строки, содержащей JSON | ||||
| /// </summary> | ||||
| /// <param name="text"></param> | ||||
|
|
r180 | public JSONParser(string text) { | ||
|
|
r163 | Safe.ArgumentNotEmpty(text, "text"); | ||
|
|
r180 | m_scanner = new JSONScanner(text); | ||
|
|
r163 | } | ||
| /// <summary> | ||||
| /// Создает новый экземпляр парсера, на основе текстового потока. | ||||
| /// </summary> | ||||
| /// <param name="reader">Текстовый поток.</param> | ||||
|
|
r180 | public JSONParser(TextReader reader) { | ||
|
|
r163 | Safe.ArgumentNotNull(reader, "reader"); | ||
|
|
r180 | m_scanner = new JSONScanner(reader); | ||
| } | ||||
| public int Level { | ||||
| get { return m_stack.Count; } | ||||
|
|
r163 | } | ||
| /// <summary> | ||||
| /// Тип текущего элемента на котором стоит парсер. | ||||
| /// </summary> | ||||
| public JSONElementType ElementType { | ||||
| get { return m_elementType; } | ||||
| } | ||||
| /// <summary> | ||||
| /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда | ||||
| /// пустая строка. | ||||
| /// </summary> | ||||
| public string ElementName { | ||||
|
|
r180 | get { return m_memberName; } | ||
|
|
r163 | } | ||
| /// <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() { | ||||
| object tokenValue; | ||||
| JsonTokenType tokenType; | ||||
|
|
r180 | |||
| m_memberName = String.Empty; | ||||
|
|
r163 | while (m_scanner.ReadToken(out tokenValue, out tokenType)) { | ||
|
|
r180 | if(!m_context.Move(tokenType)) | ||
|
|
r163 | UnexpectedToken(tokenValue, tokenType); | ||
|
|
r180 | |||
|
|
r163 | switch (tokenType) { | ||
| case JsonTokenType.BeginObject: | ||||
|
|
r180 | m_stack.Push(m_context); | ||
| m_context = _objectContext; | ||||
|
|
r163 | m_elementValue = null; | ||
| m_memberContext = MemberContext.MemberName; | ||||
| m_elementType = JSONElementType.BeginObject; | ||||
| return true; | ||||
| case JsonTokenType.EndObject: | ||||
|
|
r180 | if (m_stack.Count == 0) | ||
| UnexpectedToken(tokenValue, tokenType); | ||||
| m_context = m_stack.Pop(); | ||||
|
|
r163 | m_elementValue = null; | ||
| m_elementType = JSONElementType.EndObject; | ||||
| return true; | ||||
| case JsonTokenType.BeginArray: | ||||
|
|
r180 | m_stack.Push(m_context); | ||
| m_context = _arrayContext; | ||||
|
|
r163 | m_elementValue = null; | ||
| m_memberContext = MemberContext.MemberValue; | ||||
| m_elementType = JSONElementType.BeginArray; | ||||
| return true; | ||||
| case JsonTokenType.EndArray: | ||||
|
|
r180 | if (m_stack.Count == 0) | ||
| UnexpectedToken(tokenValue, tokenType); | ||||
| m_context = m_stack.Pop(); | ||||
|
|
r163 | m_elementValue = null; | ||
| m_elementType = JSONElementType.EndArray; | ||||
| return true; | ||||
| case JsonTokenType.String: | ||||
| if (m_memberContext == MemberContext.MemberName) { | ||||
|
|
r180 | m_memberName = (string)tokenValue; | ||
|
|
r163 | 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: | ||||
|
|
r180 | m_memberContext = m_context.ElementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue; | ||
|
|
r163 | break; | ||
| default: | ||||
| UnexpectedToken(tokenValue, tokenType); | ||||
| break; | ||||
| } | ||||
| } | ||||
|
|
r180 | if (m_context.ElementContext != JSONElementContext.None) | ||
|
|
r163 | throw new ParserException("Unexpedted end of data"); | ||
|
|
r180 | |||
| EOF = true; | ||||
|
|
r163 | 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 { | ||||
|
|
r180 | get; | ||
| private set; | ||||
|
|
r163 | } | ||
|
|
r165 | protected override void Dispose(bool disposing) { | ||
|
|
r180 | if (disposing) | ||
| Safe.Dispose(m_scanner); | ||||
|
|
r163 | } | ||
| /// <summary> | ||||
| /// Переходит в конец текущего объекта. | ||||
| /// </summary> | ||||
| public void SeekElementEnd() { | ||||
| var level = Level - 1; | ||||
| Debug.Assert(level >= 0); | ||||
| while (Level != level) | ||||
| Read(); | ||||
| } | ||||
| } | ||||
| } | ||||
