comparison Implab/Formats/JSON/JSONParser.cs @ 190:1c2a16d071a7 v2

Слияние с ref20160224
author cin
date Fri, 22 Apr 2016 13:08:08 +0300
parents c32688129f14
children 7d07503621fe
comparison
equal deleted inserted replaced
161:2a8466f0cb8a 190:1c2a16d071a7
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 MemberContext m_memberContext;
114
115 JSONElementType m_elementType;
116 object m_elementValue;
117 string m_memberName = String.Empty;
118
119 Stack<ParserContext> m_stack = new Stack<ParserContext>();
120 ParserContext m_context = _jsonContext;
121
122 /// <summary>
123 /// Создает новый парсер на основе строки, содержащей JSON
124 /// </summary>
125 /// <param name="text"></param>
126 public JSONParser(string text) {
127 Safe.ArgumentNotEmpty(text, "text");
128 m_scanner = new JSONScanner(text);
129 }
130
131 /// <summary>
132 /// Создает новый экземпляр парсера, на основе текстового потока.
133 /// </summary>
134 /// <param name="reader">Текстовый поток.</param>
135 public JSONParser(TextReader reader) {
136 Safe.ArgumentNotNull(reader, "reader");
137 m_scanner = new JSONScanner(reader);
138 }
139
140 public int Level {
141 get { return m_stack.Count; }
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 {
156 get { return m_memberName; }
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;
173
174 m_memberName = String.Empty;
175
176 while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
177 if(!m_context.Move(tokenType))
178 UnexpectedToken(tokenValue, tokenType);
179
180 switch (tokenType) {
181 case JsonTokenType.BeginObject:
182 m_stack.Push(m_context);
183 m_context = _objectContext;
184
185 m_elementValue = null;
186 m_memberContext = MemberContext.MemberName;
187 m_elementType = JSONElementType.BeginObject;
188 return true;
189 case JsonTokenType.EndObject:
190 if (m_stack.Count == 0)
191 UnexpectedToken(tokenValue, tokenType);
192 m_context = m_stack.Pop();
193
194 m_elementValue = null;
195 m_elementType = JSONElementType.EndObject;
196 return true;
197 case JsonTokenType.BeginArray:
198 m_stack.Push(m_context);
199 m_context = _arrayContext;
200
201 m_elementValue = null;
202 m_memberContext = MemberContext.MemberValue;
203 m_elementType = JSONElementType.BeginArray;
204 return true;
205 case JsonTokenType.EndArray:
206 if (m_stack.Count == 0)
207 UnexpectedToken(tokenValue, tokenType);
208 m_context = m_stack.Pop();
209
210 m_elementValue = null;
211 m_elementType = JSONElementType.EndArray;
212 return true;
213 case JsonTokenType.String:
214 if (m_memberContext == MemberContext.MemberName) {
215 m_memberName = (string)tokenValue;
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:
233 m_memberContext = m_context.ElementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
234 break;
235 default:
236 UnexpectedToken(tokenValue, tokenType);
237 break;
238 }
239 }
240 if (m_context.ElementContext != JSONElementContext.None)
241 throw new ParserException("Unexpedted end of data");
242
243 EOF = true;
244
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 {
271 get;
272 private set;
273 }
274
275 protected override void Dispose(bool disposing) {
276 if (disposing)
277 Safe.Dispose(m_scanner);
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 }