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);
+        }
+    }
+}