JSONParser.cs
264 lines
| 10.5 KiB
| text/x-csharp
|
CSharpLexer
cin
|
r55 | using Implab; | ||
using Implab.Parsing; | ||||
using System; | ||||
using System.Collections.Generic; | ||||
using System.Diagnostics; | ||||
cin
|
r59 | using System.IO; | ||
cin
|
r55 | 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> | ||||
cin
|
r59 | public class JSONParser : DFAutomaton<JSONParserContext>, IDisposable { | ||
cin
|
r55 | |||
enum MemberContext { | ||||
MemberName, | ||||
MemberValue | ||||
cin
|
r59 | } | ||
cin
|
r55 | |||
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; | ||||
cin
|
r59 | /// <summary> | ||
/// Создает новый парсер на основе строки, содержащей JSON | ||||
/// </summary> | ||||
/// <param name="text"></param> | ||||
cin
|
r55 | public JSONParser(string text) | ||
cin
|
r59 | : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { | ||
cin
|
r55 | Safe.ArgumentNotEmpty(text, "text"); | ||
m_scanner = new JSONScanner(); | ||||
m_scanner.Feed(text.ToCharArray()); | ||||
} | ||||
cin
|
r59 | /// <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> | ||||
cin
|
r55 | public JSONElementType ElementType { | ||
get { return m_elementType; } | ||||
} | ||||
cin
|
r59 | /// <summary> | ||
/// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда | ||||
/// пустая строка. | ||||
/// </summary> | ||||
cin
|
r55 | public string ElementName { | ||
get { return m_context.info.memberName; } | ||||
} | ||||
cin
|
r59 | /// <summary> | ||
/// Значение элемента. Только для элементов типа <see cref="JSONElementType.Value"/>, для остальных <c>null</c> | ||||
/// </summary> | ||||
cin
|
r55 | public object ElementValue { | ||
get { return m_elementValue; } | ||||
} | ||||
cin
|
r59 | /// <summary> | ||
/// Читает слеюудущий объект из потока | ||||
/// </summary> | ||||
/// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns> | ||||
cin
|
r55 | 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, | ||||
cin
|
r59 | new JSONParserContext { | ||
cin
|
r55 | 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; | ||||
cin
|
r59 | case "false": | ||
cin
|
r55 | 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)); | ||||
} | ||||
cin
|
r57 | |||
cin
|
r59 | /// <summary> | ||
/// Признак конца потока | ||||
/// </summary> | ||||
cin
|
r57 | public bool EOF { | ||
get { | ||||
return m_scanner.EOF; | ||||
} | ||||
} | ||||
cin
|
r59 | |||
protected virtual void Dispose(bool disposing) { | ||||
if (disposing) { | ||||
m_scanner.Dispose(); | ||||
} | ||||
} | ||||
/// <summary> | ||||
/// Освобождает парсер и связанный с ним сканнер. | ||||
/// </summary> | ||||
public void Dispose() { | ||||
Dispose(true); | ||||
GC.SuppressFinalize(this); | ||||
} | ||||
~JSONParser() { | ||||
Dispose(false); | ||||
} | ||||
cin
|
r62 | |||
public void Skip() { | ||||
var level = Level-1; | ||||
Debug.Assert(level >= 0); | ||||
while (Level != level) | ||||
Read(); | ||||
} | ||||
cin
|
r55 | } | ||
} | ||||