Mercurial > pub > ImplabNet
annotate Implab/Formats/JSON/JSONParser.cs @ 208:7d07503621fe v2
RunnableComponent.Dispose(bool,Exception) changed to standart Dispose(bool)
IRunnable is now disposable
Code cleanups, suppressed some CodeAnalysis warnings
author | cin |
---|---|
date | Sun, 13 Nov 2016 18:28:17 +0300 |
parents | c32688129f14 |
children | 8d5de4eb9c2c |
rev | line source |
---|---|
165 | 1 using System; |
163 | 2 using System.Diagnostics; |
3 using System.IO; | |
165 | 4 using Implab.Automaton; |
5 using Implab.Automaton.RegularExpressions; | |
6 using System.Linq; | |
7 using Implab.Components; | |
180 | 8 using System.Collections.Generic; |
163 | 9 |
165 | 10 namespace Implab.Formats.JSON { |
163 | 11 /// <summary> |
12 /// Pull парсер JSON данных. | |
13 /// </summary> | |
14 /// <remarks> | |
15 /// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>, | |
16 /// оно означает текущий уровень вложенности объектов, однако закрывающий | |
17 /// элемент объекта и массива имеет уровень меньше, чем сам объект. | |
18 /// <code> | |
19 /// { // Level = 1 | |
20 /// "name" : "Peter", // Level = 1 | |
21 /// "address" : { // Level = 2 | |
22 /// city : "Stern" // Level = 2 | |
23 /// } // Level = 1 | |
24 /// } // Level = 0 | |
25 /// </code> | |
26 /// </remarks> | |
165 | 27 public class JSONParser : Disposable { |
163 | 28 |
29 enum MemberContext { | |
30 MemberName, | |
31 MemberValue | |
32 } | |
33 | |
178 | 34 #region Parser rules |
165 | 35 struct ParserContext { |
178 | 36 readonly int[,] m_dfa; |
37 int m_state; | |
38 | |
39 readonly JSONElementContext m_elementContext; | |
165 | 40 |
178 | 41 public ParserContext(int[,] dfa, int state, JSONElementContext context) { |
42 m_dfa = dfa; | |
43 m_state = state; | |
44 m_elementContext = context; | |
45 } | |
46 | |
47 public bool Move(JsonTokenType token) { | |
180 | 48 var next = m_dfa[m_state, (int)token]; |
178 | 49 if (next == AutomatonConst.UNREACHABLE_STATE) |
50 return false; | |
51 m_state = next; | |
180 | 52 return true; |
178 | 53 } |
54 | |
55 public JSONElementContext ElementContext { | |
56 get { return m_elementContext; } | |
57 } | |
58 } | |
163 | 59 |
180 | 60 static readonly ParserContext _jsonContext; |
61 static readonly ParserContext _objectContext; | |
62 static readonly ParserContext _arrayContext; | |
63 | |
163 | 64 static JSONParser() { |
65 | |
180 | 66 var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String); |
67 var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression); | |
163 | 68 |
69 var objectExpression = memberExpression | |
70 .Cat( | |
180 | 71 MakeToken(JsonTokenType.ValueSeparator) |
163 | 72 .Cat(memberExpression) |
73 .EClosure() | |
74 ) | |
75 .Optional() | |
180 | 76 .Cat(MakeToken(JsonTokenType.EndObject)) |
178 | 77 .End(); |
78 | |
163 | 79 var arrayExpression = valueExpression |
80 .Cat( | |
180 | 81 MakeToken(JsonTokenType.ValueSeparator) |
163 | 82 .Cat(valueExpression) |
83 .EClosure() | |
84 ) | |
85 .Optional() | |
180 | 86 .Cat(MakeToken(JsonTokenType.EndArray)) |
178 | 87 .End(); |
163 | 88 |
178 | 89 var jsonExpression = valueExpression.End(); |
163 | 90 |
180 | 91 _jsonContext = CreateParserContext(jsonExpression, JSONElementContext.None); |
92 _objectContext = CreateParserContext(objectExpression, JSONElementContext.Object); | |
93 _arrayContext = CreateParserContext(arrayExpression, JSONElementContext.Array); | |
163 | 94 } |
95 | |
180 | 96 static Token MakeToken(params JsonTokenType[] input) { |
178 | 97 return Token.New( input.Select(t => (int)t).ToArray() ); |
165 | 98 } |
99 | |
178 | 100 static ParserContext CreateParserContext(Token expr, JSONElementContext context) { |
101 | |
102 var dfa = new DFATable(); | |
103 var builder = new RegularExpressionVisitor(dfa); | |
163 | 104 expr.Accept(builder); |
178 | 105 builder.BuildDFA(); |
163 | 106 |
178 | 107 return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context); |
163 | 108 } |
109 | |
178 | 110 #endregion |
111 | |
180 | 112 readonly JSONScanner m_scanner; |
163 | 113 MemberContext m_memberContext; |
114 | |
115 JSONElementType m_elementType; | |
116 object m_elementValue; | |
180 | 117 string m_memberName = String.Empty; |
118 | |
119 Stack<ParserContext> m_stack = new Stack<ParserContext>(); | |
120 ParserContext m_context = _jsonContext; | |
163 | 121 |
122 /// <summary> | |
123 /// Создает новый парсер на основе строки, содержащей JSON | |
124 /// </summary> | |
125 /// <param name="text"></param> | |
180 | 126 public JSONParser(string text) { |
163 | 127 Safe.ArgumentNotEmpty(text, "text"); |
180 | 128 m_scanner = new JSONScanner(text); |
163 | 129 } |
130 | |
131 /// <summary> | |
132 /// Создает новый экземпляр парсера, на основе текстового потока. | |
133 /// </summary> | |
134 /// <param name="reader">Текстовый поток.</param> | |
180 | 135 public JSONParser(TextReader reader) { |
163 | 136 Safe.ArgumentNotNull(reader, "reader"); |
180 | 137 m_scanner = new JSONScanner(reader); |
138 } | |
139 | |
140 public int Level { | |
141 get { return m_stack.Count; } | |
163 | 142 } |
143 | |
144 /// <summary> | |
145 /// Тип текущего элемента на котором стоит парсер. | |
146 /// </summary> | |
147 public JSONElementType ElementType { | |
148 get { return m_elementType; } | |
149 } | |
150 | |
151 /// <summary> | |
152 /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда | |
153 /// пустая строка. | |
154 /// </summary> | |
155 public string ElementName { | |
180 | 156 get { return m_memberName; } |
163 | 157 } |
158 | |
159 /// <summary> | |
160 /// Значение элемента. Только для элементов типа <see cref="JSONElementType.Value"/>, для остальных <c>null</c> | |
161 /// </summary> | |
162 public object ElementValue { | |
163 get { return m_elementValue; } | |
164 } | |
165 | |
166 /// <summary> | |
167 /// Читает слеюудущий объект из потока | |
168 /// </summary> | |
169 /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns> | |
170 public bool Read() { | |
171 object tokenValue; | |
172 JsonTokenType tokenType; | |
180 | 173 |
174 m_memberName = String.Empty; | |
175 | |
163 | 176 while (m_scanner.ReadToken(out tokenValue, out tokenType)) { |
180 | 177 if(!m_context.Move(tokenType)) |
163 | 178 UnexpectedToken(tokenValue, tokenType); |
180 | 179 |
163 | 180 switch (tokenType) { |
181 case JsonTokenType.BeginObject: | |
180 | 182 m_stack.Push(m_context); |
183 m_context = _objectContext; | |
184 | |
163 | 185 m_elementValue = null; |
186 m_memberContext = MemberContext.MemberName; | |
187 m_elementType = JSONElementType.BeginObject; | |
188 return true; | |
189 case JsonTokenType.EndObject: | |
180 | 190 if (m_stack.Count == 0) |
191 UnexpectedToken(tokenValue, tokenType); | |
192 m_context = m_stack.Pop(); | |
193 | |
163 | 194 m_elementValue = null; |
195 m_elementType = JSONElementType.EndObject; | |
196 return true; | |
197 case JsonTokenType.BeginArray: | |
180 | 198 m_stack.Push(m_context); |
199 m_context = _arrayContext; | |
200 | |
163 | 201 m_elementValue = null; |
202 m_memberContext = MemberContext.MemberValue; | |
203 m_elementType = JSONElementType.BeginArray; | |
204 return true; | |
205 case JsonTokenType.EndArray: | |
180 | 206 if (m_stack.Count == 0) |
207 UnexpectedToken(tokenValue, tokenType); | |
208 m_context = m_stack.Pop(); | |
209 | |
163 | 210 m_elementValue = null; |
211 m_elementType = JSONElementType.EndArray; | |
212 return true; | |
213 case JsonTokenType.String: | |
214 if (m_memberContext == MemberContext.MemberName) { | |
180 | 215 m_memberName = (string)tokenValue; |
163 | 216 break; |
217 } | |
218 m_elementType = JSONElementType.Value; | |
219 m_elementValue = tokenValue; | |
220 return true; | |
221 case JsonTokenType.Number: | |
222 m_elementType = JSONElementType.Value; | |
223 m_elementValue = tokenValue; | |
224 return true; | |
225 case JsonTokenType.Literal: | |
226 m_elementType = JSONElementType.Value; | |
227 m_elementValue = ParseLiteral((string)tokenValue); | |
228 return true; | |
229 case JsonTokenType.NameSeparator: | |
230 m_memberContext = MemberContext.MemberValue; | |
231 break; | |
232 case JsonTokenType.ValueSeparator: | |
180 | 233 m_memberContext = m_context.ElementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue; |
163 | 234 break; |
235 default: | |
236 UnexpectedToken(tokenValue, tokenType); | |
237 break; | |
238 } | |
239 } | |
180 | 240 if (m_context.ElementContext != JSONElementContext.None) |
163 | 241 throw new ParserException("Unexpedted end of data"); |
180 | 242 |
243 EOF = true; | |
244 | |
163 | 245 return false; |
246 } | |
247 | |
248 object ParseLiteral(string literal) { | |
249 switch (literal) { | |
250 case "null": | |
251 return null; | |
252 case "false": | |
253 return false; | |
254 case "true": | |
255 return true; | |
256 default: | |
257 UnexpectedToken(literal, JsonTokenType.Literal); | |
258 return null; // avoid compliler error | |
259 } | |
260 } | |
261 | |
262 void UnexpectedToken(object value, JsonTokenType tokenType) { | |
263 throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value)); | |
264 } | |
265 | |
266 | |
267 /// <summary> | |
268 /// Признак конца потока | |
269 /// </summary> | |
270 public bool EOF { | |
180 | 271 get; |
272 private set; | |
163 | 273 } |
274 | |
165 | 275 protected override void Dispose(bool disposing) { |
180 | 276 if (disposing) |
208
7d07503621fe
RunnableComponent.Dispose(bool,Exception) changed to standart Dispose(bool)
cin
parents:
180
diff
changeset
|
277 m_scanner.Dispose(); |
163 | 278 } |
279 | |
280 /// <summary> | |
281 /// Переходит в конец текущего объекта. | |
282 /// </summary> | |
283 public void SeekElementEnd() { | |
284 var level = Level - 1; | |
285 | |
286 Debug.Assert(level >= 0); | |
287 | |
288 while (Level != level) | |
289 Read(); | |
290 } | |
291 } | |
292 | |
293 } |