59b252fa0a
Change-Id: I884190dffbca5eb5181f21bc12972b375e31de65 Reviewed-on: https://dart-review.googlesource.com/c/88724 Commit-Queue: Dmitry Stefantsov <dmitryas@google.com> Reviewed-by: Kevin Millikin <kmillikin@google.com>
416 lines
12 KiB
Dart
416 lines
12 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 'text_serializer.dart' show Tagger;
|
|
|
|
class DeserializationEnvironment<T extends Node> {
|
|
final Map<String, T> locals = <String, T>{};
|
|
|
|
final DeserializationEnvironment<T> parent;
|
|
|
|
final Set<String> usedNames = new Set<String>();
|
|
|
|
DeserializationEnvironment(this.parent) {
|
|
if (parent != null) {
|
|
usedNames.addAll(parent.usedNames);
|
|
}
|
|
}
|
|
|
|
T lookup(String name) => locals[name] ?? parent?.lookup(name);
|
|
|
|
T add(String name, T node) {
|
|
if (usedNames.contains(name)) {
|
|
throw StateError("name '$name' is already declared in this scope");
|
|
}
|
|
usedNames.add(name);
|
|
return locals[name] = node;
|
|
}
|
|
}
|
|
|
|
class SerializationEnvironment<T extends Node> {
|
|
final Map<T, String> locals = new Map<T, String>.identity();
|
|
|
|
int nameCount;
|
|
|
|
final SerializationEnvironment<T> parent;
|
|
|
|
static const String separator = "^";
|
|
|
|
static final int codeOfZero = "0".codeUnitAt(0);
|
|
|
|
static final int codeOfNine = "9".codeUnitAt(0);
|
|
|
|
SerializationEnvironment(this.parent) {
|
|
nameCount = (parent?.nameCount ?? 0);
|
|
}
|
|
|
|
String lookup(T node) => locals[node] ?? parent?.lookup(node);
|
|
|
|
String add(T node, String name) {
|
|
int prefixLength = name.length - 1;
|
|
bool isOnlyDigits = true;
|
|
while (prefixLength >= 0 && name[prefixLength] != separator) {
|
|
int code = name.codeUnitAt(prefixLength);
|
|
isOnlyDigits = isOnlyDigits && (codeOfZero <= code && code <= codeOfNine);
|
|
--prefixLength;
|
|
}
|
|
if (prefixLength < 0 || !isOnlyDigits) {
|
|
prefixLength = name.length;
|
|
}
|
|
String prefix = name.substring(0, prefixLength);
|
|
return locals[node] = "$prefix$separator${nameCount++}";
|
|
}
|
|
}
|
|
|
|
abstract class TextSerializer<T> {
|
|
const TextSerializer();
|
|
|
|
T readFrom(Iterator<Object> stream, DeserializationEnvironment environment);
|
|
void writeTo(
|
|
StringBuffer buffer, T object, SerializationEnvironment environment);
|
|
|
|
/// 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, DeserializationEnvironment _) {}
|
|
|
|
void writeTo(StringBuffer buffer, void ignored, SerializationEnvironment _) {}
|
|
|
|
bool get isEmpty => true;
|
|
}
|
|
|
|
class DartString extends TextSerializer<String> {
|
|
const DartString();
|
|
|
|
String readFrom(Iterator<Object> stream, DeserializationEnvironment _) {
|
|
if (stream.current is! String) {
|
|
throw StateError("expected an atom, found a list");
|
|
}
|
|
String result = json.decode(stream.current);
|
|
stream.moveNext();
|
|
return result;
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, String object, SerializationEnvironment _) {
|
|
buffer.write(json.encode(object));
|
|
}
|
|
}
|
|
|
|
class DartInt extends TextSerializer<int> {
|
|
const DartInt();
|
|
|
|
int readFrom(Iterator<Object> stream, DeserializationEnvironment _) {
|
|
if (stream.current is! String) {
|
|
throw StateError("expected an atom, found a list");
|
|
}
|
|
int result = int.parse(stream.current);
|
|
stream.moveNext();
|
|
return result;
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, int object, SerializationEnvironment _) {
|
|
buffer.write(object);
|
|
}
|
|
}
|
|
|
|
class DartDouble extends TextSerializer<double> {
|
|
const DartDouble();
|
|
|
|
double readFrom(Iterator<Object> stream, DeserializationEnvironment _) {
|
|
if (stream.current is! String) {
|
|
throw StateError("expected an atom, found a list");
|
|
}
|
|
double result = double.parse(stream.current);
|
|
stream.moveNext();
|
|
return result;
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, double object, SerializationEnvironment _) {
|
|
buffer.write(object);
|
|
}
|
|
}
|
|
|
|
class DartBool extends TextSerializer<bool> {
|
|
const DartBool();
|
|
|
|
bool readFrom(Iterator<Object> stream, DeserializationEnvironment _) {
|
|
if (stream.current is! String) {
|
|
throw StateError("expected an atom, found a list");
|
|
}
|
|
bool result;
|
|
if (stream.current == "true") {
|
|
result = true;
|
|
} else if (stream.current == "false") {
|
|
result = false;
|
|
} else {
|
|
throw StateError("expected 'true' or 'false', found '${stream.current}'");
|
|
}
|
|
stream.moveNext();
|
|
return result;
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, bool object, SerializationEnvironment _) {
|
|
buffer.write(object ? 'true' : 'false');
|
|
}
|
|
}
|
|
|
|
// == 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 Node> extends TextSerializer<T> {
|
|
final Tagger<T> tagger;
|
|
final List<String> tags;
|
|
final List<TextSerializer<T>> serializers;
|
|
|
|
Case(this.tagger, this.tags, this.serializers);
|
|
|
|
Case.uninitialized(this.tagger)
|
|
: tags = [],
|
|
serializers = [];
|
|
|
|
T readFrom(Iterator<Object> stream, DeserializationEnvironment environment) {
|
|
if (stream.current is! Iterator) {
|
|
throw StateError("expected list, found atom");
|
|
}
|
|
Iterator nested = stream.current;
|
|
nested.moveNext();
|
|
if (nested.current is! String) {
|
|
throw StateError("expected atom, found list");
|
|
}
|
|
String tag = nested.current;
|
|
for (int i = 0; i < tags.length; ++i) {
|
|
if (tags[i] == tag) {
|
|
nested.moveNext();
|
|
T result = serializers[i].readFrom(nested, environment);
|
|
if (nested.moveNext()) {
|
|
throw StateError("extra cruft in tagged '${tag}'");
|
|
}
|
|
stream.moveNext();
|
|
return result;
|
|
}
|
|
}
|
|
throw StateError("unrecognized tag '${tag}'");
|
|
}
|
|
|
|
void writeTo(
|
|
StringBuffer buffer, T object, SerializationEnvironment environment) {
|
|
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, environment);
|
|
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;
|
|
|
|
Wrapped(this.unwrap, this.wrap, this.contents);
|
|
|
|
K readFrom(Iterator<Object> stream, DeserializationEnvironment environment) {
|
|
return wrap(contents.readFrom(stream, environment));
|
|
}
|
|
|
|
void writeTo(
|
|
StringBuffer buffer, K object, SerializationEnvironment environment) {
|
|
contents.writeTo(buffer, unwrap(object), environment);
|
|
}
|
|
|
|
bool get isEmpty => contents.isEmpty;
|
|
}
|
|
|
|
class ScopedReference<T extends Node> extends TextSerializer<T> {
|
|
final DartString stringSerializer = const DartString();
|
|
|
|
const ScopedReference();
|
|
|
|
T readFrom(Iterator<Object> stream, DeserializationEnvironment environment) {
|
|
return environment.lookup(stringSerializer.readFrom(stream, null));
|
|
}
|
|
|
|
void writeTo(
|
|
StringBuffer buffer, T object, SerializationEnvironment environment) {
|
|
stringSerializer.writeTo(buffer, 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, DeserializationEnvironment environment) {
|
|
return new Tuple2(first.readFrom(stream, environment),
|
|
second.readFrom(stream, environment));
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, Tuple2<T1, T2> object,
|
|
SerializationEnvironment environment) {
|
|
first.writeTo(buffer, object.first, environment);
|
|
buffer.write(' ');
|
|
second.writeTo(buffer, object.second, environment);
|
|
}
|
|
}
|
|
|
|
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, DeserializationEnvironment environment) {
|
|
return new Tuple3(
|
|
first.readFrom(stream, environment),
|
|
second.readFrom(stream, environment),
|
|
third.readFrom(stream, environment));
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, Tuple3<T1, T2, T3> object,
|
|
SerializationEnvironment environment) {
|
|
first.writeTo(buffer, object.first, environment);
|
|
buffer.write(' ');
|
|
second.writeTo(buffer, object.second, environment);
|
|
buffer.write(' ');
|
|
third.writeTo(buffer, object.third, environment);
|
|
}
|
|
}
|
|
|
|
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, DeserializationEnvironment environment) {
|
|
return new Tuple4(
|
|
first.readFrom(stream, environment),
|
|
second.readFrom(stream, environment),
|
|
third.readFrom(stream, environment),
|
|
fourth.readFrom(stream, environment));
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, Tuple4<T1, T2, T3, T4> object,
|
|
SerializationEnvironment environment) {
|
|
first.writeTo(buffer, object.first, environment);
|
|
buffer.write(' ');
|
|
second.writeTo(buffer, object.second, environment);
|
|
buffer.write(' ');
|
|
third.writeTo(buffer, object.third, environment);
|
|
buffer.write(' ');
|
|
fourth.writeTo(buffer, object.fourth, environment);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// 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, DeserializationEnvironment environment) {
|
|
if (stream.current is! Iterator) {
|
|
throw StateError("expected a list, found an atom");
|
|
}
|
|
Iterator<Object> list = stream.current;
|
|
list.moveNext();
|
|
List<T> result = [];
|
|
while (list.current != null) {
|
|
result.add(elements.readFrom(list, environment));
|
|
}
|
|
stream.moveNext();
|
|
return result;
|
|
}
|
|
|
|
void writeTo(StringBuffer buffer, List<T> object,
|
|
SerializationEnvironment environment) {
|
|
buffer.write('(');
|
|
for (int i = 0; i < object.length; ++i) {
|
|
if (i != 0) buffer.write(' ');
|
|
elements.writeTo(buffer, object[i], environment);
|
|
}
|
|
buffer.write(')');
|
|
}
|
|
}
|
|
|
|
class Optional<T> extends TextSerializer<T> {
|
|
final TextSerializer<T> contents;
|
|
|
|
const Optional(this.contents);
|
|
|
|
T readFrom(Iterator<Object> stream, DeserializationEnvironment environment) {
|
|
if (stream.current == '_') {
|
|
stream.moveNext();
|
|
return null;
|
|
}
|
|
return contents.readFrom(stream, environment);
|
|
}
|
|
|
|
void writeTo(
|
|
StringBuffer buffer, T object, SerializationEnvironment environment) {
|
|
if (object == null) {
|
|
buffer.write('_');
|
|
} else {
|
|
contents.writeTo(buffer, object, environment);
|
|
}
|
|
}
|
|
}
|