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 {
///
/// internal
///
public struct JSONParserContext {
public string memberName;
public JSONElementContext elementContext;
}
///
/// Pull парсер JSON данных.
///
public class JSONParser : DFAutomaton, IDisposable {
enum MemberContext {
MemberName,
MemberValue
}
static readonly EnumAlphabet _alphabet = EnumAlphabet.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 BuildDFA(Token expr) {
var builder = new DFABuilder();
var dfa = new EDFADefinition(_alphabet);
expr.Accept(builder);
builder.BuildDFA(dfa);
return dfa;
}
JSONScanner m_scanner;
MemberContext m_memberContext;
JSONElementType m_elementType;
object m_elementValue;
///
/// Создает новый парсер на основе строки, содержащей JSON
///
///
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 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);
}
///
/// Тип текущего элемента на котором стоит парсер.
///
public JSONElementType ElementType {
get { return m_elementType; }
}
///
/// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
/// пустая строка.
///
public string ElementName {
get { return m_context.info.memberName; }
}
///
/// Значение элемента. Только для элементов типа , для остальных null
///
public object ElementValue {
get { return m_elementValue; }
}
///
/// Читает слеюудущий объект из потока
///
/// true - операция чтения прошла успешно, false - конец данных
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));
}
///
/// Признак конца потока
///
public bool EOF {
get {
return m_scanner.EOF;
}
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
m_scanner.Dispose();
}
}
///
/// Освобождает парсер и связанный с ним сканнер.
///
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
~JSONParser() {
Dispose(false);
}
public void Skip() {
var level = Level-1;
Debug.Assert(level >= 0);
while (Level != level)
Read();
}
}
}