Mercurial > pub > ImplabNet
comparison Implab/Formats/JSON/JSONParser.cs @ 163:419aa51b04fd ref20160224
JSON moved to Formats namespace
Working in RegularDFA
author | cin |
---|---|
date | Wed, 24 Feb 2016 20:12:52 +0300 |
parents | |
children | e227e78d72e4 |
comparison
equal
deleted
inserted
replaced
162:0526412bbb26 | 163:419aa51b04fd |
---|---|
1 using Implab.Parsing; | |
2 using System; | |
3 using System.Diagnostics; | |
4 using System.IO; | |
5 | |
6 namespace Implab.JSON { | |
7 /// <summary> | |
8 /// internal | |
9 /// </summary> | |
10 public struct JSONParserContext { | |
11 public string memberName; | |
12 public JSONElementContext elementContext; | |
13 } | |
14 | |
15 /// <summary> | |
16 /// Pull парсер JSON данных. | |
17 /// </summary> | |
18 /// <remarks> | |
19 /// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>, | |
20 /// оно означает текущий уровень вложенности объектов, однако закрывающий | |
21 /// элемент объекта и массива имеет уровень меньше, чем сам объект. | |
22 /// <code> | |
23 /// { // Level = 1 | |
24 /// "name" : "Peter", // Level = 1 | |
25 /// "address" : { // Level = 2 | |
26 /// city : "Stern" // Level = 2 | |
27 /// } // Level = 1 | |
28 /// } // Level = 0 | |
29 /// </code> | |
30 /// </remarks> | |
31 public class JSONParser : DFAutomaton<JSONParserContext>, IDisposable { | |
32 | |
33 enum MemberContext { | |
34 MemberName, | |
35 MemberValue | |
36 } | |
37 | |
38 static readonly EnumAlphabet<JsonTokenType> _alphabet = EnumAlphabet<JsonTokenType>.FullAlphabet; | |
39 static readonly DFAStateDescriptior[] _jsonDFA; | |
40 static readonly DFAStateDescriptior[] _objectDFA; | |
41 static readonly DFAStateDescriptior[] _arrayDFA; | |
42 | |
43 static JSONParser() { | |
44 | |
45 | |
46 var valueExpression = Token.New(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String); | |
47 var memberExpression = Token.New(JsonTokenType.String).Cat(Token.New(JsonTokenType.NameSeparator)).Cat(valueExpression); | |
48 | |
49 var objectExpression = memberExpression | |
50 .Cat( | |
51 Token.New(JsonTokenType.ValueSeparator) | |
52 .Cat(memberExpression) | |
53 .EClosure() | |
54 ) | |
55 .Optional() | |
56 .Cat(Token.New(JsonTokenType.EndObject)) | |
57 .Tag(0); | |
58 var arrayExpression = valueExpression | |
59 .Cat( | |
60 Token.New(JsonTokenType.ValueSeparator) | |
61 .Cat(valueExpression) | |
62 .EClosure() | |
63 ) | |
64 .Optional() | |
65 .Cat(Token.New(JsonTokenType.EndArray)) | |
66 .Tag(0); | |
67 | |
68 var jsonExpression = valueExpression.Tag(0); | |
69 | |
70 _jsonDFA = BuildDFA(jsonExpression).States; | |
71 _objectDFA = BuildDFA(objectExpression).States; | |
72 _arrayDFA = BuildDFA(arrayExpression).States; | |
73 } | |
74 | |
75 static EDFADefinition<JsonTokenType> BuildDFA(Token expr) { | |
76 var builder = new DFABuilder(); | |
77 var dfa = new EDFADefinition<JsonTokenType>(_alphabet); | |
78 expr.Accept(builder); | |
79 | |
80 builder.BuildDFA(dfa); | |
81 return dfa; | |
82 } | |
83 | |
84 JSONScanner m_scanner; | |
85 MemberContext m_memberContext; | |
86 | |
87 JSONElementType m_elementType; | |
88 object m_elementValue; | |
89 | |
90 /// <summary> | |
91 /// Создает новый парсер на основе строки, содержащей JSON | |
92 /// </summary> | |
93 /// <param name="text"></param> | |
94 public JSONParser(string text) | |
95 : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { | |
96 Safe.ArgumentNotEmpty(text, "text"); | |
97 m_scanner = new JSONScanner(); | |
98 m_scanner.Feed(text.ToCharArray()); | |
99 } | |
100 | |
101 /// <summary> | |
102 /// Создает новый экземпляр парсера, на основе текстового потока. | |
103 /// </summary> | |
104 /// <param name="reader">Текстовый поток.</param> | |
105 /// <param name="dispose">Признак того, что парсер должен конролировать время жизни входного потока.</param> | |
106 public JSONParser(TextReader reader, bool dispose) | |
107 : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { | |
108 Safe.ArgumentNotNull(reader, "reader"); | |
109 m_scanner = new JSONScanner(); | |
110 m_scanner.Feed(reader, dispose); | |
111 } | |
112 | |
113 /// <summary> | |
114 /// Тип текущего элемента на котором стоит парсер. | |
115 /// </summary> | |
116 public JSONElementType ElementType { | |
117 get { return m_elementType; } | |
118 } | |
119 | |
120 /// <summary> | |
121 /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда | |
122 /// пустая строка. | |
123 /// </summary> | |
124 public string ElementName { | |
125 get { return m_context.info.memberName; } | |
126 } | |
127 | |
128 /// <summary> | |
129 /// Значение элемента. Только для элементов типа <see cref="JSONElementType.Value"/>, для остальных <c>null</c> | |
130 /// </summary> | |
131 public object ElementValue { | |
132 get { return m_elementValue; } | |
133 } | |
134 | |
135 /// <summary> | |
136 /// Читает слеюудущий объект из потока | |
137 /// </summary> | |
138 /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns> | |
139 public bool Read() { | |
140 if (m_context.current == UNREACHEBLE_STATE) | |
141 throw new InvalidOperationException("The parser is in invalid state"); | |
142 object tokenValue; | |
143 JsonTokenType tokenType; | |
144 m_context.info.memberName = String.Empty; | |
145 while (m_scanner.ReadToken(out tokenValue, out tokenType)) { | |
146 Move((int)tokenType); | |
147 if (m_context.current == UNREACHEBLE_STATE) | |
148 UnexpectedToken(tokenValue, tokenType); | |
149 switch (tokenType) { | |
150 case JsonTokenType.BeginObject: | |
151 Switch( | |
152 _objectDFA, | |
153 INITIAL_STATE, | |
154 new JSONParserContext { | |
155 memberName = m_context.info.memberName, | |
156 elementContext = JSONElementContext.Object | |
157 } | |
158 ); | |
159 m_elementValue = null; | |
160 m_memberContext = MemberContext.MemberName; | |
161 m_elementType = JSONElementType.BeginObject; | |
162 return true; | |
163 case JsonTokenType.EndObject: | |
164 Restore(); | |
165 m_elementValue = null; | |
166 m_elementType = JSONElementType.EndObject; | |
167 return true; | |
168 case JsonTokenType.BeginArray: | |
169 Switch( | |
170 _arrayDFA, | |
171 INITIAL_STATE, | |
172 new JSONParserContext { | |
173 memberName = m_context.info.memberName, | |
174 elementContext = JSONElementContext.Array | |
175 } | |
176 ); | |
177 m_elementValue = null; | |
178 m_memberContext = MemberContext.MemberValue; | |
179 m_elementType = JSONElementType.BeginArray; | |
180 return true; | |
181 case JsonTokenType.EndArray: | |
182 Restore(); | |
183 m_elementValue = null; | |
184 m_elementType = JSONElementType.EndArray; | |
185 return true; | |
186 case JsonTokenType.String: | |
187 if (m_memberContext == MemberContext.MemberName) { | |
188 m_context.info.memberName = (string)tokenValue; | |
189 break; | |
190 } | |
191 m_elementType = JSONElementType.Value; | |
192 m_elementValue = tokenValue; | |
193 return true; | |
194 case JsonTokenType.Number: | |
195 m_elementType = JSONElementType.Value; | |
196 m_elementValue = tokenValue; | |
197 return true; | |
198 case JsonTokenType.Literal: | |
199 m_elementType = JSONElementType.Value; | |
200 m_elementValue = ParseLiteral((string)tokenValue); | |
201 return true; | |
202 case JsonTokenType.NameSeparator: | |
203 m_memberContext = MemberContext.MemberValue; | |
204 break; | |
205 case JsonTokenType.ValueSeparator: | |
206 m_memberContext = m_context.info.elementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue; | |
207 break; | |
208 default: | |
209 UnexpectedToken(tokenValue, tokenType); | |
210 break; | |
211 } | |
212 } | |
213 if (m_context.info.elementContext != JSONElementContext.None) | |
214 throw new ParserException("Unexpedted end of data"); | |
215 return false; | |
216 } | |
217 | |
218 object ParseLiteral(string literal) { | |
219 switch (literal) { | |
220 case "null": | |
221 return null; | |
222 case "false": | |
223 return false; | |
224 case "true": | |
225 return true; | |
226 default: | |
227 UnexpectedToken(literal, JsonTokenType.Literal); | |
228 return null; // avoid compliler error | |
229 } | |
230 } | |
231 | |
232 void UnexpectedToken(object value, JsonTokenType tokenType) { | |
233 throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value)); | |
234 } | |
235 | |
236 | |
237 /// <summary> | |
238 /// Признак конца потока | |
239 /// </summary> | |
240 public bool EOF { | |
241 get { | |
242 return m_scanner.EOF; | |
243 } | |
244 } | |
245 | |
246 protected virtual void Dispose(bool disposing) { | |
247 if (disposing) { | |
248 m_scanner.Dispose(); | |
249 } | |
250 } | |
251 | |
252 /// <summary> | |
253 /// Освобождает парсер и связанный с ним сканнер. | |
254 /// </summary> | |
255 public void Dispose() { | |
256 Dispose(true); | |
257 GC.SuppressFinalize(this); | |
258 } | |
259 | |
260 ~JSONParser() { | |
261 Dispose(false); | |
262 } | |
263 | |
264 /// <summary> | |
265 /// Переходит в конец текущего объекта. | |
266 /// </summary> | |
267 public void SeekElementEnd() { | |
268 var level = Level - 1; | |
269 | |
270 Debug.Assert(level >= 0); | |
271 | |
272 while (Level != level) | |
273 Read(); | |
274 } | |
275 } | |
276 | |
277 } |