| Index: pkg/analyzer/lib/src/generated/parser.dart
|
| diff --git a/pkg/analyzer/lib/src/generated/parser.dart b/pkg/analyzer/lib/src/generated/parser.dart
|
| index bfd461d21ffdea5cafbfae72fda00ec29c410844..10a1a2c1d2378c07bb12b9e23fac3abd3206d898 100644
|
| --- a/pkg/analyzer/lib/src/generated/parser.dart
|
| +++ b/pkg/analyzer/lib/src/generated/parser.dart
|
| @@ -10,6 +10,7 @@ import 'scanner.dart';
|
| import 'ast.dart';
|
| import 'utilities_dart.dart';
|
| import 'engine.dart' show AnalysisEngine;
|
| +import 'utilities_collection.dart' show TokenMap;
|
| /**
|
| * Instances of the class `CommentAndMetadata` implement a simple data-holder for a method
|
| * that needs to return multiple values.
|
| @@ -143,6 +144,1035 @@ class Modifiers {
|
| }
|
| }
|
| /**
|
| + * Instances of the class `IncrementalParseDispatcher` implement a dispatcher that will invoke
|
| + * the right parse method when re-parsing a specified child of the visited node. All of the methods
|
| + * in this class assume that the parser is positioned to parse the replacement for the node. All of
|
| + * the methods will throw an [IncrementalParseException] if the node could not be parsed for
|
| + * some reason.
|
| + */
|
| +class IncrementalParseDispatcher implements ASTVisitor<ASTNode> {
|
| +
|
| + /**
|
| + * The parser used to parse the replacement for the node.
|
| + */
|
| + Parser _parser;
|
| +
|
| + /**
|
| + * The node that is to be replaced.
|
| + */
|
| + ASTNode _oldNode;
|
| +
|
| + /**
|
| + * Initialize a newly created dispatcher to parse a single node that will replace the given node.
|
| + *
|
| + * @param parser the parser used to parse the replacement for the node
|
| + * @param oldNode the node that is to be replaced
|
| + */
|
| + IncrementalParseDispatcher(Parser parser, ASTNode oldNode) {
|
| + this._parser = parser;
|
| + this._oldNode = oldNode;
|
| + }
|
| + ASTNode visitAdjacentStrings(AdjacentStrings node) {
|
| + if (node.strings.contains(_oldNode)) {
|
| + return _parser.parseStringLiteral();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitAnnotation(Annotation node) {
|
| + if (identical(_oldNode, node.name)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.constructorName)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.arguments)) {
|
| + return _parser.parseArgumentList();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitArgumentDefinitionTest(ArgumentDefinitionTest node) {
|
| + if (identical(_oldNode, node.identifier)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitArgumentList(ArgumentList node) {
|
| + if (node.arguments.contains(_oldNode)) {
|
| + return _parser.parseArgument();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitAsExpression(AsExpression node) {
|
| + if (identical(_oldNode, node.expression)) {
|
| + return _parser.parseBitwiseOrExpression();
|
| + } else if (identical(_oldNode, node.type)) {
|
| + return _parser.parseTypeName();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitAssertStatement(AssertStatement node) {
|
| + if (identical(_oldNode, node.condition)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitAssignmentExpression(AssignmentExpression node) {
|
| + if (identical(_oldNode, node.leftHandSide)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.rightHandSide)) {
|
| + if (isCascadeAllowed(node)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return _parser.parseExpressionWithoutCascade();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitBinaryExpression(BinaryExpression node) {
|
| + if (identical(_oldNode, node.leftOperand)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.rightOperand)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitBlock(Block node) {
|
| + if (node.statements.contains(_oldNode)) {
|
| + return _parser.parseStatement2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitBlockFunctionBody(BlockFunctionBody node) {
|
| + if (identical(_oldNode, node.block)) {
|
| + return _parser.parseBlock();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitBooleanLiteral(BooleanLiteral node) => notAChild(node);
|
| + ASTNode visitBreakStatement(BreakStatement node) {
|
| + if (identical(_oldNode, node.label)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitCascadeExpression(CascadeExpression node) {
|
| + if (identical(_oldNode, node.target)) {
|
| + return _parser.parseConditionalExpression();
|
| + } else if (node.cascadeSections.contains(_oldNode)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitCatchClause(CatchClause node) {
|
| + if (identical(_oldNode, node.exceptionType)) {
|
| + return _parser.parseTypeName();
|
| + } else if (identical(_oldNode, node.exceptionParameter)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.stackTraceParameter)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.body)) {
|
| + return _parser.parseBlock();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitClassDeclaration(ClassDeclaration node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.name)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.typeParameters)) {
|
| + return _parser.parseTypeParameterList();
|
| + } else if (identical(_oldNode, node.extendsClause)) {
|
| + return _parser.parseExtendsClause();
|
| + } else if (identical(_oldNode, node.withClause)) {
|
| + return _parser.parseWithClause();
|
| + } else if (identical(_oldNode, node.implementsClause)) {
|
| + return _parser.parseImplementsClause();
|
| + } else if (node.members.contains(_oldNode)) {
|
| + return _parser.parseClassMember(node.name.name);
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitClassTypeAlias(ClassTypeAlias node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.name)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.typeParameters)) {
|
| + return _parser.parseTypeParameterList();
|
| + } else if (identical(_oldNode, node.superclass)) {
|
| + return _parser.parseTypeName();
|
| + } else if (identical(_oldNode, node.withClause)) {
|
| + return _parser.parseWithClause();
|
| + } else if (identical(_oldNode, node.implementsClause)) {
|
| + return _parser.parseImplementsClause();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitComment(Comment node) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + ASTNode visitCommentReference(CommentReference node) {
|
| + if (identical(_oldNode, node.identifier)) {
|
| + return _parser.parsePrefixedIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitCompilationUnit(CompilationUnit node) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + ASTNode visitConditionalExpression(ConditionalExpression node) {
|
| + if (identical(_oldNode, node.condition)) {
|
| + return _parser.parseLogicalOrExpression();
|
| + } else if (identical(_oldNode, node.thenExpression)) {
|
| + return _parser.parseExpressionWithoutCascade();
|
| + } else if (identical(_oldNode, node.elseExpression)) {
|
| + return _parser.parseExpressionWithoutCascade();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitConstructorDeclaration(ConstructorDeclaration node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.returnType)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.name)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.parameters)) {
|
| + return _parser.parseFormalParameterList();
|
| + } else if (identical(_oldNode, node.redirectedConstructor)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.initializers.contains(_oldNode)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.body)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
|
| + if (identical(_oldNode, node.fieldName)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.expression)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitConstructorName(ConstructorName node) {
|
| + if (identical(_oldNode, node.type)) {
|
| + return _parser.parseTypeName();
|
| + } else if (identical(_oldNode, node.name)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitContinueStatement(ContinueStatement node) {
|
| + if (identical(_oldNode, node.label)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitDeclaredIdentifier(DeclaredIdentifier node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.type)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.identifier)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitDefaultFormalParameter(DefaultFormalParameter node) {
|
| + if (identical(_oldNode, node.parameter)) {
|
| + return _parser.parseNormalFormalParameter();
|
| + } else if (identical(_oldNode, node.defaultValue)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitDoStatement(DoStatement node) {
|
| + if (identical(_oldNode, node.body)) {
|
| + return _parser.parseStatement2();
|
| + } else if (identical(_oldNode, node.condition)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitDoubleLiteral(DoubleLiteral node) => notAChild(node);
|
| + ASTNode visitEmptyFunctionBody(EmptyFunctionBody node) => notAChild(node);
|
| + ASTNode visitEmptyStatement(EmptyStatement node) => notAChild(node);
|
| + ASTNode visitExportDirective(ExportDirective node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.uri)) {
|
| + return _parser.parseStringLiteral();
|
| + } else if (node.combinators.contains(_oldNode)) {
|
| + throw new IncrementalParseException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitExpressionFunctionBody(ExpressionFunctionBody node) {
|
| + if (identical(_oldNode, node.expression)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitExpressionStatement(ExpressionStatement node) {
|
| + if (identical(_oldNode, node.expression)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitExtendsClause(ExtendsClause node) {
|
| + if (identical(_oldNode, node.superclass)) {
|
| + return _parser.parseTypeName();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitFieldDeclaration(FieldDeclaration node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.fields)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitFieldFormalParameter(FieldFormalParameter node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.type)) {
|
| + return _parser.parseTypeName();
|
| + } else if (identical(_oldNode, node.identifier)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.parameters)) {
|
| + return _parser.parseFormalParameterList();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitForEachStatement(ForEachStatement node) {
|
| + if (identical(_oldNode, node.loopVariable)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.identifier)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.body)) {
|
| + return _parser.parseStatement2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitFormalParameterList(FormalParameterList node) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + ASTNode visitForStatement(ForStatement node) {
|
| + if (identical(_oldNode, node.variables)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.initialization)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.condition)) {
|
| + return _parser.parseExpression2();
|
| + } else if (node.updaters.contains(_oldNode)) {
|
| + return _parser.parseExpression2();
|
| + } else if (identical(_oldNode, node.body)) {
|
| + return _parser.parseStatement2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitFunctionDeclaration(FunctionDeclaration node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.returnType)) {
|
| + return _parser.parseReturnType();
|
| + } else if (identical(_oldNode, node.name)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.functionExpression)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
|
| + if (identical(_oldNode, node.functionDeclaration)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitFunctionExpression(FunctionExpression node) {
|
| + if (identical(_oldNode, node.parameters)) {
|
| + return _parser.parseFormalParameterList();
|
| + } else if (identical(_oldNode, node.body)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
|
| + if (identical(_oldNode, node.function)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.argumentList)) {
|
| + return _parser.parseArgumentList();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitFunctionTypeAlias(FunctionTypeAlias node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.returnType)) {
|
| + return _parser.parseReturnType();
|
| + } else if (identical(_oldNode, node.name)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.typeParameters)) {
|
| + return _parser.parseTypeParameterList();
|
| + } else if (identical(_oldNode, node.parameters)) {
|
| + return _parser.parseFormalParameterList();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.returnType)) {
|
| + return _parser.parseReturnType();
|
| + } else if (identical(_oldNode, node.identifier)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.parameters)) {
|
| + return _parser.parseFormalParameterList();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitHideCombinator(HideCombinator node) {
|
| + if (node.hiddenNames.contains(_oldNode)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitIfStatement(IfStatement node) {
|
| + if (identical(_oldNode, node.condition)) {
|
| + return _parser.parseExpression2();
|
| + } else if (identical(_oldNode, node.thenStatement)) {
|
| + return _parser.parseStatement2();
|
| + } else if (identical(_oldNode, node.elseStatement)) {
|
| + return _parser.parseStatement2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitImplementsClause(ImplementsClause node) {
|
| + if (node.interfaces.contains(node)) {
|
| + return _parser.parseTypeName();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitImportDirective(ImportDirective node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.uri)) {
|
| + return _parser.parseStringLiteral();
|
| + } else if (identical(_oldNode, node.prefix)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (node.combinators.contains(_oldNode)) {
|
| + throw new IncrementalParseException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitIndexExpression(IndexExpression node) {
|
| + if (identical(_oldNode, node.target)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.index)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitInstanceCreationExpression(InstanceCreationExpression node) {
|
| + if (identical(_oldNode, node.constructorName)) {
|
| + return _parser.parseConstructorName();
|
| + } else if (identical(_oldNode, node.argumentList)) {
|
| + return _parser.parseArgumentList();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitIntegerLiteral(IntegerLiteral node) => notAChild(node);
|
| + ASTNode visitInterpolationExpression(InterpolationExpression node) {
|
| + if (identical(_oldNode, node.expression)) {
|
| + if (node.leftBracket == null) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return _parser.parseExpression2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitInterpolationString(InterpolationString node) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + ASTNode visitIsExpression(IsExpression node) {
|
| + if (identical(_oldNode, node.expression)) {
|
| + return _parser.parseBitwiseOrExpression();
|
| + } else if (identical(_oldNode, node.type)) {
|
| + return _parser.parseTypeName();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitLabel(Label node) {
|
| + if (identical(_oldNode, node.label)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitLabeledStatement(LabeledStatement node) {
|
| + if (node.labels.contains(_oldNode)) {
|
| + return _parser.parseLabel();
|
| + } else if (identical(_oldNode, node.statement)) {
|
| + return _parser.parseStatement2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitLibraryDirective(LibraryDirective node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.name)) {
|
| + return _parser.parseLibraryIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitLibraryIdentifier(LibraryIdentifier node) {
|
| + if (node.components.contains(_oldNode)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitListLiteral(ListLiteral node) {
|
| + if (identical(_oldNode, node.typeArguments)) {
|
| + return _parser.parseTypeArgumentList();
|
| + } else if (node.elements.contains(_oldNode)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitMapLiteral(MapLiteral node) {
|
| + if (identical(_oldNode, node.typeArguments)) {
|
| + return _parser.parseTypeArgumentList();
|
| + } else if (node.entries.contains(_oldNode)) {
|
| + return _parser.parseMapLiteralEntry();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitMapLiteralEntry(MapLiteralEntry node) {
|
| + if (identical(_oldNode, node.key)) {
|
| + return _parser.parseExpression2();
|
| + } else if (identical(_oldNode, node.value)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitMethodDeclaration(MethodDeclaration node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.returnType)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.name)) {
|
| + if (node.operatorKeyword != null) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.body)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitMethodInvocation(MethodInvocation node) {
|
| + if (identical(_oldNode, node.target)) {
|
| + throw new IncrementalParseException();
|
| + } else if (identical(_oldNode, node.methodName)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.argumentList)) {
|
| + return _parser.parseArgumentList();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitNamedExpression(NamedExpression node) {
|
| + if (identical(_oldNode, node.name)) {
|
| + return _parser.parseLabel();
|
| + } else if (identical(_oldNode, node.expression)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitNativeClause(NativeClause node) {
|
| + if (identical(_oldNode, node.name)) {
|
| + return _parser.parseStringLiteral();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitNativeFunctionBody(NativeFunctionBody node) {
|
| + if (identical(_oldNode, node.stringLiteral)) {
|
| + return _parser.parseStringLiteral();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitNullLiteral(NullLiteral node) => notAChild(node);
|
| + ASTNode visitParenthesizedExpression(ParenthesizedExpression node) {
|
| + if (identical(_oldNode, node.expression)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitPartDirective(PartDirective node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.uri)) {
|
| + return _parser.parseStringLiteral();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitPartOfDirective(PartOfDirective node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.libraryName)) {
|
| + return _parser.parseLibraryIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitPostfixExpression(PostfixExpression node) {
|
| + if (identical(_oldNode, node.operand)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitPrefixedIdentifier(PrefixedIdentifier node) {
|
| + if (identical(_oldNode, node.prefix)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.identifier)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitPrefixExpression(PrefixExpression node) {
|
| + if (identical(_oldNode, node.operand)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitPropertyAccess(PropertyAccess node) {
|
| + if (identical(_oldNode, node.target)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.propertyName)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
|
| + if (identical(_oldNode, node.constructorName)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.argumentList)) {
|
| + return _parser.parseArgumentList();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitRethrowExpression(RethrowExpression node) => notAChild(node);
|
| + ASTNode visitReturnStatement(ReturnStatement node) {
|
| + if (identical(_oldNode, node.expression)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitScriptTag(ScriptTag node) => notAChild(node);
|
| + ASTNode visitShowCombinator(ShowCombinator node) {
|
| + if (node.shownNames.contains(_oldNode)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitSimpleFormalParameter(SimpleFormalParameter node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.type)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.identifier)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitSimpleIdentifier(SimpleIdentifier node) => notAChild(node);
|
| + ASTNode visitSimpleStringLiteral(SimpleStringLiteral node) => notAChild(node);
|
| + ASTNode visitStringInterpolation(StringInterpolation node) {
|
| + if (node.elements.contains(_oldNode)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitSuperConstructorInvocation(SuperConstructorInvocation node) {
|
| + if (identical(_oldNode, node.constructorName)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.argumentList)) {
|
| + return _parser.parseArgumentList();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitSuperExpression(SuperExpression node) => notAChild(node);
|
| + ASTNode visitSwitchCase(SwitchCase node) {
|
| + if (node.labels.contains(_oldNode)) {
|
| + return _parser.parseLabel();
|
| + } else if (identical(_oldNode, node.expression)) {
|
| + return _parser.parseExpression2();
|
| + } else if (node.statements.contains(_oldNode)) {
|
| + return _parser.parseStatement2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitSwitchDefault(SwitchDefault node) {
|
| + if (node.labels.contains(_oldNode)) {
|
| + return _parser.parseLabel();
|
| + } else if (node.statements.contains(_oldNode)) {
|
| + return _parser.parseStatement2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitSwitchStatement(SwitchStatement node) {
|
| + if (identical(_oldNode, node.expression)) {
|
| + return _parser.parseExpression2();
|
| + } else if (node.members.contains(_oldNode)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitSymbolLiteral(SymbolLiteral node) => notAChild(node);
|
| + ASTNode visitThisExpression(ThisExpression node) => notAChild(node);
|
| + ASTNode visitThrowExpression(ThrowExpression node) {
|
| + if (identical(_oldNode, node.expression)) {
|
| + if (isCascadeAllowed2(node)) {
|
| + return _parser.parseExpression2();
|
| + }
|
| + return _parser.parseExpressionWithoutCascade();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.variables)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitTryStatement(TryStatement node) {
|
| + if (identical(_oldNode, node.body)) {
|
| + return _parser.parseBlock();
|
| + } else if (node.catchClauses.contains(_oldNode)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.finallyBlock)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitTypeArgumentList(TypeArgumentList node) {
|
| + if (node.arguments.contains(_oldNode)) {
|
| + return _parser.parseTypeName();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitTypeName(TypeName node) {
|
| + if (identical(_oldNode, node.name)) {
|
| + return _parser.parsePrefixedIdentifier();
|
| + } else if (identical(_oldNode, node.typeArguments)) {
|
| + return _parser.parseTypeArgumentList();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitTypeParameter(TypeParameter node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.name)) {
|
| + return _parser.parseSimpleIdentifier();
|
| + } else if (identical(_oldNode, node.bound)) {
|
| + return _parser.parseTypeName();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitTypeParameterList(TypeParameterList node) {
|
| + if (node.typeParameters.contains(node)) {
|
| + return _parser.parseTypeParameter();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitVariableDeclaration(VariableDeclaration node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (identical(_oldNode, node.name)) {
|
| + throw new InsufficientContextException();
|
| + } else if (identical(_oldNode, node.initializer)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitVariableDeclarationList(VariableDeclarationList node) {
|
| + if (identical(_oldNode, node.documentationComment)) {
|
| + throw new InsufficientContextException();
|
| + } else if (node.metadata.contains(_oldNode)) {
|
| + return _parser.parseAnnotation();
|
| + } else if (node.variables.contains(_oldNode)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitVariableDeclarationStatement(VariableDeclarationStatement node) {
|
| + if (identical(_oldNode, node.variables)) {
|
| + throw new InsufficientContextException();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitWhileStatement(WhileStatement node) {
|
| + if (identical(_oldNode, node.condition)) {
|
| + return _parser.parseExpression2();
|
| + } else if (identical(_oldNode, node.body)) {
|
| + return _parser.parseStatement2();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| + ASTNode visitWithClause(WithClause node) {
|
| + if (node.mixinTypes.contains(node)) {
|
| + return _parser.parseTypeName();
|
| + }
|
| + return notAChild(node);
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if the given assignment expression can have a cascade expression on the
|
| + * right-hand side.
|
| + *
|
| + * @param node the assignment expression being tested
|
| + * @return `true` if the right-hand side can be a cascade expression
|
| + */
|
| + bool isCascadeAllowed(AssignmentExpression node) {
|
| + throw new InsufficientContextException();
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if the given throw expression can have a cascade expression.
|
| + *
|
| + * @param node the throw expression being tested
|
| + * @return `true` if the expression can be a cascade expression
|
| + */
|
| + bool isCascadeAllowed2(ThrowExpression node) {
|
| + throw new InsufficientContextException();
|
| + }
|
| +
|
| + /**
|
| + * Throw an exception indicating that the visited node was not the parent of the node to be
|
| + * replaced.
|
| + *
|
| + * @param visitedNode the visited node that should have been the parent of the node to be replaced
|
| + */
|
| + ASTNode notAChild(ASTNode visitedNode) {
|
| + throw new IncrementalParseException.con1("Internal error: the visited node (a ${visitedNode.runtimeType.toString()}) was not the parent of the node to be replaced (a ${_oldNode.runtimeType.toString()})");
|
| + }
|
| +}
|
| +/**
|
| + * Instances of the class `IncrementalParseException` represent an exception that occurred
|
| + * while attempting to parse a replacement for a specified node in an existing AST structure.
|
| + */
|
| +class IncrementalParseException extends RuntimeException {
|
| +
|
| + /**
|
| + * Initialize a newly created exception to have no message and to be its own cause.
|
| + */
|
| + IncrementalParseException() : super();
|
| +
|
| + /**
|
| + * Initialize a newly created exception to have the given message and to be its own cause.
|
| + *
|
| + * @param message the message describing the reason for the exception
|
| + */
|
| + IncrementalParseException.con1(String message) : super(message: message);
|
| +
|
| + /**
|
| + * Initialize a newly created exception to have no message and to have the given cause.
|
| + *
|
| + * @param cause the exception that caused this exception
|
| + */
|
| + IncrementalParseException.con2(Exception cause) : super(cause: cause);
|
| +}
|
| +/**
|
| + * Instances of the class `IncrementalParser` re-parse a single AST structure within a larger
|
| + * AST structure.
|
| + */
|
| +class IncrementalParser {
|
| +
|
| + /**
|
| + * The source being parsed.
|
| + */
|
| + Source _source;
|
| +
|
| + /**
|
| + * A map from old tokens to new tokens used during the cloning process.
|
| + */
|
| + TokenMap _tokenMap;
|
| +
|
| + /**
|
| + * The error listener that will be informed of any errors that are found during the parse.
|
| + */
|
| + AnalysisErrorListener _errorListener;
|
| +
|
| + /**
|
| + * Initialize a newly created incremental parser to parse a portion of the content of the given
|
| + * source.
|
| + *
|
| + * @param source the source being parsed
|
| + * @param tokenMap a map from old tokens to new tokens used during the cloning process
|
| + * @param errorListener the error listener that will be informed of any errors that are found
|
| + * during the parse
|
| + */
|
| + IncrementalParser(Source source, TokenMap tokenMap, AnalysisErrorListener errorListener) {
|
| + this._source = source;
|
| + this._tokenMap = tokenMap;
|
| + this._errorListener = errorListener;
|
| + }
|
| +
|
| + /**
|
| + * Given a range of tokens that were re-scanned, re-parse the minimimum number of tokens to
|
| + * produce a consistent AST structure. The range is represented by the first and last tokens in
|
| + * the range. The tokens are assumed to be contained in the same token stream.
|
| + *
|
| + * @param firstToken the first token in the range of tokens that were re-scanned or `null`
|
| + * if no new tokens were inserted
|
| + * @param lastToken the last token in the range of tokens that were re-scanned or `null` if
|
| + * no new tokens were inserted
|
| + * @param originalStart the offset in the original source of the first character that was modified
|
| + * @param originalEnd the offset in the original source of the last character that was modified
|
| + */
|
| + ASTNode reparse(ASTNode originalStructure, Token firstToken, Token lastToken, int originalStart, int originalEnd) {
|
| + ASTNode oldNode = null;
|
| + ASTNode newNode = null;
|
| + if (firstToken != null) {
|
| + if (originalEnd < originalStart) {
|
| + oldNode = new NodeLocator.con1(originalStart).searchWithin(originalStructure);
|
| + } else {
|
| + oldNode = new NodeLocator.con2(originalStart, originalEnd).searchWithin(originalStructure);
|
| + }
|
| + int originalOffset = oldNode.offset;
|
| + Token parseToken = findTokenAt(firstToken, originalOffset);
|
| + if (parseToken == null) {
|
| + return null;
|
| + }
|
| + Parser parser = new Parser(_source, _errorListener);
|
| + parser.currentToken = parseToken;
|
| + while (newNode == null) {
|
| + ASTNode parent = oldNode.parent;
|
| + if (parent == null) {
|
| + parseToken = findFirstToken(parseToken);
|
| + parser.currentToken = parseToken;
|
| + return parser.parseCompilationUnit2() as ASTNode;
|
| + }
|
| + try {
|
| + IncrementalParseDispatcher dispatcher = new IncrementalParseDispatcher(parser, oldNode);
|
| + newNode = parent.accept(dispatcher);
|
| + } on InsufficientContextException catch (exception) {
|
| + oldNode = parent;
|
| + originalOffset = oldNode.offset;
|
| + parseToken = findTokenAt(parseToken, originalOffset);
|
| + parser.currentToken = parseToken;
|
| + } on JavaException catch (exception) {
|
| + return null;
|
| + }
|
| + }
|
| + if (newNode.offset != originalOffset) {
|
| + return null;
|
| + }
|
| + if (identical(oldNode, originalStructure)) {
|
| + return newNode as ASTNode;
|
| + }
|
| + ResolutionCopier.copyResolutionData(oldNode, newNode);
|
| + }
|
| + IncrementalASTCloner cloner = new IncrementalASTCloner(oldNode, newNode, _tokenMap);
|
| + return originalStructure.accept(cloner) as ASTNode;
|
| + }
|
| +
|
| + /**
|
| + * Return the first (non-EOF) token in the token stream containing the given token.
|
| + *
|
| + * @param firstToken the token from which the search is to begin
|
| + * @return the first token in the token stream containing the given token
|
| + */
|
| + Token findFirstToken(Token firstToken) {
|
| + while (firstToken.type != TokenType.EOF) {
|
| + firstToken = firstToken.previous;
|
| + }
|
| + return firstToken.next;
|
| + }
|
| +
|
| + /**
|
| + * Find the token at or before the given token with the given offset, or `null` if there is
|
| + * no such token.
|
| + *
|
| + * @param firstToken the token from which the search is to begin
|
| + * @param offset the offset of the token to be returned
|
| + * @return the token with the given offset
|
| + */
|
| + Token findTokenAt(Token firstToken, int offset) {
|
| + while (firstToken.offset > offset && firstToken.type != TokenType.EOF) {
|
| + firstToken = firstToken.previous;
|
| + }
|
| + if (firstToken.offset == offset) {
|
| + return firstToken;
|
| + }
|
| + return null;
|
| + }
|
| +}
|
| +/**
|
| + * Instances of the class `InsufficientContextException` represent a situation in which an AST
|
| + * node cannot be re-parsed because there is not enough context to know how to re-parse the node.
|
| + * Clients can attempt to re-parse the parent of the node.
|
| + */
|
| +class InsufficientContextException extends IncrementalParseException {
|
| +
|
| + /**
|
| + * Initialize a newly created exception to have no message and to be its own cause.
|
| + */
|
| + InsufficientContextException() : super();
|
| +
|
| + /**
|
| + * Initialize a newly created exception to have the given message and to be its own cause.
|
| + *
|
| + * @param message the message describing the reason for the exception
|
| + */
|
| + InsufficientContextException.con1(String message) : super.con1(message);
|
| +
|
| + /**
|
| + * Initialize a newly created exception to have no message and to have the given cause.
|
| + *
|
| + * @param cause the exception that caused this exception
|
| + */
|
| + InsufficientContextException.con2(Exception cause) : super.con2(cause);
|
| +}
|
| +/**
|
| * Instances of the class `Parser` are used to parse tokens into an AST structure.
|
| *
|
| * @coverage dart.engine.parser
|
| @@ -176,8 +1206,8 @@ class Parser {
|
| static String _HIDE = "hide";
|
| static String _OF = "of";
|
| static String _ON = "on";
|
| - static String _SHOW = "show";
|
| static String _NATIVE = "native";
|
| + static String _SHOW = "show";
|
|
|
| /**
|
| * Initialize a newly created parser.
|
| @@ -259,2168 +1289,1965 @@ class Parser {
|
| instrumentation.log();
|
| }
|
| }
|
| - void set currentToken(Token currentToken) {
|
| - this._currentToken = currentToken;
|
| - }
|
|
|
| /**
|
| - * Advance to the next token in the token stream.
|
| + * Parse an annotation.
|
| + *
|
| + * <pre>
|
| + * annotation ::=
|
| + * '@' qualified ('.' identifier)? arguments?
|
| + * </pre>
|
| + *
|
| + * @return the annotation that was parsed
|
| */
|
| - void advance() {
|
| - _currentToken = _currentToken.next;
|
| + Annotation parseAnnotation() {
|
| + Token atSign = expect2(TokenType.AT);
|
| + Identifier name = parsePrefixedIdentifier();
|
| + Token period = null;
|
| + SimpleIdentifier constructorName = null;
|
| + if (matches5(TokenType.PERIOD)) {
|
| + period = andAdvance;
|
| + constructorName = parseSimpleIdentifier();
|
| + }
|
| + ArgumentList arguments = null;
|
| + if (matches5(TokenType.OPEN_PAREN)) {
|
| + arguments = parseArgumentList();
|
| + }
|
| + return new Annotation.full(atSign, name, period, constructorName, arguments);
|
| }
|
|
|
| /**
|
| - * Append the character equivalent of the given scalar value to the given builder. Use the start
|
| - * and end indices to report an error, and don't append anything to the builder, if the scalar
|
| - * value is invalid.
|
| + * Parse an argument.
|
| *
|
| - * @param builder the builder to which the scalar value is to be appended
|
| - * @param escapeSequence the escape sequence that was parsed to produce the scalar value
|
| - * @param scalarValue the value to be appended
|
| - * @param startIndex the index of the first character representing the scalar value
|
| - * @param endIndex the index of the last character representing the scalar value
|
| + * <pre>
|
| + * argument ::=
|
| + * namedArgument
|
| + * | expression
|
| + *
|
| + * namedArgument ::=
|
| + * label expression
|
| + * </pre>
|
| + *
|
| + * @return the argument that was parsed
|
| */
|
| - void appendScalarValue(JavaStringBuilder builder, String escapeSequence, int scalarValue, int startIndex, int endIndex) {
|
| - if (scalarValue < 0 || scalarValue > Character.MAX_CODE_POINT || (scalarValue >= 0xD800 && scalarValue <= 0xDFFF)) {
|
| - reportError8(ParserErrorCode.INVALID_CODE_POINT, [escapeSequence]);
|
| - return;
|
| - }
|
| - if (scalarValue < Character.MAX_VALUE) {
|
| - builder.appendChar(scalarValue as int);
|
| + Expression parseArgument() {
|
| + if (matchesIdentifier() && matches4(peek(), TokenType.COLON)) {
|
| + return new NamedExpression.full(parseLabel(), parseExpression2());
|
| } else {
|
| - builder.append(Character.toChars(scalarValue));
|
| + return parseExpression2();
|
| }
|
| }
|
|
|
| /**
|
| - * Compute the content of a string with the given literal representation.
|
| + * Parse a list of arguments.
|
| *
|
| - * @param lexeme the literal representation of the string
|
| - * @param first `true` if this is the first token in a string literal
|
| - * @param last `true` if this is the last token in a string literal
|
| - * @return the actual value of the string
|
| - */
|
| - String computeStringValue(String lexeme, bool first, bool last) {
|
| - bool isRaw = false;
|
| - int start = 0;
|
| - if (first) {
|
| - if (lexeme.startsWith("r\"\"\"") || lexeme.startsWith("r'''")) {
|
| - isRaw = true;
|
| - start += 4;
|
| - } else if (lexeme.startsWith("r\"") || lexeme.startsWith("r'")) {
|
| - isRaw = true;
|
| - start += 2;
|
| - } else if (lexeme.startsWith("\"\"\"") || lexeme.startsWith("'''")) {
|
| - start += 3;
|
| - } else if (lexeme.startsWith("\"") || lexeme.startsWith("'")) {
|
| - start += 1;
|
| - }
|
| - }
|
| - int end = lexeme.length;
|
| - if (last) {
|
| - if (lexeme.endsWith("\"\"\"") || lexeme.endsWith("'''")) {
|
| - end -= 3;
|
| - } else if (lexeme.endsWith("\"") || lexeme.endsWith("'")) {
|
| - end -= 1;
|
| - }
|
| - }
|
| - if (end - start + 1 < 0) {
|
| - AnalysisEngine.instance.logger.logError("Internal error: computeStringValue(${lexeme}, ${first}, ${last})");
|
| - return "";
|
| - }
|
| - if (isRaw) {
|
| - return lexeme.substring(start, end);
|
| - }
|
| - JavaStringBuilder builder = new JavaStringBuilder();
|
| - int index = start;
|
| - while (index < end) {
|
| - index = translateCharacter(builder, lexeme, index);
|
| - }
|
| - return builder.toString();
|
| - }
|
| -
|
| - /**
|
| - * Convert the given method declaration into the nearest valid top-level function declaration.
|
| + * <pre>
|
| + * arguments ::=
|
| + * '(' argumentList? ')'
|
| *
|
| - * @param method the method to be converted
|
| - * @return the function declaration that most closely captures the components of the given method
|
| - * declaration
|
| - */
|
| - FunctionDeclaration convertToFunctionDeclaration(MethodDeclaration method) => new FunctionDeclaration.full(method.documentationComment, method.metadata, method.externalKeyword, method.returnType, method.propertyKeyword, method.name, new FunctionExpression.full(method.parameters, method.body));
|
| -
|
| - /**
|
| - * Return `true` if the current token could be the start of a compilation unit member. This
|
| - * method is used for recovery purposes to decide when to stop skipping tokens after finding an
|
| - * error while parsing a compilation unit member.
|
| + * argumentList ::=
|
| + * namedArgument (',' namedArgument)*
|
| + * | expressionList (',' namedArgument)*
|
| + * </pre>
|
| *
|
| - * @return `true` if the current token could be the start of a compilation unit member
|
| + * @return the argument list that was parsed
|
| */
|
| - bool couldBeStartOfCompilationUnitMember() {
|
| - if ((matches(Keyword.IMPORT) || matches(Keyword.EXPORT) || matches(Keyword.LIBRARY) || matches(Keyword.PART)) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| - return true;
|
| - } else if (matches(Keyword.CLASS)) {
|
| - return true;
|
| - } else if (matches(Keyword.TYPEDEF) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| - return true;
|
| - } else if (matches(Keyword.VOID) || ((matches(Keyword.GET) || matches(Keyword.SET)) && matchesIdentifier2(peek())) || (matches(Keyword.OPERATOR) && isOperator(peek()))) {
|
| - return true;
|
| - } else if (matchesIdentifier()) {
|
| - if (matches4(peek(), TokenType.OPEN_PAREN)) {
|
| - return true;
|
| - }
|
| - Token token = skipReturnType(_currentToken);
|
| - if (token == null) {
|
| - return false;
|
| - }
|
| - if (matches(Keyword.GET) || matches(Keyword.SET) || (matches(Keyword.OPERATOR) && isOperator(peek())) || matchesIdentifier()) {
|
| - return true;
|
| + ArgumentList parseArgumentList() {
|
| + Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| + List<Expression> arguments = new List<Expression>();
|
| + if (matches5(TokenType.CLOSE_PAREN)) {
|
| + return new ArgumentList.full(leftParenthesis, arguments, andAdvance);
|
| + }
|
| + Expression argument = parseArgument();
|
| + arguments.add(argument);
|
| + bool foundNamedArgument = argument is NamedExpression;
|
| + bool generatedError = false;
|
| + while (optional(TokenType.COMMA)) {
|
| + argument = parseArgument();
|
| + arguments.add(argument);
|
| + if (foundNamedArgument) {
|
| + if (!generatedError && argument is! NamedExpression) {
|
| + reportError8(ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT, []);
|
| + generatedError = true;
|
| + }
|
| + } else if (argument is NamedExpression) {
|
| + foundNamedArgument = true;
|
| }
|
| }
|
| - return false;
|
| + Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| + return new ArgumentList.full(leftParenthesis, arguments, rightParenthesis);
|
| }
|
|
|
| /**
|
| - * Create a synthetic identifier.
|
| - *
|
| - * @return the synthetic identifier that was created
|
| - */
|
| - SimpleIdentifier createSyntheticIdentifier() => new SimpleIdentifier.full(createSyntheticToken2(TokenType.IDENTIFIER));
|
| -
|
| - /**
|
| - * Create a synthetic string literal.
|
| - *
|
| - * @return the synthetic string literal that was created
|
| - */
|
| - SimpleStringLiteral createSyntheticStringLiteral() => new SimpleStringLiteral.full(createSyntheticToken2(TokenType.STRING), "");
|
| -
|
| - /**
|
| - * Create a synthetic token representing the given keyword.
|
| - *
|
| - * @return the synthetic token that was created
|
| - */
|
| - Token createSyntheticToken(Keyword keyword) => new Parser_SyntheticKeywordToken(keyword, _currentToken.offset);
|
| -
|
| - /**
|
| - * Create a synthetic token with the given type.
|
| - *
|
| - * @return the synthetic token that was created
|
| - */
|
| - Token createSyntheticToken2(TokenType type) => new StringToken(type, "", _currentToken.offset);
|
| -
|
| - /**
|
| - * Check that the given expression is assignable and report an error if it isn't.
|
| + * Parse a bitwise or expression.
|
| *
|
| * <pre>
|
| - * assignableExpression ::=
|
| - * primary (arguments* assignableSelector)+
|
| - * | 'super' assignableSelector
|
| - * | identifier
|
| - *
|
| - * assignableSelector ::=
|
| - * '[' expression ']'
|
| - * | '.' identifier
|
| + * bitwiseOrExpression ::=
|
| + * bitwiseXorExpression ('|' bitwiseXorExpression)*
|
| + * | 'super' ('|' bitwiseXorExpression)+
|
| * </pre>
|
| *
|
| - * @param expression the expression being checked
|
| + * @return the bitwise or expression that was parsed
|
| */
|
| - void ensureAssignable(Expression expression) {
|
| - if (expression != null && !expression.isAssignable) {
|
| - reportError8(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, []);
|
| + Expression parseBitwiseOrExpression() {
|
| + Expression expression;
|
| + if (matches(Keyword.SUPER) && matches4(peek(), TokenType.BAR)) {
|
| + expression = new SuperExpression.full(andAdvance);
|
| + } else {
|
| + expression = parseBitwiseXorExpression();
|
| }
|
| - }
|
| -
|
| - /**
|
| - * If the current token is a keyword matching the given string, return it after advancing to the
|
| - * next token. Otherwise report an error and return the current token without advancing.
|
| - *
|
| - * @param keyword the keyword that is expected
|
| - * @return the token that matched the given type
|
| - */
|
| - Token expect(Keyword keyword) {
|
| - if (matches(keyword)) {
|
| - return andAdvance;
|
| + while (matches5(TokenType.BAR)) {
|
| + Token operator = andAdvance;
|
| + expression = new BinaryExpression.full(expression, operator, parseBitwiseXorExpression());
|
| }
|
| - reportError8(ParserErrorCode.EXPECTED_TOKEN, [keyword.syntax]);
|
| - return _currentToken;
|
| + return expression;
|
| }
|
|
|
| /**
|
| - * If the current token has the expected type, return it after advancing to the next token.
|
| - * Otherwise report an error and return the current token without advancing.
|
| + * Parse a block.
|
| *
|
| - * @param type the type of token that is expected
|
| - * @return the token that matched the given type
|
| - */
|
| - Token expect2(TokenType type) {
|
| - if (matches5(type)) {
|
| - return andAdvance;
|
| - }
|
| - if (identical(type, TokenType.SEMICOLON)) {
|
| - reportError9(ParserErrorCode.EXPECTED_TOKEN, _currentToken.previous, [type.lexeme]);
|
| - } else {
|
| - reportError8(ParserErrorCode.EXPECTED_TOKEN, [type.lexeme]);
|
| - }
|
| - return _currentToken;
|
| - }
|
| -
|
| - /**
|
| - * Search the given list of ranges for a range that contains the given index. Return the range
|
| - * that was found, or `null` if none of the ranges contain the index.
|
| + * <pre>
|
| + * block ::=
|
| + * '{' statements '}'
|
| + * </pre>
|
| *
|
| - * @param ranges the ranges to be searched
|
| - * @param index the index contained in the returned range
|
| - * @return the range that was found
|
| + * @return the block that was parsed
|
| */
|
| - List<int> findRange(List<List<int>> ranges, int index) {
|
| - for (List<int> range in ranges) {
|
| - if (range[0] <= index && index <= range[1]) {
|
| - return range;
|
| - } else if (index < range[0]) {
|
| - return null;
|
| + Block parseBlock() {
|
| + Token leftBracket = expect2(TokenType.OPEN_CURLY_BRACKET);
|
| + List<Statement> statements = new List<Statement>();
|
| + Token statementStart = _currentToken;
|
| + while (!matches5(TokenType.EOF) && !matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| + Statement statement = parseStatement2();
|
| + if (statement != null) {
|
| + statements.add(statement);
|
| }
|
| - }
|
| - return null;
|
| - }
|
| - void gatherTodoComments(Token token) {
|
| - while (token != null && token.type != TokenType.EOF) {
|
| - Token commentToken = token.precedingComments;
|
| - while (commentToken != null) {
|
| - if (identical(commentToken.type, TokenType.SINGLE_LINE_COMMENT) || identical(commentToken.type, TokenType.MULTI_LINE_COMMENT)) {
|
| - scrapeTodoComment(commentToken);
|
| - }
|
| - commentToken = commentToken.next;
|
| + if (identical(_currentToken, statementStart)) {
|
| + reportError9(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
|
| + advance();
|
| }
|
| - token = token.next;
|
| + statementStart = _currentToken;
|
| }
|
| + Token rightBracket = expect2(TokenType.CLOSE_CURLY_BRACKET);
|
| + return new Block.full(leftBracket, statements, rightBracket);
|
| }
|
|
|
| /**
|
| - * Advance to the next token in the token stream, making it the new current token.
|
| + * Parse a class member.
|
| *
|
| - * @return the token that was current before this method was invoked
|
| - */
|
| - Token get andAdvance {
|
| - Token token = _currentToken;
|
| - advance();
|
| - return token;
|
| - }
|
| -
|
| - /**
|
| - * Return a list of the ranges of characters in the given comment string that should be treated as
|
| - * code blocks.
|
| + * <pre>
|
| + * classMemberDefinition ::=
|
| + * declaration ';'
|
| + * | methodSignature functionBody
|
| + * </pre>
|
| *
|
| - * @param comment the comment being processed
|
| - * @return the ranges of characters that should be treated as code blocks
|
| + * @param className the name of the class containing the member being parsed
|
| + * @return the class member that was parsed, or `null` if what was found was not a valid
|
| + * class member
|
| */
|
| - List<List<int>> getCodeBlockRanges(String comment) {
|
| - List<List<int>> ranges = new List<List<int>>();
|
| - int length = comment.length;
|
| - int index = 0;
|
| - if (comment.startsWith("/**") || comment.startsWith("///")) {
|
| - index = 3;
|
| - }
|
| - while (index < length) {
|
| - int currentChar = comment.codeUnitAt(index);
|
| - if (currentChar == 0xD || currentChar == 0xA) {
|
| - index = index + 1;
|
| - while (index < length && Character.isWhitespace(comment.codeUnitAt(index))) {
|
| - index = index + 1;
|
| - }
|
| - if (JavaString.startsWithBefore(comment, "* ", index)) {
|
| - int end = index + 6;
|
| - while (end < length && comment.codeUnitAt(end) != 0xD && comment.codeUnitAt(end) != 0xA) {
|
| - end = end + 1;
|
| + ClassMember parseClassMember(String className) {
|
| + CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| + Modifiers modifiers = parseModifiers();
|
| + if (matches(Keyword.VOID)) {
|
| + TypeName returnType = parseReturnType();
|
| + if (matches(Keyword.GET) && matchesIdentifier2(peek())) {
|
| + validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| + return parseGetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, returnType);
|
| + } else if (matches(Keyword.SET) && matchesIdentifier2(peek())) {
|
| + validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| + return parseSetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, returnType);
|
| + } else if (matches(Keyword.OPERATOR) && isOperator(peek())) {
|
| + validateModifiersForOperator(modifiers);
|
| + return parseOperator(commentAndMetadata, modifiers.externalKeyword, returnType);
|
| + } else if (matchesIdentifier() && matchesAny(peek(), [
|
| + TokenType.OPEN_PAREN,
|
| + TokenType.OPEN_CURLY_BRACKET,
|
| + TokenType.FUNCTION])) {
|
| + validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| + return parseMethodDeclaration(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, returnType);
|
| + } else {
|
| + if (matchesIdentifier()) {
|
| + if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
|
| + reportError(ParserErrorCode.VOID_VARIABLE, returnType, []);
|
| + return parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, validateModifiersForField(modifiers), returnType);
|
| }
|
| - ranges.add(<int> [index, end]);
|
| - index = end;
|
| }
|
| - } else if (JavaString.startsWithBefore(comment, "[:", index)) {
|
| - int end = JavaString.indexOf(comment, ":]", index + 2);
|
| - if (end < 0) {
|
| - end = length;
|
| + if (isOperator(_currentToken)) {
|
| + validateModifiersForOperator(modifiers);
|
| + return parseOperator(commentAndMetadata, modifiers.externalKeyword, returnType);
|
| }
|
| - ranges.add(<int> [index, end]);
|
| - index = end + 1;
|
| - } else {
|
| - index = index + 1;
|
| + reportError9(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken, []);
|
| + return null;
|
| + }
|
| + } else if (matches(Keyword.GET) && matchesIdentifier2(peek())) {
|
| + validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| + return parseGetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, null);
|
| + } else if (matches(Keyword.SET) && matchesIdentifier2(peek())) {
|
| + validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| + return parseSetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, null);
|
| + } else if (matches(Keyword.OPERATOR) && isOperator(peek())) {
|
| + validateModifiersForOperator(modifiers);
|
| + return parseOperator(commentAndMetadata, modifiers.externalKeyword, null);
|
| + } else if (!matchesIdentifier()) {
|
| + if (isOperator(_currentToken)) {
|
| + validateModifiersForOperator(modifiers);
|
| + return parseOperator(commentAndMetadata, modifiers.externalKeyword, null);
|
| + }
|
| + reportError9(ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken, []);
|
| + return null;
|
| + } else if (matches4(peek(), TokenType.PERIOD) && matchesIdentifier2(peek2(2)) && matches4(peek2(3), TokenType.OPEN_PAREN)) {
|
| + return parseConstructor(commentAndMetadata, modifiers.externalKeyword, validateModifiersForConstructor(modifiers), modifiers.factoryKeyword, parseSimpleIdentifier(), andAdvance, parseSimpleIdentifier(), parseFormalParameterList());
|
| + } else if (matches4(peek(), TokenType.OPEN_PAREN)) {
|
| + SimpleIdentifier methodName = parseSimpleIdentifier();
|
| + FormalParameterList parameters = parseFormalParameterList();
|
| + if (matches5(TokenType.COLON) || modifiers.factoryKeyword != null || methodName.name == className) {
|
| + return parseConstructor(commentAndMetadata, modifiers.externalKeyword, validateModifiersForConstructor(modifiers), modifiers.factoryKeyword, methodName, null, null, parameters);
|
| + }
|
| + validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| + validateFormalParameterList(parameters);
|
| + return parseMethodDeclaration2(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, null, methodName, parameters);
|
| + } else if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
|
| + if (modifiers.constKeyword == null && modifiers.finalKeyword == null && modifiers.varKeyword == null) {
|
| + reportError8(ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE, []);
|
| }
|
| + return parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, validateModifiersForField(modifiers), null);
|
| }
|
| - return ranges;
|
| + TypeName type = parseTypeName();
|
| + if (matches(Keyword.GET) && matchesIdentifier2(peek())) {
|
| + validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| + return parseGetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, type);
|
| + } else if (matches(Keyword.SET) && matchesIdentifier2(peek())) {
|
| + validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| + return parseSetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, type);
|
| + } else if (matches(Keyword.OPERATOR) && isOperator(peek())) {
|
| + validateModifiersForOperator(modifiers);
|
| + return parseOperator(commentAndMetadata, modifiers.externalKeyword, type);
|
| + } else if (!matchesIdentifier()) {
|
| + if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| + return parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, validateModifiersForField(modifiers), type);
|
| + }
|
| + if (isOperator(_currentToken)) {
|
| + validateModifiersForOperator(modifiers);
|
| + return parseOperator(commentAndMetadata, modifiers.externalKeyword, type);
|
| + }
|
| + reportError9(ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken, []);
|
| + return null;
|
| + } else if (matches4(peek(), TokenType.OPEN_PAREN)) {
|
| + SimpleIdentifier methodName = parseSimpleIdentifier();
|
| + FormalParameterList parameters = parseFormalParameterList();
|
| + if (methodName.name == className) {
|
| + reportError(ParserErrorCode.CONSTRUCTOR_WITH_RETURN_TYPE, type, []);
|
| + return parseConstructor(commentAndMetadata, modifiers.externalKeyword, validateModifiersForConstructor(modifiers), modifiers.factoryKeyword, methodName, null, null, parameters);
|
| + }
|
| + validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| + validateFormalParameterList(parameters);
|
| + return parseMethodDeclaration2(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, type, methodName, parameters);
|
| + }
|
| + return parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, validateModifiersForField(modifiers), type);
|
| }
|
|
|
| /**
|
| - * Return the end token associated with the given begin token, or `null` if either the given
|
| - * token is not a begin token or it does not have an end token associated with it.
|
| + * Parse a compilation unit.
|
| *
|
| - * @param beginToken the token that is expected to have an end token associated with it
|
| - * @return the end token associated with the begin token
|
| + * Specified:
|
| + *
|
| + * <pre>
|
| + * compilationUnit ::=
|
| + * scriptTag? directive* topLevelDeclaration*
|
| + * </pre>
|
| + * Actual:
|
| + *
|
| + * <pre>
|
| + * compilationUnit ::=
|
| + * scriptTag? topLevelElement*
|
| + *
|
| + * topLevelElement ::=
|
| + * directive
|
| + * | topLevelDeclaration
|
| + * </pre>
|
| + *
|
| + * @return the compilation unit that was parsed
|
| */
|
| - Token getEndToken(Token beginToken) {
|
| - if (beginToken is BeginToken) {
|
| - return ((beginToken as BeginToken)).endToken;
|
| + CompilationUnit parseCompilationUnit2() {
|
| + Token firstToken = _currentToken;
|
| + ScriptTag scriptTag = null;
|
| + if (matches5(TokenType.SCRIPT_TAG)) {
|
| + scriptTag = new ScriptTag.full(andAdvance);
|
| }
|
| - return null;
|
| + bool libraryDirectiveFound = false;
|
| + bool partOfDirectiveFound = false;
|
| + bool partDirectiveFound = false;
|
| + bool directiveFoundAfterDeclaration = false;
|
| + List<Directive> directives = new List<Directive>();
|
| + List<CompilationUnitMember> declarations = new List<CompilationUnitMember>();
|
| + Token memberStart = _currentToken;
|
| + while (!matches5(TokenType.EOF)) {
|
| + CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| + if ((matches(Keyword.IMPORT) || matches(Keyword.EXPORT) || matches(Keyword.LIBRARY) || matches(Keyword.PART)) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| + Directive directive = parseDirective(commentAndMetadata);
|
| + if (declarations.length > 0 && !directiveFoundAfterDeclaration) {
|
| + reportError8(ParserErrorCode.DIRECTIVE_AFTER_DECLARATION, []);
|
| + directiveFoundAfterDeclaration = true;
|
| + }
|
| + if (directive is LibraryDirective) {
|
| + if (libraryDirectiveFound) {
|
| + reportError8(ParserErrorCode.MULTIPLE_LIBRARY_DIRECTIVES, []);
|
| + } else {
|
| + if (directives.length > 0) {
|
| + reportError8(ParserErrorCode.LIBRARY_DIRECTIVE_NOT_FIRST, []);
|
| + }
|
| + libraryDirectiveFound = true;
|
| + }
|
| + } else if (directive is PartDirective) {
|
| + partDirectiveFound = true;
|
| + } else if (partDirectiveFound) {
|
| + if (directive is ExportDirective) {
|
| + reportError9(ParserErrorCode.EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE, ((directive as NamespaceDirective)).keyword, []);
|
| + } else if (directive is ImportDirective) {
|
| + reportError9(ParserErrorCode.IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE, ((directive as NamespaceDirective)).keyword, []);
|
| + }
|
| + }
|
| + if (directive is PartOfDirective) {
|
| + if (partOfDirectiveFound) {
|
| + reportError8(ParserErrorCode.MULTIPLE_PART_OF_DIRECTIVES, []);
|
| + } else {
|
| + for (Directive precedingDirective in directives) {
|
| + reportError9(ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART, precedingDirective.keyword, []);
|
| + }
|
| + partOfDirectiveFound = true;
|
| + }
|
| + } else {
|
| + if (partOfDirectiveFound) {
|
| + reportError9(ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART, directive.keyword, []);
|
| + }
|
| + }
|
| + directives.add(directive);
|
| + } else if (matches5(TokenType.SEMICOLON)) {
|
| + reportError9(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
|
| + advance();
|
| + } else {
|
| + CompilationUnitMember member = parseCompilationUnitMember(commentAndMetadata);
|
| + if (member != null) {
|
| + declarations.add(member);
|
| + }
|
| + }
|
| + if (identical(_currentToken, memberStart)) {
|
| + reportError9(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
|
| + advance();
|
| + while (!matches5(TokenType.EOF) && !couldBeStartOfCompilationUnitMember()) {
|
| + advance();
|
| + }
|
| + }
|
| + memberStart = _currentToken;
|
| + }
|
| + return new CompilationUnit.full(firstToken, scriptTag, directives, declarations, _currentToken);
|
| }
|
|
|
| /**
|
| - * Return `true` if the current token is the first token of a return type that is followed
|
| - * by an identifier, possibly followed by a list of type parameters, followed by a
|
| - * left-parenthesis. This is used by parseTypeAlias to determine whether or not to parse a return
|
| - * type.
|
| + * Parse a conditional expression.
|
| *
|
| - * @return `true` if we can successfully parse the rest of a type alias if we first parse a
|
| - * return type.
|
| + * <pre>
|
| + * conditionalExpression ::=
|
| + * logicalOrExpression ('?' expressionWithoutCascade ':' expressionWithoutCascade)?
|
| + * </pre>
|
| + *
|
| + * @return the conditional expression that was parsed
|
| */
|
| - bool hasReturnTypeInTypeAlias() {
|
| - Token next = skipReturnType(_currentToken);
|
| - if (next == null) {
|
| - return false;
|
| + Expression parseConditionalExpression() {
|
| + Expression condition = parseLogicalOrExpression();
|
| + if (!matches5(TokenType.QUESTION)) {
|
| + return condition;
|
| }
|
| - return matchesIdentifier2(next);
|
| + Token question = andAdvance;
|
| + Expression thenExpression = parseExpressionWithoutCascade();
|
| + Token colon = expect2(TokenType.COLON);
|
| + Expression elseExpression = parseExpressionWithoutCascade();
|
| + return new ConditionalExpression.full(condition, question, thenExpression, colon, elseExpression);
|
| }
|
|
|
| /**
|
| - * Return `true` if the current token appears to be the beginning of a function declaration.
|
| + * Parse the name of a constructor.
|
| *
|
| - * @return `true` if the current token appears to be the beginning of a function declaration
|
| - */
|
| - bool isFunctionDeclaration() {
|
| - if (matches(Keyword.VOID)) {
|
| - return true;
|
| - }
|
| - Token afterReturnType = skipTypeName(_currentToken);
|
| - if (afterReturnType == null) {
|
| - afterReturnType = _currentToken;
|
| - }
|
| - Token afterIdentifier = skipSimpleIdentifier(afterReturnType);
|
| - if (afterIdentifier == null) {
|
| - afterIdentifier = skipSimpleIdentifier(_currentToken);
|
| - }
|
| - if (afterIdentifier == null) {
|
| - return false;
|
| - }
|
| - if (isFunctionExpression(afterIdentifier)) {
|
| - return true;
|
| - }
|
| - if (matches(Keyword.GET)) {
|
| - Token afterName = skipSimpleIdentifier(_currentToken.next);
|
| - if (afterName == null) {
|
| - return false;
|
| - }
|
| - return matches4(afterName, TokenType.FUNCTION) || matches4(afterName, TokenType.OPEN_CURLY_BRACKET);
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * Return `true` if the given token appears to be the beginning of a function expression.
|
| + * <pre>
|
| + * constructorName:
|
| + * type ('.' identifier)?
|
| + * </pre>
|
| *
|
| - * @param startToken the token that might be the start of a function expression
|
| - * @return `true` if the given token appears to be the beginning of a function expression
|
| + * @return the constructor name that was parsed
|
| */
|
| - bool isFunctionExpression(Token startToken) {
|
| - Token afterParameters = skipFormalParameterList(startToken);
|
| - if (afterParameters == null) {
|
| - return false;
|
| + ConstructorName parseConstructorName() {
|
| + TypeName type = parseTypeName();
|
| + Token period = null;
|
| + SimpleIdentifier name = null;
|
| + if (matches5(TokenType.PERIOD)) {
|
| + period = andAdvance;
|
| + name = parseSimpleIdentifier();
|
| }
|
| - return matchesAny(afterParameters, [TokenType.OPEN_CURLY_BRACKET, TokenType.FUNCTION]);
|
| + return new ConstructorName.full(type, period, name);
|
| }
|
|
|
| /**
|
| - * Return `true` if the given character is a valid hexadecimal digit.
|
| - *
|
| - * @param character the character being tested
|
| - * @return `true` if the character is a valid hexadecimal digit
|
| - */
|
| - bool isHexDigit(int character) => (0x30 <= character && character <= 0x39) || (0x41 <= character && character <= 0x46) || (0x61 <= character && character <= 0x66);
|
| -
|
| - /**
|
| - * Return `true` if the current token is the first token in an initialized variable
|
| - * declaration rather than an expression. This method assumes that we have already skipped past
|
| - * any metadata that might be associated with the declaration.
|
| + * Parse an expression that does not contain any cascades.
|
| *
|
| * <pre>
|
| - * initializedVariableDeclaration ::=
|
| - * declaredIdentifier ('=' expression)? (',' initializedIdentifier)*
|
| - *
|
| - * declaredIdentifier ::=
|
| - * metadata finalConstVarOrType identifier
|
| - *
|
| - * finalConstVarOrType ::=
|
| - * 'final' type?
|
| - * | 'const' type?
|
| - * | 'var'
|
| - * | type
|
| - *
|
| - * type ::=
|
| - * qualified typeArguments?
|
| - *
|
| - * initializedIdentifier ::=
|
| - * identifier ('=' expression)?
|
| + * expression ::=
|
| + * assignableExpression assignmentOperator expression
|
| + * | conditionalExpression cascadeSection*
|
| + * | throwExpression
|
| * </pre>
|
| *
|
| - * @return `true` if the current token is the first token in an initialized variable
|
| - * declaration
|
| + * @return the expression that was parsed
|
| */
|
| - bool isInitializedVariableDeclaration() {
|
| - if (matches(Keyword.FINAL) || matches(Keyword.VAR)) {
|
| - return true;
|
| - }
|
| - if (matches(Keyword.CONST)) {
|
| - return !matchesAny(peek(), [
|
| - TokenType.LT,
|
| - TokenType.OPEN_CURLY_BRACKET,
|
| - TokenType.OPEN_SQUARE_BRACKET,
|
| - TokenType.INDEX]);
|
| - }
|
| - Token token = skipTypeName(_currentToken);
|
| - if (token == null) {
|
| - return false;
|
| + Expression parseExpression2() {
|
| + if (matches(Keyword.THROW)) {
|
| + return parseThrowExpression();
|
| + } else if (matches(Keyword.RETHROW)) {
|
| + return parseRethrowExpression();
|
| }
|
| - token = skipSimpleIdentifier(token);
|
| - if (token == null) {
|
| - return false;
|
| + Expression expression = parseConditionalExpression();
|
| + TokenType tokenType = _currentToken.type;
|
| + if (identical(tokenType, TokenType.PERIOD_PERIOD)) {
|
| + List<Expression> cascadeSections = new List<Expression>();
|
| + while (identical(tokenType, TokenType.PERIOD_PERIOD)) {
|
| + Expression section = parseCascadeSection();
|
| + if (section != null) {
|
| + cascadeSections.add(section);
|
| + }
|
| + tokenType = _currentToken.type;
|
| + }
|
| + return new CascadeExpression.full(expression, cascadeSections);
|
| + } else if (tokenType.isAssignmentOperator) {
|
| + Token operator = andAdvance;
|
| + ensureAssignable(expression);
|
| + return new AssignmentExpression.full(expression, operator, parseExpression2());
|
| }
|
| - TokenType type = token.type;
|
| - return identical(type, TokenType.EQ) || identical(type, TokenType.COMMA) || identical(type, TokenType.SEMICOLON) || matches3(token, Keyword.IN);
|
| + return expression;
|
| }
|
|
|
| /**
|
| - * Given that we have just found bracketed text within a comment, look to see whether that text is
|
| - * (a) followed by a parenthesized link address, (b) followed by a colon, or (c) followed by
|
| - * optional whitespace and another square bracket.
|
| + * Parse an expression that does not contain any cascades.
|
| *
|
| - * This method uses the syntax described by the <a
|
| - * href="http://daringfireball.net/projects/markdown/syntax">markdown</a> project.
|
| + * <pre>
|
| + * expressionWithoutCascade ::=
|
| + * assignableExpression assignmentOperator expressionWithoutCascade
|
| + * | conditionalExpression
|
| + * | throwExpressionWithoutCascade
|
| + * </pre>
|
| *
|
| - * @param comment the comment text in which the bracketed text was found
|
| - * @param rightIndex the index of the right bracket
|
| - * @return `true` if the bracketed text is followed by a link address
|
| + * @return the expression that was parsed
|
| */
|
| - bool isLinkText(String comment, int rightIndex) {
|
| - int length = comment.length;
|
| - int index = rightIndex + 1;
|
| - if (index >= length) {
|
| - return false;
|
| - }
|
| - int nextChar = comment.codeUnitAt(index);
|
| - if (nextChar == 0x28 || nextChar == 0x3A) {
|
| - return true;
|
| + Expression parseExpressionWithoutCascade() {
|
| + if (matches(Keyword.THROW)) {
|
| + return parseThrowExpressionWithoutCascade();
|
| + } else if (matches(Keyword.RETHROW)) {
|
| + return parseRethrowExpression();
|
| }
|
| - while (Character.isWhitespace(nextChar)) {
|
| - index = index + 1;
|
| - if (index >= length) {
|
| - return false;
|
| - }
|
| - nextChar = comment.codeUnitAt(index);
|
| + Expression expression = parseConditionalExpression();
|
| + if (_currentToken.type.isAssignmentOperator) {
|
| + Token operator = andAdvance;
|
| + ensureAssignable(expression);
|
| + expression = new AssignmentExpression.full(expression, operator, parseExpressionWithoutCascade());
|
| }
|
| - return nextChar == 0x5B;
|
| + return expression;
|
| }
|
|
|
| /**
|
| - * Return `true` if the given token appears to be the beginning of an operator declaration.
|
| + * Parse a class extends clause.
|
| *
|
| - * @param startToken the token that might be the start of an operator declaration
|
| - * @return `true` if the given token appears to be the beginning of an operator declaration
|
| - */
|
| - bool isOperator(Token startToken) {
|
| - if (startToken.isOperator) {
|
| - Token token = startToken.next;
|
| - while (token.isOperator) {
|
| - token = token.next;
|
| - }
|
| - return matches4(token, TokenType.OPEN_PAREN);
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * Return `true` if the current token appears to be the beginning of a switch member.
|
| + * <pre>
|
| + * classExtendsClause ::=
|
| + * 'extends' type
|
| + * </pre>
|
| *
|
| - * @return `true` if the current token appears to be the beginning of a switch member
|
| + * @return the class extends clause that was parsed
|
| */
|
| - bool isSwitchMember() {
|
| - Token token = _currentToken;
|
| - while (matches4(token, TokenType.IDENTIFIER) && matches4(token.next, TokenType.COLON)) {
|
| - token = token.next.next;
|
| - }
|
| - if (identical(token.type, TokenType.KEYWORD)) {
|
| - Keyword keyword = ((token as KeywordToken)).keyword;
|
| - return identical(keyword, Keyword.CASE) || identical(keyword, Keyword.DEFAULT);
|
| - }
|
| - return false;
|
| + ExtendsClause parseExtendsClause() {
|
| + Token keyword = expect(Keyword.EXTENDS);
|
| + TypeName superclass = parseTypeName();
|
| + return new ExtendsClause.full(keyword, superclass);
|
| }
|
|
|
| /**
|
| - * Return `true` if the given token appears to be the first token of a type name that is
|
| - * followed by a variable or field formal parameter.
|
| + * Parse a list of formal parameters.
|
| *
|
| - * @param startToken the first token of the sequence being checked
|
| - * @return `true` if there is a type name and variable starting at the given token
|
| - */
|
| - bool isTypedIdentifier(Token startToken) {
|
| - Token token = skipReturnType(startToken);
|
| - if (token == null) {
|
| - return false;
|
| - } else if (matchesIdentifier2(token)) {
|
| - return true;
|
| - } else if (matches3(token, Keyword.THIS) && matches4(token.next, TokenType.PERIOD) && matchesIdentifier2(token.next.next)) {
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * Compare the given tokens to find the token that appears first in the source being parsed. That
|
| - * is, return the left-most of all of the tokens. The arguments are allowed to be `null`.
|
| - * Return the token with the smallest offset, or `null` if there are no arguments or if all
|
| - * of the arguments are `null`.
|
| + * <pre>
|
| + * formalParameterList ::=
|
| + * '(' ')'
|
| + * | '(' normalFormalParameters (',' optionalFormalParameters)? ')'
|
| + * | '(' optionalFormalParameters ')'
|
| *
|
| - * @param tokens the tokens being compared
|
| - * @return the token with the smallest offset
|
| + * normalFormalParameters ::=
|
| + * normalFormalParameter (',' normalFormalParameter)*
|
| + *
|
| + * optionalFormalParameters ::=
|
| + * optionalPositionalFormalParameters
|
| + * | namedFormalParameters
|
| + *
|
| + * optionalPositionalFormalParameters ::=
|
| + * '[' defaultFormalParameter (',' defaultFormalParameter)* ']'
|
| + *
|
| + * namedFormalParameters ::=
|
| + * '{' defaultNamedParameter (',' defaultNamedParameter)* '}'
|
| + * </pre>
|
| + *
|
| + * @return the formal parameters that were parsed
|
| */
|
| - Token lexicallyFirst(List<Token> tokens) {
|
| - Token first = null;
|
| - int firstOffset = 2147483647;
|
| - for (Token token in tokens) {
|
| - if (token != null) {
|
| - int offset = token.offset;
|
| - if (offset < firstOffset) {
|
| - first = token;
|
| - firstOffset = offset;
|
| + FormalParameterList parseFormalParameterList() {
|
| + Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| + if (matches5(TokenType.CLOSE_PAREN)) {
|
| + return new FormalParameterList.full(leftParenthesis, null, null, null, andAdvance);
|
| + }
|
| + List<FormalParameter> parameters = new List<FormalParameter>();
|
| + List<FormalParameter> normalParameters = new List<FormalParameter>();
|
| + List<FormalParameter> positionalParameters = new List<FormalParameter>();
|
| + List<FormalParameter> namedParameters = new List<FormalParameter>();
|
| + List<FormalParameter> currentParameters = normalParameters;
|
| + Token leftSquareBracket = null;
|
| + Token rightSquareBracket = null;
|
| + Token leftCurlyBracket = null;
|
| + Token rightCurlyBracket = null;
|
| + ParameterKind kind = ParameterKind.REQUIRED;
|
| + bool firstParameter = true;
|
| + bool reportedMuliplePositionalGroups = false;
|
| + bool reportedMulipleNamedGroups = false;
|
| + bool reportedMixedGroups = false;
|
| + bool wasOptionalParameter = false;
|
| + Token initialToken = null;
|
| + do {
|
| + if (firstParameter) {
|
| + firstParameter = false;
|
| + } else if (!optional(TokenType.COMMA)) {
|
| + if (getEndToken(leftParenthesis) != null) {
|
| + reportError8(ParserErrorCode.EXPECTED_TOKEN, [TokenType.COMMA.lexeme]);
|
| + } else {
|
| + reportError9(ParserErrorCode.MISSING_CLOSING_PARENTHESIS, _currentToken.previous, []);
|
| + break;
|
| + }
|
| + }
|
| + initialToken = _currentToken;
|
| + if (matches5(TokenType.OPEN_SQUARE_BRACKET)) {
|
| + wasOptionalParameter = true;
|
| + if (leftSquareBracket != null && !reportedMuliplePositionalGroups) {
|
| + reportError8(ParserErrorCode.MULTIPLE_POSITIONAL_PARAMETER_GROUPS, []);
|
| + reportedMuliplePositionalGroups = true;
|
| + }
|
| + if (leftCurlyBracket != null && !reportedMixedGroups) {
|
| + reportError8(ParserErrorCode.MIXED_PARAMETER_GROUPS, []);
|
| + reportedMixedGroups = true;
|
| + }
|
| + leftSquareBracket = andAdvance;
|
| + currentParameters = positionalParameters;
|
| + kind = ParameterKind.POSITIONAL;
|
| + } else if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| + wasOptionalParameter = true;
|
| + if (leftCurlyBracket != null && !reportedMulipleNamedGroups) {
|
| + reportError8(ParserErrorCode.MULTIPLE_NAMED_PARAMETER_GROUPS, []);
|
| + reportedMulipleNamedGroups = true;
|
| + }
|
| + if (leftSquareBracket != null && !reportedMixedGroups) {
|
| + reportError8(ParserErrorCode.MIXED_PARAMETER_GROUPS, []);
|
| + reportedMixedGroups = true;
|
| + }
|
| + leftCurlyBracket = andAdvance;
|
| + currentParameters = namedParameters;
|
| + kind = ParameterKind.NAMED;
|
| + }
|
| + FormalParameter parameter = parseFormalParameter(kind);
|
| + parameters.add(parameter);
|
| + currentParameters.add(parameter);
|
| + if (identical(kind, ParameterKind.REQUIRED) && wasOptionalParameter) {
|
| + reportError(ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS, parameter, []);
|
| + }
|
| + if (matches5(TokenType.CLOSE_SQUARE_BRACKET)) {
|
| + rightSquareBracket = andAdvance;
|
| + currentParameters = normalParameters;
|
| + if (leftSquareBracket == null) {
|
| + if (leftCurlyBracket != null) {
|
| + reportError8(ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
|
| + rightCurlyBracket = rightSquareBracket;
|
| + rightSquareBracket = null;
|
| + } else {
|
| + reportError8(ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP, ["["]);
|
| + }
|
| + }
|
| + kind = ParameterKind.REQUIRED;
|
| + } else if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| + rightCurlyBracket = andAdvance;
|
| + currentParameters = normalParameters;
|
| + if (leftCurlyBracket == null) {
|
| + if (leftSquareBracket != null) {
|
| + reportError8(ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
|
| + rightSquareBracket = rightCurlyBracket;
|
| + rightCurlyBracket = null;
|
| + } else {
|
| + reportError8(ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP, ["{"]);
|
| + }
|
| }
|
| + kind = ParameterKind.REQUIRED;
|
| }
|
| + } while (!matches5(TokenType.CLOSE_PAREN) && initialToken != _currentToken);
|
| + Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| + if (leftSquareBracket != null && rightSquareBracket == null) {
|
| + reportError8(ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
|
| }
|
| - return first;
|
| + if (leftCurlyBracket != null && rightCurlyBracket == null) {
|
| + reportError8(ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
|
| + }
|
| + if (leftSquareBracket == null) {
|
| + leftSquareBracket = leftCurlyBracket;
|
| + }
|
| + if (rightSquareBracket == null) {
|
| + rightSquareBracket = rightCurlyBracket;
|
| + }
|
| + return new FormalParameterList.full(leftParenthesis, parameters, leftSquareBracket, rightSquareBracket, rightParenthesis);
|
| }
|
|
|
| /**
|
| - * Return `true` if the current token matches the given keyword.
|
| + * Parse a function expression.
|
| *
|
| - * @param keyword the keyword that can optionally appear in the current location
|
| - * @return `true` if the current token matches the given keyword
|
| - */
|
| - bool matches(Keyword keyword) => matches3(_currentToken, keyword);
|
| -
|
| - /**
|
| - * Return `true` if the current token matches the given identifier.
|
| + * <pre>
|
| + * functionExpression ::=
|
| + * formalParameterList functionExpressionBody
|
| + * </pre>
|
| *
|
| - * @param identifier the identifier that can optionally appear in the current location
|
| - * @return `true` if the current token matches the given identifier
|
| + * @return the function expression that was parsed
|
| */
|
| - bool matches2(String identifier) => identical(_currentToken.type, TokenType.IDENTIFIER) && _currentToken.lexeme == identifier;
|
| + FunctionExpression parseFunctionExpression() {
|
| + FormalParameterList parameters = parseFormalParameterList();
|
| + validateFormalParameterList(parameters);
|
| + FunctionBody body = parseFunctionBody(false, ParserErrorCode.MISSING_FUNCTION_BODY, true);
|
| + return new FunctionExpression.full(parameters, body);
|
| + }
|
|
|
| /**
|
| - * Return `true` if the given token matches the given keyword.
|
| + * Parse an implements clause.
|
| *
|
| - * @param token the token being tested
|
| - * @param keyword the keyword that is being tested for
|
| - * @return `true` if the given token matches the given keyword
|
| + * <pre>
|
| + * implementsClause ::=
|
| + * 'implements' type (',' type)*
|
| + * </pre>
|
| + *
|
| + * @return the implements clause that was parsed
|
| */
|
| - bool matches3(Token token, Keyword keyword) => identical(token.type, TokenType.KEYWORD) && identical(((token as KeywordToken)).keyword, keyword);
|
| + ImplementsClause parseImplementsClause() {
|
| + Token keyword = expect(Keyword.IMPLEMENTS);
|
| + List<TypeName> interfaces = new List<TypeName>();
|
| + interfaces.add(parseTypeName());
|
| + while (optional(TokenType.COMMA)) {
|
| + interfaces.add(parseTypeName());
|
| + }
|
| + return new ImplementsClause.full(keyword, interfaces);
|
| + }
|
|
|
| /**
|
| - * Return `true` if the given token has the given type.
|
| + * Parse a label.
|
| *
|
| - * @param token the token being tested
|
| - * @param type the type of token that is being tested for
|
| - * @return `true` if the given token has the given type
|
| + * <pre>
|
| + * label ::=
|
| + * identifier ':'
|
| + * </pre>
|
| + *
|
| + * @return the label that was parsed
|
| */
|
| - bool matches4(Token token, TokenType type) => identical(token.type, type);
|
| + Label parseLabel() {
|
| + SimpleIdentifier label = parseSimpleIdentifier();
|
| + Token colon = expect2(TokenType.COLON);
|
| + return new Label.full(label, colon);
|
| + }
|
|
|
| /**
|
| - * Return `true` if the current token has the given type. Note that this method, unlike
|
| - * other variants, will modify the token stream if possible to match a wider range of tokens. In
|
| - * particular, if we are attempting to match a '>' and the next token is either a '>>' or '>>>',
|
| - * the token stream will be re-written and `true` will be returned.
|
| + * Parse a library identifier.
|
| *
|
| - * @param type the type of token that can optionally appear in the current location
|
| - * @return `true` if the current token has the given type
|
| + * <pre>
|
| + * libraryIdentifier ::=
|
| + * identifier ('.' identifier)*
|
| + * </pre>
|
| + *
|
| + * @return the library identifier that was parsed
|
| */
|
| - bool matches5(TokenType type) {
|
| - TokenType currentType = _currentToken.type;
|
| - if (currentType != type) {
|
| - if (identical(type, TokenType.GT)) {
|
| - if (identical(currentType, TokenType.GT_GT)) {
|
| - int offset = _currentToken.offset;
|
| - Token first = new Token(TokenType.GT, offset);
|
| - Token second = new Token(TokenType.GT, offset + 1);
|
| - second.setNext(_currentToken.next);
|
| - first.setNext(second);
|
| - _currentToken.previous.setNext(first);
|
| - _currentToken = first;
|
| - return true;
|
| - } else if (identical(currentType, TokenType.GT_EQ)) {
|
| - int offset = _currentToken.offset;
|
| - Token first = new Token(TokenType.GT, offset);
|
| - Token second = new Token(TokenType.EQ, offset + 1);
|
| - second.setNext(_currentToken.next);
|
| - first.setNext(second);
|
| - _currentToken.previous.setNext(first);
|
| - _currentToken = first;
|
| - return true;
|
| - } else if (identical(currentType, TokenType.GT_GT_EQ)) {
|
| - int offset = _currentToken.offset;
|
| - Token first = new Token(TokenType.GT, offset);
|
| - Token second = new Token(TokenType.GT, offset + 1);
|
| - Token third = new Token(TokenType.EQ, offset + 2);
|
| - third.setNext(_currentToken.next);
|
| - second.setNext(third);
|
| - first.setNext(second);
|
| - _currentToken.previous.setNext(first);
|
| - _currentToken = first;
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| + LibraryIdentifier parseLibraryIdentifier() {
|
| + List<SimpleIdentifier> components = new List<SimpleIdentifier>();
|
| + components.add(parseSimpleIdentifier());
|
| + while (matches5(TokenType.PERIOD)) {
|
| + advance();
|
| + components.add(parseSimpleIdentifier());
|
| }
|
| - return true;
|
| + return new LibraryIdentifier.full(components);
|
| }
|
|
|
| /**
|
| - * Return `true` if the given token has any one of the given types.
|
| + * Parse a logical or expression.
|
| *
|
| - * @param token the token being tested
|
| - * @param types the types of token that are being tested for
|
| - * @return `true` if the given token has any of the given types
|
| + * <pre>
|
| + * logicalOrExpression ::=
|
| + * logicalAndExpression ('||' logicalAndExpression)*
|
| + * </pre>
|
| + *
|
| + * @return the logical or expression that was parsed
|
| */
|
| - bool matchesAny(Token token, List<TokenType> types) {
|
| - TokenType actualType = token.type;
|
| - for (TokenType type in types) {
|
| - if (identical(actualType, type)) {
|
| - return true;
|
| - }
|
| + Expression parseLogicalOrExpression() {
|
| + Expression expression = parseLogicalAndExpression();
|
| + while (matches5(TokenType.BAR_BAR)) {
|
| + Token operator = andAdvance;
|
| + expression = new BinaryExpression.full(expression, operator, parseLogicalAndExpression());
|
| }
|
| - return false;
|
| + return expression;
|
| }
|
|
|
| /**
|
| - * Return `true` if the current token is a valid identifier. Valid identifiers include
|
| - * built-in identifiers (pseudo-keywords).
|
| + * Parse a map literal entry.
|
| *
|
| - * @return `true` if the current token is a valid identifier
|
| - */
|
| - bool matchesIdentifier() => matchesIdentifier2(_currentToken);
|
| -
|
| - /**
|
| - * Return `true` if the given token is a valid identifier. Valid identifiers include
|
| - * built-in identifiers (pseudo-keywords).
|
| - *
|
| - * @return `true` if the given token is a valid identifier
|
| - */
|
| - bool matchesIdentifier2(Token token) => matches4(token, TokenType.IDENTIFIER) || (matches4(token, TokenType.KEYWORD) && ((token as KeywordToken)).keyword.isPseudoKeyword);
|
| -
|
| - /**
|
| - * If the current token has the given type, then advance to the next token and return `true`
|
| - * . Otherwise, return `false` without advancing.
|
| + * <pre>
|
| + * mapLiteralEntry ::=
|
| + * expression ':' expression
|
| + * </pre>
|
| *
|
| - * @param type the type of token that can optionally appear in the current location
|
| - * @return `true` if the current token has the given type
|
| + * @return the map literal entry that was parsed
|
| */
|
| - bool optional(TokenType type) {
|
| - if (matches5(type)) {
|
| - advance();
|
| - return true;
|
| - }
|
| - return false;
|
| + MapLiteralEntry parseMapLiteralEntry() {
|
| + Expression key = parseExpression2();
|
| + Token separator = expect2(TokenType.COLON);
|
| + Expression value = parseExpression2();
|
| + return new MapLiteralEntry.full(key, separator, value);
|
| }
|
|
|
| /**
|
| - * Parse an additive expression.
|
| + * Parse a normal formal parameter.
|
| *
|
| * <pre>
|
| - * additiveExpression ::=
|
| - * multiplicativeExpression (additiveOperator multiplicativeExpression)*
|
| - * | 'super' (additiveOperator multiplicativeExpression)+
|
| + * normalFormalParameter ::=
|
| + * functionSignature
|
| + * | fieldFormalParameter
|
| + * | simpleFormalParameter
|
| + *
|
| + * functionSignature:
|
| + * metadata returnType? identifier formalParameterList
|
| + *
|
| + * fieldFormalParameter ::=
|
| + * metadata finalConstVarOrType? 'this' '.' identifier
|
| + *
|
| + * simpleFormalParameter ::=
|
| + * declaredIdentifier
|
| + * | metadata identifier
|
| * </pre>
|
| *
|
| - * @return the additive expression that was parsed
|
| + * @return the normal formal parameter that was parsed
|
| */
|
| - Expression parseAdditiveExpression() {
|
| - Expression expression;
|
| - if (matches(Keyword.SUPER) && _currentToken.next.type.isAdditiveOperator) {
|
| - expression = new SuperExpression.full(andAdvance);
|
| - } else {
|
| - expression = parseMultiplicativeExpression();
|
| + NormalFormalParameter parseNormalFormalParameter() {
|
| + CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| + FinalConstVarOrType holder = parseFinalConstVarOrType(true);
|
| + Token thisKeyword = null;
|
| + Token period = null;
|
| + if (matches(Keyword.THIS)) {
|
| + thisKeyword = andAdvance;
|
| + period = expect2(TokenType.PERIOD);
|
| }
|
| - while (_currentToken.type.isAdditiveOperator) {
|
| - Token operator = andAdvance;
|
| - expression = new BinaryExpression.full(expression, operator, parseMultiplicativeExpression());
|
| + SimpleIdentifier identifier = parseSimpleIdentifier();
|
| + if (matches5(TokenType.OPEN_PAREN)) {
|
| + FormalParameterList parameters = parseFormalParameterList();
|
| + if (thisKeyword == null) {
|
| + if (holder.keyword != null) {
|
| + reportError9(ParserErrorCode.FUNCTION_TYPED_PARAMETER_VAR, holder.keyword, []);
|
| + }
|
| + return new FunctionTypedFormalParameter.full(commentAndMetadata.comment, commentAndMetadata.metadata, holder.type, identifier, parameters);
|
| + } else {
|
| + return new FieldFormalParameter.full(commentAndMetadata.comment, commentAndMetadata.metadata, holder.keyword, holder.type, thisKeyword, period, identifier, parameters);
|
| + }
|
| }
|
| - return expression;
|
| + TypeName type = holder.type;
|
| + if (type != null) {
|
| + if (matches3(type.name.beginToken, Keyword.VOID)) {
|
| + reportError9(ParserErrorCode.VOID_PARAMETER, type.name.beginToken, []);
|
| + } else if (holder.keyword != null && matches3(holder.keyword, Keyword.VAR)) {
|
| + reportError9(ParserErrorCode.VAR_AND_TYPE, holder.keyword, []);
|
| + }
|
| + }
|
| + if (thisKeyword != null) {
|
| + return new FieldFormalParameter.full(commentAndMetadata.comment, commentAndMetadata.metadata, holder.keyword, holder.type, thisKeyword, period, identifier, null);
|
| + }
|
| + return new SimpleFormalParameter.full(commentAndMetadata.comment, commentAndMetadata.metadata, holder.keyword, holder.type, identifier);
|
| }
|
|
|
| /**
|
| - * Parse an annotation.
|
| + * Parse a prefixed identifier.
|
| *
|
| * <pre>
|
| - * annotation ::=
|
| - * '@' qualified ('.' identifier)? arguments?
|
| + * prefixedIdentifier ::=
|
| + * identifier ('.' identifier)?
|
| * </pre>
|
| *
|
| - * @return the annotation that was parsed
|
| + * @return the prefixed identifier that was parsed
|
| */
|
| - Annotation parseAnnotation() {
|
| - Token atSign = expect2(TokenType.AT);
|
| - Identifier name = parsePrefixedIdentifier();
|
| - Token period = null;
|
| - SimpleIdentifier constructorName = null;
|
| - if (matches5(TokenType.PERIOD)) {
|
| - period = andAdvance;
|
| - constructorName = parseSimpleIdentifier();
|
| - }
|
| - ArgumentList arguments = null;
|
| - if (matches5(TokenType.OPEN_PAREN)) {
|
| - arguments = parseArgumentList();
|
| + Identifier parsePrefixedIdentifier() {
|
| + SimpleIdentifier qualifier = parseSimpleIdentifier();
|
| + if (!matches5(TokenType.PERIOD)) {
|
| + return qualifier;
|
| }
|
| - return new Annotation.full(atSign, name, period, constructorName, arguments);
|
| + Token period = andAdvance;
|
| + SimpleIdentifier qualified = parseSimpleIdentifier();
|
| + return new PrefixedIdentifier.full(qualifier, period, qualified);
|
| }
|
|
|
| /**
|
| - * Parse an argument.
|
| + * Parse a return type.
|
| *
|
| * <pre>
|
| - * argument ::=
|
| - * namedArgument
|
| - * | expression
|
| - *
|
| - * namedArgument ::=
|
| - * label expression
|
| + * returnType ::=
|
| + * 'void'
|
| + * | type
|
| * </pre>
|
| *
|
| - * @return the argument that was parsed
|
| + * @return the return type that was parsed
|
| */
|
| - Expression parseArgument() {
|
| - if (matchesIdentifier() && matches4(peek(), TokenType.COLON)) {
|
| - SimpleIdentifier label = new SimpleIdentifier.full(andAdvance);
|
| - Label name = new Label.full(label, andAdvance);
|
| - return new NamedExpression.full(name, parseExpression2());
|
| + TypeName parseReturnType() {
|
| + if (matches(Keyword.VOID)) {
|
| + return new TypeName.full(new SimpleIdentifier.full(andAdvance), null);
|
| } else {
|
| - return parseExpression2();
|
| + return parseTypeName();
|
| }
|
| }
|
|
|
| /**
|
| - * Parse an argument definition test.
|
| + * Parse a simple identifier.
|
| *
|
| * <pre>
|
| - * argumentDefinitionTest ::=
|
| - * '?' identifier
|
| + * identifier ::=
|
| + * IDENTIFIER
|
| * </pre>
|
| *
|
| - * @return the argument definition test that was parsed
|
| + * @return the simple identifier that was parsed
|
| */
|
| - ArgumentDefinitionTest parseArgumentDefinitionTest() {
|
| - Token question = expect2(TokenType.QUESTION);
|
| - SimpleIdentifier identifier = parseSimpleIdentifier();
|
| - reportError9(ParserErrorCode.DEPRECATED_ARGUMENT_DEFINITION_TEST, question, []);
|
| - return new ArgumentDefinitionTest.full(question, identifier);
|
| + SimpleIdentifier parseSimpleIdentifier() {
|
| + if (matchesIdentifier()) {
|
| + return new SimpleIdentifier.full(andAdvance);
|
| + }
|
| + reportError8(ParserErrorCode.MISSING_IDENTIFIER, []);
|
| + return createSyntheticIdentifier();
|
| }
|
|
|
| /**
|
| - * Parse a list of arguments.
|
| + * Parse a statement.
|
| *
|
| * <pre>
|
| - * arguments ::=
|
| - * '(' argumentList? ')'
|
| - *
|
| - * argumentList ::=
|
| - * namedArgument (',' namedArgument)*
|
| - * | expressionList (',' namedArgument)*
|
| + * statement ::=
|
| + * label* nonLabeledStatement
|
| * </pre>
|
| *
|
| - * @return the argument list that was parsed
|
| + * @return the statement that was parsed
|
| */
|
| - ArgumentList parseArgumentList() {
|
| - Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| - List<Expression> arguments = new List<Expression>();
|
| - if (matches5(TokenType.CLOSE_PAREN)) {
|
| - return new ArgumentList.full(leftParenthesis, arguments, andAdvance);
|
| + Statement parseStatement2() {
|
| + List<Label> labels = new List<Label>();
|
| + while (matchesIdentifier() && matches4(peek(), TokenType.COLON)) {
|
| + labels.add(parseLabel());
|
| }
|
| - Expression argument = parseArgument();
|
| - arguments.add(argument);
|
| - bool foundNamedArgument = argument is NamedExpression;
|
| - bool generatedError = false;
|
| - while (optional(TokenType.COMMA)) {
|
| - argument = parseArgument();
|
| - arguments.add(argument);
|
| - if (foundNamedArgument) {
|
| - if (!generatedError && argument is! NamedExpression) {
|
| - reportError8(ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT, []);
|
| - generatedError = true;
|
| - }
|
| - } else if (argument is NamedExpression) {
|
| - foundNamedArgument = true;
|
| - }
|
| + Statement statement = parseNonLabeledStatement();
|
| + if (labels.isEmpty) {
|
| + return statement;
|
| }
|
| - Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| - return new ArgumentList.full(leftParenthesis, arguments, rightParenthesis);
|
| + return new LabeledStatement.full(labels, statement);
|
| }
|
|
|
| /**
|
| - * Parse an assert statement.
|
| + * Parse a string literal.
|
| *
|
| * <pre>
|
| - * assertStatement ::=
|
| - * 'assert' '(' conditionalExpression ')' ';'
|
| + * stringLiteral ::=
|
| + * MULTI_LINE_STRING+
|
| + * | SINGLE_LINE_STRING+
|
| * </pre>
|
| *
|
| - * @return the assert statement
|
| + * @return the string literal that was parsed
|
| */
|
| - AssertStatement parseAssertStatement() {
|
| - Token keyword = expect(Keyword.ASSERT);
|
| - Token leftParen = expect2(TokenType.OPEN_PAREN);
|
| - Expression expression = parseExpression2();
|
| - if (expression is AssignmentExpression) {
|
| - reportError(ParserErrorCode.ASSERT_DOES_NOT_TAKE_ASSIGNMENT, expression, []);
|
| - } else if (expression is CascadeExpression) {
|
| - reportError(ParserErrorCode.ASSERT_DOES_NOT_TAKE_CASCADE, expression, []);
|
| - } else if (expression is ThrowExpression) {
|
| - reportError(ParserErrorCode.ASSERT_DOES_NOT_TAKE_THROW, expression, []);
|
| - } else if (expression is RethrowExpression) {
|
| - reportError(ParserErrorCode.ASSERT_DOES_NOT_TAKE_RETHROW, expression, []);
|
| + StringLiteral parseStringLiteral() {
|
| + List<StringLiteral> strings = new List<StringLiteral>();
|
| + while (matches5(TokenType.STRING)) {
|
| + Token string = andAdvance;
|
| + if (matches5(TokenType.STRING_INTERPOLATION_EXPRESSION) || matches5(TokenType.STRING_INTERPOLATION_IDENTIFIER)) {
|
| + strings.add(parseStringInterpolation(string));
|
| + } else {
|
| + strings.add(new SimpleStringLiteral.full(string, computeStringValue(string.lexeme, true, true)));
|
| + }
|
| + }
|
| + if (strings.length < 1) {
|
| + reportError8(ParserErrorCode.EXPECTED_STRING_LITERAL, []);
|
| + return createSyntheticStringLiteral();
|
| + } else if (strings.length == 1) {
|
| + return strings[0];
|
| + } else {
|
| + return new AdjacentStrings.full(strings);
|
| }
|
| - Token rightParen = expect2(TokenType.CLOSE_PAREN);
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new AssertStatement.full(keyword, leftParen, expression, rightParen, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse an assignable expression.
|
| + * Parse a list of type arguments.
|
| *
|
| * <pre>
|
| - * assignableExpression ::=
|
| - * primary (arguments* assignableSelector)+
|
| - * | 'super' assignableSelector
|
| - * | identifier
|
| + * typeArguments ::=
|
| + * '<' typeList '>'
|
| + *
|
| + * typeList ::=
|
| + * type (',' type)*
|
| * </pre>
|
| *
|
| - * @param primaryAllowed `true` if the expression is allowed to be a primary without any
|
| - * assignable selector
|
| - * @return the assignable expression that was parsed
|
| + * @return the type argument list that was parsed
|
| */
|
| - Expression parseAssignableExpression(bool primaryAllowed) {
|
| - if (matches(Keyword.SUPER)) {
|
| - return parseAssignableSelector(new SuperExpression.full(andAdvance), false);
|
| - }
|
| - Expression expression = parsePrimaryExpression();
|
| - bool isOptional = primaryAllowed || expression is SimpleIdentifier;
|
| - while (true) {
|
| - while (matches5(TokenType.OPEN_PAREN)) {
|
| - ArgumentList argumentList = parseArgumentList();
|
| - if (expression is SimpleIdentifier) {
|
| - expression = new MethodInvocation.full(null, null, expression as SimpleIdentifier, argumentList);
|
| - } else if (expression is PrefixedIdentifier) {
|
| - PrefixedIdentifier identifier = expression as PrefixedIdentifier;
|
| - expression = new MethodInvocation.full(identifier.prefix, identifier.period, identifier.identifier, argumentList);
|
| - } else if (expression is PropertyAccess) {
|
| - PropertyAccess access = expression as PropertyAccess;
|
| - expression = new MethodInvocation.full(access.target, access.operator, access.propertyName, argumentList);
|
| - } else {
|
| - expression = new FunctionExpressionInvocation.full(expression, argumentList);
|
| - }
|
| - if (!primaryAllowed) {
|
| - isOptional = false;
|
| - }
|
| - }
|
| - Expression selectorExpression = parseAssignableSelector(expression, isOptional || (expression is PrefixedIdentifier));
|
| - if (identical(selectorExpression, expression)) {
|
| - if (!isOptional && (expression is PrefixedIdentifier)) {
|
| - PrefixedIdentifier identifier = expression as PrefixedIdentifier;
|
| - expression = new PropertyAccess.full(identifier.prefix, identifier.period, identifier.identifier);
|
| - }
|
| - return expression;
|
| - }
|
| - expression = selectorExpression;
|
| - isOptional = true;
|
| + TypeArgumentList parseTypeArgumentList() {
|
| + Token leftBracket = expect2(TokenType.LT);
|
| + List<TypeName> arguments = new List<TypeName>();
|
| + arguments.add(parseTypeName());
|
| + while (optional(TokenType.COMMA)) {
|
| + arguments.add(parseTypeName());
|
| }
|
| + Token rightBracket = expect2(TokenType.GT);
|
| + return new TypeArgumentList.full(leftBracket, arguments, rightBracket);
|
| }
|
|
|
| /**
|
| - * Parse an assignable selector.
|
| + * Parse a type name.
|
| *
|
| * <pre>
|
| - * assignableSelector ::=
|
| - * '[' expression ']'
|
| - * | '.' identifier
|
| + * type ::=
|
| + * qualified typeArguments?
|
| * </pre>
|
| *
|
| - * @param prefix the expression preceding the selector
|
| - * @param optional `true` if the selector is optional
|
| - * @return the assignable selector that was parsed
|
| + * @return the type name that was parsed
|
| */
|
| - Expression parseAssignableSelector(Expression prefix, bool optional) {
|
| - if (matches5(TokenType.OPEN_SQUARE_BRACKET)) {
|
| - Token leftBracket = andAdvance;
|
| - Expression index = parseExpression2();
|
| - Token rightBracket = expect2(TokenType.CLOSE_SQUARE_BRACKET);
|
| - return new IndexExpression.forTarget_full(prefix, leftBracket, index, rightBracket);
|
| - } else if (matches5(TokenType.PERIOD)) {
|
| - Token period = andAdvance;
|
| - return new PropertyAccess.full(prefix, period, parseSimpleIdentifier());
|
| + TypeName parseTypeName() {
|
| + Identifier typeName;
|
| + if (matches(Keyword.VAR)) {
|
| + reportError8(ParserErrorCode.VAR_AS_TYPE_NAME, []);
|
| + typeName = new SimpleIdentifier.full(andAdvance);
|
| + } else if (matchesIdentifier()) {
|
| + typeName = parsePrefixedIdentifier();
|
| } else {
|
| - if (!optional) {
|
| - reportError8(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, []);
|
| - }
|
| - return prefix;
|
| + typeName = createSyntheticIdentifier();
|
| + reportError8(ParserErrorCode.EXPECTED_TYPE_NAME, []);
|
| + }
|
| + TypeArgumentList typeArguments = null;
|
| + if (matches5(TokenType.LT)) {
|
| + typeArguments = parseTypeArgumentList();
|
| }
|
| + return new TypeName.full(typeName, typeArguments);
|
| }
|
|
|
| /**
|
| - * Parse a bitwise and expression.
|
| + * Parse a type parameter.
|
| *
|
| * <pre>
|
| - * bitwiseAndExpression ::=
|
| - * shiftExpression ('&' shiftExpression)*
|
| - * | 'super' ('&' shiftExpression)+
|
| + * typeParameter ::=
|
| + * metadata name ('extends' bound)?
|
| * </pre>
|
| *
|
| - * @return the bitwise and expression that was parsed
|
| + * @return the type parameter that was parsed
|
| */
|
| - Expression parseBitwiseAndExpression() {
|
| - Expression expression;
|
| - if (matches(Keyword.SUPER) && matches4(peek(), TokenType.AMPERSAND)) {
|
| - expression = new SuperExpression.full(andAdvance);
|
| - } else {
|
| - expression = parseShiftExpression();
|
| - }
|
| - while (matches5(TokenType.AMPERSAND)) {
|
| - Token operator = andAdvance;
|
| - expression = new BinaryExpression.full(expression, operator, parseShiftExpression());
|
| + TypeParameter parseTypeParameter() {
|
| + CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| + SimpleIdentifier name = parseSimpleIdentifier();
|
| + if (matches(Keyword.EXTENDS)) {
|
| + Token keyword = andAdvance;
|
| + TypeName bound = parseTypeName();
|
| + return new TypeParameter.full(commentAndMetadata.comment, commentAndMetadata.metadata, name, keyword, bound);
|
| }
|
| - return expression;
|
| + return new TypeParameter.full(commentAndMetadata.comment, commentAndMetadata.metadata, name, null, null);
|
| }
|
|
|
| /**
|
| - * Parse a bitwise or expression.
|
| + * Parse a list of type parameters.
|
| *
|
| * <pre>
|
| - * bitwiseOrExpression ::=
|
| - * bitwiseXorExpression ('|' bitwiseXorExpression)*
|
| - * | 'super' ('|' bitwiseXorExpression)+
|
| + * typeParameterList ::=
|
| + * '<' typeParameter (',' typeParameter)* '>'
|
| * </pre>
|
| *
|
| - * @return the bitwise or expression that was parsed
|
| + * @return the list of type parameters that were parsed
|
| */
|
| - Expression parseBitwiseOrExpression() {
|
| - Expression expression;
|
| - if (matches(Keyword.SUPER) && matches4(peek(), TokenType.BAR)) {
|
| - expression = new SuperExpression.full(andAdvance);
|
| - } else {
|
| - expression = parseBitwiseXorExpression();
|
| - }
|
| - while (matches5(TokenType.BAR)) {
|
| - Token operator = andAdvance;
|
| - expression = new BinaryExpression.full(expression, operator, parseBitwiseXorExpression());
|
| + TypeParameterList parseTypeParameterList() {
|
| + Token leftBracket = expect2(TokenType.LT);
|
| + List<TypeParameter> typeParameters = new List<TypeParameter>();
|
| + typeParameters.add(parseTypeParameter());
|
| + while (optional(TokenType.COMMA)) {
|
| + typeParameters.add(parseTypeParameter());
|
| }
|
| - return expression;
|
| + Token rightBracket = expect2(TokenType.GT);
|
| + return new TypeParameterList.full(leftBracket, typeParameters, rightBracket);
|
| }
|
|
|
| /**
|
| - * Parse a bitwise exclusive-or expression.
|
| + * Parse a with clause.
|
| *
|
| * <pre>
|
| - * bitwiseXorExpression ::=
|
| - * bitwiseAndExpression ('^' bitwiseAndExpression)*
|
| - * | 'super' ('^' bitwiseAndExpression)+
|
| + * withClause ::=
|
| + * 'with' typeName (',' typeName)*
|
| * </pre>
|
| *
|
| - * @return the bitwise exclusive-or expression that was parsed
|
| + * @return the with clause that was parsed
|
| */
|
| - Expression parseBitwiseXorExpression() {
|
| - Expression expression;
|
| - if (matches(Keyword.SUPER) && matches4(peek(), TokenType.CARET)) {
|
| - expression = new SuperExpression.full(andAdvance);
|
| - } else {
|
| - expression = parseBitwiseAndExpression();
|
| - }
|
| - while (matches5(TokenType.CARET)) {
|
| - Token operator = andAdvance;
|
| - expression = new BinaryExpression.full(expression, operator, parseBitwiseAndExpression());
|
| + WithClause parseWithClause() {
|
| + Token with2 = expect(Keyword.WITH);
|
| + List<TypeName> types = new List<TypeName>();
|
| + types.add(parseTypeName());
|
| + while (optional(TokenType.COMMA)) {
|
| + types.add(parseTypeName());
|
| }
|
| - return expression;
|
| + return new WithClause.full(with2, types);
|
| + }
|
| + void set currentToken(Token currentToken) {
|
| + this._currentToken = currentToken;
|
| }
|
|
|
| /**
|
| - * Parse a block.
|
| - *
|
| - * <pre>
|
| - * block ::=
|
| - * '{' statements '}'
|
| - * </pre>
|
| - *
|
| - * @return the block that was parsed
|
| + * Advance to the next token in the token stream.
|
| */
|
| - Block parseBlock() {
|
| - Token leftBracket = expect2(TokenType.OPEN_CURLY_BRACKET);
|
| - List<Statement> statements = new List<Statement>();
|
| - Token statementStart = _currentToken;
|
| - while (!matches5(TokenType.EOF) && !matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| - Statement statement = parseStatement2();
|
| - if (statement != null) {
|
| - statements.add(statement);
|
| - }
|
| - if (identical(_currentToken, statementStart)) {
|
| - reportError9(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
|
| - advance();
|
| - }
|
| - statementStart = _currentToken;
|
| - }
|
| - Token rightBracket = expect2(TokenType.CLOSE_CURLY_BRACKET);
|
| - return new Block.full(leftBracket, statements, rightBracket);
|
| + void advance() {
|
| + _currentToken = _currentToken.next;
|
| }
|
|
|
| /**
|
| - * Parse a break statement.
|
| - *
|
| - * <pre>
|
| - * breakStatement ::=
|
| - * 'break' identifier? ';'
|
| - * </pre>
|
| + * Append the character equivalent of the given scalar value to the given builder. Use the start
|
| + * and end indices to report an error, and don't append anything to the builder, if the scalar
|
| + * value is invalid.
|
| *
|
| - * @return the break statement that was parsed
|
| + * @param builder the builder to which the scalar value is to be appended
|
| + * @param escapeSequence the escape sequence that was parsed to produce the scalar value
|
| + * @param scalarValue the value to be appended
|
| + * @param startIndex the index of the first character representing the scalar value
|
| + * @param endIndex the index of the last character representing the scalar value
|
| */
|
| - Statement parseBreakStatement() {
|
| - Token breakKeyword = expect(Keyword.BREAK);
|
| - SimpleIdentifier label = null;
|
| - if (matchesIdentifier()) {
|
| - label = parseSimpleIdentifier();
|
| + void appendScalarValue(JavaStringBuilder builder, String escapeSequence, int scalarValue, int startIndex, int endIndex) {
|
| + if (scalarValue < 0 || scalarValue > Character.MAX_CODE_POINT || (scalarValue >= 0xD800 && scalarValue <= 0xDFFF)) {
|
| + reportError8(ParserErrorCode.INVALID_CODE_POINT, [escapeSequence]);
|
| + return;
|
| }
|
| - if (!_inLoop && !_inSwitch && label == null) {
|
| - reportError9(ParserErrorCode.BREAK_OUTSIDE_OF_LOOP, breakKeyword, []);
|
| + if (scalarValue < Character.MAX_VALUE) {
|
| + builder.appendChar(scalarValue as int);
|
| + } else {
|
| + builder.append(Character.toChars(scalarValue));
|
| }
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new BreakStatement.full(breakKeyword, label, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse a cascade section.
|
| - *
|
| - * <pre>
|
| - * cascadeSection ::=
|
| - * '..' (cascadeSelector arguments*) (assignableSelector arguments*)* cascadeAssignment?
|
| - *
|
| - * cascadeSelector ::=
|
| - * '[' expression ']'
|
| - * | identifier
|
| - *
|
| - * cascadeAssignment ::=
|
| - * assignmentOperator expressionWithoutCascade
|
| - * </pre>
|
| + * Compute the content of a string with the given literal representation.
|
| *
|
| - * @return the expression representing the cascaded method invocation
|
| + * @param lexeme the literal representation of the string
|
| + * @param first `true` if this is the first token in a string literal
|
| + * @param last `true` if this is the last token in a string literal
|
| + * @return the actual value of the string
|
| */
|
| - Expression parseCascadeSection() {
|
| - Token period = expect2(TokenType.PERIOD_PERIOD);
|
| - Expression expression = null;
|
| - SimpleIdentifier functionName = null;
|
| - if (matchesIdentifier()) {
|
| - functionName = parseSimpleIdentifier();
|
| - } else if (identical(_currentToken.type, TokenType.OPEN_SQUARE_BRACKET)) {
|
| - Token leftBracket = andAdvance;
|
| - Expression index = parseExpression2();
|
| - Token rightBracket = expect2(TokenType.CLOSE_SQUARE_BRACKET);
|
| - expression = new IndexExpression.forCascade_full(period, leftBracket, index, rightBracket);
|
| - period = null;
|
| - } else {
|
| - reportError9(ParserErrorCode.MISSING_IDENTIFIER, _currentToken, [_currentToken.lexeme]);
|
| - functionName = createSyntheticIdentifier();
|
| - }
|
| - if (identical(_currentToken.type, TokenType.OPEN_PAREN)) {
|
| - while (identical(_currentToken.type, TokenType.OPEN_PAREN)) {
|
| - if (functionName != null) {
|
| - expression = new MethodInvocation.full(expression, period, functionName, parseArgumentList());
|
| - period = null;
|
| - functionName = null;
|
| - } else if (expression == null) {
|
| - expression = new MethodInvocation.full(expression, period, createSyntheticIdentifier(), parseArgumentList());
|
| - } else {
|
| - expression = new FunctionExpressionInvocation.full(expression, parseArgumentList());
|
| - }
|
| + String computeStringValue(String lexeme, bool first, bool last) {
|
| + bool isRaw = false;
|
| + int start = 0;
|
| + if (first) {
|
| + if (lexeme.startsWith("r\"\"\"") || lexeme.startsWith("r'''")) {
|
| + isRaw = true;
|
| + start += 4;
|
| + } else if (lexeme.startsWith("r\"") || lexeme.startsWith("r'")) {
|
| + isRaw = true;
|
| + start += 2;
|
| + } else if (lexeme.startsWith("\"\"\"") || lexeme.startsWith("'''")) {
|
| + start += 3;
|
| + } else if (lexeme.startsWith("\"") || lexeme.startsWith("'")) {
|
| + start += 1;
|
| }
|
| - } else if (functionName != null) {
|
| - expression = new PropertyAccess.full(expression, period, functionName);
|
| - period = null;
|
| }
|
| - bool progress = true;
|
| - while (progress) {
|
| - progress = false;
|
| - Expression selector = parseAssignableSelector(expression, true);
|
| - if (selector != expression) {
|
| - expression = selector;
|
| - progress = true;
|
| - while (identical(_currentToken.type, TokenType.OPEN_PAREN)) {
|
| - expression = new FunctionExpressionInvocation.full(expression, parseArgumentList());
|
| - }
|
| + int end = lexeme.length;
|
| + if (last) {
|
| + if (lexeme.endsWith("\"\"\"") || lexeme.endsWith("'''")) {
|
| + end -= 3;
|
| + } else if (lexeme.endsWith("\"") || lexeme.endsWith("'")) {
|
| + end -= 1;
|
| }
|
| }
|
| - if (_currentToken.type.isAssignmentOperator) {
|
| - Token operator = andAdvance;
|
| - ensureAssignable(expression);
|
| - expression = new AssignmentExpression.full(expression, operator, parseExpressionWithoutCascade());
|
| + if (end - start + 1 < 0) {
|
| + AnalysisEngine.instance.logger.logError("Internal error: computeStringValue(${lexeme}, ${first}, ${last})");
|
| + return "";
|
| }
|
| - return expression;
|
| + if (isRaw) {
|
| + return lexeme.substring(start, end);
|
| + }
|
| + JavaStringBuilder builder = new JavaStringBuilder();
|
| + int index = start;
|
| + while (index < end) {
|
| + index = translateCharacter(builder, lexeme, index);
|
| + }
|
| + return builder.toString();
|
| }
|
|
|
| /**
|
| - * Parse a class declaration.
|
| + * Convert the given method declaration into the nearest valid top-level function declaration.
|
| *
|
| - * <pre>
|
| - * classDeclaration ::=
|
| - * metadata 'abstract'? 'class' name typeParameterList? (extendsClause withClause?)? implementsClause? '{' classMembers '}' |
|
| - * metadata 'abstract'? 'class' mixinApplicationClass
|
| - * </pre>
|
| + * @param method the method to be converted
|
| + * @return the function declaration that most closely captures the components of the given method
|
| + * declaration
|
| + */
|
| + FunctionDeclaration convertToFunctionDeclaration(MethodDeclaration method) => new FunctionDeclaration.full(method.documentationComment, method.metadata, method.externalKeyword, method.returnType, method.propertyKeyword, method.name, new FunctionExpression.full(method.parameters, method.body));
|
| +
|
| + /**
|
| + * Return `true` if the current token could be the start of a compilation unit member. This
|
| + * method is used for recovery purposes to decide when to stop skipping tokens after finding an
|
| + * error while parsing a compilation unit member.
|
| *
|
| - * @param commentAndMetadata the metadata to be associated with the member
|
| - * @param abstractKeyword the token for the keyword 'abstract', or `null` if the keyword was
|
| - * not given
|
| - * @return the class declaration that was parsed
|
| + * @return `true` if the current token could be the start of a compilation unit member
|
| */
|
| - CompilationUnitMember parseClassDeclaration(CommentAndMetadata commentAndMetadata, Token abstractKeyword) {
|
| - Token keyword = expect(Keyword.CLASS);
|
| - if (matchesIdentifier()) {
|
| - Token next = peek();
|
| - if (matches4(next, TokenType.LT)) {
|
| - next = skipTypeParameterList(next);
|
| - if (next != null && matches4(next, TokenType.EQ)) {
|
| - return parseClassTypeAlias(commentAndMetadata, keyword);
|
| - }
|
| - } else if (matches4(next, TokenType.EQ)) {
|
| - return parseClassTypeAlias(commentAndMetadata, keyword);
|
| + bool couldBeStartOfCompilationUnitMember() {
|
| + if ((matches(Keyword.IMPORT) || matches(Keyword.EXPORT) || matches(Keyword.LIBRARY) || matches(Keyword.PART)) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| + return true;
|
| + } else if (matches(Keyword.CLASS)) {
|
| + return true;
|
| + } else if (matches(Keyword.TYPEDEF) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| + return true;
|
| + } else if (matches(Keyword.VOID) || ((matches(Keyword.GET) || matches(Keyword.SET)) && matchesIdentifier2(peek())) || (matches(Keyword.OPERATOR) && isOperator(peek()))) {
|
| + return true;
|
| + } else if (matchesIdentifier()) {
|
| + if (matches4(peek(), TokenType.OPEN_PAREN)) {
|
| + return true;
|
| }
|
| - }
|
| - SimpleIdentifier name = parseSimpleIdentifier();
|
| - String className = name.name;
|
| - TypeParameterList typeParameters = null;
|
| - if (matches5(TokenType.LT)) {
|
| - typeParameters = parseTypeParameterList();
|
| - }
|
| - ExtendsClause extendsClause = null;
|
| - WithClause withClause = null;
|
| - ImplementsClause implementsClause = null;
|
| - bool foundClause = true;
|
| - while (foundClause) {
|
| - if (matches(Keyword.EXTENDS)) {
|
| - if (extendsClause == null) {
|
| - extendsClause = parseExtendsClause();
|
| - if (withClause != null) {
|
| - reportError9(ParserErrorCode.WITH_BEFORE_EXTENDS, withClause.withKeyword, []);
|
| - } else if (implementsClause != null) {
|
| - reportError9(ParserErrorCode.IMPLEMENTS_BEFORE_EXTENDS, implementsClause.keyword, []);
|
| - }
|
| - } else {
|
| - reportError9(ParserErrorCode.MULTIPLE_EXTENDS_CLAUSES, extendsClause.keyword, []);
|
| - parseExtendsClause();
|
| - }
|
| - } else if (matches(Keyword.WITH)) {
|
| - if (withClause == null) {
|
| - withClause = parseWithClause();
|
| - if (implementsClause != null) {
|
| - reportError9(ParserErrorCode.IMPLEMENTS_BEFORE_WITH, implementsClause.keyword, []);
|
| - }
|
| - } else {
|
| - reportError9(ParserErrorCode.MULTIPLE_WITH_CLAUSES, withClause.withKeyword, []);
|
| - parseWithClause();
|
| - }
|
| - } else if (matches(Keyword.IMPLEMENTS)) {
|
| - if (implementsClause == null) {
|
| - implementsClause = parseImplementsClause();
|
| - } else {
|
| - reportError9(ParserErrorCode.MULTIPLE_IMPLEMENTS_CLAUSES, implementsClause.keyword, []);
|
| - parseImplementsClause();
|
| - }
|
| - } else {
|
| - foundClause = false;
|
| + Token token = skipReturnType(_currentToken);
|
| + if (token == null) {
|
| + return false;
|
| + }
|
| + if (matches(Keyword.GET) || matches(Keyword.SET) || (matches(Keyword.OPERATOR) && isOperator(peek())) || matchesIdentifier()) {
|
| + return true;
|
| }
|
| }
|
| - if (withClause != null && extendsClause == null) {
|
| - reportError9(ParserErrorCode.WITH_WITHOUT_EXTENDS, withClause.withKeyword, []);
|
| - }
|
| - NativeClause nativeClause = null;
|
| - if (matches2(_NATIVE) && matches4(peek(), TokenType.STRING)) {
|
| - nativeClause = parseNativeClause();
|
| - }
|
| - Token leftBracket = null;
|
| - List<ClassMember> members = null;
|
| - Token rightBracket = null;
|
| - if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| - leftBracket = expect2(TokenType.OPEN_CURLY_BRACKET);
|
| - members = parseClassMembers(className, getEndToken(leftBracket));
|
| - rightBracket = expect2(TokenType.CLOSE_CURLY_BRACKET);
|
| - } else {
|
| - leftBracket = createSyntheticToken2(TokenType.OPEN_CURLY_BRACKET);
|
| - rightBracket = createSyntheticToken2(TokenType.CLOSE_CURLY_BRACKET);
|
| - reportError8(ParserErrorCode.MISSING_CLASS_BODY, []);
|
| - }
|
| - ClassDeclaration classDeclaration = new ClassDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, abstractKeyword, keyword, name, typeParameters, extendsClause, withClause, implementsClause, leftBracket, members, rightBracket);
|
| - classDeclaration.nativeClause = nativeClause;
|
| - return classDeclaration;
|
| + return false;
|
| }
|
|
|
| /**
|
| - * Parse a class member.
|
| + * Create a synthetic identifier.
|
| + *
|
| + * @return the synthetic identifier that was created
|
| + */
|
| + SimpleIdentifier createSyntheticIdentifier() => new SimpleIdentifier.full(createSyntheticToken2(TokenType.IDENTIFIER));
|
| +
|
| + /**
|
| + * Create a synthetic string literal.
|
| + *
|
| + * @return the synthetic string literal that was created
|
| + */
|
| + SimpleStringLiteral createSyntheticStringLiteral() => new SimpleStringLiteral.full(createSyntheticToken2(TokenType.STRING), "");
|
| +
|
| + /**
|
| + * Create a synthetic token representing the given keyword.
|
| + *
|
| + * @return the synthetic token that was created
|
| + */
|
| + Token createSyntheticToken(Keyword keyword) => new Parser_SyntheticKeywordToken(keyword, _currentToken.offset);
|
| +
|
| + /**
|
| + * Create a synthetic token with the given type.
|
| + *
|
| + * @return the synthetic token that was created
|
| + */
|
| + Token createSyntheticToken2(TokenType type) => new StringToken(type, "", _currentToken.offset);
|
| +
|
| + /**
|
| + * Check that the given expression is assignable and report an error if it isn't.
|
| *
|
| * <pre>
|
| - * classMemberDefinition ::=
|
| - * declaration ';'
|
| - * | methodSignature functionBody
|
| + * assignableExpression ::=
|
| + * primary (arguments* assignableSelector)+
|
| + * | 'super' assignableSelector
|
| + * | identifier
|
| + *
|
| + * assignableSelector ::=
|
| + * '[' expression ']'
|
| + * | '.' identifier
|
| * </pre>
|
| *
|
| - * @param className the name of the class containing the member being parsed
|
| - * @return the class member that was parsed, or `null` if what was found was not a valid
|
| - * class member
|
| + * @param expression the expression being checked
|
| */
|
| - ClassMember parseClassMember(String className) {
|
| - CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| - Modifiers modifiers = parseModifiers();
|
| - if (matches(Keyword.VOID)) {
|
| - TypeName returnType = parseReturnType();
|
| - if (matches(Keyword.GET) && matchesIdentifier2(peek())) {
|
| - validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| - return parseGetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, returnType);
|
| - } else if (matches(Keyword.SET) && matchesIdentifier2(peek())) {
|
| - validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| - return parseSetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, returnType);
|
| - } else if (matches(Keyword.OPERATOR) && isOperator(peek())) {
|
| - validateModifiersForOperator(modifiers);
|
| - return parseOperator(commentAndMetadata, modifiers.externalKeyword, returnType);
|
| - } else if (matchesIdentifier() && matchesAny(peek(), [
|
| - TokenType.OPEN_PAREN,
|
| - TokenType.OPEN_CURLY_BRACKET,
|
| - TokenType.FUNCTION])) {
|
| - validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| - return parseMethodDeclaration(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, returnType);
|
| - } else {
|
| - if (matchesIdentifier()) {
|
| - if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
|
| - reportError(ParserErrorCode.VOID_VARIABLE, returnType, []);
|
| - return parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, validateModifiersForField(modifiers), returnType);
|
| - }
|
| - }
|
| - if (isOperator(_currentToken)) {
|
| - validateModifiersForOperator(modifiers);
|
| - return parseOperator(commentAndMetadata, modifiers.externalKeyword, returnType);
|
| - }
|
| - reportError9(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken, []);
|
| - return null;
|
| - }
|
| - } else if (matches(Keyword.GET) && matchesIdentifier2(peek())) {
|
| - validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| - return parseGetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, null);
|
| - } else if (matches(Keyword.SET) && matchesIdentifier2(peek())) {
|
| - validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| - return parseSetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, null);
|
| - } else if (matches(Keyword.OPERATOR) && isOperator(peek())) {
|
| - validateModifiersForOperator(modifiers);
|
| - return parseOperator(commentAndMetadata, modifiers.externalKeyword, null);
|
| - } else if (!matchesIdentifier()) {
|
| - if (isOperator(_currentToken)) {
|
| - validateModifiersForOperator(modifiers);
|
| - return parseOperator(commentAndMetadata, modifiers.externalKeyword, null);
|
| - }
|
| - reportError9(ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken, []);
|
| - return null;
|
| - } else if (matches4(peek(), TokenType.PERIOD) && matchesIdentifier2(peek2(2)) && matches4(peek2(3), TokenType.OPEN_PAREN)) {
|
| - return parseConstructor(commentAndMetadata, modifiers.externalKeyword, validateModifiersForConstructor(modifiers), modifiers.factoryKeyword, parseSimpleIdentifier(), andAdvance, parseSimpleIdentifier(), parseFormalParameterList());
|
| - } else if (matches4(peek(), TokenType.OPEN_PAREN)) {
|
| - SimpleIdentifier methodName = parseSimpleIdentifier();
|
| - FormalParameterList parameters = parseFormalParameterList();
|
| - if (matches5(TokenType.COLON) || modifiers.factoryKeyword != null || methodName.name == className) {
|
| - return parseConstructor(commentAndMetadata, modifiers.externalKeyword, validateModifiersForConstructor(modifiers), modifiers.factoryKeyword, methodName, null, null, parameters);
|
| - }
|
| - validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| - validateFormalParameterList(parameters);
|
| - return parseMethodDeclaration2(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, null, methodName, parameters);
|
| - } else if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
|
| - if (modifiers.constKeyword == null && modifiers.finalKeyword == null && modifiers.varKeyword == null) {
|
| - reportError8(ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE, []);
|
| - }
|
| - return parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, validateModifiersForField(modifiers), null);
|
| - }
|
| - TypeName type = parseTypeName();
|
| - if (matches(Keyword.GET) && matchesIdentifier2(peek())) {
|
| - validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| - return parseGetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, type);
|
| - } else if (matches(Keyword.SET) && matchesIdentifier2(peek())) {
|
| - validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| - return parseSetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, type);
|
| - } else if (matches(Keyword.OPERATOR) && isOperator(peek())) {
|
| - validateModifiersForOperator(modifiers);
|
| - return parseOperator(commentAndMetadata, modifiers.externalKeyword, type);
|
| - } else if (!matchesIdentifier()) {
|
| - if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| - return parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, validateModifiersForField(modifiers), type);
|
| - }
|
| - if (isOperator(_currentToken)) {
|
| - validateModifiersForOperator(modifiers);
|
| - return parseOperator(commentAndMetadata, modifiers.externalKeyword, type);
|
| - }
|
| - reportError9(ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken, []);
|
| - return null;
|
| - } else if (matches4(peek(), TokenType.OPEN_PAREN)) {
|
| - SimpleIdentifier methodName = parseSimpleIdentifier();
|
| - FormalParameterList parameters = parseFormalParameterList();
|
| - if (methodName.name == className) {
|
| - reportError(ParserErrorCode.CONSTRUCTOR_WITH_RETURN_TYPE, type, []);
|
| - return parseConstructor(commentAndMetadata, modifiers.externalKeyword, validateModifiersForConstructor(modifiers), modifiers.factoryKeyword, methodName, null, null, parameters);
|
| - }
|
| - validateModifiersForGetterOrSetterOrMethod(modifiers);
|
| - validateFormalParameterList(parameters);
|
| - return parseMethodDeclaration2(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, type, methodName, parameters);
|
| + void ensureAssignable(Expression expression) {
|
| + if (expression != null && !expression.isAssignable) {
|
| + reportError8(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, []);
|
| }
|
| - return parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, validateModifiersForField(modifiers), type);
|
| }
|
|
|
| /**
|
| - * Parse a list of class members.
|
| - *
|
| - * <pre>
|
| - * classMembers ::=
|
| - * (metadata memberDefinition)*
|
| - * </pre>
|
| + * If the current token is a keyword matching the given string, return it after advancing to the
|
| + * next token. Otherwise report an error and return the current token without advancing.
|
| *
|
| - * @param className the name of the class whose members are being parsed
|
| - * @param closingBracket the closing bracket for the class, or `null` if the closing bracket
|
| - * is missing
|
| - * @return the list of class members that were parsed
|
| + * @param keyword the keyword that is expected
|
| + * @return the token that matched the given type
|
| */
|
| - List<ClassMember> parseClassMembers(String className, Token closingBracket) {
|
| - List<ClassMember> members = new List<ClassMember>();
|
| - Token memberStart = _currentToken;
|
| - while (!matches5(TokenType.EOF) && !matches5(TokenType.CLOSE_CURLY_BRACKET) && (closingBracket != null || (!matches(Keyword.CLASS) && !matches(Keyword.TYPEDEF)))) {
|
| - if (matches5(TokenType.SEMICOLON)) {
|
| - reportError9(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
|
| - advance();
|
| - } else {
|
| - ClassMember member = parseClassMember(className);
|
| - if (member != null) {
|
| - members.add(member);
|
| - }
|
| - }
|
| - if (identical(_currentToken, memberStart)) {
|
| - reportError9(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
|
| - advance();
|
| - }
|
| - memberStart = _currentToken;
|
| + Token expect(Keyword keyword) {
|
| + if (matches(keyword)) {
|
| + return andAdvance;
|
| }
|
| - return members;
|
| + reportError8(ParserErrorCode.EXPECTED_TOKEN, [keyword.syntax]);
|
| + return _currentToken;
|
| }
|
|
|
| /**
|
| - * Parse a class type alias.
|
| - *
|
| - * <pre>
|
| - * classTypeAlias ::=
|
| - * identifier typeParameters? '=' 'abstract'? mixinApplication
|
| - *
|
| - * mixinApplication ::=
|
| - * type withClause implementsClause? ';'
|
| - * </pre>
|
| + * If the current token has the expected type, return it after advancing to the next token.
|
| + * Otherwise report an error and return the current token without advancing.
|
| *
|
| - * @param commentAndMetadata the metadata to be associated with the member
|
| - * @param keyword the token representing the 'typedef' keyword
|
| - * @return the class type alias that was parsed
|
| + * @param type the type of token that is expected
|
| + * @return the token that matched the given type
|
| */
|
| - ClassTypeAlias parseClassTypeAlias(CommentAndMetadata commentAndMetadata, Token keyword) {
|
| - SimpleIdentifier className = parseSimpleIdentifier();
|
| - TypeParameterList typeParameters = null;
|
| - if (matches5(TokenType.LT)) {
|
| - typeParameters = parseTypeParameterList();
|
| - }
|
| - Token equals = expect2(TokenType.EQ);
|
| - Token abstractKeyword = null;
|
| - if (matches(Keyword.ABSTRACT)) {
|
| - abstractKeyword = andAdvance;
|
| - }
|
| - TypeName superclass = parseTypeName();
|
| - WithClause withClause = null;
|
| - if (matches(Keyword.WITH)) {
|
| - withClause = parseWithClause();
|
| - }
|
| - ImplementsClause implementsClause = null;
|
| - if (matches(Keyword.IMPLEMENTS)) {
|
| - implementsClause = parseImplementsClause();
|
| + Token expect2(TokenType type) {
|
| + if (matches5(type)) {
|
| + return andAdvance;
|
| }
|
| - Token semicolon;
|
| - if (matches5(TokenType.SEMICOLON)) {
|
| - semicolon = andAdvance;
|
| + if (identical(type, TokenType.SEMICOLON)) {
|
| + reportError9(ParserErrorCode.EXPECTED_TOKEN, _currentToken.previous, [type.lexeme]);
|
| } else {
|
| - if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| - reportError8(ParserErrorCode.EXPECTED_TOKEN, [TokenType.SEMICOLON.lexeme]);
|
| - Token leftBracket = andAdvance;
|
| - parseClassMembers(className.name, getEndToken(leftBracket));
|
| - expect2(TokenType.CLOSE_CURLY_BRACKET);
|
| - } else {
|
| - reportError9(ParserErrorCode.EXPECTED_TOKEN, _currentToken.previous, [TokenType.SEMICOLON.lexeme]);
|
| - }
|
| - semicolon = createSyntheticToken2(TokenType.SEMICOLON);
|
| + reportError8(ParserErrorCode.EXPECTED_TOKEN, [type.lexeme]);
|
| }
|
| - return new ClassTypeAlias.full(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, className, typeParameters, equals, abstractKeyword, superclass, withClause, implementsClause, semicolon);
|
| + return _currentToken;
|
| }
|
|
|
| /**
|
| - * Parse a list of combinators in a directive.
|
| - *
|
| - * <pre>
|
| - * combinator ::=
|
| - * 'show' identifier (',' identifier)*
|
| - * | 'hide' identifier (',' identifier)*
|
| - * </pre>
|
| + * Search the given list of ranges for a range that contains the given index. Return the range
|
| + * that was found, or `null` if none of the ranges contain the index.
|
| *
|
| - * @return the combinators that were parsed
|
| + * @param ranges the ranges to be searched
|
| + * @param index the index contained in the returned range
|
| + * @return the range that was found
|
| */
|
| - List<Combinator> parseCombinators() {
|
| - List<Combinator> combinators = new List<Combinator>();
|
| - while (matches2(_SHOW) || matches2(_HIDE)) {
|
| - Token keyword = expect2(TokenType.IDENTIFIER);
|
| - if (keyword.lexeme == _SHOW) {
|
| - List<SimpleIdentifier> shownNames = parseIdentifierList();
|
| - combinators.add(new ShowCombinator.full(keyword, shownNames));
|
| - } else {
|
| - List<SimpleIdentifier> hiddenNames = parseIdentifierList();
|
| - combinators.add(new HideCombinator.full(keyword, hiddenNames));
|
| + List<int> findRange(List<List<int>> ranges, int index) {
|
| + for (List<int> range in ranges) {
|
| + if (range[0] <= index && index <= range[1]) {
|
| + return range;
|
| + } else if (index < range[0]) {
|
| + return null;
|
| }
|
| }
|
| - return combinators;
|
| + return null;
|
| + }
|
| + void gatherTodoComments(Token token) {
|
| + while (token != null && token.type != TokenType.EOF) {
|
| + Token commentToken = token.precedingComments;
|
| + while (commentToken != null) {
|
| + if (identical(commentToken.type, TokenType.SINGLE_LINE_COMMENT) || identical(commentToken.type, TokenType.MULTI_LINE_COMMENT)) {
|
| + scrapeTodoComment(commentToken);
|
| + }
|
| + commentToken = commentToken.next;
|
| + }
|
| + token = token.next;
|
| + }
|
| }
|
|
|
| /**
|
| - * Parse the documentation comment and metadata preceding a declaration. This method allows any
|
| - * number of documentation comments to occur before, after or between the metadata, but only
|
| - * returns the last (right-most) documentation comment that is found.
|
| - *
|
| - * <pre>
|
| - * metadata ::=
|
| - * annotation*
|
| - * </pre>
|
| + * Advance to the next token in the token stream, making it the new current token.
|
| *
|
| - * @return the documentation comment and metadata that were parsed
|
| + * @return the token that was current before this method was invoked
|
| */
|
| - CommentAndMetadata parseCommentAndMetadata() {
|
| - Comment comment = parseDocumentationComment();
|
| - List<Annotation> metadata = new List<Annotation>();
|
| - while (matches5(TokenType.AT)) {
|
| - metadata.add(parseAnnotation());
|
| - Comment optionalComment = parseDocumentationComment();
|
| - if (optionalComment != null) {
|
| - comment = optionalComment;
|
| - }
|
| - }
|
| - return new CommentAndMetadata(comment, metadata);
|
| + Token get andAdvance {
|
| + Token token = _currentToken;
|
| + advance();
|
| + return token;
|
| }
|
|
|
| /**
|
| - * Parse a comment reference from the source between square brackets.
|
| - *
|
| - * <pre>
|
| - * commentReference ::=
|
| - * 'new'? prefixedIdentifier
|
| - * </pre>
|
| + * Return a list of the ranges of characters in the given comment string that should be treated as
|
| + * code blocks.
|
| *
|
| - * @param referenceSource the source occurring between the square brackets within a documentation
|
| - * comment
|
| - * @param sourceOffset the offset of the first character of the reference source
|
| - * @return the comment reference that was parsed, or `null` if no reference could be found
|
| + * @param comment the comment being processed
|
| + * @return the ranges of characters that should be treated as code blocks
|
| */
|
| - CommentReference parseCommentReference(String referenceSource, int sourceOffset) {
|
| - if (referenceSource.length == 0) {
|
| - return null;
|
| - }
|
| - try {
|
| - List<bool> errorFound = [false];
|
| - AnalysisErrorListener listener = new AnalysisErrorListener_15(errorFound);
|
| - Scanner scanner = new Scanner(null, new SubSequenceReader(new CharSequence(referenceSource), sourceOffset), listener);
|
| - scanner.setSourceStart(1, 1);
|
| - Token firstToken = scanner.tokenize();
|
| - if (errorFound[0]) {
|
| - return null;
|
| - }
|
| - Token newKeyword = null;
|
| - if (matches3(firstToken, Keyword.NEW)) {
|
| - newKeyword = firstToken;
|
| - firstToken = firstToken.next;
|
| - }
|
| - if (matchesIdentifier2(firstToken)) {
|
| - Token secondToken = firstToken.next;
|
| - Token thirdToken = secondToken.next;
|
| - Token nextToken;
|
| - Identifier identifier;
|
| - if (matches4(secondToken, TokenType.PERIOD) && matchesIdentifier2(thirdToken)) {
|
| - identifier = new PrefixedIdentifier.full(new SimpleIdentifier.full(firstToken), secondToken, new SimpleIdentifier.full(thirdToken));
|
| - nextToken = thirdToken.next;
|
| - } else {
|
| - identifier = new SimpleIdentifier.full(firstToken);
|
| - nextToken = firstToken.next;
|
| + List<List<int>> getCodeBlockRanges(String comment) {
|
| + List<List<int>> ranges = new List<List<int>>();
|
| + int length = comment.length;
|
| + int index = 0;
|
| + if (comment.startsWith("/**") || comment.startsWith("///")) {
|
| + index = 3;
|
| + }
|
| + while (index < length) {
|
| + int currentChar = comment.codeUnitAt(index);
|
| + if (currentChar == 0xD || currentChar == 0xA) {
|
| + index = index + 1;
|
| + while (index < length && Character.isWhitespace(comment.codeUnitAt(index))) {
|
| + index = index + 1;
|
| }
|
| - if (nextToken.type != TokenType.EOF) {
|
| - return null;
|
| + if (JavaString.startsWithBefore(comment, "* ", index)) {
|
| + int end = index + 6;
|
| + while (end < length && comment.codeUnitAt(end) != 0xD && comment.codeUnitAt(end) != 0xA) {
|
| + end = end + 1;
|
| + }
|
| + ranges.add(<int> [index, end]);
|
| + index = end;
|
| }
|
| - return new CommentReference.full(newKeyword, identifier);
|
| - } else if (matches3(firstToken, Keyword.THIS) || matches3(firstToken, Keyword.NULL) || matches3(firstToken, Keyword.TRUE) || matches3(firstToken, Keyword.FALSE)) {
|
| - return null;
|
| + } else if (JavaString.startsWithBefore(comment, "[:", index)) {
|
| + int end = JavaString.indexOf(comment, ":]", index + 2);
|
| + if (end < 0) {
|
| + end = length;
|
| + }
|
| + ranges.add(<int> [index, end]);
|
| + index = end + 1;
|
| + } else {
|
| + index = index + 1;
|
| }
|
| - } on JavaException catch (exception) {
|
| }
|
| - return null;
|
| + return ranges;
|
| }
|
|
|
| /**
|
| - * Parse all of the comment references occurring in the given array of documentation comments.
|
| + * Return the end token associated with the given begin token, or `null` if either the given
|
| + * token is not a begin token or it does not have an end token associated with it.
|
| *
|
| - * <pre>
|
| - * commentReference ::=
|
| - * '[' 'new'? qualified ']' libraryReference?
|
| + * @param beginToken the token that is expected to have an end token associated with it
|
| + * @return the end token associated with the begin token
|
| + */
|
| + Token getEndToken(Token beginToken) {
|
| + if (beginToken is BeginToken) {
|
| + return ((beginToken as BeginToken)).endToken;
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if the current token is the first token of a return type that is followed
|
| + * by an identifier, possibly followed by a list of type parameters, followed by a
|
| + * left-parenthesis. This is used by parseTypeAlias to determine whether or not to parse a return
|
| + * type.
|
| *
|
| - * libraryReference ::=
|
| - * '(' stringLiteral ')'
|
| - * </pre>
|
| + * @return `true` if we can successfully parse the rest of a type alias if we first parse a
|
| + * return type.
|
| + */
|
| + bool hasReturnTypeInTypeAlias() {
|
| + Token next = skipReturnType(_currentToken);
|
| + if (next == null) {
|
| + return false;
|
| + }
|
| + return matchesIdentifier2(next);
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if the current token appears to be the beginning of a function declaration.
|
| *
|
| - * @param tokens the comment tokens representing the documentation comments to be parsed
|
| - * @return the comment references that were parsed
|
| + * @return `true` if the current token appears to be the beginning of a function declaration
|
| */
|
| - List<CommentReference> parseCommentReferences(List<Token> tokens) {
|
| - List<CommentReference> references = new List<CommentReference>();
|
| - for (Token token in tokens) {
|
| - String comment = token.lexeme;
|
| - int length = comment.length;
|
| - List<List<int>> codeBlockRanges = getCodeBlockRanges(comment);
|
| - int leftIndex = comment.indexOf('[');
|
| - while (leftIndex >= 0 && leftIndex + 1 < length) {
|
| - List<int> range = findRange(codeBlockRanges, leftIndex);
|
| - if (range == null) {
|
| - int rightIndex = JavaString.indexOf(comment, ']', leftIndex);
|
| - if (rightIndex >= 0) {
|
| - int firstChar = comment.codeUnitAt(leftIndex + 1);
|
| - if (firstChar != 0x27 && firstChar != 0x22) {
|
| - if (isLinkText(comment, rightIndex)) {
|
| - } else {
|
| - CommentReference reference = parseCommentReference(comment.substring(leftIndex + 1, rightIndex), token.offset + leftIndex + 1);
|
| - if (reference != null) {
|
| - references.add(reference);
|
| - }
|
| - }
|
| - }
|
| - } else {
|
| - rightIndex = leftIndex + 1;
|
| - }
|
| - leftIndex = JavaString.indexOf(comment, '[', rightIndex);
|
| - } else {
|
| - leftIndex = JavaString.indexOf(comment, '[', range[1] + 1);
|
| - }
|
| + bool isFunctionDeclaration() {
|
| + if (matches(Keyword.VOID)) {
|
| + return true;
|
| + }
|
| + Token afterReturnType = skipTypeName(_currentToken);
|
| + if (afterReturnType == null) {
|
| + afterReturnType = _currentToken;
|
| + }
|
| + Token afterIdentifier = skipSimpleIdentifier(afterReturnType);
|
| + if (afterIdentifier == null) {
|
| + afterIdentifier = skipSimpleIdentifier(_currentToken);
|
| + }
|
| + if (afterIdentifier == null) {
|
| + return false;
|
| + }
|
| + if (isFunctionExpression(afterIdentifier)) {
|
| + return true;
|
| + }
|
| + if (matches(Keyword.GET)) {
|
| + Token afterName = skipSimpleIdentifier(_currentToken.next);
|
| + if (afterName == null) {
|
| + return false;
|
| }
|
| + return matches4(afterName, TokenType.FUNCTION) || matches4(afterName, TokenType.OPEN_CURLY_BRACKET);
|
| }
|
| - return references;
|
| + return false;
|
| }
|
|
|
| /**
|
| - * Parse a compilation unit.
|
| + * Return `true` if the given token appears to be the beginning of a function expression.
|
| *
|
| - * Specified:
|
| + * @param startToken the token that might be the start of a function expression
|
| + * @return `true` if the given token appears to be the beginning of a function expression
|
| + */
|
| + bool isFunctionExpression(Token startToken) {
|
| + Token afterParameters = skipFormalParameterList(startToken);
|
| + if (afterParameters == null) {
|
| + return false;
|
| + }
|
| + return matchesAny(afterParameters, [TokenType.OPEN_CURLY_BRACKET, TokenType.FUNCTION]);
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if the given character is a valid hexadecimal digit.
|
| *
|
| - * <pre>
|
| - * compilationUnit ::=
|
| - * scriptTag? directive* topLevelDeclaration*
|
| - * </pre>
|
| - * Actual:
|
| + * @param character the character being tested
|
| + * @return `true` if the character is a valid hexadecimal digit
|
| + */
|
| + bool isHexDigit(int character) => (0x30 <= character && character <= 0x39) || (0x41 <= character && character <= 0x46) || (0x61 <= character && character <= 0x66);
|
| +
|
| + /**
|
| + * Return `true` if the current token is the first token in an initialized variable
|
| + * declaration rather than an expression. This method assumes that we have already skipped past
|
| + * any metadata that might be associated with the declaration.
|
| *
|
| * <pre>
|
| - * compilationUnit ::=
|
| - * scriptTag? topLevelElement*
|
| + * initializedVariableDeclaration ::=
|
| + * declaredIdentifier ('=' expression)? (',' initializedIdentifier)*
|
| *
|
| - * topLevelElement ::=
|
| - * directive
|
| - * | topLevelDeclaration
|
| + * declaredIdentifier ::=
|
| + * metadata finalConstVarOrType identifier
|
| + *
|
| + * finalConstVarOrType ::=
|
| + * 'final' type?
|
| + * | 'const' type?
|
| + * | 'var'
|
| + * | type
|
| + *
|
| + * type ::=
|
| + * qualified typeArguments?
|
| + *
|
| + * initializedIdentifier ::=
|
| + * identifier ('=' expression)?
|
| * </pre>
|
| *
|
| - * @return the compilation unit that was parsed
|
| + * @return `true` if the current token is the first token in an initialized variable
|
| + * declaration
|
| */
|
| - CompilationUnit parseCompilationUnit2() {
|
| - Token firstToken = _currentToken;
|
| - ScriptTag scriptTag = null;
|
| - if (matches5(TokenType.SCRIPT_TAG)) {
|
| - scriptTag = new ScriptTag.full(andAdvance);
|
| + bool isInitializedVariableDeclaration() {
|
| + if (matches(Keyword.FINAL) || matches(Keyword.VAR)) {
|
| + return true;
|
| }
|
| - bool libraryDirectiveFound = false;
|
| - bool partOfDirectiveFound = false;
|
| - bool partDirectiveFound = false;
|
| - bool directiveFoundAfterDeclaration = false;
|
| - List<Directive> directives = new List<Directive>();
|
| - List<CompilationUnitMember> declarations = new List<CompilationUnitMember>();
|
| - Token memberStart = _currentToken;
|
| - while (!matches5(TokenType.EOF)) {
|
| - CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| - if ((matches(Keyword.IMPORT) || matches(Keyword.EXPORT) || matches(Keyword.LIBRARY) || matches(Keyword.PART)) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| - Directive directive = parseDirective(commentAndMetadata);
|
| - if (declarations.length > 0 && !directiveFoundAfterDeclaration) {
|
| - reportError8(ParserErrorCode.DIRECTIVE_AFTER_DECLARATION, []);
|
| - directiveFoundAfterDeclaration = true;
|
| - }
|
| - if (directive is LibraryDirective) {
|
| - if (libraryDirectiveFound) {
|
| - reportError8(ParserErrorCode.MULTIPLE_LIBRARY_DIRECTIVES, []);
|
| - } else {
|
| - if (directives.length > 0) {
|
| - reportError8(ParserErrorCode.LIBRARY_DIRECTIVE_NOT_FIRST, []);
|
| - }
|
| - libraryDirectiveFound = true;
|
| - }
|
| - } else if (directive is PartDirective) {
|
| - partDirectiveFound = true;
|
| - } else if (partDirectiveFound) {
|
| - if (directive is ExportDirective) {
|
| - reportError9(ParserErrorCode.EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE, ((directive as NamespaceDirective)).keyword, []);
|
| - } else if (directive is ImportDirective) {
|
| - reportError9(ParserErrorCode.IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE, ((directive as NamespaceDirective)).keyword, []);
|
| - }
|
| - }
|
| - if (directive is PartOfDirective) {
|
| - if (partOfDirectiveFound) {
|
| - reportError8(ParserErrorCode.MULTIPLE_PART_OF_DIRECTIVES, []);
|
| - } else {
|
| - for (Directive precedingDirective in directives) {
|
| - reportError9(ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART, precedingDirective.keyword, []);
|
| - }
|
| - partOfDirectiveFound = true;
|
| - }
|
| - } else {
|
| - if (partOfDirectiveFound) {
|
| - reportError9(ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART, directive.keyword, []);
|
| - }
|
| - }
|
| - directives.add(directive);
|
| - } else if (matches5(TokenType.SEMICOLON)) {
|
| - reportError9(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
|
| - advance();
|
| - } else {
|
| - CompilationUnitMember member = parseCompilationUnitMember(commentAndMetadata);
|
| - if (member != null) {
|
| - declarations.add(member);
|
| - }
|
| - }
|
| - if (identical(_currentToken, memberStart)) {
|
| - reportError9(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
|
| - advance();
|
| - while (!matches5(TokenType.EOF) && !couldBeStartOfCompilationUnitMember()) {
|
| - advance();
|
| - }
|
| - }
|
| - memberStart = _currentToken;
|
| + if (matches(Keyword.CONST)) {
|
| + return !matchesAny(peek(), [
|
| + TokenType.LT,
|
| + TokenType.OPEN_CURLY_BRACKET,
|
| + TokenType.OPEN_SQUARE_BRACKET,
|
| + TokenType.INDEX]);
|
| }
|
| - return new CompilationUnit.full(firstToken, scriptTag, directives, declarations, _currentToken);
|
| + Token token = skipTypeName(_currentToken);
|
| + if (token == null) {
|
| + return false;
|
| + }
|
| + token = skipSimpleIdentifier(token);
|
| + if (token == null) {
|
| + return false;
|
| + }
|
| + TokenType type = token.type;
|
| + return identical(type, TokenType.EQ) || identical(type, TokenType.COMMA) || identical(type, TokenType.SEMICOLON) || matches3(token, Keyword.IN);
|
| }
|
|
|
| /**
|
| - * Parse a compilation unit member.
|
| + * Given that we have just found bracketed text within a comment, look to see whether that text is
|
| + * (a) followed by a parenthesized link address, (b) followed by a colon, or (c) followed by
|
| + * optional whitespace and another square bracket.
|
| *
|
| - * <pre>
|
| - * compilationUnitMember ::=
|
| - * classDefinition
|
| - * | functionTypeAlias
|
| - * | external functionSignature
|
| - * | external getterSignature
|
| - * | external setterSignature
|
| - * | functionSignature functionBody
|
| - * | returnType? getOrSet identifier formalParameterList functionBody
|
| - * | (final | const) type? staticFinalDeclarationList ';'
|
| - * | variableDeclaration ';'
|
| - * </pre>
|
| + * This method uses the syntax described by the <a
|
| + * href="http://daringfireball.net/projects/markdown/syntax">markdown</a> project.
|
| *
|
| - * @param commentAndMetadata the metadata to be associated with the member
|
| - * @return the compilation unit member that was parsed, or `null` if what was parsed could
|
| - * not be represented as a compilation unit member
|
| + * @param comment the comment text in which the bracketed text was found
|
| + * @param rightIndex the index of the right bracket
|
| + * @return `true` if the bracketed text is followed by a link address
|
| */
|
| - CompilationUnitMember parseCompilationUnitMember(CommentAndMetadata commentAndMetadata) {
|
| - Modifiers modifiers = parseModifiers();
|
| - if (matches(Keyword.CLASS)) {
|
| - return parseClassDeclaration(commentAndMetadata, validateModifiersForClass(modifiers));
|
| - } else if (matches(Keyword.TYPEDEF) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| - validateModifiersForTypedef(modifiers);
|
| - return parseTypeAlias(commentAndMetadata);
|
| + bool isLinkText(String comment, int rightIndex) {
|
| + int length = comment.length;
|
| + int index = rightIndex + 1;
|
| + if (index >= length) {
|
| + return false;
|
| }
|
| - if (matches(Keyword.VOID)) {
|
| - TypeName returnType = parseReturnType();
|
| - if ((matches(Keyword.GET) || matches(Keyword.SET)) && matchesIdentifier2(peek())) {
|
| - validateModifiersForTopLevelFunction(modifiers);
|
| - return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, null);
|
| - } else if (matches(Keyword.OPERATOR) && isOperator(peek())) {
|
| - reportError9(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken, []);
|
| - return convertToFunctionDeclaration(parseOperator(commentAndMetadata, modifiers.externalKeyword, returnType));
|
| - } else if (matchesIdentifier() && matchesAny(peek(), [
|
| - TokenType.OPEN_PAREN,
|
| - TokenType.OPEN_CURLY_BRACKET,
|
| - TokenType.FUNCTION])) {
|
| - validateModifiersForTopLevelFunction(modifiers);
|
| - return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, returnType);
|
| - } else {
|
| - if (matchesIdentifier()) {
|
| - if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
|
| - reportError(ParserErrorCode.VOID_VARIABLE, returnType, []);
|
| - return new TopLevelVariableDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, parseVariableDeclarationList2(null, validateModifiersForTopLevelVariable(modifiers), null), expect2(TokenType.SEMICOLON));
|
| - }
|
| - }
|
| - reportError9(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken, []);
|
| - return null;
|
| - }
|
| - } else if ((matches(Keyword.GET) || matches(Keyword.SET)) && matchesIdentifier2(peek())) {
|
| - validateModifiersForTopLevelFunction(modifiers);
|
| - return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, null);
|
| - } else if (matches(Keyword.OPERATOR) && isOperator(peek())) {
|
| - reportError9(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken, []);
|
| - return convertToFunctionDeclaration(parseOperator(commentAndMetadata, modifiers.externalKeyword, null));
|
| - } else if (!matchesIdentifier()) {
|
| - reportError9(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken, []);
|
| - return null;
|
| - } else if (matches4(peek(), TokenType.OPEN_PAREN)) {
|
| - validateModifiersForTopLevelFunction(modifiers);
|
| - return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, null);
|
| - } else if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
|
| - if (modifiers.constKeyword == null && modifiers.finalKeyword == null && modifiers.varKeyword == null) {
|
| - reportError8(ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE, []);
|
| - }
|
| - return new TopLevelVariableDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, parseVariableDeclarationList2(null, validateModifiersForTopLevelVariable(modifiers), null), expect2(TokenType.SEMICOLON));
|
| + int nextChar = comment.codeUnitAt(index);
|
| + if (nextChar == 0x28 || nextChar == 0x3A) {
|
| + return true;
|
| }
|
| - TypeName returnType = parseReturnType();
|
| - if ((matches(Keyword.GET) || matches(Keyword.SET)) && matchesIdentifier2(peek())) {
|
| - validateModifiersForTopLevelFunction(modifiers);
|
| - return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, returnType);
|
| - } else if (matches(Keyword.OPERATOR) && isOperator(peek())) {
|
| - reportError9(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken, []);
|
| - return convertToFunctionDeclaration(parseOperator(commentAndMetadata, modifiers.externalKeyword, returnType));
|
| - } else if (matches5(TokenType.AT)) {
|
| - return new TopLevelVariableDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, parseVariableDeclarationList2(null, validateModifiersForTopLevelVariable(modifiers), returnType), expect2(TokenType.SEMICOLON));
|
| - } else if (!matchesIdentifier()) {
|
| - reportError9(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken, []);
|
| - Token semicolon;
|
| - if (matches5(TokenType.SEMICOLON)) {
|
| - semicolon = andAdvance;
|
| - } else {
|
| - semicolon = createSyntheticToken2(TokenType.SEMICOLON);
|
| + while (Character.isWhitespace(nextChar)) {
|
| + index = index + 1;
|
| + if (index >= length) {
|
| + return false;
|
| }
|
| - List<VariableDeclaration> variables = new List<VariableDeclaration>();
|
| - variables.add(new VariableDeclaration.full(null, null, createSyntheticIdentifier(), null, null));
|
| - return new TopLevelVariableDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, new VariableDeclarationList.full(null, null, null, returnType, variables), semicolon);
|
| - }
|
| - if (matchesAny(peek(), [
|
| - TokenType.OPEN_PAREN,
|
| - TokenType.FUNCTION,
|
| - TokenType.OPEN_CURLY_BRACKET])) {
|
| - validateModifiersForTopLevelFunction(modifiers);
|
| - return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, returnType);
|
| + nextChar = comment.codeUnitAt(index);
|
| }
|
| - return new TopLevelVariableDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, parseVariableDeclarationList2(null, validateModifiersForTopLevelVariable(modifiers), returnType), expect2(TokenType.SEMICOLON));
|
| + return nextChar == 0x5B;
|
| }
|
|
|
| /**
|
| - * Parse a conditional expression.
|
| + * Return `true` if the given token appears to be the beginning of an operator declaration.
|
| *
|
| - * <pre>
|
| - * conditionalExpression ::=
|
| - * logicalOrExpression ('?' expressionWithoutCascade ':' expressionWithoutCascade)?
|
| - * </pre>
|
| + * @param startToken the token that might be the start of an operator declaration
|
| + * @return `true` if the given token appears to be the beginning of an operator declaration
|
| + */
|
| + bool isOperator(Token startToken) {
|
| + if (startToken.isOperator) {
|
| + Token token = startToken.next;
|
| + while (token.isOperator) {
|
| + token = token.next;
|
| + }
|
| + return matches4(token, TokenType.OPEN_PAREN);
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if the current token appears to be the beginning of a switch member.
|
| *
|
| - * @return the conditional expression that was parsed
|
| + * @return `true` if the current token appears to be the beginning of a switch member
|
| */
|
| - Expression parseConditionalExpression() {
|
| - Expression condition = parseLogicalOrExpression();
|
| - if (!matches5(TokenType.QUESTION)) {
|
| - return condition;
|
| + bool isSwitchMember() {
|
| + Token token = _currentToken;
|
| + while (matches4(token, TokenType.IDENTIFIER) && matches4(token.next, TokenType.COLON)) {
|
| + token = token.next.next;
|
| }
|
| - Token question = andAdvance;
|
| - Expression thenExpression = parseExpressionWithoutCascade();
|
| - Token colon = expect2(TokenType.COLON);
|
| - Expression elseExpression = parseExpressionWithoutCascade();
|
| - return new ConditionalExpression.full(condition, question, thenExpression, colon, elseExpression);
|
| + if (identical(token.type, TokenType.KEYWORD)) {
|
| + Keyword keyword = ((token as KeywordToken)).keyword;
|
| + return identical(keyword, Keyword.CASE) || identical(keyword, Keyword.DEFAULT);
|
| + }
|
| + return false;
|
| }
|
|
|
| /**
|
| - * Parse a const expression.
|
| + * Return `true` if the given token appears to be the first token of a type name that is
|
| + * followed by a variable or field formal parameter.
|
| *
|
| - * <pre>
|
| - * constExpression ::=
|
| - * instanceCreationExpression
|
| - * | listLiteral
|
| - * | mapLiteral
|
| - * </pre>
|
| + * @param startToken the first token of the sequence being checked
|
| + * @return `true` if there is a type name and variable starting at the given token
|
| + */
|
| + bool isTypedIdentifier(Token startToken) {
|
| + Token token = skipReturnType(startToken);
|
| + if (token == null) {
|
| + return false;
|
| + } else if (matchesIdentifier2(token)) {
|
| + return true;
|
| + } else if (matches3(token, Keyword.THIS) && matches4(token.next, TokenType.PERIOD) && matchesIdentifier2(token.next.next)) {
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * Compare the given tokens to find the token that appears first in the source being parsed. That
|
| + * is, return the left-most of all of the tokens. The arguments are allowed to be `null`.
|
| + * Return the token with the smallest offset, or `null` if there are no arguments or if all
|
| + * of the arguments are `null`.
|
| *
|
| - * @return the const expression that was parsed
|
| + * @param tokens the tokens being compared
|
| + * @return the token with the smallest offset
|
| */
|
| - Expression parseConstExpression() {
|
| - Token keyword = expect(Keyword.CONST);
|
| - if (matches5(TokenType.OPEN_SQUARE_BRACKET) || matches5(TokenType.INDEX)) {
|
| - return parseListLiteral(keyword, null);
|
| - } else if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| - return parseMapLiteral(keyword, null);
|
| - } else if (matches5(TokenType.LT)) {
|
| - return parseListOrMapLiteral(keyword);
|
| + Token lexicallyFirst(List<Token> tokens) {
|
| + Token first = null;
|
| + int firstOffset = 2147483647;
|
| + for (Token token in tokens) {
|
| + if (token != null) {
|
| + int offset = token.offset;
|
| + if (offset < firstOffset) {
|
| + first = token;
|
| + firstOffset = offset;
|
| + }
|
| + }
|
| }
|
| - return parseInstanceCreationExpression(keyword);
|
| - }
|
| - ConstructorDeclaration parseConstructor(CommentAndMetadata commentAndMetadata, Token externalKeyword, Token constKeyword, Token factoryKeyword, SimpleIdentifier returnType, Token period, SimpleIdentifier name, FormalParameterList parameters) {
|
| - bool bodyAllowed = externalKeyword == null;
|
| - Token separator = null;
|
| - List<ConstructorInitializer> initializers = null;
|
| - if (matches5(TokenType.COLON)) {
|
| - separator = andAdvance;
|
| - initializers = new List<ConstructorInitializer>();
|
| - do {
|
| - if (matches(Keyword.THIS)) {
|
| - if (matches4(peek(), TokenType.OPEN_PAREN)) {
|
| - bodyAllowed = false;
|
| - initializers.add(parseRedirectingConstructorInvocation());
|
| - } else if (matches4(peek(), TokenType.PERIOD) && matches4(peek2(3), TokenType.OPEN_PAREN)) {
|
| - bodyAllowed = false;
|
| - initializers.add(parseRedirectingConstructorInvocation());
|
| - } else {
|
| - initializers.add(parseConstructorFieldInitializer());
|
| - }
|
| - } else if (matches(Keyword.SUPER)) {
|
| - initializers.add(parseSuperConstructorInvocation());
|
| - } else {
|
| - initializers.add(parseConstructorFieldInitializer());
|
| - }
|
| - } while (optional(TokenType.COMMA));
|
| - }
|
| - ConstructorName redirectedConstructor = null;
|
| - FunctionBody body;
|
| - if (matches5(TokenType.EQ)) {
|
| - separator = andAdvance;
|
| - redirectedConstructor = parseConstructorName();
|
| - body = new EmptyFunctionBody.full(expect2(TokenType.SEMICOLON));
|
| - if (factoryKeyword == null) {
|
| - reportError(ParserErrorCode.REDIRECTION_IN_NON_FACTORY_CONSTRUCTOR, redirectedConstructor, []);
|
| - }
|
| - } else {
|
| - body = parseFunctionBody(true, ParserErrorCode.MISSING_FUNCTION_BODY, false);
|
| - if (constKeyword != null && factoryKeyword != null) {
|
| - reportError9(ParserErrorCode.CONST_FACTORY, factoryKeyword, []);
|
| - } else if (body is EmptyFunctionBody) {
|
| - if (factoryKeyword != null && externalKeyword == null) {
|
| - reportError9(ParserErrorCode.FACTORY_WITHOUT_BODY, factoryKeyword, []);
|
| - }
|
| - } else {
|
| - if (constKeyword != null) {
|
| - reportError(ParserErrorCode.CONST_CONSTRUCTOR_WITH_BODY, body, []);
|
| - } else if (!bodyAllowed) {
|
| - reportError(ParserErrorCode.EXTERNAL_CONSTRUCTOR_WITH_BODY, body, []);
|
| - }
|
| - }
|
| - }
|
| - return new ConstructorDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, constKeyword, factoryKeyword, returnType, period, name, parameters, separator, initializers, redirectedConstructor, body);
|
| + return first;
|
| }
|
|
|
| /**
|
| - * Parse a field initializer within a constructor.
|
| + * Return `true` if the current token matches the given keyword.
|
| *
|
| - * <pre>
|
| - * fieldInitializer:
|
| - * ('this' '.')? identifier '=' conditionalExpression cascadeSection*
|
| - * </pre>
|
| + * @param keyword the keyword that can optionally appear in the current location
|
| + * @return `true` if the current token matches the given keyword
|
| + */
|
| + bool matches(Keyword keyword) => matches3(_currentToken, keyword);
|
| +
|
| + /**
|
| + * Return `true` if the current token matches the given identifier.
|
| *
|
| - * @return the field initializer that was parsed
|
| + * @param identifier the identifier that can optionally appear in the current location
|
| + * @return `true` if the current token matches the given identifier
|
| */
|
| - ConstructorFieldInitializer parseConstructorFieldInitializer() {
|
| - Token keyword = null;
|
| - Token period = null;
|
| - if (matches(Keyword.THIS)) {
|
| - keyword = andAdvance;
|
| - period = expect2(TokenType.PERIOD);
|
| - }
|
| - SimpleIdentifier fieldName = parseSimpleIdentifier();
|
| - Token equals = expect2(TokenType.EQ);
|
| - Expression expression = parseConditionalExpression();
|
| - TokenType tokenType = _currentToken.type;
|
| - if (identical(tokenType, TokenType.PERIOD_PERIOD)) {
|
| - List<Expression> cascadeSections = new List<Expression>();
|
| - while (identical(tokenType, TokenType.PERIOD_PERIOD)) {
|
| - Expression section = parseCascadeSection();
|
| - if (section != null) {
|
| - cascadeSections.add(section);
|
| + bool matches2(String identifier) => identical(_currentToken.type, TokenType.IDENTIFIER) && _currentToken.lexeme == identifier;
|
| +
|
| + /**
|
| + * Return `true` if the given token matches the given keyword.
|
| + *
|
| + * @param token the token being tested
|
| + * @param keyword the keyword that is being tested for
|
| + * @return `true` if the given token matches the given keyword
|
| + */
|
| + bool matches3(Token token, Keyword keyword) => identical(token.type, TokenType.KEYWORD) && identical(((token as KeywordToken)).keyword, keyword);
|
| +
|
| + /**
|
| + * Return `true` if the given token has the given type.
|
| + *
|
| + * @param token the token being tested
|
| + * @param type the type of token that is being tested for
|
| + * @return `true` if the given token has the given type
|
| + */
|
| + bool matches4(Token token, TokenType type) => identical(token.type, type);
|
| +
|
| + /**
|
| + * Return `true` if the current token has the given type. Note that this method, unlike
|
| + * other variants, will modify the token stream if possible to match a wider range of tokens. In
|
| + * particular, if we are attempting to match a '>' and the next token is either a '>>' or '>>>',
|
| + * the token stream will be re-written and `true` will be returned.
|
| + *
|
| + * @param type the type of token that can optionally appear in the current location
|
| + * @return `true` if the current token has the given type
|
| + */
|
| + bool matches5(TokenType type) {
|
| + TokenType currentType = _currentToken.type;
|
| + if (currentType != type) {
|
| + if (identical(type, TokenType.GT)) {
|
| + if (identical(currentType, TokenType.GT_GT)) {
|
| + int offset = _currentToken.offset;
|
| + Token first = new Token(TokenType.GT, offset);
|
| + Token second = new Token(TokenType.GT, offset + 1);
|
| + second.setNext(_currentToken.next);
|
| + first.setNext(second);
|
| + _currentToken.previous.setNext(first);
|
| + _currentToken = first;
|
| + return true;
|
| + } else if (identical(currentType, TokenType.GT_EQ)) {
|
| + int offset = _currentToken.offset;
|
| + Token first = new Token(TokenType.GT, offset);
|
| + Token second = new Token(TokenType.EQ, offset + 1);
|
| + second.setNext(_currentToken.next);
|
| + first.setNext(second);
|
| + _currentToken.previous.setNext(first);
|
| + _currentToken = first;
|
| + return true;
|
| + } else if (identical(currentType, TokenType.GT_GT_EQ)) {
|
| + int offset = _currentToken.offset;
|
| + Token first = new Token(TokenType.GT, offset);
|
| + Token second = new Token(TokenType.GT, offset + 1);
|
| + Token third = new Token(TokenType.EQ, offset + 2);
|
| + third.setNext(_currentToken.next);
|
| + second.setNext(third);
|
| + first.setNext(second);
|
| + _currentToken.previous.setNext(first);
|
| + _currentToken = first;
|
| + return true;
|
| }
|
| - tokenType = _currentToken.type;
|
| }
|
| - expression = new CascadeExpression.full(expression, cascadeSections);
|
| + return false;
|
| }
|
| - return new ConstructorFieldInitializer.full(keyword, period, fieldName, equals, expression);
|
| + return true;
|
| }
|
|
|
| /**
|
| - * Parse the name of a constructor.
|
| - *
|
| - * <pre>
|
| - * constructorName:
|
| - * type ('.' identifier)?
|
| - * </pre>
|
| + * Return `true` if the given token has any one of the given types.
|
| *
|
| - * @return the constructor name that was parsed
|
| + * @param token the token being tested
|
| + * @param types the types of token that are being tested for
|
| + * @return `true` if the given token has any of the given types
|
| */
|
| - ConstructorName parseConstructorName() {
|
| - TypeName type = parseTypeName();
|
| - Token period = null;
|
| - SimpleIdentifier name = null;
|
| - if (matches5(TokenType.PERIOD)) {
|
| - period = andAdvance;
|
| - name = parseSimpleIdentifier();
|
| + bool matchesAny(Token token, List<TokenType> types) {
|
| + TokenType actualType = token.type;
|
| + for (TokenType type in types) {
|
| + if (identical(actualType, type)) {
|
| + return true;
|
| + }
|
| }
|
| - return new ConstructorName.full(type, period, name);
|
| + return false;
|
| }
|
|
|
| /**
|
| - * Parse a continue statement.
|
| + * Return `true` if the current token is a valid identifier. Valid identifiers include
|
| + * built-in identifiers (pseudo-keywords).
|
| *
|
| - * <pre>
|
| - * continueStatement ::=
|
| - * 'continue' identifier? ';'
|
| - * </pre>
|
| + * @return `true` if the current token is a valid identifier
|
| + */
|
| + bool matchesIdentifier() => matchesIdentifier2(_currentToken);
|
| +
|
| + /**
|
| + * Return `true` if the given token is a valid identifier. Valid identifiers include
|
| + * built-in identifiers (pseudo-keywords).
|
| *
|
| - * @return the continue statement that was parsed
|
| + * @return `true` if the given token is a valid identifier
|
| */
|
| - Statement parseContinueStatement() {
|
| - Token continueKeyword = expect(Keyword.CONTINUE);
|
| - if (!_inLoop && !_inSwitch) {
|
| - reportError9(ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP, continueKeyword, []);
|
| - }
|
| - SimpleIdentifier label = null;
|
| - if (matchesIdentifier()) {
|
| - label = parseSimpleIdentifier();
|
| - }
|
| - if (_inSwitch && !_inLoop && label == null) {
|
| - reportError9(ParserErrorCode.CONTINUE_WITHOUT_LABEL_IN_CASE, continueKeyword, []);
|
| + bool matchesIdentifier2(Token token) => matches4(token, TokenType.IDENTIFIER) || (matches4(token, TokenType.KEYWORD) && ((token as KeywordToken)).keyword.isPseudoKeyword);
|
| +
|
| + /**
|
| + * If the current token has the given type, then advance to the next token and return `true`
|
| + * . Otherwise, return `false` without advancing.
|
| + *
|
| + * @param type the type of token that can optionally appear in the current location
|
| + * @return `true` if the current token has the given type
|
| + */
|
| + bool optional(TokenType type) {
|
| + if (matches5(type)) {
|
| + advance();
|
| + return true;
|
| }
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new ContinueStatement.full(continueKeyword, label, semicolon);
|
| + return false;
|
| }
|
|
|
| /**
|
| - * Parse a directive.
|
| + * Parse an additive expression.
|
| *
|
| * <pre>
|
| - * directive ::=
|
| - * exportDirective
|
| - * | libraryDirective
|
| - * | importDirective
|
| - * | partDirective
|
| + * additiveExpression ::=
|
| + * multiplicativeExpression (additiveOperator multiplicativeExpression)*
|
| + * | 'super' (additiveOperator multiplicativeExpression)+
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the metadata to be associated with the directive
|
| - * @return the directive that was parsed
|
| + * @return the additive expression that was parsed
|
| */
|
| - Directive parseDirective(CommentAndMetadata commentAndMetadata) {
|
| - if (matches(Keyword.IMPORT)) {
|
| - return parseImportDirective(commentAndMetadata);
|
| - } else if (matches(Keyword.EXPORT)) {
|
| - return parseExportDirective(commentAndMetadata);
|
| - } else if (matches(Keyword.LIBRARY)) {
|
| - return parseLibraryDirective(commentAndMetadata);
|
| - } else if (matches(Keyword.PART)) {
|
| - return parsePartDirective(commentAndMetadata);
|
| + Expression parseAdditiveExpression() {
|
| + Expression expression;
|
| + if (matches(Keyword.SUPER) && _currentToken.next.type.isAdditiveOperator) {
|
| + expression = new SuperExpression.full(andAdvance);
|
| } else {
|
| - throw new IllegalStateException("parseDirective invoked in an invalid state; currentToken = ${_currentToken}");
|
| + expression = parseMultiplicativeExpression();
|
| + }
|
| + while (_currentToken.type.isAdditiveOperator) {
|
| + Token operator = andAdvance;
|
| + expression = new BinaryExpression.full(expression, operator, parseMultiplicativeExpression());
|
| }
|
| + return expression;
|
| }
|
|
|
| /**
|
| - * Parse a documentation comment.
|
| + * Parse an argument definition test.
|
| *
|
| * <pre>
|
| - * documentationComment ::=
|
| - * multiLineComment?
|
| - * | singleLineComment*
|
| + * argumentDefinitionTest ::=
|
| + * '?' identifier
|
| * </pre>
|
| *
|
| - * @return the documentation comment that was parsed, or `null` if there was no comment
|
| + * @return the argument definition test that was parsed
|
| */
|
| - Comment parseDocumentationComment() {
|
| - List<Token> commentTokens = new List<Token>();
|
| - Token commentToken = _currentToken.precedingComments;
|
| - while (commentToken != null) {
|
| - if (identical(commentToken.type, TokenType.SINGLE_LINE_COMMENT)) {
|
| - if (commentToken.lexeme.startsWith("///")) {
|
| - if (commentTokens.length == 1 && commentTokens[0].lexeme.startsWith("/**")) {
|
| - commentTokens.clear();
|
| - }
|
| - commentTokens.add(commentToken);
|
| - }
|
| - } else {
|
| - if (commentToken.lexeme.startsWith("/**")) {
|
| - commentTokens.clear();
|
| - commentTokens.add(commentToken);
|
| - }
|
| - }
|
| - commentToken = commentToken.next;
|
| - }
|
| - if (commentTokens.isEmpty) {
|
| - return null;
|
| - }
|
| - List<Token> tokens = new List.from(commentTokens);
|
| - List<CommentReference> references = parseCommentReferences(tokens);
|
| - return Comment.createDocumentationComment2(tokens, references);
|
| + ArgumentDefinitionTest parseArgumentDefinitionTest() {
|
| + Token question = expect2(TokenType.QUESTION);
|
| + SimpleIdentifier identifier = parseSimpleIdentifier();
|
| + reportError9(ParserErrorCode.DEPRECATED_ARGUMENT_DEFINITION_TEST, question, []);
|
| + return new ArgumentDefinitionTest.full(question, identifier);
|
| }
|
|
|
| /**
|
| - * Parse a do statement.
|
| + * Parse an assert statement.
|
| *
|
| * <pre>
|
| - * doStatement ::=
|
| - * 'do' statement 'while' '(' expression ')' ';'
|
| + * assertStatement ::=
|
| + * 'assert' '(' conditionalExpression ')' ';'
|
| * </pre>
|
| *
|
| - * @return the do statement that was parsed
|
| + * @return the assert statement
|
| */
|
| - Statement parseDoStatement() {
|
| - bool wasInLoop = _inLoop;
|
| - _inLoop = true;
|
| - try {
|
| - Token doKeyword = expect(Keyword.DO);
|
| - Statement body = parseStatement2();
|
| - Token whileKeyword = expect(Keyword.WHILE);
|
| - Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| - Expression condition = parseExpression2();
|
| - Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new DoStatement.full(doKeyword, body, whileKeyword, leftParenthesis, condition, rightParenthesis, semicolon);
|
| - } finally {
|
| - _inLoop = wasInLoop;
|
| + AssertStatement parseAssertStatement() {
|
| + Token keyword = expect(Keyword.ASSERT);
|
| + Token leftParen = expect2(TokenType.OPEN_PAREN);
|
| + Expression expression = parseExpression2();
|
| + if (expression is AssignmentExpression) {
|
| + reportError(ParserErrorCode.ASSERT_DOES_NOT_TAKE_ASSIGNMENT, expression, []);
|
| + } else if (expression is CascadeExpression) {
|
| + reportError(ParserErrorCode.ASSERT_DOES_NOT_TAKE_CASCADE, expression, []);
|
| + } else if (expression is ThrowExpression) {
|
| + reportError(ParserErrorCode.ASSERT_DOES_NOT_TAKE_THROW, expression, []);
|
| + } else if (expression is RethrowExpression) {
|
| + reportError(ParserErrorCode.ASSERT_DOES_NOT_TAKE_RETHROW, expression, []);
|
| }
|
| + Token rightParen = expect2(TokenType.CLOSE_PAREN);
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new AssertStatement.full(keyword, leftParen, expression, rightParen, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse an empty statement.
|
| + * Parse an assignable expression.
|
| *
|
| * <pre>
|
| - * emptyStatement ::=
|
| - * ';'
|
| + * assignableExpression ::=
|
| + * primary (arguments* assignableSelector)+
|
| + * | 'super' assignableSelector
|
| + * | identifier
|
| * </pre>
|
| *
|
| - * @return the empty statement that was parsed
|
| + * @param primaryAllowed `true` if the expression is allowed to be a primary without any
|
| + * assignable selector
|
| + * @return the assignable expression that was parsed
|
| */
|
| - Statement parseEmptyStatement() => new EmptyStatement.full(andAdvance);
|
| + Expression parseAssignableExpression(bool primaryAllowed) {
|
| + if (matches(Keyword.SUPER)) {
|
| + return parseAssignableSelector(new SuperExpression.full(andAdvance), false);
|
| + }
|
| + Expression expression = parsePrimaryExpression();
|
| + bool isOptional = primaryAllowed || expression is SimpleIdentifier;
|
| + while (true) {
|
| + while (matches5(TokenType.OPEN_PAREN)) {
|
| + ArgumentList argumentList = parseArgumentList();
|
| + if (expression is SimpleIdentifier) {
|
| + expression = new MethodInvocation.full(null, null, expression as SimpleIdentifier, argumentList);
|
| + } else if (expression is PrefixedIdentifier) {
|
| + PrefixedIdentifier identifier = expression as PrefixedIdentifier;
|
| + expression = new MethodInvocation.full(identifier.prefix, identifier.period, identifier.identifier, argumentList);
|
| + } else if (expression is PropertyAccess) {
|
| + PropertyAccess access = expression as PropertyAccess;
|
| + expression = new MethodInvocation.full(access.target, access.operator, access.propertyName, argumentList);
|
| + } else {
|
| + expression = new FunctionExpressionInvocation.full(expression, argumentList);
|
| + }
|
| + if (!primaryAllowed) {
|
| + isOptional = false;
|
| + }
|
| + }
|
| + Expression selectorExpression = parseAssignableSelector(expression, isOptional || (expression is PrefixedIdentifier));
|
| + if (identical(selectorExpression, expression)) {
|
| + if (!isOptional && (expression is PrefixedIdentifier)) {
|
| + PrefixedIdentifier identifier = expression as PrefixedIdentifier;
|
| + expression = new PropertyAccess.full(identifier.prefix, identifier.period, identifier.identifier);
|
| + }
|
| + return expression;
|
| + }
|
| + expression = selectorExpression;
|
| + isOptional = true;
|
| + }
|
| + }
|
|
|
| /**
|
| - * Parse an equality expression.
|
| + * Parse an assignable selector.
|
| *
|
| * <pre>
|
| - * equalityExpression ::=
|
| - * relationalExpression (equalityOperator relationalExpression)?
|
| - * | 'super' equalityOperator relationalExpression
|
| + * assignableSelector ::=
|
| + * '[' expression ']'
|
| + * | '.' identifier
|
| * </pre>
|
| *
|
| - * @return the equality expression that was parsed
|
| + * @param prefix the expression preceding the selector
|
| + * @param optional `true` if the selector is optional
|
| + * @return the assignable selector that was parsed
|
| */
|
| - Expression parseEqualityExpression() {
|
| - Expression expression;
|
| - if (matches(Keyword.SUPER) && _currentToken.next.type.isEqualityOperator) {
|
| - expression = new SuperExpression.full(andAdvance);
|
| + Expression parseAssignableSelector(Expression prefix, bool optional) {
|
| + if (matches5(TokenType.OPEN_SQUARE_BRACKET)) {
|
| + Token leftBracket = andAdvance;
|
| + Expression index = parseExpression2();
|
| + Token rightBracket = expect2(TokenType.CLOSE_SQUARE_BRACKET);
|
| + return new IndexExpression.forTarget_full(prefix, leftBracket, index, rightBracket);
|
| + } else if (matches5(TokenType.PERIOD)) {
|
| + Token period = andAdvance;
|
| + return new PropertyAccess.full(prefix, period, parseSimpleIdentifier());
|
| } else {
|
| - expression = parseRelationalExpression();
|
| - }
|
| - bool leftEqualityExpression = false;
|
| - while (_currentToken.type.isEqualityOperator) {
|
| - Token operator = andAdvance;
|
| - if (leftEqualityExpression) {
|
| - reportError(ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND, expression, []);
|
| + if (!optional) {
|
| + reportError8(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, []);
|
| }
|
| - expression = new BinaryExpression.full(expression, operator, parseRelationalExpression());
|
| - leftEqualityExpression = true;
|
| + return prefix;
|
| }
|
| - return expression;
|
| }
|
|
|
| /**
|
| - * Parse an export directive.
|
| + * Parse a bitwise and expression.
|
| *
|
| * <pre>
|
| - * exportDirective ::=
|
| - * metadata 'export' stringLiteral combinator*';'
|
| + * bitwiseAndExpression ::=
|
| + * shiftExpression ('&' shiftExpression)*
|
| + * | 'super' ('&' shiftExpression)+
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the metadata to be associated with the directive
|
| - * @return the export directive that was parsed
|
| + * @return the bitwise and expression that was parsed
|
| */
|
| - ExportDirective parseExportDirective(CommentAndMetadata commentAndMetadata) {
|
| - Token exportKeyword = expect(Keyword.EXPORT);
|
| - StringLiteral libraryUri = parseStringLiteral();
|
| - List<Combinator> combinators = parseCombinators();
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new ExportDirective.full(commentAndMetadata.comment, commentAndMetadata.metadata, exportKeyword, libraryUri, combinators, semicolon);
|
| + Expression parseBitwiseAndExpression() {
|
| + Expression expression;
|
| + if (matches(Keyword.SUPER) && matches4(peek(), TokenType.AMPERSAND)) {
|
| + expression = new SuperExpression.full(andAdvance);
|
| + } else {
|
| + expression = parseShiftExpression();
|
| + }
|
| + while (matches5(TokenType.AMPERSAND)) {
|
| + Token operator = andAdvance;
|
| + expression = new BinaryExpression.full(expression, operator, parseShiftExpression());
|
| + }
|
| + return expression;
|
| }
|
|
|
| /**
|
| - * Parse an expression that does not contain any cascades.
|
| + * Parse a bitwise exclusive-or expression.
|
| *
|
| * <pre>
|
| - * expression ::=
|
| - * assignableExpression assignmentOperator expression
|
| - * | conditionalExpression cascadeSection*
|
| - * | throwExpression
|
| + * bitwiseXorExpression ::=
|
| + * bitwiseAndExpression ('^' bitwiseAndExpression)*
|
| + * | 'super' ('^' bitwiseAndExpression)+
|
| * </pre>
|
| *
|
| - * @return the expression that was parsed
|
| + * @return the bitwise exclusive-or expression that was parsed
|
| */
|
| - Expression parseExpression2() {
|
| - if (matches(Keyword.THROW)) {
|
| - return parseThrowExpression();
|
| - } else if (matches(Keyword.RETHROW)) {
|
| - return parseRethrowExpression();
|
| + Expression parseBitwiseXorExpression() {
|
| + Expression expression;
|
| + if (matches(Keyword.SUPER) && matches4(peek(), TokenType.CARET)) {
|
| + expression = new SuperExpression.full(andAdvance);
|
| + } else {
|
| + expression = parseBitwiseAndExpression();
|
| }
|
| - Expression expression = parseConditionalExpression();
|
| - TokenType tokenType = _currentToken.type;
|
| - if (identical(tokenType, TokenType.PERIOD_PERIOD)) {
|
| - List<Expression> cascadeSections = new List<Expression>();
|
| - while (identical(tokenType, TokenType.PERIOD_PERIOD)) {
|
| - Expression section = parseCascadeSection();
|
| - if (section != null) {
|
| - cascadeSections.add(section);
|
| - }
|
| - tokenType = _currentToken.type;
|
| - }
|
| - return new CascadeExpression.full(expression, cascadeSections);
|
| - } else if (tokenType.isAssignmentOperator) {
|
| + while (matches5(TokenType.CARET)) {
|
| Token operator = andAdvance;
|
| - ensureAssignable(expression);
|
| - return new AssignmentExpression.full(expression, operator, parseExpression2());
|
| + expression = new BinaryExpression.full(expression, operator, parseBitwiseAndExpression());
|
| }
|
| return expression;
|
| }
|
|
|
| /**
|
| - * Parse a list of expressions.
|
| + * Parse a break statement.
|
| *
|
| * <pre>
|
| - * expressionList ::=
|
| - * expression (',' expression)*
|
| + * breakStatement ::=
|
| + * 'break' identifier? ';'
|
| * </pre>
|
| *
|
| - * @return the expression that was parsed
|
| + * @return the break statement that was parsed
|
| */
|
| - List<Expression> parseExpressionList() {
|
| - List<Expression> expressions = new List<Expression>();
|
| - expressions.add(parseExpression2());
|
| - while (optional(TokenType.COMMA)) {
|
| - expressions.add(parseExpression2());
|
| + Statement parseBreakStatement() {
|
| + Token breakKeyword = expect(Keyword.BREAK);
|
| + SimpleIdentifier label = null;
|
| + if (matchesIdentifier()) {
|
| + label = parseSimpleIdentifier();
|
| }
|
| - return expressions;
|
| + if (!_inLoop && !_inSwitch && label == null) {
|
| + reportError9(ParserErrorCode.BREAK_OUTSIDE_OF_LOOP, breakKeyword, []);
|
| + }
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new BreakStatement.full(breakKeyword, label, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse an expression that does not contain any cascades.
|
| + * Parse a cascade section.
|
| *
|
| * <pre>
|
| - * expressionWithoutCascade ::=
|
| - * assignableExpression assignmentOperator expressionWithoutCascade
|
| - * | conditionalExpression
|
| - * | throwExpressionWithoutCascade
|
| - * </pre>
|
| + * cascadeSection ::=
|
| + * '..' (cascadeSelector arguments*) (assignableSelector arguments*)* cascadeAssignment?
|
| *
|
| - * @return the expression that was parsed
|
| - */
|
| - Expression parseExpressionWithoutCascade() {
|
| - if (matches(Keyword.THROW)) {
|
| - return parseThrowExpressionWithoutCascade();
|
| - } else if (matches(Keyword.RETHROW)) {
|
| - return parseRethrowExpression();
|
| + * cascadeSelector ::=
|
| + * '[' expression ']'
|
| + * | identifier
|
| + *
|
| + * cascadeAssignment ::=
|
| + * assignmentOperator expressionWithoutCascade
|
| + * </pre>
|
| + *
|
| + * @return the expression representing the cascaded method invocation
|
| + */
|
| + Expression parseCascadeSection() {
|
| + Token period = expect2(TokenType.PERIOD_PERIOD);
|
| + Expression expression = null;
|
| + SimpleIdentifier functionName = null;
|
| + if (matchesIdentifier()) {
|
| + functionName = parseSimpleIdentifier();
|
| + } else if (identical(_currentToken.type, TokenType.OPEN_SQUARE_BRACKET)) {
|
| + Token leftBracket = andAdvance;
|
| + Expression index = parseExpression2();
|
| + Token rightBracket = expect2(TokenType.CLOSE_SQUARE_BRACKET);
|
| + expression = new IndexExpression.forCascade_full(period, leftBracket, index, rightBracket);
|
| + period = null;
|
| + } else {
|
| + reportError9(ParserErrorCode.MISSING_IDENTIFIER, _currentToken, [_currentToken.lexeme]);
|
| + functionName = createSyntheticIdentifier();
|
| + }
|
| + if (identical(_currentToken.type, TokenType.OPEN_PAREN)) {
|
| + while (identical(_currentToken.type, TokenType.OPEN_PAREN)) {
|
| + if (functionName != null) {
|
| + expression = new MethodInvocation.full(expression, period, functionName, parseArgumentList());
|
| + period = null;
|
| + functionName = null;
|
| + } else if (expression == null) {
|
| + expression = new MethodInvocation.full(expression, period, createSyntheticIdentifier(), parseArgumentList());
|
| + } else {
|
| + expression = new FunctionExpressionInvocation.full(expression, parseArgumentList());
|
| + }
|
| + }
|
| + } else if (functionName != null) {
|
| + expression = new PropertyAccess.full(expression, period, functionName);
|
| + period = null;
|
| + }
|
| + bool progress = true;
|
| + while (progress) {
|
| + progress = false;
|
| + Expression selector = parseAssignableSelector(expression, true);
|
| + if (selector != expression) {
|
| + expression = selector;
|
| + progress = true;
|
| + while (identical(_currentToken.type, TokenType.OPEN_PAREN)) {
|
| + if (expression is PropertyAccess) {
|
| + PropertyAccess propertyAccess = expression as PropertyAccess;
|
| + expression = new MethodInvocation.full(propertyAccess.target, propertyAccess.operator, propertyAccess.propertyName, parseArgumentList());
|
| + } else {
|
| + expression = new FunctionExpressionInvocation.full(expression, parseArgumentList());
|
| + }
|
| + }
|
| + }
|
| }
|
| - Expression expression = parseConditionalExpression();
|
| if (_currentToken.type.isAssignmentOperator) {
|
| Token operator = andAdvance;
|
| ensureAssignable(expression);
|
| @@ -2430,2471 +3257,2691 @@ class Parser {
|
| }
|
|
|
| /**
|
| - * Parse a class extends clause.
|
| - *
|
| - * <pre>
|
| - * classExtendsClause ::=
|
| - * 'extends' type
|
| - * </pre>
|
| - *
|
| - * @return the class extends clause that was parsed
|
| - */
|
| - ExtendsClause parseExtendsClause() {
|
| - Token keyword = expect(Keyword.EXTENDS);
|
| - TypeName superclass = parseTypeName();
|
| - return new ExtendsClause.full(keyword, superclass);
|
| - }
|
| -
|
| - /**
|
| - * Parse the 'final', 'const', 'var' or type preceding a variable declaration.
|
| + * Parse a class declaration.
|
| *
|
| * <pre>
|
| - * finalConstVarOrType ::=
|
| - * | 'final' type?
|
| - * | 'const' type?
|
| - * | 'var'
|
| - * | type
|
| + * classDeclaration ::=
|
| + * metadata 'abstract'? 'class' name typeParameterList? (extendsClause withClause?)? implementsClause? '{' classMembers '}' |
|
| + * metadata 'abstract'? 'class' mixinApplicationClass
|
| * </pre>
|
| *
|
| - * @param optional `true` if the keyword and type are optional
|
| - * @return the 'final', 'const', 'var' or type that was parsed
|
| + * @param commentAndMetadata the metadata to be associated with the member
|
| + * @param abstractKeyword the token for the keyword 'abstract', or `null` if the keyword was
|
| + * not given
|
| + * @return the class declaration that was parsed
|
| */
|
| - FinalConstVarOrType parseFinalConstVarOrType(bool optional) {
|
| - Token keyword = null;
|
| - TypeName type = null;
|
| - if (matches(Keyword.FINAL) || matches(Keyword.CONST)) {
|
| - keyword = andAdvance;
|
| - if (isTypedIdentifier(_currentToken)) {
|
| - type = parseTypeName();
|
| + CompilationUnitMember parseClassDeclaration(CommentAndMetadata commentAndMetadata, Token abstractKeyword) {
|
| + Token keyword = expect(Keyword.CLASS);
|
| + if (matchesIdentifier()) {
|
| + Token next = peek();
|
| + if (matches4(next, TokenType.LT)) {
|
| + next = skipTypeParameterList(next);
|
| + if (next != null && matches4(next, TokenType.EQ)) {
|
| + return parseClassTypeAlias(commentAndMetadata, keyword);
|
| + }
|
| + } else if (matches4(next, TokenType.EQ)) {
|
| + return parseClassTypeAlias(commentAndMetadata, keyword);
|
| }
|
| - } else if (matches(Keyword.VAR)) {
|
| - keyword = andAdvance;
|
| - } else {
|
| - if (isTypedIdentifier(_currentToken)) {
|
| - type = parseReturnType();
|
| - } else if (!optional) {
|
| - reportError8(ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE, []);
|
| + }
|
| + SimpleIdentifier name = parseSimpleIdentifier();
|
| + String className = name.name;
|
| + TypeParameterList typeParameters = null;
|
| + if (matches5(TokenType.LT)) {
|
| + typeParameters = parseTypeParameterList();
|
| + }
|
| + ExtendsClause extendsClause = null;
|
| + WithClause withClause = null;
|
| + ImplementsClause implementsClause = null;
|
| + bool foundClause = true;
|
| + while (foundClause) {
|
| + if (matches(Keyword.EXTENDS)) {
|
| + if (extendsClause == null) {
|
| + extendsClause = parseExtendsClause();
|
| + if (withClause != null) {
|
| + reportError9(ParserErrorCode.WITH_BEFORE_EXTENDS, withClause.withKeyword, []);
|
| + } else if (implementsClause != null) {
|
| + reportError9(ParserErrorCode.IMPLEMENTS_BEFORE_EXTENDS, implementsClause.keyword, []);
|
| + }
|
| + } else {
|
| + reportError9(ParserErrorCode.MULTIPLE_EXTENDS_CLAUSES, extendsClause.keyword, []);
|
| + parseExtendsClause();
|
| + }
|
| + } else if (matches(Keyword.WITH)) {
|
| + if (withClause == null) {
|
| + withClause = parseWithClause();
|
| + if (implementsClause != null) {
|
| + reportError9(ParserErrorCode.IMPLEMENTS_BEFORE_WITH, implementsClause.keyword, []);
|
| + }
|
| + } else {
|
| + reportError9(ParserErrorCode.MULTIPLE_WITH_CLAUSES, withClause.withKeyword, []);
|
| + parseWithClause();
|
| + }
|
| + } else if (matches(Keyword.IMPLEMENTS)) {
|
| + if (implementsClause == null) {
|
| + implementsClause = parseImplementsClause();
|
| + } else {
|
| + reportError9(ParserErrorCode.MULTIPLE_IMPLEMENTS_CLAUSES, implementsClause.keyword, []);
|
| + parseImplementsClause();
|
| + }
|
| + } else {
|
| + foundClause = false;
|
| }
|
| }
|
| - return new FinalConstVarOrType(keyword, type);
|
| + if (withClause != null && extendsClause == null) {
|
| + reportError9(ParserErrorCode.WITH_WITHOUT_EXTENDS, withClause.withKeyword, []);
|
| + }
|
| + NativeClause nativeClause = null;
|
| + if (matches2(_NATIVE) && matches4(peek(), TokenType.STRING)) {
|
| + nativeClause = parseNativeClause();
|
| + }
|
| + Token leftBracket = null;
|
| + List<ClassMember> members = null;
|
| + Token rightBracket = null;
|
| + if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| + leftBracket = expect2(TokenType.OPEN_CURLY_BRACKET);
|
| + members = parseClassMembers(className, getEndToken(leftBracket));
|
| + rightBracket = expect2(TokenType.CLOSE_CURLY_BRACKET);
|
| + } else {
|
| + leftBracket = createSyntheticToken2(TokenType.OPEN_CURLY_BRACKET);
|
| + rightBracket = createSyntheticToken2(TokenType.CLOSE_CURLY_BRACKET);
|
| + reportError8(ParserErrorCode.MISSING_CLASS_BODY, []);
|
| + }
|
| + ClassDeclaration classDeclaration = new ClassDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, abstractKeyword, keyword, name, typeParameters, extendsClause, withClause, implementsClause, leftBracket, members, rightBracket);
|
| + classDeclaration.nativeClause = nativeClause;
|
| + return classDeclaration;
|
| }
|
|
|
| /**
|
| - * Parse a formal parameter. At most one of `isOptional` and `isNamed` can be
|
| - * `true`.
|
| + * Parse a list of class members.
|
| *
|
| * <pre>
|
| - * defaultFormalParameter ::=
|
| - * normalFormalParameter ('=' expression)?
|
| - *
|
| - * defaultNamedParameter ::=
|
| - * normalFormalParameter (':' expression)?
|
| + * classMembers ::=
|
| + * (metadata memberDefinition)*
|
| * </pre>
|
| *
|
| - * @param kind the kind of parameter being expected based on the presence or absence of group
|
| - * delimiters
|
| - * @return the formal parameter that was parsed
|
| + * @param className the name of the class whose members are being parsed
|
| + * @param closingBracket the closing bracket for the class, or `null` if the closing bracket
|
| + * is missing
|
| + * @return the list of class members that were parsed
|
| */
|
| - FormalParameter parseFormalParameter(ParameterKind kind) {
|
| - NormalFormalParameter parameter = parseNormalFormalParameter();
|
| - if (matches5(TokenType.EQ)) {
|
| - Token seperator = andAdvance;
|
| - Expression defaultValue = parseExpression2();
|
| - if (identical(kind, ParameterKind.NAMED)) {
|
| - reportError9(ParserErrorCode.WRONG_SEPARATOR_FOR_NAMED_PARAMETER, seperator, []);
|
| - } else if (identical(kind, ParameterKind.REQUIRED)) {
|
| - reportError(ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP, parameter, []);
|
| + List<ClassMember> parseClassMembers(String className, Token closingBracket) {
|
| + List<ClassMember> members = new List<ClassMember>();
|
| + Token memberStart = _currentToken;
|
| + while (!matches5(TokenType.EOF) && !matches5(TokenType.CLOSE_CURLY_BRACKET) && (closingBracket != null || (!matches(Keyword.CLASS) && !matches(Keyword.TYPEDEF)))) {
|
| + if (matches5(TokenType.SEMICOLON)) {
|
| + reportError9(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
|
| + advance();
|
| + } else {
|
| + ClassMember member = parseClassMember(className);
|
| + if (member != null) {
|
| + members.add(member);
|
| + }
|
| }
|
| - return new DefaultFormalParameter.full(parameter, kind, seperator, defaultValue);
|
| - } else if (matches5(TokenType.COLON)) {
|
| - Token seperator = andAdvance;
|
| - Expression defaultValue = parseExpression2();
|
| - if (identical(kind, ParameterKind.POSITIONAL)) {
|
| - reportError9(ParserErrorCode.WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER, seperator, []);
|
| - } else if (identical(kind, ParameterKind.REQUIRED)) {
|
| - reportError(ParserErrorCode.NAMED_PARAMETER_OUTSIDE_GROUP, parameter, []);
|
| + if (identical(_currentToken, memberStart)) {
|
| + reportError9(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
|
| + advance();
|
| }
|
| - return new DefaultFormalParameter.full(parameter, kind, seperator, defaultValue);
|
| - } else if (kind != ParameterKind.REQUIRED) {
|
| - return new DefaultFormalParameter.full(parameter, kind, null, null);
|
| + memberStart = _currentToken;
|
| }
|
| - return parameter;
|
| + return members;
|
| }
|
|
|
| /**
|
| - * Parse a list of formal parameters.
|
| + * Parse a class type alias.
|
| *
|
| * <pre>
|
| - * formalParameterList ::=
|
| - * '(' ')'
|
| - * | '(' normalFormalParameters (',' optionalFormalParameters)? ')'
|
| - * | '(' optionalFormalParameters ')'
|
| - *
|
| - * normalFormalParameters ::=
|
| - * normalFormalParameter (',' normalFormalParameter)*
|
| + * classTypeAlias ::=
|
| + * identifier typeParameters? '=' 'abstract'? mixinApplication
|
| *
|
| - * optionalFormalParameters ::=
|
| - * optionalPositionalFormalParameters
|
| - * | namedFormalParameters
|
| + * mixinApplication ::=
|
| + * type withClause implementsClause? ';'
|
| + * </pre>
|
| *
|
| - * optionalPositionalFormalParameters ::=
|
| - * '[' defaultFormalParameter (',' defaultFormalParameter)* ']'
|
| - *
|
| - * namedFormalParameters ::=
|
| - * '{' defaultNamedParameter (',' defaultNamedParameter)* '}'
|
| - * </pre>
|
| - *
|
| - * @return the formal parameters that were parsed
|
| + * @param commentAndMetadata the metadata to be associated with the member
|
| + * @param keyword the token representing the 'typedef' keyword
|
| + * @return the class type alias that was parsed
|
| */
|
| - FormalParameterList parseFormalParameterList() {
|
| - Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| - if (matches5(TokenType.CLOSE_PAREN)) {
|
| - return new FormalParameterList.full(leftParenthesis, null, null, null, andAdvance);
|
| + ClassTypeAlias parseClassTypeAlias(CommentAndMetadata commentAndMetadata, Token keyword) {
|
| + SimpleIdentifier className = parseSimpleIdentifier();
|
| + TypeParameterList typeParameters = null;
|
| + if (matches5(TokenType.LT)) {
|
| + typeParameters = parseTypeParameterList();
|
| }
|
| - List<FormalParameter> parameters = new List<FormalParameter>();
|
| - List<FormalParameter> normalParameters = new List<FormalParameter>();
|
| - List<FormalParameter> positionalParameters = new List<FormalParameter>();
|
| - List<FormalParameter> namedParameters = new List<FormalParameter>();
|
| - List<FormalParameter> currentParameters = normalParameters;
|
| - Token leftSquareBracket = null;
|
| - Token rightSquareBracket = null;
|
| - Token leftCurlyBracket = null;
|
| - Token rightCurlyBracket = null;
|
| - ParameterKind kind = ParameterKind.REQUIRED;
|
| - bool firstParameter = true;
|
| - bool reportedMuliplePositionalGroups = false;
|
| - bool reportedMulipleNamedGroups = false;
|
| - bool reportedMixedGroups = false;
|
| - bool wasOptionalParameter = false;
|
| - Token initialToken = null;
|
| - do {
|
| - if (firstParameter) {
|
| - firstParameter = false;
|
| - } else if (!optional(TokenType.COMMA)) {
|
| - if (getEndToken(leftParenthesis) != null) {
|
| - reportError8(ParserErrorCode.EXPECTED_TOKEN, [TokenType.COMMA.lexeme]);
|
| - } else {
|
| - reportError9(ParserErrorCode.MISSING_CLOSING_PARENTHESIS, _currentToken.previous, []);
|
| - break;
|
| - }
|
| - }
|
| - initialToken = _currentToken;
|
| - if (matches5(TokenType.OPEN_SQUARE_BRACKET)) {
|
| - wasOptionalParameter = true;
|
| - if (leftSquareBracket != null && !reportedMuliplePositionalGroups) {
|
| - reportError8(ParserErrorCode.MULTIPLE_POSITIONAL_PARAMETER_GROUPS, []);
|
| - reportedMuliplePositionalGroups = true;
|
| - }
|
| - if (leftCurlyBracket != null && !reportedMixedGroups) {
|
| - reportError8(ParserErrorCode.MIXED_PARAMETER_GROUPS, []);
|
| - reportedMixedGroups = true;
|
| - }
|
| - leftSquareBracket = andAdvance;
|
| - currentParameters = positionalParameters;
|
| - kind = ParameterKind.POSITIONAL;
|
| - } else if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| - wasOptionalParameter = true;
|
| - if (leftCurlyBracket != null && !reportedMulipleNamedGroups) {
|
| - reportError8(ParserErrorCode.MULTIPLE_NAMED_PARAMETER_GROUPS, []);
|
| - reportedMulipleNamedGroups = true;
|
| - }
|
| - if (leftSquareBracket != null && !reportedMixedGroups) {
|
| - reportError8(ParserErrorCode.MIXED_PARAMETER_GROUPS, []);
|
| - reportedMixedGroups = true;
|
| - }
|
| - leftCurlyBracket = andAdvance;
|
| - currentParameters = namedParameters;
|
| - kind = ParameterKind.NAMED;
|
| - }
|
| - FormalParameter parameter = parseFormalParameter(kind);
|
| - parameters.add(parameter);
|
| - currentParameters.add(parameter);
|
| - if (identical(kind, ParameterKind.REQUIRED) && wasOptionalParameter) {
|
| - reportError(ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS, parameter, []);
|
| - }
|
| - if (matches5(TokenType.CLOSE_SQUARE_BRACKET)) {
|
| - rightSquareBracket = andAdvance;
|
| - currentParameters = normalParameters;
|
| - if (leftSquareBracket == null) {
|
| - if (leftCurlyBracket != null) {
|
| - reportError8(ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
|
| - rightCurlyBracket = rightSquareBracket;
|
| - rightSquareBracket = null;
|
| - } else {
|
| - reportError8(ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP, ["["]);
|
| - }
|
| - }
|
| - kind = ParameterKind.REQUIRED;
|
| - } else if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| - rightCurlyBracket = andAdvance;
|
| - currentParameters = normalParameters;
|
| - if (leftCurlyBracket == null) {
|
| - if (leftSquareBracket != null) {
|
| - reportError8(ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
|
| - rightSquareBracket = rightCurlyBracket;
|
| - rightCurlyBracket = null;
|
| - } else {
|
| - reportError8(ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP, ["{"]);
|
| - }
|
| - }
|
| - kind = ParameterKind.REQUIRED;
|
| - }
|
| - } while (!matches5(TokenType.CLOSE_PAREN) && initialToken != _currentToken);
|
| - Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| - if (leftSquareBracket != null && rightSquareBracket == null) {
|
| - reportError8(ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
|
| + Token equals = expect2(TokenType.EQ);
|
| + Token abstractKeyword = null;
|
| + if (matches(Keyword.ABSTRACT)) {
|
| + abstractKeyword = andAdvance;
|
| }
|
| - if (leftCurlyBracket != null && rightCurlyBracket == null) {
|
| - reportError8(ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
|
| + TypeName superclass = parseTypeName();
|
| + WithClause withClause = null;
|
| + if (matches(Keyword.WITH)) {
|
| + withClause = parseWithClause();
|
| }
|
| - if (leftSquareBracket == null) {
|
| - leftSquareBracket = leftCurlyBracket;
|
| + ImplementsClause implementsClause = null;
|
| + if (matches(Keyword.IMPLEMENTS)) {
|
| + implementsClause = parseImplementsClause();
|
| }
|
| - if (rightSquareBracket == null) {
|
| - rightSquareBracket = rightCurlyBracket;
|
| + Token semicolon;
|
| + if (matches5(TokenType.SEMICOLON)) {
|
| + semicolon = andAdvance;
|
| + } else {
|
| + if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| + reportError8(ParserErrorCode.EXPECTED_TOKEN, [TokenType.SEMICOLON.lexeme]);
|
| + Token leftBracket = andAdvance;
|
| + parseClassMembers(className.name, getEndToken(leftBracket));
|
| + expect2(TokenType.CLOSE_CURLY_BRACKET);
|
| + } else {
|
| + reportError9(ParserErrorCode.EXPECTED_TOKEN, _currentToken.previous, [TokenType.SEMICOLON.lexeme]);
|
| + }
|
| + semicolon = createSyntheticToken2(TokenType.SEMICOLON);
|
| }
|
| - return new FormalParameterList.full(leftParenthesis, parameters, leftSquareBracket, rightSquareBracket, rightParenthesis);
|
| + return new ClassTypeAlias.full(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, className, typeParameters, equals, abstractKeyword, superclass, withClause, implementsClause, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse a for statement.
|
| + * Parse a list of combinators in a directive.
|
| *
|
| * <pre>
|
| - * forStatement ::=
|
| - * 'for' '(' forLoopParts ')' statement
|
| - *
|
| - * forLoopParts ::=
|
| - * forInitializerStatement expression? ';' expressionList?
|
| - * | declaredIdentifier 'in' expression
|
| - * | identifier 'in' expression
|
| - *
|
| - * forInitializerStatement ::=
|
| - * localVariableDeclaration ';'
|
| - * | expression? ';'
|
| + * combinator ::=
|
| + * 'show' identifier (',' identifier)*
|
| + * | 'hide' identifier (',' identifier)*
|
| * </pre>
|
| *
|
| - * @return the for statement that was parsed
|
| + * @return the combinators that were parsed
|
| */
|
| - Statement parseForStatement() {
|
| - bool wasInLoop = _inLoop;
|
| - _inLoop = true;
|
| - try {
|
| - Token forKeyword = expect(Keyword.FOR);
|
| - Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| - VariableDeclarationList variableList = null;
|
| - Expression initialization = null;
|
| - if (!matches5(TokenType.SEMICOLON)) {
|
| - CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| - if (matchesIdentifier() && matches3(peek(), Keyword.IN)) {
|
| - List<VariableDeclaration> variables = new List<VariableDeclaration>();
|
| - SimpleIdentifier variableName = parseSimpleIdentifier();
|
| - variables.add(new VariableDeclaration.full(null, null, variableName, null, null));
|
| - variableList = new VariableDeclarationList.full(commentAndMetadata.comment, commentAndMetadata.metadata, null, null, variables);
|
| - } else if (isInitializedVariableDeclaration()) {
|
| - variableList = parseVariableDeclarationList(commentAndMetadata);
|
| - } else {
|
| - initialization = parseExpression2();
|
| - }
|
| - if (matches(Keyword.IN)) {
|
| - DeclaredIdentifier loopVariable = null;
|
| - SimpleIdentifier identifier = null;
|
| - if (variableList == null) {
|
| - reportError8(ParserErrorCode.MISSING_VARIABLE_IN_FOR_EACH, []);
|
| - } else {
|
| - NodeList<VariableDeclaration> variables = variableList.variables;
|
| - if (variables.length > 1) {
|
| - reportError8(ParserErrorCode.MULTIPLE_VARIABLES_IN_FOR_EACH, [variables.length.toString()]);
|
| - }
|
| - VariableDeclaration variable = variables[0];
|
| - if (variable.initializer != null) {
|
| - reportError8(ParserErrorCode.INITIALIZED_VARIABLE_IN_FOR_EACH, []);
|
| - }
|
| - Token keyword = variableList.keyword;
|
| - TypeName type = variableList.type;
|
| - if (keyword != null || type != null) {
|
| - loopVariable = new DeclaredIdentifier.full(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, type, variable.name);
|
| - } else {
|
| - if (!commentAndMetadata.metadata.isEmpty) {
|
| - }
|
| - identifier = variable.name;
|
| - }
|
| - }
|
| - Token inKeyword = expect(Keyword.IN);
|
| - Expression iterator = parseExpression2();
|
| - Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| - Statement body = parseStatement2();
|
| - if (loopVariable == null) {
|
| - return new ForEachStatement.con2_full(forKeyword, leftParenthesis, identifier, inKeyword, iterator, rightParenthesis, body);
|
| - }
|
| - return new ForEachStatement.con1_full(forKeyword, leftParenthesis, loopVariable, inKeyword, iterator, rightParenthesis, body);
|
| - }
|
| - }
|
| - Token leftSeparator = expect2(TokenType.SEMICOLON);
|
| - Expression condition = null;
|
| - if (!matches5(TokenType.SEMICOLON)) {
|
| - condition = parseExpression2();
|
| - }
|
| - Token rightSeparator = expect2(TokenType.SEMICOLON);
|
| - List<Expression> updaters = null;
|
| - if (!matches5(TokenType.CLOSE_PAREN)) {
|
| - updaters = parseExpressionList();
|
| + List<Combinator> parseCombinators() {
|
| + List<Combinator> combinators = new List<Combinator>();
|
| + while (matches2(_SHOW) || matches2(_HIDE)) {
|
| + Token keyword = expect2(TokenType.IDENTIFIER);
|
| + if (keyword.lexeme == _SHOW) {
|
| + List<SimpleIdentifier> shownNames = parseIdentifierList();
|
| + combinators.add(new ShowCombinator.full(keyword, shownNames));
|
| + } else {
|
| + List<SimpleIdentifier> hiddenNames = parseIdentifierList();
|
| + combinators.add(new HideCombinator.full(keyword, hiddenNames));
|
| }
|
| - Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| - Statement body = parseStatement2();
|
| - return new ForStatement.full(forKeyword, leftParenthesis, variableList, initialization, leftSeparator, condition, rightSeparator, updaters, rightParenthesis, body);
|
| - } finally {
|
| - _inLoop = wasInLoop;
|
| }
|
| + return combinators;
|
| }
|
|
|
| /**
|
| - * Parse a function body.
|
| + * Parse the documentation comment and metadata preceding a declaration. This method allows any
|
| + * number of documentation comments to occur before, after or between the metadata, but only
|
| + * returns the last (right-most) documentation comment that is found.
|
| *
|
| * <pre>
|
| - * functionBody ::=
|
| - * '=>' expression ';'
|
| - * | block
|
| - *
|
| - * functionExpressionBody ::=
|
| - * '=>' expression
|
| - * | block
|
| + * metadata ::=
|
| + * annotation*
|
| * </pre>
|
| *
|
| - * @param mayBeEmpty `true` if the function body is allowed to be empty
|
| - * @param emptyErrorCode the error code to report if function body expecte, but not found
|
| - * @param inExpression `true` if the function body is being parsed as part of an expression
|
| - * and therefore does not have a terminating semicolon
|
| - * @return the function body that was parsed
|
| + * @return the documentation comment and metadata that were parsed
|
| */
|
| - FunctionBody parseFunctionBody(bool mayBeEmpty, ParserErrorCode emptyErrorCode, bool inExpression) {
|
| - bool wasInLoop = _inLoop;
|
| - bool wasInSwitch = _inSwitch;
|
| - _inLoop = false;
|
| - _inSwitch = false;
|
| - try {
|
| - if (matches5(TokenType.SEMICOLON)) {
|
| - if (!mayBeEmpty) {
|
| - reportError8(emptyErrorCode, []);
|
| - }
|
| - return new EmptyFunctionBody.full(andAdvance);
|
| - } else if (matches5(TokenType.FUNCTION)) {
|
| - Token functionDefinition = andAdvance;
|
| - Expression expression = parseExpression2();
|
| - Token semicolon = null;
|
| - if (!inExpression) {
|
| - semicolon = expect2(TokenType.SEMICOLON);
|
| - }
|
| - return new ExpressionFunctionBody.full(functionDefinition, expression, semicolon);
|
| - } else if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| - return new BlockFunctionBody.full(parseBlock());
|
| - } else if (matches2(_NATIVE)) {
|
| - Token nativeToken = andAdvance;
|
| - StringLiteral stringLiteral = null;
|
| - if (matches5(TokenType.STRING)) {
|
| - stringLiteral = parseStringLiteral();
|
| - }
|
| - return new NativeFunctionBody.full(nativeToken, stringLiteral, expect2(TokenType.SEMICOLON));
|
| - } else {
|
| - reportError8(emptyErrorCode, []);
|
| - return new EmptyFunctionBody.full(createSyntheticToken2(TokenType.SEMICOLON));
|
| + CommentAndMetadata parseCommentAndMetadata() {
|
| + Comment comment = parseDocumentationComment();
|
| + List<Annotation> metadata = new List<Annotation>();
|
| + while (matches5(TokenType.AT)) {
|
| + metadata.add(parseAnnotation());
|
| + Comment optionalComment = parseDocumentationComment();
|
| + if (optionalComment != null) {
|
| + comment = optionalComment;
|
| }
|
| - } finally {
|
| - _inLoop = wasInLoop;
|
| - _inSwitch = wasInSwitch;
|
| }
|
| + return new CommentAndMetadata(comment, metadata);
|
| }
|
|
|
| /**
|
| - * Parse a function declaration.
|
| + * Parse a comment reference from the source between square brackets.
|
| *
|
| * <pre>
|
| - * functionDeclaration ::=
|
| - * functionSignature functionBody
|
| - * | returnType? getOrSet identifier formalParameterList functionBody
|
| + * commentReference ::=
|
| + * 'new'? prefixedIdentifier
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| - * declaration
|
| - * @param externalKeyword the 'external' keyword, or `null` if the function is not external
|
| - * @param returnType the return type, or `null` if there is no return type
|
| - * @param isStatement `true` if the function declaration is being parsed as a statement
|
| - * @return the function declaration that was parsed
|
| + * @param referenceSource the source occurring between the square brackets within a documentation
|
| + * comment
|
| + * @param sourceOffset the offset of the first character of the reference source
|
| + * @return the comment reference that was parsed, or `null` if no reference could be found
|
| */
|
| - FunctionDeclaration parseFunctionDeclaration(CommentAndMetadata commentAndMetadata, Token externalKeyword, TypeName returnType) {
|
| - Token keyword = null;
|
| - bool isGetter = false;
|
| - if (matches(Keyword.GET) && !matches4(peek(), TokenType.OPEN_PAREN)) {
|
| - keyword = andAdvance;
|
| - isGetter = true;
|
| - } else if (matches(Keyword.SET) && !matches4(peek(), TokenType.OPEN_PAREN)) {
|
| - keyword = andAdvance;
|
| + CommentReference parseCommentReference(String referenceSource, int sourceOffset) {
|
| + if (referenceSource.length == 0) {
|
| + return null;
|
| }
|
| - SimpleIdentifier name = parseSimpleIdentifier();
|
| - FormalParameterList parameters = null;
|
| - if (!isGetter) {
|
| - if (matches5(TokenType.OPEN_PAREN)) {
|
| - parameters = parseFormalParameterList();
|
| - validateFormalParameterList(parameters);
|
| - } else {
|
| - reportError8(ParserErrorCode.MISSING_FUNCTION_PARAMETERS, []);
|
| + try {
|
| + List<bool> errorFound = [false];
|
| + AnalysisErrorListener listener = new AnalysisErrorListener_15(errorFound);
|
| + Scanner scanner = new Scanner(null, new SubSequenceReader(new CharSequence(referenceSource), sourceOffset), listener);
|
| + scanner.setSourceStart(1, 1);
|
| + Token firstToken = scanner.tokenize();
|
| + if (errorFound[0]) {
|
| + return null;
|
| }
|
| - } else if (matches5(TokenType.OPEN_PAREN)) {
|
| - reportError8(ParserErrorCode.GETTER_WITH_PARAMETERS, []);
|
| - parseFormalParameterList();
|
| - }
|
| - FunctionBody body;
|
| - if (externalKeyword == null) {
|
| - body = parseFunctionBody(false, ParserErrorCode.MISSING_FUNCTION_BODY, false);
|
| - } else {
|
| - body = new EmptyFunctionBody.full(expect2(TokenType.SEMICOLON));
|
| + Token newKeyword = null;
|
| + if (matches3(firstToken, Keyword.NEW)) {
|
| + newKeyword = firstToken;
|
| + firstToken = firstToken.next;
|
| + }
|
| + if (matchesIdentifier2(firstToken)) {
|
| + Token secondToken = firstToken.next;
|
| + Token thirdToken = secondToken.next;
|
| + Token nextToken;
|
| + Identifier identifier;
|
| + if (matches4(secondToken, TokenType.PERIOD) && matchesIdentifier2(thirdToken)) {
|
| + identifier = new PrefixedIdentifier.full(new SimpleIdentifier.full(firstToken), secondToken, new SimpleIdentifier.full(thirdToken));
|
| + nextToken = thirdToken.next;
|
| + } else {
|
| + identifier = new SimpleIdentifier.full(firstToken);
|
| + nextToken = firstToken.next;
|
| + }
|
| + if (nextToken.type != TokenType.EOF) {
|
| + return null;
|
| + }
|
| + return new CommentReference.full(newKeyword, identifier);
|
| + } else if (matches3(firstToken, Keyword.THIS) || matches3(firstToken, Keyword.NULL) || matches3(firstToken, Keyword.TRUE) || matches3(firstToken, Keyword.FALSE)) {
|
| + return null;
|
| + }
|
| + } on JavaException catch (exception) {
|
| }
|
| - return new FunctionDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, returnType, keyword, name, new FunctionExpression.full(parameters, body));
|
| + return null;
|
| }
|
|
|
| /**
|
| - * Parse a function declaration statement.
|
| + * Parse all of the comment references occurring in the given array of documentation comments.
|
| *
|
| * <pre>
|
| - * functionDeclarationStatement ::=
|
| - * functionSignature functionBody
|
| - * </pre>
|
| - *
|
| - * @return the function declaration statement that was parsed
|
| - */
|
| - Statement parseFunctionDeclarationStatement() {
|
| - Modifiers modifiers = parseModifiers();
|
| - validateModifiersForFunctionDeclarationStatement(modifiers);
|
| - return parseFunctionDeclarationStatement2(parseCommentAndMetadata(), parseOptionalReturnType());
|
| - }
|
| -
|
| - /**
|
| - * Parse a function declaration statement.
|
| + * commentReference ::=
|
| + * '[' 'new'? qualified ']' libraryReference?
|
| *
|
| - * <pre>
|
| - * functionDeclarationStatement ::=
|
| - * functionSignature functionBody
|
| + * libraryReference ::=
|
| + * '(' stringLiteral ')'
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| - * declaration
|
| - * @param returnType the return type, or `null` if there is no return type
|
| - * @return the function declaration statement that was parsed
|
| + * @param tokens the comment tokens representing the documentation comments to be parsed
|
| + * @return the comment references that were parsed
|
| */
|
| - Statement parseFunctionDeclarationStatement2(CommentAndMetadata commentAndMetadata, TypeName returnType) {
|
| - FunctionDeclaration declaration = parseFunctionDeclaration(commentAndMetadata, null, returnType);
|
| - Token propertyKeyword = declaration.propertyKeyword;
|
| - if (propertyKeyword != null) {
|
| - if (identical(((propertyKeyword as KeywordToken)).keyword, Keyword.GET)) {
|
| - reportError9(ParserErrorCode.GETTER_IN_FUNCTION, propertyKeyword, []);
|
| - } else {
|
| - reportError9(ParserErrorCode.SETTER_IN_FUNCTION, propertyKeyword, []);
|
| + List<CommentReference> parseCommentReferences(List<Token> tokens) {
|
| + List<CommentReference> references = new List<CommentReference>();
|
| + for (Token token in tokens) {
|
| + String comment = token.lexeme;
|
| + int length = comment.length;
|
| + List<List<int>> codeBlockRanges = getCodeBlockRanges(comment);
|
| + int leftIndex = comment.indexOf('[');
|
| + while (leftIndex >= 0 && leftIndex + 1 < length) {
|
| + List<int> range = findRange(codeBlockRanges, leftIndex);
|
| + if (range == null) {
|
| + int rightIndex = JavaString.indexOf(comment, ']', leftIndex);
|
| + if (rightIndex >= 0) {
|
| + int firstChar = comment.codeUnitAt(leftIndex + 1);
|
| + if (firstChar != 0x27 && firstChar != 0x22) {
|
| + if (isLinkText(comment, rightIndex)) {
|
| + } else {
|
| + CommentReference reference = parseCommentReference(comment.substring(leftIndex + 1, rightIndex), token.offset + leftIndex + 1);
|
| + if (reference != null) {
|
| + references.add(reference);
|
| + }
|
| + }
|
| + }
|
| + } else {
|
| + rightIndex = leftIndex + 1;
|
| + }
|
| + leftIndex = JavaString.indexOf(comment, '[', rightIndex);
|
| + } else {
|
| + leftIndex = JavaString.indexOf(comment, '[', range[1] + 1);
|
| + }
|
| }
|
| }
|
| - return new FunctionDeclarationStatement.full(declaration);
|
| - }
|
| -
|
| - /**
|
| - * Parse a function expression.
|
| - *
|
| - * <pre>
|
| - * functionExpression ::=
|
| - * formalParameterList functionExpressionBody
|
| - * </pre>
|
| - *
|
| - * @return the function expression that was parsed
|
| - */
|
| - FunctionExpression parseFunctionExpression() {
|
| - FormalParameterList parameters = parseFormalParameterList();
|
| - validateFormalParameterList(parameters);
|
| - FunctionBody body = parseFunctionBody(false, ParserErrorCode.MISSING_FUNCTION_BODY, true);
|
| - return new FunctionExpression.full(parameters, body);
|
| + return references;
|
| }
|
|
|
| /**
|
| - * Parse a function type alias.
|
| + * Parse a compilation unit member.
|
| *
|
| * <pre>
|
| - * functionTypeAlias ::=
|
| - * functionPrefix typeParameterList? formalParameterList ';'
|
| - *
|
| - * functionPrefix ::=
|
| - * returnType? name
|
| + * compilationUnitMember ::=
|
| + * classDefinition
|
| + * | functionTypeAlias
|
| + * | external functionSignature
|
| + * | external getterSignature
|
| + * | external setterSignature
|
| + * | functionSignature functionBody
|
| + * | returnType? getOrSet identifier formalParameterList functionBody
|
| + * | (final | const) type? staticFinalDeclarationList ';'
|
| + * | variableDeclaration ';'
|
| * </pre>
|
| *
|
| * @param commentAndMetadata the metadata to be associated with the member
|
| - * @param keyword the token representing the 'typedef' keyword
|
| - * @return the function type alias that was parsed
|
| + * @return the compilation unit member that was parsed, or `null` if what was parsed could
|
| + * not be represented as a compilation unit member
|
| */
|
| - FunctionTypeAlias parseFunctionTypeAlias(CommentAndMetadata commentAndMetadata, Token keyword) {
|
| - TypeName returnType = null;
|
| - if (hasReturnTypeInTypeAlias()) {
|
| - returnType = parseReturnType();
|
| - }
|
| - SimpleIdentifier name = parseSimpleIdentifier();
|
| - TypeParameterList typeParameters = null;
|
| - if (matches5(TokenType.LT)) {
|
| - typeParameters = parseTypeParameterList();
|
| - }
|
| - if (matches5(TokenType.SEMICOLON) || matches5(TokenType.EOF)) {
|
| - reportError8(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS, []);
|
| - FormalParameterList parameters = new FormalParameterList.full(createSyntheticToken2(TokenType.OPEN_PAREN), null, null, null, createSyntheticToken2(TokenType.CLOSE_PAREN));
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new FunctionTypeAlias.full(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, returnType, name, typeParameters, parameters, semicolon);
|
| - } else if (!matches5(TokenType.OPEN_PAREN)) {
|
| - reportError8(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS, []);
|
| - return new FunctionTypeAlias.full(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, returnType, name, typeParameters, new FormalParameterList.full(createSyntheticToken2(TokenType.OPEN_PAREN), null, null, null, createSyntheticToken2(TokenType.CLOSE_PAREN)), createSyntheticToken2(TokenType.SEMICOLON));
|
| + CompilationUnitMember parseCompilationUnitMember(CommentAndMetadata commentAndMetadata) {
|
| + Modifiers modifiers = parseModifiers();
|
| + if (matches(Keyword.CLASS)) {
|
| + return parseClassDeclaration(commentAndMetadata, validateModifiersForClass(modifiers));
|
| + } else if (matches(Keyword.TYPEDEF) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| + validateModifiersForTypedef(modifiers);
|
| + return parseTypeAlias(commentAndMetadata);
|
| }
|
| - FormalParameterList parameters = parseFormalParameterList();
|
| - validateFormalParameterList(parameters);
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new FunctionTypeAlias.full(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, returnType, name, typeParameters, parameters, semicolon);
|
| + if (matches(Keyword.VOID)) {
|
| + TypeName returnType = parseReturnType();
|
| + if ((matches(Keyword.GET) || matches(Keyword.SET)) && matchesIdentifier2(peek())) {
|
| + validateModifiersForTopLevelFunction(modifiers);
|
| + return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, null);
|
| + } else if (matches(Keyword.OPERATOR) && isOperator(peek())) {
|
| + reportError9(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken, []);
|
| + return convertToFunctionDeclaration(parseOperator(commentAndMetadata, modifiers.externalKeyword, returnType));
|
| + } else if (matchesIdentifier() && matchesAny(peek(), [
|
| + TokenType.OPEN_PAREN,
|
| + TokenType.OPEN_CURLY_BRACKET,
|
| + TokenType.FUNCTION])) {
|
| + validateModifiersForTopLevelFunction(modifiers);
|
| + return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, returnType);
|
| + } else {
|
| + if (matchesIdentifier()) {
|
| + if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
|
| + reportError(ParserErrorCode.VOID_VARIABLE, returnType, []);
|
| + return new TopLevelVariableDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, parseVariableDeclarationList2(null, validateModifiersForTopLevelVariable(modifiers), null), expect2(TokenType.SEMICOLON));
|
| + }
|
| + }
|
| + reportError9(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken, []);
|
| + return null;
|
| + }
|
| + } else if ((matches(Keyword.GET) || matches(Keyword.SET)) && matchesIdentifier2(peek())) {
|
| + validateModifiersForTopLevelFunction(modifiers);
|
| + return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, null);
|
| + } else if (matches(Keyword.OPERATOR) && isOperator(peek())) {
|
| + reportError9(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken, []);
|
| + return convertToFunctionDeclaration(parseOperator(commentAndMetadata, modifiers.externalKeyword, null));
|
| + } else if (!matchesIdentifier()) {
|
| + reportError9(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken, []);
|
| + return null;
|
| + } else if (matches4(peek(), TokenType.OPEN_PAREN)) {
|
| + validateModifiersForTopLevelFunction(modifiers);
|
| + return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, null);
|
| + } else if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
|
| + if (modifiers.constKeyword == null && modifiers.finalKeyword == null && modifiers.varKeyword == null) {
|
| + reportError8(ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE, []);
|
| + }
|
| + return new TopLevelVariableDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, parseVariableDeclarationList2(null, validateModifiersForTopLevelVariable(modifiers), null), expect2(TokenType.SEMICOLON));
|
| + }
|
| + TypeName returnType = parseReturnType();
|
| + if ((matches(Keyword.GET) || matches(Keyword.SET)) && matchesIdentifier2(peek())) {
|
| + validateModifiersForTopLevelFunction(modifiers);
|
| + return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, returnType);
|
| + } else if (matches(Keyword.OPERATOR) && isOperator(peek())) {
|
| + reportError9(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken, []);
|
| + return convertToFunctionDeclaration(parseOperator(commentAndMetadata, modifiers.externalKeyword, returnType));
|
| + } else if (matches5(TokenType.AT)) {
|
| + return new TopLevelVariableDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, parseVariableDeclarationList2(null, validateModifiersForTopLevelVariable(modifiers), returnType), expect2(TokenType.SEMICOLON));
|
| + } else if (!matchesIdentifier()) {
|
| + reportError9(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken, []);
|
| + Token semicolon;
|
| + if (matches5(TokenType.SEMICOLON)) {
|
| + semicolon = andAdvance;
|
| + } else {
|
| + semicolon = createSyntheticToken2(TokenType.SEMICOLON);
|
| + }
|
| + List<VariableDeclaration> variables = new List<VariableDeclaration>();
|
| + variables.add(new VariableDeclaration.full(null, null, createSyntheticIdentifier(), null, null));
|
| + return new TopLevelVariableDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, new VariableDeclarationList.full(null, null, null, returnType, variables), semicolon);
|
| + }
|
| + if (matchesAny(peek(), [
|
| + TokenType.OPEN_PAREN,
|
| + TokenType.FUNCTION,
|
| + TokenType.OPEN_CURLY_BRACKET])) {
|
| + validateModifiersForTopLevelFunction(modifiers);
|
| + return parseFunctionDeclaration(commentAndMetadata, modifiers.externalKeyword, returnType);
|
| + }
|
| + return new TopLevelVariableDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, parseVariableDeclarationList2(null, validateModifiersForTopLevelVariable(modifiers), returnType), expect2(TokenType.SEMICOLON));
|
| }
|
|
|
| /**
|
| - * Parse a getter.
|
| + * Parse a const expression.
|
| *
|
| * <pre>
|
| - * getter ::=
|
| - * getterSignature functionBody?
|
| - *
|
| - * getterSignature ::=
|
| - * 'external'? 'static'? returnType? 'get' identifier
|
| + * constExpression ::=
|
| + * instanceCreationExpression
|
| + * | listLiteral
|
| + * | mapLiteral
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| - * declaration
|
| - * @param externalKeyword the 'external' token
|
| - * @param staticKeyword the static keyword, or `null` if the getter is not static
|
| - * @param the return type that has already been parsed, or `null` if there was no return
|
| - * type
|
| - * @return the getter that was parsed
|
| + * @return the const expression that was parsed
|
| */
|
| - MethodDeclaration parseGetter(CommentAndMetadata commentAndMetadata, Token externalKeyword, Token staticKeyword, TypeName returnType) {
|
| - Token propertyKeyword = expect(Keyword.GET);
|
| - SimpleIdentifier name = parseSimpleIdentifier();
|
| - if (matches5(TokenType.OPEN_PAREN) && matches4(peek(), TokenType.CLOSE_PAREN)) {
|
| - reportError8(ParserErrorCode.GETTER_WITH_PARAMETERS, []);
|
| - advance();
|
| - advance();
|
| + Expression parseConstExpression() {
|
| + Token keyword = expect(Keyword.CONST);
|
| + if (matches5(TokenType.OPEN_SQUARE_BRACKET) || matches5(TokenType.INDEX)) {
|
| + return parseListLiteral(keyword, null);
|
| + } else if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| + return parseMapLiteral(keyword, null);
|
| + } else if (matches5(TokenType.LT)) {
|
| + return parseListOrMapLiteral(keyword);
|
| }
|
| - FunctionBody body = parseFunctionBody(externalKeyword != null || staticKeyword == null, ParserErrorCode.STATIC_GETTER_WITHOUT_BODY, false);
|
| - if (externalKeyword != null && body is! EmptyFunctionBody) {
|
| - reportError8(ParserErrorCode.EXTERNAL_GETTER_WITH_BODY, []);
|
| + return parseInstanceCreationExpression(keyword);
|
| + }
|
| + ConstructorDeclaration parseConstructor(CommentAndMetadata commentAndMetadata, Token externalKeyword, Token constKeyword, Token factoryKeyword, SimpleIdentifier returnType, Token period, SimpleIdentifier name, FormalParameterList parameters) {
|
| + bool bodyAllowed = externalKeyword == null;
|
| + Token separator = null;
|
| + List<ConstructorInitializer> initializers = null;
|
| + if (matches5(TokenType.COLON)) {
|
| + separator = andAdvance;
|
| + initializers = new List<ConstructorInitializer>();
|
| + do {
|
| + if (matches(Keyword.THIS)) {
|
| + if (matches4(peek(), TokenType.OPEN_PAREN)) {
|
| + bodyAllowed = false;
|
| + initializers.add(parseRedirectingConstructorInvocation());
|
| + } else if (matches4(peek(), TokenType.PERIOD) && matches4(peek2(3), TokenType.OPEN_PAREN)) {
|
| + bodyAllowed = false;
|
| + initializers.add(parseRedirectingConstructorInvocation());
|
| + } else {
|
| + initializers.add(parseConstructorFieldInitializer());
|
| + }
|
| + } else if (matches(Keyword.SUPER)) {
|
| + initializers.add(parseSuperConstructorInvocation());
|
| + } else {
|
| + initializers.add(parseConstructorFieldInitializer());
|
| + }
|
| + } while (optional(TokenType.COMMA));
|
| }
|
| - return new MethodDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, staticKeyword, returnType, propertyKeyword, null, name, null, body);
|
| + ConstructorName redirectedConstructor = null;
|
| + FunctionBody body;
|
| + if (matches5(TokenType.EQ)) {
|
| + separator = andAdvance;
|
| + redirectedConstructor = parseConstructorName();
|
| + body = new EmptyFunctionBody.full(expect2(TokenType.SEMICOLON));
|
| + if (factoryKeyword == null) {
|
| + reportError(ParserErrorCode.REDIRECTION_IN_NON_FACTORY_CONSTRUCTOR, redirectedConstructor, []);
|
| + }
|
| + } else {
|
| + body = parseFunctionBody(true, ParserErrorCode.MISSING_FUNCTION_BODY, false);
|
| + if (constKeyword != null && factoryKeyword != null) {
|
| + reportError9(ParserErrorCode.CONST_FACTORY, factoryKeyword, []);
|
| + } else if (body is EmptyFunctionBody) {
|
| + if (factoryKeyword != null && externalKeyword == null) {
|
| + reportError9(ParserErrorCode.FACTORY_WITHOUT_BODY, factoryKeyword, []);
|
| + }
|
| + } else {
|
| + if (constKeyword != null) {
|
| + reportError(ParserErrorCode.CONST_CONSTRUCTOR_WITH_BODY, body, []);
|
| + } else if (!bodyAllowed) {
|
| + reportError(ParserErrorCode.EXTERNAL_CONSTRUCTOR_WITH_BODY, body, []);
|
| + }
|
| + }
|
| + }
|
| + return new ConstructorDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, constKeyword, factoryKeyword, returnType, period, name, parameters, separator, initializers, redirectedConstructor, body);
|
| }
|
|
|
| /**
|
| - * Parse a list of identifiers.
|
| + * Parse a field initializer within a constructor.
|
| *
|
| * <pre>
|
| - * identifierList ::=
|
| - * identifier (',' identifier)*
|
| + * fieldInitializer:
|
| + * ('this' '.')? identifier '=' conditionalExpression cascadeSection*
|
| * </pre>
|
| *
|
| - * @return the list of identifiers that were parsed
|
| + * @return the field initializer that was parsed
|
| */
|
| - List<SimpleIdentifier> parseIdentifierList() {
|
| - List<SimpleIdentifier> identifiers = new List<SimpleIdentifier>();
|
| - identifiers.add(parseSimpleIdentifier());
|
| - while (matches5(TokenType.COMMA)) {
|
| - advance();
|
| - identifiers.add(parseSimpleIdentifier());
|
| + ConstructorFieldInitializer parseConstructorFieldInitializer() {
|
| + Token keyword = null;
|
| + Token period = null;
|
| + if (matches(Keyword.THIS)) {
|
| + keyword = andAdvance;
|
| + period = expect2(TokenType.PERIOD);
|
| }
|
| - return identifiers;
|
| + SimpleIdentifier fieldName = parseSimpleIdentifier();
|
| + Token equals = expect2(TokenType.EQ);
|
| + Expression expression = parseConditionalExpression();
|
| + TokenType tokenType = _currentToken.type;
|
| + if (identical(tokenType, TokenType.PERIOD_PERIOD)) {
|
| + List<Expression> cascadeSections = new List<Expression>();
|
| + while (identical(tokenType, TokenType.PERIOD_PERIOD)) {
|
| + Expression section = parseCascadeSection();
|
| + if (section != null) {
|
| + cascadeSections.add(section);
|
| + }
|
| + tokenType = _currentToken.type;
|
| + }
|
| + expression = new CascadeExpression.full(expression, cascadeSections);
|
| + }
|
| + return new ConstructorFieldInitializer.full(keyword, period, fieldName, equals, expression);
|
| }
|
|
|
| /**
|
| - * Parse an if statement.
|
| + * Parse a continue statement.
|
| *
|
| * <pre>
|
| - * ifStatement ::=
|
| - * 'if' '(' expression ')' statement ('else' statement)?
|
| + * continueStatement ::=
|
| + * 'continue' identifier? ';'
|
| * </pre>
|
| *
|
| - * @return the if statement that was parsed
|
| + * @return the continue statement that was parsed
|
| */
|
| - Statement parseIfStatement() {
|
| - Token ifKeyword = expect(Keyword.IF);
|
| - Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| - Expression condition = parseExpression2();
|
| - Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| - Statement thenStatement = parseStatement2();
|
| - Token elseKeyword = null;
|
| - Statement elseStatement = null;
|
| - if (matches(Keyword.ELSE)) {
|
| - elseKeyword = andAdvance;
|
| - elseStatement = parseStatement2();
|
| + Statement parseContinueStatement() {
|
| + Token continueKeyword = expect(Keyword.CONTINUE);
|
| + if (!_inLoop && !_inSwitch) {
|
| + reportError9(ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP, continueKeyword, []);
|
| }
|
| - return new IfStatement.full(ifKeyword, leftParenthesis, condition, rightParenthesis, thenStatement, elseKeyword, elseStatement);
|
| + SimpleIdentifier label = null;
|
| + if (matchesIdentifier()) {
|
| + label = parseSimpleIdentifier();
|
| + }
|
| + if (_inSwitch && !_inLoop && label == null) {
|
| + reportError9(ParserErrorCode.CONTINUE_WITHOUT_LABEL_IN_CASE, continueKeyword, []);
|
| + }
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new ContinueStatement.full(continueKeyword, label, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse an implements clause.
|
| + * Parse a directive.
|
| *
|
| * <pre>
|
| - * implementsClause ::=
|
| - * 'implements' type (',' type)*
|
| + * directive ::=
|
| + * exportDirective
|
| + * | libraryDirective
|
| + * | importDirective
|
| + * | partDirective
|
| * </pre>
|
| *
|
| - * @return the implements clause that was parsed
|
| + * @param commentAndMetadata the metadata to be associated with the directive
|
| + * @return the directive that was parsed
|
| */
|
| - ImplementsClause parseImplementsClause() {
|
| - Token keyword = expect(Keyword.IMPLEMENTS);
|
| - List<TypeName> interfaces = new List<TypeName>();
|
| - interfaces.add(parseTypeName());
|
| - while (optional(TokenType.COMMA)) {
|
| - interfaces.add(parseTypeName());
|
| - }
|
| - return new ImplementsClause.full(keyword, interfaces);
|
| - }
|
| + Directive parseDirective(CommentAndMetadata commentAndMetadata) {
|
| + if (matches(Keyword.IMPORT)) {
|
| + return parseImportDirective(commentAndMetadata);
|
| + } else if (matches(Keyword.EXPORT)) {
|
| + return parseExportDirective(commentAndMetadata);
|
| + } else if (matches(Keyword.LIBRARY)) {
|
| + return parseLibraryDirective(commentAndMetadata);
|
| + } else if (matches(Keyword.PART)) {
|
| + return parsePartDirective(commentAndMetadata);
|
| + } else {
|
| + throw new IllegalStateException("parseDirective invoked in an invalid state; currentToken = ${_currentToken}");
|
| + }
|
| + }
|
|
|
| /**
|
| - * Parse an import directive.
|
| + * Parse a documentation comment.
|
| *
|
| * <pre>
|
| - * importDirective ::=
|
| - * metadata 'import' stringLiteral ('as' identifier)? combinator*';'
|
| + * documentationComment ::=
|
| + * multiLineComment?
|
| + * | singleLineComment*
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the metadata to be associated with the directive
|
| - * @return the import directive that was parsed
|
| + * @return the documentation comment that was parsed, or `null` if there was no comment
|
| */
|
| - ImportDirective parseImportDirective(CommentAndMetadata commentAndMetadata) {
|
| - Token importKeyword = expect(Keyword.IMPORT);
|
| - StringLiteral libraryUri = parseStringLiteral();
|
| - Token asToken = null;
|
| - SimpleIdentifier prefix = null;
|
| - if (matches(Keyword.AS)) {
|
| - asToken = andAdvance;
|
| - prefix = parseSimpleIdentifier();
|
| + Comment parseDocumentationComment() {
|
| + List<Token> commentTokens = new List<Token>();
|
| + Token commentToken = _currentToken.precedingComments;
|
| + while (commentToken != null) {
|
| + if (identical(commentToken.type, TokenType.SINGLE_LINE_COMMENT)) {
|
| + if (commentToken.lexeme.startsWith("///")) {
|
| + if (commentTokens.length == 1 && commentTokens[0].lexeme.startsWith("/**")) {
|
| + commentTokens.clear();
|
| + }
|
| + commentTokens.add(commentToken);
|
| + }
|
| + } else {
|
| + if (commentToken.lexeme.startsWith("/**")) {
|
| + commentTokens.clear();
|
| + commentTokens.add(commentToken);
|
| + }
|
| + }
|
| + commentToken = commentToken.next;
|
| }
|
| - List<Combinator> combinators = parseCombinators();
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new ImportDirective.full(commentAndMetadata.comment, commentAndMetadata.metadata, importKeyword, libraryUri, asToken, prefix, combinators, semicolon);
|
| + if (commentTokens.isEmpty) {
|
| + return null;
|
| + }
|
| + List<Token> tokens = new List.from(commentTokens);
|
| + List<CommentReference> references = parseCommentReferences(tokens);
|
| + return Comment.createDocumentationComment2(tokens, references);
|
| }
|
|
|
| /**
|
| - * Parse a list of initialized identifiers.
|
| + * Parse a do statement.
|
| *
|
| * <pre>
|
| - * ?? ::=
|
| - * 'static'? ('var' | type) initializedIdentifierList ';'
|
| - * | 'final' type? initializedIdentifierList ';'
|
| - *
|
| - * initializedIdentifierList ::=
|
| - * initializedIdentifier (',' initializedIdentifier)*
|
| - *
|
| - * initializedIdentifier ::=
|
| - * identifier ('=' expression)?
|
| + * doStatement ::=
|
| + * 'do' statement 'while' '(' expression ')' ';'
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| - * declaration
|
| - * @param staticKeyword the static keyword, or `null` if the getter is not static
|
| - * @param keyword the token representing the 'final', 'const' or 'var' keyword, or `null` if
|
| - * there is no keyword
|
| - * @param type the type that has already been parsed, or `null` if 'var' was provided
|
| - * @return the getter that was parsed
|
| + * @return the do statement that was parsed
|
| */
|
| - FieldDeclaration parseInitializedIdentifierList(CommentAndMetadata commentAndMetadata, Token staticKeyword, Token keyword, TypeName type) {
|
| - VariableDeclarationList fieldList = parseVariableDeclarationList2(null, keyword, type);
|
| - return new FieldDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, staticKeyword, fieldList, expect2(TokenType.SEMICOLON));
|
| + Statement parseDoStatement() {
|
| + bool wasInLoop = _inLoop;
|
| + _inLoop = true;
|
| + try {
|
| + Token doKeyword = expect(Keyword.DO);
|
| + Statement body = parseStatement2();
|
| + Token whileKeyword = expect(Keyword.WHILE);
|
| + Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| + Expression condition = parseExpression2();
|
| + Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new DoStatement.full(doKeyword, body, whileKeyword, leftParenthesis, condition, rightParenthesis, semicolon);
|
| + } finally {
|
| + _inLoop = wasInLoop;
|
| + }
|
| }
|
|
|
| /**
|
| - * Parse an instance creation expression.
|
| + * Parse an empty statement.
|
| *
|
| * <pre>
|
| - * instanceCreationExpression ::=
|
| - * ('new' | 'const') type ('.' identifier)? argumentList
|
| + * emptyStatement ::=
|
| + * ';'
|
| * </pre>
|
| *
|
| - * @param keyword the 'new' or 'const' keyword that introduces the expression
|
| - * @return the instance creation expression that was parsed
|
| + * @return the empty statement that was parsed
|
| */
|
| - InstanceCreationExpression parseInstanceCreationExpression(Token keyword) {
|
| - ConstructorName constructorName = parseConstructorName();
|
| - ArgumentList argumentList = parseArgumentList();
|
| - return new InstanceCreationExpression.full(keyword, constructorName, argumentList);
|
| - }
|
| + Statement parseEmptyStatement() => new EmptyStatement.full(andAdvance);
|
|
|
| /**
|
| - * Parse a library directive.
|
| + * Parse an equality expression.
|
| *
|
| * <pre>
|
| - * libraryDirective ::=
|
| - * metadata 'library' identifier ';'
|
| + * equalityExpression ::=
|
| + * relationalExpression (equalityOperator relationalExpression)?
|
| + * | 'super' equalityOperator relationalExpression
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the metadata to be associated with the directive
|
| - * @return the library directive that was parsed
|
| + * @return the equality expression that was parsed
|
| */
|
| - LibraryDirective parseLibraryDirective(CommentAndMetadata commentAndMetadata) {
|
| - Token keyword = expect(Keyword.LIBRARY);
|
| - LibraryIdentifier libraryName = parseLibraryName(ParserErrorCode.MISSING_NAME_IN_LIBRARY_DIRECTIVE, keyword);
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new LibraryDirective.full(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, libraryName, semicolon);
|
| + Expression parseEqualityExpression() {
|
| + Expression expression;
|
| + if (matches(Keyword.SUPER) && _currentToken.next.type.isEqualityOperator) {
|
| + expression = new SuperExpression.full(andAdvance);
|
| + } else {
|
| + expression = parseRelationalExpression();
|
| + }
|
| + bool leftEqualityExpression = false;
|
| + while (_currentToken.type.isEqualityOperator) {
|
| + Token operator = andAdvance;
|
| + if (leftEqualityExpression) {
|
| + reportError(ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND, expression, []);
|
| + }
|
| + expression = new BinaryExpression.full(expression, operator, parseRelationalExpression());
|
| + leftEqualityExpression = true;
|
| + }
|
| + return expression;
|
| }
|
|
|
| /**
|
| - * Parse a library identifier.
|
| + * Parse an export directive.
|
| *
|
| * <pre>
|
| - * libraryIdentifier ::=
|
| - * identifier ('.' identifier)*
|
| + * exportDirective ::=
|
| + * metadata 'export' stringLiteral combinator*';'
|
| * </pre>
|
| *
|
| - * @return the library identifier that was parsed
|
| + * @param commentAndMetadata the metadata to be associated with the directive
|
| + * @return the export directive that was parsed
|
| */
|
| - LibraryIdentifier parseLibraryIdentifier() {
|
| - List<SimpleIdentifier> components = new List<SimpleIdentifier>();
|
| - components.add(parseSimpleIdentifier());
|
| - while (matches5(TokenType.PERIOD)) {
|
| - advance();
|
| - components.add(parseSimpleIdentifier());
|
| - }
|
| - return new LibraryIdentifier.full(components);
|
| + ExportDirective parseExportDirective(CommentAndMetadata commentAndMetadata) {
|
| + Token exportKeyword = expect(Keyword.EXPORT);
|
| + StringLiteral libraryUri = parseStringLiteral();
|
| + List<Combinator> combinators = parseCombinators();
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new ExportDirective.full(commentAndMetadata.comment, commentAndMetadata.metadata, exportKeyword, libraryUri, combinators, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse a library name.
|
| + * Parse a list of expressions.
|
| *
|
| * <pre>
|
| - * libraryName ::=
|
| - * libraryIdentifier
|
| + * expressionList ::=
|
| + * expression (',' expression)*
|
| * </pre>
|
| *
|
| - * @param missingNameError the error code to be used if the library name is missing
|
| - * @param missingNameToken the token associated with the error produced if the library name is
|
| - * missing
|
| - * @return the library name that was parsed
|
| + * @return the expression that was parsed
|
| */
|
| - LibraryIdentifier parseLibraryName(ParserErrorCode missingNameError, Token missingNameToken) {
|
| - if (matchesIdentifier()) {
|
| - return parseLibraryIdentifier();
|
| - } else if (matches5(TokenType.STRING)) {
|
| - StringLiteral string = parseStringLiteral();
|
| - reportError(ParserErrorCode.NON_IDENTIFIER_LIBRARY_NAME, string, []);
|
| - } else {
|
| - reportError9(missingNameError, missingNameToken, []);
|
| + List<Expression> parseExpressionList() {
|
| + List<Expression> expressions = new List<Expression>();
|
| + expressions.add(parseExpression2());
|
| + while (optional(TokenType.COMMA)) {
|
| + expressions.add(parseExpression2());
|
| }
|
| - List<SimpleIdentifier> components = new List<SimpleIdentifier>();
|
| - components.add(createSyntheticIdentifier());
|
| - return new LibraryIdentifier.full(components);
|
| + return expressions;
|
| }
|
|
|
| /**
|
| - * Parse a list literal.
|
| + * Parse the 'final', 'const', 'var' or type preceding a variable declaration.
|
| *
|
| * <pre>
|
| - * listLiteral ::=
|
| - * 'const'? typeArguments? '[' (expressionList ','?)? ']'
|
| + * finalConstVarOrType ::=
|
| + * | 'final' type?
|
| + * | 'const' type?
|
| + * | 'var'
|
| + * | type
|
| * </pre>
|
| *
|
| - * @param modifier the 'const' modifier appearing before the literal, or `null` if there is
|
| - * no modifier
|
| - * @param typeArguments the type arguments appearing before the literal, or `null` if there
|
| - * are no type arguments
|
| - * @return the list literal that was parsed
|
| + * @param optional `true` if the keyword and type are optional
|
| + * @return the 'final', 'const', 'var' or type that was parsed
|
| */
|
| - ListLiteral parseListLiteral(Token modifier, TypeArgumentList typeArguments) {
|
| - if (matches5(TokenType.INDEX)) {
|
| - BeginToken leftBracket = new BeginToken(TokenType.OPEN_SQUARE_BRACKET, _currentToken.offset);
|
| - Token rightBracket = new Token(TokenType.CLOSE_SQUARE_BRACKET, _currentToken.offset + 1);
|
| - leftBracket.endToken = rightBracket;
|
| - rightBracket.setNext(_currentToken.next);
|
| - leftBracket.setNext(rightBracket);
|
| - _currentToken.previous.setNext(leftBracket);
|
| - _currentToken = _currentToken.next;
|
| - return new ListLiteral.full(modifier, typeArguments, leftBracket, null, rightBracket);
|
| - }
|
| - Token leftBracket = expect2(TokenType.OPEN_SQUARE_BRACKET);
|
| - if (matches5(TokenType.CLOSE_SQUARE_BRACKET)) {
|
| - return new ListLiteral.full(modifier, typeArguments, leftBracket, null, andAdvance);
|
| - }
|
| - List<Expression> elements = new List<Expression>();
|
| - elements.add(parseExpression2());
|
| - while (optional(TokenType.COMMA)) {
|
| - if (matches5(TokenType.CLOSE_SQUARE_BRACKET)) {
|
| - return new ListLiteral.full(modifier, typeArguments, leftBracket, elements, andAdvance);
|
| + FinalConstVarOrType parseFinalConstVarOrType(bool optional) {
|
| + Token keyword = null;
|
| + TypeName type = null;
|
| + if (matches(Keyword.FINAL) || matches(Keyword.CONST)) {
|
| + keyword = andAdvance;
|
| + if (isTypedIdentifier(_currentToken)) {
|
| + type = parseTypeName();
|
| + }
|
| + } else if (matches(Keyword.VAR)) {
|
| + keyword = andAdvance;
|
| + } else {
|
| + if (isTypedIdentifier(_currentToken)) {
|
| + type = parseReturnType();
|
| + } else if (!optional) {
|
| + reportError8(ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE, []);
|
| }
|
| - elements.add(parseExpression2());
|
| }
|
| - Token rightBracket = expect2(TokenType.CLOSE_SQUARE_BRACKET);
|
| - return new ListLiteral.full(modifier, typeArguments, leftBracket, elements, rightBracket);
|
| + return new FinalConstVarOrType(keyword, type);
|
| }
|
|
|
| /**
|
| - * Parse a list or map literal.
|
| + * Parse a formal parameter. At most one of `isOptional` and `isNamed` can be
|
| + * `true`.
|
| *
|
| * <pre>
|
| - * listOrMapLiteral ::=
|
| - * listLiteral
|
| - * | mapLiteral
|
| + * defaultFormalParameter ::=
|
| + * normalFormalParameter ('=' expression)?
|
| + *
|
| + * defaultNamedParameter ::=
|
| + * normalFormalParameter (':' expression)?
|
| * </pre>
|
| *
|
| - * @param modifier the 'const' modifier appearing before the literal, or `null` if there is
|
| - * no modifier
|
| - * @return the list or map literal that was parsed
|
| + * @param kind the kind of parameter being expected based on the presence or absence of group
|
| + * delimiters
|
| + * @return the formal parameter that was parsed
|
| */
|
| - TypedLiteral parseListOrMapLiteral(Token modifier) {
|
| - TypeArgumentList typeArguments = null;
|
| - if (matches5(TokenType.LT)) {
|
| - typeArguments = parseTypeArgumentList();
|
| - }
|
| - if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| - return parseMapLiteral(modifier, typeArguments);
|
| - } else if (matches5(TokenType.OPEN_SQUARE_BRACKET) || matches5(TokenType.INDEX)) {
|
| - return parseListLiteral(modifier, typeArguments);
|
| + FormalParameter parseFormalParameter(ParameterKind kind) {
|
| + NormalFormalParameter parameter = parseNormalFormalParameter();
|
| + if (matches5(TokenType.EQ)) {
|
| + Token seperator = andAdvance;
|
| + Expression defaultValue = parseExpression2();
|
| + if (identical(kind, ParameterKind.NAMED)) {
|
| + reportError9(ParserErrorCode.WRONG_SEPARATOR_FOR_NAMED_PARAMETER, seperator, []);
|
| + } else if (identical(kind, ParameterKind.REQUIRED)) {
|
| + reportError(ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP, parameter, []);
|
| + }
|
| + return new DefaultFormalParameter.full(parameter, kind, seperator, defaultValue);
|
| + } else if (matches5(TokenType.COLON)) {
|
| + Token seperator = andAdvance;
|
| + Expression defaultValue = parseExpression2();
|
| + if (identical(kind, ParameterKind.POSITIONAL)) {
|
| + reportError9(ParserErrorCode.WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER, seperator, []);
|
| + } else if (identical(kind, ParameterKind.REQUIRED)) {
|
| + reportError(ParserErrorCode.NAMED_PARAMETER_OUTSIDE_GROUP, parameter, []);
|
| + }
|
| + return new DefaultFormalParameter.full(parameter, kind, seperator, defaultValue);
|
| + } else if (kind != ParameterKind.REQUIRED) {
|
| + return new DefaultFormalParameter.full(parameter, kind, null, null);
|
| }
|
| - reportError8(ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL, []);
|
| - return new ListLiteral.full(modifier, typeArguments, createSyntheticToken2(TokenType.OPEN_SQUARE_BRACKET), null, createSyntheticToken2(TokenType.CLOSE_SQUARE_BRACKET));
|
| + return parameter;
|
| }
|
|
|
| /**
|
| - * Parse a logical and expression.
|
| + * Parse a for statement.
|
| *
|
| * <pre>
|
| - * logicalAndExpression ::=
|
| - * equalityExpression ('&&' equalityExpression)*
|
| + * forStatement ::=
|
| + * 'for' '(' forLoopParts ')' statement
|
| + *
|
| + * forLoopParts ::=
|
| + * forInitializerStatement expression? ';' expressionList?
|
| + * | declaredIdentifier 'in' expression
|
| + * | identifier 'in' expression
|
| + *
|
| + * forInitializerStatement ::=
|
| + * localVariableDeclaration ';'
|
| + * | expression? ';'
|
| * </pre>
|
| *
|
| - * @return the logical and expression that was parsed
|
| + * @return the for statement that was parsed
|
| */
|
| - Expression parseLogicalAndExpression() {
|
| - Expression expression = parseEqualityExpression();
|
| - while (matches5(TokenType.AMPERSAND_AMPERSAND)) {
|
| - Token operator = andAdvance;
|
| - expression = new BinaryExpression.full(expression, operator, parseEqualityExpression());
|
| + Statement parseForStatement() {
|
| + bool wasInLoop = _inLoop;
|
| + _inLoop = true;
|
| + try {
|
| + Token forKeyword = expect(Keyword.FOR);
|
| + Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| + VariableDeclarationList variableList = null;
|
| + Expression initialization = null;
|
| + if (!matches5(TokenType.SEMICOLON)) {
|
| + CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| + if (matchesIdentifier() && matches3(peek(), Keyword.IN)) {
|
| + List<VariableDeclaration> variables = new List<VariableDeclaration>();
|
| + SimpleIdentifier variableName = parseSimpleIdentifier();
|
| + variables.add(new VariableDeclaration.full(null, null, variableName, null, null));
|
| + variableList = new VariableDeclarationList.full(commentAndMetadata.comment, commentAndMetadata.metadata, null, null, variables);
|
| + } else if (isInitializedVariableDeclaration()) {
|
| + variableList = parseVariableDeclarationList(commentAndMetadata);
|
| + } else {
|
| + initialization = parseExpression2();
|
| + }
|
| + if (matches(Keyword.IN)) {
|
| + DeclaredIdentifier loopVariable = null;
|
| + SimpleIdentifier identifier = null;
|
| + if (variableList == null) {
|
| + reportError8(ParserErrorCode.MISSING_VARIABLE_IN_FOR_EACH, []);
|
| + } else {
|
| + NodeList<VariableDeclaration> variables = variableList.variables;
|
| + if (variables.length > 1) {
|
| + reportError8(ParserErrorCode.MULTIPLE_VARIABLES_IN_FOR_EACH, [variables.length.toString()]);
|
| + }
|
| + VariableDeclaration variable = variables[0];
|
| + if (variable.initializer != null) {
|
| + reportError8(ParserErrorCode.INITIALIZED_VARIABLE_IN_FOR_EACH, []);
|
| + }
|
| + Token keyword = variableList.keyword;
|
| + TypeName type = variableList.type;
|
| + if (keyword != null || type != null) {
|
| + loopVariable = new DeclaredIdentifier.full(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, type, variable.name);
|
| + } else {
|
| + if (!commentAndMetadata.metadata.isEmpty) {
|
| + }
|
| + identifier = variable.name;
|
| + }
|
| + }
|
| + Token inKeyword = expect(Keyword.IN);
|
| + Expression iterator = parseExpression2();
|
| + Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| + Statement body = parseStatement2();
|
| + if (loopVariable == null) {
|
| + return new ForEachStatement.con2_full(forKeyword, leftParenthesis, identifier, inKeyword, iterator, rightParenthesis, body);
|
| + }
|
| + return new ForEachStatement.con1_full(forKeyword, leftParenthesis, loopVariable, inKeyword, iterator, rightParenthesis, body);
|
| + }
|
| + }
|
| + Token leftSeparator = expect2(TokenType.SEMICOLON);
|
| + Expression condition = null;
|
| + if (!matches5(TokenType.SEMICOLON)) {
|
| + condition = parseExpression2();
|
| + }
|
| + Token rightSeparator = expect2(TokenType.SEMICOLON);
|
| + List<Expression> updaters = null;
|
| + if (!matches5(TokenType.CLOSE_PAREN)) {
|
| + updaters = parseExpressionList();
|
| + }
|
| + Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| + Statement body = parseStatement2();
|
| + return new ForStatement.full(forKeyword, leftParenthesis, variableList, initialization, leftSeparator, condition, rightSeparator, updaters, rightParenthesis, body);
|
| + } finally {
|
| + _inLoop = wasInLoop;
|
| }
|
| - return expression;
|
| }
|
|
|
| /**
|
| - * Parse a logical or expression.
|
| + * Parse a function body.
|
| *
|
| * <pre>
|
| - * logicalOrExpression ::=
|
| - * logicalAndExpression ('||' logicalAndExpression)*
|
| + * functionBody ::=
|
| + * '=>' expression ';'
|
| + * | block
|
| + *
|
| + * functionExpressionBody ::=
|
| + * '=>' expression
|
| + * | block
|
| * </pre>
|
| *
|
| - * @return the logical or expression that was parsed
|
| + * @param mayBeEmpty `true` if the function body is allowed to be empty
|
| + * @param emptyErrorCode the error code to report if function body expecte, but not found
|
| + * @param inExpression `true` if the function body is being parsed as part of an expression
|
| + * and therefore does not have a terminating semicolon
|
| + * @return the function body that was parsed
|
| */
|
| - Expression parseLogicalOrExpression() {
|
| - Expression expression = parseLogicalAndExpression();
|
| - while (matches5(TokenType.BAR_BAR)) {
|
| - Token operator = andAdvance;
|
| - expression = new BinaryExpression.full(expression, operator, parseLogicalAndExpression());
|
| + FunctionBody parseFunctionBody(bool mayBeEmpty, ParserErrorCode emptyErrorCode, bool inExpression) {
|
| + bool wasInLoop = _inLoop;
|
| + bool wasInSwitch = _inSwitch;
|
| + _inLoop = false;
|
| + _inSwitch = false;
|
| + try {
|
| + if (matches5(TokenType.SEMICOLON)) {
|
| + if (!mayBeEmpty) {
|
| + reportError8(emptyErrorCode, []);
|
| + }
|
| + return new EmptyFunctionBody.full(andAdvance);
|
| + } else if (matches5(TokenType.FUNCTION)) {
|
| + Token functionDefinition = andAdvance;
|
| + Expression expression = parseExpression2();
|
| + Token semicolon = null;
|
| + if (!inExpression) {
|
| + semicolon = expect2(TokenType.SEMICOLON);
|
| + }
|
| + return new ExpressionFunctionBody.full(functionDefinition, expression, semicolon);
|
| + } else if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| + return new BlockFunctionBody.full(parseBlock());
|
| + } else if (matches2(_NATIVE)) {
|
| + Token nativeToken = andAdvance;
|
| + StringLiteral stringLiteral = null;
|
| + if (matches5(TokenType.STRING)) {
|
| + stringLiteral = parseStringLiteral();
|
| + }
|
| + return new NativeFunctionBody.full(nativeToken, stringLiteral, expect2(TokenType.SEMICOLON));
|
| + } else {
|
| + reportError8(emptyErrorCode, []);
|
| + return new EmptyFunctionBody.full(createSyntheticToken2(TokenType.SEMICOLON));
|
| + }
|
| + } finally {
|
| + _inLoop = wasInLoop;
|
| + _inSwitch = wasInSwitch;
|
| }
|
| - return expression;
|
| }
|
|
|
| /**
|
| - * Parse a map literal.
|
| + * Parse a function declaration.
|
| *
|
| * <pre>
|
| - * mapLiteral ::=
|
| - * 'const'? typeArguments? '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}'
|
| + * functionDeclaration ::=
|
| + * functionSignature functionBody
|
| + * | returnType? getOrSet identifier formalParameterList functionBody
|
| * </pre>
|
| *
|
| - * @param modifier the 'const' modifier appearing before the literal, or `null` if there is
|
| - * no modifier
|
| - * @param typeArguments the type arguments that were declared, or `null` if there are no
|
| - * type arguments
|
| - * @return the map literal that was parsed
|
| + * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| + * declaration
|
| + * @param externalKeyword the 'external' keyword, or `null` if the function is not external
|
| + * @param returnType the return type, or `null` if there is no return type
|
| + * @param isStatement `true` if the function declaration is being parsed as a statement
|
| + * @return the function declaration that was parsed
|
| */
|
| - MapLiteral parseMapLiteral(Token modifier, TypeArgumentList typeArguments) {
|
| - Token leftBracket = expect2(TokenType.OPEN_CURLY_BRACKET);
|
| - List<MapLiteralEntry> entries = new List<MapLiteralEntry>();
|
| - if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| - return new MapLiteral.full(modifier, typeArguments, leftBracket, entries, andAdvance);
|
| + FunctionDeclaration parseFunctionDeclaration(CommentAndMetadata commentAndMetadata, Token externalKeyword, TypeName returnType) {
|
| + Token keyword = null;
|
| + bool isGetter = false;
|
| + if (matches(Keyword.GET) && !matches4(peek(), TokenType.OPEN_PAREN)) {
|
| + keyword = andAdvance;
|
| + isGetter = true;
|
| + } else if (matches(Keyword.SET) && !matches4(peek(), TokenType.OPEN_PAREN)) {
|
| + keyword = andAdvance;
|
| }
|
| - entries.add(parseMapLiteralEntry());
|
| - while (optional(TokenType.COMMA)) {
|
| - if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| - return new MapLiteral.full(modifier, typeArguments, leftBracket, entries, andAdvance);
|
| + SimpleIdentifier name = parseSimpleIdentifier();
|
| + FormalParameterList parameters = null;
|
| + if (!isGetter) {
|
| + if (matches5(TokenType.OPEN_PAREN)) {
|
| + parameters = parseFormalParameterList();
|
| + validateFormalParameterList(parameters);
|
| + } else {
|
| + reportError8(ParserErrorCode.MISSING_FUNCTION_PARAMETERS, []);
|
| }
|
| - entries.add(parseMapLiteralEntry());
|
| + } else if (matches5(TokenType.OPEN_PAREN)) {
|
| + reportError8(ParserErrorCode.GETTER_WITH_PARAMETERS, []);
|
| + parseFormalParameterList();
|
| }
|
| - Token rightBracket = expect2(TokenType.CLOSE_CURLY_BRACKET);
|
| - return new MapLiteral.full(modifier, typeArguments, leftBracket, entries, rightBracket);
|
| + FunctionBody body;
|
| + if (externalKeyword == null) {
|
| + body = parseFunctionBody(false, ParserErrorCode.MISSING_FUNCTION_BODY, false);
|
| + } else {
|
| + body = new EmptyFunctionBody.full(expect2(TokenType.SEMICOLON));
|
| + }
|
| + return new FunctionDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, returnType, keyword, name, new FunctionExpression.full(parameters, body));
|
| }
|
|
|
| /**
|
| - * Parse a map literal entry.
|
| + * Parse a function declaration statement.
|
| *
|
| * <pre>
|
| - * mapLiteralEntry ::=
|
| - * expression ':' expression
|
| - * </pre>
|
| + * functionDeclarationStatement ::=
|
| + * functionSignature functionBody
|
| + * </pre>
|
| *
|
| - * @return the map literal entry that was parsed
|
| + * @return the function declaration statement that was parsed
|
| */
|
| - MapLiteralEntry parseMapLiteralEntry() {
|
| - Expression key = parseExpression2();
|
| - Token separator = expect2(TokenType.COLON);
|
| - Expression value = parseExpression2();
|
| - return new MapLiteralEntry.full(key, separator, value);
|
| + Statement parseFunctionDeclarationStatement() {
|
| + Modifiers modifiers = parseModifiers();
|
| + validateModifiersForFunctionDeclarationStatement(modifiers);
|
| + return parseFunctionDeclarationStatement2(parseCommentAndMetadata(), parseOptionalReturnType());
|
| }
|
|
|
| /**
|
| - * Parse a method declaration.
|
| + * Parse a function declaration statement.
|
| *
|
| * <pre>
|
| - * functionDeclaration ::=
|
| - * 'external'? 'static'? functionSignature functionBody
|
| - * | 'external'? functionSignature ';'
|
| + * functionDeclarationStatement ::=
|
| + * functionSignature functionBody
|
| * </pre>
|
| *
|
| * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| * declaration
|
| - * @param externalKeyword the 'external' token
|
| - * @param staticKeyword the static keyword, or `null` if the getter is not static
|
| - * @param returnType the return type of the method
|
| - * @return the method declaration that was parsed
|
| + * @param returnType the return type, or `null` if there is no return type
|
| + * @return the function declaration statement that was parsed
|
| */
|
| - MethodDeclaration parseMethodDeclaration(CommentAndMetadata commentAndMetadata, Token externalKeyword, Token staticKeyword, TypeName returnType) {
|
| - SimpleIdentifier methodName = parseSimpleIdentifier();
|
| + Statement parseFunctionDeclarationStatement2(CommentAndMetadata commentAndMetadata, TypeName returnType) {
|
| + FunctionDeclaration declaration = parseFunctionDeclaration(commentAndMetadata, null, returnType);
|
| + Token propertyKeyword = declaration.propertyKeyword;
|
| + if (propertyKeyword != null) {
|
| + if (identical(((propertyKeyword as KeywordToken)).keyword, Keyword.GET)) {
|
| + reportError9(ParserErrorCode.GETTER_IN_FUNCTION, propertyKeyword, []);
|
| + } else {
|
| + reportError9(ParserErrorCode.SETTER_IN_FUNCTION, propertyKeyword, []);
|
| + }
|
| + }
|
| + return new FunctionDeclarationStatement.full(declaration);
|
| + }
|
| +
|
| + /**
|
| + * Parse a function type alias.
|
| + *
|
| + * <pre>
|
| + * functionTypeAlias ::=
|
| + * functionPrefix typeParameterList? formalParameterList ';'
|
| + *
|
| + * functionPrefix ::=
|
| + * returnType? name
|
| + * </pre>
|
| + *
|
| + * @param commentAndMetadata the metadata to be associated with the member
|
| + * @param keyword the token representing the 'typedef' keyword
|
| + * @return the function type alias that was parsed
|
| + */
|
| + FunctionTypeAlias parseFunctionTypeAlias(CommentAndMetadata commentAndMetadata, Token keyword) {
|
| + TypeName returnType = null;
|
| + if (hasReturnTypeInTypeAlias()) {
|
| + returnType = parseReturnType();
|
| + }
|
| + SimpleIdentifier name = parseSimpleIdentifier();
|
| + TypeParameterList typeParameters = null;
|
| + if (matches5(TokenType.LT)) {
|
| + typeParameters = parseTypeParameterList();
|
| + }
|
| + if (matches5(TokenType.SEMICOLON) || matches5(TokenType.EOF)) {
|
| + reportError8(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS, []);
|
| + FormalParameterList parameters = new FormalParameterList.full(createSyntheticToken2(TokenType.OPEN_PAREN), null, null, null, createSyntheticToken2(TokenType.CLOSE_PAREN));
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new FunctionTypeAlias.full(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, returnType, name, typeParameters, parameters, semicolon);
|
| + } else if (!matches5(TokenType.OPEN_PAREN)) {
|
| + reportError8(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS, []);
|
| + return new FunctionTypeAlias.full(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, returnType, name, typeParameters, new FormalParameterList.full(createSyntheticToken2(TokenType.OPEN_PAREN), null, null, null, createSyntheticToken2(TokenType.CLOSE_PAREN)), createSyntheticToken2(TokenType.SEMICOLON));
|
| + }
|
| FormalParameterList parameters = parseFormalParameterList();
|
| validateFormalParameterList(parameters);
|
| - return parseMethodDeclaration2(commentAndMetadata, externalKeyword, staticKeyword, returnType, methodName, parameters);
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new FunctionTypeAlias.full(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, returnType, name, typeParameters, parameters, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse a method declaration.
|
| + * Parse a getter.
|
| *
|
| * <pre>
|
| - * functionDeclaration ::=
|
| - * ('external' 'static'?)? functionSignature functionBody
|
| - * | 'external'? functionSignature ';'
|
| + * getter ::=
|
| + * getterSignature functionBody?
|
| + *
|
| + * getterSignature ::=
|
| + * 'external'? 'static'? returnType? 'get' identifier
|
| * </pre>
|
| *
|
| * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| * declaration
|
| * @param externalKeyword the 'external' token
|
| * @param staticKeyword the static keyword, or `null` if the getter is not static
|
| - * @param returnType the return type of the method
|
| - * @param name the name of the method
|
| - * @param parameters the parameters to the method
|
| - * @return the method declaration that was parsed
|
| + * @param the return type that has already been parsed, or `null` if there was no return
|
| + * type
|
| + * @return the getter that was parsed
|
| */
|
| - MethodDeclaration parseMethodDeclaration2(CommentAndMetadata commentAndMetadata, Token externalKeyword, Token staticKeyword, TypeName returnType, SimpleIdentifier name, FormalParameterList parameters) {
|
| - FunctionBody body = parseFunctionBody(externalKeyword != null || staticKeyword == null, ParserErrorCode.MISSING_FUNCTION_BODY, false);
|
| - if (externalKeyword != null) {
|
| - if (body is! EmptyFunctionBody) {
|
| - reportError(ParserErrorCode.EXTERNAL_METHOD_WITH_BODY, body, []);
|
| - }
|
| - } else if (staticKeyword != null) {
|
| - if (body is EmptyFunctionBody) {
|
| - reportError(ParserErrorCode.ABSTRACT_STATIC_METHOD, body, []);
|
| - }
|
| + MethodDeclaration parseGetter(CommentAndMetadata commentAndMetadata, Token externalKeyword, Token staticKeyword, TypeName returnType) {
|
| + Token propertyKeyword = expect(Keyword.GET);
|
| + SimpleIdentifier name = parseSimpleIdentifier();
|
| + if (matches5(TokenType.OPEN_PAREN) && matches4(peek(), TokenType.CLOSE_PAREN)) {
|
| + reportError8(ParserErrorCode.GETTER_WITH_PARAMETERS, []);
|
| + advance();
|
| + advance();
|
| }
|
| - return new MethodDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, staticKeyword, returnType, null, null, name, parameters, body);
|
| + FunctionBody body = parseFunctionBody(externalKeyword != null || staticKeyword == null, ParserErrorCode.STATIC_GETTER_WITHOUT_BODY, false);
|
| + if (externalKeyword != null && body is! EmptyFunctionBody) {
|
| + reportError8(ParserErrorCode.EXTERNAL_GETTER_WITH_BODY, []);
|
| + }
|
| + return new MethodDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, staticKeyword, returnType, propertyKeyword, null, name, null, body);
|
| }
|
|
|
| /**
|
| - * Parse the modifiers preceding a declaration. This method allows the modifiers to appear in any
|
| - * order but does generate errors for duplicated modifiers. Checks for other problems, such as
|
| - * having the modifiers appear in the wrong order or specifying both 'const' and 'final', are
|
| - * reported in one of the methods whose name is prefixed with `validateModifiersFor`.
|
| + * Parse a list of identifiers.
|
| *
|
| * <pre>
|
| - * modifiers ::=
|
| - * ('abstract' | 'const' | 'external' | 'factory' | 'final' | 'static' | 'var')*
|
| + * identifierList ::=
|
| + * identifier (',' identifier)*
|
| * </pre>
|
| *
|
| - * @return the modifiers that were parsed
|
| + * @return the list of identifiers that were parsed
|
| */
|
| - Modifiers parseModifiers() {
|
| - Modifiers modifiers = new Modifiers();
|
| - bool progress = true;
|
| - while (progress) {
|
| - if (matches(Keyword.ABSTRACT) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| - if (modifiers.abstractKeyword != null) {
|
| - reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| - advance();
|
| - } else {
|
| - modifiers.abstractKeyword = andAdvance;
|
| - }
|
| - } else if (matches(Keyword.CONST)) {
|
| - if (modifiers.constKeyword != null) {
|
| - reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| - advance();
|
| - } else {
|
| - modifiers.constKeyword = andAdvance;
|
| - }
|
| - } else if (matches(Keyword.EXTERNAL) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| - if (modifiers.externalKeyword != null) {
|
| - reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| - advance();
|
| - } else {
|
| - modifiers.externalKeyword = andAdvance;
|
| - }
|
| - } else if (matches(Keyword.FACTORY) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| - if (modifiers.factoryKeyword != null) {
|
| - reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| - advance();
|
| - } else {
|
| - modifiers.factoryKeyword = andAdvance;
|
| - }
|
| - } else if (matches(Keyword.FINAL)) {
|
| - if (modifiers.finalKeyword != null) {
|
| - reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| - advance();
|
| - } else {
|
| - modifiers.finalKeyword = andAdvance;
|
| - }
|
| - } else if (matches(Keyword.STATIC) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| - if (modifiers.staticKeyword != null) {
|
| - reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| - advance();
|
| - } else {
|
| - modifiers.staticKeyword = andAdvance;
|
| - }
|
| - } else if (matches(Keyword.VAR)) {
|
| - if (modifiers.varKeyword != null) {
|
| - reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| - advance();
|
| - } else {
|
| - modifiers.varKeyword = andAdvance;
|
| - }
|
| - } else {
|
| - progress = false;
|
| - }
|
| + List<SimpleIdentifier> parseIdentifierList() {
|
| + List<SimpleIdentifier> identifiers = new List<SimpleIdentifier>();
|
| + identifiers.add(parseSimpleIdentifier());
|
| + while (matches5(TokenType.COMMA)) {
|
| + advance();
|
| + identifiers.add(parseSimpleIdentifier());
|
| }
|
| - return modifiers;
|
| + return identifiers;
|
| }
|
|
|
| /**
|
| - * Parse a multiplicative expression.
|
| + * Parse an if statement.
|
| *
|
| * <pre>
|
| - * multiplicativeExpression ::=
|
| - * unaryExpression (multiplicativeOperator unaryExpression)*
|
| - * | 'super' (multiplicativeOperator unaryExpression)+
|
| + * ifStatement ::=
|
| + * 'if' '(' expression ')' statement ('else' statement)?
|
| * </pre>
|
| *
|
| - * @return the multiplicative expression that was parsed
|
| + * @return the if statement that was parsed
|
| */
|
| - Expression parseMultiplicativeExpression() {
|
| - Expression expression;
|
| - if (matches(Keyword.SUPER) && _currentToken.next.type.isMultiplicativeOperator) {
|
| - expression = new SuperExpression.full(andAdvance);
|
| - } else {
|
| - expression = parseUnaryExpression();
|
| - }
|
| - while (_currentToken.type.isMultiplicativeOperator) {
|
| - Token operator = andAdvance;
|
| - expression = new BinaryExpression.full(expression, operator, parseUnaryExpression());
|
| + Statement parseIfStatement() {
|
| + Token ifKeyword = expect(Keyword.IF);
|
| + Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| + Expression condition = parseExpression2();
|
| + Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| + Statement thenStatement = parseStatement2();
|
| + Token elseKeyword = null;
|
| + Statement elseStatement = null;
|
| + if (matches(Keyword.ELSE)) {
|
| + elseKeyword = andAdvance;
|
| + elseStatement = parseStatement2();
|
| }
|
| - return expression;
|
| + return new IfStatement.full(ifKeyword, leftParenthesis, condition, rightParenthesis, thenStatement, elseKeyword, elseStatement);
|
| }
|
|
|
| /**
|
| - * Parse a class native clause.
|
| + * Parse an import directive.
|
| *
|
| * <pre>
|
| - * classNativeClause ::=
|
| - * 'native' name
|
| + * importDirective ::=
|
| + * metadata 'import' stringLiteral ('as' identifier)? combinator*';'
|
| * </pre>
|
| *
|
| - * @return the class native clause that was parsed
|
| + * @param commentAndMetadata the metadata to be associated with the directive
|
| + * @return the import directive that was parsed
|
| */
|
| - NativeClause parseNativeClause() {
|
| - Token keyword = andAdvance;
|
| - StringLiteral name = parseStringLiteral();
|
| - return new NativeClause.full(keyword, name);
|
| + ImportDirective parseImportDirective(CommentAndMetadata commentAndMetadata) {
|
| + Token importKeyword = expect(Keyword.IMPORT);
|
| + StringLiteral libraryUri = parseStringLiteral();
|
| + Token asToken = null;
|
| + SimpleIdentifier prefix = null;
|
| + if (matches(Keyword.AS)) {
|
| + asToken = andAdvance;
|
| + prefix = parseSimpleIdentifier();
|
| + }
|
| + List<Combinator> combinators = parseCombinators();
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new ImportDirective.full(commentAndMetadata.comment, commentAndMetadata.metadata, importKeyword, libraryUri, asToken, prefix, combinators, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse a new expression.
|
| + * Parse a list of initialized identifiers.
|
| *
|
| * <pre>
|
| - * newExpression ::=
|
| - * instanceCreationExpression
|
| - * </pre>
|
| + * ?? ::=
|
| + * 'static'? ('var' | type) initializedIdentifierList ';'
|
| + * | 'final' type? initializedIdentifierList ';'
|
| *
|
| - * @return the new expression that was parsed
|
| + * initializedIdentifierList ::=
|
| + * initializedIdentifier (',' initializedIdentifier)*
|
| + *
|
| + * initializedIdentifier ::=
|
| + * identifier ('=' expression)?
|
| + * </pre>
|
| + *
|
| + * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| + * declaration
|
| + * @param staticKeyword the static keyword, or `null` if the getter is not static
|
| + * @param keyword the token representing the 'final', 'const' or 'var' keyword, or `null` if
|
| + * there is no keyword
|
| + * @param type the type that has already been parsed, or `null` if 'var' was provided
|
| + * @return the getter that was parsed
|
| */
|
| - InstanceCreationExpression parseNewExpression() => parseInstanceCreationExpression(expect(Keyword.NEW));
|
| + FieldDeclaration parseInitializedIdentifierList(CommentAndMetadata commentAndMetadata, Token staticKeyword, Token keyword, TypeName type) {
|
| + VariableDeclarationList fieldList = parseVariableDeclarationList2(null, keyword, type);
|
| + return new FieldDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, staticKeyword, fieldList, expect2(TokenType.SEMICOLON));
|
| + }
|
|
|
| /**
|
| - * Parse a non-labeled statement.
|
| + * Parse an instance creation expression.
|
| *
|
| * <pre>
|
| - * nonLabeledStatement ::=
|
| - * block
|
| - * | assertStatement
|
| - * | breakStatement
|
| - * | continueStatement
|
| - * | doStatement
|
| - * | forStatement
|
| - * | ifStatement
|
| - * | returnStatement
|
| - * | switchStatement
|
| - * | tryStatement
|
| - * | whileStatement
|
| - * | variableDeclarationList ';'
|
| - * | expressionStatement
|
| - * | functionSignature functionBody
|
| + * instanceCreationExpression ::=
|
| + * ('new' | 'const') type ('.' identifier)? argumentList
|
| * </pre>
|
| *
|
| - * @return the non-labeled statement that was parsed
|
| + * @param keyword the 'new' or 'const' keyword that introduces the expression
|
| + * @return the instance creation expression that was parsed
|
| */
|
| - Statement parseNonLabeledStatement() {
|
| - CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| - if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| - if (matches4(peek(), TokenType.STRING)) {
|
| - Token afterString = skipStringLiteral(_currentToken.next);
|
| - if (afterString != null && identical(afterString.type, TokenType.COLON)) {
|
| - return new ExpressionStatement.full(parseExpression2(), expect2(TokenType.SEMICOLON));
|
| - }
|
| - }
|
| - return parseBlock();
|
| - } else if (matches5(TokenType.KEYWORD) && !((_currentToken as KeywordToken)).keyword.isPseudoKeyword) {
|
| - Keyword keyword = ((_currentToken as KeywordToken)).keyword;
|
| - if (identical(keyword, Keyword.ASSERT)) {
|
| - return parseAssertStatement();
|
| - } else if (identical(keyword, Keyword.BREAK)) {
|
| - return parseBreakStatement();
|
| - } else if (identical(keyword, Keyword.CONTINUE)) {
|
| - return parseContinueStatement();
|
| - } else if (identical(keyword, Keyword.DO)) {
|
| - return parseDoStatement();
|
| - } else if (identical(keyword, Keyword.FOR)) {
|
| - return parseForStatement();
|
| - } else if (identical(keyword, Keyword.IF)) {
|
| - return parseIfStatement();
|
| - } else if (identical(keyword, Keyword.RETHROW)) {
|
| - return new ExpressionStatement.full(parseRethrowExpression(), expect2(TokenType.SEMICOLON));
|
| - } else if (identical(keyword, Keyword.RETURN)) {
|
| - return parseReturnStatement();
|
| - } else if (identical(keyword, Keyword.SWITCH)) {
|
| - return parseSwitchStatement();
|
| - } else if (identical(keyword, Keyword.THROW)) {
|
| - return new ExpressionStatement.full(parseThrowExpression(), expect2(TokenType.SEMICOLON));
|
| - } else if (identical(keyword, Keyword.TRY)) {
|
| - return parseTryStatement();
|
| - } else if (identical(keyword, Keyword.WHILE)) {
|
| - return parseWhileStatement();
|
| - } else if (identical(keyword, Keyword.VAR) || identical(keyword, Keyword.FINAL)) {
|
| - return parseVariableDeclarationStatement(commentAndMetadata);
|
| - } else if (identical(keyword, Keyword.VOID)) {
|
| - TypeName returnType = parseReturnType();
|
| - if (matchesIdentifier() && matchesAny(peek(), [
|
| - TokenType.OPEN_PAREN,
|
| - TokenType.OPEN_CURLY_BRACKET,
|
| - TokenType.FUNCTION])) {
|
| - return parseFunctionDeclarationStatement2(commentAndMetadata, returnType);
|
| - } else {
|
| - if (matchesIdentifier()) {
|
| - if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
|
| - reportError(ParserErrorCode.VOID_VARIABLE, returnType, []);
|
| - return parseVariableDeclarationStatement(commentAndMetadata);
|
| - }
|
| - } else if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| - return parseVariableDeclarationStatement2(commentAndMetadata, null, returnType);
|
| - }
|
| - reportError8(ParserErrorCode.MISSING_STATEMENT, []);
|
| - return new EmptyStatement.full(createSyntheticToken2(TokenType.SEMICOLON));
|
| - }
|
| - } else if (identical(keyword, Keyword.CONST)) {
|
| - if (matchesAny(peek(), [
|
| - TokenType.LT,
|
| - TokenType.OPEN_CURLY_BRACKET,
|
| - TokenType.OPEN_SQUARE_BRACKET,
|
| - TokenType.INDEX])) {
|
| - return new ExpressionStatement.full(parseExpression2(), expect2(TokenType.SEMICOLON));
|
| - } else if (matches4(peek(), TokenType.IDENTIFIER)) {
|
| - Token afterType = skipTypeName(peek());
|
| - if (afterType != null) {
|
| - if (matches4(afterType, TokenType.OPEN_PAREN) || (matches4(afterType, TokenType.PERIOD) && matches4(afterType.next, TokenType.IDENTIFIER) && matches4(afterType.next.next, TokenType.OPEN_PAREN))) {
|
| - return new ExpressionStatement.full(parseExpression2(), expect2(TokenType.SEMICOLON));
|
| - }
|
| - }
|
| - }
|
| - return parseVariableDeclarationStatement(commentAndMetadata);
|
| - } else if (identical(keyword, Keyword.NEW) || identical(keyword, Keyword.TRUE) || identical(keyword, Keyword.FALSE) || identical(keyword, Keyword.NULL) || identical(keyword, Keyword.SUPER) || identical(keyword, Keyword.THIS)) {
|
| - return new ExpressionStatement.full(parseExpression2(), expect2(TokenType.SEMICOLON));
|
| - } else {
|
| - reportError8(ParserErrorCode.MISSING_STATEMENT, []);
|
| - return new EmptyStatement.full(createSyntheticToken2(TokenType.SEMICOLON));
|
| - }
|
| - } else if (matches5(TokenType.SEMICOLON)) {
|
| - return parseEmptyStatement();
|
| - } else if (isInitializedVariableDeclaration()) {
|
| - return parseVariableDeclarationStatement(commentAndMetadata);
|
| - } else if (isFunctionDeclaration()) {
|
| - return parseFunctionDeclarationStatement();
|
| - } else if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| - reportError8(ParserErrorCode.MISSING_STATEMENT, []);
|
| - return new EmptyStatement.full(createSyntheticToken2(TokenType.SEMICOLON));
|
| - } else {
|
| - return new ExpressionStatement.full(parseExpression2(), expect2(TokenType.SEMICOLON));
|
| - }
|
| + InstanceCreationExpression parseInstanceCreationExpression(Token keyword) {
|
| + ConstructorName constructorName = parseConstructorName();
|
| + ArgumentList argumentList = parseArgumentList();
|
| + return new InstanceCreationExpression.full(keyword, constructorName, argumentList);
|
| }
|
|
|
| /**
|
| - * Parse a normal formal parameter.
|
| + * Parse a library directive.
|
| *
|
| * <pre>
|
| - * normalFormalParameter ::=
|
| - * functionSignature
|
| - * | fieldFormalParameter
|
| - * | simpleFormalParameter
|
| - *
|
| - * functionSignature:
|
| - * metadata returnType? identifier formalParameterList
|
| - *
|
| - * fieldFormalParameter ::=
|
| - * metadata finalConstVarOrType? 'this' '.' identifier
|
| - *
|
| - * simpleFormalParameter ::=
|
| - * declaredIdentifier
|
| - * | metadata identifier
|
| + * libraryDirective ::=
|
| + * metadata 'library' identifier ';'
|
| * </pre>
|
| *
|
| - * @return the normal formal parameter that was parsed
|
| + * @param commentAndMetadata the metadata to be associated with the directive
|
| + * @return the library directive that was parsed
|
| */
|
| - NormalFormalParameter parseNormalFormalParameter() {
|
| - CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| - FinalConstVarOrType holder = parseFinalConstVarOrType(true);
|
| - Token thisKeyword = null;
|
| - Token period = null;
|
| - if (matches(Keyword.THIS)) {
|
| - thisKeyword = andAdvance;
|
| - period = expect2(TokenType.PERIOD);
|
| - }
|
| - SimpleIdentifier identifier = parseSimpleIdentifier();
|
| - if (matches5(TokenType.OPEN_PAREN)) {
|
| - FormalParameterList parameters = parseFormalParameterList();
|
| - if (thisKeyword == null) {
|
| - if (holder.keyword != null) {
|
| - reportError9(ParserErrorCode.FUNCTION_TYPED_PARAMETER_VAR, holder.keyword, []);
|
| - }
|
| - return new FunctionTypedFormalParameter.full(commentAndMetadata.comment, commentAndMetadata.metadata, holder.type, identifier, parameters);
|
| - } else {
|
| - return new FieldFormalParameter.full(commentAndMetadata.comment, commentAndMetadata.metadata, holder.keyword, holder.type, thisKeyword, period, identifier, parameters);
|
| - }
|
| - }
|
| - TypeName type = holder.type;
|
| - if (type != null) {
|
| - if (matches3(type.name.beginToken, Keyword.VOID)) {
|
| - reportError9(ParserErrorCode.VOID_PARAMETER, type.name.beginToken, []);
|
| - } else if (holder.keyword != null && matches3(holder.keyword, Keyword.VAR)) {
|
| - reportError9(ParserErrorCode.VAR_AND_TYPE, holder.keyword, []);
|
| - }
|
| - }
|
| - if (thisKeyword != null) {
|
| - return new FieldFormalParameter.full(commentAndMetadata.comment, commentAndMetadata.metadata, holder.keyword, holder.type, thisKeyword, period, identifier, null);
|
| - }
|
| - return new SimpleFormalParameter.full(commentAndMetadata.comment, commentAndMetadata.metadata, holder.keyword, holder.type, identifier);
|
| + LibraryDirective parseLibraryDirective(CommentAndMetadata commentAndMetadata) {
|
| + Token keyword = expect(Keyword.LIBRARY);
|
| + LibraryIdentifier libraryName = parseLibraryName(ParserErrorCode.MISSING_NAME_IN_LIBRARY_DIRECTIVE, keyword);
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new LibraryDirective.full(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, libraryName, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse an operator declaration.
|
| + * Parse a library name.
|
| *
|
| * <pre>
|
| - * operatorDeclaration ::=
|
| - * operatorSignature (';' | functionBody)
|
| - *
|
| - * operatorSignature ::=
|
| - * 'external'? returnType? 'operator' operator formalParameterList
|
| + * libraryName ::=
|
| + * libraryIdentifier
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| - * declaration
|
| - * @param externalKeyword the 'external' token
|
| - * @param the return type that has already been parsed, or `null` if there was no return
|
| - * type
|
| - * @return the operator declaration that was parsed
|
| + * @param missingNameError the error code to be used if the library name is missing
|
| + * @param missingNameToken the token associated with the error produced if the library name is
|
| + * missing
|
| + * @return the library name that was parsed
|
| */
|
| - MethodDeclaration parseOperator(CommentAndMetadata commentAndMetadata, Token externalKeyword, TypeName returnType) {
|
| - Token operatorKeyword;
|
| - if (matches(Keyword.OPERATOR)) {
|
| - operatorKeyword = andAdvance;
|
| + LibraryIdentifier parseLibraryName(ParserErrorCode missingNameError, Token missingNameToken) {
|
| + if (matchesIdentifier()) {
|
| + return parseLibraryIdentifier();
|
| + } else if (matches5(TokenType.STRING)) {
|
| + StringLiteral string = parseStringLiteral();
|
| + reportError(ParserErrorCode.NON_IDENTIFIER_LIBRARY_NAME, string, []);
|
| } else {
|
| - reportError9(ParserErrorCode.MISSING_KEYWORD_OPERATOR, _currentToken, []);
|
| - operatorKeyword = createSyntheticToken(Keyword.OPERATOR);
|
| - }
|
| - if (!_currentToken.isUserDefinableOperator) {
|
| - reportError8(ParserErrorCode.NON_USER_DEFINABLE_OPERATOR, [_currentToken.lexeme]);
|
| - }
|
| - SimpleIdentifier name = new SimpleIdentifier.full(andAdvance);
|
| - if (matches5(TokenType.EQ)) {
|
| - Token previous = _currentToken.previous;
|
| - if ((matches4(previous, TokenType.EQ_EQ) || matches4(previous, TokenType.BANG_EQ)) && _currentToken.offset == previous.offset + 2) {
|
| - reportError8(ParserErrorCode.INVALID_OPERATOR, ["${previous.lexeme}${_currentToken.lexeme}"]);
|
| - advance();
|
| - }
|
| - }
|
| - FormalParameterList parameters = parseFormalParameterList();
|
| - validateFormalParameterList(parameters);
|
| - FunctionBody body = parseFunctionBody(true, ParserErrorCode.MISSING_FUNCTION_BODY, false);
|
| - if (externalKeyword != null && body is! EmptyFunctionBody) {
|
| - reportError8(ParserErrorCode.EXTERNAL_OPERATOR_WITH_BODY, []);
|
| + reportError9(missingNameError, missingNameToken, []);
|
| }
|
| - return new MethodDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, null, returnType, null, operatorKeyword, name, parameters, body);
|
| + List<SimpleIdentifier> components = new List<SimpleIdentifier>();
|
| + components.add(createSyntheticIdentifier());
|
| + return new LibraryIdentifier.full(components);
|
| }
|
|
|
| /**
|
| - * Parse a return type if one is given, otherwise return `null` without advancing.
|
| - *
|
| - * @return the return type that was parsed
|
| - */
|
| - TypeName parseOptionalReturnType() {
|
| - if (matches(Keyword.VOID)) {
|
| - return parseReturnType();
|
| - } else if (matchesIdentifier() && !matches(Keyword.GET) && !matches(Keyword.SET) && !matches(Keyword.OPERATOR) && (matchesIdentifier2(peek()) || matches4(peek(), TokenType.LT))) {
|
| - return parseReturnType();
|
| - } else if (matchesIdentifier() && matches4(peek(), TokenType.PERIOD) && matchesIdentifier2(peek2(2)) && (matchesIdentifier2(peek2(3)) || matches4(peek2(3), TokenType.LT))) {
|
| - return parseReturnType();
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - /**
|
| - * Parse a part or part-of directive.
|
| + * Parse a list literal.
|
| *
|
| * <pre>
|
| - * partDirective ::=
|
| - * metadata 'part' stringLiteral ';'
|
| - *
|
| - * partOfDirective ::=
|
| - * metadata 'part' 'of' identifier ';'
|
| + * listLiteral ::=
|
| + * 'const'? typeArguments? '[' (expressionList ','?)? ']'
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the metadata to be associated with the directive
|
| - * @return the part or part-of directive that was parsed
|
| + * @param modifier the 'const' modifier appearing before the literal, or `null` if there is
|
| + * no modifier
|
| + * @param typeArguments the type arguments appearing before the literal, or `null` if there
|
| + * are no type arguments
|
| + * @return the list literal that was parsed
|
| */
|
| - Directive parsePartDirective(CommentAndMetadata commentAndMetadata) {
|
| - Token partKeyword = expect(Keyword.PART);
|
| - if (matches2(_OF)) {
|
| - Token ofKeyword = andAdvance;
|
| - LibraryIdentifier libraryName = parseLibraryName(ParserErrorCode.MISSING_NAME_IN_PART_OF_DIRECTIVE, ofKeyword);
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new PartOfDirective.full(commentAndMetadata.comment, commentAndMetadata.metadata, partKeyword, ofKeyword, libraryName, semicolon);
|
| + ListLiteral parseListLiteral(Token modifier, TypeArgumentList typeArguments) {
|
| + if (matches5(TokenType.INDEX)) {
|
| + BeginToken leftBracket = new BeginToken(TokenType.OPEN_SQUARE_BRACKET, _currentToken.offset);
|
| + Token rightBracket = new Token(TokenType.CLOSE_SQUARE_BRACKET, _currentToken.offset + 1);
|
| + leftBracket.endToken = rightBracket;
|
| + rightBracket.setNext(_currentToken.next);
|
| + leftBracket.setNext(rightBracket);
|
| + _currentToken.previous.setNext(leftBracket);
|
| + _currentToken = _currentToken.next;
|
| + return new ListLiteral.full(modifier, typeArguments, leftBracket, null, rightBracket);
|
| }
|
| - StringLiteral partUri = parseStringLiteral();
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new PartDirective.full(commentAndMetadata.comment, commentAndMetadata.metadata, partKeyword, partUri, semicolon);
|
| + Token leftBracket = expect2(TokenType.OPEN_SQUARE_BRACKET);
|
| + if (matches5(TokenType.CLOSE_SQUARE_BRACKET)) {
|
| + return new ListLiteral.full(modifier, typeArguments, leftBracket, null, andAdvance);
|
| + }
|
| + List<Expression> elements = new List<Expression>();
|
| + elements.add(parseExpression2());
|
| + while (optional(TokenType.COMMA)) {
|
| + if (matches5(TokenType.CLOSE_SQUARE_BRACKET)) {
|
| + return new ListLiteral.full(modifier, typeArguments, leftBracket, elements, andAdvance);
|
| + }
|
| + elements.add(parseExpression2());
|
| + }
|
| + Token rightBracket = expect2(TokenType.CLOSE_SQUARE_BRACKET);
|
| + return new ListLiteral.full(modifier, typeArguments, leftBracket, elements, rightBracket);
|
| }
|
|
|
| /**
|
| - * Parse a postfix expression.
|
| + * Parse a list or map literal.
|
| *
|
| * <pre>
|
| - * postfixExpression ::=
|
| - * assignableExpression postfixOperator
|
| - * | primary selector*
|
| - *
|
| - * selector ::=
|
| - * assignableSelector
|
| - * | argumentList
|
| + * listOrMapLiteral ::=
|
| + * listLiteral
|
| + * | mapLiteral
|
| * </pre>
|
| *
|
| - * @return the postfix expression that was parsed
|
| + * @param modifier the 'const' modifier appearing before the literal, or `null` if there is
|
| + * no modifier
|
| + * @return the list or map literal that was parsed
|
| */
|
| - Expression parsePostfixExpression() {
|
| - Expression operand = parseAssignableExpression(true);
|
| - if (matches5(TokenType.OPEN_SQUARE_BRACKET) || matches5(TokenType.PERIOD) || matches5(TokenType.OPEN_PAREN)) {
|
| - do {
|
| - if (matches5(TokenType.OPEN_PAREN)) {
|
| - ArgumentList argumentList = parseArgumentList();
|
| - if (operand is PropertyAccess) {
|
| - PropertyAccess access = operand as PropertyAccess;
|
| - operand = new MethodInvocation.full(access.target, access.operator, access.propertyName, argumentList);
|
| - } else {
|
| - operand = new FunctionExpressionInvocation.full(operand, argumentList);
|
| - }
|
| - } else {
|
| - operand = parseAssignableSelector(operand, true);
|
| - }
|
| - } while (matches5(TokenType.OPEN_SQUARE_BRACKET) || matches5(TokenType.PERIOD) || matches5(TokenType.OPEN_PAREN));
|
| - return operand;
|
| - }
|
| - if (!_currentToken.type.isIncrementOperator) {
|
| - return operand;
|
| + TypedLiteral parseListOrMapLiteral(Token modifier) {
|
| + TypeArgumentList typeArguments = null;
|
| + if (matches5(TokenType.LT)) {
|
| + typeArguments = parseTypeArgumentList();
|
| }
|
| - if (operand is Literal || operand is FunctionExpressionInvocation) {
|
| - reportError8(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, []);
|
| + if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| + return parseMapLiteral(modifier, typeArguments);
|
| + } else if (matches5(TokenType.OPEN_SQUARE_BRACKET) || matches5(TokenType.INDEX)) {
|
| + return parseListLiteral(modifier, typeArguments);
|
| }
|
| - Token operator = andAdvance;
|
| - return new PostfixExpression.full(operand, operator);
|
| + reportError8(ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL, []);
|
| + return new ListLiteral.full(modifier, typeArguments, createSyntheticToken2(TokenType.OPEN_SQUARE_BRACKET), null, createSyntheticToken2(TokenType.CLOSE_SQUARE_BRACKET));
|
| }
|
|
|
| /**
|
| - * Parse a prefixed identifier.
|
| + * Parse a logical and expression.
|
| *
|
| * <pre>
|
| - * prefixedIdentifier ::=
|
| - * identifier ('.' identifier)?
|
| + * logicalAndExpression ::=
|
| + * equalityExpression ('&&' equalityExpression)*
|
| * </pre>
|
| *
|
| - * @return the prefixed identifier that was parsed
|
| + * @return the logical and expression that was parsed
|
| */
|
| - Identifier parsePrefixedIdentifier() {
|
| - SimpleIdentifier qualifier = parseSimpleIdentifier();
|
| - if (!matches5(TokenType.PERIOD)) {
|
| - return qualifier;
|
| + Expression parseLogicalAndExpression() {
|
| + Expression expression = parseEqualityExpression();
|
| + while (matches5(TokenType.AMPERSAND_AMPERSAND)) {
|
| + Token operator = andAdvance;
|
| + expression = new BinaryExpression.full(expression, operator, parseEqualityExpression());
|
| }
|
| - Token period = andAdvance;
|
| - SimpleIdentifier qualified = parseSimpleIdentifier();
|
| - return new PrefixedIdentifier.full(qualifier, period, qualified);
|
| + return expression;
|
| }
|
|
|
| /**
|
| - * Parse a primary expression.
|
| + * Parse a map literal.
|
| *
|
| * <pre>
|
| - * primary ::=
|
| - * thisExpression
|
| - * | 'super' assignableSelector
|
| - * | functionExpression
|
| - * | literal
|
| - * | identifier
|
| - * | newExpression
|
| - * | constObjectExpression
|
| - * | '(' expression ')'
|
| - * | argumentDefinitionTest
|
| - *
|
| - * literal ::=
|
| - * nullLiteral
|
| - * | booleanLiteral
|
| - * | numericLiteral
|
| - * | stringLiteral
|
| - * | symbolLiteral
|
| - * | mapLiteral
|
| - * | listLiteral
|
| + * mapLiteral ::=
|
| + * 'const'? typeArguments? '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}'
|
| * </pre>
|
| *
|
| - * @return the primary expression that was parsed
|
| + * @param modifier the 'const' modifier appearing before the literal, or `null` if there is
|
| + * no modifier
|
| + * @param typeArguments the type arguments that were declared, or `null` if there are no
|
| + * type arguments
|
| + * @return the map literal that was parsed
|
| */
|
| - Expression parsePrimaryExpression() {
|
| - if (matches(Keyword.THIS)) {
|
| - return new ThisExpression.full(andAdvance);
|
| - } else if (matches(Keyword.SUPER)) {
|
| - return parseAssignableSelector(new SuperExpression.full(andAdvance), false);
|
| - } else if (matches(Keyword.NULL)) {
|
| - return new NullLiteral.full(andAdvance);
|
| - } else if (matches(Keyword.FALSE)) {
|
| - return new BooleanLiteral.full(andAdvance, false);
|
| - } else if (matches(Keyword.TRUE)) {
|
| - return new BooleanLiteral.full(andAdvance, true);
|
| - } else if (matches5(TokenType.DOUBLE)) {
|
| - Token token = andAdvance;
|
| - double value = 0.0;
|
| - try {
|
| - value = double.parse(token.lexeme);
|
| - } on FormatException catch (exception) {
|
| - }
|
| - return new DoubleLiteral.full(token, value);
|
| - } else if (matches5(TokenType.HEXADECIMAL)) {
|
| - Token token = andAdvance;
|
| - int value = null;
|
| - try {
|
| - value = int.parse(token.lexeme.substring(2), radix: 16);
|
| - } on FormatException catch (exception) {
|
| - }
|
| - return new IntegerLiteral.full(token, value);
|
| - } else if (matches5(TokenType.INT)) {
|
| - Token token = andAdvance;
|
| - int value = null;
|
| - try {
|
| - value = int.parse(token.lexeme);
|
| - } on FormatException catch (exception) {
|
| - }
|
| - return new IntegerLiteral.full(token, value);
|
| - } else if (matches5(TokenType.STRING)) {
|
| - return parseStringLiteral();
|
| - } else if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| - return parseMapLiteral(null, null);
|
| - } else if (matches5(TokenType.OPEN_SQUARE_BRACKET) || matches5(TokenType.INDEX)) {
|
| - return parseListLiteral(null, null);
|
| - } else if (matchesIdentifier()) {
|
| - return parsePrefixedIdentifier();
|
| - } else if (matches(Keyword.NEW)) {
|
| - return parseNewExpression();
|
| - } else if (matches(Keyword.CONST)) {
|
| - return parseConstExpression();
|
| - } else if (matches5(TokenType.OPEN_PAREN)) {
|
| - if (isFunctionExpression(_currentToken)) {
|
| - return parseFunctionExpression();
|
| + MapLiteral parseMapLiteral(Token modifier, TypeArgumentList typeArguments) {
|
| + Token leftBracket = expect2(TokenType.OPEN_CURLY_BRACKET);
|
| + List<MapLiteralEntry> entries = new List<MapLiteralEntry>();
|
| + if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| + return new MapLiteral.full(modifier, typeArguments, leftBracket, entries, andAdvance);
|
| + }
|
| + entries.add(parseMapLiteralEntry());
|
| + while (optional(TokenType.COMMA)) {
|
| + if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| + return new MapLiteral.full(modifier, typeArguments, leftBracket, entries, andAdvance);
|
| }
|
| - Token leftParenthesis = andAdvance;
|
| - Expression expression = parseExpression2();
|
| - Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| - return new ParenthesizedExpression.full(leftParenthesis, expression, rightParenthesis);
|
| - } else if (matches5(TokenType.LT)) {
|
| - return parseListOrMapLiteral(null);
|
| - } else if (matches5(TokenType.QUESTION)) {
|
| - return parseArgumentDefinitionTest();
|
| - } else if (matches(Keyword.VOID)) {
|
| - reportError8(ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
|
| - advance();
|
| - return parsePrimaryExpression();
|
| - } else if (matches5(TokenType.HASH)) {
|
| - return parseSymbolLiteral();
|
| - } else {
|
| - reportError8(ParserErrorCode.MISSING_IDENTIFIER, []);
|
| - return createSyntheticIdentifier();
|
| + entries.add(parseMapLiteralEntry());
|
| }
|
| + Token rightBracket = expect2(TokenType.CLOSE_CURLY_BRACKET);
|
| + return new MapLiteral.full(modifier, typeArguments, leftBracket, entries, rightBracket);
|
| }
|
|
|
| /**
|
| - * Parse a redirecting constructor invocation.
|
| + * Parse a method declaration.
|
| *
|
| * <pre>
|
| - * redirectingConstructorInvocation ::=
|
| - * 'this' ('.' identifier)? arguments
|
| + * functionDeclaration ::=
|
| + * 'external'? 'static'? functionSignature functionBody
|
| + * | 'external'? functionSignature ';'
|
| * </pre>
|
| *
|
| - * @return the redirecting constructor invocation that was parsed
|
| + * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| + * declaration
|
| + * @param externalKeyword the 'external' token
|
| + * @param staticKeyword the static keyword, or `null` if the getter is not static
|
| + * @param returnType the return type of the method
|
| + * @return the method declaration that was parsed
|
| */
|
| - RedirectingConstructorInvocation parseRedirectingConstructorInvocation() {
|
| - Token keyword = expect(Keyword.THIS);
|
| - Token period = null;
|
| - SimpleIdentifier constructorName = null;
|
| - if (matches5(TokenType.PERIOD)) {
|
| - period = andAdvance;
|
| - constructorName = parseSimpleIdentifier();
|
| - }
|
| - ArgumentList argumentList = parseArgumentList();
|
| - return new RedirectingConstructorInvocation.full(keyword, period, constructorName, argumentList);
|
| + MethodDeclaration parseMethodDeclaration(CommentAndMetadata commentAndMetadata, Token externalKeyword, Token staticKeyword, TypeName returnType) {
|
| + SimpleIdentifier methodName = parseSimpleIdentifier();
|
| + FormalParameterList parameters = parseFormalParameterList();
|
| + validateFormalParameterList(parameters);
|
| + return parseMethodDeclaration2(commentAndMetadata, externalKeyword, staticKeyword, returnType, methodName, parameters);
|
| }
|
|
|
| /**
|
| - * Parse a relational expression.
|
| + * Parse a method declaration.
|
| *
|
| * <pre>
|
| - * relationalExpression ::=
|
| - * bitwiseOrExpression ('is' '!'? type | 'as' type | relationalOperator bitwiseOrExpression)?
|
| - * | 'super' relationalOperator bitwiseOrExpression
|
| + * functionDeclaration ::=
|
| + * ('external' 'static'?)? functionSignature functionBody
|
| + * | 'external'? functionSignature ';'
|
| * </pre>
|
| *
|
| - * @return the relational expression that was parsed
|
| + * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| + * declaration
|
| + * @param externalKeyword the 'external' token
|
| + * @param staticKeyword the static keyword, or `null` if the getter is not static
|
| + * @param returnType the return type of the method
|
| + * @param name the name of the method
|
| + * @param parameters the parameters to the method
|
| + * @return the method declaration that was parsed
|
| */
|
| - Expression parseRelationalExpression() {
|
| - if (matches(Keyword.SUPER) && _currentToken.next.type.isRelationalOperator) {
|
| - Expression expression = new SuperExpression.full(andAdvance);
|
| - Token operator = andAdvance;
|
| - expression = new BinaryExpression.full(expression, operator, parseBitwiseOrExpression());
|
| - return expression;
|
| - }
|
| - Expression expression = parseBitwiseOrExpression();
|
| - if (matches(Keyword.AS)) {
|
| - Token asOperator = andAdvance;
|
| - expression = new AsExpression.full(expression, asOperator, parseTypeName());
|
| - } else if (matches(Keyword.IS)) {
|
| - Token isOperator = andAdvance;
|
| - Token notOperator = null;
|
| - if (matches5(TokenType.BANG)) {
|
| - notOperator = andAdvance;
|
| + MethodDeclaration parseMethodDeclaration2(CommentAndMetadata commentAndMetadata, Token externalKeyword, Token staticKeyword, TypeName returnType, SimpleIdentifier name, FormalParameterList parameters) {
|
| + FunctionBody body = parseFunctionBody(externalKeyword != null || staticKeyword == null, ParserErrorCode.MISSING_FUNCTION_BODY, false);
|
| + if (externalKeyword != null) {
|
| + if (body is! EmptyFunctionBody) {
|
| + reportError(ParserErrorCode.EXTERNAL_METHOD_WITH_BODY, body, []);
|
| + }
|
| + } else if (staticKeyword != null) {
|
| + if (body is EmptyFunctionBody) {
|
| + reportError(ParserErrorCode.ABSTRACT_STATIC_METHOD, body, []);
|
| }
|
| - expression = new IsExpression.full(expression, isOperator, notOperator, parseTypeName());
|
| - } else if (_currentToken.type.isRelationalOperator) {
|
| - Token operator = andAdvance;
|
| - expression = new BinaryExpression.full(expression, operator, parseBitwiseOrExpression());
|
| }
|
| - return expression;
|
| + return new MethodDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, staticKeyword, returnType, null, null, name, parameters, body);
|
| }
|
|
|
| /**
|
| - * Parse a rethrow expression.
|
| - *
|
| - * <pre>
|
| - * rethrowExpression ::=
|
| - * 'rethrow'
|
| - * </pre>
|
| - *
|
| - * @return the rethrow expression that was parsed
|
| - */
|
| - Expression parseRethrowExpression() => new RethrowExpression.full(expect(Keyword.RETHROW));
|
| -
|
| - /**
|
| - * Parse a return statement.
|
| + * Parse the modifiers preceding a declaration. This method allows the modifiers to appear in any
|
| + * order but does generate errors for duplicated modifiers. Checks for other problems, such as
|
| + * having the modifiers appear in the wrong order or specifying both 'const' and 'final', are
|
| + * reported in one of the methods whose name is prefixed with `validateModifiersFor`.
|
| *
|
| * <pre>
|
| - * returnStatement ::=
|
| - * 'return' expression? ';'
|
| + * modifiers ::=
|
| + * ('abstract' | 'const' | 'external' | 'factory' | 'final' | 'static' | 'var')*
|
| * </pre>
|
| *
|
| - * @return the return statement that was parsed
|
| + * @return the modifiers that were parsed
|
| */
|
| - Statement parseReturnStatement() {
|
| - Token returnKeyword = expect(Keyword.RETURN);
|
| - if (matches5(TokenType.SEMICOLON)) {
|
| - return new ReturnStatement.full(returnKeyword, null, andAdvance);
|
| + Modifiers parseModifiers() {
|
| + Modifiers modifiers = new Modifiers();
|
| + bool progress = true;
|
| + while (progress) {
|
| + if (matches(Keyword.ABSTRACT) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| + if (modifiers.abstractKeyword != null) {
|
| + reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| + advance();
|
| + } else {
|
| + modifiers.abstractKeyword = andAdvance;
|
| + }
|
| + } else if (matches(Keyword.CONST)) {
|
| + if (modifiers.constKeyword != null) {
|
| + reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| + advance();
|
| + } else {
|
| + modifiers.constKeyword = andAdvance;
|
| + }
|
| + } else if (matches(Keyword.EXTERNAL) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| + if (modifiers.externalKeyword != null) {
|
| + reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| + advance();
|
| + } else {
|
| + modifiers.externalKeyword = andAdvance;
|
| + }
|
| + } else if (matches(Keyword.FACTORY) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| + if (modifiers.factoryKeyword != null) {
|
| + reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| + advance();
|
| + } else {
|
| + modifiers.factoryKeyword = andAdvance;
|
| + }
|
| + } else if (matches(Keyword.FINAL)) {
|
| + if (modifiers.finalKeyword != null) {
|
| + reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| + advance();
|
| + } else {
|
| + modifiers.finalKeyword = andAdvance;
|
| + }
|
| + } else if (matches(Keyword.STATIC) && !matches4(peek(), TokenType.PERIOD) && !matches4(peek(), TokenType.LT)) {
|
| + if (modifiers.staticKeyword != null) {
|
| + reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| + advance();
|
| + } else {
|
| + modifiers.staticKeyword = andAdvance;
|
| + }
|
| + } else if (matches(Keyword.VAR)) {
|
| + if (modifiers.varKeyword != null) {
|
| + reportError8(ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
|
| + advance();
|
| + } else {
|
| + modifiers.varKeyword = andAdvance;
|
| + }
|
| + } else {
|
| + progress = false;
|
| + }
|
| }
|
| - Expression expression = parseExpression2();
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new ReturnStatement.full(returnKeyword, expression, semicolon);
|
| + return modifiers;
|
| }
|
|
|
| /**
|
| - * Parse a return type.
|
| + * Parse a multiplicative expression.
|
| *
|
| * <pre>
|
| - * returnType ::=
|
| - * 'void'
|
| - * | type
|
| + * multiplicativeExpression ::=
|
| + * unaryExpression (multiplicativeOperator unaryExpression)*
|
| + * | 'super' (multiplicativeOperator unaryExpression)+
|
| * </pre>
|
| *
|
| - * @return the return type that was parsed
|
| + * @return the multiplicative expression that was parsed
|
| */
|
| - TypeName parseReturnType() {
|
| - if (matches(Keyword.VOID)) {
|
| - return new TypeName.full(new SimpleIdentifier.full(andAdvance), null);
|
| + Expression parseMultiplicativeExpression() {
|
| + Expression expression;
|
| + if (matches(Keyword.SUPER) && _currentToken.next.type.isMultiplicativeOperator) {
|
| + expression = new SuperExpression.full(andAdvance);
|
| } else {
|
| - return parseTypeName();
|
| + expression = parseUnaryExpression();
|
| + }
|
| + while (_currentToken.type.isMultiplicativeOperator) {
|
| + Token operator = andAdvance;
|
| + expression = new BinaryExpression.full(expression, operator, parseUnaryExpression());
|
| }
|
| + return expression;
|
| }
|
|
|
| /**
|
| - * Parse a setter.
|
| + * Parse a class native clause.
|
| *
|
| * <pre>
|
| - * setter ::=
|
| - * setterSignature functionBody?
|
| - *
|
| - * setterSignature ::=
|
| - * 'external'? 'static'? returnType? 'set' identifier formalParameterList
|
| + * classNativeClause ::=
|
| + * 'native' name
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| - * declaration
|
| - * @param externalKeyword the 'external' token
|
| - * @param staticKeyword the static keyword, or `null` if the setter is not static
|
| - * @param the return type that has already been parsed, or `null` if there was no return
|
| - * type
|
| - * @return the setter that was parsed
|
| + * @return the class native clause that was parsed
|
| */
|
| - MethodDeclaration parseSetter(CommentAndMetadata commentAndMetadata, Token externalKeyword, Token staticKeyword, TypeName returnType) {
|
| - Token propertyKeyword = expect(Keyword.SET);
|
| - SimpleIdentifier name = parseSimpleIdentifier();
|
| - FormalParameterList parameters = parseFormalParameterList();
|
| - validateFormalParameterList(parameters);
|
| - FunctionBody body = parseFunctionBody(externalKeyword != null || staticKeyword == null, ParserErrorCode.STATIC_SETTER_WITHOUT_BODY, false);
|
| - if (externalKeyword != null && body is! EmptyFunctionBody) {
|
| - reportError8(ParserErrorCode.EXTERNAL_SETTER_WITH_BODY, []);
|
| - }
|
| - return new MethodDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, staticKeyword, returnType, propertyKeyword, null, name, parameters, body);
|
| + NativeClause parseNativeClause() {
|
| + Token keyword = andAdvance;
|
| + StringLiteral name = parseStringLiteral();
|
| + return new NativeClause.full(keyword, name);
|
| }
|
|
|
| /**
|
| - * Parse a shift expression.
|
| + * Parse a new expression.
|
| *
|
| * <pre>
|
| - * shiftExpression ::=
|
| - * additiveExpression (shiftOperator additiveExpression)*
|
| - * | 'super' (shiftOperator additiveExpression)+
|
| + * newExpression ::=
|
| + * instanceCreationExpression
|
| * </pre>
|
| *
|
| - * @return the shift expression that was parsed
|
| + * @return the new expression that was parsed
|
| */
|
| - Expression parseShiftExpression() {
|
| - Expression expression;
|
| - if (matches(Keyword.SUPER) && _currentToken.next.type.isShiftOperator) {
|
| - expression = new SuperExpression.full(andAdvance);
|
| - } else {
|
| - expression = parseAdditiveExpression();
|
| - }
|
| - while (_currentToken.type.isShiftOperator) {
|
| - Token operator = andAdvance;
|
| - expression = new BinaryExpression.full(expression, operator, parseAdditiveExpression());
|
| - }
|
| - return expression;
|
| - }
|
| + InstanceCreationExpression parseNewExpression() => parseInstanceCreationExpression(expect(Keyword.NEW));
|
|
|
| /**
|
| - * Parse a simple identifier.
|
| + * Parse a non-labeled statement.
|
| *
|
| * <pre>
|
| - * identifier ::=
|
| - * IDENTIFIER
|
| + * nonLabeledStatement ::=
|
| + * block
|
| + * | assertStatement
|
| + * | breakStatement
|
| + * | continueStatement
|
| + * | doStatement
|
| + * | forStatement
|
| + * | ifStatement
|
| + * | returnStatement
|
| + * | switchStatement
|
| + * | tryStatement
|
| + * | whileStatement
|
| + * | variableDeclarationList ';'
|
| + * | expressionStatement
|
| + * | functionSignature functionBody
|
| * </pre>
|
| *
|
| - * @return the simple identifier that was parsed
|
| + * @return the non-labeled statement that was parsed
|
| */
|
| - SimpleIdentifier parseSimpleIdentifier() {
|
| - if (matchesIdentifier()) {
|
| - return new SimpleIdentifier.full(andAdvance);
|
| - }
|
| - reportError8(ParserErrorCode.MISSING_IDENTIFIER, []);
|
| - return createSyntheticIdentifier();
|
| - }
|
| -
|
| - /**
|
| - * Parse a statement.
|
| - *
|
| - * <pre>
|
| - * statement ::=
|
| - * label* nonLabeledStatement
|
| - * </pre>
|
| - *
|
| - * @return the statement that was parsed
|
| - */
|
| - Statement parseStatement2() {
|
| - List<Label> labels = new List<Label>();
|
| - while (matchesIdentifier() && matches4(peek(), TokenType.COLON)) {
|
| - SimpleIdentifier label = parseSimpleIdentifier();
|
| - Token colon = expect2(TokenType.COLON);
|
| - labels.add(new Label.full(label, colon));
|
| - }
|
| - Statement statement = parseNonLabeledStatement();
|
| - if (labels.isEmpty) {
|
| - return statement;
|
| + Statement parseNonLabeledStatement() {
|
| + CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| + if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| + if (matches4(peek(), TokenType.STRING)) {
|
| + Token afterString = skipStringLiteral(_currentToken.next);
|
| + if (afterString != null && identical(afterString.type, TokenType.COLON)) {
|
| + return new ExpressionStatement.full(parseExpression2(), expect2(TokenType.SEMICOLON));
|
| + }
|
| + }
|
| + return parseBlock();
|
| + } else if (matches5(TokenType.KEYWORD) && !((_currentToken as KeywordToken)).keyword.isPseudoKeyword) {
|
| + Keyword keyword = ((_currentToken as KeywordToken)).keyword;
|
| + if (identical(keyword, Keyword.ASSERT)) {
|
| + return parseAssertStatement();
|
| + } else if (identical(keyword, Keyword.BREAK)) {
|
| + return parseBreakStatement();
|
| + } else if (identical(keyword, Keyword.CONTINUE)) {
|
| + return parseContinueStatement();
|
| + } else if (identical(keyword, Keyword.DO)) {
|
| + return parseDoStatement();
|
| + } else if (identical(keyword, Keyword.FOR)) {
|
| + return parseForStatement();
|
| + } else if (identical(keyword, Keyword.IF)) {
|
| + return parseIfStatement();
|
| + } else if (identical(keyword, Keyword.RETHROW)) {
|
| + return new ExpressionStatement.full(parseRethrowExpression(), expect2(TokenType.SEMICOLON));
|
| + } else if (identical(keyword, Keyword.RETURN)) {
|
| + return parseReturnStatement();
|
| + } else if (identical(keyword, Keyword.SWITCH)) {
|
| + return parseSwitchStatement();
|
| + } else if (identical(keyword, Keyword.THROW)) {
|
| + return new ExpressionStatement.full(parseThrowExpression(), expect2(TokenType.SEMICOLON));
|
| + } else if (identical(keyword, Keyword.TRY)) {
|
| + return parseTryStatement();
|
| + } else if (identical(keyword, Keyword.WHILE)) {
|
| + return parseWhileStatement();
|
| + } else if (identical(keyword, Keyword.VAR) || identical(keyword, Keyword.FINAL)) {
|
| + return parseVariableDeclarationStatement(commentAndMetadata);
|
| + } else if (identical(keyword, Keyword.VOID)) {
|
| + TypeName returnType = parseReturnType();
|
| + if (matchesIdentifier() && matchesAny(peek(), [
|
| + TokenType.OPEN_PAREN,
|
| + TokenType.OPEN_CURLY_BRACKET,
|
| + TokenType.FUNCTION])) {
|
| + return parseFunctionDeclarationStatement2(commentAndMetadata, returnType);
|
| + } else {
|
| + if (matchesIdentifier()) {
|
| + if (matchesAny(peek(), [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) {
|
| + reportError(ParserErrorCode.VOID_VARIABLE, returnType, []);
|
| + return parseVariableDeclarationStatement(commentAndMetadata);
|
| + }
|
| + } else if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| + return parseVariableDeclarationStatement2(commentAndMetadata, null, returnType);
|
| + }
|
| + reportError8(ParserErrorCode.MISSING_STATEMENT, []);
|
| + return new EmptyStatement.full(createSyntheticToken2(TokenType.SEMICOLON));
|
| + }
|
| + } else if (identical(keyword, Keyword.CONST)) {
|
| + if (matchesAny(peek(), [
|
| + TokenType.LT,
|
| + TokenType.OPEN_CURLY_BRACKET,
|
| + TokenType.OPEN_SQUARE_BRACKET,
|
| + TokenType.INDEX])) {
|
| + return new ExpressionStatement.full(parseExpression2(), expect2(TokenType.SEMICOLON));
|
| + } else if (matches4(peek(), TokenType.IDENTIFIER)) {
|
| + Token afterType = skipTypeName(peek());
|
| + if (afterType != null) {
|
| + if (matches4(afterType, TokenType.OPEN_PAREN) || (matches4(afterType, TokenType.PERIOD) && matches4(afterType.next, TokenType.IDENTIFIER) && matches4(afterType.next.next, TokenType.OPEN_PAREN))) {
|
| + return new ExpressionStatement.full(parseExpression2(), expect2(TokenType.SEMICOLON));
|
| + }
|
| + }
|
| + }
|
| + return parseVariableDeclarationStatement(commentAndMetadata);
|
| + } else if (identical(keyword, Keyword.NEW) || identical(keyword, Keyword.TRUE) || identical(keyword, Keyword.FALSE) || identical(keyword, Keyword.NULL) || identical(keyword, Keyword.SUPER) || identical(keyword, Keyword.THIS)) {
|
| + return new ExpressionStatement.full(parseExpression2(), expect2(TokenType.SEMICOLON));
|
| + } else {
|
| + reportError8(ParserErrorCode.MISSING_STATEMENT, []);
|
| + return new EmptyStatement.full(createSyntheticToken2(TokenType.SEMICOLON));
|
| + }
|
| + } else if (matches5(TokenType.SEMICOLON)) {
|
| + return parseEmptyStatement();
|
| + } else if (isInitializedVariableDeclaration()) {
|
| + return parseVariableDeclarationStatement(commentAndMetadata);
|
| + } else if (isFunctionDeclaration()) {
|
| + return parseFunctionDeclarationStatement();
|
| + } else if (matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| + reportError8(ParserErrorCode.MISSING_STATEMENT, []);
|
| + return new EmptyStatement.full(createSyntheticToken2(TokenType.SEMICOLON));
|
| + } else {
|
| + return new ExpressionStatement.full(parseExpression2(), expect2(TokenType.SEMICOLON));
|
| }
|
| - return new LabeledStatement.full(labels, statement);
|
| }
|
|
|
| /**
|
| - * Parse a list of statements within a switch statement.
|
| + * Parse an operator declaration.
|
| *
|
| * <pre>
|
| - * statements ::=
|
| - * statement*
|
| + * operatorDeclaration ::=
|
| + * operatorSignature (';' | functionBody)
|
| + *
|
| + * operatorSignature ::=
|
| + * 'external'? returnType? 'operator' operator formalParameterList
|
| * </pre>
|
| *
|
| - * @return the statements that were parsed
|
| + * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| + * declaration
|
| + * @param externalKeyword the 'external' token
|
| + * @param the return type that has already been parsed, or `null` if there was no return
|
| + * type
|
| + * @return the operator declaration that was parsed
|
| */
|
| - List<Statement> parseStatements2() {
|
| - List<Statement> statements = new List<Statement>();
|
| - Token statementStart = _currentToken;
|
| - while (!matches5(TokenType.EOF) && !matches5(TokenType.CLOSE_CURLY_BRACKET) && !isSwitchMember()) {
|
| - statements.add(parseStatement2());
|
| - if (identical(_currentToken, statementStart)) {
|
| - reportError9(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
|
| + MethodDeclaration parseOperator(CommentAndMetadata commentAndMetadata, Token externalKeyword, TypeName returnType) {
|
| + Token operatorKeyword;
|
| + if (matches(Keyword.OPERATOR)) {
|
| + operatorKeyword = andAdvance;
|
| + } else {
|
| + reportError9(ParserErrorCode.MISSING_KEYWORD_OPERATOR, _currentToken, []);
|
| + operatorKeyword = createSyntheticToken(Keyword.OPERATOR);
|
| + }
|
| + if (!_currentToken.isUserDefinableOperator) {
|
| + reportError8(ParserErrorCode.NON_USER_DEFINABLE_OPERATOR, [_currentToken.lexeme]);
|
| + }
|
| + SimpleIdentifier name = new SimpleIdentifier.full(andAdvance);
|
| + if (matches5(TokenType.EQ)) {
|
| + Token previous = _currentToken.previous;
|
| + if ((matches4(previous, TokenType.EQ_EQ) || matches4(previous, TokenType.BANG_EQ)) && _currentToken.offset == previous.offset + 2) {
|
| + reportError8(ParserErrorCode.INVALID_OPERATOR, ["${previous.lexeme}${_currentToken.lexeme}"]);
|
| advance();
|
| }
|
| - statementStart = _currentToken;
|
| }
|
| - return statements;
|
| + FormalParameterList parameters = parseFormalParameterList();
|
| + validateFormalParameterList(parameters);
|
| + FunctionBody body = parseFunctionBody(true, ParserErrorCode.MISSING_FUNCTION_BODY, false);
|
| + if (externalKeyword != null && body is! EmptyFunctionBody) {
|
| + reportError8(ParserErrorCode.EXTERNAL_OPERATOR_WITH_BODY, []);
|
| + }
|
| + return new MethodDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, null, returnType, null, operatorKeyword, name, parameters, body);
|
| }
|
|
|
| /**
|
| - * Parse a string literal that contains interpolations.
|
| + * Parse a return type if one is given, otherwise return `null` without advancing.
|
| *
|
| - * @return the string literal that was parsed
|
| + * @return the return type that was parsed
|
| */
|
| - StringInterpolation parseStringInterpolation(Token string) {
|
| - List<InterpolationElement> elements = new List<InterpolationElement>();
|
| - bool hasMore = matches5(TokenType.STRING_INTERPOLATION_EXPRESSION) || matches5(TokenType.STRING_INTERPOLATION_IDENTIFIER);
|
| - elements.add(new InterpolationString.full(string, computeStringValue(string.lexeme, true, !hasMore)));
|
| - while (hasMore) {
|
| - if (matches5(TokenType.STRING_INTERPOLATION_EXPRESSION)) {
|
| - Token openToken = andAdvance;
|
| - Expression expression = parseExpression2();
|
| - Token rightBracket = expect2(TokenType.CLOSE_CURLY_BRACKET);
|
| - elements.add(new InterpolationExpression.full(openToken, expression, rightBracket));
|
| - } else {
|
| - Token openToken = andAdvance;
|
| - Expression expression = null;
|
| - if (matches(Keyword.THIS)) {
|
| - expression = new ThisExpression.full(andAdvance);
|
| - } else {
|
| - expression = parseSimpleIdentifier();
|
| - }
|
| - elements.add(new InterpolationExpression.full(openToken, expression, null));
|
| - }
|
| - if (matches5(TokenType.STRING)) {
|
| - string = andAdvance;
|
| - hasMore = matches5(TokenType.STRING_INTERPOLATION_EXPRESSION) || matches5(TokenType.STRING_INTERPOLATION_IDENTIFIER);
|
| - elements.add(new InterpolationString.full(string, computeStringValue(string.lexeme, false, !hasMore)));
|
| - }
|
| + TypeName parseOptionalReturnType() {
|
| + if (matches(Keyword.VOID)) {
|
| + return parseReturnType();
|
| + } else if (matchesIdentifier() && !matches(Keyword.GET) && !matches(Keyword.SET) && !matches(Keyword.OPERATOR) && (matchesIdentifier2(peek()) || matches4(peek(), TokenType.LT))) {
|
| + return parseReturnType();
|
| + } else if (matchesIdentifier() && matches4(peek(), TokenType.PERIOD) && matchesIdentifier2(peek2(2)) && (matchesIdentifier2(peek2(3)) || matches4(peek2(3), TokenType.LT))) {
|
| + return parseReturnType();
|
| }
|
| - return new StringInterpolation.full(elements);
|
| + return null;
|
| }
|
|
|
| /**
|
| - * Parse a string literal.
|
| + * Parse a part or part-of directive.
|
| *
|
| * <pre>
|
| - * stringLiteral ::=
|
| - * MULTI_LINE_STRING+
|
| - * | SINGLE_LINE_STRING+
|
| + * partDirective ::=
|
| + * metadata 'part' stringLiteral ';'
|
| + *
|
| + * partOfDirective ::=
|
| + * metadata 'part' 'of' identifier ';'
|
| * </pre>
|
| *
|
| - * @return the string literal that was parsed
|
| + * @param commentAndMetadata the metadata to be associated with the directive
|
| + * @return the part or part-of directive that was parsed
|
| */
|
| - StringLiteral parseStringLiteral() {
|
| - List<StringLiteral> strings = new List<StringLiteral>();
|
| - while (matches5(TokenType.STRING)) {
|
| - Token string = andAdvance;
|
| - if (matches5(TokenType.STRING_INTERPOLATION_EXPRESSION) || matches5(TokenType.STRING_INTERPOLATION_IDENTIFIER)) {
|
| - strings.add(parseStringInterpolation(string));
|
| - } else {
|
| - strings.add(new SimpleStringLiteral.full(string, computeStringValue(string.lexeme, true, true)));
|
| - }
|
| - }
|
| - if (strings.length < 1) {
|
| - reportError8(ParserErrorCode.EXPECTED_STRING_LITERAL, []);
|
| - return createSyntheticStringLiteral();
|
| - } else if (strings.length == 1) {
|
| - return strings[0];
|
| - } else {
|
| - return new AdjacentStrings.full(strings);
|
| + Directive parsePartDirective(CommentAndMetadata commentAndMetadata) {
|
| + Token partKeyword = expect(Keyword.PART);
|
| + if (matches2(_OF)) {
|
| + Token ofKeyword = andAdvance;
|
| + LibraryIdentifier libraryName = parseLibraryName(ParserErrorCode.MISSING_NAME_IN_PART_OF_DIRECTIVE, ofKeyword);
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new PartOfDirective.full(commentAndMetadata.comment, commentAndMetadata.metadata, partKeyword, ofKeyword, libraryName, semicolon);
|
| }
|
| + StringLiteral partUri = parseStringLiteral();
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new PartDirective.full(commentAndMetadata.comment, commentAndMetadata.metadata, partKeyword, partUri, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse a super constructor invocation.
|
| + * Parse a postfix expression.
|
| *
|
| * <pre>
|
| - * superConstructorInvocation ::=
|
| - * 'super' ('.' identifier)? arguments
|
| + * postfixExpression ::=
|
| + * assignableExpression postfixOperator
|
| + * | primary selector*
|
| + *
|
| + * selector ::=
|
| + * assignableSelector
|
| + * | argumentList
|
| * </pre>
|
| *
|
| - * @return the super constructor invocation that was parsed
|
| + * @return the postfix expression that was parsed
|
| */
|
| - SuperConstructorInvocation parseSuperConstructorInvocation() {
|
| - Token keyword = expect(Keyword.SUPER);
|
| - Token period = null;
|
| - SimpleIdentifier constructorName = null;
|
| - if (matches5(TokenType.PERIOD)) {
|
| - period = andAdvance;
|
| - constructorName = parseSimpleIdentifier();
|
| - }
|
| - ArgumentList argumentList = parseArgumentList();
|
| - return new SuperConstructorInvocation.full(keyword, period, constructorName, argumentList);
|
| + Expression parsePostfixExpression() {
|
| + Expression operand = parseAssignableExpression(true);
|
| + if (matches5(TokenType.OPEN_SQUARE_BRACKET) || matches5(TokenType.PERIOD) || matches5(TokenType.OPEN_PAREN)) {
|
| + do {
|
| + if (matches5(TokenType.OPEN_PAREN)) {
|
| + ArgumentList argumentList = parseArgumentList();
|
| + if (operand is PropertyAccess) {
|
| + PropertyAccess access = operand as PropertyAccess;
|
| + operand = new MethodInvocation.full(access.target, access.operator, access.propertyName, argumentList);
|
| + } else {
|
| + operand = new FunctionExpressionInvocation.full(operand, argumentList);
|
| + }
|
| + } else {
|
| + operand = parseAssignableSelector(operand, true);
|
| + }
|
| + } while (matches5(TokenType.OPEN_SQUARE_BRACKET) || matches5(TokenType.PERIOD) || matches5(TokenType.OPEN_PAREN));
|
| + return operand;
|
| + }
|
| + if (!_currentToken.type.isIncrementOperator) {
|
| + return operand;
|
| + }
|
| + if (operand is Literal || operand is FunctionExpressionInvocation) {
|
| + reportError8(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, []);
|
| + }
|
| + Token operator = andAdvance;
|
| + return new PostfixExpression.full(operand, operator);
|
| }
|
|
|
| /**
|
| - * Parse a switch statement.
|
| + * Parse a primary expression.
|
| *
|
| * <pre>
|
| - * switchStatement ::=
|
| - * 'switch' '(' expression ')' '{' switchCase* defaultCase? '}'
|
| - *
|
| - * switchCase ::=
|
| - * label* ('case' expression ':') statements
|
| + * primary ::=
|
| + * thisExpression
|
| + * | 'super' assignableSelector
|
| + * | functionExpression
|
| + * | literal
|
| + * | identifier
|
| + * | newExpression
|
| + * | constObjectExpression
|
| + * | '(' expression ')'
|
| + * | argumentDefinitionTest
|
| *
|
| - * defaultCase ::=
|
| - * label* 'default' ':' statements
|
| + * literal ::=
|
| + * nullLiteral
|
| + * | booleanLiteral
|
| + * | numericLiteral
|
| + * | stringLiteral
|
| + * | symbolLiteral
|
| + * | mapLiteral
|
| + * | listLiteral
|
| * </pre>
|
| *
|
| - * @return the switch statement that was parsed
|
| + * @return the primary expression that was parsed
|
| */
|
| - SwitchStatement parseSwitchStatement() {
|
| - bool wasInSwitch = _inSwitch;
|
| - _inSwitch = true;
|
| - try {
|
| - Set<String> definedLabels = new Set<String>();
|
| - Token keyword = expect(Keyword.SWITCH);
|
| - Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| + Expression parsePrimaryExpression() {
|
| + if (matches(Keyword.THIS)) {
|
| + return new ThisExpression.full(andAdvance);
|
| + } else if (matches(Keyword.SUPER)) {
|
| + return parseAssignableSelector(new SuperExpression.full(andAdvance), false);
|
| + } else if (matches(Keyword.NULL)) {
|
| + return new NullLiteral.full(andAdvance);
|
| + } else if (matches(Keyword.FALSE)) {
|
| + return new BooleanLiteral.full(andAdvance, false);
|
| + } else if (matches(Keyword.TRUE)) {
|
| + return new BooleanLiteral.full(andAdvance, true);
|
| + } else if (matches5(TokenType.DOUBLE)) {
|
| + Token token = andAdvance;
|
| + double value = 0.0;
|
| + try {
|
| + value = double.parse(token.lexeme);
|
| + } on FormatException catch (exception) {
|
| + }
|
| + return new DoubleLiteral.full(token, value);
|
| + } else if (matches5(TokenType.HEXADECIMAL)) {
|
| + Token token = andAdvance;
|
| + int value = null;
|
| + try {
|
| + value = int.parse(token.lexeme.substring(2), radix: 16);
|
| + } on FormatException catch (exception) {
|
| + }
|
| + return new IntegerLiteral.full(token, value);
|
| + } else if (matches5(TokenType.INT)) {
|
| + Token token = andAdvance;
|
| + int value = null;
|
| + try {
|
| + value = int.parse(token.lexeme);
|
| + } on FormatException catch (exception) {
|
| + }
|
| + return new IntegerLiteral.full(token, value);
|
| + } else if (matches5(TokenType.STRING)) {
|
| + return parseStringLiteral();
|
| + } else if (matches5(TokenType.OPEN_CURLY_BRACKET)) {
|
| + return parseMapLiteral(null, null);
|
| + } else if (matches5(TokenType.OPEN_SQUARE_BRACKET) || matches5(TokenType.INDEX)) {
|
| + return parseListLiteral(null, null);
|
| + } else if (matchesIdentifier()) {
|
| + return parsePrefixedIdentifier();
|
| + } else if (matches(Keyword.NEW)) {
|
| + return parseNewExpression();
|
| + } else if (matches(Keyword.CONST)) {
|
| + return parseConstExpression();
|
| + } else if (matches5(TokenType.OPEN_PAREN)) {
|
| + if (isFunctionExpression(_currentToken)) {
|
| + return parseFunctionExpression();
|
| + }
|
| + Token leftParenthesis = andAdvance;
|
| Expression expression = parseExpression2();
|
| Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| - Token leftBracket = expect2(TokenType.OPEN_CURLY_BRACKET);
|
| - Token defaultKeyword = null;
|
| - List<SwitchMember> members = new List<SwitchMember>();
|
| - while (!matches5(TokenType.EOF) && !matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| - List<Label> labels = new List<Label>();
|
| - while (matchesIdentifier() && matches4(peek(), TokenType.COLON)) {
|
| - SimpleIdentifier identifier = parseSimpleIdentifier();
|
| - String label = identifier.token.lexeme;
|
| - if (definedLabels.contains(label)) {
|
| - reportError9(ParserErrorCode.DUPLICATE_LABEL_IN_SWITCH_STATEMENT, identifier.token, [label]);
|
| - } else {
|
| - javaSetAdd(definedLabels, label);
|
| - }
|
| - Token colon = expect2(TokenType.COLON);
|
| - labels.add(new Label.full(identifier, colon));
|
| - }
|
| - if (matches(Keyword.CASE)) {
|
| - Token caseKeyword = andAdvance;
|
| - Expression caseExpression = parseExpression2();
|
| - Token colon = expect2(TokenType.COLON);
|
| - members.add(new SwitchCase.full(labels, caseKeyword, caseExpression, colon, parseStatements2()));
|
| - if (defaultKeyword != null) {
|
| - reportError9(ParserErrorCode.SWITCH_HAS_CASE_AFTER_DEFAULT_CASE, caseKeyword, []);
|
| - }
|
| - } else if (matches(Keyword.DEFAULT)) {
|
| - if (defaultKeyword != null) {
|
| - reportError9(ParserErrorCode.SWITCH_HAS_MULTIPLE_DEFAULT_CASES, peek(), []);
|
| - }
|
| - defaultKeyword = andAdvance;
|
| - Token colon = expect2(TokenType.COLON);
|
| - members.add(new SwitchDefault.full(labels, defaultKeyword, colon, parseStatements2()));
|
| - } else {
|
| - reportError8(ParserErrorCode.EXPECTED_CASE_OR_DEFAULT, []);
|
| - while (!matches5(TokenType.EOF) && !matches5(TokenType.CLOSE_CURLY_BRACKET) && !matches(Keyword.CASE) && !matches(Keyword.DEFAULT)) {
|
| - advance();
|
| - }
|
| - }
|
| - }
|
| - Token rightBracket = expect2(TokenType.CLOSE_CURLY_BRACKET);
|
| - return new SwitchStatement.full(keyword, leftParenthesis, expression, rightParenthesis, leftBracket, members, rightBracket);
|
| - } finally {
|
| - _inSwitch = wasInSwitch;
|
| + return new ParenthesizedExpression.full(leftParenthesis, expression, rightParenthesis);
|
| + } else if (matches5(TokenType.LT)) {
|
| + return parseListOrMapLiteral(null);
|
| + } else if (matches5(TokenType.QUESTION)) {
|
| + return parseArgumentDefinitionTest();
|
| + } else if (matches(Keyword.VOID)) {
|
| + reportError8(ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
|
| + advance();
|
| + return parsePrimaryExpression();
|
| + } else if (matches5(TokenType.HASH)) {
|
| + return parseSymbolLiteral();
|
| + } else {
|
| + reportError8(ParserErrorCode.MISSING_IDENTIFIER, []);
|
| + return createSyntheticIdentifier();
|
| }
|
| }
|
|
|
| /**
|
| - * Parse a symbol literal.
|
| + * Parse a redirecting constructor invocation.
|
| *
|
| * <pre>
|
| - * symbolLiteral ::=
|
| - * '#' identifier ('.' identifier)*
|
| + * redirectingConstructorInvocation ::=
|
| + * 'this' ('.' identifier)? arguments
|
| * </pre>
|
| *
|
| - * @return the symbol literal that was parsed
|
| + * @return the redirecting constructor invocation that was parsed
|
| */
|
| - SymbolLiteral parseSymbolLiteral() {
|
| - Token poundSign = andAdvance;
|
| - List<Token> components = new List<Token>();
|
| - if (matchesIdentifier()) {
|
| - components.add(andAdvance);
|
| - while (matches5(TokenType.PERIOD)) {
|
| - advance();
|
| - if (matchesIdentifier()) {
|
| - components.add(andAdvance);
|
| - } else {
|
| - reportError8(ParserErrorCode.MISSING_IDENTIFIER, []);
|
| - components.add(createSyntheticToken2(TokenType.IDENTIFIER));
|
| - break;
|
| - }
|
| - }
|
| - } else if (_currentToken.isOperator) {
|
| - components.add(andAdvance);
|
| - } else {
|
| - reportError8(ParserErrorCode.MISSING_IDENTIFIER, []);
|
| - components.add(createSyntheticToken2(TokenType.IDENTIFIER));
|
| + RedirectingConstructorInvocation parseRedirectingConstructorInvocation() {
|
| + Token keyword = expect(Keyword.THIS);
|
| + Token period = null;
|
| + SimpleIdentifier constructorName = null;
|
| + if (matches5(TokenType.PERIOD)) {
|
| + period = andAdvance;
|
| + constructorName = parseSimpleIdentifier();
|
| }
|
| - return new SymbolLiteral.full(poundSign, new List.from(components));
|
| + ArgumentList argumentList = parseArgumentList();
|
| + return new RedirectingConstructorInvocation.full(keyword, period, constructorName, argumentList);
|
| }
|
|
|
| /**
|
| - * Parse a throw expression.
|
| + * Parse a relational expression.
|
| *
|
| * <pre>
|
| - * throwExpression ::=
|
| - * 'throw' expression
|
| + * relationalExpression ::=
|
| + * bitwiseOrExpression ('is' '!'? type | 'as' type | relationalOperator bitwiseOrExpression)?
|
| + * | 'super' relationalOperator bitwiseOrExpression
|
| * </pre>
|
| *
|
| - * @return the throw expression that was parsed
|
| + * @return the relational expression that was parsed
|
| */
|
| - Expression parseThrowExpression() {
|
| - Token keyword = expect(Keyword.THROW);
|
| - if (matches5(TokenType.SEMICOLON) || matches5(TokenType.CLOSE_PAREN)) {
|
| - reportError9(ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken, []);
|
| - return new ThrowExpression.full(keyword, createSyntheticIdentifier());
|
| + Expression parseRelationalExpression() {
|
| + if (matches(Keyword.SUPER) && _currentToken.next.type.isRelationalOperator) {
|
| + Expression expression = new SuperExpression.full(andAdvance);
|
| + Token operator = andAdvance;
|
| + expression = new BinaryExpression.full(expression, operator, parseBitwiseOrExpression());
|
| + return expression;
|
| }
|
| - Expression expression = parseExpression2();
|
| - return new ThrowExpression.full(keyword, expression);
|
| + Expression expression = parseBitwiseOrExpression();
|
| + if (matches(Keyword.AS)) {
|
| + Token asOperator = andAdvance;
|
| + expression = new AsExpression.full(expression, asOperator, parseTypeName());
|
| + } else if (matches(Keyword.IS)) {
|
| + Token isOperator = andAdvance;
|
| + Token notOperator = null;
|
| + if (matches5(TokenType.BANG)) {
|
| + notOperator = andAdvance;
|
| + }
|
| + expression = new IsExpression.full(expression, isOperator, notOperator, parseTypeName());
|
| + } else if (_currentToken.type.isRelationalOperator) {
|
| + Token operator = andAdvance;
|
| + expression = new BinaryExpression.full(expression, operator, parseBitwiseOrExpression());
|
| + }
|
| + return expression;
|
| }
|
|
|
| /**
|
| - * Parse a throw expression.
|
| + * Parse a rethrow expression.
|
| *
|
| * <pre>
|
| - * throwExpressionWithoutCascade ::=
|
| - * 'throw' expressionWithoutCascade
|
| + * rethrowExpression ::=
|
| + * 'rethrow'
|
| * </pre>
|
| *
|
| - * @return the throw expression that was parsed
|
| + * @return the rethrow expression that was parsed
|
| */
|
| - Expression parseThrowExpressionWithoutCascade() {
|
| - Token keyword = expect(Keyword.THROW);
|
| - if (matches5(TokenType.SEMICOLON) || matches5(TokenType.CLOSE_PAREN)) {
|
| - reportError9(ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken, []);
|
| - return new ThrowExpression.full(keyword, createSyntheticIdentifier());
|
| - }
|
| - Expression expression = parseExpressionWithoutCascade();
|
| - return new ThrowExpression.full(keyword, expression);
|
| - }
|
| + Expression parseRethrowExpression() => new RethrowExpression.full(expect(Keyword.RETHROW));
|
|
|
| /**
|
| - * Parse a try statement.
|
| + * Parse a return statement.
|
| *
|
| * <pre>
|
| - * tryStatement ::=
|
| - * 'try' block (onPart+ finallyPart? | finallyPart)
|
| - *
|
| - * onPart ::=
|
| - * catchPart block
|
| - * | 'on' type catchPart? block
|
| - *
|
| - * catchPart ::=
|
| - * 'catch' '(' identifier (',' identifier)? ')'
|
| - *
|
| - * finallyPart ::=
|
| - * 'finally' block
|
| + * returnStatement ::=
|
| + * 'return' expression? ';'
|
| * </pre>
|
| *
|
| - * @return the try statement that was parsed
|
| + * @return the return statement that was parsed
|
| */
|
| - Statement parseTryStatement() {
|
| - Token tryKeyword = expect(Keyword.TRY);
|
| - Block body = parseBlock();
|
| - List<CatchClause> catchClauses = new List<CatchClause>();
|
| - Block finallyClause = null;
|
| - while (matches2(_ON) || matches(Keyword.CATCH)) {
|
| - Token onKeyword = null;
|
| - TypeName exceptionType = null;
|
| - if (matches2(_ON)) {
|
| - onKeyword = andAdvance;
|
| - exceptionType = parseTypeName();
|
| - }
|
| - Token catchKeyword = null;
|
| - Token leftParenthesis = null;
|
| - SimpleIdentifier exceptionParameter = null;
|
| - Token comma = null;
|
| - SimpleIdentifier stackTraceParameter = null;
|
| - Token rightParenthesis = null;
|
| - if (matches(Keyword.CATCH)) {
|
| - catchKeyword = andAdvance;
|
| - leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| - exceptionParameter = parseSimpleIdentifier();
|
| - if (matches5(TokenType.COMMA)) {
|
| - comma = andAdvance;
|
| - stackTraceParameter = parseSimpleIdentifier();
|
| - }
|
| - rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| - }
|
| - Block catchBody = parseBlock();
|
| - catchClauses.add(new CatchClause.full(onKeyword, exceptionType, catchKeyword, leftParenthesis, exceptionParameter, comma, stackTraceParameter, rightParenthesis, catchBody));
|
| - }
|
| - Token finallyKeyword = null;
|
| - if (matches(Keyword.FINALLY)) {
|
| - finallyKeyword = andAdvance;
|
| - finallyClause = parseBlock();
|
| - } else {
|
| - if (catchClauses.isEmpty) {
|
| - reportError8(ParserErrorCode.MISSING_CATCH_OR_FINALLY, []);
|
| - }
|
| + Statement parseReturnStatement() {
|
| + Token returnKeyword = expect(Keyword.RETURN);
|
| + if (matches5(TokenType.SEMICOLON)) {
|
| + return new ReturnStatement.full(returnKeyword, null, andAdvance);
|
| }
|
| - return new TryStatement.full(tryKeyword, body, catchClauses, finallyKeyword, finallyClause);
|
| + Expression expression = parseExpression2();
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new ReturnStatement.full(returnKeyword, expression, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse a type alias.
|
| + * Parse a setter.
|
| *
|
| * <pre>
|
| - * typeAlias ::=
|
| - * 'typedef' typeAliasBody
|
| - *
|
| - * typeAliasBody ::=
|
| - * classTypeAlias
|
| - * | functionTypeAlias
|
| - *
|
| - * classTypeAlias ::=
|
| - * identifier typeParameters? '=' 'abstract'? mixinApplication
|
| - *
|
| - * mixinApplication ::=
|
| - * qualified withClause implementsClause? ';'
|
| - *
|
| - * functionTypeAlias ::=
|
| - * functionPrefix typeParameterList? formalParameterList ';'
|
| + * setter ::=
|
| + * setterSignature functionBody?
|
| *
|
| - * functionPrefix ::=
|
| - * returnType? name
|
| + * setterSignature ::=
|
| + * 'external'? 'static'? returnType? 'set' identifier formalParameterList
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the metadata to be associated with the member
|
| - * @return the type alias that was parsed
|
| + * @param commentAndMetadata the documentation comment and metadata to be associated with the
|
| + * declaration
|
| + * @param externalKeyword the 'external' token
|
| + * @param staticKeyword the static keyword, or `null` if the setter is not static
|
| + * @param the return type that has already been parsed, or `null` if there was no return
|
| + * type
|
| + * @return the setter that was parsed
|
| */
|
| - TypeAlias parseTypeAlias(CommentAndMetadata commentAndMetadata) {
|
| - Token keyword = expect(Keyword.TYPEDEF);
|
| - if (matchesIdentifier()) {
|
| - Token next = peek();
|
| - if (matches4(next, TokenType.LT)) {
|
| - next = skipTypeParameterList(next);
|
| - if (next != null && matches4(next, TokenType.EQ)) {
|
| - TypeAlias typeAlias = parseClassTypeAlias(commentAndMetadata, keyword);
|
| - reportError9(ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword, []);
|
| - return typeAlias;
|
| - }
|
| - } else if (matches4(next, TokenType.EQ)) {
|
| - TypeAlias typeAlias = parseClassTypeAlias(commentAndMetadata, keyword);
|
| - reportError9(ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword, []);
|
| - return typeAlias;
|
| - }
|
| + MethodDeclaration parseSetter(CommentAndMetadata commentAndMetadata, Token externalKeyword, Token staticKeyword, TypeName returnType) {
|
| + Token propertyKeyword = expect(Keyword.SET);
|
| + SimpleIdentifier name = parseSimpleIdentifier();
|
| + FormalParameterList parameters = parseFormalParameterList();
|
| + validateFormalParameterList(parameters);
|
| + FunctionBody body = parseFunctionBody(externalKeyword != null || staticKeyword == null, ParserErrorCode.STATIC_SETTER_WITHOUT_BODY, false);
|
| + if (externalKeyword != null && body is! EmptyFunctionBody) {
|
| + reportError8(ParserErrorCode.EXTERNAL_SETTER_WITH_BODY, []);
|
| }
|
| - return parseFunctionTypeAlias(commentAndMetadata, keyword);
|
| + return new MethodDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, staticKeyword, returnType, propertyKeyword, null, name, parameters, body);
|
| }
|
|
|
| /**
|
| - * Parse a list of type arguments.
|
| + * Parse a shift expression.
|
| *
|
| * <pre>
|
| - * typeArguments ::=
|
| - * '<' typeList '>'
|
| - *
|
| - * typeList ::=
|
| - * type (',' type)*
|
| + * shiftExpression ::=
|
| + * additiveExpression (shiftOperator additiveExpression)*
|
| + * | 'super' (shiftOperator additiveExpression)+
|
| * </pre>
|
| *
|
| - * @return the type argument list that was parsed
|
| + * @return the shift expression that was parsed
|
| */
|
| - TypeArgumentList parseTypeArgumentList() {
|
| - Token leftBracket = expect2(TokenType.LT);
|
| - List<TypeName> arguments = new List<TypeName>();
|
| - arguments.add(parseTypeName());
|
| - while (optional(TokenType.COMMA)) {
|
| - arguments.add(parseTypeName());
|
| + Expression parseShiftExpression() {
|
| + Expression expression;
|
| + if (matches(Keyword.SUPER) && _currentToken.next.type.isShiftOperator) {
|
| + expression = new SuperExpression.full(andAdvance);
|
| + } else {
|
| + expression = parseAdditiveExpression();
|
| }
|
| - Token rightBracket = expect2(TokenType.GT);
|
| - return new TypeArgumentList.full(leftBracket, arguments, rightBracket);
|
| + while (_currentToken.type.isShiftOperator) {
|
| + Token operator = andAdvance;
|
| + expression = new BinaryExpression.full(expression, operator, parseAdditiveExpression());
|
| + }
|
| + return expression;
|
| }
|
|
|
| /**
|
| - * Parse a type name.
|
| + * Parse a list of statements within a switch statement.
|
| *
|
| * <pre>
|
| - * type ::=
|
| - * qualified typeArguments?
|
| + * statements ::=
|
| + * statement*
|
| * </pre>
|
| *
|
| - * @return the type name that was parsed
|
| + * @return the statements that were parsed
|
| */
|
| - TypeName parseTypeName() {
|
| - Identifier typeName;
|
| - if (matches(Keyword.VAR)) {
|
| - reportError8(ParserErrorCode.VAR_AS_TYPE_NAME, []);
|
| - typeName = new SimpleIdentifier.full(andAdvance);
|
| - } else if (matchesIdentifier()) {
|
| - typeName = parsePrefixedIdentifier();
|
| - } else {
|
| - typeName = createSyntheticIdentifier();
|
| - reportError8(ParserErrorCode.EXPECTED_TYPE_NAME, []);
|
| - }
|
| - TypeArgumentList typeArguments = null;
|
| - if (matches5(TokenType.LT)) {
|
| - typeArguments = parseTypeArgumentList();
|
| + List<Statement> parseStatements2() {
|
| + List<Statement> statements = new List<Statement>();
|
| + Token statementStart = _currentToken;
|
| + while (!matches5(TokenType.EOF) && !matches5(TokenType.CLOSE_CURLY_BRACKET) && !isSwitchMember()) {
|
| + statements.add(parseStatement2());
|
| + if (identical(_currentToken, statementStart)) {
|
| + reportError9(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]);
|
| + advance();
|
| + }
|
| + statementStart = _currentToken;
|
| }
|
| - return new TypeName.full(typeName, typeArguments);
|
| + return statements;
|
| }
|
|
|
| /**
|
| - * Parse a type parameter.
|
| - *
|
| - * <pre>
|
| - * typeParameter ::=
|
| - * metadata name ('extends' bound)?
|
| - * </pre>
|
| + * Parse a string literal that contains interpolations.
|
| *
|
| - * @return the type parameter that was parsed
|
| + * @return the string literal that was parsed
|
| */
|
| - TypeParameter parseTypeParameter() {
|
| - CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| - SimpleIdentifier name = parseSimpleIdentifier();
|
| - if (matches(Keyword.EXTENDS)) {
|
| - Token keyword = andAdvance;
|
| - TypeName bound = parseTypeName();
|
| - return new TypeParameter.full(commentAndMetadata.comment, commentAndMetadata.metadata, name, keyword, bound);
|
| + StringInterpolation parseStringInterpolation(Token string) {
|
| + List<InterpolationElement> elements = new List<InterpolationElement>();
|
| + bool hasMore = matches5(TokenType.STRING_INTERPOLATION_EXPRESSION) || matches5(TokenType.STRING_INTERPOLATION_IDENTIFIER);
|
| + elements.add(new InterpolationString.full(string, computeStringValue(string.lexeme, true, !hasMore)));
|
| + while (hasMore) {
|
| + if (matches5(TokenType.STRING_INTERPOLATION_EXPRESSION)) {
|
| + Token openToken = andAdvance;
|
| + Expression expression = parseExpression2();
|
| + Token rightBracket = expect2(TokenType.CLOSE_CURLY_BRACKET);
|
| + elements.add(new InterpolationExpression.full(openToken, expression, rightBracket));
|
| + } else {
|
| + Token openToken = andAdvance;
|
| + Expression expression = null;
|
| + if (matches(Keyword.THIS)) {
|
| + expression = new ThisExpression.full(andAdvance);
|
| + } else {
|
| + expression = parseSimpleIdentifier();
|
| + }
|
| + elements.add(new InterpolationExpression.full(openToken, expression, null));
|
| + }
|
| + if (matches5(TokenType.STRING)) {
|
| + string = andAdvance;
|
| + hasMore = matches5(TokenType.STRING_INTERPOLATION_EXPRESSION) || matches5(TokenType.STRING_INTERPOLATION_IDENTIFIER);
|
| + elements.add(new InterpolationString.full(string, computeStringValue(string.lexeme, false, !hasMore)));
|
| + }
|
| }
|
| - return new TypeParameter.full(commentAndMetadata.comment, commentAndMetadata.metadata, name, null, null);
|
| + return new StringInterpolation.full(elements);
|
| }
|
|
|
| /**
|
| - * Parse a list of type parameters.
|
| + * Parse a super constructor invocation.
|
| *
|
| * <pre>
|
| - * typeParameterList ::=
|
| - * '<' typeParameter (',' typeParameter)* '>'
|
| + * superConstructorInvocation ::=
|
| + * 'super' ('.' identifier)? arguments
|
| * </pre>
|
| *
|
| - * @return the list of type parameters that were parsed
|
| + * @return the super constructor invocation that was parsed
|
| */
|
| - TypeParameterList parseTypeParameterList() {
|
| - Token leftBracket = expect2(TokenType.LT);
|
| - List<TypeParameter> typeParameters = new List<TypeParameter>();
|
| - typeParameters.add(parseTypeParameter());
|
| - while (optional(TokenType.COMMA)) {
|
| - typeParameters.add(parseTypeParameter());
|
| - }
|
| - Token rightBracket = expect2(TokenType.GT);
|
| - return new TypeParameterList.full(leftBracket, typeParameters, rightBracket);
|
| + SuperConstructorInvocation parseSuperConstructorInvocation() {
|
| + Token keyword = expect(Keyword.SUPER);
|
| + Token period = null;
|
| + SimpleIdentifier constructorName = null;
|
| + if (matches5(TokenType.PERIOD)) {
|
| + period = andAdvance;
|
| + constructorName = parseSimpleIdentifier();
|
| + }
|
| + ArgumentList argumentList = parseArgumentList();
|
| + return new SuperConstructorInvocation.full(keyword, period, constructorName, argumentList);
|
| }
|
|
|
| /**
|
| - * Parse a unary expression.
|
| + * Parse a switch statement.
|
| *
|
| * <pre>
|
| - * unaryExpression ::=
|
| - * prefixOperator unaryExpression
|
| - * | postfixExpression
|
| - * | unaryOperator 'super'
|
| - * | '-' 'super'
|
| - * | incrementOperator assignableExpression
|
| + * switchStatement ::=
|
| + * 'switch' '(' expression ')' '{' switchCase* defaultCase? '}'
|
| + *
|
| + * switchCase ::=
|
| + * label* ('case' expression ':') statements
|
| + *
|
| + * defaultCase ::=
|
| + * label* 'default' ':' statements
|
| * </pre>
|
| *
|
| - * @return the unary expression that was parsed
|
| + * @return the switch statement that was parsed
|
| */
|
| - Expression parseUnaryExpression() {
|
| - if (matches5(TokenType.MINUS) || matches5(TokenType.BANG) || matches5(TokenType.TILDE)) {
|
| - Token operator = andAdvance;
|
| - if (matches(Keyword.SUPER)) {
|
| - if (matches4(peek(), TokenType.OPEN_SQUARE_BRACKET) || matches4(peek(), TokenType.PERIOD)) {
|
| - return new PrefixExpression.full(operator, parseUnaryExpression());
|
| - }
|
| - return new PrefixExpression.full(operator, new SuperExpression.full(andAdvance));
|
| - }
|
| - return new PrefixExpression.full(operator, parseUnaryExpression());
|
| - } else if (_currentToken.type.isIncrementOperator) {
|
| - Token operator = andAdvance;
|
| - if (matches(Keyword.SUPER)) {
|
| - if (matches4(peek(), TokenType.OPEN_SQUARE_BRACKET) || matches4(peek(), TokenType.PERIOD)) {
|
| - return new PrefixExpression.full(operator, parseUnaryExpression());
|
| + SwitchStatement parseSwitchStatement() {
|
| + bool wasInSwitch = _inSwitch;
|
| + _inSwitch = true;
|
| + try {
|
| + Set<String> definedLabels = new Set<String>();
|
| + Token keyword = expect(Keyword.SWITCH);
|
| + Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| + Expression expression = parseExpression2();
|
| + Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| + Token leftBracket = expect2(TokenType.OPEN_CURLY_BRACKET);
|
| + Token defaultKeyword = null;
|
| + List<SwitchMember> members = new List<SwitchMember>();
|
| + while (!matches5(TokenType.EOF) && !matches5(TokenType.CLOSE_CURLY_BRACKET)) {
|
| + List<Label> labels = new List<Label>();
|
| + while (matchesIdentifier() && matches4(peek(), TokenType.COLON)) {
|
| + SimpleIdentifier identifier = parseSimpleIdentifier();
|
| + String label = identifier.token.lexeme;
|
| + if (definedLabels.contains(label)) {
|
| + reportError9(ParserErrorCode.DUPLICATE_LABEL_IN_SWITCH_STATEMENT, identifier.token, [label]);
|
| + } else {
|
| + javaSetAdd(definedLabels, label);
|
| + }
|
| + Token colon = expect2(TokenType.COLON);
|
| + labels.add(new Label.full(identifier, colon));
|
| }
|
| - if (identical(operator.type, TokenType.MINUS_MINUS)) {
|
| - int offset = operator.offset;
|
| - Token firstOperator = new Token(TokenType.MINUS, offset);
|
| - Token secondOperator = new Token(TokenType.MINUS, offset + 1);
|
| - secondOperator.setNext(_currentToken);
|
| - firstOperator.setNext(secondOperator);
|
| - operator.previous.setNext(firstOperator);
|
| - return new PrefixExpression.full(firstOperator, new PrefixExpression.full(secondOperator, new SuperExpression.full(andAdvance)));
|
| + if (matches(Keyword.CASE)) {
|
| + Token caseKeyword = andAdvance;
|
| + Expression caseExpression = parseExpression2();
|
| + Token colon = expect2(TokenType.COLON);
|
| + members.add(new SwitchCase.full(labels, caseKeyword, caseExpression, colon, parseStatements2()));
|
| + if (defaultKeyword != null) {
|
| + reportError9(ParserErrorCode.SWITCH_HAS_CASE_AFTER_DEFAULT_CASE, caseKeyword, []);
|
| + }
|
| + } else if (matches(Keyword.DEFAULT)) {
|
| + if (defaultKeyword != null) {
|
| + reportError9(ParserErrorCode.SWITCH_HAS_MULTIPLE_DEFAULT_CASES, peek(), []);
|
| + }
|
| + defaultKeyword = andAdvance;
|
| + Token colon = expect2(TokenType.COLON);
|
| + members.add(new SwitchDefault.full(labels, defaultKeyword, colon, parseStatements2()));
|
| } else {
|
| - reportError8(ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [operator.lexeme]);
|
| - return new PrefixExpression.full(operator, new SuperExpression.full(andAdvance));
|
| + reportError8(ParserErrorCode.EXPECTED_CASE_OR_DEFAULT, []);
|
| + while (!matches5(TokenType.EOF) && !matches5(TokenType.CLOSE_CURLY_BRACKET) && !matches(Keyword.CASE) && !matches(Keyword.DEFAULT)) {
|
| + advance();
|
| + }
|
| }
|
| }
|
| - return new PrefixExpression.full(operator, parseAssignableExpression(false));
|
| - } else if (matches5(TokenType.PLUS)) {
|
| - reportError8(ParserErrorCode.MISSING_IDENTIFIER, []);
|
| - return createSyntheticIdentifier();
|
| + Token rightBracket = expect2(TokenType.CLOSE_CURLY_BRACKET);
|
| + return new SwitchStatement.full(keyword, leftParenthesis, expression, rightParenthesis, leftBracket, members, rightBracket);
|
| + } finally {
|
| + _inSwitch = wasInSwitch;
|
| }
|
| - return parsePostfixExpression();
|
| }
|
|
|
| /**
|
| - * Parse a variable declaration.
|
| + * Parse a symbol literal.
|
| *
|
| * <pre>
|
| - * variableDeclaration ::=
|
| - * identifier ('=' expression)?
|
| + * symbolLiteral ::=
|
| + * '#' identifier ('.' identifier)*
|
| * </pre>
|
| *
|
| - * @return the variable declaration that was parsed
|
| + * @return the symbol literal that was parsed
|
| */
|
| - VariableDeclaration parseVariableDeclaration() {
|
| - CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| - SimpleIdentifier name = parseSimpleIdentifier();
|
| - Token equals = null;
|
| - Expression initializer = null;
|
| - if (matches5(TokenType.EQ)) {
|
| - equals = andAdvance;
|
| - initializer = parseExpression2();
|
| + SymbolLiteral parseSymbolLiteral() {
|
| + Token poundSign = andAdvance;
|
| + List<Token> components = new List<Token>();
|
| + if (matchesIdentifier()) {
|
| + components.add(andAdvance);
|
| + while (matches5(TokenType.PERIOD)) {
|
| + advance();
|
| + if (matchesIdentifier()) {
|
| + components.add(andAdvance);
|
| + } else {
|
| + reportError8(ParserErrorCode.MISSING_IDENTIFIER, []);
|
| + components.add(createSyntheticToken2(TokenType.IDENTIFIER));
|
| + break;
|
| + }
|
| + }
|
| + } else if (_currentToken.isOperator) {
|
| + components.add(andAdvance);
|
| + } else {
|
| + reportError8(ParserErrorCode.MISSING_IDENTIFIER, []);
|
| + components.add(createSyntheticToken2(TokenType.IDENTIFIER));
|
| }
|
| - return new VariableDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, name, equals, initializer);
|
| + return new SymbolLiteral.full(poundSign, new List.from(components));
|
| }
|
|
|
| /**
|
| - * Parse a variable declaration list.
|
| + * Parse a throw expression.
|
| *
|
| * <pre>
|
| - * variableDeclarationList ::=
|
| - * finalConstVarOrType variableDeclaration (',' variableDeclaration)*
|
| + * throwExpression ::=
|
| + * 'throw' expression
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the metadata to be associated with the variable declaration list
|
| - * @return the variable declaration list that was parsed
|
| + * @return the throw expression that was parsed
|
| */
|
| - VariableDeclarationList parseVariableDeclarationList(CommentAndMetadata commentAndMetadata) {
|
| - FinalConstVarOrType holder = parseFinalConstVarOrType(false);
|
| - return parseVariableDeclarationList2(commentAndMetadata, holder.keyword, holder.type);
|
| + Expression parseThrowExpression() {
|
| + Token keyword = expect(Keyword.THROW);
|
| + if (matches5(TokenType.SEMICOLON) || matches5(TokenType.CLOSE_PAREN)) {
|
| + reportError9(ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken, []);
|
| + return new ThrowExpression.full(keyword, createSyntheticIdentifier());
|
| + }
|
| + Expression expression = parseExpression2();
|
| + return new ThrowExpression.full(keyword, expression);
|
| }
|
|
|
| /**
|
| - * Parse a variable declaration list.
|
| + * Parse a throw expression.
|
| *
|
| * <pre>
|
| - * variableDeclarationList ::=
|
| - * finalConstVarOrType variableDeclaration (',' variableDeclaration)*
|
| + * throwExpressionWithoutCascade ::=
|
| + * 'throw' expressionWithoutCascade
|
| * </pre>
|
| *
|
| - * @param commentAndMetadata the metadata to be associated with the variable declaration list, or
|
| - * `null` if there is no attempt at parsing the comment and metadata
|
| - * @param keyword the token representing the 'final', 'const' or 'var' keyword, or `null` if
|
| - * there is no keyword
|
| - * @param type the type of the variables in the list
|
| - * @return the variable declaration list that was parsed
|
| + * @return the throw expression that was parsed
|
| */
|
| - VariableDeclarationList parseVariableDeclarationList2(CommentAndMetadata commentAndMetadata, Token keyword, TypeName type) {
|
| - if (type != null && keyword != null && matches3(keyword, Keyword.VAR)) {
|
| - reportError9(ParserErrorCode.VAR_AND_TYPE, keyword, []);
|
| - }
|
| - List<VariableDeclaration> variables = new List<VariableDeclaration>();
|
| - variables.add(parseVariableDeclaration());
|
| - while (matches5(TokenType.COMMA)) {
|
| - advance();
|
| - variables.add(parseVariableDeclaration());
|
| + Expression parseThrowExpressionWithoutCascade() {
|
| + Token keyword = expect(Keyword.THROW);
|
| + if (matches5(TokenType.SEMICOLON) || matches5(TokenType.CLOSE_PAREN)) {
|
| + reportError9(ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken, []);
|
| + return new ThrowExpression.full(keyword, createSyntheticIdentifier());
|
| }
|
| - return new VariableDeclarationList.full(commentAndMetadata != null ? commentAndMetadata.comment : null, commentAndMetadata != null ? commentAndMetadata.metadata : null, keyword, type, variables);
|
| + Expression expression = parseExpressionWithoutCascade();
|
| + return new ThrowExpression.full(keyword, expression);
|
| }
|
|
|
| /**
|
| - * Parse a variable declaration statement.
|
| + * Parse a try statement.
|
| *
|
| * <pre>
|
| - * variableDeclarationStatement ::=
|
| - * variableDeclarationList ';'
|
| - * </pre>
|
| - *
|
| - * @param commentAndMetadata the metadata to be associated with the variable declaration
|
| - * statement, or `null` if there is no attempt at parsing the comment and metadata
|
| - * @return the variable declaration statement that was parsed
|
| - */
|
| - VariableDeclarationStatement parseVariableDeclarationStatement(CommentAndMetadata commentAndMetadata) {
|
| - VariableDeclarationList variableList = parseVariableDeclarationList(commentAndMetadata);
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new VariableDeclarationStatement.full(variableList, semicolon);
|
| - }
|
| -
|
| - /**
|
| - * Parse a variable declaration statement.
|
| + * tryStatement ::=
|
| + * 'try' block (onPart+ finallyPart? | finallyPart)
|
| *
|
| - * <pre>
|
| - * variableDeclarationStatement ::=
|
| - * variableDeclarationList ';'
|
| - * </pre>
|
| + * onPart ::=
|
| + * catchPart block
|
| + * | 'on' type catchPart? block
|
| *
|
| - * @param commentAndMetadata the metadata to be associated with the variable declaration
|
| - * statement, or `null` if there is no attempt at parsing the comment and metadata
|
| - * @param keyword the token representing the 'final', 'const' or 'var' keyword, or `null` if
|
| - * there is no keyword
|
| - * @param type the type of the variables in the list
|
| - * @return the variable declaration statement that was parsed
|
| - */
|
| - VariableDeclarationStatement parseVariableDeclarationStatement2(CommentAndMetadata commentAndMetadata, Token keyword, TypeName type) {
|
| - VariableDeclarationList variableList = parseVariableDeclarationList2(commentAndMetadata, keyword, type);
|
| - Token semicolon = expect2(TokenType.SEMICOLON);
|
| - return new VariableDeclarationStatement.full(variableList, semicolon);
|
| - }
|
| -
|
| - /**
|
| - * Parse a while statement.
|
| + * catchPart ::=
|
| + * 'catch' '(' identifier (',' identifier)? ')'
|
| *
|
| - * <pre>
|
| - * whileStatement ::=
|
| - * 'while' '(' expression ')' statement
|
| + * finallyPart ::=
|
| + * 'finally' block
|
| * </pre>
|
| *
|
| - * @return the while statement that was parsed
|
| + * @return the try statement that was parsed
|
| */
|
| - Statement parseWhileStatement() {
|
| - bool wasInLoop = _inLoop;
|
| - _inLoop = true;
|
| - try {
|
| - Token keyword = expect(Keyword.WHILE);
|
| - Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| - Expression condition = parseExpression2();
|
| - Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| - Statement body = parseStatement2();
|
| - return new WhileStatement.full(keyword, leftParenthesis, condition, rightParenthesis, body);
|
| - } finally {
|
| - _inLoop = wasInLoop;
|
| + Statement parseTryStatement() {
|
| + Token tryKeyword = expect(Keyword.TRY);
|
| + Block body = parseBlock();
|
| + List<CatchClause> catchClauses = new List<CatchClause>();
|
| + Block finallyClause = null;
|
| + while (matches2(_ON) || matches(Keyword.CATCH)) {
|
| + Token onKeyword = null;
|
| + TypeName exceptionType = null;
|
| + if (matches2(_ON)) {
|
| + onKeyword = andAdvance;
|
| + exceptionType = parseTypeName();
|
| + }
|
| + Token catchKeyword = null;
|
| + Token leftParenthesis = null;
|
| + SimpleIdentifier exceptionParameter = null;
|
| + Token comma = null;
|
| + SimpleIdentifier stackTraceParameter = null;
|
| + Token rightParenthesis = null;
|
| + if (matches(Keyword.CATCH)) {
|
| + catchKeyword = andAdvance;
|
| + leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| + exceptionParameter = parseSimpleIdentifier();
|
| + if (matches5(TokenType.COMMA)) {
|
| + comma = andAdvance;
|
| + stackTraceParameter = parseSimpleIdentifier();
|
| + }
|
| + rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| + }
|
| + Block catchBody = parseBlock();
|
| + catchClauses.add(new CatchClause.full(onKeyword, exceptionType, catchKeyword, leftParenthesis, exceptionParameter, comma, stackTraceParameter, rightParenthesis, catchBody));
|
| }
|
| + Token finallyKeyword = null;
|
| + if (matches(Keyword.FINALLY)) {
|
| + finallyKeyword = andAdvance;
|
| + finallyClause = parseBlock();
|
| + } else {
|
| + if (catchClauses.isEmpty) {
|
| + reportError8(ParserErrorCode.MISSING_CATCH_OR_FINALLY, []);
|
| + }
|
| + }
|
| + return new TryStatement.full(tryKeyword, body, catchClauses, finallyKeyword, finallyClause);
|
| }
|
|
|
| /**
|
| - * Parse a with clause.
|
| + * Parse a type alias.
|
| *
|
| * <pre>
|
| - * withClause ::=
|
| - * 'with' typeName (',' typeName)*
|
| + * typeAlias ::=
|
| + * 'typedef' typeAliasBody
|
| + *
|
| + * typeAliasBody ::=
|
| + * classTypeAlias
|
| + * | functionTypeAlias
|
| + *
|
| + * classTypeAlias ::=
|
| + * identifier typeParameters? '=' 'abstract'? mixinApplication
|
| + *
|
| + * mixinApplication ::=
|
| + * qualified withClause implementsClause? ';'
|
| + *
|
| + * functionTypeAlias ::=
|
| + * functionPrefix typeParameterList? formalParameterList ';'
|
| + *
|
| + * functionPrefix ::=
|
| + * returnType? name
|
| * </pre>
|
| *
|
| - * @return the with clause that was parsed
|
| + * @param commentAndMetadata the metadata to be associated with the member
|
| + * @return the type alias that was parsed
|
| */
|
| - WithClause parseWithClause() {
|
| - Token with2 = expect(Keyword.WITH);
|
| - List<TypeName> types = new List<TypeName>();
|
| - types.add(parseTypeName());
|
| - while (optional(TokenType.COMMA)) {
|
| - types.add(parseTypeName());
|
| + TypeAlias parseTypeAlias(CommentAndMetadata commentAndMetadata) {
|
| + Token keyword = expect(Keyword.TYPEDEF);
|
| + if (matchesIdentifier()) {
|
| + Token next = peek();
|
| + if (matches4(next, TokenType.LT)) {
|
| + next = skipTypeParameterList(next);
|
| + if (next != null && matches4(next, TokenType.EQ)) {
|
| + TypeAlias typeAlias = parseClassTypeAlias(commentAndMetadata, keyword);
|
| + reportError9(ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword, []);
|
| + return typeAlias;
|
| + }
|
| + } else if (matches4(next, TokenType.EQ)) {
|
| + TypeAlias typeAlias = parseClassTypeAlias(commentAndMetadata, keyword);
|
| + reportError9(ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword, []);
|
| + return typeAlias;
|
| + }
|
| }
|
| - return new WithClause.full(with2, types);
|
| + return parseFunctionTypeAlias(commentAndMetadata, keyword);
|
| }
|
|
|
| /**
|
| - * Return the token that is immediately after the current token. This is equivalent to
|
| - * [peek].
|
| + * Parse a unary expression.
|
| *
|
| - * @return the token that is immediately after the current token
|
| - */
|
| - Token peek() => _currentToken.next;
|
| -
|
| - /**
|
| - * Return the token that is the given distance after the current token.
|
| + * <pre>
|
| + * unaryExpression ::=
|
| + * prefixOperator unaryExpression
|
| + * | postfixExpression
|
| + * | unaryOperator 'super'
|
| + * | '-' 'super'
|
| + * | incrementOperator assignableExpression
|
| + * </pre>
|
| *
|
| - * @param distance the number of tokens to look ahead, where `0` is the current token,
|
| - * `1` is the next token, etc.
|
| - * @return the token that is the given distance after the current token
|
| + * @return the unary expression that was parsed
|
| */
|
| - Token peek2(int distance) {
|
| - Token token = _currentToken;
|
| - for (int i = 0; i < distance; i++) {
|
| - token = token.next;
|
| + Expression parseUnaryExpression() {
|
| + if (matches5(TokenType.MINUS) || matches5(TokenType.BANG) || matches5(TokenType.TILDE)) {
|
| + Token operator = andAdvance;
|
| + if (matches(Keyword.SUPER)) {
|
| + if (matches4(peek(), TokenType.OPEN_SQUARE_BRACKET) || matches4(peek(), TokenType.PERIOD)) {
|
| + return new PrefixExpression.full(operator, parseUnaryExpression());
|
| + }
|
| + return new PrefixExpression.full(operator, new SuperExpression.full(andAdvance));
|
| + }
|
| + return new PrefixExpression.full(operator, parseUnaryExpression());
|
| + } else if (_currentToken.type.isIncrementOperator) {
|
| + Token operator = andAdvance;
|
| + if (matches(Keyword.SUPER)) {
|
| + if (matches4(peek(), TokenType.OPEN_SQUARE_BRACKET) || matches4(peek(), TokenType.PERIOD)) {
|
| + return new PrefixExpression.full(operator, parseUnaryExpression());
|
| + }
|
| + if (identical(operator.type, TokenType.MINUS_MINUS)) {
|
| + int offset = operator.offset;
|
| + Token firstOperator = new Token(TokenType.MINUS, offset);
|
| + Token secondOperator = new Token(TokenType.MINUS, offset + 1);
|
| + secondOperator.setNext(_currentToken);
|
| + firstOperator.setNext(secondOperator);
|
| + operator.previous.setNext(firstOperator);
|
| + return new PrefixExpression.full(firstOperator, new PrefixExpression.full(secondOperator, new SuperExpression.full(andAdvance)));
|
| + } else {
|
| + reportError8(ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [operator.lexeme]);
|
| + return new PrefixExpression.full(operator, new SuperExpression.full(andAdvance));
|
| + }
|
| + }
|
| + return new PrefixExpression.full(operator, parseAssignableExpression(false));
|
| + } else if (matches5(TokenType.PLUS)) {
|
| + reportError8(ParserErrorCode.MISSING_IDENTIFIER, []);
|
| + return createSyntheticIdentifier();
|
| }
|
| - return token;
|
| + return parsePostfixExpression();
|
| }
|
|
|
| /**
|
| - * Report an error with the given error code and arguments.
|
| + * Parse a variable declaration.
|
| *
|
| - * @param errorCode the error code of the error to be reported
|
| - * @param node the node specifying the location of the error
|
| - * @param arguments the arguments to the error, used to compose the error message
|
| + * <pre>
|
| + * variableDeclaration ::=
|
| + * identifier ('=' expression)?
|
| + * </pre>
|
| + *
|
| + * @return the variable declaration that was parsed
|
| */
|
| - void reportError(ParserErrorCode errorCode, ASTNode node, List<Object> arguments) {
|
| - _errorListener.onError(new AnalysisError.con2(_source, node.offset, node.length, errorCode, arguments));
|
| + VariableDeclaration parseVariableDeclaration() {
|
| + CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
|
| + SimpleIdentifier name = parseSimpleIdentifier();
|
| + Token equals = null;
|
| + Expression initializer = null;
|
| + if (matches5(TokenType.EQ)) {
|
| + equals = andAdvance;
|
| + initializer = parseExpression2();
|
| + }
|
| + return new VariableDeclaration.full(commentAndMetadata.comment, commentAndMetadata.metadata, name, equals, initializer);
|
| }
|
|
|
| /**
|
| - * Report an error with the given error code and arguments.
|
| + * Parse a variable declaration list.
|
| *
|
| - * @param errorCode the error code of the error to be reported
|
| - * @param arguments the arguments to the error, used to compose the error message
|
| + * <pre>
|
| + * variableDeclarationList ::=
|
| + * finalConstVarOrType variableDeclaration (',' variableDeclaration)*
|
| + * </pre>
|
| + *
|
| + * @param commentAndMetadata the metadata to be associated with the variable declaration list
|
| + * @return the variable declaration list that was parsed
|
| */
|
| - void reportError8(ParserErrorCode errorCode, List<Object> arguments) {
|
| - reportError9(errorCode, _currentToken, arguments);
|
| + VariableDeclarationList parseVariableDeclarationList(CommentAndMetadata commentAndMetadata) {
|
| + FinalConstVarOrType holder = parseFinalConstVarOrType(false);
|
| + return parseVariableDeclarationList2(commentAndMetadata, holder.keyword, holder.type);
|
| }
|
|
|
| /**
|
| - * Report an error with the given error code and arguments.
|
| + * Parse a variable declaration list.
|
| *
|
| - * @param errorCode the error code of the error to be reported
|
| - * @param token the token specifying the location of the error
|
| - * @param arguments the arguments to the error, used to compose the error message
|
| + * <pre>
|
| + * variableDeclarationList ::=
|
| + * finalConstVarOrType variableDeclaration (',' variableDeclaration)*
|
| + * </pre>
|
| + *
|
| + * @param commentAndMetadata the metadata to be associated with the variable declaration list, or
|
| + * `null` if there is no attempt at parsing the comment and metadata
|
| + * @param keyword the token representing the 'final', 'const' or 'var' keyword, or `null` if
|
| + * there is no keyword
|
| + * @param type the type of the variables in the list
|
| + * @return the variable declaration list that was parsed
|
| */
|
| - void reportError9(ParserErrorCode errorCode, Token token, List<Object> arguments) {
|
| - _errorListener.onError(new AnalysisError.con2(_source, token.offset, token.length, errorCode, arguments));
|
| + VariableDeclarationList parseVariableDeclarationList2(CommentAndMetadata commentAndMetadata, Token keyword, TypeName type) {
|
| + if (type != null && keyword != null && matches3(keyword, Keyword.VAR)) {
|
| + reportError9(ParserErrorCode.VAR_AND_TYPE, keyword, []);
|
| + }
|
| + List<VariableDeclaration> variables = new List<VariableDeclaration>();
|
| + variables.add(parseVariableDeclaration());
|
| + while (matches5(TokenType.COMMA)) {
|
| + advance();
|
| + variables.add(parseVariableDeclaration());
|
| + }
|
| + return new VariableDeclarationList.full(commentAndMetadata != null ? commentAndMetadata.comment : null, commentAndMetadata != null ? commentAndMetadata.metadata : null, keyword, type, variables);
|
| }
|
|
|
| /**
|
| - * Look for user defined tasks in comments and convert them into info level analysis issues.
|
| + * Parse a variable declaration statement.
|
| *
|
| - * @param commentToken the comment token to analyze
|
| + * <pre>
|
| + * variableDeclarationStatement ::=
|
| + * variableDeclarationList ';'
|
| + * </pre>
|
| + *
|
| + * @param commentAndMetadata the metadata to be associated with the variable declaration
|
| + * statement, or `null` if there is no attempt at parsing the comment and metadata
|
| + * @return the variable declaration statement that was parsed
|
| */
|
| - void scrapeTodoComment(Token commentToken) {
|
| - JavaPatternMatcher matcher = new JavaPatternMatcher(TodoCode.TODO_REGEX, commentToken.lexeme);
|
| - if (matcher.find()) {
|
| - int offset = commentToken.offset + matcher.start() + matcher.group(1).length;
|
| - int length = matcher.group(2).length;
|
| - // _errorListener.onError(new AnalysisError.con2(_source, offset, length, TodoCode.TODO, [matcher.group(2)]));
|
| - }
|
| + VariableDeclarationStatement parseVariableDeclarationStatement(CommentAndMetadata commentAndMetadata) {
|
| + VariableDeclarationList variableList = parseVariableDeclarationList(commentAndMetadata);
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new VariableDeclarationStatement.full(variableList, semicolon);
|
| }
|
|
|
| /**
|
| - * Parse the 'final', 'const', 'var' or type preceding a variable declaration, starting at the
|
| - * given token, without actually creating a type or changing the current token. Return the token
|
| - * following the type that was parsed, or `null` if the given token is not the first token
|
| - * in a valid type.
|
| + * Parse a variable declaration statement.
|
| *
|
| * <pre>
|
| - * finalConstVarOrType ::=
|
| - * | 'final' type?
|
| - * | 'const' type?
|
| - * | 'var'
|
| - * | type
|
| + * variableDeclarationStatement ::=
|
| + * variableDeclarationList ';'
|
| * </pre>
|
| *
|
| - * @param startToken the token at which parsing is to begin
|
| - * @return the token following the type that was parsed
|
| - */
|
| + * @param commentAndMetadata the metadata to be associated with the variable declaration
|
| + * statement, or `null` if there is no attempt at parsing the comment and metadata
|
| + * @param keyword the token representing the 'final', 'const' or 'var' keyword, or `null` if
|
| + * there is no keyword
|
| + * @param type the type of the variables in the list
|
| + * @return the variable declaration statement that was parsed
|
| + */
|
| + VariableDeclarationStatement parseVariableDeclarationStatement2(CommentAndMetadata commentAndMetadata, Token keyword, TypeName type) {
|
| + VariableDeclarationList variableList = parseVariableDeclarationList2(commentAndMetadata, keyword, type);
|
| + Token semicolon = expect2(TokenType.SEMICOLON);
|
| + return new VariableDeclarationStatement.full(variableList, semicolon);
|
| + }
|
| +
|
| + /**
|
| + * Parse a while statement.
|
| + *
|
| + * <pre>
|
| + * whileStatement ::=
|
| + * 'while' '(' expression ')' statement
|
| + * </pre>
|
| + *
|
| + * @return the while statement that was parsed
|
| + */
|
| + Statement parseWhileStatement() {
|
| + bool wasInLoop = _inLoop;
|
| + _inLoop = true;
|
| + try {
|
| + Token keyword = expect(Keyword.WHILE);
|
| + Token leftParenthesis = expect2(TokenType.OPEN_PAREN);
|
| + Expression condition = parseExpression2();
|
| + Token rightParenthesis = expect2(TokenType.CLOSE_PAREN);
|
| + Statement body = parseStatement2();
|
| + return new WhileStatement.full(keyword, leftParenthesis, condition, rightParenthesis, body);
|
| + } finally {
|
| + _inLoop = wasInLoop;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Return the token that is immediately after the current token. This is equivalent to
|
| + * [peek].
|
| + *
|
| + * @return the token that is immediately after the current token
|
| + */
|
| + Token peek() => _currentToken.next;
|
| +
|
| + /**
|
| + * Return the token that is the given distance after the current token.
|
| + *
|
| + * @param distance the number of tokens to look ahead, where `0` is the current token,
|
| + * `1` is the next token, etc.
|
| + * @return the token that is the given distance after the current token
|
| + */
|
| + Token peek2(int distance) {
|
| + Token token = _currentToken;
|
| + for (int i = 0; i < distance; i++) {
|
| + token = token.next;
|
| + }
|
| + return token;
|
| + }
|
| +
|
| + /**
|
| + * Report an error with the given error code and arguments.
|
| + *
|
| + * @param errorCode the error code of the error to be reported
|
| + * @param node the node specifying the location of the error
|
| + * @param arguments the arguments to the error, used to compose the error message
|
| + */
|
| + void reportError(ParserErrorCode errorCode, ASTNode node, List<Object> arguments) {
|
| + _errorListener.onError(new AnalysisError.con2(_source, node.offset, node.length, errorCode, arguments));
|
| + }
|
| +
|
| + /**
|
| + * Report an error with the given error code and arguments.
|
| + *
|
| + * @param errorCode the error code of the error to be reported
|
| + * @param arguments the arguments to the error, used to compose the error message
|
| + */
|
| + void reportError8(ParserErrorCode errorCode, List<Object> arguments) {
|
| + reportError9(errorCode, _currentToken, arguments);
|
| + }
|
| +
|
| + /**
|
| + * Report an error with the given error code and arguments.
|
| + *
|
| + * @param errorCode the error code of the error to be reported
|
| + * @param token the token specifying the location of the error
|
| + * @param arguments the arguments to the error, used to compose the error message
|
| + */
|
| + void reportError9(ParserErrorCode errorCode, Token token, List<Object> arguments) {
|
| + _errorListener.onError(new AnalysisError.con2(_source, token.offset, token.length, errorCode, arguments));
|
| + }
|
| +
|
| + /**
|
| + * Look for user defined tasks in comments and convert them into info level analysis issues.
|
| + *
|
| + * @param commentToken the comment token to analyze
|
| + */
|
| + void scrapeTodoComment(Token commentToken) {
|
| + JavaPatternMatcher matcher = new JavaPatternMatcher(TodoCode.TODO_REGEX, commentToken.lexeme);
|
| + if (matcher.find()) {
|
| + int offset = commentToken.offset + matcher.start() + matcher.group(1).length;
|
| + int length = matcher.group(2).length;
|
| + // _errorListener.onError(new AnalysisError.con2(_source, offset, length, TodoCode.TODO, [matcher.group(2)]));
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Parse the 'final', 'const', 'var' or type preceding a variable declaration, starting at the
|
| + * given token, without actually creating a type or changing the current token. Return the token
|
| + * following the type that was parsed, or `null` if the given token is not the first token
|
| + * in a valid type.
|
| + *
|
| + * <pre>
|
| + * finalConstVarOrType ::=
|
| + * | 'final' type?
|
| + * | 'const' type?
|
| + * | 'var'
|
| + * | type
|
| + * </pre>
|
| + *
|
| + * @param startToken the token at which parsing is to begin
|
| + * @return the token following the type that was parsed
|
| + */
|
| Token skipFinalConstVarOrType(Token startToken) {
|
| if (matches3(startToken, Keyword.FINAL) || matches3(startToken, Keyword.CONST)) {
|
| Token next = startToken.next;
|
| @@ -6003,6 +7050,769 @@ class ParserErrorCode extends Enum<ParserErrorCode> implements ErrorCode {
|
| ErrorType get type => ErrorType.SYNTACTIC_ERROR;
|
| }
|
| /**
|
| + * Instances of the class `ResolutionCopier` copies resolution information from one AST
|
| + * structure to another as long as the structures of the corresponding children of a pair of nodes
|
| + * are the same.
|
| + */
|
| +class ResolutionCopier implements ASTVisitor<bool> {
|
| +
|
| + /**
|
| + * Copy resolution data from one node to another.
|
| + *
|
| + * @param fromNode the node from which resolution information will be copied
|
| + * @param toNode the node to which resolution information will be copied
|
| + */
|
| + static void copyResolutionData(ASTNode fromNode, ASTNode toNode) {
|
| + ResolutionCopier copier = new ResolutionCopier();
|
| + copier.isEqual(fromNode, toNode);
|
| + }
|
| +
|
| + /**
|
| + * The AST node with which the node being visited is to be compared. This is only valid at the
|
| + * beginning of each visit method (until [isEqual] is invoked).
|
| + */
|
| + ASTNode _toNode;
|
| + bool visitAdjacentStrings(AdjacentStrings node) {
|
| + AdjacentStrings toNode = this._toNode as AdjacentStrings;
|
| + return isEqual2(node.strings, toNode.strings);
|
| + }
|
| + bool visitAnnotation(Annotation node) {
|
| + Annotation toNode = this._toNode as Annotation;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.atSign, toNode.atSign), isEqual(node.name, toNode.name)), isEqual3(node.period, toNode.period)), isEqual(node.constructorName, toNode.constructorName)), isEqual(node.arguments, toNode.arguments))) {
|
| + toNode.element = node.element;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitArgumentDefinitionTest(ArgumentDefinitionTest node) {
|
| + ArgumentDefinitionTest toNode = this._toNode as ArgumentDefinitionTest;
|
| + if (javaBooleanAnd(isEqual3(node.question, toNode.question), isEqual(node.identifier, toNode.identifier))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitArgumentList(ArgumentList node) {
|
| + ArgumentList toNode = this._toNode as ArgumentList;
|
| + return javaBooleanAnd(javaBooleanAnd(isEqual3(node.leftParenthesis, toNode.leftParenthesis), isEqual2(node.arguments, toNode.arguments)), isEqual3(node.rightParenthesis, toNode.rightParenthesis));
|
| + }
|
| + bool visitAsExpression(AsExpression node) {
|
| + AsExpression toNode = this._toNode as AsExpression;
|
| + if (javaBooleanAnd(javaBooleanAnd(isEqual(node.expression, toNode.expression), isEqual3(node.asOperator, toNode.asOperator)), isEqual(node.type, toNode.type))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitAssertStatement(AssertStatement node) {
|
| + AssertStatement toNode = this._toNode as AssertStatement;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual3(node.leftParenthesis, toNode.leftParenthesis)), isEqual(node.condition, toNode.condition)), isEqual3(node.rightParenthesis, toNode.rightParenthesis)), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitAssignmentExpression(AssignmentExpression node) {
|
| + AssignmentExpression toNode = this._toNode as AssignmentExpression;
|
| + if (javaBooleanAnd(javaBooleanAnd(isEqual(node.leftHandSide, toNode.leftHandSide), isEqual3(node.operator, toNode.operator)), isEqual(node.rightHandSide, toNode.rightHandSide))) {
|
| + toNode.propagatedElement = node.propagatedElement;
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticElement = node.staticElement;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitBinaryExpression(BinaryExpression node) {
|
| + BinaryExpression toNode = this._toNode as BinaryExpression;
|
| + if (javaBooleanAnd(javaBooleanAnd(isEqual(node.leftOperand, toNode.leftOperand), isEqual3(node.operator, toNode.operator)), isEqual(node.rightOperand, toNode.rightOperand))) {
|
| + toNode.propagatedElement = node.propagatedElement;
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticElement = node.staticElement;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitBlock(Block node) {
|
| + Block toNode = this._toNode as Block;
|
| + return javaBooleanAnd(javaBooleanAnd(isEqual3(node.leftBracket, toNode.leftBracket), isEqual2(node.statements, toNode.statements)), isEqual3(node.rightBracket, toNode.rightBracket));
|
| + }
|
| + bool visitBlockFunctionBody(BlockFunctionBody node) {
|
| + BlockFunctionBody toNode = this._toNode as BlockFunctionBody;
|
| + return isEqual(node.block, toNode.block);
|
| + }
|
| + bool visitBooleanLiteral(BooleanLiteral node) {
|
| + BooleanLiteral toNode = this._toNode as BooleanLiteral;
|
| + if (javaBooleanAnd(isEqual3(node.literal, toNode.literal), identical(node.value, toNode.value))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitBreakStatement(BreakStatement node) {
|
| + BreakStatement toNode = this._toNode as BreakStatement;
|
| + return javaBooleanAnd(javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual(node.label, toNode.label)), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitCascadeExpression(CascadeExpression node) {
|
| + CascadeExpression toNode = this._toNode as CascadeExpression;
|
| + if (javaBooleanAnd(isEqual(node.target, toNode.target), isEqual2(node.cascadeSections, toNode.cascadeSections))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitCatchClause(CatchClause node) {
|
| + CatchClause toNode = this._toNode as CatchClause;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.onKeyword, toNode.onKeyword), isEqual(node.exceptionType, toNode.exceptionType)), isEqual3(node.catchKeyword, toNode.catchKeyword)), isEqual3(node.leftParenthesis, toNode.leftParenthesis)), isEqual(node.exceptionParameter, toNode.exceptionParameter)), isEqual3(node.comma, toNode.comma)), isEqual(node.stackTraceParameter, toNode.stackTraceParameter)), isEqual3(node.rightParenthesis, toNode.rightParenthesis)), isEqual(node.body, toNode.body));
|
| + }
|
| + bool visitClassDeclaration(ClassDeclaration node) {
|
| + ClassDeclaration toNode = this._toNode as ClassDeclaration;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.abstractKeyword, toNode.abstractKeyword)), isEqual3(node.classKeyword, toNode.classKeyword)), isEqual(node.name, toNode.name)), isEqual(node.typeParameters, toNode.typeParameters)), isEqual(node.extendsClause, toNode.extendsClause)), isEqual(node.withClause, toNode.withClause)), isEqual(node.implementsClause, toNode.implementsClause)), isEqual3(node.leftBracket, toNode.leftBracket)), isEqual2(node.members, toNode.members)), isEqual3(node.rightBracket, toNode.rightBracket));
|
| + }
|
| + bool visitClassTypeAlias(ClassTypeAlias node) {
|
| + ClassTypeAlias toNode = this._toNode as ClassTypeAlias;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.keyword, toNode.keyword)), isEqual(node.name, toNode.name)), isEqual(node.typeParameters, toNode.typeParameters)), isEqual3(node.equals, toNode.equals)), isEqual3(node.abstractKeyword, toNode.abstractKeyword)), isEqual(node.superclass, toNode.superclass)), isEqual(node.withClause, toNode.withClause)), isEqual(node.implementsClause, toNode.implementsClause)), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitComment(Comment node) {
|
| + Comment toNode = this._toNode as Comment;
|
| + return isEqual2(node.references, toNode.references);
|
| + }
|
| + bool visitCommentReference(CommentReference node) {
|
| + CommentReference toNode = this._toNode as CommentReference;
|
| + return javaBooleanAnd(isEqual3(node.newKeyword, toNode.newKeyword), isEqual(node.identifier, toNode.identifier));
|
| + }
|
| + bool visitCompilationUnit(CompilationUnit node) {
|
| + CompilationUnit toNode = this._toNode as CompilationUnit;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.beginToken, toNode.beginToken), isEqual(node.scriptTag, toNode.scriptTag)), isEqual2(node.directives, toNode.directives)), isEqual2(node.declarations, toNode.declarations)), isEqual3(node.endToken, toNode.endToken))) {
|
| + toNode.element = node.element;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitConditionalExpression(ConditionalExpression node) {
|
| + ConditionalExpression toNode = this._toNode as ConditionalExpression;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.condition, toNode.condition), isEqual3(node.question, toNode.question)), isEqual(node.thenExpression, toNode.thenExpression)), isEqual3(node.colon, toNode.colon)), isEqual(node.elseExpression, toNode.elseExpression))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitConstructorDeclaration(ConstructorDeclaration node) {
|
| + ConstructorDeclaration toNode = this._toNode as ConstructorDeclaration;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.externalKeyword, toNode.externalKeyword)), isEqual3(node.constKeyword, toNode.constKeyword)), isEqual3(node.factoryKeyword, toNode.factoryKeyword)), isEqual(node.returnType, toNode.returnType)), isEqual3(node.period, toNode.period)), isEqual(node.name, toNode.name)), isEqual(node.parameters, toNode.parameters)), isEqual3(node.separator, toNode.separator)), isEqual2(node.initializers, toNode.initializers)), isEqual(node.redirectedConstructor, toNode.redirectedConstructor)), isEqual(node.body, toNode.body))) {
|
| + toNode.element = node.element;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
|
| + ConstructorFieldInitializer toNode = this._toNode as ConstructorFieldInitializer;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual3(node.period, toNode.period)), isEqual(node.fieldName, toNode.fieldName)), isEqual3(node.equals, toNode.equals)), isEqual(node.expression, toNode.expression));
|
| + }
|
| + bool visitConstructorName(ConstructorName node) {
|
| + ConstructorName toNode = this._toNode as ConstructorName;
|
| + if (javaBooleanAnd(javaBooleanAnd(isEqual(node.type, toNode.type), isEqual3(node.period, toNode.period)), isEqual(node.name, toNode.name))) {
|
| + toNode.staticElement = node.staticElement;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitContinueStatement(ContinueStatement node) {
|
| + ContinueStatement toNode = this._toNode as ContinueStatement;
|
| + return javaBooleanAnd(javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual(node.label, toNode.label)), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitDeclaredIdentifier(DeclaredIdentifier node) {
|
| + DeclaredIdentifier toNode = this._toNode as DeclaredIdentifier;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.keyword, toNode.keyword)), isEqual(node.type, toNode.type)), isEqual(node.identifier, toNode.identifier));
|
| + }
|
| + bool visitDefaultFormalParameter(DefaultFormalParameter node) {
|
| + DefaultFormalParameter toNode = this._toNode as DefaultFormalParameter;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.parameter, toNode.parameter), identical(node.kind, toNode.kind)), isEqual3(node.separator, toNode.separator)), isEqual(node.defaultValue, toNode.defaultValue));
|
| + }
|
| + bool visitDoStatement(DoStatement node) {
|
| + DoStatement toNode = this._toNode as DoStatement;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.doKeyword, toNode.doKeyword), isEqual(node.body, toNode.body)), isEqual3(node.whileKeyword, toNode.whileKeyword)), isEqual3(node.leftParenthesis, toNode.leftParenthesis)), isEqual(node.condition, toNode.condition)), isEqual3(node.rightParenthesis, toNode.rightParenthesis)), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitDoubleLiteral(DoubleLiteral node) {
|
| + DoubleLiteral toNode = this._toNode as DoubleLiteral;
|
| + if (javaBooleanAnd(isEqual3(node.literal, toNode.literal), node.value == toNode.value)) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitEmptyFunctionBody(EmptyFunctionBody node) {
|
| + EmptyFunctionBody toNode = this._toNode as EmptyFunctionBody;
|
| + return isEqual3(node.semicolon, toNode.semicolon);
|
| + }
|
| + bool visitEmptyStatement(EmptyStatement node) {
|
| + EmptyStatement toNode = this._toNode as EmptyStatement;
|
| + return isEqual3(node.semicolon, toNode.semicolon);
|
| + }
|
| + bool visitExportDirective(ExportDirective node) {
|
| + ExportDirective toNode = this._toNode as ExportDirective;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.keyword, toNode.keyword)), isEqual(node.uri, toNode.uri)), isEqual2(node.combinators, toNode.combinators)), isEqual3(node.semicolon, toNode.semicolon))) {
|
| + toNode.element = node.element;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitExpressionFunctionBody(ExpressionFunctionBody node) {
|
| + ExpressionFunctionBody toNode = this._toNode as ExpressionFunctionBody;
|
| + return javaBooleanAnd(javaBooleanAnd(isEqual3(node.functionDefinition, toNode.functionDefinition), isEqual(node.expression, toNode.expression)), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitExpressionStatement(ExpressionStatement node) {
|
| + ExpressionStatement toNode = this._toNode as ExpressionStatement;
|
| + return javaBooleanAnd(isEqual(node.expression, toNode.expression), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitExtendsClause(ExtendsClause node) {
|
| + ExtendsClause toNode = this._toNode as ExtendsClause;
|
| + return javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual(node.superclass, toNode.superclass));
|
| + }
|
| + bool visitFieldDeclaration(FieldDeclaration node) {
|
| + FieldDeclaration toNode = this._toNode as FieldDeclaration;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.staticKeyword, toNode.staticKeyword)), isEqual(node.fields, toNode.fields)), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitFieldFormalParameter(FieldFormalParameter node) {
|
| + FieldFormalParameter toNode = this._toNode as FieldFormalParameter;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.keyword, toNode.keyword)), isEqual(node.type, toNode.type)), isEqual3(node.thisToken, toNode.thisToken)), isEqual3(node.period, toNode.period)), isEqual(node.identifier, toNode.identifier));
|
| + }
|
| + bool visitForEachStatement(ForEachStatement node) {
|
| + ForEachStatement toNode = this._toNode as ForEachStatement;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.forKeyword, toNode.forKeyword), isEqual3(node.leftParenthesis, toNode.leftParenthesis)), isEqual(node.loopVariable, toNode.loopVariable)), isEqual3(node.inKeyword, toNode.inKeyword)), isEqual(node.iterator, toNode.iterator)), isEqual3(node.rightParenthesis, toNode.rightParenthesis)), isEqual(node.body, toNode.body));
|
| + }
|
| + bool visitFormalParameterList(FormalParameterList node) {
|
| + FormalParameterList toNode = this._toNode as FormalParameterList;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.leftParenthesis, toNode.leftParenthesis), isEqual2(node.parameters, toNode.parameters)), isEqual3(node.leftDelimiter, toNode.leftDelimiter)), isEqual3(node.rightDelimiter, toNode.rightDelimiter)), isEqual3(node.rightParenthesis, toNode.rightParenthesis));
|
| + }
|
| + bool visitForStatement(ForStatement node) {
|
| + ForStatement toNode = this._toNode as ForStatement;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.forKeyword, toNode.forKeyword), isEqual3(node.leftParenthesis, toNode.leftParenthesis)), isEqual(node.variables, toNode.variables)), isEqual(node.initialization, toNode.initialization)), isEqual3(node.leftSeparator, toNode.leftSeparator)), isEqual(node.condition, toNode.condition)), isEqual3(node.rightSeparator, toNode.rightSeparator)), isEqual2(node.updaters, toNode.updaters)), isEqual3(node.rightParenthesis, toNode.rightParenthesis)), isEqual(node.body, toNode.body));
|
| + }
|
| + bool visitFunctionDeclaration(FunctionDeclaration node) {
|
| + FunctionDeclaration toNode = this._toNode as FunctionDeclaration;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.externalKeyword, toNode.externalKeyword)), isEqual(node.returnType, toNode.returnType)), isEqual3(node.propertyKeyword, toNode.propertyKeyword)), isEqual(node.name, toNode.name)), isEqual(node.functionExpression, toNode.functionExpression));
|
| + }
|
| + bool visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
|
| + FunctionDeclarationStatement toNode = this._toNode as FunctionDeclarationStatement;
|
| + return isEqual(node.functionDeclaration, toNode.functionDeclaration);
|
| + }
|
| + bool visitFunctionExpression(FunctionExpression node) {
|
| + FunctionExpression toNode = this._toNode as FunctionExpression;
|
| + if (javaBooleanAnd(isEqual(node.parameters, toNode.parameters), isEqual(node.body, toNode.body))) {
|
| + toNode.element = node.element;
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
|
| + FunctionExpressionInvocation toNode = this._toNode as FunctionExpressionInvocation;
|
| + if (javaBooleanAnd(isEqual(node.function, toNode.function), isEqual(node.argumentList, toNode.argumentList))) {
|
| + toNode.propagatedElement = node.propagatedElement;
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticElement = node.staticElement;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitFunctionTypeAlias(FunctionTypeAlias node) {
|
| + FunctionTypeAlias toNode = this._toNode as FunctionTypeAlias;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.keyword, toNode.keyword)), isEqual(node.returnType, toNode.returnType)), isEqual(node.name, toNode.name)), isEqual(node.typeParameters, toNode.typeParameters)), isEqual(node.parameters, toNode.parameters)), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
|
| + FunctionTypedFormalParameter toNode = this._toNode as FunctionTypedFormalParameter;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual(node.returnType, toNode.returnType)), isEqual(node.identifier, toNode.identifier)), isEqual(node.parameters, toNode.parameters));
|
| + }
|
| + bool visitHideCombinator(HideCombinator node) {
|
| + HideCombinator toNode = this._toNode as HideCombinator;
|
| + return javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual2(node.hiddenNames, toNode.hiddenNames));
|
| + }
|
| + bool visitIfStatement(IfStatement node) {
|
| + IfStatement toNode = this._toNode as IfStatement;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.ifKeyword, toNode.ifKeyword), isEqual3(node.leftParenthesis, toNode.leftParenthesis)), isEqual(node.condition, toNode.condition)), isEqual3(node.rightParenthesis, toNode.rightParenthesis)), isEqual(node.thenStatement, toNode.thenStatement)), isEqual3(node.elseKeyword, toNode.elseKeyword)), isEqual(node.elseStatement, toNode.elseStatement));
|
| + }
|
| + bool visitImplementsClause(ImplementsClause node) {
|
| + ImplementsClause toNode = this._toNode as ImplementsClause;
|
| + return javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual2(node.interfaces, toNode.interfaces));
|
| + }
|
| + bool visitImportDirective(ImportDirective node) {
|
| + ImportDirective toNode = this._toNode as ImportDirective;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.keyword, toNode.keyword)), isEqual(node.uri, toNode.uri)), isEqual3(node.asToken, toNode.asToken)), isEqual(node.prefix, toNode.prefix)), isEqual2(node.combinators, toNode.combinators)), isEqual3(node.semicolon, toNode.semicolon))) {
|
| + toNode.element = node.element;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitIndexExpression(IndexExpression node) {
|
| + IndexExpression toNode = this._toNode as IndexExpression;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.target, toNode.target), isEqual3(node.leftBracket, toNode.leftBracket)), isEqual(node.index, toNode.index)), isEqual3(node.rightBracket, toNode.rightBracket))) {
|
| + toNode.auxiliaryElements = node.auxiliaryElements;
|
| + toNode.propagatedElement = node.propagatedElement;
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticElement = node.staticElement;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitInstanceCreationExpression(InstanceCreationExpression node) {
|
| + InstanceCreationExpression toNode = this._toNode as InstanceCreationExpression;
|
| + if (javaBooleanAnd(javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual(node.constructorName, toNode.constructorName)), isEqual(node.argumentList, toNode.argumentList))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticElement = node.staticElement;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitIntegerLiteral(IntegerLiteral node) {
|
| + IntegerLiteral toNode = this._toNode as IntegerLiteral;
|
| + if (javaBooleanAnd(isEqual3(node.literal, toNode.literal), identical(node.value, toNode.value))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitInterpolationExpression(InterpolationExpression node) {
|
| + InterpolationExpression toNode = this._toNode as InterpolationExpression;
|
| + return javaBooleanAnd(javaBooleanAnd(isEqual3(node.leftBracket, toNode.leftBracket), isEqual(node.expression, toNode.expression)), isEqual3(node.rightBracket, toNode.rightBracket));
|
| + }
|
| + bool visitInterpolationString(InterpolationString node) {
|
| + InterpolationString toNode = this._toNode as InterpolationString;
|
| + return javaBooleanAnd(isEqual3(node.contents, toNode.contents), node.value == toNode.value);
|
| + }
|
| + bool visitIsExpression(IsExpression node) {
|
| + IsExpression toNode = this._toNode as IsExpression;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.expression, toNode.expression), isEqual3(node.isOperator, toNode.isOperator)), isEqual3(node.notOperator, toNode.notOperator)), isEqual(node.type, toNode.type))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitLabel(Label node) {
|
| + Label toNode = this._toNode as Label;
|
| + return javaBooleanAnd(isEqual(node.label, toNode.label), isEqual3(node.colon, toNode.colon));
|
| + }
|
| + bool visitLabeledStatement(LabeledStatement node) {
|
| + LabeledStatement toNode = this._toNode as LabeledStatement;
|
| + return javaBooleanAnd(isEqual2(node.labels, toNode.labels), isEqual(node.statement, toNode.statement));
|
| + }
|
| + bool visitLibraryDirective(LibraryDirective node) {
|
| + LibraryDirective toNode = this._toNode as LibraryDirective;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.libraryToken, toNode.libraryToken)), isEqual(node.name, toNode.name)), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitLibraryIdentifier(LibraryIdentifier node) {
|
| + LibraryIdentifier toNode = this._toNode as LibraryIdentifier;
|
| + if (isEqual2(node.components, toNode.components)) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitListLiteral(ListLiteral node) {
|
| + ListLiteral toNode = this._toNode as ListLiteral;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.constKeyword, toNode.constKeyword), isEqual(node.typeArguments, toNode.typeArguments)), isEqual3(node.leftBracket, toNode.leftBracket)), isEqual2(node.elements, toNode.elements)), isEqual3(node.rightBracket, toNode.rightBracket))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitMapLiteral(MapLiteral node) {
|
| + MapLiteral toNode = this._toNode as MapLiteral;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.constKeyword, toNode.constKeyword), isEqual(node.typeArguments, toNode.typeArguments)), isEqual3(node.leftBracket, toNode.leftBracket)), isEqual2(node.entries, toNode.entries)), isEqual3(node.rightBracket, toNode.rightBracket))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitMapLiteralEntry(MapLiteralEntry node) {
|
| + MapLiteralEntry toNode = this._toNode as MapLiteralEntry;
|
| + return javaBooleanAnd(javaBooleanAnd(isEqual(node.key, toNode.key), isEqual3(node.separator, toNode.separator)), isEqual(node.value, toNode.value));
|
| + }
|
| + bool visitMethodDeclaration(MethodDeclaration node) {
|
| + MethodDeclaration toNode = this._toNode as MethodDeclaration;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.externalKeyword, toNode.externalKeyword)), isEqual3(node.modifierKeyword, toNode.modifierKeyword)), isEqual(node.returnType, toNode.returnType)), isEqual3(node.propertyKeyword, toNode.propertyKeyword)), isEqual3(node.propertyKeyword, toNode.propertyKeyword)), isEqual(node.name, toNode.name)), isEqual(node.parameters, toNode.parameters)), isEqual(node.body, toNode.body));
|
| + }
|
| + bool visitMethodInvocation(MethodInvocation node) {
|
| + MethodInvocation toNode = this._toNode as MethodInvocation;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.target, toNode.target), isEqual3(node.period, toNode.period)), isEqual(node.methodName, toNode.methodName)), isEqual(node.argumentList, toNode.argumentList))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitNamedExpression(NamedExpression node) {
|
| + NamedExpression toNode = this._toNode as NamedExpression;
|
| + if (javaBooleanAnd(isEqual(node.name, toNode.name), isEqual(node.expression, toNode.expression))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitNativeClause(NativeClause node) {
|
| + NativeClause toNode = this._toNode as NativeClause;
|
| + return javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual(node.name, toNode.name));
|
| + }
|
| + bool visitNativeFunctionBody(NativeFunctionBody node) {
|
| + NativeFunctionBody toNode = this._toNode as NativeFunctionBody;
|
| + return javaBooleanAnd(javaBooleanAnd(isEqual3(node.nativeToken, toNode.nativeToken), isEqual(node.stringLiteral, toNode.stringLiteral)), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitNullLiteral(NullLiteral node) {
|
| + NullLiteral toNode = this._toNode as NullLiteral;
|
| + if (isEqual3(node.literal, toNode.literal)) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitParenthesizedExpression(ParenthesizedExpression node) {
|
| + ParenthesizedExpression toNode = this._toNode as ParenthesizedExpression;
|
| + if (javaBooleanAnd(javaBooleanAnd(isEqual3(node.leftParenthesis, toNode.leftParenthesis), isEqual(node.expression, toNode.expression)), isEqual3(node.rightParenthesis, toNode.rightParenthesis))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitPartDirective(PartDirective node) {
|
| + PartDirective toNode = this._toNode as PartDirective;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.partToken, toNode.partToken)), isEqual(node.uri, toNode.uri)), isEqual3(node.semicolon, toNode.semicolon))) {
|
| + toNode.element = node.element;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitPartOfDirective(PartOfDirective node) {
|
| + PartOfDirective toNode = this._toNode as PartOfDirective;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.partToken, toNode.partToken)), isEqual3(node.ofToken, toNode.ofToken)), isEqual(node.libraryName, toNode.libraryName)), isEqual3(node.semicolon, toNode.semicolon))) {
|
| + toNode.element = node.element;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitPostfixExpression(PostfixExpression node) {
|
| + PostfixExpression toNode = this._toNode as PostfixExpression;
|
| + if (javaBooleanAnd(isEqual(node.operand, toNode.operand), isEqual3(node.operator, toNode.operator))) {
|
| + toNode.propagatedElement = node.propagatedElement;
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticElement = node.staticElement;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitPrefixedIdentifier(PrefixedIdentifier node) {
|
| + PrefixedIdentifier toNode = this._toNode as PrefixedIdentifier;
|
| + if (javaBooleanAnd(javaBooleanAnd(isEqual(node.prefix, toNode.prefix), isEqual3(node.period, toNode.period)), isEqual(node.identifier, toNode.identifier))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitPrefixExpression(PrefixExpression node) {
|
| + PrefixExpression toNode = this._toNode as PrefixExpression;
|
| + if (javaBooleanAnd(isEqual3(node.operator, toNode.operator), isEqual(node.operand, toNode.operand))) {
|
| + toNode.propagatedElement = node.propagatedElement;
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticElement = node.staticElement;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitPropertyAccess(PropertyAccess node) {
|
| + PropertyAccess toNode = this._toNode as PropertyAccess;
|
| + if (javaBooleanAnd(javaBooleanAnd(isEqual(node.target, toNode.target), isEqual3(node.operator, toNode.operator)), isEqual(node.propertyName, toNode.propertyName))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
|
| + RedirectingConstructorInvocation toNode = this._toNode as RedirectingConstructorInvocation;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual3(node.period, toNode.period)), isEqual(node.constructorName, toNode.constructorName)), isEqual(node.argumentList, toNode.argumentList))) {
|
| + toNode.staticElement = node.staticElement;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitRethrowExpression(RethrowExpression node) {
|
| + RethrowExpression toNode = this._toNode as RethrowExpression;
|
| + if (isEqual3(node.keyword, toNode.keyword)) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitReturnStatement(ReturnStatement node) {
|
| + ReturnStatement toNode = this._toNode as ReturnStatement;
|
| + return javaBooleanAnd(javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual(node.expression, toNode.expression)), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitScriptTag(ScriptTag node) {
|
| + ScriptTag toNode = this._toNode as ScriptTag;
|
| + return isEqual3(node.scriptTag, toNode.scriptTag);
|
| + }
|
| + bool visitShowCombinator(ShowCombinator node) {
|
| + ShowCombinator toNode = this._toNode as ShowCombinator;
|
| + return javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual2(node.shownNames, toNode.shownNames));
|
| + }
|
| + bool visitSimpleFormalParameter(SimpleFormalParameter node) {
|
| + SimpleFormalParameter toNode = this._toNode as SimpleFormalParameter;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.keyword, toNode.keyword)), isEqual(node.type, toNode.type)), isEqual(node.identifier, toNode.identifier));
|
| + }
|
| + bool visitSimpleIdentifier(SimpleIdentifier node) {
|
| + SimpleIdentifier toNode = this._toNode as SimpleIdentifier;
|
| + if (isEqual3(node.token, toNode.token)) {
|
| + toNode.staticElement = node.staticElement;
|
| + toNode.staticType = node.staticType;
|
| + toNode.propagatedElement = node.propagatedElement;
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.auxiliaryElements = node.auxiliaryElements;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitSimpleStringLiteral(SimpleStringLiteral node) {
|
| + SimpleStringLiteral toNode = this._toNode as SimpleStringLiteral;
|
| + if (javaBooleanAnd(isEqual3(node.literal, toNode.literal), identical(node.value, toNode.value))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitStringInterpolation(StringInterpolation node) {
|
| + StringInterpolation toNode = this._toNode as StringInterpolation;
|
| + if (isEqual2(node.elements, toNode.elements)) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitSuperConstructorInvocation(SuperConstructorInvocation node) {
|
| + SuperConstructorInvocation toNode = this._toNode as SuperConstructorInvocation;
|
| + if (javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual3(node.period, toNode.period)), isEqual(node.constructorName, toNode.constructorName)), isEqual(node.argumentList, toNode.argumentList))) {
|
| + toNode.staticElement = node.staticElement;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitSuperExpression(SuperExpression node) {
|
| + SuperExpression toNode = this._toNode as SuperExpression;
|
| + if (isEqual3(node.keyword, toNode.keyword)) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitSwitchCase(SwitchCase node) {
|
| + SwitchCase toNode = this._toNode as SwitchCase;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual2(node.labels, toNode.labels), isEqual3(node.keyword, toNode.keyword)), isEqual(node.expression, toNode.expression)), isEqual3(node.colon, toNode.colon)), isEqual2(node.statements, toNode.statements));
|
| + }
|
| + bool visitSwitchDefault(SwitchDefault node) {
|
| + SwitchDefault toNode = this._toNode as SwitchDefault;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual2(node.labels, toNode.labels), isEqual3(node.keyword, toNode.keyword)), isEqual3(node.colon, toNode.colon)), isEqual2(node.statements, toNode.statements));
|
| + }
|
| + bool visitSwitchStatement(SwitchStatement node) {
|
| + SwitchStatement toNode = this._toNode as SwitchStatement;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual3(node.leftParenthesis, toNode.leftParenthesis)), isEqual(node.expression, toNode.expression)), isEqual3(node.rightParenthesis, toNode.rightParenthesis)), isEqual3(node.leftBracket, toNode.leftBracket)), isEqual2(node.members, toNode.members)), isEqual3(node.rightBracket, toNode.rightBracket));
|
| + }
|
| + bool visitSymbolLiteral(SymbolLiteral node) {
|
| + SymbolLiteral toNode = this._toNode as SymbolLiteral;
|
| + if (javaBooleanAnd(isEqual3(node.poundSign, toNode.poundSign), isEqual4(node.components, toNode.components))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitThisExpression(ThisExpression node) {
|
| + ThisExpression toNode = this._toNode as ThisExpression;
|
| + if (isEqual3(node.keyword, toNode.keyword)) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitThrowExpression(ThrowExpression node) {
|
| + ThrowExpression toNode = this._toNode as ThrowExpression;
|
| + if (javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual(node.expression, toNode.expression))) {
|
| + toNode.propagatedType = node.propagatedType;
|
| + toNode.staticType = node.staticType;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
|
| + TopLevelVariableDeclaration toNode = this._toNode as TopLevelVariableDeclaration;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual(node.variables, toNode.variables)), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitTryStatement(TryStatement node) {
|
| + TryStatement toNode = this._toNode as TryStatement;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.tryKeyword, toNode.tryKeyword), isEqual(node.body, toNode.body)), isEqual2(node.catchClauses, toNode.catchClauses)), isEqual3(node.finallyKeyword, toNode.finallyKeyword)), isEqual(node.finallyBlock, toNode.finallyBlock));
|
| + }
|
| + bool visitTypeArgumentList(TypeArgumentList node) {
|
| + TypeArgumentList toNode = this._toNode as TypeArgumentList;
|
| + return javaBooleanAnd(javaBooleanAnd(isEqual3(node.leftBracket, toNode.leftBracket), isEqual2(node.arguments, toNode.arguments)), isEqual3(node.rightBracket, toNode.rightBracket));
|
| + }
|
| + bool visitTypeName(TypeName node) {
|
| + TypeName toNode = this._toNode as TypeName;
|
| + if (javaBooleanAnd(isEqual(node.name, toNode.name), isEqual(node.typeArguments, toNode.typeArguments))) {
|
| + toNode.type = node.type;
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| + bool visitTypeParameter(TypeParameter node) {
|
| + TypeParameter toNode = this._toNode as TypeParameter;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual(node.name, toNode.name)), isEqual3(node.keyword, toNode.keyword)), isEqual(node.bound, toNode.bound));
|
| + }
|
| + bool visitTypeParameterList(TypeParameterList node) {
|
| + TypeParameterList toNode = this._toNode as TypeParameterList;
|
| + return javaBooleanAnd(javaBooleanAnd(isEqual3(node.leftBracket, toNode.leftBracket), isEqual2(node.typeParameters, toNode.typeParameters)), isEqual3(node.rightBracket, toNode.rightBracket));
|
| + }
|
| + bool visitVariableDeclaration(VariableDeclaration node) {
|
| + VariableDeclaration toNode = this._toNode as VariableDeclaration;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual(node.name, toNode.name)), isEqual3(node.equals, toNode.equals)), isEqual(node.initializer, toNode.initializer));
|
| + }
|
| + bool visitVariableDeclarationList(VariableDeclarationList node) {
|
| + VariableDeclarationList toNode = this._toNode as VariableDeclarationList;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual(node.documentationComment, toNode.documentationComment), isEqual2(node.metadata, toNode.metadata)), isEqual3(node.keyword, toNode.keyword)), isEqual(node.type, toNode.type)), isEqual2(node.variables, toNode.variables));
|
| + }
|
| + bool visitVariableDeclarationStatement(VariableDeclarationStatement node) {
|
| + VariableDeclarationStatement toNode = this._toNode as VariableDeclarationStatement;
|
| + return javaBooleanAnd(isEqual(node.variables, toNode.variables), isEqual3(node.semicolon, toNode.semicolon));
|
| + }
|
| + bool visitWhileStatement(WhileStatement node) {
|
| + WhileStatement toNode = this._toNode as WhileStatement;
|
| + return javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(javaBooleanAnd(isEqual3(node.keyword, toNode.keyword), isEqual3(node.leftParenthesis, toNode.leftParenthesis)), isEqual(node.condition, toNode.condition)), isEqual3(node.rightParenthesis, toNode.rightParenthesis)), isEqual(node.body, toNode.body));
|
| + }
|
| + bool visitWithClause(WithClause node) {
|
| + WithClause toNode = this._toNode as WithClause;
|
| + return javaBooleanAnd(isEqual3(node.withKeyword, toNode.withKeyword), isEqual2(node.mixinTypes, toNode.mixinTypes));
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if the given AST nodes have the same structure. As a side-effect, if the
|
| + * nodes do have the same structure, any resolution data from the first node will be copied to the
|
| + * second node.
|
| + *
|
| + * @param fromNode the node from which resolution information will be copied
|
| + * @param toNode the node to which resolution information will be copied
|
| + * @return `true` if the given AST nodes have the same structure
|
| + */
|
| + bool isEqual(ASTNode fromNode, ASTNode toNode) {
|
| + if (fromNode == null) {
|
| + return toNode == null;
|
| + } else if (toNode == null) {
|
| + return false;
|
| + } else if (fromNode.runtimeType == toNode.runtimeType) {
|
| + this._toNode = toNode;
|
| + return fromNode.accept(this);
|
| + }
|
| + if (toNode is PrefixedIdentifier) {
|
| + SimpleIdentifier prefix = ((toNode as PrefixedIdentifier)).prefix;
|
| + if (fromNode.runtimeType == prefix.runtimeType) {
|
| + this._toNode = prefix;
|
| + return fromNode.accept(this);
|
| + }
|
| + } else if (toNode is PropertyAccess) {
|
| + Expression target = ((toNode as PropertyAccess)).target;
|
| + if (fromNode.runtimeType == target.runtimeType) {
|
| + this._toNode = target;
|
| + return fromNode.accept(this);
|
| + }
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if the given lists of AST nodes have the same size and corresponding
|
| + * elements are equal.
|
| + *
|
| + * @param first the first node being compared
|
| + * @param second the second node being compared
|
| + * @return `true` if the given AST nodes have the same size and corresponding elements are
|
| + * equal
|
| + */
|
| + bool isEqual2(NodeList first, NodeList second) {
|
| + if (first == null) {
|
| + return second == null;
|
| + } else if (second == null) {
|
| + return false;
|
| + }
|
| + int size = first.length;
|
| + if (second.length != size) {
|
| + return false;
|
| + }
|
| + bool equal = true;
|
| + for (int i = 0; i < size; i++) {
|
| + if (!isEqual(first[i], second[i])) {
|
| + equal = false;
|
| + }
|
| + }
|
| + return equal;
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if the given tokens have the same structure.
|
| + *
|
| + * @param first the first node being compared
|
| + * @param second the second node being compared
|
| + * @return `true` if the given tokens have the same structure
|
| + */
|
| + bool isEqual3(Token first, Token second) {
|
| + if (first == null) {
|
| + return second == null;
|
| + } else if (second == null) {
|
| + return false;
|
| + }
|
| + return first.lexeme == second.lexeme;
|
| + }
|
| +
|
| + /**
|
| + * Return `true` if the given arrays of tokens have the same length and corresponding
|
| + * elements are equal.
|
| + *
|
| + * @param first the first node being compared
|
| + * @param second the second node being compared
|
| + * @return `true` if the given arrays of tokens have the same length and corresponding
|
| + * elements are equal
|
| + */
|
| + bool isEqual4(List<Token> first, List<Token> second) {
|
| + int length = first.length;
|
| + if (second.length != length) {
|
| + return false;
|
| + }
|
| + for (int i = 0; i < length; i++) {
|
| + if (!isEqual3(first[i], second[i])) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| +}
|
| +/**
|
| * Instances of the class {link ToFormattedSourceVisitor} write a source representation of a visited
|
| * AST node (and all of it's children) to a writer.
|
| */
|
|
|