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