Mercurial > pub > ImplabNet
diff Implab/Formats/JSON/JSONScanner.cs @ 163:419aa51b04fd ref20160224
JSON moved to Formats namespace
Working in RegularDFA
author | cin |
---|---|
date | Wed, 24 Feb 2016 20:12:52 +0300 |
parents | |
children | e227e78d72e4 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Formats/JSON/JSONScanner.cs Wed Feb 24 20:12:52 2016 +0300 @@ -0,0 +1,100 @@ +using Implab.Parsing; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Implab.JSON { + /// <summary> + /// Сканнер (лексер), разбивающий поток символов на токены JSON. + /// </summary> + public class JSONScanner : Scanner { + char[] m_stringBuffer; + DFAStateDescriptior[] m_stringDFA; + int[] m_stringAlphabet; + + /// <summary> + /// Создает новый экземпляр сканнера + /// </summary> + public JSONScanner() + : base(JSONGrammar.Instance.JsonDFA.States, JSONGrammar.Instance.JsonDFA.Alphabet.GetTranslationMap()) { + m_stringBuffer = new char[1024]; + var dfa = JSONGrammar.Instance.JsonStringDFA; + m_stringAlphabet = dfa.Alphabet.GetTranslationMap(); + m_stringDFA = dfa.States; + } + + /// <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) { + if (ReadTokenInternal()) { + switch ((JSONGrammar.TokenType)m_currentState.tag[0]) { + case JSONGrammar.TokenType.StringBound: + tokenValue = ReadString(); + tokenType = JsonTokenType.String; + break; + case JSONGrammar.TokenType.Number: + tokenValue = Double.Parse(new String(m_buffer, m_tokenOffset, m_tokenLen), CultureInfo.InvariantCulture); + tokenType = JsonTokenType.Number; + break; + default: + tokenType = (JsonTokenType)m_currentState.tag[0]; + tokenValue = new String(m_buffer, m_tokenOffset, m_tokenLen); + break; + } + return true; + } + tokenValue = null; + tokenType = JsonTokenType.None; + return false; + } + + string ReadString() { + int pos = 0; + Switch(m_stringDFA, m_stringAlphabet); + while (ReadTokenInternal()) { + switch ((JSONGrammar.TokenType)m_currentState.tag[0]) { + case JSONGrammar.TokenType.StringBound: + Restore(); + return new String(m_stringBuffer, 0, pos); + case JSONGrammar.TokenType.UnescapedChar: + EnsureStringBufferSize(pos + m_tokenLen); + Array.Copy(m_buffer, m_tokenOffset, m_stringBuffer, pos, m_tokenLen); + pos += m_tokenLen; + break; + case JSONGrammar.TokenType.EscapedUnicode: + EnsureStringBufferSize(pos + 1); + m_stringBuffer[pos] = StringTranslator.TranslateHexUnicode(m_buffer, m_tokenOffset + 2); + pos++; + break; + case JSONGrammar.TokenType.EscapedChar: + EnsureStringBufferSize(pos + 1); + m_stringBuffer[pos] = StringTranslator.TranslateEscapedChar(m_buffer[m_tokenOffset + 1]); + pos++; + break; + default: + break; + } + + } + + throw new ParserException("Unexpected end of data"); + } + + void EnsureStringBufferSize(int size) { + if (size > m_stringBuffer.Length) { + var newBuffer = new char[size]; + m_stringBuffer.CopyTo(newBuffer, 0); + m_stringBuffer = newBuffer; + } + } + } +}