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

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

Issue 2041723008: Rework the parser to improve performance (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: clean-up Created 4 years, 6 months 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 | « no previous file | pkg/analyzer/test/generated/parser_test.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 f63edfa49cf1b6af5fd63d53d368778a6cbcde31..c90928f8fc26bd1fafc715eb46010b02bfd9d3c7 100644
--- a/pkg/analyzer/lib/src/generated/parser.dart
+++ b/pkg/analyzer/lib/src/generated/parser.dart
@@ -216,16 +216,19 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
8,
(Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) => target
._parseConstructor(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)),
- 'parseConstructorFieldInitializer_0': new MethodTrampoline(
- 0, (Parser target) => target._parseConstructorFieldInitializer()),
+ 'parseConstructorFieldInitializer_1': new MethodTrampoline(1,
+ (Parser target, arg0) => target._parseConstructorFieldInitializer(arg0)),
'parseContinueStatement_0': new MethodTrampoline(
0, (Parser target) => target._parseContinueStatement()),
'parseDirective_1': new MethodTrampoline(
1, (Parser target, arg0) => target._parseDirective(arg0)),
'parseDirectives_0':
new MethodTrampoline(0, (Parser target) => target._parseDirectives()),
- 'parseDocumentationComment_0': new MethodTrampoline(
- 0, (Parser target) => target._parseDocumentationComment()),
+ 'parseDocumentationComment_0': new MethodTrampoline(0, (Parser target) {
+ List<DocumentationCommentToken> tokens =
+ target._parseDocumentationCommentTokens();
+ return target._parseDocumentationComment(tokens);
+ }),
'parseDoStatement_0':
new MethodTrampoline(0, (Parser target) => target._parseDoStatement()),
'parseDottedName_0':
@@ -320,13 +323,15 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
'parseOptionalReturnType_0': new MethodTrampoline(
0, (Parser target) => target._parseOptionalReturnType()),
'parsePartDirective_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._parsePartDirective(arg0)),
+ 1, (Parser target, arg0) => target._parsePartOrPartOfDirective(arg0)),
'parsePostfixExpression_0': new MethodTrampoline(
0, (Parser target) => target._parsePostfixExpression()),
'parsePrimaryExpression_0': new MethodTrampoline(
0, (Parser target) => target._parsePrimaryExpression()),
- 'parseRedirectingConstructorInvocation_0': new MethodTrampoline(
- 0, (Parser target) => target._parseRedirectingConstructorInvocation()),
+ 'parseRedirectingConstructorInvocation_1': new MethodTrampoline(
+ 1,
+ (Parser target, arg0) =>
+ target._parseRedirectingConstructorInvocation(arg0)),
'parseRelationalExpression_0': new MethodTrampoline(
0, (Parser target) => target._parseRelationalExpression()),
'parseRethrowExpression_0': new MethodTrampoline(
@@ -493,7 +498,7 @@ class CommentAndMetadata {
final Comment comment;
/**
- * The metadata that was parsed.
+ * The metadata that was parsed, or `null` if none was given.
*/
final List<Annotation> metadata;
@@ -501,6 +506,11 @@ class CommentAndMetadata {
* Initialize a newly created holder with the given [comment] and [metadata].
*/
CommentAndMetadata(this.comment, this.metadata);
+
+ /**
+ * Return `true` if some metadata was parsed.
+ */
+ bool get hasMetadata => metadata != null && metadata.isNotEmpty;
}
/**
@@ -2147,7 +2157,7 @@ class Parser {
/**
* A flag indicating whether the parser is currently in a function body marked
- * as being 'async'.
+ * (by a star) as being a generator.
*/
bool _inGenerator = false;
@@ -2163,7 +2173,7 @@ class Parser {
/**
* A flag indicating whether the parser is currently in a constructor field
- * initializer, with no intervening parens, braces, or brackets.
+ * initializer, with no intervening parentheses, braces, or brackets.
*/
bool _inInitializer = false;
@@ -2179,14 +2189,16 @@ class Parser {
bool parseGenericMethodComments = false;
/**
- * Initialize a newly created parser to parse the content of the given
- * [_source] and to report any errors that are found to the given
- * [_errorListener].
+ * Initialize a newly created parser to parse tokens in the given [_source]
+ * and to report any errors that are found to the given [_errorListener].
*/
Parser(this._source, this._errorListener);
- void set currentToken(Token currentToken) {
- this._currentToken = currentToken;
+ /**
+ * Set the token with which the parse is to begin to the given [token].
+ */
+ void set currentToken(Token token) {
+ this._currentToken = token;
}
/**
@@ -2195,6 +2207,7 @@ class Parser {
* parameters, followed by a left-parenthesis. This is used by
* [_parseTypeAlias] to determine whether or not to parse a return type.
*/
+ @deprecated
bool get hasReturnTypeInTypeAlias {
Token next = _skipReturnType(_currentToken);
if (next == null) {
@@ -2229,19 +2242,20 @@ class Parser {
*/
Token getAndAdvance() {
Token token = _currentToken;
- _advance();
+ _currentToken = _currentToken.next;
return token;
}
/**
* Parse an annotation. Return the annotation that was parsed.
*
+ * This method assumes that the current token matches [TokenType.AT].
+ *
* annotation ::=
* '@' qualified ('.' identifier)? arguments?
- *
*/
Annotation parseAnnotation() {
- Token atSign = _expect(TokenType.AT);
+ Token atSign = getAndAdvance();
Identifier name = parsePrefixedIdentifier();
Token period = null;
SimpleIdentifier constructorName = null;
@@ -2267,6 +2281,9 @@ class Parser {
* label expression
*/
Expression parseArgument() {
+ // TODO(brianwilkerson) Consider returning a wrapper indicating whether the
+ // expression is a named expression in order to remove the 'is' check in
+ // 'parseArgumentList'.
//
// Both namedArgument and expression can start with an identifier, but only
// namedArgument can have an identifier followed by a colon.
@@ -2281,6 +2298,8 @@ class Parser {
/**
* Parse a list of arguments. Return the argument list that was parsed.
*
+ * This method assumes that the current token matches [TokenType.OPEN_PAREN].
+ *
* arguments ::=
* '(' argumentList? ')'
*
@@ -2289,10 +2308,9 @@ class Parser {
* | expressionList (',' namedArgument)*
*/
ArgumentList parseArgumentList() {
- Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
- List<Expression> arguments = new List<Expression>();
+ Token leftParenthesis = getAndAdvance();
if (_matches(TokenType.CLOSE_PAREN)) {
- return new ArgumentList(leftParenthesis, arguments, getAndAdvance());
+ return new ArgumentList(leftParenthesis, null, getAndAdvance());
}
//
// Even though unnamed arguments must all appear before any named arguments,
@@ -2302,30 +2320,30 @@ class Parser {
_inInitializer = false;
try {
Expression argument = parseArgument();
- arguments.add(argument);
+ List<Expression> arguments = <Expression>[argument];
bool foundNamedArgument = argument is NamedExpression;
bool generatedError = false;
while (_optional(TokenType.COMMA)) {
argument = parseArgument();
arguments.add(argument);
- if (foundNamedArgument) {
- bool blankArgument =
- argument is SimpleIdentifier && argument.name.isEmpty;
- if (!generatedError &&
- !(argument is NamedExpression && !blankArgument)) {
- // Report the error, once, but allow the arguments to be in any
- // order in the AST.
- _reportErrorForCurrentToken(
- ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT);
- generatedError = true;
- }
- } else if (argument is NamedExpression) {
+ if (argument is NamedExpression) {
foundNamedArgument = true;
+ } else if (foundNamedArgument) {
+ if (!generatedError) {
+ if (!argument.isSynthetic) {
+ // Report the error, once, but allow the arguments to be in any
+ // order in the AST.
+ _reportErrorForCurrentToken(
+ ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT);
+ generatedError = true;
+ }
+ }
}
}
- // TODO(brianwilkerson) Recovery: Look at the left parenthesis to see
- // whether there is a matching right parenthesis. If there is, then we're
- // more likely missing a comma and should go back to parsing arguments.
+ // Recovery: If the next token is not a right parenthesis, look at the
+ // left parenthesis to see whether there is a matching right parenthesis.
+ // If there is, then we're more likely missing a comma and should go back
+ // to parsing arguments.
Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
return new ArgumentList(leftParenthesis, arguments, rightParenthesis);
} finally {
@@ -2343,16 +2361,15 @@ class Parser {
*/
Expression parseBitwiseOrExpression() {
Expression expression;
- if (_matchesKeyword(Keyword.SUPER) &&
- _tokenMatches(_peek(), TokenType.BAR)) {
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type == TokenType.BAR) {
expression = new SuperExpression(getAndAdvance());
} else {
expression = _parseBitwiseXorExpression();
}
- while (_matches(TokenType.BAR)) {
- Token operator = getAndAdvance();
+ while (_currentToken.type == TokenType.BAR) {
expression = new BinaryExpression(
- expression, operator, _parseBitwiseXorExpression());
+ expression, getAndAdvance(), _parseBitwiseXorExpression());
}
return expression;
}
@@ -2360,27 +2377,37 @@ class Parser {
/**
* Parse a block. Return the block that was parsed.
*
+ * This method assumes that the current token matches
+ * [TokenType.OPEN_CURLY_BRACKET].
+ *
* block ::=
* '{' statements '}'
*/
Block parseBlock() {
- Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
- List<Statement> statements = new List<Statement>();
+ bool isEndOfBlock() {
+ TokenType type = _currentToken.type;
+ return type == TokenType.EOF || type == TokenType.CLOSE_CURLY_BRACKET;
+ }
+
+ Token leftBracket = getAndAdvance();
+ List<Statement> statements = <Statement>[];
Token statementStart = _currentToken;
- while (
- !_matches(TokenType.EOF) && !_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+ while (!isEndOfBlock()) {
Statement statement = parseStatement2();
- if (statement != null) {
- statements.add(statement);
- }
if (identical(_currentToken, statementStart)) {
// Ensure that we are making progress and report an error if we're not.
_reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
[_currentToken.lexeme]);
_advance();
+ } else if (statement != null) {
+ statements.add(statement);
}
statementStart = _currentToken;
}
+ // Recovery: If the next token is not a right curly bracket, look at the
+ // left curly bracket to see whether there is a matching right bracket. If
+ // there is, then we're more likely missing a semi-colon and should go back
+ // to parsing statements.
Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
return new Block(leftBracket, statements, rightBracket);
}
@@ -2397,21 +2424,25 @@ class Parser {
ClassMember parseClassMember(String className) {
CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
Modifiers modifiers = _parseModifiers();
- if (_matchesKeyword(Keyword.VOID)) {
- TypeName returnType = parseReturnType();
- if (_matchesKeyword(Keyword.GET) && _tokenMatchesIdentifier(_peek())) {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.VOID) {
+ TypeName returnType =
+ new TypeName(new SimpleIdentifier(getAndAdvance()), null);
+ keyword = _currentToken.keyword;
+ Token next = _peek();
+ bool isFollowedByIdentifier = _tokenMatchesIdentifier(next);
+ if (keyword == Keyword.GET && isFollowedByIdentifier) {
_validateModifiersForGetterOrSetterOrMethod(modifiers);
return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, returnType);
- } else if (_matchesKeyword(Keyword.SET) &&
- _tokenMatchesIdentifier(_peek())) {
+ } else if (keyword == Keyword.SET && isFollowedByIdentifier) {
_validateModifiersForGetterOrSetterOrMethod(modifiers);
return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, returnType);
- } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
+ } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
_validateModifiersForOperator(modifiers);
- return _parseOperator(
- commentAndMetadata, modifiers.externalKeyword, returnType);
+ return _parseOperatorAfterKeyword(commentAndMetadata,
+ modifiers.externalKeyword, returnType, getAndAdvance());
} else if (_matchesIdentifier() &&
_peek().matchesAny(const <TokenType>[
TokenType.OPEN_PAREN,
@@ -2456,20 +2487,21 @@ class Parser {
ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
return null;
}
- } else if (_matchesKeyword(Keyword.GET) &&
- _tokenMatchesIdentifier(_peek())) {
+ }
+ Token next = _peek();
+ bool isFollowedByIdentifier = _tokenMatchesIdentifier(next);
+ if (keyword == Keyword.GET && isFollowedByIdentifier) {
_validateModifiersForGetterOrSetterOrMethod(modifiers);
return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, null);
- } else if (_matchesKeyword(Keyword.SET) &&
- _tokenMatchesIdentifier(_peek())) {
+ } else if (keyword == Keyword.SET && isFollowedByIdentifier) {
_validateModifiersForGetterOrSetterOrMethod(modifiers);
return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, null);
- } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
+ } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
_validateModifiersForOperator(modifiers);
- return _parseOperator(
- commentAndMetadata, modifiers.externalKeyword, null);
+ return _parseOperatorAfterKeyword(
+ commentAndMetadata, modifiers.externalKeyword, null, getAndAdvance());
} else if (!_matchesIdentifier()) {
//
// Recover from an error.
@@ -2514,9 +2546,9 @@ class Parser {
// We appear to have found an incomplete field declaration.
//
_reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
- List<VariableDeclaration> variables = new List<VariableDeclaration>();
- variables.add(
- new VariableDeclaration(_createSyntheticIdentifier(), null, null));
+ VariableDeclaration variable =
+ new VariableDeclaration(_createSyntheticIdentifier(), null, null);
+ List<VariableDeclaration> variables = <VariableDeclaration>[variable];
return new FieldDeclaration(
commentAndMetadata.comment,
commentAndMetadata.metadata,
@@ -2527,7 +2559,7 @@ class Parser {
_reportErrorForToken(
ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken);
if (commentAndMetadata.comment != null ||
- !commentAndMetadata.metadata.isEmpty) {
+ commentAndMetadata.hasMetadata) {
//
// We appear to have found an incomplete declaration at the end of the
// class. At this point it consists of a metadata, which we don't want
@@ -2549,7 +2581,7 @@ class Parser {
new EmptyFunctionBody(_createSyntheticToken(TokenType.SEMICOLON)));
}
return null;
- } else if (_tokenMatches(_peek(), TokenType.PERIOD) &&
+ } else if (_tokenMatches(next, TokenType.PERIOD) &&
_tokenMatchesIdentifier(_peekAt(2)) &&
_tokenMatches(_peekAt(3), TokenType.OPEN_PAREN)) {
return _parseConstructor(
@@ -2561,7 +2593,7 @@ class Parser {
getAndAdvance(),
parseSimpleIdentifier(isDeclaration: true),
parseFormalParameterList());
- } else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+ } else if (_tokenMatches(next, TokenType.OPEN_PAREN)) {
TypeName returnType = _parseOptionalTypeNameComment();
SimpleIdentifier methodName = parseSimpleIdentifier(isDeclaration: true);
TypeParameterList typeParameters = _parseGenericCommentTypeParameters();
@@ -2589,7 +2621,7 @@ class Parser {
methodName,
typeParameters,
parameters);
- } else if (_peek().matchesAny(const <TokenType>[
+ } else if (next.matchesAny(const <TokenType>[
TokenType.EQ,
TokenType.COMMA,
TokenType.SEMICOLON
@@ -2602,7 +2634,7 @@ class Parser {
}
return _parseInitializedIdentifierList(commentAndMetadata,
modifiers.staticKeyword, _validateModifiersForField(modifiers), null);
- } else if (_matchesKeyword(Keyword.TYPEDEF)) {
+ } else if (keyword == Keyword.TYPEDEF) {
_reportErrorForCurrentToken(ParserErrorCode.TYPEDEF_IN_CLASS);
// TODO(brianwilkerson) We don't currently have any way to capture the
// function type alias that was parsed.
@@ -2615,20 +2647,22 @@ class Parser {
modifiers.externalKeyword, modifiers.staticKeyword, null);
}
}
- TypeName type = parseTypeName();
- if (_matchesKeyword(Keyword.GET) && _tokenMatchesIdentifier(_peek())) {
+ TypeName type = _parseTypeNameAfterIdentifier();
+ keyword = _currentToken.keyword;
+ next = _peek();
+ isFollowedByIdentifier = _tokenMatchesIdentifier(next);
+ if (keyword == Keyword.GET && isFollowedByIdentifier) {
_validateModifiersForGetterOrSetterOrMethod(modifiers);
return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, type);
- } else if (_matchesKeyword(Keyword.SET) &&
- _tokenMatchesIdentifier(_peek())) {
+ } else if (keyword == Keyword.SET && isFollowedByIdentifier) {
_validateModifiersForGetterOrSetterOrMethod(modifiers);
return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, type);
- } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
+ } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
_validateModifiersForOperator(modifiers);
- return _parseOperator(
- commentAndMetadata, modifiers.externalKeyword, type);
+ return _parseOperatorAfterKeyword(
+ commentAndMetadata, modifiers.externalKeyword, type, getAndAdvance());
} else if (!_matchesIdentifier()) {
if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
//
@@ -2668,8 +2702,9 @@ class Parser {
} finally {
_unlockErrorListener();
}
- } else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
- SimpleIdentifier methodName = parseSimpleIdentifier(isDeclaration: true);
+ } else if (_tokenMatches(next, TokenType.OPEN_PAREN)) {
+ SimpleIdentifier methodName =
+ _parseSimpleIdentifierUnchecked(isDeclaration: true);
TypeParameterList typeParameters = _parseGenericCommentTypeParameters();
FormalParameterList parameters = parseFormalParameterList();
if (methodName.name == className) {
@@ -2694,10 +2729,10 @@ class Parser {
methodName,
typeParameters,
parameters);
- } else if (parseGenericMethods && _tokenMatches(_peek(), TokenType.LT)) {
+ } else if (parseGenericMethods && _tokenMatches(next, TokenType.LT)) {
return _parseMethodDeclarationAfterReturnType(commentAndMetadata,
modifiers.externalKeyword, modifiers.staticKeyword, type);
- } else if (_tokenMatches(_peek(), TokenType.OPEN_CURLY_BRACKET)) {
+ } else if (_tokenMatches(next, TokenType.OPEN_CURLY_BRACKET)) {
// We have found "TypeName identifier {", and are guessing that this is a
// getter without the keyword 'get'.
_validateModifiersForGetterOrSetterOrMethod(modifiers);
@@ -2720,14 +2755,10 @@ class Parser {
* | 'hide' identifier (',' identifier)*
*/
Combinator parseCombinator() {
- if (_matchesString(_SHOW) || _matchesString(_HIDE)) {
- Token keyword = getAndAdvance();
- List<SimpleIdentifier> names = _parseIdentifierList();
- if (keyword.lexeme == _SHOW) {
- return new ShowCombinator(keyword, names);
- } else {
- return new HideCombinator(keyword, names);
- }
+ if (_matchesString(_SHOW)) {
+ return new ShowCombinator(getAndAdvance(), _parseIdentifierList());
+ } else if (_matchesString(_HIDE)) {
+ return new HideCombinator(getAndAdvance(), _parseIdentifierList());
}
return null;
}
@@ -2773,70 +2804,72 @@ class Parser {
bool partOfDirectiveFound = false;
bool partDirectiveFound = false;
bool directiveFoundAfterDeclaration = false;
- List<Directive> directives = new List<Directive>();
- List<CompilationUnitMember> declarations =
- new List<CompilationUnitMember>();
+ List<Directive> directives = <Directive>[];
+ List<CompilationUnitMember> declarations = <CompilationUnitMember>[];
Token memberStart = _currentToken;
- while (!_matches(TokenType.EOF)) {
+ TokenType type = _currentToken.type;
+ while (type != TokenType.EOF) {
CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
- if ((_matchesKeyword(Keyword.IMPORT) ||
- _matchesKeyword(Keyword.EXPORT) ||
- _matchesKeyword(Keyword.LIBRARY) ||
- _matchesKeyword(Keyword.PART)) &&
- !_tokenMatches(_peek(), TokenType.PERIOD) &&
- !_tokenMatches(_peek(), TokenType.LT) &&
- !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
- Directive directive = _parseDirective(commentAndMetadata);
+ Keyword keyword = _currentToken.keyword;
+ TokenType nextType = _currentToken.next.type;
+ if ((keyword == Keyword.IMPORT ||
+ keyword == Keyword.EXPORT ||
+ keyword == Keyword.LIBRARY ||
+ keyword == Keyword.PART) &&
+ nextType != TokenType.PERIOD &&
+ nextType != TokenType.LT &&
+ nextType != TokenType.OPEN_PAREN) {
+ Directive parseDirective() {
+ if (keyword == Keyword.IMPORT) {
+ if (partDirectiveFound) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE);
+ }
+ return _parseImportDirective(commentAndMetadata);
+ } else if (keyword == Keyword.EXPORT) {
+ if (partDirectiveFound) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE);
+ }
+ return _parseExportDirective(commentAndMetadata);
+ } else if (keyword == Keyword.LIBRARY) {
+ if (libraryDirectiveFound) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MULTIPLE_LIBRARY_DIRECTIVES);
+ } else {
+ if (directives.length > 0) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.LIBRARY_DIRECTIVE_NOT_FIRST);
+ }
+ libraryDirectiveFound = true;
+ }
+ return _parseLibraryDirective(commentAndMetadata);
+ } else if (keyword == Keyword.PART) {
+ if (_tokenMatchesString(_peek(), _OF)) {
+ partOfDirectiveFound = true;
+ return _parsePartOfDirective(commentAndMetadata);
+ } else {
+ partDirectiveFound = true;
+ return _parsePartDirective(commentAndMetadata);
+ }
+ } else {
+ // Internal error: this method should not have been invoked if the
+ // current token was something other than one of the above.
+ throw new IllegalStateException(
+ "parseDirective invoked in an invalid state (currentToken = $_currentToken)");
+ }
+ }
+ Directive directive = parseDirective();
if (declarations.length > 0 && !directiveFoundAfterDeclaration) {
_reportErrorForToken(ParserErrorCode.DIRECTIVE_AFTER_DECLARATION,
directive.beginToken);
directiveFoundAfterDeclaration = true;
}
- if (directive is LibraryDirective) {
- if (libraryDirectiveFound) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MULTIPLE_LIBRARY_DIRECTIVES);
- } else {
- if (directives.length > 0) {
- _reportErrorForToken(ParserErrorCode.LIBRARY_DIRECTIVE_NOT_FIRST,
- directive.libraryKeyword);
- }
- libraryDirectiveFound = true;
- }
- } else if (directive is PartDirective) {
- partDirectiveFound = true;
- } else if (partDirectiveFound) {
- if (directive is ExportDirective) {
- _reportErrorForToken(
- ParserErrorCode.EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE,
- directive.keyword);
- } else if (directive is ImportDirective) {
- _reportErrorForToken(
- ParserErrorCode.IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE,
- directive.keyword);
- }
- }
- if (directive is PartOfDirective) {
- if (partOfDirectiveFound) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MULTIPLE_PART_OF_DIRECTIVES);
- } else {
- int directiveCount = directives.length;
- for (int i = 0; i < directiveCount; i++) {
- _reportErrorForToken(
- ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART,
- directives[i].keyword);
- }
- partOfDirectiveFound = true;
- }
- } else {
- if (partOfDirectiveFound) {
- _reportErrorForToken(ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART,
- directive.keyword);
- }
- }
directives.add(directive);
- } else if (_matches(TokenType.SEMICOLON)) {
+ } else if (type == TokenType.SEMICOLON) {
+ // TODO(brianwilkerson) Consider moving this error detection into
+ // _parseCompilationUnitMember (in the places where EXPECTED_EXECUTABLE
+ // is being generated).
_reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
[_currentToken.lexeme]);
_advance();
@@ -2857,6 +2890,38 @@ class Parser {
}
}
memberStart = _currentToken;
+ type = _currentToken.type;
+ }
+ if (partOfDirectiveFound && directives.length > 1) {
+ // TODO(brianwilkerson) Improve error reporting when both a library and
+ // part-of directive are found.
+// if (libraryDirectiveFound) {
+// int directiveCount = directives.length;
+// for (int i = 0; i < directiveCount; i++) {
+// Directive directive = directives[i];
+// if (directive is PartOfDirective) {
+// _reportErrorForToken(
+// ParserErrorCode.PART_OF_IN_LIBRARY, directive.partKeyword);
+// }
+// }
+// } else {
+ bool firstPartOf = true;
+ int directiveCount = directives.length;
+ for (int i = 0; i < directiveCount; i++) {
+ Directive directive = directives[i];
+ if (directive is PartOfDirective) {
+ if (firstPartOf) {
+ firstPartOf = false;
+ } else {
+ _reportErrorForToken(ParserErrorCode.MULTIPLE_PART_OF_DIRECTIVES,
+ directive.partKeyword);
+ }
+ } else {
+ _reportErrorForToken(ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART,
+ directives[i].keyword);
+ }
+// }
+ }
}
return new CompilationUnit(
firstToken, scriptTag, directives, declarations, _currentToken);
@@ -2871,7 +2936,7 @@ class Parser {
*/
Expression parseConditionalExpression() {
Expression condition = parseIfNullExpression();
- if (!_matches(TokenType.QUESTION)) {
+ if (_currentToken.type != TokenType.QUESTION) {
return condition;
}
Token question = getAndAdvance();
@@ -2932,9 +2997,10 @@ class Parser {
* | throwExpression
*/
Expression parseExpression2() {
- if (_matchesKeyword(Keyword.THROW)) {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.THROW) {
return _parseThrowExpression();
- } else if (_matchesKeyword(Keyword.RETHROW)) {
+ } else if (keyword == Keyword.RETHROW) {
// TODO(brianwilkerson) Rethrow is a statement again.
return _parseRethrowExpression();
}
@@ -2945,18 +3011,17 @@ class Parser {
// grammar after making that determination.
//
Expression expression = parseConditionalExpression();
- TokenType tokenType = _currentToken.type;
- if (tokenType == TokenType.PERIOD_PERIOD) {
- List<Expression> cascadeSections = new List<Expression>();
- while (tokenType == TokenType.PERIOD_PERIOD) {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.PERIOD_PERIOD) {
+ List<Expression> cascadeSections = <Expression>[];
+ do {
Expression section = _parseCascadeSection();
if (section != null) {
cascadeSections.add(section);
}
- tokenType = _currentToken.type;
- }
+ } while (_currentToken.type == TokenType.PERIOD_PERIOD);
return new CascadeExpression(expression, cascadeSections);
- } else if (tokenType.isAssignmentOperator) {
+ } else if (type.isAssignmentOperator) {
Token operator = getAndAdvance();
_ensureAssignable(expression);
return new AssignmentExpression(expression, operator, parseExpression2());
@@ -2999,11 +3064,13 @@ class Parser {
* Parse a class extends clause. Return the class extends clause that was
* parsed.
*
+ * This method assumes that the current token matches `Keyword.EXTENDS`.
+ *
* classExtendsClause ::=
* 'extends' type
*/
ExtendsClause parseExtendsClause() {
- Token keyword = _expectKeyword(Keyword.EXTENDS);
+ Token keyword = getAndAdvance();
TypeName superclass = parseTypeName();
return new ExtendsClause(keyword, superclass);
}
@@ -3031,145 +3098,16 @@ class Parser {
* '{' defaultNamedParameter (',' defaultNamedParameter)* '}'
*/
FormalParameterList parseFormalParameterList() {
- Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
- if (_matches(TokenType.CLOSE_PAREN)) {
- return new FormalParameterList(
- leftParenthesis, null, null, null, getAndAdvance());
- }
- //
- // Even though it is invalid to have default parameters outside of brackets,
- // required parameters inside of brackets, or multiple groups of default and
- // named parameters, we allow all of these cases so that we can recover
- // better.
- //
- List<FormalParameter> parameters = new List<FormalParameter>();
- Token leftSquareBracket = null;
- Token rightSquareBracket = null;
- Token leftCurlyBracket = null;
- Token rightCurlyBracket = null;
- ParameterKind kind = ParameterKind.REQUIRED;
- bool firstParameter = true;
- bool reportedMultiplePositionalGroups = false;
- bool reportedMultipleNamedGroups = false;
- bool reportedMixedGroups = false;
- bool wasOptionalParameter = false;
- Token initialToken = null;
- do {
- if (firstParameter) {
- firstParameter = false;
- } else if (!_optional(TokenType.COMMA)) {
- // TODO(brianwilkerson) The token is wrong, we need to recover from this
- // case.
- if (_getEndToken(leftParenthesis) != null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.EXPECTED_TOKEN, [TokenType.COMMA.lexeme]);
- } else {
- _reportErrorForToken(ParserErrorCode.MISSING_CLOSING_PARENTHESIS,
- _currentToken.previous);
- break;
- }
- }
- initialToken = _currentToken;
- //
- // Handle the beginning of parameter groups.
- //
- if (_matches(TokenType.OPEN_SQUARE_BRACKET)) {
- wasOptionalParameter = true;
- if (leftSquareBracket != null && !reportedMultiplePositionalGroups) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MULTIPLE_POSITIONAL_PARAMETER_GROUPS);
- reportedMultiplePositionalGroups = true;
- }
- if (leftCurlyBracket != null && !reportedMixedGroups) {
- _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
- reportedMixedGroups = true;
- }
- leftSquareBracket = getAndAdvance();
- kind = ParameterKind.POSITIONAL;
- } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
- wasOptionalParameter = true;
- if (leftCurlyBracket != null && !reportedMultipleNamedGroups) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MULTIPLE_NAMED_PARAMETER_GROUPS);
- reportedMultipleNamedGroups = true;
- }
- if (leftSquareBracket != null && !reportedMixedGroups) {
- _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
- reportedMixedGroups = true;
- }
- leftCurlyBracket = getAndAdvance();
- kind = ParameterKind.NAMED;
- }
- //
- // Parse and record the parameter.
- //
- FormalParameter parameter = _parseFormalParameter(kind);
- parameters.add(parameter);
- if (kind == ParameterKind.REQUIRED && wasOptionalParameter) {
- _reportErrorForNode(
- ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS, parameter);
- }
- //
- // Handle the end of parameter groups.
- //
- // TODO(brianwilkerson) Improve the detection and reporting of missing and
- // mismatched delimiters.
- if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
- rightSquareBracket = getAndAdvance();
- if (leftSquareBracket == null) {
- if (leftCurlyBracket != null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
- rightCurlyBracket = rightSquareBracket;
- rightSquareBracket = null;
- } else {
- _reportErrorForCurrentToken(
- ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP,
- ["["]);
- }
- }
- kind = ParameterKind.REQUIRED;
- } else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
- rightCurlyBracket = getAndAdvance();
- if (leftCurlyBracket == null) {
- if (leftSquareBracket != null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
- rightSquareBracket = rightCurlyBracket;
- rightCurlyBracket = null;
- } else {
- _reportErrorForCurrentToken(
- ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP,
- ["{"]);
- }
- }
- kind = ParameterKind.REQUIRED;
- }
- } while (!_matches(TokenType.CLOSE_PAREN) &&
- !identical(initialToken, _currentToken));
- Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
- //
- // Check that the groups were closed correctly.
- //
- if (leftSquareBracket != null && rightSquareBracket == null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
- }
- if (leftCurlyBracket != null && rightCurlyBracket == null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
- }
- //
- // Build the parameter list.
- //
- if (leftSquareBracket == null) {
- leftSquareBracket = leftCurlyBracket;
- }
- if (rightSquareBracket == null) {
- rightSquareBracket = rightCurlyBracket;
+ if (_matches(TokenType.OPEN_PAREN)) {
+ return _parseFormalParameterListUnchecked();
}
- return new FormalParameterList(leftParenthesis, parameters,
- leftSquareBracket, rightSquareBracket, rightParenthesis);
+ // TODO(brianwilkerson) Improve the error message.
+ _reportErrorForCurrentToken(
+ ParserErrorCode.EXPECTED_TOKEN, [TokenType.OPEN_PAREN.lexeme]);
+ // Recovery: Check for an unmatched closing paren and parse parameters until
+ // it is reached.
+ return _parseFormalParameterListAfterParen(
+ _createSyntheticToken(TokenType.OPEN_PAREN));
}
/**
@@ -3196,10 +3134,9 @@ class Parser {
*/
Expression parseIfNullExpression() {
Expression expression = parseLogicalOrExpression();
- while (_matches(TokenType.QUESTION_QUESTION)) {
- Token operator = getAndAdvance();
+ while (_currentToken.type == TokenType.QUESTION_QUESTION) {
expression = new BinaryExpression(
- expression, operator, parseLogicalOrExpression());
+ expression, getAndAdvance(), parseLogicalOrExpression());
}
return expression;
}
@@ -3207,12 +3144,14 @@ class Parser {
/**
* Parse an implements clause. Return the implements clause that was parsed.
*
+ * This method assumes that the current token matches `Keyword.IMPLEMENTS`.
+ *
* implementsClause ::=
* 'implements' type (',' type)*
*/
ImplementsClause parseImplementsClause() {
- Token keyword = _expectKeyword(Keyword.IMPLEMENTS);
- List<TypeName> interfaces = new List<TypeName>();
+ Token keyword = getAndAdvance();
+ List<TypeName> interfaces = <TypeName>[];
interfaces.add(parseTypeName());
while (_optional(TokenType.COMMA)) {
interfaces.add(parseTypeName());
@@ -3223,13 +3162,16 @@ class Parser {
/**
* Parse a label. Return the label that was parsed.
*
+ * This method assumes that the current token matches an identifier and that
+ * the following token matches `TokenType.COLON`.
+ *
* label ::=
* identifier ':'
*/
Label parseLabel({bool isDeclaration: false}) {
SimpleIdentifier label =
- parseSimpleIdentifier(isDeclaration: isDeclaration);
- Token colon = _expect(TokenType.COLON);
+ _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration);
+ Token colon = getAndAdvance();
return new Label(label, colon);
}
@@ -3240,10 +3182,9 @@ class Parser {
* identifier ('.' identifier)*
*/
LibraryIdentifier parseLibraryIdentifier() {
- List<SimpleIdentifier> components = new List<SimpleIdentifier>();
+ List<SimpleIdentifier> components = <SimpleIdentifier>[];
components.add(parseSimpleIdentifier());
- while (_matches(TokenType.PERIOD)) {
- _advance();
+ while (_optional(TokenType.PERIOD)) {
components.add(parseSimpleIdentifier());
}
return new LibraryIdentifier(components);
@@ -3258,10 +3199,9 @@ class Parser {
*/
Expression parseLogicalOrExpression() {
Expression expression = _parseLogicalAndExpression();
- while (_matches(TokenType.BAR_BAR)) {
- Token operator = getAndAdvance();
+ while (_currentToken.type == TokenType.BAR_BAR) {
expression = new BinaryExpression(
- expression, operator, _parseLogicalAndExpression());
+ expression, getAndAdvance(), _parseLogicalAndExpression());
}
return expression;
}
@@ -3310,7 +3250,7 @@ class Parser {
SimpleIdentifier identifier = parseSimpleIdentifier();
TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
if (_matches(TokenType.OPEN_PAREN)) {
- FormalParameterList parameters = parseFormalParameterList();
+ FormalParameterList parameters = _parseFormalParameterListUnchecked();
if (thisKeyword == null) {
if (holder.keyword != null) {
_reportErrorForToken(
@@ -3381,13 +3321,7 @@ class Parser {
* identifier ('.' identifier)?
*/
Identifier parsePrefixedIdentifier() {
- SimpleIdentifier qualifier = parseSimpleIdentifier();
- if (!_matches(TokenType.PERIOD) || _injectGenericCommentTypeList()) {
- return qualifier;
- }
- Token period = getAndAdvance();
- SimpleIdentifier qualified = parseSimpleIdentifier();
- return new PrefixedIdentifier(qualifier, period, qualified);
+ return _parsePrefixedIdentifierAfterIdentifier(parseSimpleIdentifier());
}
/**
@@ -3398,7 +3332,7 @@ class Parser {
* | type
*/
TypeName parseReturnType() {
- if (_matchesKeyword(Keyword.VOID)) {
+ if (_currentToken.keyword == Keyword.VOID) {
return new TypeName(new SimpleIdentifier(getAndAdvance()), null);
} else {
return parseTypeName();
@@ -3413,14 +3347,7 @@ class Parser {
*/
SimpleIdentifier parseSimpleIdentifier({bool isDeclaration: false}) {
if (_matchesIdentifier()) {
- String lexeme = _currentToken.lexeme;
- if ((_inAsync || _inGenerator) &&
- (lexeme == 'async' || lexeme == 'await' || lexeme == 'yield')) {
- _reportErrorForCurrentToken(
- ParserErrorCode.ASYNC_KEYWORD_USED_AS_IDENTIFIER);
- }
- return new SimpleIdentifier(getAndAdvance(),
- isDeclaration: isDeclaration);
+ return _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration);
}
_reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
return _createSyntheticIdentifier(isDeclaration: isDeclaration);
@@ -3444,7 +3371,7 @@ class Parser {
*/
Statement parseStatement2() {
List<Label> labels = null;
- while (_matchesIdentifier() && _tokenMatches(_peek(), TokenType.COLON)) {
+ while (_matchesIdentifier() && _currentToken.next.type == TokenType.COLON) {
Label label = parseLabel(isDeclaration: true);
if (labels == null) {
labels = <Label>[label];
@@ -3477,31 +3404,19 @@ class Parser {
* | SINGLE_LINE_STRING+
*/
StringLiteral parseStringLiteral() {
- List<StringLiteral> strings = new List<StringLiteral>();
- while (_matches(TokenType.STRING)) {
- Token string = getAndAdvance();
- if (_matches(TokenType.STRING_INTERPOLATION_EXPRESSION) ||
- _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER)) {
- strings.add(_parseStringInterpolation(string));
- } else {
- strings.add(new SimpleStringLiteral(
- string, _computeStringValue(string.lexeme, true, true)));
- }
- }
- if (strings.length < 1) {
- _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_STRING_LITERAL);
- return _createSyntheticStringLiteral();
- } else if (strings.length == 1) {
- return strings[0];
- } else {
- return new AdjacentStrings(strings);
+ if (_matches(TokenType.STRING)) {
+ return _parseStringLiteralUnchecked();
}
+ _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_STRING_LITERAL);
+ return _createSyntheticStringLiteral();
}
/**
* Parse a list of type arguments. Return the type argument list that was
* parsed.
*
+ * This method assumes that the current token matches `TokenType.LT`.
+ *
* typeArguments ::=
* '<' typeList '>'
*
@@ -3509,9 +3424,8 @@ class Parser {
* type (',' type)*
*/
TypeArgumentList parseTypeArgumentList() {
- Token leftBracket = _expect(TokenType.LT);
- List<TypeName> arguments = new List<TypeName>();
- arguments.add(parseTypeName());
+ Token leftBracket = getAndAdvance();
+ List<TypeName> arguments = <TypeName>[parseTypeName()];
while (_optional(TokenType.COMMA)) {
arguments.add(parseTypeName());
}
@@ -3531,8 +3445,8 @@ class Parser {
// type to replace the real type name.
// TODO(jmesserly): this feels like a big hammer. Can we restrict it to
// only work inside generic methods?
- TypeName typeComment = _parseOptionalTypeNameComment();
- return typeComment ?? realType;
+ TypeName typeFromComment = _parseOptionalTypeNameComment();
+ return typeFromComment ?? realType;
}
/**
@@ -3558,13 +3472,14 @@ class Parser {
* Parse a list of type parameters. Return the list of type parameters that
* were parsed.
*
+ * This method assumes that the current token matches `TokenType.LT`.
+ *
* typeParameterList ::=
* '<' typeParameter (',' typeParameter)* '>'
*/
TypeParameterList parseTypeParameterList() {
- Token leftBracket = _expect(TokenType.LT);
- List<TypeParameter> typeParameters = new List<TypeParameter>();
- typeParameters.add(parseTypeParameter());
+ Token leftBracket = getAndAdvance();
+ List<TypeParameter> typeParameters = <TypeParameter>[parseTypeParameter()];
while (_optional(TokenType.COMMA)) {
typeParameters.add(parseTypeParameter());
}
@@ -3575,17 +3490,18 @@ class Parser {
/**
* Parse a with clause. Return the with clause that was parsed.
*
+ * This method assumes that the current token matches `Keyword.WITH`.
+ *
* withClause ::=
* 'with' typeName (',' typeName)*
*/
WithClause parseWithClause() {
- Token with2 = _expectKeyword(Keyword.WITH);
- List<TypeName> types = new List<TypeName>();
- types.add(parseTypeName());
+ Token withKeyword = getAndAdvance();
+ List<TypeName> types = <TypeName>[parseTypeName()];
while (_optional(TokenType.COMMA)) {
types.add(parseTypeName());
}
- return new WithClause(with2, types);
+ return new WithClause(withKeyword, types);
}
/**
@@ -3669,30 +3585,33 @@ class Parser {
* member.
*/
bool _couldBeStartOfCompilationUnitMember() {
- if ((_matchesKeyword(Keyword.IMPORT) ||
- _matchesKeyword(Keyword.EXPORT) ||
- _matchesKeyword(Keyword.LIBRARY) ||
- _matchesKeyword(Keyword.PART)) &&
- !_tokenMatches(_peek(), TokenType.PERIOD) &&
- !_tokenMatches(_peek(), TokenType.LT)) {
+ Keyword keyword = _currentToken.keyword;
+ Token next = _currentToken.next;
+ TokenType nextType = next.type;
+ if ((keyword == Keyword.IMPORT ||
+ keyword == Keyword.EXPORT ||
+ keyword == Keyword.LIBRARY ||
+ keyword == Keyword.PART) &&
+ nextType != TokenType.PERIOD &&
+ nextType != TokenType.LT) {
// This looks like the start of a directive
return true;
- } else if (_matchesKeyword(Keyword.CLASS)) {
+ } else if (keyword == Keyword.CLASS) {
// This looks like the start of a class definition
return true;
- } else if (_matchesKeyword(Keyword.TYPEDEF) &&
- !_tokenMatches(_peek(), TokenType.PERIOD) &&
- !_tokenMatches(_peek(), TokenType.LT)) {
+ } else if (keyword == Keyword.TYPEDEF &&
+ nextType != TokenType.PERIOD &&
+ nextType != TokenType.LT) {
// This looks like the start of a typedef
return true;
- } else if (_matchesKeyword(Keyword.VOID) ||
- ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) &&
- _tokenMatchesIdentifier(_peek())) ||
- (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek()))) {
+ } else if (keyword == Keyword.VOID ||
+ ((keyword == Keyword.GET || keyword == Keyword.SET) &&
+ _tokenMatchesIdentifier(next)) ||
+ (keyword == Keyword.OPERATOR && _isOperator(next))) {
// This looks like the start of a function
return true;
} else if (_matchesIdentifier()) {
- if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+ if (nextType == TokenType.OPEN_PAREN) {
// This looks like the start of a function
return true;
}
@@ -3700,9 +3619,10 @@ class Parser {
if (token == null) {
return false;
}
- if (_matchesKeyword(Keyword.GET) ||
- _matchesKeyword(Keyword.SET) ||
- (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) ||
+ // TODO(brianwilkerson) This looks wrong; should we be checking 'token'?
+ if (keyword == Keyword.GET ||
+ keyword == Keyword.SET ||
+ (keyword == Keyword.OPERATOR && _isOperator(next)) ||
_matchesIdentifier()) {
return true;
}
@@ -3872,7 +3792,7 @@ class Parser {
* should be treated as code blocks.
*/
List<List<int>> _getCodeBlockRanges(String comment) {
- List<List<int>> ranges = new List<List<int>>();
+ List<List<int>> ranges = <List<int>>[];
int length = comment.length;
if (length < 3) {
return ranges;
@@ -4015,7 +3935,8 @@ class Parser {
* function declaration.
*/
bool _isFunctionDeclaration() {
- if (_matchesKeyword(Keyword.VOID)) {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.VOID) {
return true;
}
Token afterReturnType = _skipTypeName(_currentToken);
@@ -4038,20 +3959,20 @@ class Parser {
}
// It's possible that we have found a getter. While this isn't valid at this
// point we test for it in order to recover better.
- if (_matchesKeyword(Keyword.GET)) {
+ if (keyword == Keyword.GET) {
Token afterName = _skipSimpleIdentifier(_currentToken.next);
if (afterName == null) {
return false;
}
- return _tokenMatches(afterName, TokenType.FUNCTION) ||
- _tokenMatches(afterName, TokenType.OPEN_CURLY_BRACKET);
+ TokenType type = afterName.type;
+ return type == TokenType.FUNCTION || type == TokenType.OPEN_CURLY_BRACKET;
} else if (_tokenMatchesKeyword(afterReturnType, Keyword.GET)) {
Token afterName = _skipSimpleIdentifier(afterReturnType.next);
if (afterName == null) {
return false;
}
- return _tokenMatches(afterName, TokenType.FUNCTION) ||
- _tokenMatches(afterName, TokenType.OPEN_CURLY_BRACKET);
+ TokenType type = afterName.type;
+ return type == TokenType.FUNCTION || type == TokenType.OPEN_CURLY_BRACKET;
}
return false;
}
@@ -4114,12 +4035,13 @@ class Parser {
* identifier ('=' expression)?
*/
bool _isInitializedVariableDeclaration() {
- if (_matchesKeyword(Keyword.FINAL) || _matchesKeyword(Keyword.VAR)) {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.FINAL || keyword == Keyword.VAR) {
// An expression cannot start with a keyword other than 'const',
// 'rethrow', or 'throw'.
return true;
}
- if (_matchesKeyword(Keyword.CONST)) {
+ if (keyword == Keyword.CONST) {
// Look to see whether we might be at the start of a list or map literal,
// otherwise this should be the start of a variable declaration.
return !_peek().matchesAny(const <TokenType>[
@@ -4156,7 +4078,7 @@ class Parser {
if (type == TokenType.EQ ||
type == TokenType.COMMA ||
type == TokenType.SEMICOLON ||
- _tokenMatchesKeyword(token, Keyword.IN)) {
+ token.keyword == Keyword.IN) {
return true;
}
// It is OK to parse as a variable declaration in these cases:
@@ -4178,6 +4100,8 @@ class Parser {
}
bool _isLikelyArgumentList() {
+ // Try to reduce the amount of lookahead required here before enabling
+ // generic methods.
if (_matches(TokenType.OPEN_PAREN)) {
return true;
}
@@ -4228,7 +4152,7 @@ class Parser {
if (!startToken.isOperator) {
return false;
}
- // Token "=" means that it is actually field initializer.
+ // Token "=" means that it is actually a field initializer.
if (startToken.type == TokenType.EQ) {
return false;
}
@@ -4372,7 +4296,7 @@ class Parser {
* should not be invoked with an argument value of [TokenType.GT].
*/
bool _optional(TokenType type) {
- if (_matches(type)) {
+ if (_currentToken.type == type) {
_advance();
return true;
}
@@ -4389,28 +4313,45 @@ class Parser {
*/
Expression _parseAdditiveExpression() {
Expression expression;
- if (_matchesKeyword(Keyword.SUPER) &&
+ if (_currentToken.keyword == Keyword.SUPER &&
_currentToken.next.type.isAdditiveOperator) {
expression = new SuperExpression(getAndAdvance());
} else {
expression = _parseMultiplicativeExpression();
}
while (_currentToken.type.isAdditiveOperator) {
- Token operator = getAndAdvance();
expression = new BinaryExpression(
- expression, operator, _parseMultiplicativeExpression());
+ expression, getAndAdvance(), _parseMultiplicativeExpression());
}
return expression;
}
/**
+ * Parse an argument list when we need to check for an open paren and recover
+ * when there isn't one. Return the argument list that was parsed.
+ */
+ ArgumentList _parseArgumentListChecked() {
+ if (_matches(TokenType.OPEN_PAREN)) {
+ return parseArgumentList();
+ }
+ _reportErrorForCurrentToken(
+ ParserErrorCode.EXPECTED_TOKEN, [TokenType.OPEN_PAREN.lexeme]);
+ // Recovery: Look to see whether there is a close paren that isn't matched
+ // to an open paren and if so parse the list of arguments as normal.
+ return new ArgumentList(_createSyntheticToken(TokenType.OPEN_PAREN), null,
+ _createSyntheticToken(TokenType.CLOSE_PAREN));
+ }
+
+ /**
* Parse an assert statement. Return the assert statement.
*
+ * This method assumes that the current token matches `Keyword.ASSERT`.
+ *
* assertStatement ::=
* 'assert' '(' conditionalExpression ')' ';'
*/
AssertStatement _parseAssertStatement() {
- Token keyword = _expectKeyword(Keyword.ASSERT);
+ Token keyword = getAndAdvance();
Token leftParen = _expect(TokenType.OPEN_PAREN);
Expression expression = parseExpression2();
if (expression is AssignmentExpression) {
@@ -4454,6 +4395,17 @@ class Parser {
new SuperExpression(getAndAdvance()), false,
allowConditional: false);
}
+ return _parseAssignableExpressionNotStartingWithSuper(primaryAllowed);
+ }
+
+ /**
+ * Parse an assignable expression given that the current token is not 'super'.
+ * The [primaryAllowed] is `true` if the expression is allowed to be a primary
+ * without any assignable selector. Return the assignable expression that was
+ * parsed.
+ */
+ Expression _parseAssignableExpressionNotStartingWithSuper(
+ bool primaryAllowed) {
//
// A primary expression can start with an identifier. We resolve the
// ambiguity by determining whether the primary consists of anything other
@@ -4523,7 +4475,8 @@ class Parser {
*/
Expression _parseAssignableSelector(Expression prefix, bool optional,
{bool allowConditional: true}) {
- if (_matches(TokenType.OPEN_SQUARE_BRACKET)) {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.OPEN_SQUARE_BRACKET) {
Token leftBracket = getAndAdvance();
bool wasInInitializer = _inInitializer;
_inInitializer = false;
@@ -4535,27 +4488,32 @@ class Parser {
} finally {
_inInitializer = wasInInitializer;
}
- } else if (_matches(TokenType.PERIOD) ||
- _matches(TokenType.QUESTION_PERIOD)) {
- if (_matches(TokenType.QUESTION_PERIOD) && !allowConditional) {
- _reportErrorForCurrentToken(
- ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [_currentToken.lexeme]);
- }
- Token operator = getAndAdvance();
- return new PropertyAccess(prefix, operator, parseSimpleIdentifier());
} else {
- if (!optional) {
- // Report the missing selector.
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR);
+ bool isQuestionPeriod = type == TokenType.QUESTION_PERIOD;
+ if (type == TokenType.PERIOD || isQuestionPeriod) {
+ if (isQuestionPeriod && !allowConditional) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.INVALID_OPERATOR_FOR_SUPER,
+ [_currentToken.lexeme]);
+ }
+ Token operator = getAndAdvance();
+ return new PropertyAccess(prefix, operator, parseSimpleIdentifier());
+ } else {
+ if (!optional) {
+ // Report the missing selector.
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR);
+ }
+ return prefix;
}
- return prefix;
}
}
/**
* Parse a await expression. Return the await expression that was parsed.
*
+ * This method assumes that the current token matches `_AWAIT`.
+ *
* awaitExpression ::=
* 'await' unaryExpression
*/
@@ -4575,16 +4533,15 @@ class Parser {
*/
Expression _parseBitwiseAndExpression() {
Expression expression;
- if (_matchesKeyword(Keyword.SUPER) &&
- _tokenMatches(_peek(), TokenType.AMPERSAND)) {
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type == TokenType.AMPERSAND) {
expression = new SuperExpression(getAndAdvance());
} else {
expression = _parseShiftExpression();
}
- while (_matches(TokenType.AMPERSAND)) {
- Token operator = getAndAdvance();
- expression =
- new BinaryExpression(expression, operator, _parseShiftExpression());
+ while (_currentToken.type == TokenType.AMPERSAND) {
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), _parseShiftExpression());
}
return expression;
}
@@ -4599,31 +4556,52 @@ class Parser {
*/
Expression _parseBitwiseXorExpression() {
Expression expression;
- if (_matchesKeyword(Keyword.SUPER) &&
- _tokenMatches(_peek(), TokenType.CARET)) {
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type == TokenType.CARET) {
expression = new SuperExpression(getAndAdvance());
} else {
expression = _parseBitwiseAndExpression();
}
- while (_matches(TokenType.CARET)) {
- Token operator = getAndAdvance();
+ while (_currentToken.type == TokenType.CARET) {
expression = new BinaryExpression(
- expression, operator, _parseBitwiseAndExpression());
+ expression, getAndAdvance(), _parseBitwiseAndExpression());
}
return expression;
}
/**
+ * Parse a block when we need to check for an open curly brace and recover
+ * when there isn't one. Return the block that was parsed.
+ *
+ * block ::=
+ * '{' statements '}'
+ */
+ Block _parseBlockChecked() {
+ if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+ return parseBlock();
+ }
+ // TODO(brianwilkerson) Improve the error message.
+ _reportErrorForCurrentToken(
+ ParserErrorCode.EXPECTED_TOKEN, [TokenType.OPEN_CURLY_BRACKET.lexeme]);
+ // Recovery: Check for an unmatched closing curly bracket and parse
+ // statements until it is reached.
+ return new Block(_createSyntheticToken(TokenType.OPEN_CURLY_BRACKET), null,
+ _createSyntheticToken(TokenType.CLOSE_CURLY_BRACKET));
+ }
+
+ /**
* Parse a break statement. Return the break statement that was parsed.
*
+ * This method assumes that the current token matches `Keyword.BREAK`.
+ *
* breakStatement ::=
* 'break' identifier? ';'
*/
Statement _parseBreakStatement() {
- Token breakKeyword = _expectKeyword(Keyword.BREAK);
+ Token breakKeyword = getAndAdvance();
SimpleIdentifier label = null;
if (_matchesIdentifier()) {
- label = parseSimpleIdentifier();
+ label = _parseSimpleIdentifierUnchecked();
}
if (!_inLoop && !_inSwitch && label == null) {
_reportErrorForToken(ParserErrorCode.BREAK_OUTSIDE_OF_LOOP, breakKeyword);
@@ -4636,6 +4614,9 @@ class Parser {
* Parse a cascade section. Return the expression representing the cascaded
* method invocation.
*
+ * This method assumes that the current token matches
+ * `TokenType.PERIOD_PERIOD`.
+ *
* cascadeSection ::=
* '..' (cascadeSelector typeArguments? arguments*)
* (assignableSelector typeArguments? arguments*)* cascadeAssignment?
@@ -4648,11 +4629,11 @@ class Parser {
* assignmentOperator expressionWithoutCascade
*/
Expression _parseCascadeSection() {
- Token period = _expect(TokenType.PERIOD_PERIOD);
+ Token period = getAndAdvance();
Expression expression = null;
SimpleIdentifier functionName = null;
if (_matchesIdentifier()) {
- functionName = parseSimpleIdentifier();
+ functionName = _parseSimpleIdentifierUnchecked();
} else if (_currentToken.type == TokenType.OPEN_SQUARE_BRACKET) {
Token leftBracket = getAndAdvance();
bool wasInInitializer = _inInitializer;
@@ -4674,7 +4655,7 @@ class Parser {
assert((expression == null && functionName != null) ||
(expression != null && functionName == null));
if (_isLikelyArgumentList()) {
- while (_isLikelyArgumentList()) {
+ do {
TypeArgumentList typeArguments = _parseOptionalTypeArguments();
if (functionName != null) {
expression = new MethodInvocation(expression, period, functionName,
@@ -4689,7 +4670,7 @@ class Parser {
expression = new FunctionExpressionInvocation(
expression, typeArguments, parseArgumentList());
}
- }
+ } while (_isLikelyArgumentList());
} else if (functionName != null) {
expression = new PropertyAccess(expression, period, functionName);
period = null;
@@ -4734,31 +4715,33 @@ class Parser {
* keyword 'abstract', or `null` if the keyword was not given. Return the
* class declaration that was parsed.
*
+ * This method assumes that the current token matches `Keyword.CLASS`.
+ *
* classDeclaration ::=
* metadata 'abstract'? 'class' name typeParameterList? (extendsClause withClause?)? implementsClause? '{' classMembers '}' |
* metadata 'abstract'? 'class' mixinApplicationClass
*/
CompilationUnitMember _parseClassDeclaration(
CommentAndMetadata commentAndMetadata, Token abstractKeyword) {
- Token keyword = _expectKeyword(Keyword.CLASS);
- if (_matchesIdentifier()) {
- Token next = _peek();
- if (_tokenMatches(next, TokenType.LT)) {
- next = _skipTypeParameterList(next);
- if (next != null && _tokenMatches(next, TokenType.EQ)) {
- return _parseClassTypeAlias(
- commentAndMetadata, abstractKeyword, keyword);
- }
- } else if (_tokenMatches(next, TokenType.EQ)) {
- return _parseClassTypeAlias(
- commentAndMetadata, abstractKeyword, keyword);
- }
- }
+ //
+ // Parse the name and type parameters.
+ //
+ Token keyword = getAndAdvance();
SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
String className = name.name;
TypeParameterList typeParameters = null;
- if (_matches(TokenType.LT)) {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.LT) {
typeParameters = parseTypeParameterList();
+ type = _currentToken.type;
+ }
+ //
+ // Check to see whether this might be a class type alias rather than a class
+ // declaration.
+ //
+ if (type == TokenType.EQ) {
+ return _parseClassTypeAliasAfterName(
+ commentAndMetadata, abstractKeyword, keyword, name, typeParameters);
}
//
// Parse the clauses. The parser accepts clauses in any order, but will
@@ -4770,7 +4753,8 @@ class Parser {
ImplementsClause implementsClause = null;
bool foundClause = true;
while (foundClause) {
- if (_matchesKeyword(Keyword.EXTENDS)) {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.EXTENDS) {
if (extendsClause == null) {
extendsClause = parseExtendsClause();
if (withClause != null) {
@@ -4785,7 +4769,7 @@ class Parser {
extendsClause.extendsKeyword);
parseExtendsClause();
}
- } else if (_matchesKeyword(Keyword.WITH)) {
+ } else if (keyword == Keyword.WITH) {
if (withClause == null) {
withClause = parseWithClause();
if (implementsClause != null) {
@@ -4799,7 +4783,7 @@ class Parser {
// TODO(brianwilkerson) Should we merge the list of applied mixins
// into a single list?
}
- } else if (_matchesKeyword(Keyword.IMPLEMENTS)) {
+ } else if (keyword == Keyword.IMPLEMENTS) {
if (implementsClause == null) {
implementsClause = parseImplementsClause();
} else {
@@ -4831,10 +4815,12 @@ class Parser {
List<ClassMember> members = null;
Token rightBracket = null;
if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
- leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
+ leftBracket = getAndAdvance();
members = _parseClassMembers(className, _getEndToken(leftBracket));
rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
} else {
+ // Recovery: Check for an unmatched closing curly bracket and parse
+ // members until it is reached.
leftBracket = _createSyntheticToken(TokenType.OPEN_CURLY_BRACKET);
rightBracket = _createSyntheticToken(TokenType.CLOSE_CURLY_BRACKET);
_reportErrorForCurrentToken(ParserErrorCode.MISSING_CLASS_BODY);
@@ -4866,14 +4852,15 @@ class Parser {
* (metadata memberDefinition)*
*/
List<ClassMember> _parseClassMembers(String className, Token closingBracket) {
- List<ClassMember> members = new List<ClassMember>();
+ List<ClassMember> members = <ClassMember>[];
Token memberStart = _currentToken;
- while (!_matches(TokenType.EOF) &&
- !_matches(TokenType.CLOSE_CURLY_BRACKET) &&
+ TokenType type = _currentToken.type;
+ Keyword keyword = _currentToken.keyword;
+ while (type != TokenType.EOF &&
+ type != TokenType.CLOSE_CURLY_BRACKET &&
(closingBracket != null ||
- (!_matchesKeyword(Keyword.CLASS) &&
- !_matchesKeyword(Keyword.TYPEDEF)))) {
- if (_matches(TokenType.SEMICOLON)) {
+ (keyword != Keyword.CLASS && keyword != Keyword.TYPEDEF))) {
+ if (type == TokenType.SEMICOLON) {
_reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
[_currentToken.lexeme]);
_advance();
@@ -4889,6 +4876,8 @@ class Parser {
_advance();
}
memberStart = _currentToken;
+ type = _currentToken.type;
+ keyword = _currentToken.keyword;
}
return members;
}
@@ -4899,6 +4888,8 @@ class Parser {
* the 'abstract' keyword. The [classKeyword] is the token representing the
* 'class' keyword. Return the class type alias that was parsed.
*
+ * This method assumes that the current token matches an identifier.
+ *
* classTypeAlias ::=
* identifier typeParameters? '=' 'abstract'? mixinApplication
*
@@ -4907,11 +4898,36 @@ class Parser {
*/
ClassTypeAlias _parseClassTypeAlias(CommentAndMetadata commentAndMetadata,
Token abstractKeyword, Token classKeyword) {
- SimpleIdentifier className = parseSimpleIdentifier(isDeclaration: true);
+ SimpleIdentifier className =
+ _parseSimpleIdentifierUnchecked(isDeclaration: true);
TypeParameterList typeParameters = null;
if (_matches(TokenType.LT)) {
typeParameters = parseTypeParameterList();
}
+ return _parseClassTypeAliasAfterName(commentAndMetadata, abstractKeyword,
+ classKeyword, className, typeParameters);
+ }
+
+ /**
+ * Parse a class type alias. The [commentAndMetadata] is the metadata to be
+ * associated with the member. The [abstractKeyword] is the token representing
+ * the 'abstract' keyword. The [classKeyword] is the token representing the
+ * 'class' keyword. The [className] is the name of the alias, and the
+ * [typeParameters] are the type parameters following the name. Return the
+ * class type alias that was parsed.
+ *
+ * classTypeAlias ::=
+ * identifier typeParameters? '=' 'abstract'? mixinApplication
+ *
+ * mixinApplication ::=
+ * type withClause implementsClause? ';'
+ */
+ ClassTypeAlias _parseClassTypeAliasAfterName(
+ CommentAndMetadata commentAndMetadata,
+ Token abstractKeyword,
+ Token classKeyword,
+ SimpleIdentifier className,
+ TypeParameterList typeParameters) {
Token equals = _expect(TokenType.EQ);
TypeName superclass = parseTypeName();
WithClause withClause = null;
@@ -4957,19 +4973,20 @@ class Parser {
/**
* Parse a list of combinators in a directive. Return the combinators that
- * were parsed.
+ * were parsed, or `null` if there are no combinators.
*
* combinator ::=
* 'show' identifier (',' identifier)*
* | 'hide' identifier (',' identifier)*
*/
List<Combinator> _parseCombinators() {
- List<Combinator> combinators = new List<Combinator>();
+ List<Combinator> combinators = null;
while (true) {
Combinator combinator = parseCombinator();
if (combinator == null) {
break;
}
+ combinators ??= <Combinator>[];
combinators.add(combinator);
}
return combinators;
@@ -4986,18 +5003,20 @@ class Parser {
* annotation*
*/
CommentAndMetadata _parseCommentAndMetadata() {
- Comment comment = _parseDocumentationComment();
+ // TODO(brianwilkerson) Consider making the creation of documentation
+ // comments be lazy.
+ List<DocumentationCommentToken> tokens = _parseDocumentationCommentTokens();
List<Annotation> metadata = null;
while (_matches(TokenType.AT)) {
- metadata ??= new List<Annotation>();
+ metadata ??= <Annotation>[];
metadata.add(parseAnnotation());
- Comment optionalComment = _parseDocumentationComment();
- if (optionalComment != null) {
- comment = optionalComment;
+ List<DocumentationCommentToken> optionalTokens =
+ _parseDocumentationCommentTokens();
+ if (optionalTokens != null) {
+ tokens = optionalTokens;
}
}
- metadata ??= const <Annotation>[];
- return new CommentAndMetadata(comment, metadata);
+ return new CommentAndMetadata(_parseDocumentationComment(tokens), metadata);
}
/**
@@ -5051,16 +5070,19 @@ class Parser {
return null;
}
return new CommentReference(newKeyword, identifier);
- } else if (_tokenMatchesKeyword(firstToken, Keyword.THIS) ||
- _tokenMatchesKeyword(firstToken, Keyword.NULL) ||
- _tokenMatchesKeyword(firstToken, Keyword.TRUE) ||
- _tokenMatchesKeyword(firstToken, Keyword.FALSE)) {
- // TODO(brianwilkerson) If we want to support this we will need to
- // extend the definition of CommentReference to take an expression
- // rather than an identifier. For now we just ignore it to reduce the
- // number of errors produced, but that's probably not a valid long term
- // approach.
- return null;
+ } else {
+ Keyword keyword = firstToken.keyword;
+ if (keyword == Keyword.THIS ||
+ keyword == Keyword.NULL ||
+ keyword == Keyword.TRUE ||
+ keyword == Keyword.FALSE) {
+ // TODO(brianwilkerson) If we want to support this we will need to
+ // extend the definition of CommentReference to take an expression
+ // rather than an identifier. For now we just ignore it to reduce the
+ // number of errors produced, but that's probably not a valid long term
+ // approach.
+ return null;
+ }
}
} catch (exception) {
// Ignored because we assume that it wasn't a real comment reference.
@@ -5082,7 +5104,7 @@ class Parser {
*/
List<CommentReference> _parseCommentReferences(
List<DocumentationCommentToken> tokens) {
- List<CommentReference> references = new List<CommentReference>();
+ List<CommentReference> references = <CommentReference>[];
for (DocumentationCommentToken token in tokens) {
String comment = token.lexeme;
comment = _removeCodeBlocksGitHub(comment);
@@ -5158,32 +5180,41 @@ class Parser {
CompilationUnitMember _parseCompilationUnitMember(
CommentAndMetadata commentAndMetadata) {
Modifiers modifiers = _parseModifiers();
- if (_matchesKeyword(Keyword.CLASS)) {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.CLASS) {
return _parseClassDeclaration(
commentAndMetadata, _validateModifiersForClass(modifiers));
- } else if (_matchesKeyword(Keyword.TYPEDEF) &&
- !_tokenMatches(_peek(), TokenType.PERIOD) &&
- !_tokenMatches(_peek(), TokenType.LT) &&
- !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+ }
+ Token next = _peek();
+ TokenType nextType = next.type;
+ if (keyword == Keyword.TYPEDEF &&
+ nextType != TokenType.PERIOD &&
+ nextType != TokenType.LT &&
+ nextType != TokenType.OPEN_PAREN) {
_validateModifiersForTypedef(modifiers);
return _parseTypeAlias(commentAndMetadata);
- } else if (_matchesKeyword(Keyword.ENUM)) {
+ } else if (keyword == Keyword.ENUM) {
_validateModifiersForEnum(modifiers);
return _parseEnumDeclaration(commentAndMetadata);
- }
- if (_matchesKeyword(Keyword.VOID)) {
- TypeName returnType = parseReturnType();
- if ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) &&
- _tokenMatchesIdentifier(_peek())) {
+ } else if (keyword == Keyword.VOID) {
+ TypeName returnType =
+ new TypeName(new SimpleIdentifier(getAndAdvance()), null);
+ keyword = _currentToken.keyword;
+ next = _peek();
+ if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
+ _tokenMatchesIdentifier(next)) {
_validateModifiersForTopLevelFunction(modifiers);
return _parseFunctionDeclaration(
commentAndMetadata, modifiers.externalKeyword, returnType);
- } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
+ } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
_reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
- return _convertToFunctionDeclaration(_parseOperator(
- commentAndMetadata, modifiers.externalKeyword, returnType));
+ return _convertToFunctionDeclaration(_parseOperatorAfterKeyword(
+ commentAndMetadata,
+ modifiers.externalKeyword,
+ returnType,
+ getAndAdvance()));
} else if (_matchesIdentifier() &&
- _peek().matchesAny(const <TokenType>[
+ next.matchesAny(const <TokenType>[
TokenType.OPEN_PAREN,
TokenType.OPEN_CURLY_BRACKET,
TokenType.FUNCTION,
@@ -5197,7 +5228,7 @@ class Parser {
// We have found an error of some kind. Try to recover.
//
if (_matchesIdentifier()) {
- if (_peek().matchesAny(const <TokenType>[
+ if (next.matchesAny(const <TokenType>[
TokenType.EQ,
TokenType.COMMA,
TokenType.SEMICOLON
@@ -5218,15 +5249,18 @@ class Parser {
ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
return null;
}
- } else if ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) &&
- _tokenMatchesIdentifier(_peek())) {
+ } else if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
+ _tokenMatchesIdentifier(next)) {
_validateModifiersForTopLevelFunction(modifiers);
return _parseFunctionDeclaration(
commentAndMetadata, modifiers.externalKeyword, null);
- } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
+ } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
_reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
- return _convertToFunctionDeclaration(
- _parseOperator(commentAndMetadata, modifiers.externalKeyword, null));
+ return _convertToFunctionDeclaration(_parseOperatorAfterKeyword(
+ commentAndMetadata,
+ modifiers.externalKeyword,
+ null,
+ getAndAdvance()));
} else if (!_matchesIdentifier()) {
Token keyword = modifiers.varKeyword;
if (keyword == null) {
@@ -5240,9 +5274,9 @@ class Parser {
// We appear to have found an incomplete top-level variable declaration.
//
_reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
- List<VariableDeclaration> variables = new List<VariableDeclaration>();
- variables.add(
- new VariableDeclaration(_createSyntheticIdentifier(), null, null));
+ VariableDeclaration variable =
+ new VariableDeclaration(_createSyntheticIdentifier(), null, null);
+ List<VariableDeclaration> variables = <VariableDeclaration>[variable];
return new TopLevelVariableDeclaration(
commentAndMetadata.comment,
commentAndMetadata.metadata,
@@ -5254,12 +5288,12 @@ class Parser {
} else if (_isPeekGenericTypeParametersAndOpenParen()) {
return _parseFunctionDeclaration(
commentAndMetadata, modifiers.externalKeyword, null);
- } else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+ } else if (_tokenMatches(next, TokenType.OPEN_PAREN)) {
TypeName returnType = _parseOptionalTypeNameComment();
_validateModifiersForTopLevelFunction(modifiers);
return _parseFunctionDeclaration(
commentAndMetadata, modifiers.externalKeyword, returnType);
- } else if (_peek().matchesAny(const <TokenType>[
+ } else if (next.matchesAny(const <TokenType>[
TokenType.EQ,
TokenType.COMMA,
TokenType.SEMICOLON
@@ -5278,15 +5312,20 @@ class Parser {
_expect(TokenType.SEMICOLON));
}
TypeName returnType = parseReturnType();
- if ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) &&
- _tokenMatchesIdentifier(_peek())) {
+ keyword = _currentToken.keyword;
+ next = _peek();
+ if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
+ _tokenMatchesIdentifier(next)) {
_validateModifiersForTopLevelFunction(modifiers);
return _parseFunctionDeclaration(
commentAndMetadata, modifiers.externalKeyword, returnType);
- } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) {
+ } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
_reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
- return _convertToFunctionDeclaration(_parseOperator(
- commentAndMetadata, modifiers.externalKeyword, returnType));
+ return _convertToFunctionDeclaration(_parseOperatorAfterKeyword(
+ commentAndMetadata,
+ modifiers.externalKeyword,
+ returnType,
+ getAndAdvance()));
} else if (_matches(TokenType.AT)) {
return new TopLevelVariableDeclaration(
commentAndMetadata.comment,
@@ -5304,16 +5343,15 @@ class Parser {
} else {
semicolon = _createSyntheticToken(TokenType.SEMICOLON);
}
- List<VariableDeclaration> variables = new List<VariableDeclaration>();
- variables.add(
- new VariableDeclaration(_createSyntheticIdentifier(), null, null));
+ VariableDeclaration variable =
+ new VariableDeclaration(_createSyntheticIdentifier(), null, null);
+ List<VariableDeclaration> variables = <VariableDeclaration>[variable];
return new TopLevelVariableDeclaration(
commentAndMetadata.comment,
commentAndMetadata.metadata,
new VariableDeclarationList(null, null, null, returnType, variables),
semicolon);
- }
- if (_peek().matchesAny(const <TokenType>[
+ } else if (next.matchesAny(const <TokenType>[
TokenType.OPEN_PAREN,
TokenType.FUNCTION,
TokenType.OPEN_CURLY_BRACKET,
@@ -5334,6 +5372,8 @@ class Parser {
/**
* Parse a configuration in either an import or export directive.
*
+ * This method assumes that the current token matches `Keyword.IF`.
+ *
* configuration ::=
* 'if' '(' test ')' uri
*
@@ -5344,7 +5384,7 @@ class Parser {
* identifier ('.' identifier)*
*/
Configuration _parseConfiguration() {
- Token ifKeyword = _expectKeyword(Keyword.IF);
+ Token ifKeyword = getAndAdvance();
Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
DottedName name = _parseDottedName();
Token equalToken = null;
@@ -5364,12 +5404,13 @@ class Parser {
}
/**
- * Parse a list of configurations. If conditional directives are not
- * supported, return an empty list without attempting to parse anything.
+ * Parse a list of configurations. Return the configurations that were parsed,
+ * or `null` if there are no configurations.
*/
List<Configuration> _parseConfigurations() {
- List<Configuration> configurations = <Configuration>[];
+ List<Configuration> configurations = null;
while (_matchesKeyword(Keyword.IF)) {
+ configurations ??= <Configuration>[];
configurations.add(_parseConfiguration());
}
return configurations;
@@ -5378,19 +5419,22 @@ class Parser {
/**
* Parse a const expression. Return the const expression that was parsed.
*
+ * This method assumes that the current token matches `Keyword.CONST`.
+ *
* constExpression ::=
* instanceCreationExpression
* | listLiteral
* | mapLiteral
*/
Expression _parseConstExpression() {
- Token keyword = _expectKeyword(Keyword.CONST);
- if (_matches(TokenType.LT) || _injectGenericCommentTypeList()) {
+ Token keyword = getAndAdvance();
+ TokenType type = _currentToken.type;
+ if (type == TokenType.LT || _injectGenericCommentTypeList()) {
return _parseListOrMapLiteral(keyword);
- } else if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
- _matches(TokenType.INDEX)) {
+ } else if (type == TokenType.OPEN_SQUARE_BRACKET ||
+ type == TokenType.INDEX) {
return _parseListLiteral(keyword, null);
- } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+ } else if (type == TokenType.OPEN_CURLY_BRACKET) {
return _parseMapLiteral(keyword, null);
}
return _parseInstanceCreationExpression(keyword);
@@ -5410,26 +5454,28 @@ class Parser {
List<ConstructorInitializer> initializers = null;
if (_matches(TokenType.COLON)) {
separator = getAndAdvance();
- initializers = new List<ConstructorInitializer>();
+ initializers = <ConstructorInitializer>[];
do {
- if (_matchesKeyword(Keyword.THIS)) {
- if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.THIS) {
+ TokenType nextType = _peek().type;
+ if (nextType == TokenType.OPEN_PAREN) {
bodyAllowed = false;
- initializers.add(_parseRedirectingConstructorInvocation());
- } else if (_tokenMatches(_peek(), TokenType.PERIOD) &&
+ initializers.add(_parseRedirectingConstructorInvocation(false));
+ } else if (nextType == TokenType.PERIOD &&
_tokenMatches(_peekAt(3), TokenType.OPEN_PAREN)) {
bodyAllowed = false;
- initializers.add(_parseRedirectingConstructorInvocation());
+ initializers.add(_parseRedirectingConstructorInvocation(true));
} else {
- initializers.add(_parseConstructorFieldInitializer());
+ initializers.add(_parseConstructorFieldInitializer(true));
}
- } else if (_matchesKeyword(Keyword.SUPER)) {
+ } else if (keyword == Keyword.SUPER) {
initializers.add(_parseSuperConstructorInvocation());
} else if (_matches(TokenType.OPEN_CURLY_BRACKET) ||
_matches(TokenType.FUNCTION)) {
_reportErrorForCurrentToken(ParserErrorCode.MISSING_INITIALIZER);
} else {
- initializers.add(_parseConstructorFieldInitializer());
+ initializers.add(_parseConstructorFieldInitializer(false));
}
} while (_optional(TokenType.COMMA));
if (factoryKeyword != null) {
@@ -5492,54 +5538,55 @@ class Parser {
}
/**
- * Parse a field initializer within a constructor. Return the field
- * initializer that was parsed.
+ * Parse a field initializer within a constructor. The flag [hasThis] should
+ * be true if the current token is `this`. Return the field initializer that
+ * was parsed.
*
* fieldInitializer:
* ('this' '.')? identifier '=' conditionalExpression cascadeSection*
*/
- ConstructorFieldInitializer _parseConstructorFieldInitializer() {
- Token keyword = null;
+ ConstructorFieldInitializer _parseConstructorFieldInitializer(bool hasThis) {
+ Token keywordToken = null;
Token period = null;
- if (_matchesKeyword(Keyword.THIS)) {
- keyword = getAndAdvance();
+ if (hasThis) {
+ keywordToken = getAndAdvance();
period = _expect(TokenType.PERIOD);
}
SimpleIdentifier fieldName = parseSimpleIdentifier();
Token equals = null;
- if (_matches(TokenType.EQ)) {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.EQ) {
equals = getAndAdvance();
- } else if (!_matchesKeyword(Keyword.THIS) &&
- !_matchesKeyword(Keyword.SUPER) &&
- !_matches(TokenType.OPEN_CURLY_BRACKET) &&
- !_matches(TokenType.FUNCTION)) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_ASSIGNMENT_IN_INITIALIZER);
- equals = _createSyntheticToken(TokenType.EQ);
} else {
_reportErrorForCurrentToken(
ParserErrorCode.MISSING_ASSIGNMENT_IN_INITIALIZER);
- return new ConstructorFieldInitializer(keyword, period, fieldName,
- _createSyntheticToken(TokenType.EQ), _createSyntheticIdentifier());
+ Keyword keyword = _currentToken.keyword;
+ if (keyword != Keyword.THIS &&
+ keyword != Keyword.SUPER &&
+ type != TokenType.OPEN_CURLY_BRACKET &&
+ type != TokenType.FUNCTION) {
+ equals = _createSyntheticToken(TokenType.EQ);
+ } else {
+ return new ConstructorFieldInitializer(keywordToken, period, fieldName,
+ _createSyntheticToken(TokenType.EQ), _createSyntheticIdentifier());
+ }
}
bool wasInInitializer = _inInitializer;
_inInitializer = true;
try {
Expression expression = parseConditionalExpression();
- TokenType tokenType = _currentToken.type;
- if (tokenType == TokenType.PERIOD_PERIOD) {
- List<Expression> cascadeSections = new List<Expression>();
- while (tokenType == TokenType.PERIOD_PERIOD) {
+ if (_matches(TokenType.PERIOD_PERIOD)) {
+ List<Expression> cascadeSections = <Expression>[];
+ do {
Expression section = _parseCascadeSection();
if (section != null) {
cascadeSections.add(section);
}
- tokenType = _currentToken.type;
- }
+ } while (_matches(TokenType.PERIOD_PERIOD));
expression = new CascadeExpression(expression, cascadeSections);
}
return new ConstructorFieldInitializer(
- keyword, period, fieldName, equals, expression);
+ keywordToken, period, fieldName, equals, expression);
} finally {
_inInitializer = wasInInitializer;
}
@@ -5548,18 +5595,20 @@ class Parser {
/**
* Parse a continue statement. Return the continue statement that was parsed.
*
+ * This method assumes that the current token matches `Keyword.CONTINUE`.
+ *
* continueStatement ::=
* 'continue' identifier? ';'
*/
Statement _parseContinueStatement() {
- Token continueKeyword = _expectKeyword(Keyword.CONTINUE);
+ Token continueKeyword = getAndAdvance();
if (!_inLoop && !_inSwitch) {
_reportErrorForToken(
ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP, continueKeyword);
}
SimpleIdentifier label = null;
if (_matchesIdentifier()) {
- label = parseSimpleIdentifier();
+ label = _parseSimpleIdentifierUnchecked();
}
if (_inSwitch && !_inLoop && label == null) {
_reportErrorForToken(
@@ -5587,7 +5636,7 @@ class Parser {
} else if (_matchesKeyword(Keyword.LIBRARY)) {
return _parseLibraryDirective(commentAndMetadata);
} else if (_matchesKeyword(Keyword.PART)) {
- return _parsePartDirective(commentAndMetadata);
+ return _parsePartOrPartOfDirective(commentAndMetadata);
} else {
// Internal error: this method should not have been invoked if the current
// token was something other than one of the above.
@@ -5609,16 +5658,18 @@ class Parser {
if (_matches(TokenType.SCRIPT_TAG)) {
scriptTag = new ScriptTag(getAndAdvance());
}
- List<Directive> directives = new List<Directive>();
+ List<Directive> directives = <Directive>[];
while (!_matches(TokenType.EOF)) {
CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
- if ((_matchesKeyword(Keyword.IMPORT) ||
- _matchesKeyword(Keyword.EXPORT) ||
- _matchesKeyword(Keyword.LIBRARY) ||
- _matchesKeyword(Keyword.PART)) &&
- !_tokenMatches(_peek(), TokenType.PERIOD) &&
- !_tokenMatches(_peek(), TokenType.LT) &&
- !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+ Keyword keyword = _currentToken.keyword;
+ TokenType type = _peek().type;
+ if ((keyword == Keyword.IMPORT ||
+ keyword == Keyword.EXPORT ||
+ keyword == Keyword.LIBRARY ||
+ keyword == Keyword.PART) &&
+ type != TokenType.PERIOD &&
+ type != TokenType.LT &&
+ type != TokenType.OPEN_PAREN) {
directives.add(_parseDirective(commentAndMetadata));
} else if (_matches(TokenType.SEMICOLON)) {
_advance();
@@ -5626,12 +5677,29 @@ class Parser {
while (!_matches(TokenType.EOF)) {
_advance();
}
- return new CompilationUnit(firstToken, scriptTag, directives,
- new List<CompilationUnitMember>(), _currentToken);
+ return new CompilationUnit(
+ firstToken, scriptTag, directives, null, _currentToken);
}
}
- return new CompilationUnit(firstToken, scriptTag, directives,
- new List<CompilationUnitMember>(), _currentToken);
+ return new CompilationUnit(
+ firstToken, scriptTag, directives, null, _currentToken);
+ }
+
+ /**
+ * Parse a documentation comment based on the given list of documentation
+ * comment tokens. Return the documentation comment that was parsed, or `null`
+ * if there was no comment.
+ *
+ * documentationComment ::=
+ * multiLineComment?
+ * | singleLineComment*
+ */
+ Comment _parseDocumentationComment(List<DocumentationCommentToken> tokens) {
+ if (tokens == null) {
+ return null;
+ }
+ List<CommentReference> references = _parseCommentReferences(tokens);
+ return Comment.createDocumentationCommentWithReferences(tokens, references);
}
/**
@@ -5642,37 +5710,32 @@ class Parser {
* multiLineComment?
* | singleLineComment*
*/
- Comment _parseDocumentationComment() {
- List<DocumentationCommentToken> documentationTokens =
- <DocumentationCommentToken>[];
+ List<DocumentationCommentToken> _parseDocumentationCommentTokens() {
+ List<DocumentationCommentToken> tokens = <DocumentationCommentToken>[];
CommentToken commentToken = _currentToken.precedingComments;
while (commentToken != null) {
if (commentToken is DocumentationCommentToken) {
- if (documentationTokens.isNotEmpty) {
+ if (tokens.isNotEmpty) {
if (commentToken.type == TokenType.SINGLE_LINE_COMMENT) {
- if (documentationTokens[0].type != TokenType.SINGLE_LINE_COMMENT) {
- documentationTokens.clear();
+ if (tokens[0].type != TokenType.SINGLE_LINE_COMMENT) {
+ tokens.clear();
}
} else {
- documentationTokens.clear();
+ tokens.clear();
}
}
- documentationTokens.add(commentToken);
+ tokens.add(commentToken);
}
commentToken = commentToken.next;
}
- if (documentationTokens.isEmpty) {
- return null;
- }
- List<CommentReference> references =
- _parseCommentReferences(documentationTokens);
- return Comment.createDocumentationCommentWithReferences(
- documentationTokens, references);
+ return tokens.isEmpty ? null : tokens;
}
/**
* Parse a do statement. Return the do statement that was parsed.
*
+ * This method assumes that the current token matches `Keyword.DO`.
+ *
* doStatement ::=
* 'do' statement 'while' '(' expression ')' ';'
*/
@@ -5680,7 +5743,7 @@ class Parser {
bool wasInLoop = _inLoop;
_inLoop = true;
try {
- Token doKeyword = _expectKeyword(Keyword.DO);
+ Token doKeyword = getAndAdvance();
Statement body = parseStatement2();
Token whileKeyword = _expectKeyword(Keyword.WHILE);
Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
@@ -5701,10 +5764,10 @@ class Parser {
* identifier ('.' identifier)*
*/
DottedName _parseDottedName() {
- List<SimpleIdentifier> components = new List<SimpleIdentifier>();
- components.add(parseSimpleIdentifier());
- while (_matches(TokenType.PERIOD)) {
- _advance();
+ List<SimpleIdentifier> components = <SimpleIdentifier>[
+ parseSimpleIdentifier()
+ ];
+ while (_optional(TokenType.PERIOD)) {
components.add(parseSimpleIdentifier());
}
return new DottedName(components);
@@ -5713,20 +5776,36 @@ class Parser {
/**
* Parse an empty statement. Return the empty statement that was parsed.
*
+ * This method assumes that the current token matches `TokenType.SEMICOLON`.
+ *
* emptyStatement ::=
* ';'
*/
Statement _parseEmptyStatement() => new EmptyStatement(getAndAdvance());
+ /**
+ * Parse an enum constant declaration. Return the enum constant declaration
+ * that was parsed.
+ *
+ * Specified:
+ *
+ * enumConstant ::=
+ * id
+ *
+ * Actual:
+ *
+ * enumConstant ::=
+ * metadata id
+ */
EnumConstantDeclaration _parseEnumConstantDeclaration() {
CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
SimpleIdentifier name;
if (_matchesIdentifier()) {
- name = parseSimpleIdentifier(isDeclaration: true);
+ name = _parseSimpleIdentifierUnchecked(isDeclaration: true);
} else {
name = _createSyntheticIdentifier();
}
- if (commentAndMetadata.metadata.isNotEmpty) {
+ if (commentAndMetadata.hasMetadata) {
_reportErrorForNode(ParserErrorCode.ANNOTATION_ON_ENUM_CONSTANT,
commentAndMetadata.metadata[0]);
}
@@ -5738,18 +5817,19 @@ class Parser {
* Parse an enum declaration. The [commentAndMetadata] is the metadata to be
* associated with the member. Return the enum declaration that was parsed.
*
+ * This method assumes that the current token matches `Keyword.ENUM`.
+ *
* enumType ::=
* metadata 'enum' id '{' id (',' id)* (',')? '}'
*/
EnumDeclaration _parseEnumDeclaration(CommentAndMetadata commentAndMetadata) {
- Token keyword = _expectKeyword(Keyword.ENUM);
+ Token keyword = getAndAdvance();
SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
Token leftBracket = null;
- List<EnumConstantDeclaration> constants =
- new List<EnumConstantDeclaration>();
+ List<EnumConstantDeclaration> constants = <EnumConstantDeclaration>[];
Token rightBracket = null;
if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
- leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
+ leftBracket = getAndAdvance();
if (_matchesIdentifier() || _matches(TokenType.AT)) {
constants.add(_parseEnumConstantDeclaration());
} else if (_matches(TokenType.COMMA) &&
@@ -5792,7 +5872,7 @@ class Parser {
*/
Expression _parseEqualityExpression() {
Expression expression;
- if (_matchesKeyword(Keyword.SUPER) &&
+ if (_currentToken.keyword == Keyword.SUPER &&
_currentToken.next.type.isEqualityOperator) {
expression = new SuperExpression(getAndAdvance());
} else {
@@ -5800,13 +5880,12 @@ class Parser {
}
bool leftEqualityExpression = false;
while (_currentToken.type.isEqualityOperator) {
- Token operator = getAndAdvance();
if (leftEqualityExpression) {
_reportErrorForNode(
ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND, expression);
}
expression = new BinaryExpression(
- expression, operator, _parseRelationalExpression());
+ expression, getAndAdvance(), _parseRelationalExpression());
leftEqualityExpression = true;
}
return expression;
@@ -5816,11 +5895,13 @@ class Parser {
* Parse an export directive. The [commentAndMetadata] is the metadata to be
* associated with the directive. Return the export directive that was parsed.
*
+ * This method assumes that the current token matches `Keyword.EXPORT`.
+ *
* exportDirective ::=
* metadata 'export' stringLiteral configuration* combinator*';'
*/
ExportDirective _parseExportDirective(CommentAndMetadata commentAndMetadata) {
- Token exportKeyword = _expectKeyword(Keyword.EXPORT);
+ Token exportKeyword = getAndAdvance();
StringLiteral libraryUri = _parseUri();
List<Configuration> configurations = _parseConfigurations();
List<Combinator> combinators = _parseCombinators();
@@ -5842,8 +5923,7 @@ class Parser {
* expression (',' expression)*
*/
List<Expression> _parseExpressionList() {
- List<Expression> expressions = new List<Expression>();
- expressions.add(parseExpression2());
+ List<Expression> expressions = <Expression>[parseExpression2()];
while (_optional(TokenType.COMMA)) {
expressions.add(parseExpression2());
}
@@ -5862,23 +5942,24 @@ class Parser {
* | type
*/
FinalConstVarOrType _parseFinalConstVarOrType(bool optional) {
- Token keyword = null;
+ Token keywordToken = null;
TypeName type = null;
- if (_matchesKeyword(Keyword.FINAL) || _matchesKeyword(Keyword.CONST)) {
- keyword = getAndAdvance();
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.FINAL || keyword == Keyword.CONST) {
+ keywordToken = getAndAdvance();
if (_isTypedIdentifier(_currentToken)) {
type = parseTypeName();
} else {
// Support `final/*=T*/ x;`
type = _parseOptionalTypeNameComment();
}
- } else if (_matchesKeyword(Keyword.VAR)) {
- keyword = getAndAdvance();
+ } else if (keyword == Keyword.VAR) {
+ keywordToken = getAndAdvance();
// Support `var/*=T*/ x;`
type = _parseOptionalTypeNameComment();
if (type != null) {
// Clear the keyword to prevent an error.
- keyword = null;
+ keywordToken = null;
}
} else if (_isTypedIdentifier(_currentToken)) {
type = parseReturnType();
@@ -5890,7 +5971,7 @@ class Parser {
// This is not supported if the type is required.
type = _parseOptionalTypeNameComment();
}
- return new FinalConstVarOrType(keyword, type);
+ return new FinalConstVarOrType(keywordToken, type);
}
/**
@@ -5907,31 +5988,32 @@ class Parser {
*/
FormalParameter _parseFormalParameter(ParameterKind kind) {
NormalFormalParameter parameter = parseNormalFormalParameter();
- if (_matches(TokenType.EQ)) {
- Token seperator = getAndAdvance();
+ TokenType type = _currentToken.type;
+ if (type == TokenType.EQ) {
+ Token separator = getAndAdvance();
Expression defaultValue = parseExpression2();
if (kind == ParameterKind.NAMED) {
_reportErrorForToken(
- ParserErrorCode.WRONG_SEPARATOR_FOR_NAMED_PARAMETER, seperator);
+ ParserErrorCode.WRONG_SEPARATOR_FOR_NAMED_PARAMETER, separator);
} else if (kind == ParameterKind.REQUIRED) {
_reportErrorForNode(
ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP, parameter);
}
return new DefaultFormalParameter(
- parameter, kind, seperator, defaultValue);
- } else if (_matches(TokenType.COLON)) {
- Token seperator = getAndAdvance();
+ parameter, kind, separator, defaultValue);
+ } else if (type == TokenType.COLON) {
+ Token separator = getAndAdvance();
Expression defaultValue = parseExpression2();
if (kind == ParameterKind.POSITIONAL) {
_reportErrorForToken(
ParserErrorCode.WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER,
- seperator);
+ separator);
} else if (kind == ParameterKind.REQUIRED) {
_reportErrorForNode(
ParserErrorCode.NAMED_PARAMETER_OUTSIDE_GROUP, parameter);
}
return new DefaultFormalParameter(
- parameter, kind, seperator, defaultValue);
+ parameter, kind, separator, defaultValue);
} else if (kind != ParameterKind.REQUIRED) {
return new DefaultFormalParameter(parameter, kind, null, null);
}
@@ -5939,6 +6021,160 @@ class Parser {
}
/**
+ * Parse a list of formal parameters given that the list starts with the given
+ * [leftParenthesis]. Return the formal parameters that were parsed.
+ */
+ FormalParameterList _parseFormalParameterListAfterParen(
+ Token leftParenthesis) {
+ if (_matches(TokenType.CLOSE_PAREN)) {
+ return new FormalParameterList(
+ leftParenthesis, null, null, null, getAndAdvance());
+ }
+ //
+ // Even though it is invalid to have default parameters outside of brackets,
+ // required parameters inside of brackets, or multiple groups of default and
+ // named parameters, we allow all of these cases so that we can recover
+ // better.
+ //
+ List<FormalParameter> parameters = <FormalParameter>[];
+ Token leftSquareBracket = null;
+ Token rightSquareBracket = null;
+ Token leftCurlyBracket = null;
+ Token rightCurlyBracket = null;
+ ParameterKind kind = ParameterKind.REQUIRED;
+ bool firstParameter = true;
+ bool reportedMultiplePositionalGroups = false;
+ bool reportedMultipleNamedGroups = false;
+ bool reportedMixedGroups = false;
+ bool wasOptionalParameter = false;
+ Token initialToken = null;
+ do {
+ if (firstParameter) {
+ firstParameter = false;
+ } else if (!_optional(TokenType.COMMA)) {
+ // TODO(brianwilkerson) The token is wrong, we need to recover from this
+ // case.
+ if (_getEndToken(leftParenthesis) != null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.EXPECTED_TOKEN, [TokenType.COMMA.lexeme]);
+ } else {
+ _reportErrorForToken(ParserErrorCode.MISSING_CLOSING_PARENTHESIS,
+ _currentToken.previous);
+ break;
+ }
+ }
+ initialToken = _currentToken;
+ //
+ // Handle the beginning of parameter groups.
+ //
+ TokenType type = _currentToken.type;
+ if (type == TokenType.OPEN_SQUARE_BRACKET) {
+ wasOptionalParameter = true;
+ if (leftSquareBracket != null && !reportedMultiplePositionalGroups) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MULTIPLE_POSITIONAL_PARAMETER_GROUPS);
+ reportedMultiplePositionalGroups = true;
+ }
+ if (leftCurlyBracket != null && !reportedMixedGroups) {
+ _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
+ reportedMixedGroups = true;
+ }
+ leftSquareBracket = getAndAdvance();
+ kind = ParameterKind.POSITIONAL;
+ } else if (type == TokenType.OPEN_CURLY_BRACKET) {
+ wasOptionalParameter = true;
+ if (leftCurlyBracket != null && !reportedMultipleNamedGroups) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MULTIPLE_NAMED_PARAMETER_GROUPS);
+ reportedMultipleNamedGroups = true;
+ }
+ if (leftSquareBracket != null && !reportedMixedGroups) {
+ _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
+ reportedMixedGroups = true;
+ }
+ leftCurlyBracket = getAndAdvance();
+ kind = ParameterKind.NAMED;
+ }
+ //
+ // Parse and record the parameter.
+ //
+ FormalParameter parameter = _parseFormalParameter(kind);
+ parameters.add(parameter);
+ if (kind == ParameterKind.REQUIRED && wasOptionalParameter) {
+ _reportErrorForNode(
+ ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS, parameter);
+ }
+ //
+ // Handle the end of parameter groups.
+ //
+ // TODO(brianwilkerson) Improve the detection and reporting of missing and
+ // mismatched delimiters.
+ type = _currentToken.type;
+ if (type == TokenType.CLOSE_SQUARE_BRACKET) {
+ rightSquareBracket = getAndAdvance();
+ if (leftSquareBracket == null) {
+ if (leftCurlyBracket != null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
+ rightCurlyBracket = rightSquareBracket;
+ rightSquareBracket = null;
+ } else {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP,
+ ["["]);
+ }
+ }
+ kind = ParameterKind.REQUIRED;
+ } else if (type == TokenType.CLOSE_CURLY_BRACKET) {
+ rightCurlyBracket = getAndAdvance();
+ if (leftCurlyBracket == null) {
+ if (leftSquareBracket != null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
+ rightSquareBracket = rightCurlyBracket;
+ rightCurlyBracket = null;
+ } else {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP,
+ ["{"]);
+ }
+ }
+ kind = ParameterKind.REQUIRED;
+ }
+ } while (!_matches(TokenType.CLOSE_PAREN) &&
+ !identical(initialToken, _currentToken));
+ Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ //
+ // Check that the groups were closed correctly.
+ //
+ if (leftSquareBracket != null && rightSquareBracket == null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]);
+ }
+ if (leftCurlyBracket != null && rightCurlyBracket == null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
+ }
+ //
+ // Build the parameter list.
+ //
+ leftSquareBracket ??= leftCurlyBracket;
+ rightSquareBracket ??= rightCurlyBracket;
+ return new FormalParameterList(leftParenthesis, parameters,
+ leftSquareBracket, rightSquareBracket, rightParenthesis);
+ }
+
+ /**
+ * Parse a list of formal parameters. Return the formal parameters that were
+ * parsed.
+ *
+ * This method assumes that the current token matches `TokenType.OPEN_PAREN`.
+ */
+ FormalParameterList _parseFormalParameterListUnchecked() {
+ return _parseFormalParameterListAfterParen(getAndAdvance());
+ }
+
+ /**
* Parse a for statement. Return the for statement that was parsed.
*
* forStatement ::=
@@ -5970,19 +6206,20 @@ class Parser {
if (_matchesIdentifier() &&
(_tokenMatchesKeyword(_peek(), Keyword.IN) ||
_tokenMatches(_peek(), TokenType.COLON))) {
- List<VariableDeclaration> variables = new List<VariableDeclaration>();
- SimpleIdentifier variableName = parseSimpleIdentifier();
- variables.add(new VariableDeclaration(variableName, null, null));
+ SimpleIdentifier variableName = _parseSimpleIdentifierUnchecked();
variableList = new VariableDeclarationList(commentAndMetadata.comment,
- commentAndMetadata.metadata, null, null, variables);
+ commentAndMetadata.metadata, null, null, <VariableDeclaration>[
+ new VariableDeclaration(variableName, null, null)
+ ]);
} else if (_isInitializedVariableDeclaration()) {
variableList =
_parseVariableDeclarationListAfterMetadata(commentAndMetadata);
} else {
initialization = parseExpression2();
}
- if (_matchesKeyword(Keyword.IN) || _matches(TokenType.COLON)) {
- if (_matches(TokenType.COLON)) {
+ TokenType type = _currentToken.type;
+ if (_matchesKeyword(Keyword.IN) || type == TokenType.COLON) {
+ if (type == TokenType.COLON) {
_reportErrorForCurrentToken(ParserErrorCode.COLON_IN_PLACE_OF_IN);
}
DeclaredIdentifier loopVariable = null;
@@ -6014,7 +6251,7 @@ class Parser {
new SimpleIdentifier(variable.name.token,
isDeclaration: true));
} else {
- if (!commentAndMetadata.metadata.isEmpty) {
+ if (commentAndMetadata.hasMetadata) {
// TODO(jwren) metadata isn't allowed before the identifier in
// "identifier in expression", add warning if commentAndMetadata
// has content
@@ -6106,45 +6343,47 @@ class Parser {
_inLoop = false;
_inSwitch = false;
try {
- if (_matches(TokenType.SEMICOLON)) {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.SEMICOLON) {
if (!mayBeEmpty) {
_reportErrorForCurrentToken(emptyErrorCode);
}
return new EmptyFunctionBody(getAndAdvance());
- } else if (_matchesString(_NATIVE)) {
- Token nativeToken = getAndAdvance();
- StringLiteral stringLiteral = null;
- if (_matches(TokenType.STRING)) {
- stringLiteral = parseStringLiteral();
- }
- return new NativeFunctionBody(
- nativeToken, stringLiteral, _expect(TokenType.SEMICOLON));
}
Token keyword = null;
Token star = null;
- if (_matchesString(ASYNC)) {
- keyword = getAndAdvance();
- if (!_parseAsync) {
- _reportErrorForToken(ParserErrorCode.ASYNC_NOT_SUPPORTED, keyword);
- }
- if (_matches(TokenType.STAR)) {
- star = getAndAdvance();
- _inGenerator = true;
- }
- _inAsync = true;
- } else if (_matchesString(SYNC)) {
- keyword = getAndAdvance();
- if (!_parseAsync) {
- _reportErrorForToken(ParserErrorCode.ASYNC_NOT_SUPPORTED, keyword);
- }
- if (_matches(TokenType.STAR)) {
- star = getAndAdvance();
- _inGenerator = true;
+ bool foundAsync = false;
+ bool foundSync = false;
+ if (type == TokenType.IDENTIFIER) {
+ String lexeme = _currentToken.lexeme;
+ if (lexeme == ASYNC) {
+ foundAsync = true;
+ keyword = getAndAdvance();
+ if (!_parseAsync) {
+ _reportErrorForToken(ParserErrorCode.ASYNC_NOT_SUPPORTED, keyword);
+ }
+ if (_matches(TokenType.STAR)) {
+ star = getAndAdvance();
+ _inGenerator = true;
+ }
+ type = _currentToken.type;
+ _inAsync = true;
+ } else if (lexeme == SYNC) {
+ foundSync = true;
+ keyword = getAndAdvance();
+ if (!_parseAsync) {
+ _reportErrorForToken(ParserErrorCode.ASYNC_NOT_SUPPORTED, keyword);
+ }
+ if (_matches(TokenType.STAR)) {
+ star = getAndAdvance();
+ _inGenerator = true;
+ }
+ type = _currentToken.type;
}
}
- if (_matches(TokenType.FUNCTION)) {
+ if (type == TokenType.FUNCTION) {
if (keyword != null) {
- if (!_tokenMatchesString(keyword, ASYNC)) {
+ if (!foundAsync) {
_reportErrorForToken(ParserErrorCode.INVALID_SYNC, keyword);
keyword = null;
} else if (star != null) {
@@ -6169,9 +6408,9 @@ class Parser {
}
return new ExpressionFunctionBody(
keyword, functionDefinition, expression, semicolon);
- } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+ } else if (type == TokenType.OPEN_CURLY_BRACKET) {
if (keyword != null) {
- if (_tokenMatchesString(keyword, SYNC) && star == null) {
+ if (foundSync && star == null) {
_reportErrorForToken(
ParserErrorCode.MISSING_STAR_AFTER_SYNC, keyword);
}
@@ -6182,6 +6421,14 @@ class Parser {
_createSyntheticToken(TokenType.SEMICOLON));
}
return new BlockFunctionBody(keyword, star, parseBlock());
+ } else if (_matchesString(_NATIVE)) {
+ Token nativeToken = getAndAdvance();
+ StringLiteral stringLiteral = null;
+ if (_matches(TokenType.STRING)) {
+ stringLiteral = _parseStringLiteralUnchecked();
+ }
+ return new NativeFunctionBody(
+ nativeToken, stringLiteral, _expect(TokenType.SEMICOLON));
} else {
// Invalid function body
_reportErrorForCurrentToken(emptyErrorCode);
@@ -6213,22 +6460,28 @@ class Parser {
CommentAndMetadata commentAndMetadata,
Token externalKeyword,
TypeName returnType) {
- Token keyword = null;
+ Token keywordToken = null;
bool isGetter = false;
- if (_matchesKeyword(Keyword.GET) &&
- !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
- keyword = getAndAdvance();
+ Keyword keyword = _currentToken.keyword;
+ SimpleIdentifier name = null;
+ if (keyword == Keyword.GET) {
+ keywordToken = getAndAdvance();
isGetter = true;
- } else if (_matchesKeyword(Keyword.SET) &&
- !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
- keyword = getAndAdvance();
+ } else if (keyword == Keyword.SET) {
+ keywordToken = getAndAdvance();
+ }
+ if (keywordToken != null && _matches(TokenType.OPEN_PAREN)) {
+ name = new SimpleIdentifier(keywordToken, isDeclaration: true);
+ keywordToken = null;
+ isGetter = false;
+ } else {
+ name = parseSimpleIdentifier(isDeclaration: true);
}
- SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
FormalParameterList parameters = null;
if (!isGetter) {
if (_matches(TokenType.OPEN_PAREN)) {
- parameters = parseFormalParameterList();
+ parameters = _parseFormalParameterListUnchecked();
_validateFormalParameterList(parameters);
} else {
_reportErrorForCurrentToken(
@@ -6242,7 +6495,7 @@ class Parser {
}
} else if (_matches(TokenType.OPEN_PAREN)) {
_reportErrorForCurrentToken(ParserErrorCode.GETTER_WITH_PARAMETERS);
- parseFormalParameterList();
+ _parseFormalParameterListUnchecked();
}
FunctionBody body;
if (externalKeyword == null) {
@@ -6261,7 +6514,7 @@ class Parser {
commentAndMetadata.metadata,
externalKeyword,
returnType,
- keyword,
+ keywordToken,
name,
new FunctionExpression(typeParameters, parameters, body));
}
@@ -6328,7 +6581,8 @@ class Parser {
if (_matches(TokenType.LT)) {
typeParameters = parseTypeParameterList();
}
- if (_matches(TokenType.SEMICOLON) || _matches(TokenType.EOF)) {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.SEMICOLON || type == TokenType.EOF) {
_reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS);
FormalParameterList parameters = new FormalParameterList(
_createSyntheticToken(TokenType.OPEN_PAREN),
@@ -6346,12 +6600,24 @@ class Parser {
typeParameters,
parameters,
semicolon);
- } else if (!_matches(TokenType.OPEN_PAREN)) {
+ } else if (type == TokenType.OPEN_PAREN) {
+ FormalParameterList parameters = _parseFormalParameterListUnchecked();
+ _validateFormalParameterList(parameters);
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new FunctionTypeAlias(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ keyword,
+ returnType,
+ name,
+ typeParameters,
+ parameters,
+ semicolon);
+ } else {
_reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS);
- // TODO(brianwilkerson) Recover from this error. At the very least we
- // should skip to the start of the next valid compilation unit member,
- // allowing for the possibility of finding the typedef parameters before
- // that point.
+ // Recovery: At the very least we should skip to the start of the next
+ // valid compilation unit member, allowing for the possibility of finding
+ // the typedef parameters before that point.
return new FunctionTypeAlias(
commentAndMetadata.comment,
commentAndMetadata.metadata,
@@ -6363,18 +6629,6 @@ class Parser {
null, null, null, _createSyntheticToken(TokenType.CLOSE_PAREN)),
_createSyntheticToken(TokenType.SEMICOLON));
}
- FormalParameterList parameters = parseFormalParameterList();
- _validateFormalParameterList(parameters);
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new FunctionTypeAlias(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- keyword,
- returnType,
- name,
- typeParameters,
- parameters,
- semicolon);
}
/**
@@ -6416,6 +6670,8 @@ class Parser {
* been parsed, or `null` if there was no return type. Return the getter that
* was parsed.
*
+ * This method assumes that the current token matches `Keyword.GET`.
+ *
* getter ::=
* getterSignature functionBody?
*
@@ -6424,7 +6680,7 @@ class Parser {
*/
MethodDeclaration _parseGetter(CommentAndMetadata commentAndMetadata,
Token externalKeyword, Token staticKeyword, TypeName returnType) {
- Token propertyKeyword = _expectKeyword(Keyword.GET);
+ Token propertyKeyword = getAndAdvance();
SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
if (_matches(TokenType.OPEN_PAREN) &&
_tokenMatches(_peek(), TokenType.CLOSE_PAREN)) {
@@ -6461,10 +6717,10 @@ class Parser {
* identifier (',' identifier)*
*/
List<SimpleIdentifier> _parseIdentifierList() {
- List<SimpleIdentifier> identifiers = new List<SimpleIdentifier>();
- identifiers.add(parseSimpleIdentifier());
- while (_matches(TokenType.COMMA)) {
- _advance();
+ List<SimpleIdentifier> identifiers = <SimpleIdentifier>[
+ parseSimpleIdentifier()
+ ];
+ while (_optional(TokenType.COMMA)) {
identifiers.add(parseSimpleIdentifier());
}
return identifiers;
@@ -6473,11 +6729,13 @@ class Parser {
/**
* Parse an if statement. Return the if statement that was parsed.
*
+ * This method assumes that the current token matches `Keyword.IF`.
+ *
* ifStatement ::=
* 'if' '(' expression ')' statement ('else' statement)?
*/
Statement _parseIfStatement() {
- Token ifKeyword = _expectKeyword(Keyword.IF);
+ Token ifKeyword = getAndAdvance();
Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
Expression condition = parseExpression2();
Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
@@ -6496,11 +6754,13 @@ class Parser {
* Parse an import directive. The [commentAndMetadata] is the metadata to be
* associated with the directive. Return the import directive that was parsed.
*
+ * This method assumes that the current token matches `Keyword.IMPORT`.
+ *
* importDirective ::=
* metadata 'import' stringLiteral configuration* (deferred)? ('as' identifier)? combinator*';'
*/
ImportDirective _parseImportDirective(CommentAndMetadata commentAndMetadata) {
- Token importKeyword = _expectKeyword(Keyword.IMPORT);
+ Token importKeyword = getAndAdvance();
StringLiteral libraryUri = _parseUri();
List<Configuration> configurations = _parseConfigurations();
Token deferredToken = null;
@@ -6590,7 +6850,7 @@ class Parser {
*/
InstanceCreationExpression _parseInstanceCreationExpression(Token keyword) {
ConstructorName constructorName = parseConstructorName();
- ArgumentList argumentList = parseArgumentList();
+ ArgumentList argumentList = _parseArgumentListChecked();
return new InstanceCreationExpression(
keyword, constructorName, argumentList);
}
@@ -6600,12 +6860,14 @@ class Parser {
* associated with the directive. Return the library directive that was
* parsed.
*
+ * This method assumes that the current token matches `Keyword.LIBRARY`.
+ *
* libraryDirective ::=
* metadata 'library' identifier ';'
*/
LibraryDirective _parseLibraryDirective(
CommentAndMetadata commentAndMetadata) {
- Token keyword = _expectKeyword(Keyword.LIBRARY);
+ Token keyword = getAndAdvance();
LibraryIdentifier libraryName = _parseLibraryName(
ParserErrorCode.MISSING_NAME_IN_LIBRARY_DIRECTIVE, keyword);
Token semicolon = _expect(TokenType.SEMICOLON);
@@ -6627,17 +6889,15 @@ class Parser {
if (_matchesIdentifier()) {
return parseLibraryIdentifier();
} else if (_matches(TokenType.STRING)) {
- // TODO(brianwilkerson) Recovery: This should be extended to handle
- // arbitrary tokens until we can find a token that can start a compilation
- // unit member.
+ // Recovery: This should be extended to handle arbitrary tokens until we
+ // can find a token that can start a compilation unit member.
StringLiteral string = parseStringLiteral();
_reportErrorForNode(ParserErrorCode.NON_IDENTIFIER_LIBRARY_NAME, string);
} else {
_reportErrorForToken(missingNameError, missingNameToken);
}
- List<SimpleIdentifier> components = new List<SimpleIdentifier>();
- components.add(_createSyntheticIdentifier());
- return new LibraryIdentifier(components);
+ return new LibraryIdentifier(
+ <SimpleIdentifier>[_createSyntheticIdentifier()]);
}
/**
@@ -6646,13 +6906,16 @@ class Parser {
* is the type arguments appearing before the literal, or `null` if there are
* no type arguments. Return the list literal that was parsed.
*
+ * This method assumes that the current token matches either
+ * `TokenType.OPEN_SQUARE_BRACKET` or `TokenType.INDEX`.
+ *
* listLiteral ::=
* 'const'? typeArguments? '[' (expressionList ','?)? ']'
*/
ListLiteral _parseListLiteral(
Token modifier, TypeArgumentList typeArguments) {
- // may be empty list literal
if (_matches(TokenType.INDEX)) {
+ // Split the token into two separate tokens.
BeginToken leftBracket = _createToken(
_currentToken, TokenType.OPEN_SQUARE_BRACKET,
isBegin: true);
@@ -6666,8 +6929,7 @@ class Parser {
return new ListLiteral(
modifier, typeArguments, leftBracket, null, rightBracket);
}
- // open
- Token leftBracket = _expect(TokenType.OPEN_SQUARE_BRACKET);
+ Token leftBracket = getAndAdvance();
if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
return new ListLiteral(
modifier, typeArguments, leftBracket, null, getAndAdvance());
@@ -6675,8 +6937,7 @@ class Parser {
bool wasInInitializer = _inInitializer;
_inInitializer = false;
try {
- List<Expression> elements = new List<Expression>();
- elements.add(parseExpression2());
+ List<Expression> elements = <Expression>[parseExpression2()];
while (_optional(TokenType.COMMA)) {
if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
return new ListLiteral(
@@ -6727,10 +6988,9 @@ class Parser {
*/
Expression _parseLogicalAndExpression() {
Expression expression = _parseEqualityExpression();
- while (_matches(TokenType.AMPERSAND_AMPERSAND)) {
- Token operator = getAndAdvance();
+ while (_currentToken.type == TokenType.AMPERSAND_AMPERSAND) {
expression = new BinaryExpression(
- expression, operator, _parseEqualityExpression());
+ expression, getAndAdvance(), _parseEqualityExpression());
}
return expression;
}
@@ -6741,20 +7001,22 @@ class Parser {
* is the type arguments that were declared, or `null` if there are no type
* arguments. Return the map literal that was parsed.
*
+ * This method assumes that the current token matches
+ * `TokenType.OPEN_CURLY_BRACKET`.
+ *
* mapLiteral ::=
* 'const'? typeArguments? '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}'
*/
MapLiteral _parseMapLiteral(Token modifier, TypeArgumentList typeArguments) {
- Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
- List<MapLiteralEntry> entries = new List<MapLiteralEntry>();
+ Token leftBracket = getAndAdvance();
if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
return new MapLiteral(
- modifier, typeArguments, leftBracket, entries, getAndAdvance());
+ modifier, typeArguments, leftBracket, null, getAndAdvance());
}
bool wasInInitializer = _inInitializer;
_inInitializer = false;
try {
- entries.add(parseMapLiteralEntry());
+ List<MapLiteralEntry> entries = <MapLiteralEntry>[parseMapLiteralEntry()];
while (_optional(TokenType.COMMA)) {
if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
return new MapLiteral(
@@ -6838,9 +7100,11 @@ class Parser {
SimpleIdentifier methodName = parseSimpleIdentifier(isDeclaration: true);
TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
FormalParameterList parameters;
- if (!_matches(TokenType.OPEN_PAREN) &&
- (_matches(TokenType.OPEN_CURLY_BRACKET) ||
- _matches(TokenType.FUNCTION))) {
+ TokenType type = _currentToken.type;
+ // TODO(brianwilkerson) Figure out why we care what the current token is if
+ // it isn't a paren.
+ if (type != TokenType.OPEN_PAREN &&
+ (type == TokenType.OPEN_CURLY_BRACKET || type == TokenType.FUNCTION)) {
_reportErrorForToken(
ParserErrorCode.MISSING_METHOD_PARAMETERS, _currentToken.previous);
parameters = new FormalParameterList(
@@ -6878,12 +7142,14 @@ class Parser {
Modifiers modifiers = new Modifiers();
bool progress = true;
while (progress) {
- if (_tokenMatches(_peek(), TokenType.PERIOD) ||
- _tokenMatches(_peek(), TokenType.LT) ||
- _tokenMatches(_peek(), TokenType.OPEN_PAREN)) {
+ TokenType nextType = _peek().type;
+ if (nextType == TokenType.PERIOD ||
+ nextType == TokenType.LT ||
+ nextType == TokenType.OPEN_PAREN) {
return modifiers;
}
- if (_matchesKeyword(Keyword.ABSTRACT)) {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.ABSTRACT) {
if (modifiers.abstractKeyword != null) {
_reportErrorForCurrentToken(
ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
@@ -6891,7 +7157,7 @@ class Parser {
} else {
modifiers.abstractKeyword = getAndAdvance();
}
- } else if (_matchesKeyword(Keyword.CONST)) {
+ } else if (keyword == Keyword.CONST) {
if (modifiers.constKeyword != null) {
_reportErrorForCurrentToken(
ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
@@ -6899,9 +7165,7 @@ class Parser {
} else {
modifiers.constKeyword = getAndAdvance();
}
- } else if (_matchesKeyword(Keyword.EXTERNAL) &&
- !_tokenMatches(_peek(), TokenType.PERIOD) &&
- !_tokenMatches(_peek(), TokenType.LT)) {
+ } else if (keyword == Keyword.EXTERNAL) {
if (modifiers.externalKeyword != null) {
_reportErrorForCurrentToken(
ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
@@ -6909,9 +7173,7 @@ class Parser {
} else {
modifiers.externalKeyword = getAndAdvance();
}
- } else if (_matchesKeyword(Keyword.FACTORY) &&
- !_tokenMatches(_peek(), TokenType.PERIOD) &&
- !_tokenMatches(_peek(), TokenType.LT)) {
+ } else if (keyword == Keyword.FACTORY) {
if (modifiers.factoryKeyword != null) {
_reportErrorForCurrentToken(
ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
@@ -6919,7 +7181,7 @@ class Parser {
} else {
modifiers.factoryKeyword = getAndAdvance();
}
- } else if (_matchesKeyword(Keyword.FINAL)) {
+ } else if (keyword == Keyword.FINAL) {
if (modifiers.finalKeyword != null) {
_reportErrorForCurrentToken(
ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
@@ -6927,9 +7189,7 @@ class Parser {
} else {
modifiers.finalKeyword = getAndAdvance();
}
- } else if (_matchesKeyword(Keyword.STATIC) &&
- !_tokenMatches(_peek(), TokenType.PERIOD) &&
- !_tokenMatches(_peek(), TokenType.LT)) {
+ } else if (keyword == Keyword.STATIC) {
if (modifiers.staticKeyword != null) {
_reportErrorForCurrentToken(
ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
@@ -6937,7 +7197,7 @@ class Parser {
} else {
modifiers.staticKeyword = getAndAdvance();
}
- } else if (_matchesKeyword(Keyword.VAR)) {
+ } else if (keyword == Keyword.VAR) {
if (modifiers.varKeyword != null) {
_reportErrorForCurrentToken(
ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
@@ -6962,16 +7222,15 @@ class Parser {
*/
Expression _parseMultiplicativeExpression() {
Expression expression;
- if (_matchesKeyword(Keyword.SUPER) &&
+ if (_currentToken.keyword == Keyword.SUPER &&
_currentToken.next.type.isMultiplicativeOperator) {
expression = new SuperExpression(getAndAdvance());
} else {
expression = _parseUnaryExpression();
}
while (_currentToken.type.isMultiplicativeOperator) {
- Token operator = getAndAdvance();
- expression =
- new BinaryExpression(expression, operator, _parseUnaryExpression());
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), _parseUnaryExpression());
}
return expression;
}
@@ -6979,6 +7238,8 @@ class Parser {
/**
* Parse a class native clause. Return the native clause that was parsed.
*
+ * This method assumes that the current token matches `_NATIVE`.
+ *
* classNativeClause ::=
* 'native' name
*/
@@ -6991,11 +7252,13 @@ class Parser {
/**
* Parse a new expression. Return the new expression that was parsed.
*
+ * This method assumes that the current token matches `Keyword.NEW`.
+ *
* newExpression ::=
* instanceCreationExpression
*/
InstanceCreationExpression _parseNewExpression() =>
- _parseInstanceCreationExpression(_expectKeyword(Keyword.NEW));
+ _parseInstanceCreationExpression(getAndAdvance());
/**
* Parse a non-labeled statement. Return the non-labeled statement that was
@@ -7020,7 +7283,8 @@ class Parser {
Statement _parseNonLabeledStatement() {
// TODO(brianwilkerson) Pass the comment and metadata on where appropriate.
CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
- if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.OPEN_CURLY_BRACKET) {
if (_tokenMatches(_peek(), TokenType.STRING)) {
Token afterString = _skipStringLiteral(_currentToken.next);
if (afterString != null && afterString.type == TokenType.COLON) {
@@ -7029,7 +7293,7 @@ class Parser {
}
}
return parseBlock();
- } else if (_matches(TokenType.KEYWORD) &&
+ } else if (type == TokenType.KEYWORD &&
!_currentToken.keyword.isPseudoKeyword) {
Keyword keyword = _currentToken.keyword;
// TODO(jwren) compute some metrics to figure out a better order for this
@@ -7064,9 +7328,11 @@ class Parser {
return _parseVariableDeclarationStatementAfterMetadata(
commentAndMetadata);
} else if (keyword == Keyword.VOID) {
- TypeName returnType = parseReturnType();
+ TypeName returnType =
+ new TypeName(new SimpleIdentifier(getAndAdvance()), null);
+ Token next = _currentToken.next;
if (_matchesIdentifier() &&
- _peek().matchesAny(const <TokenType>[
+ next.matchesAny(const <TokenType>[
TokenType.OPEN_PAREN,
TokenType.OPEN_CURLY_BRACKET,
TokenType.FUNCTION,
@@ -7079,7 +7345,7 @@ class Parser {
// We have found an error of some kind. Try to recover.
//
if (_matchesIdentifier()) {
- if (_peek().matchesAny(const <TokenType>[
+ if (next.matchesAny(const <TokenType>[
TokenType.EQ,
TokenType.COMMA,
TokenType.SEMICOLON
@@ -7104,7 +7370,8 @@ class Parser {
return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
}
} else if (keyword == Keyword.CONST) {
- if (_peek().matchesAny(const <TokenType>[
+ Token next = _currentToken.next;
+ if (next.matchesAny(const <TokenType>[
TokenType.LT,
TokenType.OPEN_CURLY_BRACKET,
TokenType.OPEN_SQUARE_BRACKET,
@@ -7112,8 +7379,8 @@ class Parser {
])) {
return new ExpressionStatement(
parseExpression2(), _expect(TokenType.SEMICOLON));
- } else if (_tokenMatches(_peek(), TokenType.IDENTIFIER)) {
- Token afterType = _skipTypeName(_peek());
+ } else if (_tokenMatches(next, TokenType.IDENTIFIER)) {
+ Token afterType = _skipTypeName(next);
if (afterType != null) {
if (_tokenMatches(afterType, TokenType.OPEN_PAREN) ||
(_tokenMatches(afterType, TokenType.PERIOD) &&
@@ -7158,14 +7425,14 @@ class Parser {
CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT, awaitToken);
}
return statement;
- } else if (_matches(TokenType.SEMICOLON)) {
+ } else if (type == TokenType.SEMICOLON) {
return _parseEmptyStatement();
} else if (_isInitializedVariableDeclaration()) {
return _parseVariableDeclarationStatementAfterMetadata(
commentAndMetadata);
} else if (_isFunctionDeclaration()) {
return _parseFunctionDeclarationStatement();
- } else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+ } else if (type == TokenType.CLOSE_CURLY_BRACKET) {
_reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT);
return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
} else {
@@ -7197,6 +7464,29 @@ class Parser {
ParserErrorCode.MISSING_KEYWORD_OPERATOR, _currentToken);
operatorKeyword = _createSyntheticKeyword(Keyword.OPERATOR);
}
+ return _parseOperatorAfterKeyword(
+ commentAndMetadata, externalKeyword, returnType, operatorKeyword);
+ }
+
+ /**
+ * Parse an operator declaration starting after the 'operator' keyword. The
+ * [commentAndMetadata] is the documentation comment and metadata to be
+ * associated with the declaration. The [externalKeyword] is the 'external'
+ * token. The [returnType] is the return type that has already been parsed, or
+ * `null` if there was no return type. The [operatorKeyword] is the 'operator'
+ * keyword. Return the operator declaration that was parsed.
+ *
+ * operatorDeclaration ::=
+ * operatorSignature (';' | functionBody)
+ *
+ * operatorSignature ::=
+ * 'external'? returnType? 'operator' operator formalParameterList
+ */
+ MethodDeclaration _parseOperatorAfterKeyword(
+ CommentAndMetadata commentAndMetadata,
+ Token externalKeyword,
+ TypeName returnType,
+ Token operatorKeyword) {
if (!_currentToken.isUserDefinableOperator) {
_reportErrorForCurrentToken(
ParserErrorCode.NON_USER_DEFINABLE_OPERATOR, [_currentToken.lexeme]);
@@ -7242,21 +7532,27 @@ class Parser {
TypeName typeComment = _parseOptionalTypeNameComment();
if (typeComment != null) {
return typeComment;
- } else if (_matchesKeyword(Keyword.VOID)) {
- return parseReturnType();
- } else if (_matchesIdentifier() &&
- !_matchesKeyword(Keyword.GET) &&
- !_matchesKeyword(Keyword.SET) &&
- !_matchesKeyword(Keyword.OPERATOR) &&
- (_tokenMatchesIdentifier(_peek()) ||
- _tokenMatches(_peek(), TokenType.LT))) {
- return parseReturnType();
- } else if (_matchesIdentifier() &&
- _tokenMatches(_peek(), TokenType.PERIOD) &&
- _tokenMatchesIdentifier(_peekAt(2)) &&
- (_tokenMatchesIdentifier(_peekAt(3)) ||
- _tokenMatches(_peekAt(3), TokenType.LT))) {
- return parseReturnType();
+ }
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.VOID) {
+ return new TypeName(new SimpleIdentifier(getAndAdvance()), null);
+ } else if (_matchesIdentifier()) {
+ Token next = _peek();
+ if (keyword != Keyword.GET &&
+ keyword != Keyword.SET &&
+ keyword != Keyword.OPERATOR &&
+ (_tokenMatchesIdentifier(next) ||
+ _tokenMatches(next, TokenType.LT))) {
+ return parseReturnType();
+ }
+ Token next2 = next.next;
+ Token next3 = next2.next;
+ if (_tokenMatches(next, TokenType.PERIOD) &&
+ _tokenMatchesIdentifier(next2) &&
+ (_tokenMatchesIdentifier(next3) ||
+ _tokenMatches(next3, TokenType.LT))) {
+ return parseReturnType();
+ }
}
return null;
}
@@ -7280,35 +7576,67 @@ class Parser {
}
/**
+ * Parse a part directive. The [commentAndMetadata] is the metadata to be
+ * associated with the directive. Return the part or part-of directive that
+ * was parsed.
+ *
+ * This method assumes that the current token matches `Keyword.PART`.
+ *
+ * partDirective ::=
+ * metadata 'part' stringLiteral ';'
+ */
+ Directive _parsePartDirective(CommentAndMetadata commentAndMetadata) {
+ Token partKeyword = getAndAdvance();
+ StringLiteral partUri = _parseUri();
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new PartDirective(commentAndMetadata.comment,
+ commentAndMetadata.metadata, partKeyword, partUri, semicolon);
+ }
+
+ /**
+ * Parse a part-of directive. The [commentAndMetadata] is the metadata to be
+ * associated with the directive. Return the part or part-of directive that
+ * was parsed.
+ *
+ * This method assumes that the current token matches [Keyword.PART] and that
+ * the following token matches the identifier 'of'.
+ *
+ * partOfDirective ::=
+ * metadata 'part' 'of' identifier ';'
+ */
+ Directive _parsePartOfDirective(CommentAndMetadata commentAndMetadata) {
+ Token partKeyword = getAndAdvance();
+ Token ofKeyword = getAndAdvance();
+ LibraryIdentifier libraryName = _parseLibraryName(
+ ParserErrorCode.MISSING_NAME_IN_PART_OF_DIRECTIVE, ofKeyword);
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new PartOfDirective(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ partKeyword,
+ ofKeyword,
+ libraryName,
+ semicolon);
+ }
+
+ /**
* Parse a part or part-of directive. The [commentAndMetadata] is the metadata
* to be associated with the directive. Return the part or part-of directive
* that was parsed.
*
+ * This method assumes that the current token matches `Keyword.PART`.
+ *
* partDirective ::=
* metadata 'part' stringLiteral ';'
*
* partOfDirective ::=
* metadata 'part' 'of' identifier ';'
*/
- Directive _parsePartDirective(CommentAndMetadata commentAndMetadata) {
- Token partKeyword = _expectKeyword(Keyword.PART);
- if (_matchesString(_OF)) {
- Token ofKeyword = getAndAdvance();
- LibraryIdentifier libraryName = _parseLibraryName(
- ParserErrorCode.MISSING_NAME_IN_PART_OF_DIRECTIVE, ofKeyword);
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new PartOfDirective(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- partKeyword,
- ofKeyword,
- libraryName,
- semicolon);
+ Directive _parsePartOrPartOfDirective(CommentAndMetadata commentAndMetadata) {
+ if (_tokenMatchesString(_peek(), _OF)) {
+ return _parsePartOfDirective(commentAndMetadata);
}
- StringLiteral partUri = _parseUri();
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new PartDirective(commentAndMetadata.comment,
- commentAndMetadata.metadata, partKeyword, partUri, semicolon);
+ return _parsePartDirective(commentAndMetadata);
}
/**
@@ -7324,11 +7652,12 @@ class Parser {
*/
Expression _parsePostfixExpression() {
Expression operand = _parseAssignableExpression(true);
- if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
- _matches(TokenType.PERIOD) ||
- _matches(TokenType.QUESTION_PERIOD) ||
- _matches(TokenType.OPEN_PAREN) ||
- (parseGenericMethods && _matches(TokenType.LT))) {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.OPEN_SQUARE_BRACKET ||
+ type == TokenType.PERIOD ||
+ type == TokenType.QUESTION_PERIOD ||
+ type == TokenType.OPEN_PAREN ||
+ (parseGenericMethods && type == TokenType.LT)) {
do {
if (_isLikelyArgumentList()) {
TypeArgumentList typeArguments = _parseOptionalTypeArguments();
@@ -7348,10 +7677,11 @@ class Parser {
} else {
operand = _parseAssignableSelector(operand, true);
}
- } while (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
- _matches(TokenType.PERIOD) ||
- _matches(TokenType.QUESTION_PERIOD) ||
- _matches(TokenType.OPEN_PAREN));
+ type = _currentToken.type;
+ } while (type == TokenType.OPEN_SQUARE_BRACKET ||
+ type == TokenType.PERIOD ||
+ type == TokenType.QUESTION_PERIOD ||
+ type == TokenType.OPEN_PAREN);
return operand;
}
if (!_currentToken.type.isIncrementOperator) {
@@ -7363,6 +7693,37 @@ class Parser {
}
/**
+ * Parse a prefixed identifier given that the given [qualifier] was already
+ * parsed. Return the prefixed identifier that was parsed.
+ *
+ * prefixedIdentifier ::=
+ * identifier ('.' identifier)?
+ */
+ Identifier _parsePrefixedIdentifierAfterIdentifier(
+ SimpleIdentifier qualifier) {
+ if (!_matches(TokenType.PERIOD) || _injectGenericCommentTypeList()) {
+ return qualifier;
+ }
+ Token period = getAndAdvance();
+ SimpleIdentifier qualified = parseSimpleIdentifier();
+ return new PrefixedIdentifier(qualifier, period, qualified);
+ }
+
+ /**
+ * Parse a prefixed identifier. Return the prefixed identifier that was
+ * parsed.
+ *
+ * This method assumes that the current token matches an identifier.
+ *
+ * prefixedIdentifier ::=
+ * identifier ('.' identifier)?
+ */
+ Identifier _parsePrefixedIdentifierUnchecked() {
+ return _parsePrefixedIdentifierAfterIdentifier(
+ _parseSimpleIdentifierUnchecked());
+ }
+
+ /**
* Parse a primary expression. Return the primary expression that was parsed.
*
* primary ::=
@@ -7386,21 +7747,52 @@ class Parser {
* | listLiteral
*/
Expression _parsePrimaryExpression() {
- if (_matchesKeyword(Keyword.THIS)) {
+ if (_matchesIdentifier()) {
+ // TODO(brianwilkerson) The code below was an attempt to recover from an
+ // error case, but it needs to be applied as a recovery only after we
+ // know that parsing it as an identifier doesn't work. Leaving the code as
+ // a reminder of how to recover.
+// if (isFunctionExpression(_peek())) {
+// //
+// // Function expressions were allowed to have names at one point, but this is now illegal.
+// //
+// reportError(ParserErrorCode.NAMED_FUNCTION_EXPRESSION, getAndAdvance());
+// return parseFunctionExpression();
+// }
+ return _parsePrefixedIdentifierUnchecked();
+ }
+ TokenType type = _currentToken.type;
+ if (type == TokenType.STRING) {
+ return parseStringLiteral();
+ } else if (type == TokenType.INT) {
+ Token token = getAndAdvance();
+ int value = null;
+ try {
+ value = int.parse(token.lexeme);
+ } on FormatException {
+ // The invalid format should have been reported by the scanner.
+ }
+ return new IntegerLiteral(token, value);
+ }
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.NULL) {
+ return new NullLiteral(getAndAdvance());
+ } else if (keyword == Keyword.NEW) {
+ return _parseNewExpression();
+ } else if (keyword == Keyword.THIS) {
return new ThisExpression(getAndAdvance());
- } else if (_matchesKeyword(Keyword.SUPER)) {
+ } else if (keyword == Keyword.SUPER) {
// TODO(paulberry): verify with Gilad that "super" must be followed by
// unconditionalAssignableSelector in this case.
return _parseAssignableSelector(
new SuperExpression(getAndAdvance()), false,
allowConditional: false);
- } else if (_matchesKeyword(Keyword.NULL)) {
- return new NullLiteral(getAndAdvance());
- } else if (_matchesKeyword(Keyword.FALSE)) {
+ } else if (keyword == Keyword.FALSE) {
return new BooleanLiteral(getAndAdvance(), false);
- } else if (_matchesKeyword(Keyword.TRUE)) {
+ } else if (keyword == Keyword.TRUE) {
return new BooleanLiteral(getAndAdvance(), true);
- } else if (_matches(TokenType.DOUBLE)) {
+ }
+ if (type == TokenType.DOUBLE) {
Token token = getAndAdvance();
double value = 0.0;
try {
@@ -7409,7 +7801,7 @@ class Parser {
// The invalid format should have been reported by the scanner.
}
return new DoubleLiteral(token, value);
- } else if (_matches(TokenType.HEXADECIMAL)) {
+ } else if (type == TokenType.HEXADECIMAL) {
Token token = getAndAdvance();
int value = null;
try {
@@ -7418,35 +7810,9 @@ class Parser {
// The invalid format should have been reported by the scanner.
}
return new IntegerLiteral(token, value);
- } else if (_matches(TokenType.INT)) {
- Token token = getAndAdvance();
- int value = null;
- try {
- value = int.parse(token.lexeme);
- } on FormatException {
- // The invalid format should have been reported by the scanner.
- }
- return new IntegerLiteral(token, value);
- } else if (_matches(TokenType.STRING)) {
- return parseStringLiteral();
- } else if (_matchesIdentifier()) {
- // TODO(brianwilkerson) The code below was an attempt to recover from an
- // error case, but it needs to be applied as a recovery only after we
- // know that parsing it as an identifier doesn't work. Leaving the code as
- // a reminder of how to recover.
-// if (isFunctionExpression(peek())) {
-// //
-// // Function expressions were allowed to have names at one point, but this is now illegal.
-// //
-// reportError(ParserErrorCode.NAMED_FUNCTION_EXPRESSION, getAndAdvance());
-// return parseFunctionExpression();
-// }
- return parsePrefixedIdentifier();
- } else if (_matchesKeyword(Keyword.NEW)) {
- return _parseNewExpression();
- } else if (_matchesKeyword(Keyword.CONST)) {
+ } else if (keyword == Keyword.CONST) {
return _parseConstExpression();
- } else if (_matches(TokenType.OPEN_PAREN)) {
+ } else if (type == TokenType.OPEN_PAREN) {
if (_isFunctionExpression(_currentToken)) {
return parseFunctionExpression();
}
@@ -7461,20 +7827,20 @@ class Parser {
} finally {
_inInitializer = wasInInitializer;
}
- } else if (_matches(TokenType.LT) || _injectGenericCommentTypeList()) {
+ } else if (type == TokenType.LT || _injectGenericCommentTypeList()) {
return _parseListOrMapLiteral(null);
- } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+ } else if (type == TokenType.OPEN_CURLY_BRACKET) {
return _parseMapLiteral(null, null);
- } else if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
- _matches(TokenType.INDEX)) {
+ } else if (type == TokenType.OPEN_SQUARE_BRACKET ||
+ type == TokenType.INDEX) {
return _parseListLiteral(null, null);
- } else if (_matches(TokenType.QUESTION) &&
+ } else if (type == TokenType.QUESTION &&
_tokenMatches(_peek(), TokenType.IDENTIFIER)) {
_reportErrorForCurrentToken(
ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
_advance();
return _parsePrimaryExpression();
- } else if (_matchesKeyword(Keyword.VOID)) {
+ } else if (keyword == Keyword.VOID) {
//
// Recover from having a return type of "void" where a return type is not
// expected.
@@ -7484,7 +7850,7 @@ class Parser {
ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
_advance();
return _parsePrimaryExpression();
- } else if (_matches(TokenType.HASH)) {
+ } else if (type == TokenType.HASH) {
return _parseSymbolLiteral();
} else {
_reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
@@ -7493,21 +7859,31 @@ class Parser {
}
/**
- * Parse a redirecting constructor invocation. Return the redirecting
+ * Parse a redirecting constructor invocation. The flag [hasPeriod] should be
+ * `true` if the `this` is followed by a period. Return the redirecting
* constructor invocation that was parsed.
*
+ * This method assumes that the current token matches `Keyword.THIS`.
+ *
* redirectingConstructorInvocation ::=
* 'this' ('.' identifier)? arguments
*/
- RedirectingConstructorInvocation _parseRedirectingConstructorInvocation() {
- Token keyword = _expectKeyword(Keyword.THIS);
+ RedirectingConstructorInvocation _parseRedirectingConstructorInvocation(
+ bool hasPeriod) {
+ Token keyword = getAndAdvance();
Token period = null;
SimpleIdentifier constructorName = null;
- if (_matches(TokenType.PERIOD)) {
+ if (hasPeriod) {
period = getAndAdvance();
- constructorName = parseSimpleIdentifier();
+ if (_matchesIdentifier()) {
+ constructorName = _parseSimpleIdentifierUnchecked(isDeclaration: false);
+ } else {
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+ constructorName = _createSyntheticIdentifier(isDeclaration: false);
+ _advance();
+ }
}
- ArgumentList argumentList = parseArgumentList();
+ ArgumentList argumentList = _parseArgumentListChecked();
return new RedirectingConstructorInvocation(
keyword, period, constructorName, argumentList);
}
@@ -7521,29 +7897,29 @@ class Parser {
* | 'super' relationalOperator bitwiseOrExpression
*/
Expression _parseRelationalExpression() {
- if (_matchesKeyword(Keyword.SUPER) &&
+ if (_currentToken.keyword == Keyword.SUPER &&
_currentToken.next.type.isRelationalOperator) {
Expression expression = new SuperExpression(getAndAdvance());
Token operator = getAndAdvance();
- expression = new BinaryExpression(
+ return new BinaryExpression(
expression, operator, parseBitwiseOrExpression());
- return expression;
}
Expression expression = parseBitwiseOrExpression();
- if (_matchesKeyword(Keyword.AS)) {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.AS) {
Token asOperator = getAndAdvance();
- expression = new AsExpression(expression, asOperator, parseTypeName());
- } else if (_matchesKeyword(Keyword.IS)) {
+ return new AsExpression(expression, asOperator, parseTypeName());
+ } else if (keyword == Keyword.IS) {
Token isOperator = getAndAdvance();
Token notOperator = null;
if (_matches(TokenType.BANG)) {
notOperator = getAndAdvance();
}
- expression = new IsExpression(
+ return new IsExpression(
expression, isOperator, notOperator, parseTypeName());
} else if (_currentToken.type.isRelationalOperator) {
Token operator = getAndAdvance();
- expression = new BinaryExpression(
+ return new BinaryExpression(
expression, operator, parseBitwiseOrExpression());
}
return expression;
@@ -7552,20 +7928,24 @@ class Parser {
/**
* Parse a rethrow expression. Return the rethrow expression that was parsed.
*
+ * This method assumes that the current token matches `Keyword.RETHROW`.
+ *
* rethrowExpression ::=
* 'rethrow'
*/
Expression _parseRethrowExpression() =>
- new RethrowExpression(_expectKeyword(Keyword.RETHROW));
+ new RethrowExpression(getAndAdvance());
/**
* Parse a return statement. Return the return statement that was parsed.
*
+ * This method assumes that the current token matches `Keyword.RETURN`.
+ *
* returnStatement ::=
* 'return' expression? ';'
*/
Statement _parseReturnStatement() {
- Token returnKeyword = _expectKeyword(Keyword.RETURN);
+ Token returnKeyword = getAndAdvance();
if (_matches(TokenType.SEMICOLON)) {
return new ReturnStatement(returnKeyword, null, getAndAdvance());
}
@@ -7582,6 +7962,8 @@ class Parser {
* already been parsed, or `null` if there was no return type. Return the
* setter that was parsed.
*
+ * This method assumes that the current token matches `Keyword.SET`.
+ *
* setter ::=
* setterSignature functionBody?
*
@@ -7590,7 +7972,7 @@ class Parser {
*/
MethodDeclaration _parseSetter(CommentAndMetadata commentAndMetadata,
Token externalKeyword, Token staticKeyword, TypeName returnType) {
- Token propertyKeyword = _expectKeyword(Keyword.SET);
+ Token propertyKeyword = getAndAdvance();
SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
FormalParameterList parameters = parseFormalParameterList();
_validateFormalParameterList(parameters);
@@ -7624,21 +8006,39 @@ class Parser {
*/
Expression _parseShiftExpression() {
Expression expression;
- if (_matchesKeyword(Keyword.SUPER) &&
+ if (_currentToken.keyword == Keyword.SUPER &&
_currentToken.next.type.isShiftOperator) {
expression = new SuperExpression(getAndAdvance());
} else {
expression = _parseAdditiveExpression();
}
while (_currentToken.type.isShiftOperator) {
- Token operator = getAndAdvance();
expression = new BinaryExpression(
- expression, operator, _parseAdditiveExpression());
+ expression, getAndAdvance(), _parseAdditiveExpression());
}
return expression;
}
/**
+ * Parse a simple identifier. Return the simple identifier that was parsed.
+ *
+ * This method assumes that the current token matches an identifier.
+ *
+ * identifier ::=
+ * IDENTIFIER
+ */
+ SimpleIdentifier _parseSimpleIdentifierUnchecked(
+ {bool isDeclaration: false}) {
+ String lexeme = _currentToken.lexeme;
+ if ((_inAsync || _inGenerator) &&
+ (lexeme == ASYNC || lexeme == _AWAIT || lexeme == _YIELD)) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.ASYNC_KEYWORD_USED_AS_IDENTIFIER);
+ }
+ return new SimpleIdentifier(getAndAdvance(), isDeclaration: isDeclaration);
+ }
+
+ /**
* Parse a list of statements within a switch statement. Return the statements
* that were parsed.
*
@@ -7646,10 +8046,11 @@ class Parser {
* statement*
*/
List<Statement> _parseStatementList() {
- List<Statement> statements = new List<Statement>();
+ List<Statement> statements = <Statement>[];
Token statementStart = _currentToken;
- while (!_matches(TokenType.EOF) &&
- !_matches(TokenType.CLOSE_CURLY_BRACKET) &&
+ TokenType type = _currentToken.type;
+ while (type != TokenType.EOF &&
+ type != TokenType.CLOSE_CURLY_BRACKET &&
!_isSwitchMember()) {
statements.add(parseStatement2());
if (identical(_currentToken, statementStart)) {
@@ -7658,6 +8059,7 @@ class Parser {
_advance();
}
statementStart = _currentToken;
+ type = _currentToken.type;
}
return statements;
}
@@ -7665,15 +8067,20 @@ class Parser {
/**
* Parse a string literal that contains interpolations. Return the string
* literal that was parsed.
+ *
+ * This method assumes that the current token matches either
+ * [TokenType.STRING_INTERPOLATION_EXPRESSION] or
+ * [TokenType.STRING_INTERPOLATION_IDENTIFIER].
*/
StringInterpolation _parseStringInterpolation(Token string) {
- List<InterpolationElement> elements = new List<InterpolationElement>();
- bool hasMore = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION) ||
- _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER);
- elements.add(new InterpolationString(
- string, _computeStringValue(string.lexeme, true, !hasMore)));
+ List<InterpolationElement> elements = <InterpolationElement>[
+ new InterpolationString(
+ string, _computeStringValue(string.lexeme, true, false))
+ ];
+ bool hasMore = true;
+ bool isExpression = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION);
while (hasMore) {
- if (_matches(TokenType.STRING_INTERPOLATION_EXPRESSION)) {
+ if (isExpression) {
Token openToken = getAndAdvance();
bool wasInInitializer = _inInitializer;
_inInitializer = false;
@@ -7697,8 +8104,9 @@ class Parser {
}
if (_matches(TokenType.STRING)) {
string = getAndAdvance();
- hasMore = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION) ||
- _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER);
+ isExpression = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION);
+ hasMore =
+ isExpression || _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER);
elements.add(new InterpolationString(
string, _computeStringValue(string.lexeme, false, !hasMore)));
} else {
@@ -7709,21 +8117,47 @@ class Parser {
}
/**
+ * Parse a string literal. Return the string literal that was parsed.
+ *
+ * This method assumes that the current token matches `TokenType.STRING`.
+ *
+ * stringLiteral ::=
+ * MULTI_LINE_STRING+
+ * | SINGLE_LINE_STRING+
+ */
+ StringLiteral _parseStringLiteralUnchecked() {
+ List<StringLiteral> strings = <StringLiteral>[];
+ do {
+ Token string = getAndAdvance();
+ if (_matches(TokenType.STRING_INTERPOLATION_EXPRESSION) ||
+ _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER)) {
+ strings.add(_parseStringInterpolation(string));
+ } else {
+ strings.add(new SimpleStringLiteral(
+ string, _computeStringValue(string.lexeme, true, true)));
+ }
+ } while (_matches(TokenType.STRING));
+ return strings.length == 1 ? strings[0] : new AdjacentStrings(strings);
+ }
+
+ /**
* Parse a super constructor invocation. Return the super constructor
* invocation that was parsed.
*
+ * This method assumes that the current token matches [Keyword.SUPER].
+ *
* superConstructorInvocation ::=
* 'super' ('.' identifier)? arguments
*/
SuperConstructorInvocation _parseSuperConstructorInvocation() {
- Token keyword = _expectKeyword(Keyword.SUPER);
+ Token keyword = getAndAdvance();
Token period = null;
SimpleIdentifier constructorName = null;
if (_matches(TokenType.PERIOD)) {
period = getAndAdvance();
constructorName = parseSimpleIdentifier();
}
- ArgumentList argumentList = parseArgumentList();
+ ArgumentList argumentList = _parseArgumentListChecked();
return new SuperConstructorInvocation(
keyword, period, constructorName, argumentList);
}
@@ -7751,14 +8185,14 @@ class Parser {
Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
Token defaultKeyword = null;
- List<SwitchMember> members = new List<SwitchMember>();
- while (!_matches(TokenType.EOF) &&
- !_matches(TokenType.CLOSE_CURLY_BRACKET)) {
- List<Label> labels = new List<Label>();
+ List<SwitchMember> members = <SwitchMember>[];
+ TokenType type = _currentToken.type;
+ while (type != TokenType.EOF && type != TokenType.CLOSE_CURLY_BRACKET) {
+ List<Label> labels = <Label>[];
while (
_matchesIdentifier() && _tokenMatches(_peek(), TokenType.COLON)) {
SimpleIdentifier identifier =
- parseSimpleIdentifier(isDeclaration: true);
+ _parseSimpleIdentifierUnchecked(isDeclaration: true);
String label = identifier.token.lexeme;
if (definedLabels.contains(label)) {
_reportErrorForToken(
@@ -7768,10 +8202,11 @@ class Parser {
} else {
definedLabels.add(label);
}
- Token colon = _expect(TokenType.COLON);
+ Token colon = getAndAdvance();
labels.add(new Label(identifier, colon));
}
- if (_matchesKeyword(Keyword.CASE)) {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.CASE) {
Token caseKeyword = getAndAdvance();
Expression caseExpression = parseExpression2();
Token colon = _expect(TokenType.COLON);
@@ -7782,7 +8217,7 @@ class Parser {
ParserErrorCode.SWITCH_HAS_CASE_AFTER_DEFAULT_CASE,
caseKeyword);
}
- } else if (_matchesKeyword(Keyword.DEFAULT)) {
+ } else if (keyword == Keyword.DEFAULT) {
if (defaultKeyword != null) {
_reportErrorForToken(
ParserErrorCode.SWITCH_HAS_MULTIPLE_DEFAULT_CASES, _peek());
@@ -7795,13 +8230,20 @@ class Parser {
// We need to advance, otherwise we could end up in an infinite loop,
// but this could be a lot smarter about recovering from the error.
_reportErrorForCurrentToken(ParserErrorCode.EXPECTED_CASE_OR_DEFAULT);
- while (!_matches(TokenType.EOF) &&
- !_matches(TokenType.CLOSE_CURLY_BRACKET) &&
- !_matchesKeyword(Keyword.CASE) &&
- !_matchesKeyword(Keyword.DEFAULT)) {
+ bool atEndOrNextMember() {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.EOF ||
+ type == TokenType.CLOSE_CURLY_BRACKET) {
+ return true;
+ }
+ Keyword keyword = _currentToken.keyword;
+ return keyword == Keyword.CASE || keyword == Keyword.DEFAULT;
+ }
+ while (!atEndOrNextMember()) {
_advance();
}
}
+ type = _currentToken.type;
}
Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
return new SwitchStatement(keyword, leftParenthesis, expression,
@@ -7814,16 +8256,17 @@ class Parser {
/**
* Parse a symbol literal. Return the symbol literal that was parsed.
*
+ * This method assumes that the current token matches [TokenType.HASH].
+ *
* symbolLiteral ::=
* '#' identifier ('.' identifier)*
*/
SymbolLiteral _parseSymbolLiteral() {
Token poundSign = getAndAdvance();
- List<Token> components = new List<Token>();
+ List<Token> components = <Token>[];
if (_matchesIdentifier()) {
components.add(getAndAdvance());
- while (_matches(TokenType.PERIOD)) {
- _advance();
+ while (_optional(TokenType.PERIOD)) {
if (_matchesIdentifier()) {
components.add(getAndAdvance());
} else {
@@ -7834,7 +8277,7 @@ class Parser {
}
} else if (_currentToken.isOperator) {
components.add(getAndAdvance());
- } else if (_tokenMatchesKeyword(_currentToken, Keyword.VOID)) {
+ } else if (_matchesKeyword(Keyword.VOID)) {
components.add(getAndAdvance());
} else {
_reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
@@ -7846,12 +8289,15 @@ class Parser {
/**
* Parse a throw expression. Return the throw expression that was parsed.
*
+ * This method assumes that the current token matches [Keyword.THROW].
+ *
* throwExpression ::=
* 'throw' expression
*/
Expression _parseThrowExpression() {
- Token keyword = _expectKeyword(Keyword.THROW);
- if (_matches(TokenType.SEMICOLON) || _matches(TokenType.CLOSE_PAREN)) {
+ Token keyword = getAndAdvance();
+ TokenType type = _currentToken.type;
+ if (type == TokenType.SEMICOLON || type == TokenType.CLOSE_PAREN) {
_reportErrorForToken(
ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken);
return new ThrowExpression(keyword, _createSyntheticIdentifier());
@@ -7863,12 +8309,15 @@ class Parser {
/**
* Parse a throw expression. Return the throw expression that was parsed.
*
+ * This method assumes that the current token matches [Keyword.THROW].
+ *
* throwExpressionWithoutCascade ::=
* 'throw' expressionWithoutCascade
*/
Expression _parseThrowExpressionWithoutCascade() {
- Token keyword = _expectKeyword(Keyword.THROW);
- if (_matches(TokenType.SEMICOLON) || _matches(TokenType.CLOSE_PAREN)) {
+ Token keyword = getAndAdvance();
+ TokenType type = _currentToken.type;
+ if (type == TokenType.SEMICOLON || type == TokenType.CLOSE_PAREN) {
_reportErrorForToken(
ParserErrorCode.MISSING_EXPRESSION_IN_THROW, _currentToken);
return new ThrowExpression(keyword, _createSyntheticIdentifier());
@@ -7880,6 +8329,8 @@ class Parser {
/**
* Parse a try statement. Return the try statement that was parsed.
*
+ * This method assumes that the current token matches [Keyword.TRY].
+ *
* tryStatement ::=
* 'try' block (onPart+ finallyPart? | finallyPart)
*
@@ -7894,9 +8345,9 @@ class Parser {
* 'finally' block
*/
Statement _parseTryStatement() {
- Token tryKeyword = _expectKeyword(Keyword.TRY);
- Block body = parseBlock();
- List<CatchClause> catchClauses = new List<CatchClause>();
+ Token tryKeyword = getAndAdvance();
+ Block body = _parseBlockChecked();
+ List<CatchClause> catchClauses = <CatchClause>[];
Block finallyClause = null;
while (_matchesString(_ON) || _matchesKeyword(Keyword.CATCH)) {
Token onKeyword = null;
@@ -7921,7 +8372,7 @@ class Parser {
}
rightParenthesis = _expect(TokenType.CLOSE_PAREN);
}
- Block catchBody = parseBlock();
+ Block catchBody = _parseBlockChecked();
catchClauses.add(new CatchClause(
onKeyword,
exceptionType,
@@ -7936,11 +8387,9 @@ class Parser {
Token finallyKeyword = null;
if (_matchesKeyword(Keyword.FINALLY)) {
finallyKeyword = getAndAdvance();
- finallyClause = parseBlock();
- } else {
- if (catchClauses.isEmpty) {
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_CATCH_OR_FINALLY);
- }
+ finallyClause = _parseBlockChecked();
+ } else if (catchClauses.isEmpty) {
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_CATCH_OR_FINALLY);
}
return new TryStatement(
tryKeyword, body, catchClauses, finallyKeyword, finallyClause);
@@ -7950,6 +8399,8 @@ class Parser {
* Parse a type alias. The [commentAndMetadata] is the metadata to be
* associated with the member. Return the type alias that was parsed.
*
+ * This method assumes that the current token matches [Keyword.TYPEDEF].
+ *
* typeAlias ::=
* 'typedef' typeAliasBody
*
@@ -7970,7 +8421,7 @@ class Parser {
* returnType? name
*/
TypeAlias _parseTypeAlias(CommentAndMetadata commentAndMetadata) {
- Token keyword = _expectKeyword(Keyword.TYPEDEF);
+ Token keyword = getAndAdvance();
if (_matchesIdentifier()) {
Token next = _peek();
if (_tokenMatches(next, TokenType.LT)) {
@@ -7995,11 +8446,11 @@ class Parser {
TypeName _parseTypeName() {
Identifier typeName;
- if (_matchesKeyword(Keyword.VAR)) {
+ if (_matchesIdentifier()) {
+ typeName = _parsePrefixedIdentifierUnchecked();
+ } else if (_matchesKeyword(Keyword.VAR)) {
_reportErrorForCurrentToken(ParserErrorCode.VAR_AS_TYPE_NAME);
typeName = new SimpleIdentifier(getAndAdvance());
- } else if (_matchesIdentifier()) {
- typeName = parsePrefixedIdentifier();
} else {
typeName = _createSyntheticIdentifier();
_reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TYPE_NAME);
@@ -8009,6 +8460,25 @@ class Parser {
}
/**
+ * Parse a type name. Return the type name that was parsed.
+ *
+ * This method assumes that the current token is an identifier.
+ *
+ * type ::=
+ * qualified typeArguments?
+ */
+ TypeName _parseTypeNameAfterIdentifier() {
+ Identifier typeName = _parsePrefixedIdentifierUnchecked();
+ TypeArgumentList typeArguments = _parseOptionalTypeArguments();
+ // If this is followed by a generic method type comment, allow the comment
+ // type to replace the real type name.
+ // TODO(jmesserly): this feels like a big hammer. Can we restrict it to
+ // only work inside generic methods?
+ TypeName typeFromComment = _parseOptionalTypeNameComment();
+ return typeFromComment ?? new TypeName(typeName, typeArguments);
+ }
+
+ /**
* Parse a unary expression. Return the unary expression that was parsed.
*
* unaryExpression ::=
@@ -8020,13 +8490,15 @@ class Parser {
* | incrementOperator assignableExpression
*/
Expression _parseUnaryExpression() {
- if (_matches(TokenType.MINUS) ||
- _matches(TokenType.BANG) ||
- _matches(TokenType.TILDE)) {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.MINUS ||
+ type == TokenType.BANG ||
+ type == TokenType.TILDE) {
Token operator = getAndAdvance();
if (_matchesKeyword(Keyword.SUPER)) {
- if (_tokenMatches(_peek(), TokenType.OPEN_SQUARE_BRACKET) ||
- _tokenMatches(_peek(), TokenType.PERIOD)) {
+ TokenType nextType = _peek().type;
+ if (nextType == TokenType.OPEN_SQUARE_BRACKET ||
+ nextType == TokenType.PERIOD) {
// "prefixOperator unaryExpression"
// --> "prefixOperator postfixExpression"
// --> "prefixOperator primary selector*"
@@ -8040,8 +8512,9 @@ class Parser {
} else if (_currentToken.type.isIncrementOperator) {
Token operator = getAndAdvance();
if (_matchesKeyword(Keyword.SUPER)) {
- if (_tokenMatches(_peek(), TokenType.OPEN_SQUARE_BRACKET) ||
- _tokenMatches(_peek(), TokenType.PERIOD)) {
+ TokenType nextType = _peek().type;
+ if (nextType == TokenType.OPEN_SQUARE_BRACKET ||
+ nextType == TokenType.PERIOD) {
// --> "prefixOperator 'super' assignableSelector selector*"
return new PrefixExpression(operator, _parseUnaryExpression());
}
@@ -8052,7 +8525,7 @@ class Parser {
// we cannot do the same for "++super" because "+super" is also not
// valid.
//
- if (operator.type == TokenType.MINUS_MINUS) {
+ if (type == TokenType.MINUS_MINUS) {
Token firstOperator = _createToken(operator, TokenType.MINUS);
Token secondOperator =
new Token(TokenType.MINUS, operator.offset + 1);
@@ -8063,16 +8536,16 @@ class Parser {
firstOperator,
new PrefixExpression(
secondOperator, new SuperExpression(getAndAdvance())));
- } else {
- // Invalid operator before 'super'
- _reportErrorForCurrentToken(
- ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [operator.lexeme]);
- return new PrefixExpression(
- operator, new SuperExpression(getAndAdvance()));
}
+ // Invalid operator before 'super'
+ _reportErrorForCurrentToken(
+ ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [operator.lexeme]);
+ return new PrefixExpression(
+ operator, new SuperExpression(getAndAdvance()));
}
- return new PrefixExpression(operator, _parseAssignableExpression(false));
- } else if (_matches(TokenType.PLUS)) {
+ return new PrefixExpression(
+ operator, _parseAssignableExpressionNotStartingWithSuper(false));
+ } else if (type == TokenType.PLUS) {
_reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
return _createSyntheticIdentifier();
} else if (_inAsync && _matchesString(_AWAIT)) {
@@ -8086,28 +8559,35 @@ class Parser {
* was parsed.
*/
StringLiteral _parseUri() {
- bool iskeywordAfterUri(Token token) =>
+ // TODO(brianwilkerson) Should this function also return true for valid
+ // top-level keywords?
+ bool isKeywordAfterUri(Token token) =>
token.lexeme == Keyword.AS.syntax ||
token.lexeme == _HIDE ||
token.lexeme == _SHOW;
- if (!_matches(TokenType.STRING) &&
- !_matches(TokenType.SEMICOLON) &&
- !iskeywordAfterUri(_currentToken)) {
+ TokenType type = _currentToken.type;
+ if (type != TokenType.STRING &&
+ type != TokenType.SEMICOLON &&
+ !isKeywordAfterUri(_currentToken)) {
// Attempt to recover in the case where the URI was not enclosed in
// quotes.
Token token = _currentToken;
- while ((_tokenMatchesIdentifier(token) && !iskeywordAfterUri(token)) ||
- _tokenMatches(token, TokenType.COLON) ||
- _tokenMatches(token, TokenType.SLASH) ||
- _tokenMatches(token, TokenType.PERIOD) ||
- _tokenMatches(token, TokenType.PERIOD_PERIOD) ||
- _tokenMatches(token, TokenType.PERIOD_PERIOD_PERIOD) ||
- _tokenMatches(token, TokenType.INT) ||
- _tokenMatches(token, TokenType.DOUBLE)) {
+ bool isValidInUri(Token token) {
+ TokenType type = token.type;
+ return type == TokenType.COLON ||
+ type == TokenType.SLASH ||
+ type == TokenType.PERIOD ||
+ type == TokenType.PERIOD_PERIOD ||
+ type == TokenType.PERIOD_PERIOD_PERIOD ||
+ type == TokenType.INT ||
+ type == TokenType.DOUBLE;
+ }
+ while ((_tokenMatchesIdentifier(token) && !isKeywordAfterUri(token)) ||
+ isValidInUri(token)) {
token = token.next;
}
if (_tokenMatches(token, TokenType.SEMICOLON) ||
- iskeywordAfterUri(token)) {
+ isKeywordAfterUri(token)) {
Token endToken = token.previous;
token = _currentToken;
int endOffset = token.end;
@@ -8193,10 +8673,10 @@ class Parser {
_tokenMatchesKeyword(keyword, Keyword.VAR)) {
_reportErrorForToken(ParserErrorCode.VAR_AND_TYPE, keyword);
}
- List<VariableDeclaration> variables = new List<VariableDeclaration>();
- variables.add(_parseVariableDeclaration());
- while (_matches(TokenType.COMMA)) {
- _advance();
+ List<VariableDeclaration> variables = <VariableDeclaration>[
+ _parseVariableDeclaration()
+ ];
+ while (_optional(TokenType.COMMA)) {
variables.add(_parseVariableDeclaration());
}
return new VariableDeclarationList(commentAndMetadata?.comment,
@@ -8250,6 +8730,8 @@ class Parser {
/**
* Parse a while statement. Return the while statement that was parsed.
*
+ * This method assumes that the current token matches [Keyword.WHILE].
+ *
* whileStatement ::=
* 'while' '(' expression ')' statement
*/
@@ -8257,7 +8739,7 @@ class Parser {
bool wasInLoop = _inLoop;
_inLoop = true;
try {
- Token keyword = _expectKeyword(Keyword.WHILE);
+ Token keyword = getAndAdvance();
Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
Expression condition = parseExpression2();
Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
@@ -8272,6 +8754,8 @@ class Parser {
/**
* Parse a yield statement. Return the yield statement that was parsed.
*
+ * This method assumes that the current token matches [Keyword.YIELD].
+ *
* yieldStatement ::=
* 'yield' '*'? expression ';'
*/
@@ -8415,8 +8899,8 @@ class Parser {
* | type
*/
Token _skipFinalConstVarOrType(Token startToken) {
- if (_tokenMatchesKeyword(startToken, Keyword.FINAL) ||
- _tokenMatchesKeyword(startToken, Keyword.CONST)) {
+ Keyword keyword = startToken.keyword;
+ if (keyword == Keyword.FINAL || keyword == Keyword.CONST) {
Token next = startToken.next;
if (_tokenMatchesIdentifier(next)) {
Token next2 = next.next;
@@ -8429,7 +8913,7 @@ class Parser {
// "parameter"
return next;
}
- } else if (_tokenMatchesKeyword(startToken, Keyword.VAR)) {
+ } else if (keyword == Keyword.VAR) {
return startToken.next;
} else if (_tokenMatchesIdentifier(startToken)) {
Token next = startToken.next;
« no previous file with comments | « no previous file | pkg/analyzer/test/generated/parser_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698