| Index: samples/buildbot/json.dart
|
| diff --git a/samples/buildbot/json.dart b/samples/buildbot/json.dart
|
| index 68b4ee4f0290f1e7158e902d1e0b8fe62a3154ec..b02a008b5b0bc48c661578086003243f45a08aa0 100644
|
| --- a/samples/buildbot/json.dart
|
| +++ b/samples/buildbot/json.dart
|
| @@ -19,10 +19,8 @@ dynamic getJson(String host, String resource) {
|
| }
|
|
|
| class JsonParser {
|
| - int _index = 0;
|
| + int _offset = 0;
|
| String _json;
|
| - String _whitespaceUnits = ' \t\n';
|
| - String _delimiterUnits = ' \t\n,[]{}';
|
|
|
| JsonParser(this._json);
|
|
|
| @@ -31,37 +29,38 @@ class JsonParser {
|
| var result = _parseValue();
|
| _whitespace();
|
| if (_hasCurrent) {
|
| - throw "Parse failed to consume: ${_json.substring(_index)}";
|
| + throw "Parse failed to consume: ${_json.substring(_offset)}";
|
| }
|
| return result;
|
| }
|
|
|
| - int get _currentUnit => _json.codeUnitAt(_index);
|
| - String get _current => _json[_index];
|
| - bool get _hasCurrent => _index < _json.length;
|
| + bool get _hasCurrent => _offset < _json.length;
|
| + int get _current => _json.codeUnitAt(_offset);
|
| + String get _currentChar => _json[_offset];
|
| + void _consume() { ++_offset; }
|
|
|
| - bool _containsCurrent(String units) {
|
| - int length = units.length;
|
| - int unit = _currentUnit;
|
| - for (int i = 0; i < length; ++i) {
|
| - if (unit == units.codeUnitAt(i)) return true;
|
| - }
|
| - return false;
|
| + void _whitespace() {
|
| + while (_hasCurrent && _isWhitespace(_current)) _consume();
|
| }
|
|
|
| - void _whitespace() {
|
| - while (_hasCurrent && _containsCurrent(_whitespaceUnits)) ++_index;
|
| + bool _optional(int char) {
|
| + if (_current == char) {
|
| + _consume();
|
| + return true;
|
| + }
|
| + return false;
|
| }
|
|
|
| - void _parseToken(String token) {
|
| - if (!_tryParseToken(token)) {
|
| - throw "Expected $token at index $_index, found $_current";
|
| + void _expect(int char) {
|
| + if (!_optional(char)) {
|
| + String string = new String.fromCharCodes([char]);
|
| + throw "Expected $string at index $_offset, found $_currentChar";
|
| }
|
| }
|
|
|
| bool _tryParseToken(String token) {
|
| int length = token.length;
|
| - int start = _index;
|
| + int start = _offset;
|
| int end = start + length;
|
| if (end > _json.length) return false;
|
| for (int i = 0; i < length; ++i) {
|
| @@ -69,69 +68,107 @@ class JsonParser {
|
| return false;
|
| }
|
| }
|
| - _index += length;
|
| + _offset += length;
|
| return true;
|
| }
|
|
|
| - bool get _isDelimiter => _containsCurrent(_delimiterUnits);
|
| -
|
| String _readToken() {
|
| - int start = _index;
|
| - while (_hasCurrent && !_isDelimiter) ++_index;
|
| - return _json.substring(start, _index);
|
| + int start = _offset;
|
| + while (_hasCurrent && !_isSeparator(_current)) _consume();
|
| + return _json.substring(start, _offset);
|
| }
|
|
|
| dynamic _parseValue() {
|
| _whitespace();
|
| - if (_tryParseToken('{')) return _parseMap();
|
| - if (_tryParseToken('[')) return _parseList();
|
| - if (_tryParseToken('"')) return _parseString();
|
| - if (_tryParseToken('true')) return true;
|
| - if (_tryParseToken('false')) return false;
|
| - if (_tryParseToken('null')) return null;
|
| -
|
| - int tryIndex = _index;
|
| + if (_optional(_CHAR_BRACE_OPEN)) return _parseMap();
|
| + if (_optional(_CHAR_SQUARE_OPEN)) return _parseList();
|
| + if (_optional(_CHAR_DOUBLE_QUOTE)) return _parseString();
|
| + if (_tryParseToken(_TOKEN_TRUE)) return true;
|
| + if (_tryParseToken(_TOKEN_FALSE)) return false;
|
| + if (_tryParseToken(_TOKEN_NULL)) return null;
|
| +
|
| + int start = _offset;
|
| String token = _readToken();
|
| bool error = false;
|
| // TODO(zerny) floating point numbers.
|
| int tryInt = int.parse(token, onError: (_) { error = true; return 0; });
|
| if (!error) return tryInt;
|
|
|
| - String context = _json.substring(tryIndex, _min(tryIndex + 10, _json.length));
|
| - throw "Parse failed at index: $_index (context: '$context...')";
|
| + String context = _json.substring(start, _min(start + 10, _json.length));
|
| + throw "Parse failed at index: $_offset (context: '$context...')";
|
| }
|
|
|
| Map _parseMap() {
|
| Map map = new Map();
|
| _whitespace();
|
| - while (!_tryParseToken('}')) {
|
| - var key = _parseValue();
|
| - _whitespace();
|
| - _parseToken(':');
|
| - var value = _parseValue();
|
| - map[key] = value;
|
| - _whitespace();
|
| - _tryParseToken(',');
|
| - _whitespace();
|
| + if (_current != _CHAR_BRACE_CLOSE) {
|
| + do {
|
| + var key = _parseValue();
|
| + _whitespace();
|
| + _expect(_CHAR_COLON);
|
| + var value = _parseValue();
|
| + map[key] = value;
|
| + _whitespace();
|
| + } while (_optional(_CHAR_COMMA));
|
| }
|
| + _expect(_CHAR_BRACE_CLOSE);
|
| return map;
|
| }
|
|
|
| List _parseList() {
|
| List list = new List();
|
| _whitespace();
|
| - while (!_tryParseToken(']')) {
|
| - list.add(_parseValue());
|
| - _whitespace();
|
| - _tryParseToken(',');
|
| - _whitespace();
|
| + if (_current != _CHAR_SQUARE_CLOSE) {
|
| + do {
|
| + list.add(_parseValue());
|
| + _whitespace();
|
| + } while (_optional(_CHAR_COMMA));
|
| }
|
| + _expect(_CHAR_SQUARE_CLOSE);
|
| return list;
|
| }
|
|
|
| String _parseString() {
|
| - int start = _index;
|
| - while (_hasCurrent && _current != '"') ++_index;
|
| - return _json.substring(start, _index++);
|
| + int start = _offset;
|
| + while (_hasCurrent && _current != _CHAR_DOUBLE_QUOTE) {
|
| + if (_current == _CHAR_BACKSLASH) _consume();
|
| + _consume();
|
| + }
|
| + _expect(_CHAR_DOUBLE_QUOTE);
|
| + return _json.substring(start, _offset - 1);
|
| }
|
| +
|
| + static bool _isWhitespace(int char) =>
|
| + char == _CHAR_TAB ||
|
| + char == _CHAR_LINE_FEED ||
|
| + char == _CHAR_SPACE;
|
| +
|
| + static bool _isSeparator(int char) =>
|
| + _isWhitespace(char) ||
|
| + char == _CHAR_PAREN_OPEN ||
|
| + char == _CHAR_PAREN_CLOSE ||
|
| + char == _CHAR_COMMA ||
|
| + char == _CHAR_COLON ||
|
| + char == _CHAR_SQUARE_OPEN ||
|
| + char == _CHAR_SQUARE_CLOSE ||
|
| + char == _CHAR_BRACE_OPEN ||
|
| + char == _CHAR_BRACE_CLOSE;
|
| +
|
| + static const String _TOKEN_TRUE = 'true';
|
| + static const String _TOKEN_FALSE = 'false';
|
| + static const String _TOKEN_NULL = 'null';
|
| +
|
| + static const int _CHAR_TAB = 9;
|
| + static const int _CHAR_LINE_FEED = 10;
|
| + static const int _CHAR_SPACE = 32;
|
| + static const int _CHAR_DOUBLE_QUOTE = 34;
|
| + static const int _CHAR_PAREN_OPEN = 40;
|
| + static const int _CHAR_PAREN_CLOSE = 41;
|
| + static const int _CHAR_COMMA = 44;
|
| + static const int _CHAR_COLON = 58;
|
| + static const int _CHAR_SQUARE_OPEN = 91;
|
| + static const int _CHAR_BACKSLASH = 92;
|
| + static const int _CHAR_SQUARE_CLOSE = 93;
|
| + static const int _CHAR_BRACE_OPEN = 123;
|
| + static const int _CHAR_BRACE_CLOSE = 125;
|
| }
|
|
|