// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. // @dart = 2.7 /// Decode a lax JSON encoded text, that is, JSON with Dart-like comments and /// trailing commas. /// /// This is used together with `load.dart` and `save.dart` to allow for an easy /// editing of a human-readable source-map file. library lazon; decode(String text) { return new _Decoder(text)._decode(); } class _Decoder { static final int LF = '\n'.codeUnits.single; static final int CR = '\r'.codeUnits.single; static final int BACKSPACE = '\b'.codeUnits.single; static final int FORMFEED = '\f'.codeUnits.single; static final int TAB = '\t'.codeUnits.single; static final int SPACE = ' '.codeUnits.single; static final int SLASH = '/'.codeUnits.single; static final int STAR = '*'.codeUnits.single; static final int QUOTE = '"'.codeUnits.single; static final int BACKSLASH = '\\'.codeUnits.single; static final int COMMA = ','.codeUnits.single; static final int COLON = ':'.codeUnits.single; static final int LEFT_BRACE = '{'.codeUnits.single; static final int RIGHT_BRACE = '}'.codeUnits.single; static final int LEFT_BRACKET = '['.codeUnits.single; static final int RIGHT_BRACKET = ']'.codeUnits.single; static final int T = 't'.codeUnits.single; static final List TRUE = 'true'.codeUnits; static final int F = 'f'.codeUnits.single; static final List FALSE = 'false'.codeUnits; static final int N = 'n'.codeUnits.single; static final List NULL = 'null'.codeUnits; static final int B = 'b'.codeUnits.single; static final int R = 'r'.codeUnits.single; static final int U = 'u'.codeUnits.single; final List codeUnits; final int length; int position = 0; _Decoder(String text) : codeUnits = text.codeUnits, length = text.length; _decode() { var result = _decodeValue(); _trimWhitespace(); if (position != codeUnits.length) { throw new ArgumentError("Unexpected trailing text: " "'${new String.fromCharCodes(codeUnits, position)}'."); } return result; } _decodeValue() { var result; _trimWhitespace(); if (position < codeUnits.length) { int codeUnit = codeUnits[position]; if (codeUnit == QUOTE) { result = _decodeString(); } else if (codeUnit == LEFT_BRACE) { result = _decodeMap(); } else if (codeUnit == LEFT_BRACKET) { result = _decodeList(); } else if (codeUnit == T) { result = _decodeTrue(); } else if (codeUnit == F) { result = _decodeFalse(); } else if (codeUnit == N) { result = _decodeNull(); } else { result = _decodeNumber(); } } else { throw new ArgumentError("No value found in text: " "'${new String.fromCharCodes(codeUnits, 0)}'."); } return result; } void _trimWhitespace() { OUTER: while (position < codeUnits.length) { int codeUnit = codeUnits[position]; if (codeUnit == SLASH) { if (position + 1 < codeUnits.length) { int nextCodeUnit = codeUnits[position + 1]; if (nextCodeUnit == SLASH) { position += 2; while (position < codeUnits.length) { codeUnit = codeUnits[position]; if (codeUnit == LF || codeUnit == CR) { continue OUTER; } position++; } } else if (nextCodeUnit == STAR) { position += 2; while (position < codeUnits.length) { codeUnit = codeUnits[position]; if (codeUnit == STAR && position + 1 < codeUnits.length && codeUnits[position + 1] == SLASH) { position += 2; continue OUTER; } position++; } } } break; } else if (codeUnit == LF || codeUnit == CR || codeUnit == TAB || codeUnit == SPACE) { position++; } else { break; } } } String _decodeString() { int codeUnit = codeUnits[position]; if (codeUnit != QUOTE) { _fail(); } position++; List charCodes = []; while (position < length) { codeUnit = codeUnits[position]; if (codeUnit == QUOTE) { break; } else if (codeUnit == BACKSLASH) { if (position + 1 >= length) { _fail(); } codeUnit = codeUnits[++position]; if (codeUnit == B) { codeUnit = BACKSPACE; } else if (codeUnit == F) { codeUnit = FORMFEED; } else if (codeUnit == N) { codeUnit = LF; } else if (codeUnit == R) { codeUnit = CR; } else if (codeUnit == T) { codeUnit = TAB; } else if (codeUnit == U) { throw new UnsupportedError('unicode escapes'); } else if (codeUnit == QUOTE || codeUnit == SLASH || codeUnit == BACKSLASH) { // Ok. } else { _fail(); } } charCodes.add(codeUnit); position++; } if (codeUnit != QUOTE) { _fail(); } position++; return new String.fromCharCodes(charCodes); } Map _decodeMap() { int codeUnit = codeUnits[position]; if (codeUnit != LEFT_BRACE) { _fail(); } position++; _trimWhitespace(); Map result = {}; while (position < length) { codeUnit = codeUnits[position]; if (codeUnit == RIGHT_BRACE) { break; } String key = _decodeString(); _trimWhitespace(); if (position < length) { codeUnit = codeUnits[position]; if (codeUnit == COLON) { position++; _trimWhitespace(); } else { _fail(); } } else { _fail(); } var value = _decodeValue(); result[key] = value; _trimWhitespace(); if (position < length) { codeUnit = codeUnits[position]; if (codeUnit == COMMA) { position++; _trimWhitespace(); continue; } else { break; } } } if (codeUnit != RIGHT_BRACE) { _fail(); } position++; return result; } List _decodeList() { int codeUnit = codeUnits[position]; if (codeUnit != LEFT_BRACKET) { _fail(); } position++; _trimWhitespace(); List result = []; while (position < length) { codeUnit = codeUnits[position]; if (codeUnit == RIGHT_BRACKET) { break; } result.add(_decodeValue()); _trimWhitespace(); if (position < length) { codeUnit = codeUnits[position]; if (codeUnit == COMMA) { position++; _trimWhitespace(); continue; } else { break; } } } if (codeUnit != RIGHT_BRACKET) { _fail(); } position++; return result; } bool _decodeTrue() { match(TRUE); return true; } bool _decodeFalse() { match(FALSE); return false; } Null _decodeNull() { match(NULL); return null; } void match(List codes) { if (position + codes.length > length) { _fail(); } for (int i = 0; i < codes.length; i++) { if (codes[i] != codeUnits[position + i]) { _fail(); } } position += codes.length; } num _decodeNumber() { throw new UnsupportedError('_decodeNumber'); } void _fail() { throw new ArgumentError("Unexpected value: " "'${new String.fromCharCodes(codeUnits, position)}'."); } }