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;
using System.Text;
using System.Globalization;
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 JsonReader : 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 JsonReader() {
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;
// json starts from the value context and may content even a single literal
MemberContext m_memberContext = MemberContext.MemberValue;
JsonElementType m_elementType;
object m_elementValue;
string m_memberName = String.Empty;
Stack m_stack = new Stack();
ParserContext m_context = _jsonContext;
///
/// Создает новый парсер на основе строки, содержащей JSON
///
///
JsonReader(JsonScanner scanner) {
m_scanner = scanner;
}
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() {
string 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 = tokenValue;
break;
}
m_elementType = JsonElementType.Value;
m_elementValue = tokenValue;
return true;
case JsonTokenType.Number:
m_elementType = JsonElementType.Value;
m_elementValue = double.Parse(tokenValue, CultureInfo.InvariantCulture);
return true;
case JsonTokenType.Literal:
m_elementType = JsonElementType.Value;
m_elementValue = ParseLiteral(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();
}
public static JsonReader Create(string file, Encoding encoding) {
return new JsonReader(JsonTextScanner.Create(file, encoding));
}
public static JsonReader Create(string file) {
return new JsonReader(JsonTextScanner.Create(file));
}
public static JsonReader Create(Stream stream, Encoding encoding) {
return new JsonReader(JsonTextScanner.Create(stream, encoding));
}
public static JsonReader Create(Stream stream) {
return new JsonReader(JsonTextScanner.Create(stream));
}
public static JsonReader Create(TextReader reader) {
return new JsonReader(JsonTextScanner.Create(reader));
}
public static JsonReader ParseString(string data) {
return new JsonReader(JsonStringScanner.Create(data));
}
public static JsonReader ParseString(string data, int offset, int length) {
return new JsonReader(JsonStringScanner.Create(data, offset, length));
}
public static JsonReader ParseString(char[] data, int offset, int lenght) {
return new JsonReader(JsonStringScanner.Create(data, offset, lenght));
}
}
}