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 // JSON parsing and serialization. | 7 // JSON parsing and serialization. |
8 | 8 |
9 /** | 9 /** |
10 * Error thrown by JSON serialization if an object cannot be serialized. | 10 * Error thrown by JSON serialization if an object cannot be serialized. |
(...skipping 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
317 final JsonListener listener; | 317 final JsonListener listener; |
318 JsonParser(this.source, this.listener); | 318 JsonParser(this.source, this.listener); |
319 | 319 |
320 /** Parses [source], or throws if it fails. */ | 320 /** Parses [source], or throws if it fails. */ |
321 void parse() { | 321 void parse() { |
322 final List<int> states = <int>[]; | 322 final List<int> states = <int>[]; |
323 int state = STATE_INITIAL; | 323 int state = STATE_INITIAL; |
324 int position = 0; | 324 int position = 0; |
325 int length = source.length; | 325 int length = source.length; |
326 while (position < length) { | 326 while (position < length) { |
327 int char = source.charCodeAt(position); | 327 int char = source.codeUnitAt(position); |
328 switch (char) { | 328 switch (char) { |
329 case SPACE: | 329 case SPACE: |
330 case CARRIAGE_RETURN: | 330 case CARRIAGE_RETURN: |
331 case NEWLINE: | 331 case NEWLINE: |
332 case TAB: | 332 case TAB: |
333 position++; | 333 position++; |
334 break; | 334 break; |
335 case QUOTE: | 335 case QUOTE: |
336 if ((state & ALLOW_STRING_MASK) != 0) fail(position); | 336 if ((state & ALLOW_STRING_MASK) != 0) fail(position); |
337 position = parseString(position + 1); | 337 position = parseString(position + 1); |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
418 } | 418 } |
419 if (state != STATE_END) fail(position); | 419 if (state != STATE_END) fail(position); |
420 } | 420 } |
421 | 421 |
422 /** | 422 /** |
423 * Parses a "true" literal starting at [position]. | 423 * Parses a "true" literal starting at [position]. |
424 * | 424 * |
425 * [:source[position]:] must be "t". | 425 * [:source[position]:] must be "t". |
426 */ | 426 */ |
427 int parseTrue(int position) { | 427 int parseTrue(int position) { |
428 assert(source.charCodeAt(position) == CHAR_t); | 428 assert(source.codeUnitAt(position) == CHAR_t); |
429 if (source.length < position + 4) fail(position, "Unexpected identifier"); | 429 if (source.length < position + 4) fail(position, "Unexpected identifier"); |
430 if (source.charCodeAt(position + 1) != CHAR_r || | 430 if (source.codeUnitAt(position + 1) != CHAR_r || |
431 source.charCodeAt(position + 2) != CHAR_u || | 431 source.codeUnitAt(position + 2) != CHAR_u || |
432 source.charCodeAt(position + 3) != CHAR_e) { | 432 source.codeUnitAt(position + 3) != CHAR_e) { |
433 fail(position); | 433 fail(position); |
434 } | 434 } |
435 listener.handleBool(true); | 435 listener.handleBool(true); |
436 return position + 4; | 436 return position + 4; |
437 } | 437 } |
438 | 438 |
439 /** | 439 /** |
440 * Parses a "false" literal starting at [position]. | 440 * Parses a "false" literal starting at [position]. |
441 * | 441 * |
442 * [:source[position]:] must be "f". | 442 * [:source[position]:] must be "f". |
443 */ | 443 */ |
444 int parseFalse(int position) { | 444 int parseFalse(int position) { |
445 assert(source.charCodeAt(position) == CHAR_f); | 445 assert(source.codeUnitAt(position) == CHAR_f); |
446 if (source.length < position + 5) fail(position, "Unexpected identifier"); | 446 if (source.length < position + 5) fail(position, "Unexpected identifier"); |
447 if (source.charCodeAt(position + 1) != CHAR_a || | 447 if (source.codeUnitAt(position + 1) != CHAR_a || |
448 source.charCodeAt(position + 2) != CHAR_l || | 448 source.codeUnitAt(position + 2) != CHAR_l || |
449 source.charCodeAt(position + 3) != CHAR_s || | 449 source.codeUnitAt(position + 3) != CHAR_s || |
450 source.charCodeAt(position + 4) != CHAR_e) { | 450 source.codeUnitAt(position + 4) != CHAR_e) { |
451 fail(position); | 451 fail(position); |
452 } | 452 } |
453 listener.handleBool(false); | 453 listener.handleBool(false); |
454 return position + 5; | 454 return position + 5; |
455 } | 455 } |
456 | 456 |
457 /** Parses a "null" literal starting at [position]. | 457 /** Parses a "null" literal starting at [position]. |
458 * | 458 * |
459 * [:source[position]:] must be "n". | 459 * [:source[position]:] must be "n". |
460 */ | 460 */ |
461 int parseNull(int position) { | 461 int parseNull(int position) { |
462 assert(source.charCodeAt(position) == CHAR_n); | 462 assert(source.codeUnitAt(position) == CHAR_n); |
463 if (source.length < position + 4) fail(position, "Unexpected identifier"); | 463 if (source.length < position + 4) fail(position, "Unexpected identifier"); |
464 if (source.charCodeAt(position + 1) != CHAR_u || | 464 if (source.codeUnitAt(position + 1) != CHAR_u || |
465 source.charCodeAt(position + 2) != CHAR_l || | 465 source.codeUnitAt(position + 2) != CHAR_l || |
466 source.charCodeAt(position + 3) != CHAR_l) { | 466 source.codeUnitAt(position + 3) != CHAR_l) { |
467 fail(position); | 467 fail(position); |
468 } | 468 } |
469 listener.handleNull(); | 469 listener.handleNull(); |
470 return position + 4; | 470 return position + 4; |
471 } | 471 } |
472 | 472 |
473 int parseString(int position) { | 473 int parseString(int position) { |
474 // Format: '"'([^\x00-\x1f\\\"]|'\\'[bfnrt/\\"])*'"' | 474 // Format: '"'([^\x00-\x1f\\\"]|'\\'[bfnrt/\\"])*'"' |
475 // Initial position is right after first '"'. | 475 // Initial position is right after first '"'. |
476 int start = position; | 476 int start = position; |
477 int char; | 477 int char; |
478 do { | 478 do { |
479 if (position == source.length) { | 479 if (position == source.length) { |
480 fail(start - 1, "Unterminated string"); | 480 fail(start - 1, "Unterminated string"); |
481 } | 481 } |
482 char = source.charCodeAt(position); | 482 char = source.codeUnitAt(position); |
483 if (char == QUOTE) { | 483 if (char == QUOTE) { |
484 listener.handleString(source.substring(start, position)); | 484 listener.handleString(source.substring(start, position)); |
485 return position + 1; | 485 return position + 1; |
486 } | 486 } |
487 if (char < SPACE) { | 487 if (char < SPACE) { |
488 fail(position, "Control character in string"); | 488 fail(position, "Control character in string"); |
489 } | 489 } |
490 position++; | 490 position++; |
491 } while (char != BACKSLASH); | 491 } while (char != BACKSLASH); |
492 // Backslash escape detected. Collect character codes for rest of string. | 492 // Backslash escape detected. Collect character codes for rest of string. |
493 int firstEscape = position - 1; | 493 int firstEscape = position - 1; |
494 List<int> chars = <int>[]; | 494 List<int> chars = <int>[]; |
495 while (true) { | 495 while (true) { |
496 if (position == source.length) { | 496 if (position == source.length) { |
497 fail(start - 1, "Unterminated string"); | 497 fail(start - 1, "Unterminated string"); |
498 } | 498 } |
499 char = source.charCodeAt(position); | 499 char = source.codeUnitAt(position); |
500 switch (char) { | 500 switch (char) { |
501 case CHAR_b: char = BACKSPACE; break; | 501 case CHAR_b: char = BACKSPACE; break; |
502 case CHAR_f: char = FORM_FEED; break; | 502 case CHAR_f: char = FORM_FEED; break; |
503 case CHAR_n: char = NEWLINE; break; | 503 case CHAR_n: char = NEWLINE; break; |
504 case CHAR_r: char = CARRIAGE_RETURN; break; | 504 case CHAR_r: char = CARRIAGE_RETURN; break; |
505 case CHAR_t: char = TAB; break; | 505 case CHAR_t: char = TAB; break; |
506 case SLASH: | 506 case SLASH: |
507 case BACKSLASH: | 507 case BACKSLASH: |
508 case QUOTE: | 508 case QUOTE: |
509 break; | 509 break; |
510 case CHAR_u: | 510 case CHAR_u: |
511 int hexStart = position - 1; | 511 int hexStart = position - 1; |
512 int value = 0; | 512 int value = 0; |
513 for (int i = 0; i < 4; i++) { | 513 for (int i = 0; i < 4; i++) { |
514 position++; | 514 position++; |
515 if (position == source.length) { | 515 if (position == source.length) { |
516 fail(start - 1, "Unterminated string"); | 516 fail(start - 1, "Unterminated string"); |
517 } | 517 } |
518 char = source.charCodeAt(position); | 518 char = source.codeUnitAt(position); |
519 char -= 0x30; | 519 char -= 0x30; |
520 if (char < 0) fail(hexStart, "Invalid unicode escape"); | 520 if (char < 0) fail(hexStart, "Invalid unicode escape"); |
521 if (char < 10) { | 521 if (char < 10) { |
522 value = value * 16 + char; | 522 value = value * 16 + char; |
523 } else { | 523 } else { |
524 char = (char | 0x20) - 0x31; | 524 char = (char | 0x20) - 0x31; |
525 if (char < 0 || char > 5) { | 525 if (char < 0 || char > 5) { |
526 fail(hexStart, "Invalid unicode escape"); | 526 fail(hexStart, "Invalid unicode escape"); |
527 } | 527 } |
528 value = value * 16 + char + 10; | 528 value = value * 16 + char + 10; |
529 } | 529 } |
530 } | 530 } |
531 char = value; | 531 char = value; |
532 break; | 532 break; |
533 default: | 533 default: |
534 if (char < SPACE) fail(position, "Control character in string"); | 534 if (char < SPACE) fail(position, "Control character in string"); |
535 fail(position, "Unrecognized string escape"); | 535 fail(position, "Unrecognized string escape"); |
536 } | 536 } |
537 do { | 537 do { |
538 chars.add(char); | 538 chars.add(char); |
539 position++; | 539 position++; |
540 if (position == source.length) fail(start - 1, "Unterminated string"); | 540 if (position == source.length) fail(start - 1, "Unterminated string"); |
541 char = source.charCodeAt(position); | 541 char = source.codeUnitAt(position); |
542 if (char == QUOTE) { | 542 if (char == QUOTE) { |
543 String result = new String.fromCharCodes(chars); | 543 String result = new String.fromCharCodes(chars); |
544 if (start < firstEscape) { | 544 if (start < firstEscape) { |
545 result = "${source.substring(start, firstEscape)}$result"; | 545 result = "${source.substring(start, firstEscape)}$result"; |
546 } | 546 } |
547 listener.handleString(result); | 547 listener.handleString(result); |
548 return position + 1; | 548 return position + 1; |
549 } | 549 } |
550 if (char < SPACE) { | 550 if (char < SPACE) { |
551 fail(position, "Control character in string"); | 551 fail(position, "Control character in string"); |
552 } | 552 } |
553 } while (char != BACKSLASH); | 553 } while (char != BACKSLASH); |
554 position++; | 554 position++; |
555 } | 555 } |
556 } | 556 } |
557 | 557 |
558 int parseNumber(int char, int position) { | 558 int parseNumber(int char, int position) { |
559 // Format: | 559 // Format: |
560 // '-'?('0'|[1-9][0-9]*)('.'[0-9]+)?([eE][+-]?[0-9]+)? | 560 // '-'?('0'|[1-9][0-9]*)('.'[0-9]+)?([eE][+-]?[0-9]+)? |
561 int start = position; | 561 int start = position; |
562 int length = source.length; | 562 int length = source.length; |
563 bool isDouble = false; | 563 bool isDouble = false; |
564 if (char == MINUS) { | 564 if (char == MINUS) { |
565 position++; | 565 position++; |
566 if (position == length) fail(position, "Missing expected digit"); | 566 if (position == length) fail(position, "Missing expected digit"); |
567 char = source.charCodeAt(position); | 567 char = source.codeUnitAt(position); |
568 } | 568 } |
569 if (char < CHAR_0 || char > CHAR_9) { | 569 if (char < CHAR_0 || char > CHAR_9) { |
570 fail(position, "Missing expected digit"); | 570 fail(position, "Missing expected digit"); |
571 } | 571 } |
572 int handleLiteral(position) { | 572 int handleLiteral(position) { |
573 String literal = source.substring(start, position); | 573 String literal = source.substring(start, position); |
574 // This correctly creates -0 for doubles. | 574 // This correctly creates -0 for doubles. |
575 num value = (isDouble ? double.parse(literal) : int.parse(literal)); | 575 num value = (isDouble ? double.parse(literal) : int.parse(literal)); |
576 listener.handleNumber(value); | 576 listener.handleNumber(value); |
577 return position; | 577 return position; |
578 } | 578 } |
579 if (char == CHAR_0) { | 579 if (char == CHAR_0) { |
580 position++; | 580 position++; |
581 if (position == length) return handleLiteral(position); | 581 if (position == length) return handleLiteral(position); |
582 char = source.charCodeAt(position); | 582 char = source.codeUnitAt(position); |
583 if (CHAR_0 <= char && char <= CHAR_9) { | 583 if (CHAR_0 <= char && char <= CHAR_9) { |
584 fail(position); | 584 fail(position); |
585 } | 585 } |
586 } else { | 586 } else { |
587 do { | 587 do { |
588 position++; | 588 position++; |
589 if (position == length) return handleLiteral(position); | 589 if (position == length) return handleLiteral(position); |
590 char = source.charCodeAt(position); | 590 char = source.codeUnitAt(position); |
591 } while (CHAR_0 <= char && char <= CHAR_9); | 591 } while (CHAR_0 <= char && char <= CHAR_9); |
592 } | 592 } |
593 if (char == DECIMALPOINT) { | 593 if (char == DECIMALPOINT) { |
594 isDouble = true; | 594 isDouble = true; |
595 position++; | 595 position++; |
596 if (position == length) fail(position, "Missing expected digit"); | 596 if (position == length) fail(position, "Missing expected digit"); |
597 char = source.charCodeAt(position); | 597 char = source.codeUnitAt(position); |
598 if (char < CHAR_0 || char > CHAR_9) fail(position); | 598 if (char < CHAR_0 || char > CHAR_9) fail(position); |
599 do { | 599 do { |
600 position++; | 600 position++; |
601 if (position == length) return handleLiteral(position); | 601 if (position == length) return handleLiteral(position); |
602 char = source.charCodeAt(position); | 602 char = source.codeUnitAt(position); |
603 } while (CHAR_0 <= char && char <= CHAR_9); | 603 } while (CHAR_0 <= char && char <= CHAR_9); |
604 } | 604 } |
605 if (char == CHAR_e || char == CHAR_E) { | 605 if (char == CHAR_e || char == CHAR_E) { |
606 isDouble = true; | 606 isDouble = true; |
607 position++; | 607 position++; |
608 if (position == length) fail(position, "Missing expected digit"); | 608 if (position == length) fail(position, "Missing expected digit"); |
609 char = source.charCodeAt(position); | 609 char = source.codeUnitAt(position); |
610 if (char == PLUS || char == MINUS) { | 610 if (char == PLUS || char == MINUS) { |
611 position++; | 611 position++; |
612 if (position == length) fail(position, "Missing expected digit"); | 612 if (position == length) fail(position, "Missing expected digit"); |
613 char = source.charCodeAt(position); | 613 char = source.codeUnitAt(position); |
614 } | 614 } |
615 if (char < CHAR_0 || char > CHAR_9) { | 615 if (char < CHAR_0 || char > CHAR_9) { |
616 fail(position, "Missing expected digit"); | 616 fail(position, "Missing expected digit"); |
617 } | 617 } |
618 do { | 618 do { |
619 position++; | 619 position++; |
620 if (position == length) return handleLiteral(position); | 620 if (position == length) return handleLiteral(position); |
621 char = source.charCodeAt(position); | 621 char = source.codeUnitAt(position); |
622 } while (CHAR_0 <= char && char <= CHAR_9); | 622 } while (CHAR_0 <= char && char <= CHAR_9); |
623 } | 623 } |
624 return handleLiteral(position); | 624 return handleLiteral(position); |
625 } | 625 } |
626 | 626 |
627 void fail(int position, [String message]) { | 627 void fail(int position, [String message]) { |
628 if (message == null) message = "Unexpected character"; | 628 if (message == null) message = "Unexpected character"; |
629 listener.fail(source, position, message); | 629 listener.fail(source, position, message); |
630 // If the listener didn't throw, do it here. | 630 // If the listener didn't throw, do it here. |
631 String slice; | 631 String slice; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
663 } | 663 } |
664 | 664 |
665 // ('0' + x) or ('a' + x - 10) | 665 // ('0' + x) or ('a' + x - 10) |
666 static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x; | 666 static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x; |
667 | 667 |
668 static void escape(StringBuffer sb, String s) { | 668 static void escape(StringBuffer sb, String s) { |
669 final int length = s.length; | 669 final int length = s.length; |
670 bool needsEscape = false; | 670 bool needsEscape = false; |
671 final charCodes = new List<int>(); | 671 final charCodes = new List<int>(); |
672 for (int i = 0; i < length; i++) { | 672 for (int i = 0; i < length; i++) { |
673 int charCode = s.charCodeAt(i); | 673 int charCode = s.codeUnitAt(i); |
674 if (charCode < 32) { | 674 if (charCode < 32) { |
675 needsEscape = true; | 675 needsEscape = true; |
676 charCodes.add(JsonParser.BACKSLASH); | 676 charCodes.add(JsonParser.BACKSLASH); |
677 switch (charCode) { | 677 switch (charCode) { |
678 case JsonParser.BACKSPACE: | 678 case JsonParser.BACKSPACE: |
679 charCodes.add(JsonParser.CHAR_b); | 679 charCodes.add(JsonParser.CHAR_b); |
680 break; | 680 break; |
681 case JsonParser.TAB: | 681 case JsonParser.TAB: |
682 charCodes.add(JsonParser.CHAR_t); | 682 charCodes.add(JsonParser.CHAR_t); |
683 break; | 683 break; |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
795 first = false; | 795 first = false; |
796 }); | 796 }); |
797 sb.add('}'); | 797 sb.add('}'); |
798 seen.removeLast(); | 798 seen.removeLast(); |
799 return true; | 799 return true; |
800 } else { | 800 } else { |
801 return false; | 801 return false; |
802 } | 802 } |
803 } | 803 } |
804 } | 804 } |
OLD | NEW |