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;
+""");
+}