4d07f9ace4
Change 64060a8ddf accidentally lost `?? []` for non-optional fields when parsing JSON. This change restores that (in `generate_dart_common.dart`), along with some minor tweaks to get the codegen to work on Windows.
For reasons I don't understand, the generated files were formatted differently to how the formatter formats them on my machine today, which unfortunately makes the diff larger than the intended change.
Change-Id: Ibadaa3ec4c6af4e636c1e5ffd2c7e792bc1e8a14
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/497600
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
197 lines
4.0 KiB
Dart
197 lines
4.0 KiB
Dart
// Copyright (c) 2015, 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.
|
|
|
|
library parser;
|
|
|
|
class Token {
|
|
static final RegExp _alpha = RegExp(r'^[0-9a-zA-Z_\-@]+$');
|
|
|
|
final String? text;
|
|
Token? next;
|
|
|
|
Token(this.text);
|
|
|
|
bool get eof => text == null;
|
|
|
|
bool get isName {
|
|
if (text == null || text!.isEmpty) return false;
|
|
return _alpha.hasMatch(text!);
|
|
}
|
|
|
|
bool get isComment => text != null && text!.startsWith('//');
|
|
|
|
@override
|
|
String toString() => text == null ? 'EOF' : text!;
|
|
}
|
|
|
|
class Tokenizer {
|
|
static final alphaNum =
|
|
'@abcdefghijklmnopqrstuvwxyz-_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
static final whitespace = ' \n\t\r';
|
|
|
|
String text;
|
|
Token? _head;
|
|
Token? _last;
|
|
|
|
final _newLineRegExp = RegExp('\r?\n');
|
|
|
|
Tokenizer(this.text);
|
|
|
|
Token? tokenize() {
|
|
_emit(null);
|
|
|
|
for (int i = 0; i < text.length; i++) {
|
|
String c = text[i];
|
|
|
|
if (whitespace.contains(c)) {
|
|
// skip
|
|
} else if (c == '/' && _peek(i) == '/') {
|
|
int index = text.indexOf(_newLineRegExp, i);
|
|
if (index == -1) index = text.length;
|
|
_emit(text.substring(i, index));
|
|
i = index;
|
|
} else if (alphaNum.contains(c)) {
|
|
int start = i;
|
|
|
|
while (alphaNum.contains(_peek(i))) {
|
|
i++;
|
|
}
|
|
|
|
_emit(text.substring(start, i + 1));
|
|
} else {
|
|
_emit(c);
|
|
}
|
|
}
|
|
|
|
_emit(null);
|
|
|
|
_head = _head!.next;
|
|
|
|
return _head;
|
|
}
|
|
|
|
void _emit(String? value) {
|
|
Token token = Token(value);
|
|
_head ??= token;
|
|
if (_last != null) _last!.next = token;
|
|
_last = token;
|
|
}
|
|
|
|
String _peek(int i) {
|
|
i += 1;
|
|
return i < text.length ? text[i] : String.fromCharCodes([0]);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
StringBuffer buf = StringBuffer();
|
|
|
|
Token t = _head!;
|
|
|
|
buf.write('[$t]\n');
|
|
|
|
while (!t.eof) {
|
|
t = t.next!;
|
|
buf.write('[$t]\n');
|
|
}
|
|
|
|
return buf.toString().trim();
|
|
}
|
|
}
|
|
|
|
abstract class Parser {
|
|
final Token? startToken;
|
|
|
|
Token? current;
|
|
|
|
Parser(this.startToken);
|
|
|
|
Token expect(String text) {
|
|
Token t = advance()!;
|
|
if (text != t.text) fail('expected $text, got $t');
|
|
return t;
|
|
}
|
|
|
|
bool consume(String text) {
|
|
if (peek()!.text == text) {
|
|
advance();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Token? peek() => current == null
|
|
? startToken
|
|
: current!.eof
|
|
? current
|
|
: current!.next;
|
|
|
|
Token expectName() {
|
|
Token t = advance()!;
|
|
if (!t.isName) fail('expected name token, got $t');
|
|
return t;
|
|
}
|
|
|
|
Token? advance() {
|
|
if (current == null) {
|
|
current = startToken;
|
|
} else if (!current!.eof) {
|
|
current = current!.next;
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
String? collectComments() {
|
|
StringBuffer buf = StringBuffer();
|
|
|
|
while (peek()!.isComment) {
|
|
Token t = advance()!;
|
|
String str = t.text!.substring(2);
|
|
|
|
if (str.startsWith(' ')) str = str.substring(1);
|
|
|
|
if (str.startsWith(' ')) {
|
|
buf.write('\n - ${str.substring(2)}');
|
|
} else if (str.isEmpty) {
|
|
buf.write('\n\n');
|
|
} else {
|
|
buf.write('$str ');
|
|
}
|
|
}
|
|
|
|
if (buf.isEmpty) return null;
|
|
return buf
|
|
.toString()
|
|
.split('\n')
|
|
.map((line) => line.trimRight())
|
|
.join('\n')
|
|
.trim();
|
|
}
|
|
|
|
String? consumeString() {
|
|
StringBuffer buf = StringBuffer();
|
|
String startQuotation = advance()!.text!;
|
|
if (startQuotation != '"' && startQuotation != "'") {
|
|
return null;
|
|
}
|
|
while (peek()!.text != startQuotation) {
|
|
Token t = advance()!;
|
|
if (t.text == null) {
|
|
throw FormatException('Reached EOF');
|
|
}
|
|
buf.write('${t.text} ');
|
|
}
|
|
advance();
|
|
return buf.toString().trim();
|
|
}
|
|
|
|
void validate(bool result, String message) {
|
|
if (!result) throw 'expected $message';
|
|
}
|
|
|
|
void fail(String message) => throw message;
|
|
}
|