20faaa5d94
Change-Id: Ib18f554c1076f27a3f7c0df5a36410da41a1f467 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/148784 Reviewed-by: Paul Berry <paulberry@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
297 lines
7.6 KiB
Dart
297 lines
7.6 KiB
Dart
// 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<int> TRUE = 'true'.codeUnits;
|
|
static final int F = 'f'.codeUnits.single;
|
|
static final List<int> FALSE = 'false'.codeUnits;
|
|
static final int N = 'n'.codeUnits.single;
|
|
static final List<int> 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<int> 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<int> charCodes = <int>[];
|
|
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<int> 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)}'.");
|
|
}
|
|
}
|