|
|
using System;
|
|
|
using System.Globalization;
|
|
|
using Implab.Automaton;
|
|
|
using System.Text;
|
|
|
using Implab.Components;
|
|
|
using System.IO;
|
|
|
|
|
|
namespace Implab.Formats.JSON {
|
|
|
/// <summary>
|
|
|
/// Сканнер (лексер), разбивающий поток символов на токены JSON.
|
|
|
/// </summary>
|
|
|
public class JSONScanner : Disposable {
|
|
|
readonly StringBuilder m_builder = new StringBuilder();
|
|
|
|
|
|
readonly ScannerContext<JSONGrammar.TokenType> m_jsonContext = JSONGrammar.Instance.JsonExpression;
|
|
|
readonly ScannerContext<JSONGrammar.TokenType> m_stringContext = JSONGrammar.Instance.JsonStringExpression;
|
|
|
|
|
|
|
|
|
readonly TextScanner m_scanner;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Создает новый экземпляр сканнера
|
|
|
/// </summary>
|
|
|
public JSONScanner(string text) {
|
|
|
Safe.ArgumentNotEmpty(text, "text");
|
|
|
|
|
|
m_scanner = new StringScanner(text);
|
|
|
}
|
|
|
|
|
|
public JSONScanner(TextReader reader, int bufferMax, int chunkSize) {
|
|
|
Safe.ArgumentNotNull(reader, "reader");
|
|
|
|
|
|
m_scanner = new ReaderScanner(reader, bufferMax, chunkSize);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Читает следующий лексический элемент из входных данных.
|
|
|
/// </summary>
|
|
|
/// <param name="tokenValue">Возвращает значение прочитанного токена.</param>
|
|
|
/// <param name="tokenType">Возвращает тип прочитанного токена.</param>
|
|
|
/// <returns><c>true</c> - чтение произведено успешно. <c>false</c> - достигнут конец входных данных</returns>
|
|
|
/// <remarks>В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е.
|
|
|
/// в строках обрабатываются экранированные символы, числа становтся типа double.</remarks>
|
|
|
public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) {
|
|
|
JSONGrammar.TokenType[] tag;
|
|
|
if (m_jsonContext.Execute(m_scanner, out tag)) {
|
|
|
switch (tag[0]) {
|
|
|
case JSONGrammar.TokenType.StringBound:
|
|
|
tokenValue = ReadString();
|
|
|
tokenType = JsonTokenType.String;
|
|
|
break;
|
|
|
case JSONGrammar.TokenType.Number:
|
|
|
tokenValue = Double.Parse(m_scanner.GetTokenValue(), CultureInfo.InvariantCulture);
|
|
|
tokenType = JsonTokenType.Number;
|
|
|
break;
|
|
|
default:
|
|
|
tokenType = (JsonTokenType)tag[0];
|
|
|
tokenValue = m_scanner.GetTokenValue();
|
|
|
break;
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
tokenValue = null;
|
|
|
tokenType = JsonTokenType.None;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
string ReadString() {
|
|
|
int pos = 0;
|
|
|
var buf = new char[6]; // the buffer for unescaping chars
|
|
|
|
|
|
JSONGrammar.TokenType[] tag;
|
|
|
m_builder.Clear();
|
|
|
|
|
|
while (m_stringContext.Execute(m_scanner, out tag)) {
|
|
|
switch (tag[0]) {
|
|
|
case JSONGrammar.TokenType.StringBound:
|
|
|
return m_builder.ToString();
|
|
|
case JSONGrammar.TokenType.UnescapedChar:
|
|
|
m_scanner.CopyTokenTo(m_builder);
|
|
|
break;
|
|
|
case JSONGrammar.TokenType.EscapedUnicode: // \xXXXX - unicode escape sequence
|
|
|
m_scanner.CopyTokenTo(buf, 0);
|
|
|
m_builder.Append(StringTranslator.TranslateHexUnicode(buf, 2));
|
|
|
pos++;
|
|
|
break;
|
|
|
case JSONGrammar.TokenType.EscapedChar: // \t - escape sequence
|
|
|
m_scanner.CopyTokenTo(buf, 0);
|
|
|
m_builder.Append(StringTranslator.TranslateEscapedChar(buf[1]));
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
throw new ParserException("Unexpected end of data");
|
|
|
}
|
|
|
|
|
|
protected override void Dispose(bool disposing) {
|
|
|
if (disposing)
|
|
|
Safe.Dispose(m_scanner);
|
|
|
base.Dispose(disposing);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|