| // Copyright (c) 2016, 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.ast_to_text; |
| |
| import 'dart:core' hide MapEntry; |
| import 'dart:core' as core show MapEntry; |
| |
| import 'dart:convert' show json; |
| |
| import '../ast.dart'; |
| import '../import_table.dart'; |
| import '../src/text_util.dart'; |
| |
| abstract class Namer<T> { |
| int index = 0; |
| final Map<T, String> map = <T, String>{}; |
| |
| String getName(T key) => map.putIfAbsent(key, () => '$prefix${++index}'); |
| |
| String get prefix; |
| } |
| |
| class NormalNamer<T> extends Namer<T> { |
| final String prefix; |
| NormalNamer(this.prefix); |
| } |
| |
| class ConstantNamer extends RecursiveVisitor<Null> with Namer<Constant> { |
| final String prefix; |
| ConstantNamer(this.prefix); |
| |
| String getName(Constant constant) { |
| if (!map.containsKey(constant)) { |
| // When printing a non-fully linked kernel AST (i.e. some [Reference]s |
| // are not bound) to text, we need to avoid dereferencing any |
| // references. |
| // |
| // The normal visitor API causes references to be dereferenced in order |
| // to call the `visit<name>(<name>)` / `visit<name>Reference(<name>)`. |
| // |
| // We therefore handle any subclass of [Constant] which has [Reference]s |
| // specially here. |
| // |
| if (constant is InstanceConstant) { |
| // Avoid visiting `InstanceConstant.classReference`. |
| for (final value in constant.fieldValues.values) { |
| // Name everything in post-order visit of DAG. |
| getName(value); |
| } |
| } else if (constant is TearOffConstant) { |
| // We only care about naming the constants themselves. [TearOffConstant] |
| // has no Constant children. |
| // Avoid visiting `TearOffConstant.procedureReference`. |
| } else { |
| // Name everything in post-order visit of DAG. |
| constant.visitChildren(this); |
| } |
| } |
| return super.getName(constant); |
| } |
| |
| defaultConstantReference(Constant constant) { |
| getName(constant); |
| } |
| |
| defaultDartType(DartType type) { |
| // No need to recurse into dart types, we only care about naming the |
| // constants themselves. |
| } |
| } |
| |
| class Disambiguator<T, U> { |
| final Map<T, String> namesT = <T, String>{}; |
| final Map<U, String> namesU = <U, String>{}; |
| final Set<String> usedNames = new Set<String>(); |
| |
| String disambiguate(T key1, U key2, String proposeName()) { |
| getNewName() { |
| var proposedName = proposeName(); |
| if (usedNames.add(proposedName)) return proposedName; |
| int i = 2; |
| while (!usedNames.add('$proposedName$i')) { |
| ++i; |
| } |
| return '$proposedName$i'; |
| } |
| |
| if (key1 != null) { |
| String result = namesT[key1]; |
| if (result != null) return result; |
| return namesT[key1] = getNewName(); |
| } |
| if (key2 != null) { |
| String result = namesU[key2]; |
| if (result != null) return result; |
| return namesU[key2] = getNewName(); |
| } |
| throw "Cannot disambiguate"; |
| } |
| } |
| |
| NameSystem globalDebuggingNames = new NameSystem(); |
| |
| String debugLibraryName(Library node) { |
| return node == null |
| ? 'null' |
| : node.name ?? globalDebuggingNames.nameLibrary(node); |
| } |
| |
| String debugClassName(Class node) { |
| return node == null |
| ? 'null' |
| : node.name ?? globalDebuggingNames.nameClass(node); |
| } |
| |
| String debugQualifiedClassName(Class node) { |
| return debugLibraryName(node.enclosingLibrary) + '::' + debugClassName(node); |
| } |
| |
| String debugMemberName(Member node) { |
| return node.name?.name ?? globalDebuggingNames.nameMember(node); |
| } |
| |
| String debugQualifiedMemberName(Member node) { |
| if (node.enclosingClass != null) { |
| return debugQualifiedClassName(node.enclosingClass) + |
| '::' + |
| debugMemberName(node); |
| } else { |
| return debugLibraryName(node.enclosingLibrary) + |
| '::' + |
| debugMemberName(node); |
| } |
| } |
| |
| String debugTypeParameterName(TypeParameter node) { |
| return node.name ?? globalDebuggingNames.nameTypeParameter(node); |
| } |
| |
| String debugQualifiedTypeParameterName(TypeParameter node) { |
| if (node.parent is Class) { |
| return debugQualifiedClassName(node.parent) + |
| '::' + |
| debugTypeParameterName(node); |
| } |
| if (node.parent is Member) { |
| return debugQualifiedMemberName(node.parent) + |
| '::' + |
| debugTypeParameterName(node); |
| } |
| return debugTypeParameterName(node); |
| } |
| |
| String debugVariableDeclarationName(VariableDeclaration node) { |
| return node.name ?? globalDebuggingNames.nameVariable(node); |
| } |
| |
| String debugNodeToString(Node node) { |
| StringBuffer buffer = new StringBuffer(); |
| new Printer(buffer, syntheticNames: globalDebuggingNames).writeNode(node); |
| return '$buffer'; |
| } |
| |
| String debugLibraryToString(Library library) { |
| StringBuffer buffer = new StringBuffer(); |
| new Printer(buffer, syntheticNames: globalDebuggingNames) |
| .writeLibraryFile(library); |
| return '$buffer'; |
| } |
| |
| String componentToString(Component node) { |
| StringBuffer buffer = new StringBuffer(); |
| new Printer(buffer, syntheticNames: new NameSystem()) |
| .writeComponentFile(node); |
| return '$buffer'; |
| } |
| |
| class NameSystem { |
| final Namer<VariableDeclaration> variables = |
| new NormalNamer<VariableDeclaration>('#t'); |
| final Namer<Member> members = new NormalNamer<Member>('#m'); |
| final Namer<Class> classes = new NormalNamer<Class>('#class'); |
| final Namer<Extension> extensions = new NormalNamer<Extension>('#extension'); |
| final Namer<Library> libraries = new NormalNamer<Library>('#lib'); |
| final Namer<TypeParameter> typeParameters = |
| new NormalNamer<TypeParameter>('#T'); |
| final Namer<TreeNode> labels = new NormalNamer<TreeNode>('#L'); |
| final Namer<Constant> constants = new ConstantNamer('#C'); |
| final Disambiguator<Reference, CanonicalName> prefixes = |
| new Disambiguator<Reference, CanonicalName>(); |
| |
| nameVariable(VariableDeclaration node) => variables.getName(node); |
| nameMember(Member node) => members.getName(node); |
| nameClass(Class node) => classes.getName(node); |
| nameExtension(Extension node) => extensions.getName(node); |
| nameLibrary(Library node) => libraries.getName(node); |
| nameTypeParameter(TypeParameter node) => typeParameters.getName(node); |
| nameSwitchCase(SwitchCase node) => labels.getName(node); |
| nameLabeledStatement(LabeledStatement node) => labels.getName(node); |
| nameConstant(Constant node) => constants.getName(node); |
| |
| final RegExp pathSeparator = new RegExp('[\\/]'); |
| |
| nameLibraryPrefix(Library node, {String proposedName}) { |
| return prefixes.disambiguate(node.reference, node.reference.canonicalName, |
| () { |
| if (proposedName != null) return proposedName; |
| if (node.name != null) return abbreviateName(node.name); |
| if (node.importUri != null) { |
| var path = node.importUri.hasEmptyPath |
| ? '${node.importUri}' |
| : node.importUri.pathSegments.last; |
| if (path.endsWith('.dart')) { |
| path = path.substring(0, path.length - '.dart'.length); |
| } |
| return abbreviateName(path); |
| } |
| return 'L'; |
| }); |
| } |
| |
| nameCanonicalNameAsLibraryPrefix(Reference node, CanonicalName name, |
| {String proposedName}) { |
| return prefixes.disambiguate(node, name, () { |
| if (proposedName != null) return proposedName; |
| CanonicalName canonicalName = name ?? node.canonicalName; |
| if (canonicalName?.name != null) { |
| var path = canonicalName.name; |
| int slash = path.lastIndexOf(pathSeparator); |
| if (slash >= 0) { |
| path = path.substring(slash + 1); |
| } |
| if (path.endsWith('.dart')) { |
| path = path.substring(0, path.length - '.dart'.length); |
| } |
| return abbreviateName(path); |
| } |
| return 'L'; |
| }); |
| } |
| |
| final RegExp punctuation = new RegExp('[.:]'); |
| |
| String abbreviateName(String name) { |
| int dot = name.lastIndexOf(punctuation); |
| if (dot != -1) { |
| name = name.substring(dot + 1); |
| } |
| if (name.length > 4) { |
| return name.substring(0, 3); |
| } |
| return name; |
| } |
| } |
| |
| abstract class Annotator { |
| String annotateVariable(Printer printer, VariableDeclaration node); |
| String annotateReturn(Printer printer, FunctionNode node); |
| String annotateField(Printer printer, Field node); |
| } |
| |
| /// A quick and dirty ambiguous text printer. |
| class Printer extends Visitor<Null> { |
| final NameSystem syntheticNames; |
| final StringSink sink; |
| final Annotator annotator; |
| final Map<String, MetadataRepository<Object>> metadata; |
| ImportTable importTable; |
| int indentation = 0; |
| int column = 0; |
| bool showOffsets; |
| bool showMetadata; |
| |
| static int SPACE = 0; |
| static int WORD = 1; |
| static int SYMBOL = 2; |
| int state = SPACE; |
| |
| Printer(this.sink, |
| {NameSystem syntheticNames, |
| this.showOffsets: false, |
| this.showMetadata: false, |
| this.importTable, |
| this.annotator, |
| this.metadata}) |
| : this.syntheticNames = syntheticNames ?? new NameSystem(); |
| |
| Printer createInner(ImportTable importTable, |
| Map<String, MetadataRepository<Object>> metadata) { |
| return new Printer(sink, |
| importTable: importTable, |
| metadata: metadata, |
| syntheticNames: syntheticNames, |
| annotator: annotator, |
| showOffsets: showOffsets, |
| showMetadata: showMetadata); |
| } |
| |
| bool shouldHighlight(Node node) { |
| return false; |
| } |
| |
| void startHighlight(Node node) {} |
| void endHighlight(Node node) {} |
| |
| String getLibraryName(Library node) { |
| return node.name ?? syntheticNames.nameLibrary(node); |
| } |
| |
| String getLibraryReference(Library node) { |
| if (node == null) return '<No Library>'; |
| if (importTable != null && importTable.getImportIndex(node) != -1) { |
| return syntheticNames.nameLibraryPrefix(node); |
| } |
| return getLibraryName(node); |
| } |
| |
| String getClassName(Class node) { |
| return node.name ?? syntheticNames.nameClass(node); |
| } |
| |
| String getExtensionName(Extension node) { |
| return node.name ?? syntheticNames.nameExtension(node); |
| } |
| |
| String getClassReference(Class node) { |
| if (node == null) return '<No Class>'; |
| String name = getClassName(node); |
| String library = getLibraryReference(node.enclosingLibrary); |
| return '$library::$name'; |
| } |
| |
| String getTypedefReference(Typedef node) { |
| if (node == null) return '<No Typedef>'; |
| String library = getLibraryReference(node.enclosingLibrary); |
| return '$library::${node.name}'; |
| } |
| |
| static final String emptyNameString = '•'; |
| static final Name emptyName = new Name(emptyNameString); |
| |
| Name getMemberName(Member node) { |
| if (node.name?.name == '') return emptyName; |
| if (node.name != null) return node.name; |
| return new Name(syntheticNames.nameMember(node)); |
| } |
| |
| String getMemberReference(Member node) { |
| if (node == null) return '<No Member>'; |
| String name = getMemberName(node).name; |
| if (node.parent is Class) { |
| String className = getClassReference(node.parent); |
| return '$className::$name'; |
| } else { |
| String library = getLibraryReference(node.enclosingLibrary); |
| return '$library::$name'; |
| } |
| } |
| |
| String getVariableName(VariableDeclaration node) { |
| return node.name ?? syntheticNames.nameVariable(node); |
| } |
| |
| String getVariableReference(VariableDeclaration node) { |
| if (node == null) return '<No VariableDeclaration>'; |
| return getVariableName(node); |
| } |
| |
| String getTypeParameterName(TypeParameter node) { |
| return node.name ?? syntheticNames.nameTypeParameter(node); |
| } |
| |
| String getTypeParameterReference(TypeParameter node) { |
| if (node == null) return '<No TypeParameter>'; |
| String name = getTypeParameterName(node); |
| if (node.parent is FunctionNode && node.parent.parent is Member) { |
| String member = getMemberReference(node.parent.parent); |
| return '$member::$name'; |
| } else if (node.parent is Class) { |
| String className = getClassReference(node.parent); |
| return '$className::$name'; |
| } else { |
| return name; // Bound inside a function type. |
| } |
| } |
| |
| void writeComponentProblems(Component component) { |
| writeProblemsAsJson("Problems in component", component.problemsAsJson); |
| } |
| |
| void writeProblemsAsJson(String header, List<String> problemsAsJson) { |
| if (problemsAsJson?.isEmpty == false) { |
| endLine("//"); |
| write("// "); |
| write(header); |
| endLine(":"); |
| endLine("//"); |
| for (String s in problemsAsJson) { |
| Map<String, Object> decoded = json.decode(s); |
| List<Object> plainTextFormatted = decoded["plainTextFormatted"]; |
| List<String> lines = plainTextFormatted.join("\n").split("\n"); |
| for (int i = 0; i < lines.length; i++) { |
| write("//"); |
| String trimmed = lines[i].trimRight(); |
| if (trimmed.isNotEmpty) write(" "); |
| endLine(trimmed); |
| } |
| if (lines.isNotEmpty) endLine("//"); |
| } |
| } |
| } |
| |
| void writeLibraryFile(Library library) { |
| writeAnnotationList(library.annotations); |
| writeWord('library'); |
| if (library.name != null) { |
| writeWord(library.name); |
| } |
| if (library.isNonNullableByDefault) { |
| writeWord("/*isNonNullableByDefault*/"); |
| } |
| endLine(';'); |
| |
| LibraryImportTable imports = new LibraryImportTable(library); |
| Printer inner = createInner(imports, library.enclosingComponent?.metadata); |
| inner.writeStandardLibraryContent(library, |
| outerPrinter: this, importsToPrint: imports); |
| } |
| |
| void printLibraryImportTable(LibraryImportTable imports) { |
| for (var library in imports.importedLibraries) { |
| var importPath = imports.getImportPath(library); |
| if (importPath == "") { |
| var prefix = |
| syntheticNames.nameLibraryPrefix(library, proposedName: 'self'); |
| endLine('import self as $prefix;'); |
| } else { |
| var prefix = syntheticNames.nameLibraryPrefix(library); |
| endLine('import "$importPath" as $prefix;'); |
| } |
| } |
| } |
| |
| void writeStandardLibraryContent(Library library, |
| {Printer outerPrinter, LibraryImportTable importsToPrint}) { |
| outerPrinter ??= this; |
| outerPrinter.writeProblemsAsJson( |
| "Problems in library", library.problemsAsJson); |
| |
| if (importsToPrint != null) { |
| outerPrinter.printLibraryImportTable(importsToPrint); |
| } |
| |
| writeAdditionalExports(library.additionalExports); |
| endLine(); |
| library.dependencies.forEach(writeNode); |
| if (library.dependencies.isNotEmpty) endLine(); |
| library.parts.forEach(writeNode); |
| library.typedefs.forEach(writeNode); |
| library.classes.forEach(writeNode); |
| library.extensions.forEach(writeNode); |
| library.fields.forEach(writeNode); |
| library.procedures.forEach(writeNode); |
| } |
| |
| void writeAdditionalExports(List<Reference> additionalExports) { |
| if (additionalExports.isEmpty) return; |
| write('additionalExports = ('); |
| for (int i = 0; i < additionalExports.length; i++) { |
| Reference reference = additionalExports[i]; |
| var node = reference.node; |
| if (node is Class) { |
| Library nodeLibrary = node.enclosingLibrary; |
| String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary); |
| write(prefix + '::' + node.name); |
| } else if (node is Extension) { |
| Library nodeLibrary = node.enclosingLibrary; |
| String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary); |
| write(prefix + '::' + node.name); |
| } else if (node is Field) { |
| Library nodeLibrary = node.enclosingLibrary; |
| String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary); |
| write(prefix + '::' + node.name.name); |
| } else if (node is Procedure) { |
| Library nodeLibrary = node.enclosingLibrary; |
| String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary); |
| write(prefix + '::' + node.name.name); |
| } else if (node is Typedef) { |
| Library nodeLibrary = node.enclosingLibrary; |
| String prefix = syntheticNames.nameLibraryPrefix(nodeLibrary); |
| write(prefix + '::' + node.name); |
| } else if (reference.canonicalName != null) { |
| write(reference.canonicalName.toString()); |
| } else { |
| throw new UnimplementedError('${node.runtimeType}'); |
| } |
| |
| if (i + 1 == additionalExports.length) { |
| endLine(")"); |
| } else { |
| endLine(","); |
| write(" "); |
| } |
| } |
| } |
| |
| void writeComponentFile(Component component) { |
| ImportTable imports = new ComponentImportTable(component); |
| var inner = createInner(imports, component.metadata); |
| writeWord('main'); |
| writeSpaced('='); |
| inner.writeMemberReferenceFromReference(component.mainMethodName); |
| endLine(';'); |
| if (showMetadata) { |
| inner.writeMetadata(component); |
| } |
| writeComponentProblems(component); |
| for (var library in component.libraries) { |
| if (showMetadata) { |
| inner.writeMetadata(library); |
| } |
| writeAnnotationList(library.annotations); |
| writeWord('library'); |
| if (library.name != null) { |
| writeWord(library.name); |
| } |
| if (library.importUri != null) { |
| writeSpaced('from'); |
| writeWord('"${library.importUri}"'); |
| } |
| var prefix = syntheticNames.nameLibraryPrefix(library); |
| writeSpaced('as'); |
| writeWord(prefix); |
| endLine(' {'); |
| ++inner.indentation; |
| |
| inner.writeStandardLibraryContent(library); |
| --inner.indentation; |
| endLine('}'); |
| } |
| writeConstantTable(component); |
| } |
| |
| void writeConstantTable(Component component) { |
| if (syntheticNames.constants.map.isEmpty) return; |
| ImportTable imports = new ComponentImportTable(component); |
| var inner = createInner(imports, component.metadata); |
| writeWord('constants '); |
| endLine(' {'); |
| ++inner.indentation; |
| for (final Constant constant |
| in syntheticNames.constants.map.keys.toList()) { |
| inner.writeNode(constant); |
| } |
| --inner.indentation; |
| endLine('}'); |
| } |
| |
| int getPrecedence(TreeNode node) { |
| return Precedence.of(node); |
| } |
| |
| void write(String string) { |
| sink.write(string); |
| column += string.length; |
| } |
| |
| void writeSpace([String string = ' ']) { |
| write(string); |
| state = SPACE; |
| } |
| |
| void ensureSpace() { |
| if (state != SPACE) writeSpace(); |
| } |
| |
| void writeSymbol(String string) { |
| write(string); |
| state = SYMBOL; |
| } |
| |
| void writeSpaced(String string) { |
| ensureSpace(); |
| write(string); |
| writeSpace(); |
| } |
| |
| void writeComma([String string = ',']) { |
| write(string); |
| writeSpace(); |
| } |
| |
| void writeWord(String string) { |
| if (string.isEmpty) return; |
| ensureWordBoundary(); |
| write(string); |
| state = WORD; |
| } |
| |
| void ensureWordBoundary() { |
| if (state == WORD) { |
| writeSpace(); |
| } |
| } |
| |
| void writeIndentation() { |
| writeSpace(' ' * indentation); |
| } |
| |
| void writeNode(Node node) { |
| if (node == null) { |
| writeSymbol("<Null>"); |
| } else { |
| final highlight = shouldHighlight(node); |
| if (highlight) { |
| startHighlight(node); |
| } |
| |
| if (showOffsets && node is TreeNode) { |
| writeWord("[${node.fileOffset}]"); |
| } |
| if (showMetadata && node is TreeNode) { |
| writeMetadata(node); |
| } |
| |
| node.accept(this); |
| |
| if (highlight) { |
| endHighlight(node); |
| } |
| } |
| } |
| |
| void writeOptionalNode(Node node) { |
| if (node != null) { |
| node.accept(this); |
| } |
| } |
| |
| void writeMetadata(TreeNode node) { |
| if (metadata != null) { |
| for (var md in metadata.values) { |
| final nodeMetadata = md.mapping[node]; |
| if (nodeMetadata != null) { |
| writeWord("[@${md.tag}=${nodeMetadata}]"); |
| } |
| } |
| } |
| } |
| |
| void writeAnnotatedType(DartType type, String annotation) { |
| writeType(type); |
| if (annotation != null) { |
| write('/'); |
| write(annotation); |
| state = WORD; |
| } |
| } |
| |
| void writeType(DartType type) { |
| if (type == null) { |
| write('<No DartType>'); |
| } else { |
| type.accept(this); |
| } |
| } |
| |
| void writeOptionalType(DartType type) { |
| if (type != null) { |
| type.accept(this); |
| } |
| } |
| |
| visitSupertype(Supertype type) { |
| if (type == null) { |
| write('<No Supertype>'); |
| } else { |
| writeClassReferenceFromReference(type.className); |
| if (type.typeArguments.isNotEmpty) { |
| writeSymbol('<'); |
| writeList(type.typeArguments, writeType); |
| writeSymbol('>'); |
| } |
| } |
| } |
| |
| visitTypedefType(TypedefType type) { |
| writeTypedefReference(type.typedefNode); |
| if (type.typeArguments.isNotEmpty) { |
| writeSymbol('<'); |
| writeList(type.typeArguments, writeType); |
| writeSymbol('>'); |
| } |
| } |
| |
| void writeModifier(bool isThere, String name) { |
| if (isThere) { |
| writeWord(name); |
| } |
| } |
| |
| void writeName(Name name) { |
| if (name?.name == '') { |
| writeWord(emptyNameString); |
| } else { |
| writeWord(name?.name ?? '<anonymous>'); // TODO: write library name |
| } |
| } |
| |
| void endLine([String string]) { |
| if (string != null) { |
| write(string); |
| } |
| write('\n'); |
| state = SPACE; |
| column = 0; |
| } |
| |
| void writeFunction(FunctionNode function, |
| {name, List<Initializer> initializers, bool terminateLine: true}) { |
| if (name is String) { |
| writeWord(name); |
| } else if (name is Name) { |
| writeName(name); |
| } else { |
| assert(name == null); |
| } |
| writeTypeParameterList(function.typeParameters); |
| writeParameterList(function.positionalParameters, function.namedParameters, |
| function.requiredParameterCount); |
| writeReturnType( |
| function.returnType, annotator?.annotateReturn(this, function)); |
| if (initializers != null && initializers.isNotEmpty) { |
| endLine(); |
| ++indentation; |
| writeIndentation(); |
| writeComma(':'); |
| writeList(initializers, writeNode); |
| --indentation; |
| } |
| if (function.asyncMarker != AsyncMarker.Sync) { |
| writeSpaced(getAsyncMarkerKeyword(function.asyncMarker)); |
| } |
| if (function.dartAsyncMarker != AsyncMarker.Sync && |
| function.dartAsyncMarker != function.asyncMarker) { |
| writeSpaced("/* originally"); |
| writeSpaced(getAsyncMarkerKeyword(function.dartAsyncMarker)); |
| writeSpaced("*/"); |
| } |
| if (function.body != null) { |
| writeFunctionBody(function.body, terminateLine: terminateLine); |
| } else if (terminateLine) { |
| endLine(';'); |
| } |
| } |
| |
| String getAsyncMarkerKeyword(AsyncMarker marker) { |
| switch (marker) { |
| case AsyncMarker.Sync: |
| return 'sync'; |
| case AsyncMarker.SyncStar: |
| return 'sync*'; |
| case AsyncMarker.Async: |
| return 'async'; |
| case AsyncMarker.AsyncStar: |
| return 'async*'; |
| case AsyncMarker.SyncYielding: |
| return 'yielding'; |
| default: |
| return '<Invalid async marker: $marker>'; |
| } |
| } |
| |
| void writeFunctionBody(Statement body, {bool terminateLine: true}) { |
| if (body is Block && body.statements.isEmpty) { |
| ensureSpace(); |
| writeSymbol('{}'); |
| state = WORD; |
| if (terminateLine) { |
| endLine(); |
| } |
| } else if (body is Block) { |
| ensureSpace(); |
| endLine('{'); |
| ++indentation; |
| body.statements.forEach(writeNode); |
| --indentation; |
| writeIndentation(); |
| writeSymbol('}'); |
| state = WORD; |
| if (terminateLine) { |
| endLine(); |
| } |
| } else if (body is ReturnStatement && !terminateLine) { |
| writeSpaced('=>'); |
| writeExpression(body.expression); |
| } else { |
| writeBody(body); |
| } |
| } |
| |
| writeFunctionType(FunctionType node, |
| {List<VariableDeclaration> typedefPositional, |
| List<VariableDeclaration> typedefNamed}) { |
| if (state == WORD) { |
| ensureSpace(); |
| } |
| writeTypeParameterList(node.typeParameters); |
| writeSymbol('('); |
| List<DartType> positional = node.positionalParameters; |
| |
| bool parametersAnnotated = false; |
| if (typedefPositional != null) { |
| for (VariableDeclaration formal in typedefPositional) { |
| parametersAnnotated = |
| parametersAnnotated || formal.annotations.length > 0; |
| } |
| } |
| if (typedefNamed != null) { |
| for (VariableDeclaration formal in typedefNamed) { |
| parametersAnnotated = |
| parametersAnnotated || formal.annotations.length > 0; |
| } |
| } |
| |
| if (parametersAnnotated && typedefPositional != null) { |
| writeList(typedefPositional.take(node.requiredParameterCount), |
| writeVariableDeclaration); |
| } else { |
| writeList(positional.take(node.requiredParameterCount), writeType); |
| } |
| |
| if (node.requiredParameterCount < positional.length) { |
| if (node.requiredParameterCount > 0) { |
| writeComma(); |
| } |
| writeSymbol('['); |
| if (parametersAnnotated && typedefPositional != null) { |
| writeList(typedefPositional.skip(node.requiredParameterCount), |
| writeVariableDeclaration); |
| } else { |
| writeList(positional.skip(node.requiredParameterCount), writeType); |
| } |
| writeSymbol(']'); |
| } |
| if (node.namedParameters.isNotEmpty) { |
| if (node.positionalParameters.isNotEmpty) { |
| writeComma(); |
| } |
| writeSymbol('{'); |
| if (parametersAnnotated && typedefNamed != null) { |
| writeList(typedefNamed, writeVariableDeclaration); |
| } else { |
| writeList(node.namedParameters, visitNamedType); |
| } |
| writeSymbol('}'); |
| } |
| writeSymbol(')'); |
| ensureSpace(); |
| write('→'); |
| writeNullability(node.nullability); |
| writeSpace(); |
| writeType(node.returnType); |
| } |
| |
| void writeBody(Statement body) { |
| if (body is Block) { |
| endLine(' {'); |
| ++indentation; |
| body.statements.forEach(writeNode); |
| --indentation; |
| writeIndentation(); |
| endLine('}'); |
| } else { |
| endLine(); |
| ++indentation; |
| writeNode(body); |
| --indentation; |
| } |
| } |
| |
| void writeReturnType(DartType type, String annotation) { |
| if (type == null) return; |
| writeSpaced('→'); |
| writeAnnotatedType(type, annotation); |
| } |
| |
| void writeTypeParameterList(List<TypeParameter> typeParameters) { |
| if (typeParameters.isEmpty) return; |
| writeSymbol('<'); |
| writeList(typeParameters, writeNode); |
| writeSymbol('>'); |
| state = WORD; // Ensure space if not followed by another symbol. |
| } |
| |
| void writeParameterList(List<VariableDeclaration> positional, |
| List<VariableDeclaration> named, int requiredParameterCount) { |
| writeSymbol('('); |
| writeList( |
| positional.take(requiredParameterCount), writeVariableDeclaration); |
| if (requiredParameterCount < positional.length) { |
| if (requiredParameterCount > 0) { |
| writeComma(); |
| } |
| writeSymbol('['); |
| writeList( |
| positional.skip(requiredParameterCount), writeVariableDeclaration); |
| writeSymbol(']'); |
| } |
| if (named.isNotEmpty) { |
| if (positional.isNotEmpty) { |
| writeComma(); |
| } |
| writeSymbol('{'); |
| writeList(named, writeVariableDeclaration); |
| writeSymbol('}'); |
| } |
| writeSymbol(')'); |
| } |
| |
| void writeList<T>(Iterable<T> nodes, void callback(T x), |
| {String separator: ','}) { |
| bool first = true; |
| for (var node in nodes) { |
| if (first) { |
| first = false; |
| } else { |
| writeComma(separator); |
| } |
| callback(node); |
| } |
| } |
| |
| void writeClassReferenceFromReference(Reference reference) { |
| writeWord(getClassReferenceFromReference(reference)); |
| } |
| |
| String getClassReferenceFromReference(Reference reference) { |
| if (reference == null) return '<No Class>'; |
| if (reference.node != null) return getClassReference(reference.asClass); |
| if (reference.canonicalName != null) |
| return getCanonicalNameString(reference.canonicalName); |
| throw "Neither node nor canonical name found"; |
| } |
| |
| void writeMemberReferenceFromReference(Reference reference) { |
| writeWord(getMemberReferenceFromReference(reference)); |
| } |
| |
| String getMemberReferenceFromReference(Reference reference) { |
| if (reference == null) return '<No Member>'; |
| if (reference.node != null) return getMemberReference(reference.asMember); |
| if (reference.canonicalName != null) |
| return getCanonicalNameString(reference.canonicalName); |
| throw "Neither node nor canonical name found"; |
| } |
| |
| String getCanonicalNameString(CanonicalName name) { |
| if (name.isRoot) throw 'unexpected root'; |
| if (name.name.startsWith('@')) throw 'unexpected @'; |
| |
| libraryString(CanonicalName lib) { |
| if (lib.reference?.node != null) |
| return getLibraryReference(lib.reference.asLibrary); |
| return syntheticNames.nameCanonicalNameAsLibraryPrefix( |
| lib.reference, lib); |
| } |
| |
| classString(CanonicalName cls) => |
| libraryString(cls.parent) + '::' + cls.name; |
| |
| if (name.parent.isRoot) return libraryString(name); |
| if (name.parent.parent.isRoot) return classString(name); |
| |
| CanonicalName atNode = name.parent; |
| while (!atNode.name.startsWith('@')) atNode = atNode.parent; |
| |
| String parent = ""; |
| if (atNode.parent.parent.isRoot) { |
| parent = libraryString(atNode.parent); |
| } else { |
| parent = classString(atNode.parent); |
| } |
| |
| if (name.name == '') return "$parent::$emptyNameString"; |
| return "$parent::${name.name}"; |
| } |
| |
| void writeTypedefReference(Typedef typedefNode) { |
| writeWord(getTypedefReference(typedefNode)); |
| } |
| |
| void writeVariableReference(VariableDeclaration variable) { |
| final highlight = shouldHighlight(variable); |
| if (highlight) { |
| startHighlight(variable); |
| } |
| writeWord(getVariableReference(variable)); |
| if (highlight) { |
| endHighlight(variable); |
| } |
| } |
| |
| void writeTypeParameterReference(TypeParameter node) { |
| writeWord(getTypeParameterReference(node)); |
| } |
| |
| void writeExpression(Expression node, [int minimumPrecedence]) { |
| final highlight = shouldHighlight(node); |
| if (highlight) { |
| startHighlight(node); |
| } |
| if (showOffsets) writeWord("[${node.fileOffset}]"); |
| bool needsParenteses = false; |
| if (minimumPrecedence != null && getPrecedence(node) < minimumPrecedence) { |
| needsParenteses = true; |
| writeSymbol('('); |
| } |
| writeNode(node); |
| if (needsParenteses) { |
| writeSymbol(')'); |
| } |
| if (highlight) { |
| endHighlight(node); |
| } |
| } |
| |
| void writeAnnotation(Expression node) { |
| writeSymbol('@'); |
| if (node is ConstructorInvocation) { |
| writeMemberReferenceFromReference(node.targetReference); |
| visitArguments(node.arguments); |
| } else { |
| writeExpression(node); |
| } |
| } |
| |
| void writeAnnotationList(List<Expression> nodes, {bool separateLines: true}) { |
| for (Expression node in nodes) { |
| if (separateLines) { |
| writeIndentation(); |
| } |
| writeAnnotation(node); |
| if (separateLines) { |
| endLine(); |
| } else { |
| writeSpace(); |
| } |
| } |
| } |
| |
| visitLibrary(Library node) {} |
| |
| visitField(Field node) { |
| writeAnnotationList(node.annotations); |
| writeIndentation(); |
| writeModifier(node.isLate, 'late'); |
| writeModifier(node.isStatic, 'static'); |
| writeModifier(node.isCovariant, 'covariant'); |
| writeModifier(node.isGenericCovariantImpl, 'generic-covariant-impl'); |
| writeModifier(node.isFinal, 'final'); |
| writeModifier(node.isConst, 'const'); |
| // Only show implicit getter/setter modifiers in cases where they are |
| // out of the ordinary. |
| if (node.isStatic) { |
| writeModifier(node.hasImplicitGetter, '[getter]'); |
| writeModifier(node.hasImplicitSetter, '[setter]'); |
| } else { |
| writeModifier(!node.hasImplicitGetter, '[no-getter]'); |
| if (node.isFinal) { |
| writeModifier(node.hasImplicitSetter, '[setter]'); |
| } else { |
| writeModifier(!node.hasImplicitSetter, '[no-setter]'); |
| } |
| } |
| writeWord('field'); |
| writeSpace(); |
| writeAnnotatedType(node.type, annotator?.annotateField(this, node)); |
| writeName(getMemberName(node)); |
| if (node.initializer != null) { |
| writeSpaced('='); |
| writeExpression(node.initializer); |
| } |
| List<String> features = <String>[]; |
| if (node.enclosingLibrary.isNonNullableByDefault != |
| node.isNonNullableByDefault) { |
| if (node.isNonNullableByDefault) { |
| features.add("isNonNullableByDefault"); |
| } else { |
| features.add("isNullableByDefault"); |
| } |
| } |
| if ((node.enclosingClass == null && |
| node.enclosingLibrary.fileUri != node.fileUri) || |
| (node.enclosingClass != null && |
| node.enclosingClass.fileUri != node.fileUri)) { |
| features.add(" from ${node.fileUri} "); |
| } |
| if (features.isNotEmpty) { |
| writeWord("/*${features.join(',')}*/"); |
| } |
| endLine(';'); |
| } |
| |
| visitProcedure(Procedure node) { |
| writeAnnotationList(node.annotations); |
| writeIndentation(); |
| writeModifier(node.isExternal, 'external'); |
| writeModifier(node.isStatic, 'static'); |
| writeModifier(node.isAbstract, 'abstract'); |
| writeModifier(node.isForwardingStub, 'forwarding-stub'); |
| writeModifier(node.isForwardingSemiStub, 'forwarding-semi-stub'); |
| writeModifier(node.isMemberSignature, 'member-signature'); |
| writeModifier(node.isNoSuchMethodForwarder, 'no-such-method-forwarder'); |
| writeWord(procedureKindToString(node.kind)); |
| List<String> features = <String>[]; |
| if (node.enclosingLibrary.isNonNullableByDefault != |
| node.isNonNullableByDefault) { |
| if (node.isNonNullableByDefault) { |
| features.add("isNonNullableByDefault"); |
| } else { |
| features.add("isNullableByDefault"); |
| } |
| } |
| if ((node.enclosingClass == null && |
| node.enclosingLibrary.fileUri != node.fileUri) || |
| (node.enclosingClass != null && |
| node.enclosingClass.fileUri != node.fileUri)) { |
| features.add(" from ${node.fileUri} "); |
| } |
| if (features.isNotEmpty) { |
| writeWord("/*${features.join(',')}*/"); |
| } |
| writeFunction(node.function, name: getMemberName(node)); |
| } |
| |
| visitConstructor(Constructor node) { |
| writeAnnotationList(node.annotations); |
| writeIndentation(); |
| writeModifier(node.isExternal, 'external'); |
| writeModifier(node.isConst, 'const'); |
| writeModifier(node.isSynthetic, 'synthetic'); |
| writeWord('constructor'); |
| List<String> features = <String>[]; |
| if (node.enclosingLibrary.isNonNullableByDefault != |
| node.isNonNullableByDefault) { |
| if (node.isNonNullableByDefault) { |
| features.add("isNonNullableByDefault"); |
| } else { |
| features.add("isNullableByDefault"); |
| } |
| } |
| if (features.isNotEmpty) { |
| writeWord("/*${features.join(',')}*/"); |
| } |
| writeFunction(node.function, |
| name: node.name, initializers: node.initializers); |
| } |
| |
| visitRedirectingFactoryConstructor(RedirectingFactoryConstructor node) { |
| writeAnnotationList(node.annotations); |
| writeIndentation(); |
| writeModifier(node.isExternal, 'external'); |
| writeModifier(node.isConst, 'const'); |
| writeWord('redirecting_factory'); |
| |
| if (node.name != null) { |
| writeName(node.name); |
| } |
| writeTypeParameterList(node.typeParameters); |
| writeParameterList(node.positionalParameters, node.namedParameters, |
| node.requiredParameterCount); |
| writeSpaced('='); |
| writeMemberReferenceFromReference(node.targetReference); |
| if (node.typeArguments.isNotEmpty) { |
| writeSymbol('<'); |
| writeList(node.typeArguments, writeType); |
| writeSymbol('>'); |
| } |
| List<String> features = <String>[]; |
| if (node.enclosingLibrary.isNonNullableByDefault != |
| node.isNonNullableByDefault) { |
| if (node.isNonNullableByDefault) { |
| features.add("isNonNullableByDefault"); |
| } else { |
| features.add("isNullableByDefault"); |
| } |
| } |
| if (features.isNotEmpty) { |
| writeWord("/*${features.join(',')}*/"); |
| } |
| endLine(';'); |
| } |
| |
| visitClass(Class node) { |
| writeAnnotationList(node.annotations); |
| writeIndentation(); |
| writeModifier(node.isAbstract, 'abstract'); |
| writeWord('class'); |
| writeWord(getClassName(node)); |
| writeTypeParameterList(node.typeParameters); |
| if (node.isMixinApplication) { |
| writeSpaced('='); |
| visitSupertype(node.supertype); |
| writeSpaced('with'); |
| visitSupertype(node.mixedInType); |
| } else if (node.supertype != null) { |
| writeSpaced('extends'); |
| visitSupertype(node.supertype); |
| } |
| if (node.implementedTypes.isNotEmpty) { |
| writeSpaced('implements'); |
| writeList(node.implementedTypes, visitSupertype); |
| } |
| List<String> features = <String>[]; |
| if (node.isEnum) { |
| features.add('isEnum'); |
| } |
| if (node.isAnonymousMixin) { |
| features.add('isAnonymousMixin'); |
| } |
| if (node.isEliminatedMixin) { |
| features.add('isEliminatedMixin'); |
| } |
| if (node.isMixinDeclaration) { |
| features.add('isMixinDeclaration'); |
| } |
| if (node.hasConstConstructor) { |
| features.add('hasConstConstructor'); |
| } |
| if (features.isNotEmpty) { |
| writeSpaced('/*${features.join(',')}*/'); |
| } |
| var endLineString = ' {'; |
| if (node.enclosingLibrary.fileUri != node.fileUri) { |
| endLineString += ' // from ${node.fileUri}'; |
| } |
| endLine(endLineString); |
| ++indentation; |
| node.fields.forEach(writeNode); |
| node.constructors.forEach(writeNode); |
| node.procedures.forEach(writeNode); |
| node.redirectingFactoryConstructors.forEach(writeNode); |
| --indentation; |
| writeIndentation(); |
| endLine('}'); |
| } |
| |
| visitExtension(Extension node) { |
| writeIndentation(); |
| writeWord('extension'); |
| writeWord(getExtensionName(node)); |
| writeTypeParameterList(node.typeParameters); |
| writeSpaced('on'); |
| writeType(node.onType); |
| var endLineString = ' {'; |
| if (node.enclosingLibrary.fileUri != node.fileUri) { |
| endLineString += ' // from ${node.fileUri}'; |
| } |
| endLine(endLineString); |
| ++indentation; |
| node.members.forEach((ExtensionMemberDescriptor descriptor) { |
| writeIndentation(); |
| writeModifier(descriptor.isStatic, 'static'); |
| switch (descriptor.kind) { |
| case ExtensionMemberKind.Method: |
| writeWord('method'); |
| break; |
| case ExtensionMemberKind.Getter: |
| writeWord('get'); |
| break; |
| case ExtensionMemberKind.Setter: |
| writeWord('set'); |
| break; |
| case ExtensionMemberKind.Operator: |
| writeWord('operator'); |
| break; |
| case ExtensionMemberKind.Field: |
| writeWord('field'); |
| break; |
| case ExtensionMemberKind.TearOff: |
| writeWord('tearoff'); |
| break; |
| } |
| writeName(descriptor.name); |
| writeSpaced('='); |
| Member member = descriptor.member.asMember; |
| if (member is Procedure) { |
| if (member.isGetter) { |
| writeWord('get'); |
| } else if (member.isSetter) { |
| writeWord('set'); |
| } |
| } |
| writeMemberReferenceFromReference(descriptor.member); |
| endLine(';'); |
| }); |
| --indentation; |
| writeIndentation(); |
| endLine('}'); |
| } |
| |
| visitTypedef(Typedef node) { |
| writeAnnotationList(node.annotations); |
| writeIndentation(); |
| writeWord('typedef'); |
| writeWord(node.name); |
| writeTypeParameterList(node.typeParameters); |
| writeSpaced('='); |
| if (node.type is FunctionType) { |
| writeFunctionType(node.type, |
| typedefPositional: node.positionalParameters, |
| typedefNamed: node.namedParameters); |
| } else { |
| writeNode(node.type); |
| } |
| endLine(';'); |
| } |
| |
| visitInvalidExpression(InvalidExpression node) { |
| writeWord('invalid-expression'); |
| if (node.message != null) { |
| writeWord('"${escapeString(node.message)}"'); |
| } |
| } |
| |
| visitMethodInvocation(MethodInvocation node) { |
| writeExpression(node.receiver, Precedence.PRIMARY); |
| writeSymbol('.'); |
| writeInterfaceTarget(node.name, node.interfaceTargetReference); |
| writeNode(node.arguments); |
| } |
| |
| visitDirectMethodInvocation(DirectMethodInvocation node) { |
| writeExpression(node.receiver, Precedence.PRIMARY); |
| writeSymbol('.{='); |
| writeMemberReferenceFromReference(node.targetReference); |
| writeSymbol('}'); |
| writeNode(node.arguments); |
| } |
| |
| visitSuperMethodInvocation(SuperMethodInvocation node) { |
| writeWord('super'); |
| writeSymbol('.'); |
| writeInterfaceTarget(node.name, node.interfaceTargetReference); |
| writeNode(node.arguments); |
| } |
| |
| visitStaticInvocation(StaticInvocation node) { |
| writeModifier(node.isConst, 'const'); |
| writeMemberReferenceFromReference(node.targetReference); |
| writeNode(node.arguments); |
| } |
| |
| visitConstructorInvocation(ConstructorInvocation node) { |
| writeWord(node.isConst ? 'const' : 'new'); |
| writeMemberReferenceFromReference(node.targetReference); |
| writeNode(node.arguments); |
| } |
| |
| visitNot(Not node) { |
| writeSymbol('!'); |
| writeExpression(node.operand, Precedence.PREFIX); |
| } |
| |
| visitNullCheck(NullCheck node) { |
| writeExpression(node.operand, Precedence.POSTFIX); |
| writeSymbol('!'); |
| } |
| |
| visitLogicalExpression(LogicalExpression node) { |
| int precedence = Precedence.binaryPrecedence[node.operator]; |
| writeExpression(node.left, precedence); |
| writeSpaced(node.operator); |
| writeExpression(node.right, precedence + 1); |
| } |
| |
| visitConditionalExpression(ConditionalExpression node) { |
| writeExpression(node.condition, Precedence.LOGICAL_OR); |
| ensureSpace(); |
| write('?'); |
| writeStaticType(node.staticType); |
| writeSpace(); |
| writeExpression(node.then); |
| writeSpaced(':'); |
| writeExpression(node.otherwise); |
| } |
| |
| visitStringConcatenation(StringConcatenation node) { |
| if (state == WORD) { |
| writeSpace(); |
| } |
| write('"'); |
| for (var part in node.expressions) { |
| if (part is StringLiteral) { |
| writeSymbol(escapeString(part.value)); |
| } else { |
| writeSymbol(r'${'); |
| writeExpression(part); |
| writeSymbol('}'); |
| } |
| } |
| write('"'); |
| state = WORD; |
| } |
| |
| visitListConcatenation(ListConcatenation node) { |
| bool first = true; |
| for (Expression part in node.lists) { |
| if (!first) writeSpaced('+'); |
| writeExpression(part); |
| first = false; |
| } |
| } |
| |
| visitSetConcatenation(SetConcatenation node) { |
| bool first = true; |
| for (Expression part in node.sets) { |
| if (!first) writeSpaced('+'); |
| writeExpression(part); |
| first = false; |
| } |
| } |
| |
| visitMapConcatenation(MapConcatenation node) { |
| bool first = true; |
| for (Expression part in node.maps) { |
| if (!first) writeSpaced('+'); |
| writeExpression(part); |
| first = false; |
| } |
| } |
| |
| visitInstanceCreation(InstanceCreation node) { |
| writeClassReferenceFromReference(node.classReference); |
| if (node.typeArguments.isNotEmpty) { |
| writeSymbol('<'); |
| writeList(node.typeArguments, writeType); |
| writeSymbol('>'); |
| } |
| writeSymbol('{'); |
| bool first = true; |
| node.fieldValues.forEach((Reference fieldRef, Expression value) { |
| if (!first) { |
| writeComma(); |
| } |
| writeWord('${fieldRef.asField.name.name}'); |
| writeSymbol(':'); |
| writeExpression(value); |
| first = false; |
| }); |
| for (AssertStatement assert_ in node.asserts) { |
| if (!first) { |
| writeComma(); |
| } |
| write('assert('); |
| writeExpression(assert_.condition); |
| if (assert_.message != null) { |
| writeComma(); |
| writeExpression(assert_.message); |
| } |
| write(')'); |
| first = false; |
| } |
| for (Expression unusedArgument in node.unusedArguments) { |
| if (!first) { |
| writeComma(); |
| } |
| writeExpression(unusedArgument); |
| first = false; |
| } |
| writeSymbol('}'); |
| } |
| |
| visitFileUriExpression(FileUriExpression node) { |
| writeExpression(node.expression); |
| } |
| |
| visitIsExpression(IsExpression node) { |
| writeExpression(node.operand, Precedence.BITWISE_OR); |
| writeSpaced( |
| node.isForNonNullableByDefault ? 'is{ForNonNullableByDefault}' : 'is'); |
| writeType(node.type); |
| } |
| |
| visitAsExpression(AsExpression node) { |
| writeExpression(node.operand, Precedence.BITWISE_OR); |
| List<String> flags = <String>[]; |
| if (node.isTypeError) { |
| flags.add('TypeError'); |
| } |
| if (node.isCovarianceCheck) { |
| flags.add('CovarianceCheck'); |
| } |
| if (node.isForDynamic) { |
| flags.add('ForDynamic'); |
| } |
| if (node.isForNonNullableByDefault) { |
| flags.add('ForNonNullableByDefault'); |
| } |
| writeSpaced(flags.isNotEmpty ? 'as{${flags.join(',')}}' : 'as'); |
| writeType(node.type); |
| } |
| |
| visitSymbolLiteral(SymbolLiteral node) { |
| writeSymbol('#'); |
| writeWord(node.value); |
| } |
| |
| visitTypeLiteral(TypeLiteral node) { |
| writeType(node.type); |
| } |
| |
| visitThisExpression(ThisExpression node) { |
| writeWord('this'); |
| } |
| |
| visitRethrow(Rethrow node) { |
| writeWord('rethrow'); |
| } |
| |
| visitThrow(Throw node) { |
| writeWord('throw'); |
| writeSpace(); |
| writeExpression(node.expression); |
| } |
| |
| visitListLiteral(ListLiteral node) { |
| if (node.isConst) { |
| writeWord('const'); |
| writeSpace(); |
| } |
| if (node.typeArgument != null) { |
| writeSymbol('<'); |
| writeType(node.typeArgument); |
| writeSymbol('>'); |
| } |
| writeSymbol('['); |
| writeList(node.expressions, writeNode); |
| writeSymbol(']'); |
| } |
| |
| visitSetLiteral(SetLiteral node) { |
| if (node.isConst) { |
| writeWord('const'); |
| writeSpace(); |
| } |
| if (node.typeArgument != null) { |
| writeSymbol('<'); |
| writeType(node.typeArgument); |
| writeSymbol('>'); |
| } |
| writeSymbol('{'); |
| writeList(node.expressions, writeNode); |
| writeSymbol('}'); |
| } |
| |
| visitMapLiteral(MapLiteral node) { |
| if (node.isConst) { |
| writeWord('const'); |
| writeSpace(); |
| } |
| if (node.keyType != null) { |
| writeSymbol('<'); |
| writeList([node.keyType, node.valueType], writeType); |
| writeSymbol('>'); |
| } |
| writeSymbol('{'); |
| writeList(node.entries, writeNode); |
| writeSymbol('}'); |
| } |
| |
| visitMapEntry(MapEntry node) { |
| writeExpression(node.key); |
| writeComma(':'); |
| writeExpression(node.value); |
| } |
| |
| visitAwaitExpression(AwaitExpression node) { |
| writeWord('await'); |
| writeExpression(node.operand); |
| } |
| |
| visitFunctionExpression(FunctionExpression node) { |
| writeFunction(node.function, terminateLine: false); |
| } |
| |
| visitStringLiteral(StringLiteral node) { |
| writeWord('"${escapeString(node.value)}"'); |
| } |
| |
| visitIntLiteral(IntLiteral node) { |
| writeWord('${node.value}'); |
| } |
| |
| visitDoubleLiteral(DoubleLiteral node) { |
| writeWord('${node.value}'); |
| } |
| |
| visitBoolLiteral(BoolLiteral node) { |
| writeWord('${node.value}'); |
| } |
| |
| visitNullLiteral(NullLiteral node) { |
| writeWord('null'); |
| } |
| |
| visitLet(Let node) { |
| writeWord('let'); |
| writeVariableDeclaration(node.variable); |
| writeSpaced('in'); |
| writeExpression(node.body); |
| } |
| |
| visitBlockExpression(BlockExpression node) { |
| writeSpaced('block'); |
| writeBlockBody(node.body.statements, asExpression: true); |
| writeSymbol(' =>'); |
| writeExpression(node.value); |
| } |
| |
| visitInstantiation(Instantiation node) { |
| writeExpression(node.expression); |
| writeSymbol('<'); |
| writeList(node.typeArguments, writeType); |
| writeSymbol('>'); |
| } |
| |
| visitLoadLibrary(LoadLibrary node) { |
| writeWord('LoadLibrary'); |
| writeSymbol('('); |
| writeWord(node.import.name); |
| writeSymbol(')'); |
| state = WORD; |
| } |
| |
| visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) { |
| writeWord('CheckLibraryIsLoaded'); |
| writeSymbol('('); |
| writeWord(node.import.name); |
| writeSymbol(')'); |
| state = WORD; |
| } |
| |
| visitLibraryPart(LibraryPart node) { |
| writeAnnotationList(node.annotations); |
| writeIndentation(); |
| writeWord('part'); |
| writeWord(node.partUri); |
| endLine(";"); |
| } |
| |
| visitLibraryDependency(LibraryDependency node) { |
| writeIndentation(); |
| writeWord(node.isImport ? 'import' : 'export'); |
| var uriString; |
| if (node.importedLibraryReference?.node != null) { |
| uriString = '${node.targetLibrary.importUri}'; |
| } else { |
| uriString = '${node.importedLibraryReference?.canonicalName?.name}'; |
| } |
| writeWord('"$uriString"'); |
| if (node.isDeferred) { |
| writeWord('deferred'); |
| } |
| if (node.name != null) { |
| writeWord('as'); |
| writeWord(node.name); |
| } |
| endLine(';'); |
| } |
| |
| defaultExpression(Expression node) { |
| writeWord('${node.runtimeType}'); |
| } |
| |
| visitVariableGet(VariableGet node) { |
| writeVariableReference(node.variable); |
| if (node.promotedType != null) { |
| writeSymbol('{'); |
| writeNode(node.promotedType); |
| writeSymbol('}'); |
| state = WORD; |
| } |
| } |
| |
| visitVariableSet(VariableSet node) { |
| writeVariableReference(node.variable); |
| writeSpaced('='); |
| writeExpression(node.value); |
| } |
| |
| void writeInterfaceTarget(Name name, Reference target) { |
| if (target != null) { |
| writeSymbol('{'); |
| writeMemberReferenceFromReference(target); |
| writeSymbol('}'); |
| } else { |
| writeName(name); |
| } |
| } |
| |
| void writeStaticType(DartType type) { |
| if (type != null) { |
| writeSymbol('{'); |
| writeType(type); |
| writeSymbol('}'); |
| } |
| } |
| |
| visitPropertyGet(PropertyGet node) { |
| writeExpression(node.receiver, Precedence.PRIMARY); |
| writeSymbol('.'); |
| writeInterfaceTarget(node.name, node.interfaceTargetReference); |
| } |
| |
| visitPropertySet(PropertySet node) { |
| writeExpression(node.receiver, Precedence.PRIMARY); |
| writeSymbol('.'); |
| writeInterfaceTarget(node.name, node.interfaceTargetReference); |
| writeSpaced('='); |
| writeExpression(node.value); |
| } |
| |
| visitSuperPropertyGet(SuperPropertyGet node) { |
| writeWord('super'); |
| writeSymbol('.'); |
| writeInterfaceTarget(node.name, node.interfaceTargetReference); |
| } |
| |
| visitSuperPropertySet(SuperPropertySet node) { |
| writeWord('super'); |
| writeSymbol('.'); |
| writeInterfaceTarget(node.name, node.interfaceTargetReference); |
| writeSpaced('='); |
| writeExpression(node.value); |
| } |
| |
| visitDirectPropertyGet(DirectPropertyGet node) { |
| writeExpression(node.receiver, Precedence.PRIMARY); |
| writeSymbol('.{='); |
| writeMemberReferenceFromReference(node.targetReference); |
| writeSymbol('}'); |
| } |
| |
| visitDirectPropertySet(DirectPropertySet node) { |
| writeExpression(node.receiver, Precedence.PRIMARY); |
| writeSymbol('.{='); |
| writeMemberReferenceFromReference(node.targetReference); |
| writeSymbol('}'); |
| writeSpaced('='); |
| writeExpression(node.value); |
| } |
| |
| visitStaticGet(StaticGet node) { |
| writeMemberReferenceFromReference(node.targetReference); |
| } |
| |
| visitStaticSet(StaticSet node) { |
| writeMemberReferenceFromReference(node.targetReference); |
| writeSpaced('='); |
| writeExpression(node.value); |
| } |
| |
| visitExpressionStatement(ExpressionStatement node) { |
| writeIndentation(); |
| writeExpression(node.expression); |
| endLine(';'); |
| } |
| |
| void writeBlockBody(List<Statement> statements, {bool asExpression = false}) { |
| if (statements.isEmpty) { |
| asExpression ? writeSymbol('{}') : endLine('{}'); |
| return; |
| } |
| endLine('{'); |
| ++indentation; |
| statements.forEach(writeNode); |
| --indentation; |
| writeIndentation(); |
| asExpression ? writeSymbol('}') : endLine('}'); |
| } |
| |
| visitBlock(Block node) { |
| writeIndentation(); |
| writeBlockBody(node.statements); |
| } |
| |
| visitAssertBlock(AssertBlock node) { |
| writeIndentation(); |
| writeSpaced('assert'); |
| writeBlockBody(node.statements); |
| } |
| |
| visitEmptyStatement(EmptyStatement node) { |
| writeIndentation(); |
| endLine(';'); |
| } |
| |
| visitAssertStatement(AssertStatement node, {bool asExpression = false}) { |
| if (!asExpression) { |
| writeIndentation(); |
| } |
| writeWord('assert'); |
| writeSymbol('('); |
| writeExpression(node.condition); |
| if (node.message != null) { |
| writeComma(); |
| writeExpression(node.message); |
| } |
| if (!asExpression) { |
| endLine(');'); |
| } else { |
| writeSymbol(')'); |
| } |
| } |
| |
| visitLabeledStatement(LabeledStatement node) { |
| writeIndentation(); |
| writeWord(syntheticNames.nameLabeledStatement(node)); |
| endLine(':'); |
| writeNode(node.body); |
| } |
| |
| visitBreakStatement(BreakStatement node) { |
| writeIndentation(); |
| writeWord('break'); |
| writeWord(syntheticNames.nameLabeledStatement(node.target)); |
| endLine(';'); |
| } |
| |
| visitWhileStatement(WhileStatement node) { |
| writeIndentation(); |
| writeSpaced('while'); |
| writeSymbol('('); |
| writeExpression(node.condition); |
| writeSymbol(')'); |
| writeBody(node.body); |
| } |
| |
| visitDoStatement(DoStatement node) { |
| writeIndentation(); |
| writeWord('do'); |
| writeBody(node.body); |
| writeIndentation(); |
| writeSpaced('while'); |
| writeSymbol('('); |
| writeExpression(node.condition); |
| endLine(')'); |
| } |
| |
| visitForStatement(ForStatement node) { |
| writeIndentation(); |
| writeSpaced('for'); |
| writeSymbol('('); |
| writeList(node.variables, writeVariableDeclaration); |
| writeComma(';'); |
| if (node.condition != null) { |
| writeExpression(node.condition); |
| } |
| writeComma(';'); |
| writeList(node.updates, writeExpression); |
| writeSymbol(')'); |
| writeBody(node.body); |
| } |
| |
| visitForInStatement(ForInStatement node) { |
| writeIndentation(); |
| if (node.isAsync) { |
| writeSpaced('await'); |
| } |
| writeSpaced('for'); |
| writeSymbol('('); |
| writeVariableDeclaration(node.variable, useVarKeyword: true); |
| writeSpaced('in'); |
| writeExpression(node.iterable); |
| writeSymbol(')'); |
| writeBody(node.body); |
| } |
| |
| visitSwitchStatement(SwitchStatement node) { |
| writeIndentation(); |
| writeWord('switch'); |
| writeSymbol('('); |
| writeExpression(node.expression); |
| endLine(') {'); |
| ++indentation; |
| node.cases.forEach(writeNode); |
| --indentation; |
| writeIndentation(); |
| endLine('}'); |
| } |
| |
| visitSwitchCase(SwitchCase node) { |
| String label = syntheticNames.nameSwitchCase(node); |
| writeIndentation(); |
| writeWord(label); |
| endLine(':'); |
| for (var expression in node.expressions) { |
| writeIndentation(); |
| writeWord('case'); |
| writeExpression(expression); |
| endLine(':'); |
| } |
| if (node.isDefault) { |
| writeIndentation(); |
| writeWord('default'); |
| endLine(':'); |
| } |
| ++indentation; |
| writeNode(node.body); |
| --indentation; |
| } |
| |
| visitContinueSwitchStatement(ContinueSwitchStatement node) { |
| writeIndentation(); |
| writeWord('continue'); |
| writeWord(syntheticNames.nameSwitchCase(node.target)); |
| endLine(';'); |
| } |
| |
| visitIfStatement(IfStatement node) { |
| writeIndentation(); |
| writeWord('if'); |
| writeSymbol('('); |
| writeExpression(node.condition); |
| writeSymbol(')'); |
| writeBody(node.then); |
| if (node.otherwise != null) { |
| writeIndentation(); |
| writeWord('else'); |
| writeBody(node.otherwise); |
| } |
| } |
| |
| visitReturnStatement(ReturnStatement node) { |
| writeIndentation(); |
| writeWord('return'); |
| if (node.expression != null) { |
| writeSpace(); |
| writeExpression(node.expression); |
| } |
| endLine(';'); |
| } |
| |
| visitTryCatch(TryCatch node) { |
| writeIndentation(); |
| writeWord('try'); |
| writeBody(node.body); |
| node.catches.forEach(writeNode); |
| } |
| |
| visitCatch(Catch node) { |
| writeIndentation(); |
| if (node.guard != null) { |
| writeWord('on'); |
| writeType(node.guard); |
| writeSpace(); |
| } |
| writeWord('catch'); |
| writeSymbol('('); |
| if (node.exception != null) { |
| writeVariableDeclaration(node.exception); |
| } else { |
| writeWord('no-exception-var'); |
| } |
| if (node.stackTrace != null) { |
| writeComma(); |
| writeVariableDeclaration(node.stackTrace); |
| } |
| writeSymbol(')'); |
| writeBody(node.body); |
| } |
| |
| visitTryFinally(TryFinally node) { |
| writeIndentation(); |
| writeWord('try'); |
| writeBody(node.body); |
| writeIndentation(); |
| writeWord('finally'); |
| writeBody(node.finalizer); |
| } |
| |
| visitYieldStatement(YieldStatement node) { |
| writeIndentation(); |
| if (node.isYieldStar) { |
| writeWord('yield*'); |
| } else if (node.isNative) { |
| writeWord('[yield]'); |
| } else { |
| writeWord('yield'); |
| } |
| writeExpression(node.expression); |
| endLine(';'); |
| } |
| |
| visitVariableDeclaration(VariableDeclaration node) { |
| writeIndentation(); |
| writeVariableDeclaration(node, useVarKeyword: true); |
| endLine(';'); |
| } |
| |
| visitFunctionDeclaration(FunctionDeclaration node) { |
| writeAnnotationList(node.variable.annotations); |
| writeIndentation(); |
| writeWord('function'); |
| if (node.function != null) { |
| writeFunction(node.function, name: getVariableName(node.variable)); |
| } else { |
| writeWord(getVariableName(node.variable)); |
| endLine('...;'); |
| } |
| } |
| |
| void writeVariableDeclaration(VariableDeclaration node, |
| {bool useVarKeyword: false}) { |
| if (showOffsets) writeWord("[${node.fileOffset}]"); |
| if (showMetadata) writeMetadata(node); |
| writeAnnotationList(node.annotations, separateLines: false); |
| writeModifier(node.isLate, 'late'); |
| writeModifier(node.isRequired, 'required'); |
| writeModifier(node.isCovariant, 'covariant'); |
| writeModifier(node.isGenericCovariantImpl, 'generic-covariant-impl'); |
| writeModifier(node.isFinal, 'final'); |
| writeModifier(node.isConst, 'const'); |
| if (node.type != null) { |
| writeAnnotatedType(node.type, annotator?.annotateVariable(this, node)); |
| } |
| if (useVarKeyword && !node.isFinal && !node.isConst && node.type == null) { |
| writeWord('var'); |
| } |
| writeWord(getVariableName(node)); |
| if (node.initializer != null) { |
| writeSpaced('='); |
| writeExpression(node.initializer); |
| } |
| } |
| |
| visitArguments(Arguments node) { |
| if (node.types.isNotEmpty) { |
| writeSymbol('<'); |
| writeList(node.types, writeType); |
| writeSymbol('>'); |
| } |
| writeSymbol('('); |
| var allArgs = |
| <List<TreeNode>>[node.positional, node.named].expand((x) => x); |
| writeList(allArgs, writeNode); |
| writeSymbol(')'); |
| } |
| |
| visitNamedExpression(NamedExpression node) { |
| writeWord(node.name); |
| writeComma(':'); |
| writeExpression(node.value); |
| } |
| |
| defaultStatement(Statement node) { |
| writeIndentation(); |
| endLine('${node.runtimeType}'); |
| } |
| |
| visitInvalidInitializer(InvalidInitializer node) { |
| writeWord('invalid-initializer'); |
| } |
| |
| visitFieldInitializer(FieldInitializer node) { |
| writeMemberReferenceFromReference(node.fieldReference); |
| writeSpaced('='); |
| writeExpression(node.value); |
| } |
| |
| visitSuperInitializer(SuperInitializer node) { |
| writeWord('super'); |
| writeMemberReferenceFromReference(node.targetReference); |
| writeNode(node.arguments); |
| } |
| |
| visitRedirectingInitializer(RedirectingInitializer node) { |
| writeWord('this'); |
| writeMemberReferenceFromReference(node.targetReference); |
| writeNode(node.arguments); |
| } |
| |
| visitLocalInitializer(LocalInitializer node) { |
| writeVariableDeclaration(node.variable); |
| } |
| |
| visitAssertInitializer(AssertInitializer node) { |
| visitAssertStatement(node.statement, asExpression: true); |
| } |
| |
| defaultInitializer(Initializer node) { |
| writeIndentation(); |
| endLine(': ${node.runtimeType}'); |
| } |
| |
| void writeNullability(Nullability nullability, {bool inComment = false}) { |
| switch (nullability) { |
| case Nullability.legacy: |
| writeSymbol('*'); |
| if (!inComment) { |
| state = WORD; // Disallow a word immediately after the '*'. |
| } |
| break; |
| case Nullability.nullable: |
| writeSymbol('?'); |
| if (!inComment) { |
| state = WORD; // Disallow a word immediately after the '?'. |
| } |
| break; |
| case Nullability.undetermined: |
| writeSymbol('%'); |
| if (!inComment) { |
| state = WORD; // Disallow a word immediately after the '%'. |
| } |
| break; |
| case Nullability.nonNullable: |
| if (inComment) { |
| writeSymbol("!"); |
| } |
| break; |
| } |
| } |
| |
| void writeDartTypeNullability(DartType type, {bool inComment = false}) { |
| if (type is InvalidType) { |
| writeNullability(Nullability.undetermined); |
| } else { |
| writeNullability(type.nullability, inComment: inComment); |
| } |
| } |
| |
| visitInvalidType(InvalidType node) { |
| writeWord('invalid-type'); |
| } |
| |
| visitDynamicType(DynamicType node) { |
| writeWord('dynamic'); |
| } |
| |
| visitVoidType(VoidType node) { |
| writeWord('void'); |
| } |
| |
| visitNeverType(NeverType node) { |
| writeWord('Never'); |
| writeNullability(node.nullability); |
| } |
| |
| visitInterfaceType(InterfaceType node) { |
| writeClassReferenceFromReference(node.className); |
| if (node.typeArguments.isNotEmpty) { |
| writeSymbol('<'); |
| writeList(node.typeArguments, writeType); |
| writeSymbol('>'); |
| state = WORD; // Disallow a word immediately after the '>'. |
| } |
| writeNullability(node.nullability); |
| } |
| |
| visitFutureOrType(FutureOrType node) { |
| writeWord('FutureOr'); |
| writeSymbol('<'); |
| writeNode(node.typeArgument); |
| writeSymbol('>'); |
| writeNullability(node.declaredNullability); |
| } |
| |
| visitFunctionType(FunctionType node) { |
| writeFunctionType(node); |
| } |
| |
| visitNamedType(NamedType node) { |
| writeModifier(node.isRequired, 'required'); |
| writeWord(node.name); |
| writeSymbol(':'); |
| writeSpace(); |
| writeType(node.type); |
| } |
| |
| visitTypeParameterType(TypeParameterType node) { |
| writeTypeParameterReference(node.parameter); |
| writeNullability(node.declaredNullability); |
| if (node.promotedBound != null) { |
| writeSpaced('&'); |
| writeType(node.promotedBound); |
| |
| writeWord("/* '"); |
| writeNullability(node.declaredNullability, inComment: true); |
| writeWord("' & '"); |
| writeDartTypeNullability(node.promotedBound, inComment: true); |
| writeWord("' = '"); |
| writeNullability(node.nullability, inComment: true); |
| writeWord("' */"); |
| } |
| } |
| |
| visitTypeParameter(TypeParameter node) { |
| writeModifier(node.isGenericCovariantImpl, 'generic-covariant-impl'); |
| writeAnnotationList(node.annotations, separateLines: false); |
| if (node.variance != Variance.covariant) { |
| writeWord(const <String>[ |
| "unrelated", |
| "covariant", |
| "contravariant", |
| "invariant" |
| ][node.variance]); |
| } |
| writeWord(getTypeParameterName(node)); |
| writeSpaced('extends'); |
| writeType(node.bound); |
| if (node.defaultType != null) { |
| writeSpaced('='); |
| writeType(node.defaultType); |
| } |
| } |
| |
| void writeConstantReference(Constant node) { |
| writeWord(syntheticNames.nameConstant(node)); |
| } |
| |
| visitConstantExpression(ConstantExpression node) { |
| writeConstantReference(node.constant); |
| } |
| |
| defaultConstant(Constant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| endLine('${node.runtimeType}'); |
| } |
| |
| visitNullConstant(NullConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| endLine('${node.value}'); |
| } |
| |
| visitBoolConstant(BoolConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| endLine('${node.value}'); |
| } |
| |
| visitIntConstant(IntConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| endLine('${node.value}'); |
| } |
| |
| visitDoubleConstant(DoubleConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| endLine('${node.value}'); |
| } |
| |
| visitSymbolConstant(SymbolConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| String text = node.libraryReference != null |
| ? '#${node.libraryReference.asLibrary.importUri}::${node.name}' |
| : '#${node.name}'; |
| endLine('${text}'); |
| } |
| |
| visitListConstant(ListConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| writeSymbol('<'); |
| writeType(node.typeArgument); |
| writeSymbol('>['); |
| writeList(node.entries, writeConstantReference); |
| endLine(']'); |
| } |
| |
| visitSetConstant(SetConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| writeSymbol('<'); |
| writeType(node.typeArgument); |
| writeSymbol('>{'); |
| writeList(node.entries, writeConstantReference); |
| endLine('}'); |
| } |
| |
| visitMapConstant(MapConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| writeSymbol('<'); |
| writeList([node.keyType, node.valueType], writeType); |
| writeSymbol('>{'); |
| writeList(node.entries, (entry) { |
| writeConstantReference(entry.key); |
| writeSymbol(':'); |
| writeConstantReference(entry.value); |
| }); |
| endLine(')'); |
| } |
| |
| visitTypeLiteralConstant(TypeLiteralConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| writeWord('${node.runtimeType}'); |
| writeSymbol('('); |
| writeNode(node.type); |
| endLine(')'); |
| } |
| |
| visitInstanceConstant(InstanceConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| writeClassReferenceFromReference(node.classReference); |
| if (!node.typeArguments.isEmpty) { |
| writeSymbol('<'); |
| writeList(node.typeArguments, writeType); |
| writeSymbol('>'); |
| } |
| |
| writeSymbol(' {'); |
| writeList(node.fieldValues.entries, |
| (core.MapEntry<Reference, Constant> entry) { |
| if (entry.key.node != null) { |
| writeWord('${entry.key.asField.name.name}'); |
| } else { |
| writeWord('${entry.key.canonicalName.name}'); |
| } |
| writeSymbol(':'); |
| writeConstantReference(entry.value); |
| }); |
| endLine('}'); |
| } |
| |
| visitPartialInstantiationConstant(PartialInstantiationConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| writeWord('partial-instantiation'); |
| writeSpace(); |
| writeMemberReferenceFromReference(node.tearOffConstant.procedureReference); |
| writeSpace(); |
| writeSymbol('<'); |
| writeList(node.types, writeType); |
| writeSymbol('>'); |
| endLine(); |
| } |
| |
| visitStringConstant(StringConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| endLine('"${escapeString(node.value)}"'); |
| } |
| |
| visitTearOffConstant(TearOffConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| writeWord('tearoff'); |
| writeSpace(); |
| writeMemberReferenceFromReference(node.procedureReference); |
| endLine(); |
| } |
| |
| visitUnevaluatedConstant(UnevaluatedConstant node) { |
| writeIndentation(); |
| writeConstantReference(node); |
| writeSpaced('='); |
| writeSymbol('eval'); |
| writeSpace(); |
| writeExpression(node.expression); |
| endLine(); |
| } |
| |
| defaultNode(Node node) { |
| write('<${node.runtimeType}>'); |
| } |
| } |
| |
| class Precedence extends ExpressionVisitor<int> { |
| static final Precedence instance = new Precedence(); |
| |
| static int of(Expression node) => node.accept(instance); |
| |
| static const int EXPRESSION = 1; |
| static const int CONDITIONAL = 2; |
| static const int LOGICAL_NULL_AWARE = 3; |
| static const int LOGICAL_OR = 4; |
| static const int LOGICAL_AND = 5; |
| static const int EQUALITY = 6; |
| static const int RELATIONAL = 7; |
| static const int BITWISE_OR = 8; |
| static const int BITWISE_XOR = 9; |
| static const int BITWISE_AND = 10; |
| static const int SHIFT = 11; |
| static const int ADDITIVE = 12; |
| static const int MULTIPLICATIVE = 13; |
| static const int PREFIX = 14; |
| static const int POSTFIX = 15; |
| static const int TYPE_LITERAL = 19; |
| static const int PRIMARY = 20; |
| static const int CALLEE = 21; |
| |
| static const Map<String, int> binaryPrecedence = const { |
| '&&': LOGICAL_AND, |
| '||': LOGICAL_OR, |
| '??': LOGICAL_NULL_AWARE, |
| '==': EQUALITY, |
| '!=': EQUALITY, |
| '>': RELATIONAL, |
| '>=': RELATIONAL, |
| '<': RELATIONAL, |
| '<=': RELATIONAL, |
| '|': BITWISE_OR, |
| '^': BITWISE_XOR, |
| '&': BITWISE_AND, |
| '>>': SHIFT, |
| '<<': SHIFT, |
| '+': ADDITIVE, |
| '-': ADDITIVE, |
| '*': MULTIPLICATIVE, |
| '%': MULTIPLICATIVE, |
| '/': MULTIPLICATIVE, |
| '~/': MULTIPLICATIVE, |
| null: EXPRESSION, |
| }; |
| |
| static bool isAssociativeBinaryOperator(int precedence) { |
| return precedence != EQUALITY && precedence != RELATIONAL; |
| } |
| |
| int defaultExpression(Expression node) => EXPRESSION; |
| int visitInvalidExpression(InvalidExpression node) => CALLEE; |
| int visitMethodInvocation(MethodInvocation node) => CALLEE; |
| int visitSuperMethodInvocation(SuperMethodInvocation node) => CALLEE; |
| int visitDirectMethodInvocation(DirectMethodInvocation node) => CALLEE; |
| int visitStaticInvocation(StaticInvocation node) => CALLEE; |
| int visitConstructorInvocation(ConstructorInvocation node) => CALLEE; |
| int visitNot(Not node) => PREFIX; |
| int visitNullCheck(NullCheck node) => PRIMARY; |
| int visitLogicalExpression(LogicalExpression node) => |
| binaryPrecedence[node.operator]; |
| int visitConditionalExpression(ConditionalExpression node) => CONDITIONAL; |
| int visitStringConcatenation(StringConcatenation node) => PRIMARY; |
| int visitIsExpression(IsExpression node) => RELATIONAL; |
| int visitAsExpression(AsExpression node) => RELATIONAL; |
| int visitSymbolLiteral(SymbolLiteral node) => PRIMARY; |
| int visitTypeLiteral(TypeLiteral node) => PRIMARY; |
| int visitThisExpression(ThisExpression node) => CALLEE; |
| int visitRethrow(Rethrow node) => PRIMARY; |
| int visitThrow(Throw node) => EXPRESSION; |
| int visitListLiteral(ListLiteral node) => PRIMARY; |
| int visitSetLiteral(SetLiteral node) => PRIMARY; |
| int visitMapLiteral(MapLiteral node) => PRIMARY; |
| int visitAwaitExpression(AwaitExpression node) => PREFIX; |
| int visitFunctionExpression(FunctionExpression node) => EXPRESSION; |
| int visitStringLiteral(StringLiteral node) => CALLEE; |
| int visitIntLiteral(IntLiteral node) => CALLEE; |
| int visitDoubleLiteral(DoubleLiteral node) => CALLEE; |
| int visitBoolLiteral(BoolLiteral node) => CALLEE; |
| int visitNullLiteral(NullLiteral node) => CALLEE; |
| int visitVariableGet(VariableGet node) => PRIMARY; |
| int visitVariableSet(VariableSet node) => EXPRESSION; |
| int visitPropertyGet(PropertyGet node) => PRIMARY; |
| int visitPropertySet(PropertySet node) => EXPRESSION; |
| int visitSuperPropertyGet(SuperPropertyGet node) => PRIMARY; |
| int visitSuperPropertySet(SuperPropertySet node) => EXPRESSION; |
| int visitDirectPropertyGet(DirectPropertyGet node) => PRIMARY; |
| int visitDirectPropertySet(DirectPropertySet node) => EXPRESSION; |
| int visitStaticGet(StaticGet node) => PRIMARY; |
| int visitStaticSet(StaticSet node) => EXPRESSION; |
| int visitLet(Let node) => EXPRESSION; |
| } |
| |
| String procedureKindToString(ProcedureKind kind) { |
| switch (kind) { |
| case ProcedureKind.Method: |
| return 'method'; |
| case ProcedureKind.Getter: |
| return 'get'; |
| case ProcedureKind.Setter: |
| return 'set'; |
| case ProcedureKind.Operator: |
| return 'operator'; |
| case ProcedureKind.Factory: |
| return 'factory'; |
| } |
| throw 'illegal ProcedureKind: $kind'; |
| } |