| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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("dart:json"); | 5 #library("dart:json"); |
| 6 | 6 |
| 7 #import('dart:math'); | 7 #import('dart:math'); |
| 8 | 8 |
| 9 // JSON parsing and serialization. | 9 // JSON parsing and serialization. |
| 10 | 10 |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 165 final int length; | 165 final int length; |
| 166 int position = 0; | 166 int position = 0; |
| 167 | 167 |
| 168 static parse(String json) { | 168 static parse(String json) { |
| 169 return new _JsonParser(json).parseToplevel(); | 169 return new _JsonParser(json).parseToplevel(); |
| 170 } | 170 } |
| 171 | 171 |
| 172 _JsonParser(String json) | 172 _JsonParser(String json) |
| 173 : json = json, | 173 : json = json, |
| 174 length = json.length { | 174 length = json.length { |
| 175 if (tokens !== null) return; | 175 if (tokens != null) return; |
| 176 | 176 |
| 177 // Use a list as jump-table. It is faster than switch and if. | 177 // Use a list as jump-table. It is faster than switch and if. |
| 178 tokens = new List<int>(LAST_ASCII + 1); | 178 tokens = new List<int>(LAST_ASCII + 1); |
| 179 tokens[TAB] = WHITESPACE; | 179 tokens[TAB] = WHITESPACE; |
| 180 tokens[NEW_LINE] = WHITESPACE; | 180 tokens[NEW_LINE] = WHITESPACE; |
| 181 tokens[CARRIAGE_RETURN] = WHITESPACE; | 181 tokens[CARRIAGE_RETURN] = WHITESPACE; |
| 182 tokens[SPACE] = WHITESPACE; | 182 tokens[SPACE] = WHITESPACE; |
| 183 tokens[CHAR_0] = NUMBER_LITERAL; | 183 tokens[CHAR_0] = NUMBER_LITERAL; |
| 184 tokens[CHAR_1] = NUMBER_LITERAL; | 184 tokens[CHAR_1] = NUMBER_LITERAL; |
| 185 tokens[CHAR_2] = NUMBER_LITERAL; | 185 tokens[CHAR_2] = NUMBER_LITERAL; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 198 tokens[QUOTE] = STRING_LITERAL; | 198 tokens[QUOTE] = STRING_LITERAL; |
| 199 tokens[COLON] = COLON; | 199 tokens[COLON] = COLON; |
| 200 tokens[COMMA] = COMMA; | 200 tokens[COMMA] = COMMA; |
| 201 tokens[CHAR_N] = NULL_LITERAL; | 201 tokens[CHAR_N] = NULL_LITERAL; |
| 202 tokens[CHAR_T] = TRUE_LITERAL; | 202 tokens[CHAR_T] = TRUE_LITERAL; |
| 203 tokens[CHAR_F] = FALSE_LITERAL; | 203 tokens[CHAR_F] = FALSE_LITERAL; |
| 204 } | 204 } |
| 205 | 205 |
| 206 parseToplevel() { | 206 parseToplevel() { |
| 207 final result = parseValue(); | 207 final result = parseValue(); |
| 208 if (token() !== null) { | 208 if (token() != null) { |
| 209 error('Junk at the end of JSON input'); | 209 error('Junk at the end of JSON input'); |
| 210 } | 210 } |
| 211 return result; | 211 return result; |
| 212 } | 212 } |
| 213 | 213 |
| 214 parseValue() { | 214 parseValue() { |
| 215 final int token = token(); | 215 final int token = token(); |
| 216 if (token === null) { | 216 if (token == null) { |
| 217 error('Nothing to parse'); | 217 error('Nothing to parse'); |
| 218 } | 218 } |
| 219 switch (token) { | 219 switch (token) { |
| 220 case STRING_LITERAL: return parseString(); | 220 case STRING_LITERAL: return parseString(); |
| 221 case NUMBER_LITERAL: return parseNumber(); | 221 case NUMBER_LITERAL: return parseNumber(); |
| 222 case NULL_LITERAL: return expectKeyword(NULL_STRING, null); | 222 case NULL_LITERAL: return expectKeyword(NULL_STRING, null); |
| 223 case FALSE_LITERAL: return expectKeyword(FALSE_STRING, false); | 223 case FALSE_LITERAL: return expectKeyword(FALSE_STRING, false); |
| 224 case TRUE_LITERAL: return expectKeyword(TRUE_STRING, true); | 224 case TRUE_LITERAL: return expectKeyword(TRUE_STRING, true); |
| 225 case LBRACE: return parseObject(); | 225 case LBRACE: return parseObject(); |
| 226 case LBRACKET: return parseList(); | 226 case LBRACKET: return parseList(); |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 346 } | 346 } |
| 347 | 347 |
| 348 return new String.fromCharCodes(charCodes); | 348 return new String.fromCharCodes(charCodes); |
| 349 } | 349 } |
| 350 | 350 |
| 351 num parseNumber() { | 351 num parseNumber() { |
| 352 if (!isToken(NUMBER_LITERAL)) error('Expected number literal'); | 352 if (!isToken(NUMBER_LITERAL)) error('Expected number literal'); |
| 353 | 353 |
| 354 final int startPos = position; | 354 final int startPos = position; |
| 355 int char = char(); | 355 int char = char(); |
| 356 if (char === MINUS) char = nextChar(); | 356 if (identical(char, MINUS)) char = nextChar(); |
| 357 if (char === CHAR_0) { | 357 if (identical(char, CHAR_0)) { |
| 358 char = nextChar(); | 358 char = nextChar(); |
| 359 } else if (isDigit(char)) { | 359 } else if (isDigit(char)) { |
| 360 char = nextChar(); | 360 char = nextChar(); |
| 361 while (isDigit(char)) char = nextChar(); | 361 while (isDigit(char)) char = nextChar(); |
| 362 } else { | 362 } else { |
| 363 error('Expected digit when parsing number'); | 363 error('Expected digit when parsing number'); |
| 364 } | 364 } |
| 365 | 365 |
| 366 bool isInt = true; | 366 bool isInt = true; |
| 367 if (char === DOT) { | 367 if (identical(char, DOT)) { |
| 368 char = nextChar(); | 368 char = nextChar(); |
| 369 if (isDigit(char)) { | 369 if (isDigit(char)) { |
| 370 char = nextChar(); | 370 char = nextChar(); |
| 371 isInt = false; | 371 isInt = false; |
| 372 while (isDigit(char)) char = nextChar(); | 372 while (isDigit(char)) char = nextChar(); |
| 373 } else { | 373 } else { |
| 374 error('Expected digit following comma'); | 374 error('Expected digit following comma'); |
| 375 } | 375 } |
| 376 } | 376 } |
| 377 | 377 |
| 378 if (char === CHAR_E || char === CHAR_CAPITAL_E) { | 378 if (identical(char, CHAR_E) || identical(char, CHAR_CAPITAL_E)) { |
| 379 char = nextChar(); | 379 char = nextChar(); |
| 380 if (char === MINUS || char === PLUS) char = nextChar(); | 380 if (identical(char, MINUS) || identical(char, PLUS)) char = nextChar(); |
| 381 if (isDigit(char)) { | 381 if (isDigit(char)) { |
| 382 char = nextChar(); | 382 char = nextChar(); |
| 383 isInt = false; | 383 isInt = false; |
| 384 while (isDigit(char)) char = nextChar(); | 384 while (isDigit(char)) char = nextChar(); |
| 385 } else { | 385 } else { |
| 386 error('Expected digit following \'e\' or \'E\''); | 386 error('Expected digit following \'e\' or \'E\''); |
| 387 } | 387 } |
| 388 } | 388 } |
| 389 | 389 |
| 390 String number = json.substring(startPos, position); | 390 String number = json.substring(startPos, position); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 417 position++; | 417 position++; |
| 418 if (position >= length) return 0; | 418 if (position >= length) return 0; |
| 419 return json.charCodeAt(position); | 419 return json.charCodeAt(position); |
| 420 } | 420 } |
| 421 | 421 |
| 422 int token() { | 422 int token() { |
| 423 while (true) { | 423 while (true) { |
| 424 if (position >= length) return null; | 424 if (position >= length) return null; |
| 425 int char = json.charCodeAt(position); | 425 int char = json.charCodeAt(position); |
| 426 int token = tokens[char]; | 426 int token = tokens[char]; |
| 427 if (token === WHITESPACE) { | 427 if (identical(token, WHITESPACE)) { |
| 428 position++; | 428 position++; |
| 429 continue; | 429 continue; |
| 430 } | 430 } |
| 431 if (token === null) return 0; | 431 if (token == null) return 0; |
| 432 return token; | 432 return token; |
| 433 } | 433 } |
| 434 } | 434 } |
| 435 | 435 |
| 436 void error(String message) { | 436 void error(String message) { |
| 437 throw message; | 437 throw message; |
| 438 } | 438 } |
| 439 } | 439 } |
| 440 | 440 |
| 441 class _JsonStringifier { | 441 class _JsonStringifier { |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 504 } else { | 504 } else { |
| 505 charCodes.add(charCode); | 505 charCodes.add(charCode); |
| 506 } | 506 } |
| 507 } | 507 } |
| 508 sb.add(needsEscape ? new String.fromCharCodes(charCodes) : s); | 508 sb.add(needsEscape ? new String.fromCharCodes(charCodes) : s); |
| 509 } | 509 } |
| 510 | 510 |
| 511 void checkCycle(final object) { | 511 void checkCycle(final object) { |
| 512 // TODO: use Iterables. | 512 // TODO: use Iterables. |
| 513 for (int i = 0; i < seen.length; i++) { | 513 for (int i = 0; i < seen.length; i++) { |
| 514 if (seen[i] === object) { | 514 if (identical(seen[i], object)) { |
| 515 throw 'Cyclic structure'; | 515 throw 'Cyclic structure'; |
| 516 } | 516 } |
| 517 } | 517 } |
| 518 seen.add(object); | 518 seen.add(object); |
| 519 } | 519 } |
| 520 | 520 |
| 521 void stringifyValue(final object) { | 521 void stringifyValue(final object) { |
| 522 // Tries stringifying object directly. If it's not a simple value, List or | 522 // Tries stringifying object directly. If it's not a simple value, List or |
| 523 // Map, call toJson() to get a custom representation and try serializing | 523 // Map, call toJson() to get a custom representation and try serializing |
| 524 // that. | 524 // that. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 540 * Serializes a [num], [String], [bool], [Null], [List] or [Map] value. | 540 * Serializes a [num], [String], [bool], [Null], [List] or [Map] value. |
| 541 * | 541 * |
| 542 * Returns true if the value is one of these types, and false if not. | 542 * Returns true if the value is one of these types, and false if not. |
| 543 * If a value is both a [List] and a [Map], it's serialized as a [List]. | 543 * If a value is both a [List] and a [Map], it's serialized as a [List]. |
| 544 */ | 544 */ |
| 545 bool stringifyJsonValue(final object) { | 545 bool stringifyJsonValue(final object) { |
| 546 if (object is num) { | 546 if (object is num) { |
| 547 // TODO: use writeOn. | 547 // TODO: use writeOn. |
| 548 sb.add(numberToString(object)); | 548 sb.add(numberToString(object)); |
| 549 return true; | 549 return true; |
| 550 } else if (object === true) { | 550 } else if (identical(object, true)) { |
| 551 sb.add('true'); | 551 sb.add('true'); |
| 552 return true; | 552 return true; |
| 553 } else if (object === false) { | 553 } else if (identical(object, false)) { |
| 554 sb.add('false'); | 554 sb.add('false'); |
| 555 return true; | 555 return true; |
| 556 } else if (object === null) { | 556 } else if (object == null) { |
| 557 sb.add('null'); | 557 sb.add('null'); |
| 558 return true; | 558 return true; |
| 559 } else if (object is String) { | 559 } else if (object is String) { |
| 560 sb.add('"'); | 560 sb.add('"'); |
| 561 escape(sb, object); | 561 escape(sb, object); |
| 562 sb.add('"'); | 562 sb.add('"'); |
| 563 return true; | 563 return true; |
| 564 } else if (object is List) { | 564 } else if (object is List) { |
| 565 checkCycle(object); | 565 checkCycle(object); |
| 566 List a = object; | 566 List a = object; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 593 first = false; | 593 first = false; |
| 594 }); | 594 }); |
| 595 sb.add('}'); | 595 sb.add('}'); |
| 596 seen.removeLast(); | 596 seen.removeLast(); |
| 597 return true; | 597 return true; |
| 598 } else { | 598 } else { |
| 599 return false; | 599 return false; |
| 600 } | 600 } |
| 601 } | 601 } |
| 602 } | 602 } |
| OLD | NEW |