286702df6b
Change-Id: If426fc15f7bce54b51eaac3354061684cd5b0e17 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/207129 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
876 lines
27 KiB
Dart
876 lines
27 KiB
Dart
// Copyright (c) 2018, 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 kernel.serializer_combinators;
|
|
|
|
import 'dart:convert' show json;
|
|
|
|
import '../ast.dart' show Node;
|
|
import '../canonical_name.dart' show CanonicalName;
|
|
import 'text_serializer.dart' show Tagger;
|
|
|
|
class DeserializationEnvironment<T extends Node> {
|
|
final DeserializationEnvironment<T>? parent;
|
|
|
|
final Map<String, T> locals = <String, T>{};
|
|
|
|
final Map<String, T> binders = <String, T>{};
|
|
|
|
final Map<T, String> distinctNames = new Map<T, String>.identity();
|
|
|
|
DeserializationEnvironment(this.parent);
|
|
|
|
T lookup(String name) =>
|
|
locals[name] ??
|
|
parent?.lookup(name) ??
|
|
(throw StateError("No local '${name}' found in this scope."));
|
|
|
|
T addBinder(T node, String distinctName) {
|
|
if (lookupDistinctName(node) != null) {
|
|
throw StateError(
|
|
"Name '${distinctName}' is already declared in this scope.");
|
|
}
|
|
distinctNames[node] = distinctName;
|
|
return binders[distinctName] = node;
|
|
}
|
|
|
|
// TODO(dmitryas): Consider combining with [addBinder] into a single method.
|
|
void extend() {
|
|
locals.addAll(binders);
|
|
binders.clear();
|
|
}
|
|
|
|
String? lookupDistinctName(T object) {
|
|
return distinctNames[object] ?? parent?.lookupDistinctName(object);
|
|
}
|
|
}
|
|
|
|
class SerializationEnvironment<T extends Node> {
|
|
final SerializationEnvironment<T>? parent;
|
|
|
|
final Map<T, String> locals = new Map<T, String>.identity();
|
|
|
|
final Map<T, String> binders = new Map<T, String>.identity();
|
|
|
|
int nameCount;
|
|
|
|
Map<T, String> distinctNames = new Map<T, String>.identity();
|
|
|
|
SerializationEnvironment(this.parent) : nameCount = parent?.nameCount ?? 0;
|
|
|
|
String lookup(T node) =>
|
|
locals[node] ??
|
|
parent?.lookup(node) ??
|
|
(throw StateError("No name for ${node} found in this scope."));
|
|
|
|
String addBinder(T node, {String? nameClue}) {
|
|
final String separator = "^";
|
|
final int codeOfZero = "0".codeUnitAt(0);
|
|
final int codeOfNine = "9".codeUnitAt(0);
|
|
|
|
String prefix;
|
|
if (nameClue != null) {
|
|
int prefixLength = nameClue.length - 1;
|
|
bool isOnlyDigits = true;
|
|
while (prefixLength >= 0 && nameClue[prefixLength] != separator) {
|
|
int code = nameClue.codeUnitAt(prefixLength);
|
|
isOnlyDigits =
|
|
isOnlyDigits && (codeOfZero <= code && code <= codeOfNine);
|
|
--prefixLength;
|
|
}
|
|
if (prefixLength < 0 || !isOnlyDigits) {
|
|
prefixLength = nameClue.length;
|
|
}
|
|
prefix = nameClue.substring(0, prefixLength);
|
|
} else {
|
|
prefix = "ID";
|
|
}
|
|
String distinctName = "$prefix$separator${nameCount++}";
|
|
// The following checks for an internal error, not an error caused by the
|
|
// user. So, an assert is used instead of an exception.
|
|
assert(
|
|
lookupDistinctName(node) == null,
|
|
"Can't assign distinct name '${distinctName}' "
|
|
"to an object of kind '${node.runtimeType}': "
|
|
"it's already known by name '${lookupDistinctName(node)}'.");
|
|
distinctNames[node] = distinctName;
|
|
return binders[node] = distinctName;
|
|
}
|
|
|
|
// TODO(dmitryas): Consider combining with [addBinder] into a single method.
|
|
void extend() {
|
|
locals.addAll(binders);
|
|
binders.clear();
|
|
}
|
|
|
|
String? lookupDistinctName(T object) {
|
|
return distinctNames[object] ?? parent?.lookupDistinctName(object);
|
|
}
|
|
}
|
|
|
|
class DeserializationState {
|
|
final DeserializationEnvironment environment;
|
|
final CanonicalName nameRoot;
|
|
|
|
DeserializationState(this.environment, this.nameRoot);
|
|
}
|
|
|
|
class SerializationState {
|
|
final SerializationEnvironment environment;
|
|
|
|
SerializationState(this.environment);
|
|
}
|
|
|
|
abstract class TextSerializer<T> {
|
|
const TextSerializer();
|
|
|
|
T readFrom(Iterator<Object?> stream, DeserializationState? state);
|
|
void writeTo(StringBuffer buffer, T object, SerializationState? state);
|
|
|
|
/// True if this serializer/deserializer writes/reads nothing. This is true
|
|
/// for the serializer [Nothing] and also some serializers derived from it.
|
|
bool get isEmpty => false;
|
|
}
|
|
|
|
class Nothing extends TextSerializer<void> {
|
|
const Nothing();
|
|
|
|
void readFrom(Iterator<Object?> stream, DeserializationState? _) {}
|
|
|
|
void writeTo(StringBuffer buffer, void ignored, SerializationState? _) {}
|
|
|
|
bool get isEmpty => true;
|
|
}
|
|
|
|
class DartString extends TextSerializer<String> {
|
|
const DartString();
|
|
|
|
String readFrom(Iterator<Object?> stream, DeserializationState? _) {
|
|
Object? current = stream.current;
|
|
if (current is! String) {
|
|
throw StateError("Expected an atom, found a list: '${current}'.");
|
|
}
|
|
String result = json.decode(current);
|
|
stream.moveNext();
|
|
return result;
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, String object, SerializationState? _) {
|
|
buffer.write(json.encode(object));
|
|
}
|
|
}
|
|
|
|
class DartInt extends TextSerializer<int> {
|
|
const DartInt();
|
|
|
|
int readFrom(Iterator<Object?> stream, DeserializationState? _) {
|
|
Object? current = stream.current;
|
|
if (current is! String) {
|
|
throw StateError("Expected an atom, found a list: '${current}'.");
|
|
}
|
|
int result = int.parse(current);
|
|
stream.moveNext();
|
|
return result;
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, int object, SerializationState? _) {
|
|
buffer.write(object);
|
|
}
|
|
}
|
|
|
|
class DartDouble extends TextSerializer<double> {
|
|
const DartDouble();
|
|
|
|
double readFrom(Iterator<Object?> stream, DeserializationState? _) {
|
|
Object? current = stream.current;
|
|
if (current is! String) {
|
|
throw StateError("Expected an atom, found a list: '${current}'.");
|
|
}
|
|
double result = double.parse(current);
|
|
stream.moveNext();
|
|
return result;
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, double object, SerializationState? _) {
|
|
buffer.write(object);
|
|
}
|
|
}
|
|
|
|
class DartBool extends TextSerializer<bool> {
|
|
const DartBool();
|
|
|
|
bool readFrom(Iterator<Object?> stream, DeserializationState? _) {
|
|
Object? current = stream.current;
|
|
if (current is! String) {
|
|
throw StateError("Expected an atom, found a list: '${current}'.");
|
|
}
|
|
bool result;
|
|
if (current == "true") {
|
|
result = true;
|
|
} else if (current == "false") {
|
|
result = false;
|
|
} else {
|
|
throw StateError("Expected 'true' or 'false', found '${current}'");
|
|
}
|
|
stream.moveNext();
|
|
return result;
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, bool object, SerializationState? _) {
|
|
buffer.write(object ? 'true' : 'false');
|
|
}
|
|
}
|
|
|
|
class UriSerializer extends TextSerializer<Uri> {
|
|
const UriSerializer();
|
|
|
|
Uri readFrom(Iterator<Object?> stream, DeserializationState? state) {
|
|
String uriAsString = const DartString().readFrom(stream, state);
|
|
return Uri.parse(uriAsString);
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, Uri object, SerializationState? state) {
|
|
const DartString().writeTo(buffer, object.toString(), state);
|
|
}
|
|
}
|
|
|
|
// == Serializers for tagged (disjoint) unions.
|
|
//
|
|
// They require a function mapping serializables to a tag string. This is
|
|
// implemented by Tagger visitors.
|
|
// A tagged union of serializer/deserializers.
|
|
class Case<T> extends TextSerializer<T> {
|
|
final Tagger<T> tagger;
|
|
final List<String> _tags;
|
|
final List<TextSerializer<T>> _serializers;
|
|
|
|
Case(this.tagger, Map<String, TextSerializer<T>> tagsAndSerializers)
|
|
: _tags = tagsAndSerializers.keys.toList(),
|
|
_serializers = tagsAndSerializers.values.toList();
|
|
|
|
Case.uninitialized(this.tagger)
|
|
: _tags = [],
|
|
_serializers = [];
|
|
|
|
void registerTags(Map<String, TextSerializer<T>> tagsAndSerializers) {
|
|
_tags.addAll(tagsAndSerializers.keys);
|
|
_serializers.addAll(tagsAndSerializers.values);
|
|
}
|
|
|
|
T readFrom(Iterator<Object?> stream, DeserializationState? state) {
|
|
Object? iterator = stream.current;
|
|
if (iterator is! Iterator<Object?>) {
|
|
throw StateError("Expected list, found atom: '${iterator}'.");
|
|
}
|
|
iterator.moveNext();
|
|
Object? element = iterator.current;
|
|
if (element is! String) {
|
|
throw StateError("Expected atom, found list: '${element}'.");
|
|
}
|
|
String tag = element;
|
|
for (int i = 0; i < _tags.length; ++i) {
|
|
if (_tags[i] == tag) {
|
|
iterator.moveNext();
|
|
T result = _serializers[i].readFrom(iterator, state);
|
|
if (iterator.moveNext()) {
|
|
throw StateError(
|
|
"Extra data in tagged '${tag}': '${iterator.current}'.");
|
|
}
|
|
stream.moveNext();
|
|
return result;
|
|
}
|
|
}
|
|
throw StateError("Unrecognized tag '${tag}'.");
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, T object, SerializationState? state) {
|
|
String tag = tagger.tag(object);
|
|
for (int i = 0; i < _tags.length; ++i) {
|
|
if (_tags[i] == tag) {
|
|
buffer.write("(${tag}");
|
|
if (!_serializers[i].isEmpty) {
|
|
buffer.write(" ");
|
|
}
|
|
_serializers[i].writeTo(buffer, object, state);
|
|
buffer.write(")");
|
|
return;
|
|
}
|
|
}
|
|
throw StateError("Unrecognized tag '${tag}'.");
|
|
}
|
|
}
|
|
|
|
// A serializer/deserializer that unwraps/wraps nodes before serialization and
|
|
// after deserialization.
|
|
class Wrapped<S, K> extends TextSerializer<K> {
|
|
final S Function(K) unwrap;
|
|
final K Function(S) wrap;
|
|
final TextSerializer<S> contents;
|
|
|
|
const Wrapped(this.unwrap, this.wrap, this.contents);
|
|
|
|
K readFrom(Iterator<Object?> stream, DeserializationState? state) {
|
|
return wrap(contents.readFrom(stream, state));
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, K object, SerializationState? state) {
|
|
contents.writeTo(buffer, unwrap(object), state);
|
|
}
|
|
|
|
bool get isEmpty => contents.isEmpty;
|
|
}
|
|
|
|
class ScopedUse<T extends Node> extends TextSerializer<T> {
|
|
final DartString stringSerializer = const DartString();
|
|
|
|
const ScopedUse();
|
|
|
|
T readFrom(Iterator<Object?> stream, DeserializationState? state) {
|
|
if (state == null) {
|
|
throw StateError(
|
|
"No deserialization state provided for ${runtimeType}.readFrom.");
|
|
}
|
|
return state.environment.lookup(stringSerializer.readFrom(stream, null))
|
|
as T;
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, T object, SerializationState? state) {
|
|
if (state == null) {
|
|
throw StateError(
|
|
"No serialization state provided for ${runtimeType}.writeTo.");
|
|
}
|
|
stringSerializer.writeTo(buffer, state.environment.lookup(object), null);
|
|
}
|
|
}
|
|
|
|
// A serializer/deserializer for pairs.
|
|
class Tuple2Serializer<T1, T2> extends TextSerializer<Tuple2<T1, T2>> {
|
|
final TextSerializer<T1> first;
|
|
final TextSerializer<T2> second;
|
|
|
|
const Tuple2Serializer(this.first, this.second);
|
|
|
|
Tuple2<T1, T2> readFrom(
|
|
Iterator<Object?> stream, DeserializationState? state) {
|
|
return new Tuple2(
|
|
first.readFrom(stream, state), second.readFrom(stream, state));
|
|
}
|
|
|
|
void writeTo(
|
|
StringBuffer buffer, Tuple2<T1, T2> object, SerializationState? state) {
|
|
first.writeTo(buffer, object.first, state);
|
|
if (!second.isEmpty) buffer.write(' ');
|
|
second.writeTo(buffer, object.second, state);
|
|
}
|
|
}
|
|
|
|
class Tuple2<T1, T2> {
|
|
final T1 first;
|
|
final T2 second;
|
|
|
|
const Tuple2(this.first, this.second);
|
|
}
|
|
|
|
class Tuple3Serializer<T1, T2, T3> extends TextSerializer<Tuple3<T1, T2, T3>> {
|
|
final TextSerializer<T1> first;
|
|
final TextSerializer<T2> second;
|
|
final TextSerializer<T3> third;
|
|
|
|
const Tuple3Serializer(this.first, this.second, this.third);
|
|
|
|
Tuple3<T1, T2, T3> readFrom(
|
|
Iterator<Object?> stream, DeserializationState? state) {
|
|
return new Tuple3(first.readFrom(stream, state),
|
|
second.readFrom(stream, state), third.readFrom(stream, state));
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, Tuple3<T1, T2, T3> object,
|
|
SerializationState? state) {
|
|
first.writeTo(buffer, object.first, state);
|
|
if (!second.isEmpty) buffer.write(' ');
|
|
second.writeTo(buffer, object.second, state);
|
|
if (!third.isEmpty) buffer.write(' ');
|
|
third.writeTo(buffer, object.third, state);
|
|
}
|
|
}
|
|
|
|
class Tuple3<T1, T2, T3> {
|
|
final T1 first;
|
|
final T2 second;
|
|
final T3 third;
|
|
|
|
const Tuple3(this.first, this.second, this.third);
|
|
}
|
|
|
|
class Tuple4Serializer<T1, T2, T3, T4>
|
|
extends TextSerializer<Tuple4<T1, T2, T3, T4>> {
|
|
final TextSerializer<T1> first;
|
|
final TextSerializer<T2> second;
|
|
final TextSerializer<T3> third;
|
|
final TextSerializer<T4> fourth;
|
|
|
|
const Tuple4Serializer(this.first, this.second, this.third, this.fourth);
|
|
|
|
Tuple4<T1, T2, T3, T4> readFrom(
|
|
Iterator<Object?> stream, DeserializationState? state) {
|
|
return new Tuple4(
|
|
first.readFrom(stream, state),
|
|
second.readFrom(stream, state),
|
|
third.readFrom(stream, state),
|
|
fourth.readFrom(stream, state));
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, Tuple4<T1, T2, T3, T4> object,
|
|
SerializationState? state) {
|
|
first.writeTo(buffer, object.first, state);
|
|
if (!second.isEmpty) buffer.write(' ');
|
|
second.writeTo(buffer, object.second, state);
|
|
if (!third.isEmpty) buffer.write(' ');
|
|
third.writeTo(buffer, object.third, state);
|
|
if (!fourth.isEmpty) buffer.write(' ');
|
|
fourth.writeTo(buffer, object.fourth, state);
|
|
}
|
|
}
|
|
|
|
class Tuple4<T1, T2, T3, T4> {
|
|
final T1 first;
|
|
final T2 second;
|
|
final T3 third;
|
|
final T4 fourth;
|
|
|
|
const Tuple4(this.first, this.second, this.third, this.fourth);
|
|
}
|
|
|
|
class Tuple5Serializer<T1, T2, T3, T4, T5>
|
|
extends TextSerializer<Tuple5<T1, T2, T3, T4, T5>> {
|
|
final TextSerializer<T1> first;
|
|
final TextSerializer<T2> second;
|
|
final TextSerializer<T3> third;
|
|
final TextSerializer<T4> fourth;
|
|
final TextSerializer<T5> fifth;
|
|
|
|
const Tuple5Serializer(
|
|
this.first, this.second, this.third, this.fourth, this.fifth);
|
|
|
|
Tuple5<T1, T2, T3, T4, T5> readFrom(
|
|
Iterator<Object?> stream, DeserializationState? state) {
|
|
return new Tuple5(
|
|
first.readFrom(stream, state),
|
|
second.readFrom(stream, state),
|
|
third.readFrom(stream, state),
|
|
fourth.readFrom(stream, state),
|
|
fifth.readFrom(stream, state));
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, Tuple5<T1, T2, T3, T4, T5> object,
|
|
SerializationState? state) {
|
|
first.writeTo(buffer, object.first, state);
|
|
if (!second.isEmpty) buffer.write(' ');
|
|
second.writeTo(buffer, object.second, state);
|
|
if (!third.isEmpty) buffer.write(' ');
|
|
third.writeTo(buffer, object.third, state);
|
|
if (!fourth.isEmpty) buffer.write(' ');
|
|
fourth.writeTo(buffer, object.fourth, state);
|
|
if (!fifth.isEmpty) buffer.write(' ');
|
|
fifth.writeTo(buffer, object.fifth, state);
|
|
}
|
|
}
|
|
|
|
class Tuple5<T1, T2, T3, T4, T5> {
|
|
final T1 first;
|
|
final T2 second;
|
|
final T3 third;
|
|
final T4 fourth;
|
|
final T5 fifth;
|
|
|
|
const Tuple5(this.first, this.second, this.third, this.fourth, this.fifth);
|
|
}
|
|
|
|
class Tuple6Serializer<T1, T2, T3, T4, T5, T6>
|
|
extends TextSerializer<Tuple6<T1, T2, T3, T4, T5, T6>> {
|
|
final TextSerializer<T1> first;
|
|
final TextSerializer<T2> second;
|
|
final TextSerializer<T3> third;
|
|
final TextSerializer<T4> fourth;
|
|
final TextSerializer<T5> fifth;
|
|
final TextSerializer<T6> sixth;
|
|
|
|
const Tuple6Serializer(
|
|
this.first, this.second, this.third, this.fourth, this.fifth, this.sixth);
|
|
|
|
Tuple6<T1, T2, T3, T4, T5, T6> readFrom(
|
|
Iterator<Object?> stream, DeserializationState? state) {
|
|
return new Tuple6(
|
|
first.readFrom(stream, state),
|
|
second.readFrom(stream, state),
|
|
third.readFrom(stream, state),
|
|
fourth.readFrom(stream, state),
|
|
fifth.readFrom(stream, state),
|
|
sixth.readFrom(stream, state));
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, Tuple6<T1, T2, T3, T4, T5, T6> object,
|
|
SerializationState? state) {
|
|
first.writeTo(buffer, object.first, state);
|
|
if (!second.isEmpty) buffer.write(' ');
|
|
second.writeTo(buffer, object.second, state);
|
|
if (!third.isEmpty) buffer.write(' ');
|
|
third.writeTo(buffer, object.third, state);
|
|
if (!fourth.isEmpty) buffer.write(' ');
|
|
fourth.writeTo(buffer, object.fourth, state);
|
|
if (!fifth.isEmpty) buffer.write(' ');
|
|
fifth.writeTo(buffer, object.fifth, state);
|
|
if (!sixth.isEmpty) buffer.write(' ');
|
|
sixth.writeTo(buffer, object.sixth, state);
|
|
}
|
|
}
|
|
|
|
class Tuple6<T1, T2, T3, T4, T5, T6> {
|
|
final T1 first;
|
|
final T2 second;
|
|
final T3 third;
|
|
final T4 fourth;
|
|
final T5 fifth;
|
|
final T6 sixth;
|
|
|
|
const Tuple6(
|
|
this.first, this.second, this.third, this.fourth, this.fifth, this.sixth);
|
|
}
|
|
|
|
class Tuple7Serializer<T1, T2, T3, T4, T5, T6, T7>
|
|
extends TextSerializer<Tuple7<T1, T2, T3, T4, T5, T6, T7>> {
|
|
final TextSerializer<T1> first;
|
|
final TextSerializer<T2> second;
|
|
final TextSerializer<T3> third;
|
|
final TextSerializer<T4> fourth;
|
|
final TextSerializer<T5> fifth;
|
|
final TextSerializer<T6> sixth;
|
|
final TextSerializer<T7> seventh;
|
|
|
|
const Tuple7Serializer(this.first, this.second, this.third, this.fourth,
|
|
this.fifth, this.sixth, this.seventh);
|
|
|
|
Tuple7<T1, T2, T3, T4, T5, T6, T7> readFrom(
|
|
Iterator<Object?> stream, DeserializationState? state) {
|
|
return new Tuple7(
|
|
first.readFrom(stream, state),
|
|
second.readFrom(stream, state),
|
|
third.readFrom(stream, state),
|
|
fourth.readFrom(stream, state),
|
|
fifth.readFrom(stream, state),
|
|
sixth.readFrom(stream, state),
|
|
seventh.readFrom(stream, state));
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, Tuple7<T1, T2, T3, T4, T5, T6, T7> object,
|
|
SerializationState? state) {
|
|
first.writeTo(buffer, object.first, state);
|
|
if (!second.isEmpty) buffer.write(' ');
|
|
second.writeTo(buffer, object.second, state);
|
|
if (!third.isEmpty) buffer.write(' ');
|
|
third.writeTo(buffer, object.third, state);
|
|
if (!fourth.isEmpty) buffer.write(' ');
|
|
fourth.writeTo(buffer, object.fourth, state);
|
|
if (!fifth.isEmpty) buffer.write(' ');
|
|
fifth.writeTo(buffer, object.fifth, state);
|
|
if (!sixth.isEmpty) buffer.write(' ');
|
|
sixth.writeTo(buffer, object.sixth, state);
|
|
if (!seventh.isEmpty) buffer.write(' ');
|
|
seventh.writeTo(buffer, object.seventh, state);
|
|
}
|
|
}
|
|
|
|
class Tuple7<T1, T2, T3, T4, T5, T6, T7> {
|
|
final T1 first;
|
|
final T2 second;
|
|
final T3 third;
|
|
final T4 fourth;
|
|
final T5 fifth;
|
|
final T6 sixth;
|
|
final T7 seventh;
|
|
|
|
const Tuple7(this.first, this.second, this.third, this.fourth, this.fifth,
|
|
this.sixth, this.seventh);
|
|
}
|
|
|
|
class Tuple8Serializer<T1, T2, T3, T4, T5, T6, T7, T8>
|
|
extends TextSerializer<Tuple8<T1, T2, T3, T4, T5, T6, T7, T8>> {
|
|
final TextSerializer<T1> first;
|
|
final TextSerializer<T2> second;
|
|
final TextSerializer<T3> third;
|
|
final TextSerializer<T4> fourth;
|
|
final TextSerializer<T5> fifth;
|
|
final TextSerializer<T6> sixth;
|
|
final TextSerializer<T7> seventh;
|
|
final TextSerializer<T8> eighth;
|
|
|
|
const Tuple8Serializer(this.first, this.second, this.third, this.fourth,
|
|
this.fifth, this.sixth, this.seventh, this.eighth);
|
|
|
|
Tuple8<T1, T2, T3, T4, T5, T6, T7, T8> readFrom(
|
|
Iterator<Object?> stream, DeserializationState? state) {
|
|
return new Tuple8(
|
|
first.readFrom(stream, state),
|
|
second.readFrom(stream, state),
|
|
third.readFrom(stream, state),
|
|
fourth.readFrom(stream, state),
|
|
fifth.readFrom(stream, state),
|
|
sixth.readFrom(stream, state),
|
|
seventh.readFrom(stream, state),
|
|
eighth.readFrom(stream, state));
|
|
}
|
|
|
|
void writeTo(
|
|
StringBuffer buffer,
|
|
Tuple8<T1, T2, T3, T4, T5, T6, T7, T8> object,
|
|
SerializationState? state) {
|
|
first.writeTo(buffer, object.first, state);
|
|
if (!second.isEmpty) buffer.write(' ');
|
|
second.writeTo(buffer, object.second, state);
|
|
if (!third.isEmpty) buffer.write(' ');
|
|
third.writeTo(buffer, object.third, state);
|
|
if (!fourth.isEmpty) buffer.write(' ');
|
|
fourth.writeTo(buffer, object.fourth, state);
|
|
if (!fifth.isEmpty) buffer.write(' ');
|
|
fifth.writeTo(buffer, object.fifth, state);
|
|
if (!sixth.isEmpty) buffer.write(' ');
|
|
sixth.writeTo(buffer, object.sixth, state);
|
|
if (!seventh.isEmpty) buffer.write(' ');
|
|
seventh.writeTo(buffer, object.seventh, state);
|
|
if (!eighth.isEmpty) buffer.write(' ');
|
|
eighth.writeTo(buffer, object.eighth, state);
|
|
}
|
|
}
|
|
|
|
class Tuple8<T1, T2, T3, T4, T5, T6, T7, T8> {
|
|
final T1 first;
|
|
final T2 second;
|
|
final T3 third;
|
|
final T4 fourth;
|
|
final T5 fifth;
|
|
final T6 sixth;
|
|
final T7 seventh;
|
|
final T8 eighth;
|
|
|
|
const Tuple8(this.first, this.second, this.third, this.fourth, this.fifth,
|
|
this.sixth, this.seventh, this.eighth);
|
|
}
|
|
|
|
// A serializer/deserializer for lists.
|
|
class ListSerializer<T> extends TextSerializer<List<T>> {
|
|
final TextSerializer<T> elements;
|
|
|
|
const ListSerializer(this.elements);
|
|
|
|
List<T> readFrom(Iterator<Object?> stream, DeserializationState? state) {
|
|
Object? iterator = stream.current;
|
|
if (iterator is! Iterator<Object?>) {
|
|
throw StateError("Expected a list, found an atom: '${iterator}'.");
|
|
}
|
|
iterator.moveNext();
|
|
List<T> result = [];
|
|
while (iterator.current != null) {
|
|
result.add(elements.readFrom(iterator, state));
|
|
}
|
|
stream.moveNext();
|
|
return result;
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, List<T> object, SerializationState? state) {
|
|
buffer.write('(');
|
|
for (int i = 0; i < object.length; ++i) {
|
|
if (i != 0) buffer.write(' ');
|
|
elements.writeTo(buffer, object[i], state);
|
|
}
|
|
buffer.write(')');
|
|
}
|
|
}
|
|
|
|
class Optional<T> extends TextSerializer<T?> {
|
|
final TextSerializer<T> contents;
|
|
|
|
const Optional(this.contents);
|
|
|
|
T? readFrom(Iterator<Object?> stream, DeserializationState? state) {
|
|
if (stream.current == '_') {
|
|
stream.moveNext();
|
|
return null;
|
|
}
|
|
return contents.readFrom(stream, state);
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, T? object, SerializationState? state) {
|
|
if (object == null) {
|
|
buffer.write('_');
|
|
} else {
|
|
contents.writeTo(buffer, object, state);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Introduces a binder to the environment.
|
|
///
|
|
/// Serializes an object and uses it as a binder for the name that is retrieved
|
|
/// from the object using [nameGetter] and (temporarily) modified using
|
|
/// [nameSetter]. The binder is added to the enclosing environment.
|
|
class Binder<T extends Node> extends TextSerializer<Tuple2<String?, T>> {
|
|
final Tuple2Serializer<String, T> namedContents;
|
|
|
|
Binder(TextSerializer<T> contents)
|
|
: namedContents = new Tuple2Serializer(const DartString(), contents);
|
|
|
|
Tuple2<String, T> readFrom(
|
|
Iterator<Object?> stream, DeserializationState? state) {
|
|
if (state == null) {
|
|
throw StateError(
|
|
"No deserialization state provided for ${runtimeType}.readFrom.");
|
|
}
|
|
Tuple2<String, T> namedObject = namedContents.readFrom(stream, state);
|
|
String name = namedObject.first;
|
|
T object = namedObject.second;
|
|
state.environment.addBinder(object, name);
|
|
return new Tuple2(name, object);
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, Tuple2<String?, T> namedObject,
|
|
SerializationState? state) {
|
|
if (state == null) {
|
|
throw StateError(
|
|
"No serialization state provided for ${runtimeType}.writeTo.");
|
|
}
|
|
String? nameClue = namedObject.first;
|
|
T object = namedObject.second;
|
|
String distinctName =
|
|
state.environment.addBinder(object, nameClue: nameClue);
|
|
namedContents.writeTo(buffer, new Tuple2(distinctName, object), state);
|
|
}
|
|
}
|
|
|
|
/// Binds binders from one term in the other.
|
|
///
|
|
/// Serializes a [Tuple2] of [pattern] and [term], closing [term] over the
|
|
/// binders found in [pattern]. The binders aren't added to the enclosing
|
|
/// environment.
|
|
class Bind<P, T> extends TextSerializer<Tuple2<P, T>> {
|
|
final TextSerializer<P> pattern;
|
|
final TextSerializer<T> term;
|
|
|
|
const Bind(this.pattern, this.term);
|
|
|
|
Tuple2<P, T> readFrom(Iterator<Object?> stream, DeserializationState? state) {
|
|
if (state == null) {
|
|
throw StateError(
|
|
"No deserialization state provided for ${runtimeType}.readFrom.");
|
|
}
|
|
DeserializationState bindingState = new DeserializationState(
|
|
new DeserializationEnvironment(state.environment), state.nameRoot);
|
|
P first = pattern.readFrom(stream, bindingState);
|
|
bindingState.environment.extend();
|
|
T second = term.readFrom(stream, bindingState);
|
|
return new Tuple2(first, second);
|
|
}
|
|
|
|
void writeTo(
|
|
StringBuffer buffer, Tuple2<P, T> tuple, SerializationState? state) {
|
|
if (state == null) {
|
|
throw StateError(
|
|
"No serialization state provided for ${runtimeType}.writeTo.");
|
|
}
|
|
SerializationState bindingState =
|
|
new SerializationState(new SerializationEnvironment(state.environment));
|
|
pattern.writeTo(buffer, tuple.first, bindingState);
|
|
bindingState.environment.extend();
|
|
buffer.write(' ');
|
|
term.writeTo(buffer, tuple.second, bindingState);
|
|
}
|
|
}
|
|
|
|
/// Nested binding pattern that also binds binders from one term in the other.
|
|
///
|
|
/// Serializes a [Tuple2] of [pattern1] and [pattern2], closing [pattern2] over
|
|
/// the binders found in [pattern1]. The binders from both [pattern1] and
|
|
/// [pattern2] are added to the enclosing environment.
|
|
class Rebind<P, T> extends TextSerializer<Tuple2<P, T>> {
|
|
final TextSerializer<P> pattern1;
|
|
final TextSerializer<T> pattern2;
|
|
|
|
const Rebind(this.pattern1, this.pattern2);
|
|
|
|
Tuple2<P, T> readFrom(Iterator<Object?> stream, DeserializationState? state) {
|
|
if (state == null) {
|
|
throw StateError(
|
|
"No deserialization state provided for ${runtimeType}.readFrom.");
|
|
}
|
|
P first = pattern1.readFrom(stream, state);
|
|
DeserializationState closedState = new DeserializationState(
|
|
new DeserializationEnvironment(state.environment)
|
|
..binders.addAll(state.environment.binders)
|
|
..extend(),
|
|
state.nameRoot);
|
|
T second = pattern2.readFrom(stream, closedState);
|
|
state.environment.binders.addAll(closedState.environment.binders);
|
|
return new Tuple2(first, second);
|
|
}
|
|
|
|
void writeTo(
|
|
StringBuffer buffer, Tuple2<P, T> tuple, SerializationState? state) {
|
|
if (state == null) {
|
|
throw StateError(
|
|
"No serialization state provided for ${runtimeType}.writeTo.");
|
|
}
|
|
pattern1.writeTo(buffer, tuple.first, state);
|
|
SerializationState closedState =
|
|
new SerializationState(new SerializationEnvironment(state.environment)
|
|
..binders.addAll(state.environment.binders)
|
|
..extend());
|
|
buffer.write(' ');
|
|
pattern2.writeTo(buffer, tuple.second, closedState);
|
|
state.environment.binders.addAll(closedState.environment.binders);
|
|
}
|
|
}
|
|
|
|
class Zip<T, T1, T2> extends TextSerializer<List<T>> {
|
|
final TextSerializer<Tuple2<List<T1>, List<T2>>> lists;
|
|
final T Function(T1, T2) zip;
|
|
final Tuple2<T1, T2> Function(T) unzip;
|
|
|
|
const Zip(this.lists, this.zip, this.unzip);
|
|
|
|
List<T> readFrom(Iterator<Object?> stream, DeserializationState? state) {
|
|
Tuple2<List<T1>, List<T2>> toZip = lists.readFrom(stream, state);
|
|
List<T1> firsts = toZip.first;
|
|
List<T> zipped;
|
|
if (firsts.isEmpty) {
|
|
zipped = <T>[];
|
|
} else {
|
|
List<T2> seconds = toZip.second;
|
|
zipped =
|
|
new List<T>.filled(toZip.first.length, zip(firsts[0], seconds[0]));
|
|
for (int i = 1; i < zipped.length; ++i) {
|
|
zipped[i] = zip(firsts[i], seconds[i]);
|
|
}
|
|
}
|
|
return zipped;
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, List<T> zipped, SerializationState? state) {
|
|
List<T1> firsts;
|
|
List<T2> seconds;
|
|
if (zipped.isEmpty) {
|
|
firsts = <T1>[];
|
|
seconds = <T2>[];
|
|
} else {
|
|
Tuple2<T1, T2> initial = unzip(zipped[0]);
|
|
firsts = new List<T1>.filled(zipped.length, initial.first);
|
|
seconds = new List<T2>.filled(zipped.length, initial.second);
|
|
for (int i = 1; i < zipped.length; ++i) {
|
|
Tuple2<T1, T2> tuple = unzip(zipped[i]);
|
|
firsts[i] = tuple.first;
|
|
seconds[i] = tuple.second;
|
|
}
|
|
}
|
|
lists.writeTo(buffer, new Tuple2(firsts, seconds), state);
|
|
}
|
|
}
|