using System;
using System.Globalization;
using Implab.Automaton;
using System.Text;
using Implab.Components;
using System.IO;
namespace Implab.Formats.JSON {
///
/// Сканнер (лексер), разбивающий поток символов на токены JSON.
///
public class JSONScanner : Disposable {
readonly StringBuilder m_builder = new StringBuilder();
readonly ScannerContext m_jsonContext = JSONGrammar.Instance.JsonExpression;
readonly ScannerContext m_stringContext = JSONGrammar.Instance.JsonStringExpression;
readonly TextScanner m_scanner;
///
/// Создает новый экземпляр сканнера
///
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);
}
public JSONScanner(TextReader reader) : this(reader, 1024*1024, 1024){
}
///
/// Читает следующий лексический элемент из входных данных.
///
/// Возвращает значение прочитанного токена.
/// Возвращает тип прочитанного токена.
/// true - чтение произведено успешно. false - достигнут конец входных данных
/// В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е.
/// в строках обрабатываются экранированные символы, числа становтся типа double.
public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) {
JSONGrammar.TokenType[] tag;
while (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;
case JSONGrammar.TokenType.Whitespace:
continue;
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)
m_scanner.Dispose();
base.Dispose(disposing);
}
}
}