##// END OF EJS Templates
fixed DFA optimization, JSON is fully functional
fixed DFA optimization, JSON is fully functional

File last commit:

r180:c32688129f14 ref20160224
r183:4f82e0f161c3 ref20160224
Show More
JSONParser.cs
293 lines | 11.0 KiB | text/x-csharp | CSharpLexer
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 {
/// <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 : 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<ParserContext> m_stack = new Stack<ParserContext>();
ParserContext m_context = _jsonContext;
/// <summary>
/// Создает новый парсер на основе строки, содержащей JSON
/// </summary>
/// <param name="text"></param>
public JSONParser(string text) {
Safe.ArgumentNotEmpty(text, "text");
m_scanner = new JSONScanner(text);
}
/// <summary>
/// Создает новый экземпляр парсера, на основе текстового потока.
/// </summary>
/// <param name="reader">Текстовый поток.</param>
public JSONParser(TextReader reader) {
Safe.ArgumentNotNull(reader, "reader");
m_scanner = new JSONScanner(reader);
}
public int Level {
get { return m_stack.Count; }
}
/// <summary>
/// Тип текущего элемента на котором стоит парсер.
/// </summary>
public JSONElementType ElementType {
get { return m_elementType; }
}
/// <summary>
/// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
/// пустая строка.
/// </summary>
public string ElementName {
get { return m_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() {
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));
}
/// <summary>
/// Признак конца потока
/// </summary>
public bool EOF {
get;
private set;
}
protected override void Dispose(bool disposing) {
if (disposing)
Safe.Dispose(m_scanner);
}
/// <summary>
/// Переходит в конец текущего объекта.
/// </summary>
public void SeekElementEnd() {
var level = Level - 1;
Debug.Assert(level >= 0);
while (Level != level)
Read();
}
}
}