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