Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Unified Diff: pkg/analyzer/lib/src/generated/parser.dart

Issue 51043010: New analyzer snapshot. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rename to getFirstToken(). Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analyzer/lib/src/generated/java_junit.dart ('k') | pkg/analyzer/lib/src/generated/resolver.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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.
*/
« no previous file with comments | « pkg/analyzer/lib/src/generated/java_junit.dart ('k') | pkg/analyzer/lib/src/generated/resolver.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698