diff --git a/Implab/Implab.csproj b/Implab/Implab.csproj --- a/Implab/Implab.csproj +++ b/Implab/Implab.csproj @@ -19,6 +19,7 @@ prompt 4 false + true full diff --git a/Implab/JSON/JSONParser.cs b/Implab/JSON/JSONParser.cs --- a/Implab/JSON/JSONParser.cs +++ b/Implab/JSON/JSONParser.cs @@ -3,6 +3,7 @@ using Implab.Parsing; using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -19,12 +20,12 @@ namespace Implab.JSON { /// /// Pull парсер JSON данных. /// - public class JSONParser : DFAutomaton { + public class JSONParser : DFAutomaton, IDisposable { enum MemberContext { MemberName, MemberValue - } + } static readonly EnumAlphabet _alphabet = EnumAlphabet.FullAlphabet; static readonly DFAStateDescriptior[] _jsonDFA; @@ -75,25 +76,55 @@ namespace Implab.JSON { JSONElementType m_elementType; object m_elementValue; + /// + /// Создает новый парсер на основе строки, содержащей JSON + /// + /// public JSONParser(string text) - : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty } ) { + : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { Safe.ArgumentNotEmpty(text, "text"); m_scanner = new JSONScanner(); m_scanner.Feed(text.ToCharArray()); } + /// + /// Создает новый экземпляр парсера, на основе текстового потока. + /// + /// Текстовый поток. + /// Признак того, что парсер должен конролировать время жизни входного потока. + public JSONParser(TextReader reader, bool dispose) + : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { + Safe.ArgumentNotNull(reader, "reader"); + m_scanner = new JSONScanner(); + m_scanner.Feed(reader, dispose); + } + + /// + /// Тип текущего элемента на котором стоит парсер. + /// public JSONElementType ElementType { get { return m_elementType; } } + /// + /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда + /// пустая строка. + /// public string ElementName { get { return m_context.info.memberName; } } + /// + /// Значение элемента. Только для элементов типа , для остальных null + /// public object ElementValue { get { return m_elementValue; } } + /// + /// Читает слеюудущий объект из потока + /// + /// true - операция чтения прошла успешно, false - конец данных public bool Read() { if (m_context.current == UNREACHEBLE_STATE) throw new InvalidOperationException("The parser is in invalid state"); @@ -109,7 +140,7 @@ namespace Implab.JSON { Switch( _objectDFA, INITIAL_STATE, - new JSONParserContext { + new JSONParserContext { memberName = m_context.info.memberName, elementContext = JSONElementContext.Object } @@ -178,7 +209,7 @@ namespace Implab.JSON { switch (literal) { case "null": return null; - case "false" : + case "false": return false; case "true": return true; @@ -193,11 +224,32 @@ namespace Implab.JSON { } + /// + /// Признак конца потока + /// public bool EOF { get { return m_scanner.EOF; } } + + protected virtual void Dispose(bool disposing) { + if (disposing) { + m_scanner.Dispose(); + } + } + + /// + /// Освобождает парсер и связанный с ним сканнер. + /// + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~JSONParser() { + Dispose(false); + } } } diff --git a/Implab/JSON/JSONXmlReader.cs b/Implab/JSON/JSONXmlReader.cs --- a/Implab/JSON/JSONXmlReader.cs +++ b/Implab/JSON/JSONXmlReader.cs @@ -272,7 +272,15 @@ namespace ConsPlay { } public override void Close() { - m_state = System.Xml.ReadState.EndOfFile ; + } + + protected override void Dispose(bool disposing) { + if (disposing) { + m_parser.Dispose(); + } + base.Dispose(disposing); + } + } } diff --git a/Implab/Parsing/Alphabet.cs b/Implab/Parsing/Alphabet.cs --- a/Implab/Parsing/Alphabet.cs +++ b/Implab/Parsing/Alphabet.cs @@ -8,6 +8,10 @@ using System.Threading.Tasks; namespace Implab.Parsing { public class Alphabet: AlphabetBase { + public Alphabet() + : base(char.MaxValue + 1) { + } + public override int GetSymbolIndex(char symbol) { return symbol; } @@ -15,9 +19,5 @@ namespace Implab.Parsing { public override IEnumerable InputSymbols { get { return Enumerable.Range(char.MinValue, char.MaxValue).Select(x => (char)x); } } - - protected override int MapSize { - get { return char.MaxValue + 1; } - } } } diff --git a/Implab/Parsing/AlphabetBase.cs b/Implab/Parsing/AlphabetBase.cs --- a/Implab/Parsing/AlphabetBase.cs +++ b/Implab/Parsing/AlphabetBase.cs @@ -17,13 +17,12 @@ namespace Implab.Parsing { get { return m_nextId; } } - protected AlphabetBase() { - m_map = new int[MapSize]; + protected AlphabetBase(int mapSize) { + m_map = new int[mapSize]; } protected AlphabetBase(int[] map) { Debug.Assert(map != null); - Debug.Assert(map.Length == MapSize); m_map = map; m_nextId = map.Max() + 1; @@ -94,8 +93,6 @@ namespace Implab.Parsing { public abstract IEnumerable InputSymbols { get; } - protected abstract int MapSize { get; } - public int[] GetTranslationMap() { return m_map; } diff --git a/Implab/Parsing/EnumAlphabet.cs b/Implab/Parsing/EnumAlphabet.cs --- a/Implab/Parsing/EnumAlphabet.cs +++ b/Implab/Parsing/EnumAlphabet.cs @@ -1,6 +1,7 @@ using Implab; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Text; @@ -45,11 +46,12 @@ namespace Implab.Parsing { public EnumAlphabet() - : base() { + : base(_symbols.Length) { } public EnumAlphabet(int[] map) : base(map) { + Debug.Assert(map.Length == _symbols.Length); } @@ -61,8 +63,5 @@ namespace Implab.Parsing { get { return _symbols; } } - protected override int MapSize { - get { return _symbols.Length; } - } } } diff --git a/Implab/Parsing/Scanner.cs b/Implab/Parsing/Scanner.cs --- a/Implab/Parsing/Scanner.cs +++ b/Implab/Parsing/Scanner.cs @@ -1,6 +1,7 @@ using Implab; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -14,7 +15,7 @@ namespace Implab.Parsing { /// указателя, начала и конца токена, при перемещении искользуется ДКА для определения /// конца токена и допустимости текущего символа. /// - public class Scanner { + public abstract class Scanner : Disposable { struct ScannerConfig { public DFAStateDescriptior[] states; public int[] alphabetMap; @@ -35,15 +36,10 @@ namespace Implab.Parsing { protected int m_bufferSize; protected int m_pointer; - public Scanner(CDFADefinition definition, string text) { - Safe.ArgumentNotNull(definition, "definition"); - Safe.ArgumentNotEmpty(text, "text"); - - m_states = definition.States; - m_alphabetMap = definition.Alphabet.GetTranslationMap(); - - Feed(text.ToCharArray()); - } + TextReader m_reader; + bool m_disposeReader; + int m_chunkSize = 1024; // 1k + int m_limit = 10 * 1024 * 1024; // 10Mb public Scanner(CDFADefinition definition) { Safe.ArgumentNotNull(definition, "definition"); @@ -76,6 +72,7 @@ namespace Implab.Parsing { public void Feed(char[] data, int length) { Safe.ArgumentNotNull(data, "data"); Safe.ArgumentInRange(length, 0, data.Length, "length"); + AssertNotDisposed(); m_pointer = -1; m_buffer = data; @@ -83,18 +80,33 @@ namespace Implab.Parsing { Shift(); } + public void Feed(TextReader reader, bool dispose) { + Safe.ArgumentNotNull(reader, "reader"); + AssertNotDisposed(); + + if (m_reader != null && m_disposeReader) + m_reader.Dispose(); + + m_reader = reader; + m_disposeReader = dispose; + m_pointer = -1; + m_buffer = new char[m_chunkSize]; + m_bufferSize = 0; + Shift(); + } + /// /// Получает текущий токен в виде строки. /// /// - public string GetTokenValue() { + protected string GetTokenValue() { return new String(m_buffer, m_tokenOffset, m_tokenLen); } /// /// Метки текущего токена, которые были назначены в регулярном выражении. /// - public int[] TokenTags { + protected int[] TokenTags { get { return m_currentState.tag; } @@ -163,13 +175,31 @@ namespace Implab.Parsing { return true; } - /// - /// Вызывается по достижению конца входного буффера для получения - /// новых данных. - /// - /// true - новые двнные получены, можно продолжать обработку. - protected virtual bool ReadNextChunk() { - return false; + bool ReadNextChunk() { + if (m_reader == null) + return false; + + // extend buffer if nesessary + if (m_pointer + m_chunkSize > m_buffer.Length) { + // trim unused buffer head + var size = m_tokenLen + m_chunkSize; + if (size >= m_limit) + throw new ParserException(String.Format("Input buffer {0} bytes limit exceeded", m_limit)); + var temp = new char[size]; + Array.Copy(m_buffer, m_tokenOffset, temp, 0, m_tokenLen); + m_pointer -= m_tokenOffset; + m_bufferSize -= m_tokenOffset; + m_tokenOffset = 0; + m_buffer = temp; + } + + var read = m_reader.Read(m_buffer, m_tokenLen, m_chunkSize); + if (read == 0) + return false; + + m_bufferSize += read; + + return true; } /// @@ -212,5 +242,18 @@ namespace Implab.Parsing { m_alphabetMap = prev.alphabetMap; m_previewCode = m_alphabetMap[m_buffer[m_pointer]]; } + + protected override void Dispose(bool disposing) { + if (disposing) { + if (m_reader != null && m_disposeReader) + m_reader.Dispose(); + m_buffer = null; + m_bufferSize = 0; + m_pointer = 0; + m_tokenLen = 0; + m_tokenOffset = 0; + } + base.Dispose(disposing); + } } } diff --git a/Implab/Properties/AssemblyInfo.cs b/Implab/Properties/AssemblyInfo.cs --- a/Implab/Properties/AssemblyInfo.cs +++ b/Implab/Properties/AssemblyInfo.cs @@ -1,5 +1,6 @@ using System.Reflection; -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. @@ -16,6 +17,7 @@ using System.Runtime.CompilerServices; // and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion("1.0.*")] +[assembly: ComVisible(false)] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing.