Mercurial > pub > ImplabNet
annotate Implab/JSON/JSONParser.cs @ 160:5802131432e4 v2
fixed regression: race condition in Promise
DFA refactoring
author | cin |
---|---|
date | Thu, 18 Feb 2016 19:38:54 +0300 |
parents | 97fbbf816844 |
children |
rev | line source |
---|---|
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
140
diff
changeset
|
1 using Implab.Parsing; |
55 | 2 using System; |
3 using System.Diagnostics; | |
59 | 4 using System.IO; |
55 | 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> | |
70 | 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> | |
59 | 31 public class JSONParser : DFAutomaton<JSONParserContext>, IDisposable { |
55 | 32 |
33 enum MemberContext { | |
34 MemberName, | |
35 MemberValue | |
59 | 36 } |
55 | 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() { | |
140 | 44 |
55 | 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); | |
140 | 48 |
55 | 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 | |
140 | 68 var jsonExpression = valueExpression.Tag(0); |
69 | |
55 | 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 | |
59 | 90 /// <summary> |
91 /// Создает новый парсер на основе строки, содержащей JSON | |
92 /// </summary> | |
93 /// <param name="text"></param> | |
55 | 94 public JSONParser(string text) |
59 | 95 : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { |
55 | 96 Safe.ArgumentNotEmpty(text, "text"); |
97 m_scanner = new JSONScanner(); | |
98 m_scanner.Feed(text.ToCharArray()); | |
99 } | |
100 | |
59 | 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> | |
55 | 116 public JSONElementType ElementType { |
117 get { return m_elementType; } | |
118 } | |
119 | |
59 | 120 /// <summary> |
121 /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда | |
122 /// пустая строка. | |
123 /// </summary> | |
55 | 124 public string ElementName { |
125 get { return m_context.info.memberName; } | |
126 } | |
127 | |
59 | 128 /// <summary> |
129 /// Значение элемента. Только для элементов типа <see cref="JSONElementType.Value"/>, для остальных <c>null</c> | |
130 /// </summary> | |
55 | 131 public object ElementValue { |
132 get { return m_elementValue; } | |
133 } | |
134 | |
59 | 135 /// <summary> |
136 /// Читает слеюудущий объект из потока | |
137 /// </summary> | |
138 /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns> | |
55 | 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, | |
59 | 154 new JSONParserContext { |
55 | 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 } | |
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
140
diff
changeset
|
191 m_elementType = JSONElementType.Value; |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
140
diff
changeset
|
192 m_elementValue = tokenValue; |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
140
diff
changeset
|
193 return true; |
55 | 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; | |
59 | 222 case "false": |
55 | 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 | |
57 | 236 |
59 | 237 /// <summary> |
238 /// Признак конца потока | |
239 /// </summary> | |
57 | 240 public bool EOF { |
241 get { | |
242 return m_scanner.EOF; | |
243 } | |
244 } | |
59 | 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 } | |
62
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
263 |
70 | 264 /// <summary> |
265 /// Переходит в конец текущего объекта. | |
266 /// </summary> | |
267 public void SeekElementEnd() { | |
268 var level = Level - 1; | |
62
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
269 |
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
270 Debug.Assert(level >= 0); |
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
271 |
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
272 while (Level != level) |
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
273 Read(); |
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
274 } |
55 | 275 } |
276 | |
277 } |