@@ -19,6 +19,7 | |||
|
19 | 19 | <ErrorReport>prompt</ErrorReport> |
|
20 | 20 | <WarningLevel>4</WarningLevel> |
|
21 | 21 | <ConsolePause>false</ConsolePause> |
|
22 | <RunCodeAnalysis>true</RunCodeAnalysis> | |
|
22 | 23 | </PropertyGroup> |
|
23 | 24 | <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|
24 | 25 | <DebugType>full</DebugType> |
@@ -3,6 +3,7 using Implab.Parsing; | |||
|
3 | 3 | using System; |
|
4 | 4 | using System.Collections.Generic; |
|
5 | 5 | using System.Diagnostics; |
|
6 | using System.IO; | |
|
6 | 7 | using System.Linq; |
|
7 | 8 | using System.Text; |
|
8 | 9 | using System.Threading.Tasks; |
@@ -19,12 +20,12 namespace Implab.JSON { | |||
|
19 | 20 | /// <summary> |
|
20 | 21 | /// Pull парсер JSON данных. |
|
21 | 22 | /// </summary> |
|
22 | public class JSONParser : DFAutomaton<JSONParserContext> { | |
|
23 | public class JSONParser : DFAutomaton<JSONParserContext>, IDisposable { | |
|
23 | 24 | |
|
24 | 25 | enum MemberContext { |
|
25 | 26 | MemberName, |
|
26 | 27 | MemberValue |
|
27 |
} |
|
|
28 | } | |
|
28 | 29 | |
|
29 | 30 | static readonly EnumAlphabet<JsonTokenType> _alphabet = EnumAlphabet<JsonTokenType>.FullAlphabet; |
|
30 | 31 | static readonly DFAStateDescriptior[] _jsonDFA; |
@@ -75,25 +76,55 namespace Implab.JSON { | |||
|
75 | 76 | JSONElementType m_elementType; |
|
76 | 77 | object m_elementValue; |
|
77 | 78 | |
|
79 | /// <summary> | |
|
80 | /// Создает новый парсер на основе строки, содержащей JSON | |
|
81 | /// </summary> | |
|
82 | /// <param name="text"></param> | |
|
78 | 83 | public JSONParser(string text) |
|
79 |
: base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty } |
|
|
84 | : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { | |
|
80 | 85 | Safe.ArgumentNotEmpty(text, "text"); |
|
81 | 86 | m_scanner = new JSONScanner(); |
|
82 | 87 | m_scanner.Feed(text.ToCharArray()); |
|
83 | 88 | } |
|
84 | 89 | |
|
90 | /// <summary> | |
|
91 | /// Создает новый экземпляр парсера, на основе текстового потока. | |
|
92 | /// </summary> | |
|
93 | /// <param name="reader">Текстовый поток.</param> | |
|
94 | /// <param name="dispose">Признак того, что парсер должен конролировать время жизни входного потока.</param> | |
|
95 | public JSONParser(TextReader reader, bool dispose) | |
|
96 | : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { | |
|
97 | Safe.ArgumentNotNull(reader, "reader"); | |
|
98 | m_scanner = new JSONScanner(); | |
|
99 | m_scanner.Feed(reader, dispose); | |
|
100 | } | |
|
101 | ||
|
102 | /// <summary> | |
|
103 | /// Тип текущего элемента на котором стоит парсер. | |
|
104 | /// </summary> | |
|
85 | 105 | public JSONElementType ElementType { |
|
86 | 106 | get { return m_elementType; } |
|
87 | 107 | } |
|
88 | 108 | |
|
109 | /// <summary> | |
|
110 | /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда | |
|
111 | /// пустая строка. | |
|
112 | /// </summary> | |
|
89 | 113 | public string ElementName { |
|
90 | 114 | get { return m_context.info.memberName; } |
|
91 | 115 | } |
|
92 | 116 | |
|
117 | /// <summary> | |
|
118 | /// Значение элемента. Только для элементов типа <see cref="JSONElementType.Value"/>, для остальных <c>null</c> | |
|
119 | /// </summary> | |
|
93 | 120 | public object ElementValue { |
|
94 | 121 | get { return m_elementValue; } |
|
95 | 122 | } |
|
96 | 123 | |
|
124 | /// <summary> | |
|
125 | /// Читает слеюудущий объект из потока | |
|
126 | /// </summary> | |
|
127 | /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns> | |
|
97 | 128 | public bool Read() { |
|
98 | 129 | if (m_context.current == UNREACHEBLE_STATE) |
|
99 | 130 | throw new InvalidOperationException("The parser is in invalid state"); |
@@ -109,7 +140,7 namespace Implab.JSON { | |||
|
109 | 140 | Switch( |
|
110 | 141 | _objectDFA, |
|
111 | 142 | INITIAL_STATE, |
|
112 |
new JSONParserContext { |
|
|
143 | new JSONParserContext { | |
|
113 | 144 | memberName = m_context.info.memberName, |
|
114 | 145 | elementContext = JSONElementContext.Object |
|
115 | 146 | } |
@@ -178,7 +209,7 namespace Implab.JSON { | |||
|
178 | 209 | switch (literal) { |
|
179 | 210 | case "null": |
|
180 | 211 | return null; |
|
181 |
case "false" |
|
|
212 | case "false": | |
|
182 | 213 | return false; |
|
183 | 214 | case "true": |
|
184 | 215 | return true; |
@@ -193,11 +224,32 namespace Implab.JSON { | |||
|
193 | 224 | } |
|
194 | 225 | |
|
195 | 226 | |
|
227 | /// <summary> | |
|
228 | /// Признак конца потока | |
|
229 | /// </summary> | |
|
196 | 230 | public bool EOF { |
|
197 | 231 | get { |
|
198 | 232 | return m_scanner.EOF; |
|
199 | 233 | } |
|
200 | 234 | } |
|
235 | ||
|
236 | protected virtual void Dispose(bool disposing) { | |
|
237 | if (disposing) { | |
|
238 | m_scanner.Dispose(); | |
|
239 | } | |
|
240 | } | |
|
241 | ||
|
242 | /// <summary> | |
|
243 | /// Освобождает парсер и связанный с ним сканнер. | |
|
244 | /// </summary> | |
|
245 | public void Dispose() { | |
|
246 | Dispose(true); | |
|
247 | GC.SuppressFinalize(this); | |
|
248 | } | |
|
249 | ||
|
250 | ~JSONParser() { | |
|
251 | Dispose(false); | |
|
252 | } | |
|
201 | 253 | } |
|
202 | 254 | |
|
203 | 255 | } |
@@ -272,7 +272,15 namespace ConsPlay { | |||
|
272 | 272 | } |
|
273 | 273 | |
|
274 | 274 | public override void Close() { |
|
275 | m_state = System.Xml.ReadState.EndOfFile ; | |
|
275 | ||
|
276 | 276 | } |
|
277 | ||
|
278 | protected override void Dispose(bool disposing) { | |
|
279 | if (disposing) { | |
|
280 | m_parser.Dispose(); | |
|
281 | } | |
|
282 | base.Dispose(disposing); | |
|
283 | } | |
|
284 | ||
|
277 | 285 | } |
|
278 | 286 | } |
@@ -8,6 +8,10 using System.Threading.Tasks; | |||
|
8 | 8 | namespace Implab.Parsing { |
|
9 | 9 | public class Alphabet: AlphabetBase<char> { |
|
10 | 10 | |
|
11 | public Alphabet() | |
|
12 | : base(char.MaxValue + 1) { | |
|
13 | } | |
|
14 | ||
|
11 | 15 | public override int GetSymbolIndex(char symbol) { |
|
12 | 16 | return symbol; |
|
13 | 17 | } |
@@ -15,9 +19,5 namespace Implab.Parsing { | |||
|
15 | 19 | public override IEnumerable<char> InputSymbols { |
|
16 | 20 | get { return Enumerable.Range(char.MinValue, char.MaxValue).Select(x => (char)x); } |
|
17 | 21 | } |
|
18 | ||
|
19 | protected override int MapSize { | |
|
20 | get { return char.MaxValue + 1; } | |
|
21 | } | |
|
22 | 22 | } |
|
23 | 23 | } |
@@ -17,13 +17,12 namespace Implab.Parsing { | |||
|
17 | 17 | get { return m_nextId; } |
|
18 | 18 | } |
|
19 | 19 | |
|
20 | protected AlphabetBase() { | |
|
21 |
m_map = new int[ |
|
|
20 | protected AlphabetBase(int mapSize) { | |
|
21 | m_map = new int[mapSize]; | |
|
22 | 22 | } |
|
23 | 23 | |
|
24 | 24 | protected AlphabetBase(int[] map) { |
|
25 | 25 | Debug.Assert(map != null); |
|
26 | Debug.Assert(map.Length == MapSize); | |
|
27 | 26 | |
|
28 | 27 | m_map = map; |
|
29 | 28 | m_nextId = map.Max() + 1; |
@@ -94,8 +93,6 namespace Implab.Parsing { | |||
|
94 | 93 | |
|
95 | 94 | public abstract IEnumerable<T> InputSymbols { get; } |
|
96 | 95 | |
|
97 | protected abstract int MapSize { get; } | |
|
98 | ||
|
99 | 96 | public int[] GetTranslationMap() { |
|
100 | 97 | return m_map; |
|
101 | 98 | } |
@@ -1,6 +1,7 | |||
|
1 | 1 | using Implab; |
|
2 | 2 | using System; |
|
3 | 3 | using System.Collections.Generic; |
|
4 | using System.Diagnostics; | |
|
4 | 5 | using System.Globalization; |
|
5 | 6 | using System.Linq; |
|
6 | 7 | using System.Text; |
@@ -45,11 +46,12 namespace Implab.Parsing { | |||
|
45 | 46 | |
|
46 | 47 | |
|
47 | 48 | public EnumAlphabet() |
|
48 | : base() { | |
|
49 | : base(_symbols.Length) { | |
|
49 | 50 | } |
|
50 | 51 | |
|
51 | 52 | public EnumAlphabet(int[] map) |
|
52 | 53 | : base(map) { |
|
54 | Debug.Assert(map.Length == _symbols.Length); | |
|
53 | 55 | } |
|
54 | 56 | |
|
55 | 57 | |
@@ -61,8 +63,5 namespace Implab.Parsing { | |||
|
61 | 63 | get { return _symbols; } |
|
62 | 64 | } |
|
63 | 65 | |
|
64 | protected override int MapSize { | |
|
65 | get { return _symbols.Length; } | |
|
66 | } | |
|
67 | 66 | } |
|
68 | 67 | } |
@@ -1,6 +1,7 | |||
|
1 | 1 | using Implab; |
|
2 | 2 | using System; |
|
3 | 3 | using System.Collections.Generic; |
|
4 | using System.IO; | |
|
4 | 5 | using System.Linq; |
|
5 | 6 | using System.Text; |
|
6 | 7 | using System.Threading.Tasks; |
@@ -14,7 +15,7 namespace Implab.Parsing { | |||
|
14 | 15 | /// указателя, начала и конца токена, при перемещении искользуется ДКА для определения |
|
15 | 16 | /// конца токена и допустимости текущего символа. |
|
16 | 17 | /// </remarks> |
|
17 | public class Scanner { | |
|
18 | public abstract class Scanner : Disposable { | |
|
18 | 19 | struct ScannerConfig { |
|
19 | 20 | public DFAStateDescriptior[] states; |
|
20 | 21 | public int[] alphabetMap; |
@@ -35,15 +36,10 namespace Implab.Parsing { | |||
|
35 | 36 | protected int m_bufferSize; |
|
36 | 37 | protected int m_pointer; |
|
37 | 38 | |
|
38 | public Scanner(CDFADefinition definition, string text) { | |
|
39 | Safe.ArgumentNotNull(definition, "definition"); | |
|
40 | Safe.ArgumentNotEmpty(text, "text"); | |
|
41 | ||
|
42 | m_states = definition.States; | |
|
43 | m_alphabetMap = definition.Alphabet.GetTranslationMap(); | |
|
44 | ||
|
45 | Feed(text.ToCharArray()); | |
|
46 | } | |
|
39 | TextReader m_reader; | |
|
40 | bool m_disposeReader; | |
|
41 | int m_chunkSize = 1024; // 1k | |
|
42 | int m_limit = 10 * 1024 * 1024; // 10Mb | |
|
47 | 43 | |
|
48 | 44 | public Scanner(CDFADefinition definition) { |
|
49 | 45 | Safe.ArgumentNotNull(definition, "definition"); |
@@ -76,6 +72,7 namespace Implab.Parsing { | |||
|
76 | 72 | public void Feed(char[] data, int length) { |
|
77 | 73 | Safe.ArgumentNotNull(data, "data"); |
|
78 | 74 | Safe.ArgumentInRange(length, 0, data.Length, "length"); |
|
75 | AssertNotDisposed(); | |
|
79 | 76 | |
|
80 | 77 | m_pointer = -1; |
|
81 | 78 | m_buffer = data; |
@@ -83,18 +80,33 namespace Implab.Parsing { | |||
|
83 | 80 | Shift(); |
|
84 | 81 | } |
|
85 | 82 | |
|
83 | public void Feed(TextReader reader, bool dispose) { | |
|
84 | Safe.ArgumentNotNull(reader, "reader"); | |
|
85 | AssertNotDisposed(); | |
|
86 | ||
|
87 | if (m_reader != null && m_disposeReader) | |
|
88 | m_reader.Dispose(); | |
|
89 | ||
|
90 | m_reader = reader; | |
|
91 | m_disposeReader = dispose; | |
|
92 | m_pointer = -1; | |
|
93 | m_buffer = new char[m_chunkSize]; | |
|
94 | m_bufferSize = 0; | |
|
95 | Shift(); | |
|
96 | } | |
|
97 | ||
|
86 | 98 | /// <summary> |
|
87 | 99 | /// Получает текущий токен в виде строки. |
|
88 | 100 | /// </summary> |
|
89 | 101 | /// <returns></returns> |
|
90 |
p |
|
|
102 | protected string GetTokenValue() { | |
|
91 | 103 | return new String(m_buffer, m_tokenOffset, m_tokenLen); |
|
92 | 104 | } |
|
93 | 105 | |
|
94 | 106 | /// <summary> |
|
95 | 107 | /// Метки текущего токена, которые были назначены в регулярном выражении. |
|
96 | 108 | /// </summary> |
|
97 |
p |
|
|
109 | protected int[] TokenTags { | |
|
98 | 110 | get { |
|
99 | 111 | return m_currentState.tag; |
|
100 | 112 | } |
@@ -163,13 +175,31 namespace Implab.Parsing { | |||
|
163 | 175 | return true; |
|
164 | 176 | } |
|
165 | 177 | |
|
166 | /// <summary> | |
|
167 | /// Вызывается по достижению конца входного буффера для получения | |
|
168 | /// новых данных. | |
|
169 | /// </summary> | |
|
170 | /// <returns><c>true</c> - новые двнные получены, можно продолжать обработку.</returns> | |
|
171 | protected virtual bool ReadNextChunk() { | |
|
172 | return false; | |
|
178 | bool ReadNextChunk() { | |
|
179 | if (m_reader == null) | |
|
180 | return false; | |
|
181 | ||
|
182 | // extend buffer if nesessary | |
|
183 | if (m_pointer + m_chunkSize > m_buffer.Length) { | |
|
184 | // trim unused buffer head | |
|
185 | var size = m_tokenLen + m_chunkSize; | |
|
186 | if (size >= m_limit) | |
|
187 | throw new ParserException(String.Format("Input buffer {0} bytes limit exceeded", m_limit)); | |
|
188 | var temp = new char[size]; | |
|
189 | Array.Copy(m_buffer, m_tokenOffset, temp, 0, m_tokenLen); | |
|
190 | m_pointer -= m_tokenOffset; | |
|
191 | m_bufferSize -= m_tokenOffset; | |
|
192 | m_tokenOffset = 0; | |
|
193 | m_buffer = temp; | |
|
194 | } | |
|
195 | ||
|
196 | var read = m_reader.Read(m_buffer, m_tokenLen, m_chunkSize); | |
|
197 | if (read == 0) | |
|
198 | return false; | |
|
199 | ||
|
200 | m_bufferSize += read; | |
|
201 | ||
|
202 | return true; | |
|
173 | 203 | } |
|
174 | 204 | |
|
175 | 205 | /// <summary> |
@@ -212,5 +242,18 namespace Implab.Parsing { | |||
|
212 | 242 | m_alphabetMap = prev.alphabetMap; |
|
213 | 243 | m_previewCode = m_alphabetMap[m_buffer[m_pointer]]; |
|
214 | 244 | } |
|
245 | ||
|
246 | protected override void Dispose(bool disposing) { | |
|
247 | if (disposing) { | |
|
248 | if (m_reader != null && m_disposeReader) | |
|
249 | m_reader.Dispose(); | |
|
250 | m_buffer = null; | |
|
251 | m_bufferSize = 0; | |
|
252 | m_pointer = 0; | |
|
253 | m_tokenLen = 0; | |
|
254 | m_tokenOffset = 0; | |
|
255 | } | |
|
256 | base.Dispose(disposing); | |
|
257 | } | |
|
215 | 258 | } |
|
216 | 259 | } |
@@ -1,5 +1,6 | |||
|
1 | 1 | using System.Reflection; |
|
2 | using System.Runtime.CompilerServices; | |
|
2 | using System.Runtime.CompilerServices; | |
|
3 | using System.Runtime.InteropServices; | |
|
3 | 4 | |
|
4 | 5 | // Information about this assembly is defined by the following attributes. |
|
5 | 6 | // Change them to the values specific to your project. |
@@ -16,6 +17,7 using System.Runtime.CompilerServices; | |||
|
16 | 17 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. |
|
17 | 18 | |
|
18 | 19 | [assembly: AssemblyVersion("1.0.*")] |
|
20 | [assembly: ComVisible(false)] | |
|
19 | 21 | |
|
20 | 22 | // The following attributes are used to specify the signing key for the assembly, |
|
21 | 23 | // if desired. See the Mono documentation for more information about signing. |
General Comments 0
You need to be logged in to leave comments.
Login now