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