| // Copyright (c) 2020, 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 'dart:io' show File; |
| import 'dart:typed_data' show Uint8List; |
| |
| import 'package:_fe_analyzer_shared/src/experiments/flags.dart'; |
| import 'package:_fe_analyzer_shared/src/messages/codes.dart'; |
| import 'package:_fe_analyzer_shared/src/parser/experimental_features.dart'; |
| import 'package:_fe_analyzer_shared/src/parser/identifier_context.dart'; |
| import 'package:_fe_analyzer_shared/src/parser/listener.dart' |
| show UnescapeErrorListener; |
| import 'package:_fe_analyzer_shared/src/parser/parser.dart' |
| show ClassMemberParser, Parser; |
| import 'package:_fe_analyzer_shared/src/parser/quote.dart' show unescapeString; |
| import 'package:_fe_analyzer_shared/src/scanner/scanner.dart' |
| show ScannerConfiguration, ScannerResult, scan; |
| import 'package:_fe_analyzer_shared/src/scanner/token.dart' show Token; |
| |
| import '../source/diet_parser.dart'; |
| import 'parser_ast_helper.dart'; |
| |
| // TODO(jensj): Possibly all the enableX bools should be replaced by an |
| // "assumed version" (from package config probably) which is then updated if |
| // a language version is seen which will then implicitly answer these questions. |
| CompilationUnitEnd getAST( |
| Uint8List rawBytes, { |
| bool includeBody = true, |
| bool includeComments = false, |
| ExperimentalFeatures experimentalFeatures = |
| const DefaultExperimentalFeatures(), |
| List<Token>? languageVersionsSeen, |
| List<int>? lineStarts, |
| }) { |
| ScannerConfiguration scannerConfiguration = new ScannerConfiguration( |
| enableTripleShift: experimentalFeatures.isExperimentEnabled( |
| ExperimentalFlag.tripleShift, |
| ), |
| ); |
| |
| ScannerResult scanResult = scan( |
| rawBytes, |
| includeComments: includeComments, |
| configuration: scannerConfiguration, |
| languageVersionChanged: (scanner, languageVersion) { |
| // Coverage-ignore-block(suite): Not run. |
| // For now don't do anything, but having it (making it non-null) means the |
| // configuration won't be reset. |
| languageVersionsSeen?.add(languageVersion); |
| // TODO(jensj): Should we perhaps update "allowPatterns" here? E.g. if |
| // on or after ExperimentalFlag.patterns.enabledVersion or something |
| }, |
| ); |
| |
| Token firstToken = scanResult.tokens; |
| if (lineStarts != null) { |
| // Coverage-ignore-block(suite): Not run. |
| lineStarts.addAll(scanResult.lineStarts); |
| } |
| ParserASTListener listener = new ParserASTListener(); |
| Parser parser; |
| if (includeBody) { |
| parser = new Parser( |
| listener, |
| useImplicitCreationExpression: useImplicitCreationExpressionInCfe, |
| experimentalFeatures: experimentalFeatures, |
| ); |
| } else { |
| parser = new ClassMemberParser( |
| listener, |
| useImplicitCreationExpression: useImplicitCreationExpressionInCfe, |
| experimentalFeatures: experimentalFeatures, |
| ); |
| } |
| parser.parseUnit(firstToken); |
| return listener.data.single as CompilationUnitEnd; |
| } |
| |
| /// Recursive Parser AST Visitor that ignores (and thus doesn't recursively |
| /// visit) a few classes for compatibility with the previous |
| /// on-the-side-visitor. For instance visiting all the nodes for |
| /// ``` |
| /// @Const() |
| /// extension Extension<@Const() T> on Class<T> { |
| /// } |
| /// ``` |
| /// will visit the first metadata, then the type variables which itself has the |
| /// second metadata, only then it visits the extension - which old "visitor" |
| /// code doesn't handle. This visitor for instance ignores the type variables |
| /// to "fix" this case. |
| class IgnoreSomeForCompatibilityAstVisitor extends RecursiveParserAstVisitor { |
| @override |
| void visitTypeVariablesEnd(TypeVariablesEnd node) { |
| // Ignored |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void visitTypeArgumentsEnd(TypeArgumentsEnd node) { |
| // Ignored |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void visitTypeListEnd(TypeListEnd node) { |
| // Ignored |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void visitFunctionTypeEnd(FunctionTypeEnd node) { |
| // Ignored |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void visitBlockEnd(BlockEnd node) { |
| // Ignored |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| /// Best-effort visitor for ParserAstNode that visits top-level entries |
| /// and class members only (i.e. no bodies, no field initializer content, no |
| /// names etc). |
| class BestEffortParserAstVisitor { |
| void accept(ParserAstNode node) { |
| if (node is CompilationUnitEnd || |
| node is TopLevelDeclarationEnd || |
| node is ClassOrMixinOrExtensionBodyEnd || |
| node is MemberEnd) { |
| visitChildren(node); |
| return; |
| } |
| |
| if (node.type == ParserAstType.BEGIN) { |
| // Ignored. These are basically just dummy nodes anyway. |
| assert(node.children == null); |
| return; |
| } |
| if (node.type == ParserAstType.HANDLE) { |
| // Ignored at least for know. |
| assert(node.children == null); |
| return; |
| } |
| if (node is TypeVariablesEnd || |
| node is TypeArgumentsEnd || |
| node is TypeListEnd || |
| node is FunctionTypeEnd || |
| node is BlockEnd) { |
| // Ignored at least for know. |
| return; |
| } |
| if (node is MetadataStarEnd) { |
| MetadataStarEnd metadata = node; |
| visitMetadataStar(metadata); |
| return; |
| } |
| if (node is TypedefEnd) { |
| TypedefEnd typedefDecl = node; |
| visitTypedef( |
| typedefDecl, |
| typedefDecl.typedefKeyword, |
| typedefDecl.endToken, |
| ); |
| return; |
| } |
| if (node is ClassDeclarationEnd) { |
| ClassDeclarationEnd cls = node; |
| visitClass(cls, cls.beginToken, cls.endToken); |
| return; |
| } |
| if (node is TopLevelMethodEnd) { |
| TopLevelMethodEnd method = node; |
| visitTopLevelMethod(method, method.beginToken, method.endToken); |
| return; |
| } |
| if (node is MethodEnd) { |
| MethodEnd method = node; |
| visitMethod(method, method.beginToken, method.endToken); |
| return; |
| } |
| if (node is ImportEnd) { |
| ImportEnd import = node; |
| visitImport(import, import.importKeyword, import.semicolon); |
| return; |
| } |
| if (node is ExportEnd) { |
| ExportEnd export = node; |
| visitExport(export, export.exportKeyword, export.semicolon); |
| return; |
| } |
| if (node is TopLevelFieldsEnd) { |
| // TODO(jensj): Possibly this could go into more details too |
| // (e.g. to split up a field declaration). |
| TopLevelFieldsEnd fields = node; |
| visitTopLevelFields(fields, fields.beginToken, fields.endToken); |
| return; |
| } |
| if (node is FieldsEnd) { |
| // TODO(jensj): Possibly this could go into more details too |
| // (e.g. to split up a field declaration). |
| FieldsEnd fields = node; |
| visitFields(fields, fields.beginToken, fields.endToken); |
| return; |
| } |
| if (node is NamedMixinApplicationEnd) { |
| NamedMixinApplicationEnd namedMixin = node; |
| visitNamedMixin(namedMixin, namedMixin.begin, namedMixin.endToken); |
| return; |
| } |
| if (node is MixinDeclarationEnd) { |
| MixinDeclarationEnd declaration = node; |
| visitMixin(declaration, declaration.beginToken, declaration.endToken); |
| return; |
| } |
| if (node is EnumDeclarationEnd) { |
| EnumDeclarationEnd declaration = node; |
| visitEnum( |
| declaration, |
| declaration.enumKeyword, |
| declaration.leftBrace.endGroup!, |
| ); |
| return; |
| } |
| if (node is LibraryNameEnd) { |
| LibraryNameEnd name = node; |
| visitLibraryName(name, name.libraryKeyword, name.semicolon); |
| return; |
| } |
| if (node is PartEnd) { |
| PartEnd part = node; |
| visitPart(part, part.partKeyword, part.semicolon); |
| return; |
| } |
| if (node is PartOfEnd) { |
| PartOfEnd partOf = node; |
| visitPartOf(partOf, partOf.partKeyword, partOf.semicolon); |
| return; |
| } |
| if (node is ExtensionDeclarationEnd) { |
| ExtensionDeclarationEnd ext = node; |
| visitExtension(ext, ext.beginToken, ext.endToken); |
| return; |
| } |
| if (node is ExtensionTypeDeclarationEnd) { |
| ExtensionTypeDeclarationEnd ext = node; |
| visitExtensionTypeDeclaration(ext, ext.extensionKeyword, ext.endToken); |
| return; |
| } |
| if (node is ConstructorEnd) { |
| ConstructorEnd decl = node; |
| visitConstructor(decl, decl.beginToken, decl.endToken); |
| return; |
| } |
| if (node is FactoryEnd) { |
| FactoryEnd decl = node; |
| visitFactory(decl, decl.beginToken, decl.endToken); |
| return; |
| } |
| if (node is MetadataEnd) { |
| MetadataEnd decl = node; |
| visitMetadata(decl, decl.beginToken, decl.endToken); |
| return; |
| } |
| |
| throw "Unknown: $node (${node.runtimeType} @ ${node.what})"; |
| } |
| |
| void visitChildren(ParserAstNode node) { |
| if (node.children == null) return; |
| final int numChildren = node.children!.length; |
| for (int i = 0; i < numChildren; i++) { |
| ParserAstNode child = node.children![i]; |
| accept(child); |
| } |
| } |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitImport(ImportEnd node, Token startInclusive, Token? endInclusive) {} |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitExport(ExportEnd node, Token startInclusive, Token endInclusive) {} |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitTypedef( |
| TypedefEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) {} |
| |
| /// Note: Implementers can call visitChildren on this node. |
| void visitMetadataStar(MetadataStarEnd node) { |
| visitChildren(node); |
| } |
| |
| /// Note: Implementers can call visitChildren on this node. |
| void visitClass( |
| ClassDeclarationEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) { |
| visitChildren(node); |
| } |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitTopLevelMethod( |
| TopLevelMethodEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) {} |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitMethod(MethodEnd node, Token startInclusive, Token endInclusive) {} |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitTopLevelFields( |
| TopLevelFieldsEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) {} |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitFields(FieldsEnd node, Token startInclusive, Token endInclusive) {} |
| |
| /// Note: Implementers can call visitChildren on this node. |
| void visitNamedMixin( |
| NamedMixinApplicationEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) { |
| visitChildren(node); |
| } |
| |
| /// Note: Implementers can call visitChildren on this node. |
| void visitMixin( |
| MixinDeclarationEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) { |
| visitChildren(node); |
| } |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitEnum( |
| EnumDeclarationEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) {} |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitLibraryName( |
| LibraryNameEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) {} |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitPart(PartEnd node, Token startInclusive, Token endInclusive) {} |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitPartOf(PartOfEnd node, Token startInclusive, Token endInclusive) {} |
| |
| /// Note: Implementers can call visitChildren on this node. |
| void visitExtension( |
| ExtensionDeclarationEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) { |
| visitChildren(node); |
| } |
| |
| /// Note: Implementers can call visitChildren on this node. |
| void visitExtensionTypeDeclaration( |
| ExtensionTypeDeclarationEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) { |
| visitChildren(node); |
| } |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitConstructor( |
| ConstructorEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) {} |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitFactory( |
| FactoryEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) {} |
| |
| /// Note: Implementers are NOT expected to call visitChildren on this node. |
| void visitMetadata( |
| MetadataEnd node, |
| Token startInclusive, |
| Token endInclusive, |
| ) {} |
| } |
| |
| enum MemberContentType { |
| ClassRecoverableError, |
| Constructor, |
| ExperimentNotEnabled, |
| Factory, |
| Fields, |
| Method, |
| Unknown, |
| } |
| |
| enum GeneralAstContentType { |
| Class, |
| Import, |
| Export, |
| Unknown, |
| Enum, |
| Typedef, |
| Script, |
| Extension, |
| ExtensionType, |
| InvalidTopLevelDeclaration, |
| RecoverableError, |
| RecoverImport, |
| MixinDeclaration, |
| NamedMixinDeclaration, |
| TopLevelMethod, |
| TopLevelFields, |
| LibraryName, |
| Part, |
| PartOf, |
| Metadata, |
| FunctionBody, |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension GeneralASTContentExtension on ParserAstNode { |
| // TODO(jensj): This might not actually be useful - we're doing a lot of if's |
| // here, but will then have to do more if's or a switch at the call site to |
| // do anything useful with it, which might not be optimal. |
| GeneralAstContentType getType() { |
| if (isClass()) return GeneralAstContentType.Class; |
| if (isImport()) return GeneralAstContentType.Import; |
| if (isExport()) return GeneralAstContentType.Export; |
| if (isExport()) return GeneralAstContentType.Export; |
| if (isEnum()) return GeneralAstContentType.Enum; |
| if (isTypedef()) return GeneralAstContentType.Typedef; |
| if (isScript()) return GeneralAstContentType.Script; |
| if (isExtension()) return GeneralAstContentType.Extension; |
| if (isExtensionType()) return GeneralAstContentType.ExtensionType; |
| if (isInvalidTopLevelDeclaration()) { |
| return GeneralAstContentType.InvalidTopLevelDeclaration; |
| } |
| if (isRecoverableError()) return GeneralAstContentType.RecoverableError; |
| if (isRecoverImport()) return GeneralAstContentType.RecoverImport; |
| if (isMixinDeclaration()) return GeneralAstContentType.MixinDeclaration; |
| if (isNamedMixinDeclaration()) { |
| return GeneralAstContentType.NamedMixinDeclaration; |
| } |
| if (isTopLevelMethod()) return GeneralAstContentType.TopLevelMethod; |
| if (isTopLevelFields()) return GeneralAstContentType.TopLevelFields; |
| if (isLibraryName()) return GeneralAstContentType.LibraryName; |
| if (isPart()) return GeneralAstContentType.Part; |
| if (isPartOf()) return GeneralAstContentType.PartOf; |
| if (isMetadata()) return GeneralAstContentType.Metadata; |
| if (isFunctionBody()) return GeneralAstContentType.FunctionBody; |
| return GeneralAstContentType.Unknown; |
| } |
| |
| bool isClass() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first |
| // ignore: lines_longer_than_80_chars |
| is! ClassOrMixinOrNamedMixinApplicationPreludeBegin) { |
| return false; |
| } |
| if (children!.last is! ClassDeclarationEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| ClassDeclarationEnd asClass() { |
| if (!isClass()) throw "Not class"; |
| return children!.last as ClassDeclarationEnd; |
| } |
| |
| bool isImport() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! UncategorizedTopLevelDeclarationBegin) { |
| return false; |
| } |
| if (children!.last is! ImportEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| ImportEnd asImport() { |
| if (!isImport()) throw "Not import"; |
| return children!.last as ImportEnd; |
| } |
| |
| bool isExport() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! UncategorizedTopLevelDeclarationBegin) { |
| return false; |
| } |
| if (children!.last is! ExportEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| ExportEnd asExport() { |
| if (!isExport()) throw "Not export"; |
| return children!.last as ExportEnd; |
| } |
| |
| bool isEnum() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! EnumDeclarationPreludeBegin) { |
| return false; |
| } |
| if (children!.last is! EnumDeclarationEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| EnumDeclarationEnd asEnum() { |
| if (!isEnum()) throw "Not enum"; |
| return children!.last as EnumDeclarationEnd; |
| } |
| |
| bool isTypedef() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! UncategorizedTopLevelDeclarationBegin) { |
| return false; |
| } |
| if (children!.last is! TypedefEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| TypedefEnd asTypedef() { |
| if (!isTypedef()) throw "Not typedef"; |
| return children!.last as TypedefEnd; |
| } |
| |
| bool isScript() { |
| if (this is! ScriptHandle) { |
| return false; |
| } |
| return true; |
| } |
| |
| ScriptHandle asScript() { |
| if (!isScript()) throw "Not script"; |
| return this as ScriptHandle; |
| } |
| |
| bool isExtension() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! ExtensionDeclarationPreludeBegin) { |
| return false; |
| } |
| if (children!.last is! ExtensionDeclarationEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| ExtensionDeclarationEnd asExtension() { |
| if (!isExtension()) throw "Not extension"; |
| return children!.last as ExtensionDeclarationEnd; |
| } |
| |
| bool isExtensionType() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! ExtensionDeclarationPreludeBegin) { |
| return false; |
| } |
| if (children!.last is! ExtensionTypeDeclarationEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| ExtensionTypeDeclarationEnd asExtensionType() { |
| if (!isExtensionType()) throw "Not extension type"; |
| return children!.last as ExtensionTypeDeclarationEnd; |
| } |
| |
| bool isInvalidTopLevelDeclaration() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! TopLevelMemberBegin) { |
| return false; |
| } |
| if (children!.last is! InvalidTopLevelDeclarationHandle) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool isRecoverableError() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! UncategorizedTopLevelDeclarationBegin) { |
| return false; |
| } |
| if (children!.last is! RecoverableErrorHandle) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool isRecoverImport() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! UncategorizedTopLevelDeclarationBegin) { |
| return false; |
| } |
| if (children!.last is! RecoverImportHandle) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool isMixinDeclaration() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first |
| // ignore: lines_longer_than_80_chars |
| is! ClassOrMixinOrNamedMixinApplicationPreludeBegin) { |
| return false; |
| } |
| if (children!.last is! MixinDeclarationEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| MixinDeclarationEnd asMixinDeclaration() { |
| if (!isMixinDeclaration()) throw "Not mixin declaration"; |
| return children!.last as MixinDeclarationEnd; |
| } |
| |
| bool isNamedMixinDeclaration() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first |
| // ignore: lines_longer_than_80_chars |
| is! ClassOrMixinOrNamedMixinApplicationPreludeBegin) { |
| return false; |
| } |
| if (children!.last is! NamedMixinApplicationEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| NamedMixinApplicationEnd asNamedMixinDeclaration() { |
| if (!isNamedMixinDeclaration()) throw "Not named mixin declaration"; |
| return children!.last as NamedMixinApplicationEnd; |
| } |
| |
| bool isTopLevelMethod() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! TopLevelMemberBegin) { |
| return false; |
| } |
| if (children!.last is! TopLevelMethodEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| TopLevelMethodEnd asTopLevelMethod() { |
| if (!isTopLevelMethod()) throw "Not top level method"; |
| return children!.last as TopLevelMethodEnd; |
| } |
| |
| bool isTopLevelFields() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! TopLevelMemberBegin) { |
| return false; |
| } |
| if (children!.last is! TopLevelFieldsEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| TopLevelFieldsEnd asTopLevelFields() { |
| if (!isTopLevelFields()) throw "Not top level fields"; |
| return children!.last as TopLevelFieldsEnd; |
| } |
| |
| bool isLibraryName() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! UncategorizedTopLevelDeclarationBegin) { |
| return false; |
| } |
| if (children!.last is! LibraryNameEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| LibraryNameEnd asLibraryName() { |
| if (!isLibraryName()) throw "Not library name"; |
| return children!.last as LibraryNameEnd; |
| } |
| |
| bool isPart() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! UncategorizedTopLevelDeclarationBegin) { |
| return false; |
| } |
| if (children!.last is! PartEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| PartEnd asPart() { |
| if (!isPart()) throw "Not part"; |
| return children!.last as PartEnd; |
| } |
| |
| bool isPartOf() { |
| if (this is! TopLevelDeclarationEnd) { |
| return false; |
| } |
| if (children!.first is! UncategorizedTopLevelDeclarationBegin) { |
| return false; |
| } |
| if (children!.last is! PartOfEnd) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| PartOfEnd asPartOf() { |
| if (!isPartOf()) throw "Not part of"; |
| return children!.last as PartOfEnd; |
| } |
| |
| bool isMetadata() { |
| if (this is! MetadataStarEnd) { |
| return false; |
| } |
| if (children!.first is! MetadataStarBegin) { |
| return false; |
| } |
| return true; |
| } |
| |
| MetadataStarEnd asMetadata() { |
| if (!isMetadata()) throw "Not metadata"; |
| return this as MetadataStarEnd; |
| } |
| |
| bool isFunctionBody() { |
| if (this is BlockFunctionBodyEnd) return true; |
| return false; |
| } |
| |
| BlockFunctionBodyEnd asFunctionBody() { |
| if (!isFunctionBody()) throw "Not function body"; |
| return this as BlockFunctionBodyEnd; |
| } |
| |
| List<E> recursivelyFind<E extends ParserAstNode>() { |
| Set<E> result = {}; |
| _recursivelyFindInternal(this, result); |
| return result.toList(); |
| } |
| |
| static void _recursivelyFindInternal<E extends ParserAstNode>( |
| ParserAstNode node, |
| Set<E> result, |
| ) { |
| if (node is E) { |
| result.add(node); |
| return; |
| } |
| if (node.children == null) return; |
| for (ParserAstNode child in node.children!) { |
| _recursivelyFindInternal(child, result); |
| } |
| } |
| |
| void debugDumpNodeRecursively({String indent = ""}) { |
| print( |
| "$indent${runtimeType} (${what}) " |
| "(${deprecatedArguments})", |
| ); |
| if (children == null) return; |
| for (ParserAstNode child in children!) { |
| child.debugDumpNodeRecursively(indent: " $indent"); |
| } |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension MetadataStarExtension on MetadataStarEnd { |
| List<MetadataEnd> getMetadataEntries() { |
| List<MetadataEnd> result = []; |
| for (ParserAstNode topLevel in children!) { |
| if (topLevel is! MetadataEnd) continue; |
| result.add(topLevel); |
| } |
| return result; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension MetadataExtension on MetadataEnd { |
| List<IdentifierHandle> getIdentifiers() { |
| List<IdentifierHandle> result = []; |
| for (ParserAstNode child in children!) { |
| if (child is IdentifierHandle) { |
| result.add(child); |
| } |
| } |
| return result; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension CompilationUnitExtension on CompilationUnitEnd { |
| List<TopLevelDeclarationEnd> getClasses() { |
| List<TopLevelDeclarationEnd> result = []; |
| for (ParserAstNode topLevel in children!) { |
| if (!topLevel.isClass()) continue; |
| result.add(topLevel as TopLevelDeclarationEnd); |
| } |
| return result; |
| } |
| |
| List<TopLevelDeclarationEnd> getMixinDeclarations() { |
| List<TopLevelDeclarationEnd> result = []; |
| for (ParserAstNode topLevel in children!) { |
| if (!topLevel.isMixinDeclaration()) continue; |
| result.add(topLevel as TopLevelDeclarationEnd); |
| } |
| return result; |
| } |
| |
| List<ImportEnd> getImports() { |
| List<ImportEnd> result = []; |
| for (ParserAstNode topLevel in children!) { |
| if (!topLevel.isImport()) continue; |
| result.add(topLevel.children!.last as ImportEnd); |
| } |
| return result; |
| } |
| |
| List<ExportEnd> getExports() { |
| List<ExportEnd> result = []; |
| for (ParserAstNode topLevel in children!) { |
| if (!topLevel.isExport()) continue; |
| result.add(topLevel.children!.last as ExportEnd); |
| } |
| return result; |
| } |
| |
| // List<MetadataStarEnd> getMetadata() { |
| // List<MetadataStarEnd> result = []; |
| // for (ParserAstNode topLevel in children) { |
| // if (!topLevel.isMetadata()) continue; |
| // result.add(topLevel); |
| // } |
| // return result; |
| // } |
| |
| // List<EnumEnd> getEnums() { |
| // List<EnumEnd> result = []; |
| // for (ParserAstNode topLevel in children) { |
| // if (!topLevel.isEnum()) continue; |
| // result.add(topLevel.children.last); |
| // } |
| // return result; |
| // } |
| |
| // List<FunctionTypeAliasEnd> getTypedefs() { |
| // List<FunctionTypeAliasEnd> result = []; |
| // for (ParserAstNode topLevel in children) { |
| // if (!topLevel.isTypedef()) continue; |
| // result.add(topLevel.children.last); |
| // } |
| // return result; |
| // } |
| |
| // List<MixinDeclarationEnd> getMixinDeclarations() { |
| // List<MixinDeclarationEnd> result = []; |
| // for (ParserAstNode topLevel in children) { |
| // if (!topLevel.isMixinDeclaration()) continue; |
| // result.add(topLevel.children.last); |
| // } |
| // return result; |
| // } |
| |
| // List<TopLevelMethodEnd> getTopLevelMethods() { |
| // List<TopLevelMethodEnd> result = []; |
| // for (ParserAstNode topLevel in children) { |
| // if (!topLevel.isTopLevelMethod()) continue; |
| // result.add(topLevel.children.last); |
| // } |
| // return result; |
| // } |
| |
| CompilationUnitBegin getBegin() { |
| return children!.first as CompilationUnitBegin; |
| } |
| } |
| |
| extension TopLevelDeclarationExtension on TopLevelDeclarationEnd { |
| IdentifierHandle getIdentifier() { |
| for (ParserAstNode child in children!) { |
| if (child is IdentifierHandle) return child; |
| } |
| throw "Not found."; |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| ClassDeclarationEnd getClassDeclaration() { |
| if (!isClass()) { |
| throw "Not a class"; |
| } |
| for (ParserAstNode child in children!) { |
| if (child is ClassDeclarationEnd) { |
| return child; |
| } |
| } |
| throw "Not found."; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension MixinDeclarationExtension on MixinDeclarationEnd { |
| ClassOrMixinOrExtensionBodyEnd getClassOrMixinOrExtensionBody() { |
| for (ParserAstNode child in children!) { |
| if (child is ClassOrMixinOrExtensionBodyEnd) { |
| return child; |
| } |
| } |
| throw "Not found."; |
| } |
| |
| IdentifierHandle getMixinIdentifier() { |
| ParserAstNode? parent = this.parent; |
| if (parent is! TopLevelDeclarationEnd) throw "Now nested as expected"; |
| return parent.getIdentifier(); |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension NamedMixinApplicationExtension on NamedMixinApplicationEnd { |
| IdentifierHandle getMixinIdentifier() { |
| ParserAstNode? parent = this.parent; |
| if (parent is! TopLevelDeclarationEnd) throw "Now nested as expected"; |
| return parent.getIdentifier(); |
| } |
| } |
| |
| extension ClassDeclarationExtension on ClassDeclarationEnd { |
| // Coverage-ignore(suite): Not run. |
| ClassOrMixinOrExtensionBodyEnd? getClassOrMixinOrExtensionBody() { |
| for (ParserAstNode child in children!) { |
| if (child is ClassOrMixinOrExtensionBodyEnd) { |
| return child; |
| } |
| } |
| return null; |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| NoClassBodyHandle? getNoClassBody() { |
| for (ParserAstNode child in children!) { |
| if (child is NoClassBodyHandle) { |
| return child; |
| } |
| } |
| return null; |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| ClassExtendsHandle getClassExtends() { |
| for (ParserAstNode child in children!) { |
| if (child is ClassExtendsHandle) return child; |
| } |
| throw "Not found."; |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| ImplementsHandle getClassImplements() { |
| for (ParserAstNode child in children!) { |
| if (child is ImplementsHandle) { |
| return child; |
| } |
| } |
| throw "Not found."; |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| ClassWithClauseHandle? getClassWithClause() { |
| for (ParserAstNode child in children!) { |
| if (child is ClassWithClauseHandle) { |
| return child; |
| } |
| } |
| return null; |
| } |
| |
| IdentifierHandle getClassIdentifier() { |
| ParserAstNode? parent = this.parent; |
| if (parent is! TopLevelDeclarationEnd) throw "Now nested as expected"; |
| return parent.getIdentifier(); |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension ClassOrMixinBodyExtension on ClassOrMixinOrExtensionBodyEnd? { |
| List<MemberEnd> getMembers() { |
| List<MemberEnd> members = []; |
| List<ParserAstNode>? children = this?.children; |
| if (children != null) { |
| for (ParserAstNode child in children) { |
| if (child is MemberEnd) { |
| members.add(child); |
| } |
| } |
| } |
| return members; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension MemberExtension on MemberEnd { |
| // TODO(jensj): This might not actually be useful - we're doing a lot of if's |
| // here, but will then have to do more if's or a switch at the call site to |
| // do anything useful with it, which might not be optimal. |
| MemberContentType getMemberType() { |
| if (isConstructor()) return MemberContentType.Constructor; |
| if (isFactory()) return MemberContentType.Factory; |
| if (isFields()) return MemberContentType.Fields; |
| if (isMethod()) return MemberContentType.Method; |
| |
| if (isClassRecoverableError()) { |
| return MemberContentType.ClassRecoverableError; |
| } |
| if (isExperimentNotEnabled()) return MemberContentType.ExperimentNotEnabled; |
| |
| return MemberContentType.Unknown; |
| } |
| |
| bool isConstructor() { |
| ParserAstNode child = children![1]; |
| if (child is ConstructorEnd) return true; |
| return false; |
| } |
| |
| ConstructorEnd getConstructor() { |
| ParserAstNode child = children![1]; |
| if (child is ConstructorEnd) return child; |
| throw "Not found"; |
| } |
| |
| bool isFactory() { |
| ParserAstNode child = children![1]; |
| if (child is FactoryEnd) return true; |
| return false; |
| } |
| |
| FactoryEnd getFactory() { |
| ParserAstNode child = children![1]; |
| if (child is FactoryEnd) return child; |
| throw "Not found"; |
| } |
| |
| bool isFields() { |
| ParserAstNode child = children![1]; |
| if (child is FieldsEnd) return true; |
| return false; |
| } |
| |
| FieldsEnd getFields() { |
| ParserAstNode child = children![1]; |
| if (child is FieldsEnd) return child; |
| throw "Not found"; |
| } |
| |
| bool isMethod() { |
| ParserAstNode child = children![1]; |
| if (child is MethodEnd) return true; |
| return false; |
| } |
| |
| MethodEnd getMethod() { |
| ParserAstNode child = children![1]; |
| if (child is MethodEnd) return child; |
| throw "Not found"; |
| } |
| |
| bool isClassRecoverableError() { |
| ParserAstNode child = children![1]; |
| if (child is RecoverableErrorHandle) return true; |
| return false; |
| } |
| |
| bool isExperimentNotEnabled() { |
| ParserAstNode child = children![1]; |
| if (child is ExperimentNotEnabledHandle) return true; |
| return false; |
| } |
| |
| bool isPrimaryConstructorBody() { |
| ParserAstNode child = children![1]; |
| if (child is PrimaryConstructorBodyEnd) return true; |
| return false; |
| } |
| |
| PrimaryConstructorBodyEnd getPrimaryConstructorBody() { |
| ParserAstNode child = children![1]; |
| if (child is PrimaryConstructorBodyEnd) return child; |
| throw "Not found"; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension FieldsExtension on FieldsEnd { |
| List<IdentifierHandle> getFieldIdentifiers() { |
| int countLeft = count; |
| List<IdentifierHandle>? identifiers; |
| for (int i = children!.length - 1; i >= 0; i--) { |
| ParserAstNode child = children![i]; |
| if (child is IdentifierHandle && |
| child.context == IdentifierContext.fieldDeclaration) { |
| countLeft--; |
| if (identifiers == null) { |
| identifiers = new List<IdentifierHandle>.filled(count, child); |
| } else { |
| identifiers[countLeft] = child; |
| } |
| if (countLeft == 0) break; |
| } |
| } |
| if (countLeft != 0) throw "Didn't find the expected number of identifiers"; |
| return identifiers ?? []; |
| } |
| |
| TypeHandle? getFirstType() { |
| for (ParserAstNode child in children!) { |
| if (child is TypeHandle) return child; |
| } |
| return null; |
| } |
| |
| FieldInitializerEnd? getFieldInitializer() { |
| for (ParserAstNode child in children!) { |
| if (child is FieldInitializerEnd) return child; |
| } |
| return null; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension EnumDeclarationExtension on EnumDeclarationEnd { |
| List<IdentifierHandle> getIdentifiers() { |
| List<IdentifierHandle> ids = []; |
| for (ParserAstNode child in children!) { |
| if (child is IdentifierHandle) ids.add(child); |
| } |
| return ids; |
| } |
| |
| IdentifierHandle getEnumIdentifier() { |
| ParserAstNode? parent = this.parent; |
| if (parent is! TopLevelDeclarationEnd) throw "Not nested as expected"; |
| return parent.getIdentifier(); |
| } |
| |
| List<MemberEnd> getMembers() { |
| List<MemberEnd> members = []; |
| for (ParserAstNode child in children!) { |
| if (child is MemberEnd) { |
| members.add(child); |
| } |
| } |
| return members; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension ExtensionDeclarationExtension on ExtensionDeclarationEnd { |
| List<IdentifierHandle> getIdentifiers() { |
| List<IdentifierHandle> ids = []; |
| for (ParserAstNode child in children!) { |
| if (child is IdentifierHandle) ids.add(child); |
| } |
| return ids; |
| } |
| |
| Token? getExtensionName() { |
| ExtensionDeclarationBegin begin = |
| children!.first as ExtensionDeclarationBegin; |
| return begin.name; |
| } |
| |
| ClassOrMixinOrExtensionBodyEnd getClassOrMixinOrExtensionBody() { |
| for (ParserAstNode child in children!) { |
| if (child is ClassOrMixinOrExtensionBodyEnd) { |
| return child; |
| } |
| } |
| throw "Not found."; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension ExtensionTypeDeclarationExtension on ExtensionTypeDeclarationEnd { |
| Token? getExtensionTypeName() { |
| ExtensionTypeDeclarationBegin begin = |
| children!.first as ExtensionTypeDeclarationBegin; |
| return begin.name; |
| } |
| |
| ClassOrMixinOrExtensionBodyEnd? getClassOrMixinOrExtensionBody() { |
| for (ParserAstNode child in children!) { |
| if (child is ClassOrMixinOrExtensionBodyEnd) { |
| return child; |
| } |
| } |
| return null; |
| } |
| |
| NoExtensionTypeBodyHandle? getNoExtensionTypeBody() { |
| for (ParserAstNode child in children!) { |
| if (child is NoExtensionTypeBodyHandle) { |
| return child; |
| } |
| } |
| return null; |
| } |
| } |
| |
| extension TopLevelMethodExtension on TopLevelMethodEnd { |
| IdentifierHandle getNameIdentifier() { |
| for (ParserAstNode child in children!) { |
| if (child is IdentifierHandle) { |
| if (child.context == IdentifierContext.topLevelFunctionDeclaration) { |
| return child; |
| } |
| } |
| } |
| throw "Didn't find the name identifier!"; |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| Token getNameIdentifierToken() { |
| return getNameIdentifier().token; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension TypedefExtension on TypedefEnd { |
| IdentifierHandle getNameIdentifier() { |
| for (ParserAstNode child in children!) { |
| if (child is IdentifierHandle) { |
| if (child.context == IdentifierContext.typedefDeclaration) { |
| return child; |
| } |
| } |
| } |
| throw "Didn't find the name identifier!"; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension ImportExtension on ImportEnd { |
| IdentifierHandle? getImportPrefix() { |
| for (ParserAstNode child in children!) { |
| if (child is IdentifierHandle) { |
| if (child.context == IdentifierContext.importPrefixDeclaration) { |
| return child; |
| } |
| } |
| } |
| return null; |
| } |
| |
| String getImportUriString() { |
| StringBuffer sb = new StringBuffer(); |
| bool foundOne = false; |
| for (ParserAstNode child in children!) { |
| if (child is LiteralStringEnd) { |
| LiteralStringBegin uri = child.children!.single as LiteralStringBegin; |
| sb.write( |
| unescapeString( |
| uri.token.lexeme, |
| uri.token, |
| const UnescapeErrorListenerDummy(), |
| ), |
| ); |
| foundOne = true; |
| } |
| } |
| if (!foundOne) throw "Didn't find any"; |
| return sb.toString(); |
| } |
| |
| List<String>? getConditionalImportUriStrings() { |
| List<String>? result; |
| for (ParserAstNode child in children!) { |
| if (child is ConditionalUrisEnd) { |
| for (ParserAstNode child2 in child.children!) { |
| if (child2 is ConditionalUriEnd) { |
| LiteralStringEnd end = child2.children!.last as LiteralStringEnd; |
| LiteralStringBegin uri = end.children!.single as LiteralStringBegin; |
| (result ??= []).add( |
| unescapeString( |
| uri.token.lexeme, |
| uri.token, |
| const UnescapeErrorListenerDummy(), |
| ), |
| ); |
| } |
| } |
| return result; |
| } |
| } |
| return result; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension ExportExtension on ExportEnd { |
| String getExportUriString() { |
| StringBuffer sb = new StringBuffer(); |
| bool foundOne = false; |
| for (ParserAstNode child in children!) { |
| if (child is LiteralStringEnd) { |
| LiteralStringBegin uri = child.children!.single as LiteralStringBegin; |
| sb.write( |
| unescapeString( |
| uri.token.lexeme, |
| uri.token, |
| const UnescapeErrorListenerDummy(), |
| ), |
| ); |
| foundOne = true; |
| } |
| } |
| if (!foundOne) throw "Didn't find any"; |
| return sb.toString(); |
| } |
| |
| List<String>? getConditionalExportUriStrings() { |
| List<String>? result; |
| for (ParserAstNode child in children!) { |
| if (child is ConditionalUrisEnd) { |
| for (ParserAstNode child2 in child.children!) { |
| if (child2 is ConditionalUriEnd) { |
| LiteralStringEnd end = child2.children!.last as LiteralStringEnd; |
| LiteralStringBegin uri = end.children!.single as LiteralStringBegin; |
| (result ??= []).add( |
| unescapeString( |
| uri.token.lexeme, |
| uri.token, |
| const UnescapeErrorListenerDummy(), |
| ), |
| ); |
| } |
| } |
| return result; |
| } |
| } |
| return result; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension PartExtension on PartEnd { |
| String getPartUriString() { |
| StringBuffer sb = new StringBuffer(); |
| bool foundOne = false; |
| for (ParserAstNode child in children!) { |
| if (child is LiteralStringEnd) { |
| LiteralStringBegin uri = child.children!.single as LiteralStringBegin; |
| sb.write( |
| unescapeString( |
| uri.token.lexeme, |
| uri.token, |
| const UnescapeErrorListenerDummy(), |
| ), |
| ); |
| foundOne = true; |
| } |
| } |
| if (!foundOne) throw "Didn't find any"; |
| return sb.toString(); |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension PartOfExtension on PartOfEnd { |
| String? getPartOfUriString() { |
| StringBuffer sb = new StringBuffer(); |
| bool foundOne = false; |
| for (ParserAstNode child in children!) { |
| if (child is LiteralStringEnd) { |
| LiteralStringBegin uri = child.children!.single as LiteralStringBegin; |
| sb.write( |
| unescapeString( |
| uri.token.lexeme, |
| uri.token, |
| const UnescapeErrorListenerDummy(), |
| ), |
| ); |
| foundOne = true; |
| } |
| } |
| if (!foundOne) return null; |
| return sb.toString(); |
| } |
| |
| List<String> getPartOfIdentifiers() { |
| List<String> result = []; |
| for (ParserAstNode child in children!) { |
| if (child is IdentifierHandle) { |
| result.add(child.token.lexeme); |
| } |
| } |
| return result; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension LibraryNameExtension on LibraryNameEnd { |
| List<String> getNameIdentifiers() { |
| List<String> result = []; |
| for (ParserAstNode child in children!) { |
| if (child is IdentifierHandle) { |
| result.add(child.token.lexeme); |
| } |
| } |
| return result; |
| } |
| } |
| |
| class UnescapeErrorListenerDummy implements UnescapeErrorListener { |
| const UnescapeErrorListenerDummy(); |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void handleUnescapeError( |
| Message message, |
| covariant location, |
| int offset, |
| int length, |
| ) { |
| // Purposely doesn't do anything. |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension TopLevelFieldsExtension on TopLevelFieldsEnd { |
| List<IdentifierHandle> getFieldIdentifiers() { |
| int countLeft = count; |
| List<IdentifierHandle>? identifiers; |
| for (int i = children!.length - 1; i >= 0; i--) { |
| ParserAstNode child = children![i]; |
| if (child is IdentifierHandle && |
| child.context == IdentifierContext.topLevelVariableDeclaration) { |
| countLeft--; |
| if (identifiers == null) { |
| identifiers = new List<IdentifierHandle>.filled(count, child); |
| } else { |
| identifiers[countLeft] = child; |
| } |
| if (countLeft == 0) break; |
| } |
| } |
| if (countLeft != 0) throw "Didn't find the expected number of identifiers"; |
| return identifiers ?? []; |
| } |
| } |
| |
| bool _isTypeOrNoType(ParserAstNode node) { |
| return node is TypeHandle || |
| node is RecordTypeEnd || |
| node is NoTypeHandle || |
| node is VoidKeywordHandle || |
| node is FunctionTypeEnd; |
| } |
| |
| extension MethodExtension on MethodEnd { |
| // Coverage-ignore(suite): Not run. |
| BlockFunctionBodyEnd? getBlockFunctionBody() { |
| for (ParserAstNode child in children!) { |
| if (child is BlockFunctionBodyEnd) { |
| return child; |
| } |
| } |
| return null; |
| } |
| |
| Token getNameIdentifierToken() { |
| bool foundType = false; |
| for (ParserAstNode child in children!) { |
| if (_isTypeOrNoType(child)) { |
| foundType = true; |
| } |
| if (foundType && child is IdentifierHandle) { |
| return child.token; |
| } else if (foundType && child is OperatorNameHandle) { |
| // Coverage-ignore-block(suite): Not run. |
| return child.token; |
| } |
| } |
| // Coverage-ignore-block(suite): Not run. |
| throw "No identifier found: $children"; |
| } |
| |
| String getNameIdentifier() { |
| return getNameIdentifierToken().lexeme; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension FactoryExtension on FactoryEnd { |
| List<IdentifierHandle> getIdentifiers() { |
| List<IdentifierHandle> result = []; |
| for (ParserAstNode child in children!) { |
| if (child is IdentifierHandle) { |
| result.add(child); |
| } else if (child is FormalParametersEnd) { |
| break; |
| } |
| } |
| return result; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension ConstructorExtension on ConstructorEnd { |
| FormalParametersEnd getFormalParameters() { |
| for (ParserAstNode child in children!) { |
| if (child is FormalParametersEnd) { |
| return child; |
| } |
| } |
| throw "Not found"; |
| } |
| |
| InitializersEnd? getInitializers() { |
| for (ParserAstNode child in children!) { |
| if (child is InitializersEnd) { |
| return child; |
| } |
| } |
| return null; |
| } |
| |
| BlockFunctionBodyEnd? getBlockFunctionBody() { |
| for (ParserAstNode child in children!) { |
| if (child is BlockFunctionBodyEnd) { |
| return child; |
| } |
| } |
| return null; |
| } |
| |
| List<IdentifierHandle> getIdentifiers() { |
| List<IdentifierHandle> result = []; |
| for (ParserAstNode child in children!) { |
| if (child is IdentifierHandle) { |
| result.add(child); |
| } |
| } |
| return result; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension FormalParametersExtension on FormalParametersEnd { |
| List<FormalParameterEnd> getFormalParameters() { |
| List<FormalParameterEnd> result = []; |
| for (ParserAstNode child in children!) { |
| if (child is FormalParameterEnd) { |
| result.add(child); |
| } |
| } |
| return result; |
| } |
| |
| OptionalFormalParametersEnd? getOptionalFormalParameters() { |
| for (ParserAstNode child in children!) { |
| if (child is OptionalFormalParametersEnd) { |
| return child; |
| } |
| } |
| return null; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension FormalParameterExtension on FormalParameterEnd { |
| FormalParameterBegin getBegin() { |
| return children!.first as FormalParameterBegin; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension OptionalFormalParametersExtension on OptionalFormalParametersEnd { |
| List<FormalParameterEnd> getFormalParameters() { |
| List<FormalParameterEnd> result = []; |
| for (ParserAstNode child in children!) { |
| if (child is FormalParameterEnd) { |
| result.add(child); |
| } |
| } |
| return result; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension InitializersExtension on InitializersEnd { |
| List<InitializerEnd> getInitializers() { |
| List<InitializerEnd> result = []; |
| for (ParserAstNode child in children!) { |
| if (child is InitializerEnd) { |
| result.add(child); |
| } |
| } |
| return result; |
| } |
| |
| InitializersBegin getBegin() { |
| return children!.first as InitializersBegin; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| extension InitializerExtension on InitializerEnd { |
| InitializerBegin getBegin() { |
| return children!.first as InitializerBegin; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| void main(List<String> args) { |
| File f = new File(args[0]); |
| Uint8List data = f.readAsBytesSync(); |
| ParserAstNode ast = getAST(data); |
| if (args.length > 1 && args[1] == "--benchmark") { |
| Stopwatch stopwatch = new Stopwatch()..start(); |
| int numRuns = 100; |
| for (int i = 0; i < numRuns; i++) { |
| ParserAstNode ast2 = getAST(data); |
| if (ast.what != ast2.what) { |
| throw "Not the same result every time"; |
| } |
| } |
| stopwatch.stop(); |
| print( |
| "First $numRuns took ${stopwatch.elapsedMilliseconds} ms " |
| "(i.e. ${stopwatch.elapsedMilliseconds / numRuns}ms/iteration)", |
| ); |
| stopwatch = new Stopwatch()..start(); |
| numRuns = 2500; |
| for (int i = 0; i < numRuns; i++) { |
| ParserAstNode ast2 = getAST(data); |
| if (ast.what != ast2.what) { |
| throw "Not the same result every time"; |
| } |
| } |
| stopwatch.stop(); |
| print( |
| "Next $numRuns took ${stopwatch.elapsedMilliseconds} ms " |
| "(i.e. ${stopwatch.elapsedMilliseconds / numRuns}ms/iteration)", |
| ); |
| } else { |
| print(ast); |
| } |
| } |
| |
| class ParserASTListener extends AbstractParserAstListener { |
| @override |
| void seen(ParserAstNode entry) { |
| switch (entry.type) { |
| case ParserAstType.BEGIN: |
| case ParserAstType.HANDLE: |
| // This just adds stuff. |
| data.add(entry); |
| break; |
| case ParserAstType.END: |
| // End should gobble up everything until the corresponding begin (which |
| // should be the latest begin). |
| int? beginIndex; |
| for (int i = data.length - 1; i >= 0; i--) { |
| if (data[i].type == ParserAstType.BEGIN) { |
| beginIndex = i; |
| break; |
| } |
| } |
| if (beginIndex == null) { |
| // Coverage-ignore-block(suite): Not run. |
| throw "Couldn't find a begin for ${entry.what}. Has:\n" |
| "${data.map((e) => "${e.what}: ${e.type}").join("\n")}"; |
| } |
| String begin = data[beginIndex].what; |
| String end = entry.what; |
| if (begin == end) { |
| // Exact match. |
| } else if (end == "TopLevelDeclaration" && |
| (begin == "EnumDeclarationPrelude" || |
| begin == "ExtensionDeclarationPrelude" || |
| begin == "ClassOrMixinOrNamedMixinApplicationPrelude" || |
| begin == "TopLevelMember" || |
| begin == "UncategorizedTopLevelDeclaration")) { |
| // endTopLevelDeclaration is started by one of |
| // beginEnumDeclarationPrelude |
| // beginExtensionDeclarationPrelude, |
| // beginClassOrNamedMixinApplicationPrelude |
| // beginTopLevelMember or beginUncategorizedTopLevelDeclaration. |
| } else if (begin == "Fields" && end == "TopLevelFields") { |
| // beginFields is ended by one of endTopLevelFields or endFields. |
| } else if (begin == "ForStatement" && end == "ForIn") { |
| // beginForStatement is ended by either endForStatement or endForIn. |
| } else if (begin == "ForControlFlow" && (end == "ForInControlFlow")) { |
| // beginForControlFlow is ended by either endForControlFlow or |
| // endForInControlFlow. |
| } else if (begin == "IfControlFlow" && (end == "IfElseControlFlow")) { |
| // beginIfControlFlow is ended by either endIfControlFlow or |
| // endIfElseControlFlow. |
| } else if (begin == "AwaitExpression" && |
| (end == "InvalidAwaitExpression")) { |
| // beginAwaitExpression is ended by either endAwaitExpression or |
| // endInvalidAwaitExpression. |
| } else if (begin == "YieldStatement" && |
| (end == "InvalidYieldStatement")) { |
| // beginYieldStatement is ended by either endYieldStatement or |
| // endInvalidYieldStatement. |
| } else if (begin == "ParenthesizedExpressionOrRecordLiteral" && |
| (end == "ParenthesizedExpression" || end == "RecordLiteral")) { |
| // beginParenthesizedExpressionOrRecordLiteral is ended by either |
| // endParenthesizedExpression or endRecordLiteral. |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| throw "Unknown combination: begin$begin and end$end"; |
| } |
| List<ParserAstNode> children = data.sublist(beginIndex); |
| for (ParserAstNode child in children) { |
| child.parent = entry; |
| } |
| data.length = beginIndex; |
| data.add(entry..children = children); |
| break; |
| } |
| } |
| |
| @override |
| void reportVarianceModifierNotEnabled(Token? variance) { |
| throw new UnimplementedError(); |
| } |
| |
| @override |
| void logEvent(String name) { |
| throw new UnimplementedError(); |
| } |
| } |