Chromium Code Reviews| Index: samples/buildbot/json.dart |
| diff --git a/samples/buildbot/json.dart b/samples/buildbot/json.dart |
| index 68b4ee4f0290f1e7158e902d1e0b8fe62a3154ec..7e69c5dfc8a1ae937c3e6df6437878dd4fee2a13 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,49 +68,47 @@ 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('}')) { |
| + while (!_optional(_CHAR_BRACE_CLOSE)) { |
| var key = _parseValue(); |
| _whitespace(); |
| - _parseToken(':'); |
| + _expect(_CHAR_COLON); |
| var value = _parseValue(); |
| map[key] = value; |
| _whitespace(); |
| - _tryParseToken(','); |
| + _optional(_CHAR_COMMA); |
| _whitespace(); |
| } |
| return map; |
| @@ -120,18 +117,56 @@ class JsonParser { |
| List _parseList() { |
| List list = new List(); |
| _whitespace(); |
| - while (!_tryParseToken(']')) { |
| + while (!_optional(_CHAR_SQUARE_CLOSE)) { |
| list.add(_parseValue()); |
| _whitespace(); |
| - _tryParseToken(','); |
| + _optional(_CHAR_COMMA); |
|
Anders Johnsen
2015/03/05 07:35:31
Would this allow
[1 2 3 4]
?
(ditto above)
zerny-google
2015/03/05 08:12:31
Yes. Fortunately it does parse well-formed json :-
|
| _whitespace(); |
| } |
| 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; |
| } |