Split the text serializer into libraries
Move the combinators and the reader into their own libraries.
Change-Id: I85042a3f319c0fc945d658dd23a3959c74b7cbca
Reviewed-on: https://dart-review.googlesource.com/c/87660
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Kevin Millikin <kmillikin@google.com>
diff --git a/pkg/kernel/lib/text/serializer_combinators.dart b/pkg/kernel/lib/text/serializer_combinators.dart
new file mode 100644
index 0000000..193874d
--- /dev/null
+++ b/pkg/kernel/lib/text/serializer_combinators.dart
@@ -0,0 +1,159 @@
+// 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 '../ast.dart' show Expression;
+import 'text_serializer.dart' show ExpressionTagger;
+
+abstract class TextSerializer<T> {
+ const TextSerializer();
+
+ T readFrom(Iterator<Object> stream);
+ void writeTo(StringBuffer buffer, T object);
+
+ /// 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) {}
+
+ void writeTo(StringBuffer buffer, void ignored) {}
+
+ bool get isEmpty => true;
+}
+
+// == Serializer/deserializers for basic Dart types
+class DartInt extends TextSerializer<int> {
+ const DartInt();
+
+ int readFrom(Iterator<Object> stream) {
+ 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) {
+ buffer.write(object);
+ }
+}
+
+class DartDouble extends TextSerializer<double> {
+ const DartDouble();
+
+ double readFrom(Iterator<Object> stream) {
+ 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) {
+ buffer.write(object);
+ }
+}
+
+class DartBool extends TextSerializer<bool> {
+ const DartBool();
+
+ bool readFrom(Iterator<Object> stream) {
+ 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) {
+ 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 Expression> extends TextSerializer<T> {
+ final List<String> tags;
+ final List<TextSerializer<T>> serializers;
+
+ const Case(this.tags, this.serializers);
+
+ T readFrom(Iterator<Object> stream) {
+ 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);
+ if (stream.moveNext()) {
+ throw StateError("extra cruft in tagged '${tag}'");
+ }
+ return result;
+ }
+ }
+ throw StateError("unrecognized tag '${tag}'");
+ }
+
+ void writeTo(StringBuffer buffer, T object) {
+ String tag = object.accept(const ExpressionTagger());
+ 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);
+ 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) {
+ return wrap(contents.readFrom(stream));
+ }
+
+ void writeTo(StringBuffer buffer, K object) {
+ contents.writeTo(buffer, unwrap(object));
+ }
+
+ bool get isEmpty => contents.isEmpty;
+}
diff --git a/pkg/kernel/lib/text/text_reader.dart b/pkg/kernel/lib/text/text_reader.dart
new file mode 100644
index 0000000..5798096
--- /dev/null
+++ b/pkg/kernel/lib/text/text_reader.dart
@@ -0,0 +1,75 @@
+// 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.text_reader;
+
+// S-expressions
+//
+// An S-expression is an atom or an S-list, an atom is a string that does not
+// contain the delimiters '(', ')', or ' ', and an S-list is a space delimited
+// sequence of S-expressions enclosed in parentheses:
+//
+// <S-expression> ::= <Atom>
+// | <S-list>
+// <S-list> ::= '(' ')'
+// | '(' <S-expression> {' ' <S-expression>}* ')'
+//
+// We use an iterator to read S-expressions. The iterator produces a stream
+// of atoms (strings) and nested iterators (S-lists).
+class TextIterator implements Iterator<Object /* String | TextIterator */ > {
+ static int space = ' '.codeUnitAt(0);
+ static int lparen = '('.codeUnitAt(0);
+ static int rparen = ')'.codeUnitAt(0);
+
+ final String input;
+ int index;
+
+ TextIterator(this.input, this.index);
+
+ // Consume spaces.
+ void skipWhitespace() {
+ while (index < input.length && input.codeUnitAt(index) == space) {
+ ++index;
+ }
+ }
+
+ // Consume the rest of a nested S-expression and the closing delimiter.
+ void skipToEndOfNested() {
+ if (current is TextIterator) {
+ TextIterator it = current;
+ while (it.moveNext());
+ index = it.index + 1;
+ }
+ }
+
+ void skipToEndOfAtom() {
+ do {
+ if (index >= input.length) return;
+ int codeUnit = input.codeUnitAt(index);
+ if (codeUnit == space || codeUnit == rparen) return;
+ ++index;
+ } while (true);
+ }
+
+ @override
+ Object current = null;
+
+ @override
+ bool moveNext() {
+ skipToEndOfNested();
+ skipWhitespace();
+ if (index >= input.length || input.codeUnitAt(index) == rparen) {
+ current = null;
+ return false;
+ }
+ if (input.codeUnitAt(index) == lparen) {
+ current = new TextIterator(input, index + 1);
+ return true;
+ }
+ int start = index;
+ skipToEndOfAtom();
+ current = input.substring(start, index);
+ return true;
+ }
+}
diff --git a/pkg/kernel/lib/text/text_serializer.dart b/pkg/kernel/lib/text/text_serializer.dart
index 6b0d487..a28a0ca 100644
--- a/pkg/kernel/lib/text/text_serializer.dart
+++ b/pkg/kernel/lib/text/text_serializer.dart
@@ -5,93 +5,11 @@
library kernel.text_serializer;
import '../ast.dart';
+
+import 'serializer_combinators.dart';
+
import '../visitor.dart' show ExpressionVisitor;
-// ==== Serialize/deserialize combinators ====
-abstract class TextSerializer<T> {
- const TextSerializer();
-
- T readFrom(Iterator<Object> stream);
- void writeTo(StringBuffer buffer, T object);
-
- /// 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) {}
-
- void writeTo(StringBuffer buffer, void ignored) {}
-
- bool get isEmpty => true;
-}
-
-// == Serializer/deserializers for basic Dart types
-class DartInt extends TextSerializer<int> {
- const DartInt();
-
- int readFrom(Iterator<Object> stream) {
- 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) {
- buffer.write(object);
- }
-}
-
-class DartDouble extends TextSerializer<double> {
- const DartDouble();
-
- double readFrom(Iterator<Object> stream) {
- 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) {
- buffer.write(object);
- }
-}
-
-class DartBool extends TextSerializer<bool> {
- const DartBool();
-
- bool readFrom(Iterator<Object> stream) {
- 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) {
- 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.
class ExpressionTagger extends ExpressionVisitor<String> {
const ExpressionTagger();
@@ -101,143 +19,6 @@
String visitNullLiteral(NullLiteral _) => "null";
}
-// A tagged union of serializer/deserializers.
-class Case<T extends Expression> extends TextSerializer<T> {
- final List<String> tags;
- final List<TextSerializer<T>> serializers;
-
- const Case(this.tags, this.serializers);
-
- T readFrom(Iterator<Object> stream) {
- 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);
- if (stream.moveNext()) {
- throw StateError("extra cruft in tagged '${tag}'");
- }
- return result;
- }
- }
- throw StateError("unrecognized tag '${tag}'");
- }
-
- void writeTo(StringBuffer buffer, T object) {
- String tag = object.accept(const ExpressionTagger());
- 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);
- 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) {
- return wrap(contents.readFrom(stream));
- }
-
- void writeTo(StringBuffer buffer, K object) {
- contents.writeTo(buffer, unwrap(object));
- }
-
- bool get isEmpty => contents.isEmpty;
-}
-
-// S-expressions
-//
-// An S-expression is an atom or an S-list, an atom is a string that does not
-// contain the delimiters '(', ')', or ' ', and an S-list is a space delimited
-// sequence of S-expressions enclosed in parentheses:
-//
-// <S-expression> ::= <Atom>
-// | <S-list>
-// <S-list> ::= '(' ')'
-// | '(' <S-expression> {' ' <S-expression>}* ')'
-//
-// We use an iterator to read S-expressions. The iterator produces a stream
-// of atoms (strings) and nested iterators (S-lists).
-class TextIterator implements Iterator<Object /* String | TextIterator */ > {
- static int space = ' '.codeUnitAt(0);
- static int lparen = '('.codeUnitAt(0);
- static int rparen = ')'.codeUnitAt(0);
-
- final String input;
- int index;
-
- TextIterator(this.input, this.index);
-
- // Consume spaces.
- void skipWhitespace() {
- while (index < input.length && input.codeUnitAt(index) == space) {
- ++index;
- }
- }
-
- // Consume the rest of a nested S-expression and the closing delimiter.
- void skipToEndOfNested() {
- if (current is TextIterator) {
- TextIterator it = current;
- while (it.moveNext());
- index = it.index + 1;
- }
- }
-
- void skipToEndOfAtom() {
- do {
- if (index >= input.length) return;
- int codeUnit = input.codeUnitAt(index);
- if (codeUnit == space || codeUnit == rparen) return;
- ++index;
- } while (true);
- }
-
- @override
- Object current = null;
-
- @override
- bool moveNext() {
- skipToEndOfNested();
- skipWhitespace();
- if (index >= input.length || input.codeUnitAt(index) == rparen) {
- current = null;
- return false;
- }
- if (input.codeUnitAt(index) == lparen) {
- current = new TextIterator(input, index + 1);
- return true;
- }
- int start = index;
- skipToEndOfAtom();
- current = input.substring(start, index);
- return true;
- }
-}
-
// ==== Serializers for BasicLiterals
const TextSerializer<BasicLiteral> basicLiteralSerializer = Case([
"int",
diff --git a/pkg/kernel/test/text_serializer_test.dart b/pkg/kernel/test/text_serializer_test.dart
index dbb5ece..699a8a2 100644
--- a/pkg/kernel/test/text_serializer_test.dart
+++ b/pkg/kernel/test/text_serializer_test.dart
@@ -4,6 +4,7 @@
library kernel.text_serializer_test;
import 'package:kernel/ast.dart';
+import 'package:kernel/text/text_reader.dart';
import 'package:kernel/text/text_serializer.dart';
void main() {