JSONScanner.cs
100 lines
| 4.6 KiB
| text/x-csharp
|
CSharpLexer
|
|
r55 | 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> | ||||
|
|
r60 | /// Сканнер (лексер), разбивающий поток символов на токены JSON. | ||
|
|
r55 | /// </summary> | ||
| public class JSONScanner : Scanner { | ||||
| char[] m_stringBuffer; | ||||
| DFAStateDescriptior[] m_stringDFA; | ||||
| int[] m_stringAlphabet; | ||||
|
|
r60 | /// <summary> | ||
| /// Создает новый экземпляр сканнера | ||||
| /// </summary> | ||||
|
|
r55 | public JSONScanner() | ||
| : base(JSONGrammar.Instance.JsonDFA) { | ||||
| m_stringBuffer = new char[1024]; | ||||
| var dfa = JSONGrammar.Instance.JsonStringDFA; | ||||
| m_stringAlphabet = dfa.Alphabet.GetTranslationMap(); | ||||
| m_stringDFA = dfa.States; | ||||
| } | ||||
|
|
r60 | /// <summary> | ||
| /// Читает следующий лексический элемент из входных данных. | ||||
| /// </summary> | ||||
| /// <param name="tokenValue">Возвращает значение прочитанного токена.</param> | ||||
| /// <param name="tokenType">Возвращает тип прочитанного токена.</param> | ||||
| /// <returns><c>true</c> - чтение произведено успешно. <c>false</c> - достигнут конец входных данных</returns> | ||||
| /// <remarks>В случе если токен не распознается, возникает исключение. Значения токенов обрабатываются, т.е. | ||||
| /// в строках обрабатываются экранированные символы, числа становтся типа double.</remarks> | ||||
|
|
r55 | 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; | ||||
| } | ||||
| } | ||||
| } | ||||
| } | ||||
