Restore type parser from reify project

Change-Id: Ic347890a4146cadb3ad1b6049b562350903505e7
Reviewed-on: https://dart-review.googlesource.com/c/89660
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/test/fasta/types/type_parser.dart b/pkg/front_end/test/fasta/types/type_parser.dart
new file mode 100644
index 0000000..e7a1901
--- /dev/null
+++ b/pkg/front_end/test/fasta/types/type_parser.dart
@@ -0,0 +1,329 @@
+// Copyright (c) 2019, 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.
+
+import "package:front_end/src/fasta/scanner.dart" show scanString, Token;
+
+abstract class ParsedType {}
+
+class ParsedInterfaceType extends ParsedType {
+  final String name;
+
+  final List<ParsedType> arguments;
+
+  ParsedInterfaceType(this.name, this.arguments);
+
+  String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.write(name);
+    if (arguments.isNotEmpty) {
+      sb.write("<");
+      sb.writeAll(arguments, ", ");
+      sb.write(">");
+    }
+    return "$sb";
+  }
+}
+
+abstract class ParsedDeclaration extends ParsedType {
+  final String name;
+
+  ParsedDeclaration(this.name);
+}
+
+class ParsedClass extends ParsedDeclaration {
+  final List<ParsedTypeVariable> typeVariables;
+  final ParsedInterfaceType supertype;
+  final List<ParsedType> interfaces;
+  final ParsedFunctionType callableType;
+
+  ParsedClass(String name, this.typeVariables, this.supertype, this.interfaces,
+      this.callableType)
+      : super(name);
+
+  String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.write("class ");
+    sb.write(name);
+    if (typeVariables.isNotEmpty) {
+      sb.write("<");
+      sb.writeAll(typeVariables, ", ");
+      sb.write(">");
+    }
+    if (supertype != null) {
+      sb.write(" extends ");
+      sb.write(supertype);
+    }
+    if (interfaces.isNotEmpty) {
+      sb.write(" implements ");
+      sb.writeAll(interfaces, ", ");
+    }
+    if (callableType != null) {
+      sb.write("{\n  ");
+      sb.write(callableType);
+      sb.write("\n}");
+    } else {
+      sb.write(";");
+    }
+    return "$sb";
+  }
+}
+
+class ParsedTypedef extends ParsedDeclaration {
+  final List<ParsedTypeVariable> typeVariables;
+
+  final ParsedType type;
+
+  ParsedTypedef(String name, this.typeVariables, this.type) : super(name);
+
+  String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.write("typedef ");
+    sb.write(name);
+    if (typeVariables.isNotEmpty) {
+      sb.write("<");
+      sb.writeAll(typeVariables, ", ");
+      sb.write(">");
+    }
+    sb.write(" ");
+    sb.write(type);
+    return "$sb;";
+  }
+}
+
+class ParsedFunctionType extends ParsedType {
+  final ParsedType returnType;
+
+  final ParsedArguments arguments;
+
+  ParsedFunctionType(this.returnType, this.arguments);
+
+  String toString() {
+    return "$arguments -> $returnType";
+  }
+}
+
+class ParsedVoidType extends ParsedType {
+  String toString() => "void";
+}
+
+class ParsedTypeVariable extends ParsedType {
+  final String name;
+
+  final ParsedType bound;
+
+  ParsedTypeVariable(this.name, this.bound);
+
+  String toString() => name;
+}
+
+class ParsedArguments {
+  final List<ParsedType> required;
+  final List optional;
+  final bool optionalAreNamed;
+
+  ParsedArguments(this.required, this.optional, this.optionalAreNamed);
+
+  String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.write("(");
+    sb.writeAll(required, ", ");
+    if (optional.isNotEmpty) {
+      if (required.isNotEmpty) {
+        sb.write(", ");
+      }
+      if (optionalAreNamed) {
+        sb.write("{");
+        for (int i = 0; i < optional.length; i += 2) {
+          if (i != 0) {
+            sb.write(", ");
+          }
+          sb.write(optional[i]);
+          sb.write(" ");
+          sb.write(optional[i + 1]);
+        }
+        sb.write("}");
+      } else {
+        sb.write("[");
+        sb.writeAll(optional, ", ");
+        sb.write("]");
+      }
+    }
+    sb.write(")");
+    return "$sb";
+  }
+}
+
+class Parser {
+  Token peek;
+
+  String source;
+
+  Parser(this.peek, this.source);
+
+  bool get atEof => peek.isEof;
+
+  void advance() {
+    peek = peek.next;
+  }
+
+  void expect(String string) {
+    if (!identical(string, peek.stringValue)) {
+      String error = "${source.substring(0, peek.charOffset)}\n>>>"
+          "\n${source.substring(peek.charOffset)}";
+      throw "Expected `$string`, but got `${peek.lexeme}`\n$error";
+    }
+    advance();
+  }
+
+  bool optional(String value) {
+    return identical(value, peek.stringValue);
+  }
+
+  bool optionalAdvance(String value) {
+    if (optional(value)) {
+      advance();
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  ParsedType parseType() {
+    if (optional("class")) return parseClass();
+    if (optional("typedef")) return parseTypedef();
+    if (optional("(")) return parseFunctionType();
+    String name = parseName();
+    List<ParsedType> arguments = <ParsedType>[];
+    if (optional("<")) {
+      advance();
+      arguments.add(parseType());
+      while (optional(",")) {
+        advance();
+        arguments.add(parseType());
+      }
+      expect(">");
+    }
+    return new ParsedInterfaceType(name, arguments);
+  }
+
+  ParsedType parseReturnType() {
+    if (optionalAdvance("void")) return new ParsedVoidType();
+    return parseType();
+  }
+
+  ParsedFunctionType parseFunctionType() {
+    ParsedArguments arguments = parseArguments();
+    expect("-");
+    expect(">");
+    ParsedType returnType = parseReturnType();
+    return new ParsedFunctionType(returnType, arguments);
+  }
+
+  String parseName() {
+    if (!peek.isIdentifier) {
+      throw "Expected a name, but got `${peek.stringValue}`";
+    }
+    String result = peek.lexeme;
+    advance();
+    return result;
+  }
+
+  ParsedArguments parseArguments() {
+    List<ParsedType> requiredArguments = <ParsedType>[];
+    List optionalArguments = [];
+    bool optionalAreNamed = false;
+    expect("(");
+    do {
+      if (optional(")")) break;
+      if (optionalAdvance("[")) {
+        do {
+          optionalArguments.add(parseType());
+        } while (optionalAdvance(","));
+        expect("]");
+        break;
+      } else if (optionalAdvance("{")) {
+        optionalAreNamed = true;
+        do {
+          optionalArguments.add(parseType());
+          optionalArguments.add(parseName());
+        } while (optionalAdvance(","));
+        expect("}");
+        break;
+      } else {
+        requiredArguments.add(parseType());
+      }
+    } while (optionalAdvance(","));
+    expect(")");
+    return new ParsedArguments(
+        requiredArguments, optionalArguments, optionalAreNamed);
+  }
+
+  List<ParsedTypeVariable> parseTypeVariablesOpt() {
+    List<ParsedTypeVariable> typeVariables = <ParsedTypeVariable>[];
+    if (optionalAdvance("<")) {
+      do {
+        typeVariables.add(parseTypeVariable());
+      } while (optionalAdvance(","));
+      expect(">");
+    }
+    return typeVariables;
+  }
+
+  ParsedTypeVariable parseTypeVariable() {
+    String name = parseName();
+    ParsedType bound;
+    if (optionalAdvance("extends")) {
+      bound = parseType();
+    }
+    return new ParsedTypeVariable(name, bound);
+  }
+
+  ParsedClass parseClass() {
+    expect("class");
+    String name = parseName();
+    List<ParsedTypeVariable> typeVariables = parseTypeVariablesOpt();
+    ParsedType supertype;
+    if (optionalAdvance("extends")) {
+      supertype = parseType();
+    }
+    List<ParsedType> interfaces = <ParsedType>[];
+    if (optionalAdvance("implements")) {
+      do {
+        interfaces.add(parseType());
+      } while (optionalAdvance(","));
+    }
+    ParsedFunctionType callableType;
+    if (optionalAdvance("{")) {
+      callableType = parseFunctionType();
+      expect("}");
+    } else {
+      expect(";");
+    }
+    return new ParsedClass(
+        name, typeVariables, supertype, interfaces, callableType);
+  }
+
+  /// This parses a general typedef on this form:
+  ///
+  ///     typedef <name> <type-variables-opt> <type> ;
+  ///
+  /// This is unlike Dart typedef.
+  ParsedTypedef parseTypedef() {
+    expect("typedef");
+    String name = parseName();
+    List<ParsedTypeVariable> typeVariables = parseTypeVariablesOpt();
+    ParsedType type = parseType();
+    expect(";");
+    return new ParsedTypedef(name, typeVariables, type);
+  }
+}
+
+List<ParsedType> parse(String text) {
+  Parser parser = new Parser(scanString(text).tokens, text);
+  List<ParsedType> types = <ParsedType>[];
+  while (!parser.atEof) {
+    types.add(parser.parseType());
+  }
+  return types;
+}
diff --git a/pkg/front_end/test/fasta/types/type_parser_test.dart b/pkg/front_end/test/fasta/types/type_parser_test.dart
new file mode 100644
index 0000000..e24fa15
--- /dev/null
+++ b/pkg/front_end/test/fasta/types/type_parser_test.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2019, 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.
+
+import "package:expect/expect.dart" show Expect;
+
+import "type_parser.dart";
+
+testParse(String text) {
+  Expect.stringEquals(text.trim(), "${parse(text).join('\n')}");
+}
+
+main() {
+  testParse("""
+() -> void
+(int) -> dynamic
+([int]) -> int
+({double parameter}) -> int
+(num, [int]) -> int
+(num, {double parameter}) -> int
+class Object;
+class Comparable<T>;
+class num implements Comparable<num>;
+class int extends num;
+class double extends num;
+class Function;
+class Iterable<E>;
+class EfficientLength;
+class List<E> implements Iterable<E>, EfficientLength;
+class Intersection implements Comparable<int>, Comparable<double>;
+typedef OtherObject Object;
+typedef MyList<T> List<T>;
+typedef StringList List<String>;
+typedef VoidFunction () -> void;
+typedef GenericFunction<T> () -> T;
+""");
+}