JSONParser.cs
280 lines
| 11.4 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
|
r163 | |||
cin
|
r165 | namespace Implab.Formats.JSON { | ||
cin
|
r163 | /// <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> | ||||
cin
|
r165 | public class JSONParser : Disposable { | ||
cin
|
r163 | |||
enum MemberContext { | ||||
MemberName, | ||||
MemberValue | ||||
} | ||||
cin
|
r165 | struct ParserContext { | ||
DFAStateDescriptior<object> | ||||
} | ||||
cin
|
r163 | static readonly EnumAlphabet<JsonTokenType> _alphabet = EnumAlphabet<JsonTokenType>.FullAlphabet; | ||
cin
|
r165 | static readonly DFAStateDescriptior<object>[] _jsonDFA; | ||
static readonly int _jsonDFAInitialState; | ||||
static readonly DFAStateDescriptior<object>[] _objectDFA; | ||||
static readonly int _objectDFAInitialState; | ||||
static readonly DFAStateDescriptior<object>[] _arrayDFA; | ||||
static readonly int _arrayDFAInitialState; | ||||
cin
|
r163 | |||
static JSONParser() { | ||||
cin
|
r165 | var valueExpression = Token(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String); | ||
var memberExpression = Token(JsonTokenType.String).Cat(Token(JsonTokenType.NameSeparator)).Cat(valueExpression); | ||||
cin
|
r163 | |||
var objectExpression = memberExpression | ||||
.Cat( | ||||
cin
|
r165 | Token(JsonTokenType.ValueSeparator) | ||
cin
|
r163 | .Cat(memberExpression) | ||
.EClosure() | ||||
) | ||||
.Optional() | ||||
cin
|
r165 | .Cat(Token(JsonTokenType.EndObject)) | ||
.Tag(null); | ||||
cin
|
r163 | var arrayExpression = valueExpression | ||
.Cat( | ||||
cin
|
r165 | Token(JsonTokenType.ValueSeparator) | ||
cin
|
r163 | .Cat(valueExpression) | ||
.EClosure() | ||||
) | ||||
.Optional() | ||||
cin
|
r165 | .Cat(Token(JsonTokenType.EndArray)) | ||
.Tag(null); | ||||
cin
|
r163 | |||
cin
|
r165 | var jsonExpression = valueExpression.Tag(null); | ||
cin
|
r163 | |||
cin
|
r165 | _jsonDFA = CreateDFA(jsonExpression).GetTransitionTable(); | ||
_objectDFA = CreateDFA(objectExpression).GetTransitionTable(); | ||||
_arrayDFA = CreateDFA(arrayExpression).GetTransitionTable(); | ||||
cin
|
r163 | } | ||
cin
|
r165 | static Token<object> Token(params JsonTokenType[] input) { | ||
return Token<object>.New(input.Select(t => _alphabet.Translate(t)).ToArray()); | ||||
} | ||||
static RegularDFADefinition<JsonTokenType,object> CreateDFA(Token<object> expr) { | ||||
var builder = new RegularDFABuilder<object>(); | ||||
var dfa = new RegularDFADefinition<JsonTokenType,object>(_alphabet); | ||||
cin
|
r163 | 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; | ||||
} | ||||
} | ||||
cin
|
r165 | protected override void Dispose(bool disposing) { | ||
cin
|
r163 | if (disposing) { | ||
m_scanner.Dispose(); | ||||
} | ||||
} | ||||
/// <summary> | ||||
/// Переходит в конец текущего объекта. | ||||
/// </summary> | ||||
public void SeekElementEnd() { | ||||
var level = Level - 1; | ||||
Debug.Assert(level >= 0); | ||||
while (Level != level) | ||||
Read(); | ||||
} | ||||
} | ||||
} | ||||