runtimeTemplate top-level constant
String
const runtimeTemplate
Implementation
const runtimeTemplate = r'''
class ErrorExpectedChar extends ParseError {
final int char;
const ErrorExpectedChar(this.char);
@override
String getMessage({
required Object? input,
required int offset,
}) {
final hexValue = char.toRadixString(16);
final value = ParseError.escape(char);
return 'Unexpected character $value (0x$hexValue)';
}
}
class ErrorExpectedEof extends ParseError {
const ErrorExpectedEof();
@override
String getMessage({
required Object? input,
required int offset,
}) {
return 'Expected end of file';
}
}
class ErrorExpectedInt extends ParseError {
final int size;
final int value;
const ErrorExpectedInt(this.size, this.value);
@override
String getMessage({
required Object? input,
required int offset,
}) {
var string = value.toRadixString(16);
if (const [8, 16, 24, 32, 40, 48, 56, 64].contains(size)) {
string = string.padLeft(size >> 2, '0');
}
if (value >= 0 && value <= 0x10ffff) {
string = '$string (${ParseError.escape(value)})';
}
return 'Expected 0x$string';
}
}
class ErrorExpectedTags extends ParseError {
final List<String> tags;
const ErrorExpectedTags(this.tags);
@override
String getMessage({
required Object? input,
required int offset,
}) {
final value = tags.map(ParseError.escape).join(', ');
return 'Expected $value';
}
}
class ErrorMessage extends ParseError {
@override
final int length;
final String message;
const ErrorMessage(this.length, this.message);
@override
String getMessage({
required Object? input,
required int offset,
}) {
return message;
}
}
class ErrorUnexpectedChar extends ParseError {
const ErrorUnexpectedChar();
@override
String getMessage({
required Object? input,
required int offset,
}) {
if (input is String) {
if (offset < input.length) {
final char = input.runeAt(offset);
final hexValue = char.toRadixString(16);
final value = ParseError.escape(char);
return 'Unexpected character $value (0x$hexValue)';
}
}
return 'Unexpected character';
}
}
class ErrorUnexpectedEof extends ParseError {
const ErrorUnexpectedEof();
@override
String getMessage({
required Object? input,
required int offset,
}) {
return 'Unexpected end of file';
}
}
class ErrorUnexpectedInput extends ParseError {
@override
final int length;
const ErrorUnexpectedInput(this.length);
@override
String getMessage({
required Object? input,
required int offset,
}) {
return 'Unexpected input';
}
}
class ErrorUnknown extends ParseError {
const ErrorUnknown();
@override
String getMessage({
required Object? input,
required int offset,
}) {
return 'Unknown error';
}
}
abstract class ParseError {
const ParseError();
int get length => 0;
String getMessage({
required Object? input,
required int offset,
});
static String errorMessage(
String input, int offset, List<ParseError> errors) {
int max(int x, int y) => x > y ? x : y;
int min(int x, int y) => x < y ? x : y;
final sb = StringBuffer();
final errorList = errors.toList();
if (offset >= input.length) {
errorList.add(const ErrorUnexpectedEof());
errorList.removeWhere((e) => e is ErrorUnexpectedChar);
}
final expectedTags = errorList.whereType<ErrorExpectedTags>().toList();
if (expectedTags.isNotEmpty) {
errorList.removeWhere((e) => e is ErrorExpectedTags);
final tags = <String>{};
for (final error in expectedTags) {
tags.addAll(error.tags);
}
final error = ErrorExpectedTags(tags.toList());
errorList.add(error);
}
final errorInfoList = errorList
.map((e) {
final offset2 = offset + e.length;
final start = min(offset2, offset);
final end = max(offset2, offset);
return (
start: start,
end: end,
message: e.getMessage(offset: start, input: input),
);
})
.toSet()
.toList();
for (var i = 0; i < errorInfoList.length; i++) {
int max(int x, int y) => x > y ? x : y;
int min(int x, int y) => x < y ? x : y;
if (sb.isNotEmpty) {
sb.writeln();
sb.writeln();
}
final errorInfo = errorInfoList[i];
final start = errorInfo.start;
final end = errorInfo.end;
final message = errorInfo.message;
var row = 1;
var lineStart = 0, next = 0, pos = 0;
while (pos < input.length) {
final c = input.codeUnitAt(pos++);
if (c == 0xa || c == 0xd) {
next = c == 0xa ? 0xd : 0xa;
if (pos < input.length && input.codeUnitAt(pos) == next) {
pos++;
}
if (pos - 1 >= start) {
break;
}
row++;
lineStart = pos;
}
}
final inputLen = input.length;
final lineLimit = min(80, inputLen);
final start2 = start;
final end2 = min(start2 + lineLimit, end);
final errorLen = end2 - start;
final extraLen = lineLimit - errorLen;
final rightLen = min(inputLen - end2, extraLen - (extraLen >> 1));
final leftLen = min(start, max(0, lineLimit - errorLen - rightLen));
final list = <int>[];
final iterator = RuneIterator.at(input, start2);
for (var i = 0; i < leftLen; i++) {
if (!iterator.movePrevious()) {
break;
}
list.add(iterator.current);
}
final column = start - lineStart + 1;
final left = String.fromCharCodes(list.reversed);
final end3 = min(inputLen, start2 + (lineLimit - leftLen));
final indicatorLen = max(1, errorLen);
final right = input.substring(start2, end3);
var text = left + right;
text = text.replaceAll('\n', ' ');
text = text.replaceAll('\r', ' ');
text = text.replaceAll('\t', ' ');
sb.writeln('line $row, column $column: $message');
sb.writeln(text);
sb.write(' ' * leftLen + '^' * indicatorLen);
}
return sb.toString();
}
static String escape(Object? value, [bool quote = true]) {
if (value is int) {
if (value >= 0 && value <= 0xd7ff ||
value >= 0xe000 && value <= 0x10ffff) {
value = String.fromCharCode(value);
} else {
return value.toString();
}
} else if (value is! String) {
return value.toString();
}
final map = {
'\b': '\\b',
'\f': '\\f',
'\n': '\\n',
'\r': '\\r',
'\t': '\\t',
'\v': '\\v',
};
var result = value.toString();
for (final key in map.keys) {
result = result.replaceAll(key, map[key]!);
}
if (quote) {
result = "'$result'";
}
return result;
}
}
class Result<T> {
final T value;
const Result(this.value);
@override
int get hashCode => value.hashCode;
@override
bool operator ==(other) => other is Result<T> && value == other.value;
@override
String toString() {
return '$value';
}
}
class State<T> {
Object? context;
final List<ParseError?> errors = List.filled(64, null, growable: false);
int errorCount = 0;
int failPos = 0;
final T input;
bool ok = false;
int pos = 0;
final List<
({
int last,
int index,
List<({int start, int end, bool ok, Object? result})?> list
})?> _cache = List.filled(64, null, growable: false);
State(this.input);
@pragma('vm:prefer-inline')
bool canHandleError(int failPos, int errorCount) => failPos == this.failPos
? errorCount < this.errorCount
: failPos < this.failPos;
void clearErrors(int failPos, int errorCount) {
if (this.failPos == failPos) {
this.errorCount = errorCount;
} else if (this.failPos > failPos) {
this.errorCount = 0;
}
}
@pragma('vm:prefer-inline')
void fail(ParseError error) {
ok = false;
if (pos >= failPos) {
if (failPos < pos) {
failPos = pos;
errorCount = 0;
}
if (errorCount < errors.length) {
errors[errorCount++] = error;
}
}
}
@pragma('vm:prefer-inline')
void failAll(List<ParseError> errors) {
ok = false;
if (pos >= failPos) {
if (failPos < pos) {
failPos = pos;
errorCount = 0;
}
for (var i = 0; i < errors.length; i++) {
if (errorCount < errors.length) {
this.errors[errorCount++] = errors[i];
}
}
}
}
@pragma('vm:prefer-inline')
void failAllAt(int offset, List<ParseError> errors) {
ok = false;
if (offset >= failPos) {
if (failPos < offset) {
failPos = offset;
errorCount = 0;
}
for (var i = 0; i < errors.length; i++) {
if (errorCount < errors.length) {
this.errors[errorCount++] = errors[i];
}
}
}
}
@pragma('vm:prefer-inline')
void failAt(int offset, ParseError error) {
ok = false;
if (offset >= failPos) {
if (failPos < offset) {
failPos = offset;
errorCount = 0;
}
if (errorCount < errors.length) {
errors[errorCount++] = error;
}
}
}
List<ParseError> getErrors() {
return List.generate(errorCount, (i) => errors[i]!);
}
@pragma('vm:prefer-inline')
void memoize(int id, int start, int end, bool ok, Object? result) {
if (id >= _cache.length) {
return;
}
var index = -1;
var record = _cache[id];
if (record == null) {
record =
(last: start, index: 0, list: List.filled(4, null, growable: false));
_cache[id] = record;
} else {
index = record.index;
}
if (record.last <= pos) {
final list = record.list;
index = index < list.length - 1 ? index + 1 : 0;
list[index] = (start: start, end: end, ok: ok, result: result);
_cache[id] = (last: pos, index: index, list: list);
}
}
@pragma('vm:prefer-inline')
({int start, int end, bool ok, Object? result})? memoized(int id, int pos) {
if (id >= _cache.length) {
return null;
}
final record = _cache[id];
if (record == null) {
return null;
}
final list = record.list;
var count = 0;
while (count < list.length) {
final value = list[count];
if (value == null) {
return null;
}
if (value.start == pos) {
return value;
}
count++;
}
return null;
}
@override
String toString() {
if (input is String) {
final s = input as String;
if (pos >= s.length) {
return '$pos:';
}
var length = s.length - pos;
length = length > 40 ? 40 : length;
final string = s.substring(pos, pos + length);
return '$pos:$string';
} else {
return super.toString();
}
}
}
extension on String {
@pragma('vm:prefer-inline')
// ignore: unused_element
int runeAt(int index) {
final w1 = codeUnitAt(index++);
if (w1 > 0xd7ff && w1 < 0xe000) {
if (index < length) {
final w2 = codeUnitAt(index);
if ((w2 & 0xfc00) == 0xdc00) {
return 0x10000 + ((w1 & 0x3ff) << 10) + (w2 & 0x3ff);
}
}
throw FormatException('Invalid UTF-16 character', this, index - 1);
}
return w1;
}
}
''';