| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #library("json"); | 5 #library("json"); |
| 6 | 6 |
| 7 // Pure Dart implementation of JSON protocol. | 7 // Pure Dart implementation of JSON protocol. |
| 8 | 8 |
| 9 /** | 9 /** |
| 10 * Utility class to parse JSON and serialize objects to JSON. | 10 * Utility class to parse JSON and serialize objects to JSON. |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 | 89 |
| 90 static final String NULL_STRING = "null"; | 90 static final String NULL_STRING = "null"; |
| 91 static final String TRUE_STRING = "true"; | 91 static final String TRUE_STRING = "true"; |
| 92 static final String FALSE_STRING = "false"; | 92 static final String FALSE_STRING = "false"; |
| 93 | 93 |
| 94 | 94 |
| 95 static parse(String json) { | 95 static parse(String json) { |
| 96 return new _JsonParser._internal(json)._parseToplevel(); | 96 return new _JsonParser._internal(json)._parseToplevel(); |
| 97 } | 97 } |
| 98 | 98 |
| 99 _JsonParser._internal(String json) : this.json = '${json} ' { | 99 _JsonParser._internal(String json) |
| 100 : json = json, |
| 101 length = json.length { |
| 100 if (tokens !== null) return; | 102 if (tokens !== null) return; |
| 101 | 103 |
| 102 // Use a list as jump-table, faster then switch and if. | 104 // Use a list as jump-table, faster then switch and if. |
| 103 tokens = new List<int>(LAST_ASCII + 1); | 105 tokens = new List<int>(LAST_ASCII + 1); |
| 104 tokens[TAB] = WHITESPACE; | 106 tokens[TAB] = WHITESPACE; |
| 105 tokens[NEW_LINE] = WHITESPACE; | 107 tokens[NEW_LINE] = WHITESPACE; |
| 106 tokens[CARRIAGE_RETURN] = WHITESPACE; | 108 tokens[CARRIAGE_RETURN] = WHITESPACE; |
| 107 tokens[SPACE] = WHITESPACE; | 109 tokens[SPACE] = WHITESPACE; |
| 108 tokens[CHAR_0] = NUMBER_LITERAL; | 110 tokens[CHAR_0] = NUMBER_LITERAL; |
| 109 tokens[CHAR_1] = NUMBER_LITERAL; | 111 tokens[CHAR_1] = NUMBER_LITERAL; |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 214 | 216 |
| 215 List<int> charCodes = new List<int>(); | 217 List<int> charCodes = new List<int>(); |
| 216 while (true) { | 218 while (true) { |
| 217 int c = _char(); | 219 int c = _char(); |
| 218 if (c == QUOTE) { | 220 if (c == QUOTE) { |
| 219 position++; | 221 position++; |
| 220 break; | 222 break; |
| 221 } | 223 } |
| 222 if (c == BACKSLASH) { | 224 if (c == BACKSLASH) { |
| 223 position++; | 225 position++; |
| 224 if (position == json.length) { | 226 if (position == length) { |
| 225 _error('\\ at the end of input'); | 227 _error('\\ at the end of input'); |
| 226 } | 228 } |
| 227 | 229 |
| 228 switch (_char()) { | 230 switch (_char()) { |
| 229 case QUOTE: | 231 case QUOTE: |
| 230 c = QUOTE; | 232 c = QUOTE; |
| 231 break; | 233 break; |
| 232 case BACKSLASH: | 234 case BACKSLASH: |
| 233 c = BACKSLASH; | 235 c = BACKSLASH; |
| 234 break; | 236 break; |
| 235 case SLASH: | 237 case SLASH: |
| 236 c = SLASH; | 238 c = SLASH; |
| 237 break; | 239 break; |
| 238 case CHAR_B: | 240 case CHAR_B: |
| 239 c = BACKSPACE; | 241 c = BACKSPACE; |
| 240 break; | 242 break; |
| 241 case CHAR_N: | 243 case CHAR_N: |
| 242 c = NEW_LINE; | 244 c = NEW_LINE; |
| 243 break; | 245 break; |
| 244 case CHAR_R: | 246 case CHAR_R: |
| 245 c = CARRIAGE_RETURN; | 247 c = CARRIAGE_RETURN; |
| 246 break; | 248 break; |
| 247 case CHAR_F: | 249 case CHAR_F: |
| 248 c = FORM_FEED; | 250 c = FORM_FEED; |
| 249 break; | 251 break; |
| 250 case CHAR_T: | 252 case CHAR_T: |
| 251 c = TAB; | 253 c = TAB; |
| 252 break; | 254 break; |
| 253 case CHAR_U: | 255 case CHAR_U: |
| 254 if (position + 5 > json.length) { | 256 if (position + 5 > length) { |
| 255 _error('Invalid unicode esacape sequence'); | 257 _error('Invalid unicode esacape sequence'); |
| 256 } | 258 } |
| 257 final codeString = json.substring(position + 1, position + 5); | 259 final codeString = json.substring(position + 1, position + 5); |
| 258 try { | 260 try { |
| 259 c = Math.parseInt('0x${codeString}'); | 261 c = Math.parseInt('0x${codeString}'); |
| 260 } catch (var e) { | 262 } catch (var e) { |
| 261 _error('Invalid unicode esacape sequence'); | 263 _error('Invalid unicode esacape sequence'); |
| 262 } | 264 } |
| 263 position += 4; | 265 position += 4; |
| 264 break; | 266 break; |
| 265 default: | 267 default: |
| 266 _error('Invalid esacape sequence in string literal'); | 268 _error('Invalid esacape sequence in string literal'); |
| 267 } | 269 } |
| 268 } | 270 } |
| 269 charCodes.add(c); | 271 charCodes.add(c); |
| 270 position++; | 272 position++; |
| 271 } | 273 } |
| 272 | 274 |
| 273 return new String.fromCharCodes(charCodes); | 275 return new String.fromCharCodes(charCodes); |
| 274 } | 276 } |
| 275 | 277 |
| 276 num _parseNumber() { | 278 num _parseNumber() { |
| 277 if (!_isToken(NUMBER_LITERAL)) _error("Expected number literal"); | 279 if (!_isToken(NUMBER_LITERAL)) _error('Expected number literal'); |
| 278 | 280 |
| 279 final int startPos = position; | 281 final int startPos = position; |
| 280 if (_isChar(MINUS)) position++; | 282 int char = _char(); |
| 281 if (_isChar(CHAR_0)) { | 283 if (char === MINUS) char = _nextChar(); |
| 282 position++; | 284 if (char === CHAR_0) { |
| 283 } else if (_isDigit()) { | 285 char = _nextChar(); |
| 284 position++; | 286 } else if (_isDigit(char)) { |
| 285 while (_isDigit()) position++; | 287 char = _nextChar(); |
| 288 while (_isDigit(char)) char = _nextChar(); |
| 286 } else { | 289 } else { |
| 287 _error("Expected digit when parsing number"); | 290 _error('Expected digit when parsing number'); |
| 288 } | 291 } |
| 289 | 292 |
| 290 bool isInt = true; | 293 bool isInt = true; |
| 291 if (_isChar(DOT)) { | 294 if (char === DOT) { |
| 292 position++; | 295 char = _nextChar(); |
| 293 if (_isDigit()) { | 296 if (_isDigit(char)) { |
| 297 char = _nextChar(); |
| 294 isInt = false; | 298 isInt = false; |
| 295 while (_isDigit()) position++; | 299 while (_isDigit(char)) char = _nextChar(); |
| 296 } else { | 300 } else { |
| 297 position--; // No digit, backtrack. | 301 _error('Expected digit following comma'); |
| 298 } | 302 } |
| 299 } | 303 } |
| 300 | 304 |
| 301 if (_isChar(CHAR_E) || _isChar(CHAR_CAPITAL_E)) { | 305 if (char === CHAR_E || char === CHAR_CAPITAL_E) { |
| 302 int backtrackTo = position; | 306 char = _nextChar(); |
| 303 position++; | 307 if (char === MINUS || char === PLUS) position++; |
| 304 if (_isChar(MINUS) || _isChar(PLUS)) position++; | 308 if (_isDigit(char)) { |
| 305 if (_isDigit()) { | 309 char = _nextChar(); |
| 306 position++; | |
| 307 isInt = false; | 310 isInt = false; |
| 308 while (_isDigit()) position++; | 311 while (_isDigit(char)) char = _nextChar(); |
| 309 } else { | 312 } else { |
| 310 position = backtrackTo; // No digit, backtrack. | 313 _error('Expected digit following \'e\' or \'E\''); |
| 311 } | 314 } |
| 312 } | 315 } |
| 313 | 316 |
| 314 String number = json.substring(startPos, position); | 317 String number = json.substring(startPos, position); |
| 315 if (isInt) { | 318 if (isInt) { |
| 316 return Math.parseInt(number); | 319 return Math.parseInt(number); |
| 317 } else { | 320 } else { |
| 318 return Math.parseDouble(number); | 321 return Math.parseDouble(number); |
| 319 } | 322 } |
| 320 } | 323 } |
| 321 | 324 |
| 322 bool _isChar(int char) => _char() == char; | 325 bool _isChar(int char) { |
| 326 if (position >= length) return false; |
| 327 return json.charCodeAt(position) == char; |
| 328 } |
| 323 | 329 |
| 324 bool _isDigit() { | 330 bool _isDigit(int char) { |
| 325 int char = _char(); | |
| 326 return char >= CHAR_0 && char <= CHAR_9; | 331 return char >= CHAR_0 && char <= CHAR_9; |
| 327 } | 332 } |
| 328 | 333 |
| 329 bool _isToken(int tokenKind) => _token() == tokenKind; | 334 bool _isToken(int tokenKind) => _token() == tokenKind; |
| 330 | 335 |
| 331 int _char() { | 336 int _char() { |
| 332 if (position >= json.length) { | 337 if (position >= length) { |
| 333 _error("Unexpected end of JSON stream"); | 338 _error('Unexpected end of JSON stream'); |
| 334 } | 339 } |
| 335 return json.charCodeAt(position); | 340 return json.charCodeAt(position); |
| 336 } | 341 } |
| 337 | 342 |
| 343 int _nextChar() { |
| 344 position++; |
| 345 if (position >= length) return 0; |
| 346 return json.charCodeAt(position); |
| 347 } |
| 348 |
| 338 int _token() { | 349 int _token() { |
| 339 while (true) { | 350 while (true) { |
| 340 if (position >= json.length) return null; | 351 if (position >= length) return null; |
| 341 int char = json.charCodeAt(position); | 352 int char = json.charCodeAt(position); |
| 342 int token = tokens[char]; | 353 int token = tokens[char]; |
| 343 if (token === WHITESPACE) { | 354 if (token === WHITESPACE) { |
| 344 position++; | 355 position++; |
| 345 continue; | 356 continue; |
| 346 } | 357 } |
| 347 if (token === null) _error("Invalid JSON token"); | 358 if (token === null) return 0; |
| 348 return token; | 359 return token; |
| 349 } | 360 } |
| 350 } | 361 } |
| 351 | 362 |
| 352 void _error(String message) { | 363 void _error(String message) { |
| 353 throw message; | 364 throw message; |
| 354 } | 365 } |
| 355 | 366 |
| 356 final String json; | 367 final String json; |
| 368 final int length; |
| 357 int position = 0; | 369 int position = 0; |
| 358 static List<int> tokens; | 370 static List<int> tokens; |
| 359 } | 371 } |
| 360 | 372 |
| 361 // TODO: proper base class. | 373 // TODO: proper base class. |
| 362 class JsonUnsupportedObjectType { | 374 class JsonUnsupportedObjectType { |
| 363 const JsonUnsupportedObjectType(); | 375 const JsonUnsupportedObjectType(); |
| 364 } | 376 } |
| 365 | 377 |
| 366 class JsonStringifier { | 378 class JsonStringifier { |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 508 }); | 520 }); |
| 509 _sb.add('}'); | 521 _sb.add('}'); |
| 510 _seen.removeLast(); | 522 _seen.removeLast(); |
| 511 return; | 523 return; |
| 512 | 524 |
| 513 default: | 525 default: |
| 514 throw const JsonUnsupportedObjectType(); | 526 throw const JsonUnsupportedObjectType(); |
| 515 } | 527 } |
| 516 } | 528 } |
| 517 } | 529 } |
| OLD | NEW |