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() {