##// END OF EJS Templates
minor fixes
minor fixes

File last commit:

r70:0349189d2564 default
r132:5fb2bbffdece v2
Show More
JSONParser.cs
280 lines | 11.3 KiB | text/x-csharp | CSharpLexer
using Implab;
using Implab.Parsing;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
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>
/// <remarks>
/// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>,
/// оно означает текущий уровень вложенности объектов, однако закрывающий
/// элемент объекта и массива имеет уровень меньше, чем сам объект.
/// <code>
/// { // Level = 1
/// "name" : "Peter", // Level = 1
/// "address" : { // Level = 2
/// city : "Stern" // Level = 2
/// } // Level = 1
/// } // Level = 0
/// </code>
/// </remarks>
public class JSONParser : DFAutomaton<JSONParserContext>, IDisposable {
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;
/// <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;
} 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));
}
/// <summary>
/// Признак конца потока
/// </summary>
public bool EOF {
get {
return m_scanner.EOF;
}
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
m_scanner.Dispose();
}
}
/// <summary>
/// Освобождает парсер и связанный с ним сканнер.
/// </summary>
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
~JSONParser() {
Dispose(false);
}
/// <summary>
/// Переходит в конец текущего объекта.
/// </summary>
public void SeekElementEnd() {
var level = Level - 1;
Debug.Assert(level >= 0);
while (Level != level)
Read();
}
}
}