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