Mercurial > pub > ImplabNet
diff Implab/Formats/JSON/JSONScanner.cs @ 190:1c2a16d071a7 v2
Слияние с ref20160224
author | cin |
---|---|
date | Fri, 22 Apr 2016 13:08:08 +0300 |
parents | 4f82e0f161c3 |
children | 7d07503621fe |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Formats/JSON/JSONScanner.cs Fri Apr 22 13:08:08 2016 +0300 @@ -0,0 +1,109 @@ +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); + } + + public JSONScanner(TextReader reader) : this(reader, 1024*1024, 1024){ + } + + /// <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; + 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) + Safe.Dispose(m_scanner); + base.Dispose(disposing); + } + } +}