From 8b214f4d72b5bc77cf4da8c963f0376e4f44842f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20von=20der=20Ahe=CC=81?= Date: Fri, 18 Jan 2019 14:09:08 +0000 Subject: [PATCH] Restore type parser from reify project MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ic347890a4146cadb3ad1b6049b562350903505e7 Reviewed-on: https://dart-review.googlesource.com/c/89660 Reviewed-by: Dmitry Stefantsov Commit-Queue: Peter von der Ahé --- .../test/fasta/types/type_parser.dart | 329 ++++++++++++++++++ .../test/fasta/types/type_parser_test.dart | 37 ++ 2 files changed, 366 insertions(+) create mode 100644 pkg/front_end/test/fasta/types/type_parser.dart create mode 100644 pkg/front_end/test/fasta/types/type_parser_test.dart 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 00000000000..e7a19012bbe --- /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 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 typeVariables; + final ParsedInterfaceType supertype; + final List 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 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 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 arguments = []; + 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 requiredArguments = []; + 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 parseTypeVariablesOpt() { + List typeVariables = []; + 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 typeVariables = parseTypeVariablesOpt(); + ParsedType supertype; + if (optionalAdvance("extends")) { + supertype = parseType(); + } + List interfaces = []; + 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 ; + /// + /// This is unlike Dart typedef. + ParsedTypedef parseTypedef() { + expect("typedef"); + String name = parseName(); + List typeVariables = parseTypeVariablesOpt(); + ParsedType type = parseType(); + expect(";"); + return new ParsedTypedef(name, typeVariables, type); + } +} + +List parse(String text) { + Parser parser = new Parser(scanString(text).tokens, text); + List types = []; + 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 00000000000..e24fa158e00 --- /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; +class num implements Comparable; +class int extends num; +class double extends num; +class Function; +class Iterable; +class EfficientLength; +class List implements Iterable, EfficientLength; +class Intersection implements Comparable, Comparable; +typedef OtherObject Object; +typedef MyList List; +typedef StringList List; +typedef VoidFunction () -> void; +typedef GenericFunction () -> T; +"""); +}