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

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

Issue 2348353002: First steps toward removing MethodTrampoline (Closed)
Patch Set: Created 4 years, 3 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 67ba79a053a2cb1522715718cbd6e42b1683b797..672f761f6fae7fd37641fdde925d4bb845fc466b 100644
--- a/pkg/analyzer/lib/src/generated/parser.dart
+++ b/pkg/analyzer/lib/src/generated/parser.dart
@@ -102,20 +102,16 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
5,
(Parser target, arg0, arg1, arg2, arg3, arg4) =>
target._appendScalarValue(arg0, arg1, arg2, arg3, arg4)),
- 'computeStringValue_3': new MethodTrampoline(
- 3,
- (Parser target, arg0, arg1, arg2) =>
- target._computeStringValue(arg0, arg1, arg2)),
'convertToFunctionDeclaration_1': new MethodTrampoline(
1, (Parser target, arg0) => target._convertToFunctionDeclaration(arg0)),
'couldBeStartOfCompilationUnitMember_0': new MethodTrampoline(
0, (Parser target) => target._couldBeStartOfCompilationUnitMember()),
'createSyntheticIdentifier_0': new MethodTrampoline(
- 0, (Parser target) => target._createSyntheticIdentifier()),
+ 0, (Parser target) => target.createSyntheticIdentifier()),
'createSyntheticKeyword_1': new MethodTrampoline(
1, (Parser target, arg0) => target._createSyntheticKeyword(arg0)),
'createSyntheticStringLiteral_0': new MethodTrampoline(
- 0, (Parser target) => target._createSyntheticStringLiteral()),
+ 0, (Parser target) => target.createSyntheticStringLiteral()),
'createSyntheticToken_1': new MethodTrampoline(
1, (Parser target, arg0) => target._createSyntheticToken(arg0)),
'ensureAssignable_1': new MethodTrampoline(
@@ -136,19 +132,19 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
'injectToken_1': new MethodTrampoline(
1, (Parser target, arg0) => target._injectToken(arg0)),
'isFunctionDeclaration_0': new MethodTrampoline(
- 0, (Parser target) => target._isFunctionDeclaration()),
+ 0, (Parser target) => target.isFunctionDeclaration()),
'isFunctionExpression_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._isFunctionExpression(arg0)),
+ 1, (Parser target, arg0) => target.isFunctionExpression(arg0)),
'isHexDigit_1': new MethodTrampoline(
1, (Parser target, arg0) => target._isHexDigit(arg0)),
'isInitializedVariableDeclaration_0': new MethodTrampoline(
- 0, (Parser target) => target._isInitializedVariableDeclaration()),
+ 0, (Parser target) => target.isInitializedVariableDeclaration()),
'isLinkText_2': new MethodTrampoline(
2, (Parser target, arg0, arg1) => target._isLinkText(arg0, arg1)),
'isOperator_1': new MethodTrampoline(
1, (Parser target, arg0) => target._isOperator(arg0)),
'isSwitchMember_0':
- new MethodTrampoline(0, (Parser target) => target._isSwitchMember()),
+ new MethodTrampoline(0, (Parser target) => target.isSwitchMember()),
'isTypedIdentifier_1': new MethodTrampoline(
1, (Parser target, arg0) => target._isTypedIdentifier(arg0)),
'lockErrorListener_0':
@@ -166,51 +162,51 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
'optional_1':
new MethodTrampoline(1, (Parser target, arg0) => target._optional(arg0)),
'parseAdditiveExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parseAdditiveExpression()),
- 'parseAssertStatement_0': new MethodTrampoline(
- 0, (Parser target) => target._parseAssertStatement()),
+ 0, (Parser target) => target.parseAdditiveExpression()),
+ 'parseAssertStatement_0':
+ new MethodTrampoline(0, (Parser target) => target.parseAssertStatement()),
'parseAssignableExpression_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._parseAssignableExpression(arg0)),
+ 1, (Parser target, arg0) => target.parseAssignableExpression(arg0)),
'parseAssignableSelector_2': new MethodTrampoline(
2,
(Parser target, arg0, arg1) =>
target._parseAssignableSelector(arg0, arg1)),
- 'parseAwaitExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parseAwaitExpression()),
+ 'parseAwaitExpression_0':
+ new MethodTrampoline(0, (Parser target) => target.parseAwaitExpression()),
'parseBitwiseAndExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parseBitwiseAndExpression()),
+ 0, (Parser target) => target.parseBitwiseAndExpression()),
'parseBitwiseXorExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parseBitwiseXorExpression()),
+ 0, (Parser target) => target.parseBitwiseXorExpression()),
'parseBreakStatement_0':
- new MethodTrampoline(0, (Parser target) => target._parseBreakStatement()),
+ new MethodTrampoline(0, (Parser target) => target.parseBreakStatement()),
'parseCascadeSection_0':
- new MethodTrampoline(0, (Parser target) => target._parseCascadeSection()),
+ new MethodTrampoline(0, (Parser target) => target.parseCascadeSection()),
'parseClassDeclaration_2': new MethodTrampoline(2,
- (Parser target, arg0, arg1) => target._parseClassDeclaration(arg0, arg1)),
+ (Parser target, arg0, arg1) => target.parseClassDeclaration(arg0, arg1)),
'parseClassMembers_2': new MethodTrampoline(
2, (Parser target, arg0, arg1) => target._parseClassMembers(arg0, arg1)),
'parseClassTypeAlias_3': new MethodTrampoline(
3,
(Parser target, arg0, arg1, arg2) =>
- target._parseClassTypeAlias(arg0, arg1, arg2)),
+ target.parseClassTypeAlias(arg0, arg1, arg2)),
'parseCombinator_0':
new MethodTrampoline(0, (Parser target) => target.parseCombinator()),
'parseCombinators_0':
- new MethodTrampoline(0, (Parser target) => target._parseCombinators()),
+ new MethodTrampoline(0, (Parser target) => target.parseCombinators()),
'parseCommentAndMetadata_0': new MethodTrampoline(
- 0, (Parser target) => target._parseCommentAndMetadata()),
+ 0, (Parser target) => target.parseCommentAndMetadata()),
'parseCommentReference_2': new MethodTrampoline(2,
- (Parser target, arg0, arg1) => target._parseCommentReference(arg0, arg1)),
+ (Parser target, arg0, arg1) => target.parseCommentReference(arg0, arg1)),
'parseCommentReferences_1': new MethodTrampoline(
1,
(Parser target, List<DocumentationCommentToken> arg0) =>
target._parseCommentReferences(arg0)),
'parseCompilationUnitMember_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._parseCompilationUnitMember(arg0)),
+ 1, (Parser target, arg0) => target.parseCompilationUnitMember(arg0)),
'parseConfiguration_0':
- new MethodTrampoline(0, (Parser target) => target._parseConfiguration()),
- 'parseConstExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parseConstExpression()),
+ new MethodTrampoline(0, (Parser target) => target.parseConfiguration()),
+ 'parseConstExpression_0':
+ new MethodTrampoline(0, (Parser target) => target.parseConstExpression()),
'parseConstructor_8': new MethodTrampoline(
8,
(Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) => target
@@ -218,48 +214,48 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
'parseConstructorFieldInitializer_1': new MethodTrampoline(1,
(Parser target, arg0) => target._parseConstructorFieldInitializer(arg0)),
'parseContinueStatement_0': new MethodTrampoline(
- 0, (Parser target) => target._parseContinueStatement()),
+ 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()),
+ new MethodTrampoline(0, (Parser target) => target.parseDirectives2()),
'parseDocumentationComment_0': new MethodTrampoline(0, (Parser target) {
List<DocumentationCommentToken> tokens =
- target._parseDocumentationCommentTokens();
- return target._parseDocumentationComment(tokens);
+ target.parseDocumentationCommentTokens();
+ return target.parseDocumentationComment(tokens);
}),
'parseDoStatement_0':
- new MethodTrampoline(0, (Parser target) => target._parseDoStatement()),
+ new MethodTrampoline(0, (Parser target) => target.parseDoStatement()),
'parseDottedName_0':
- new MethodTrampoline(0, (Parser target) => target._parseDottedName()),
+ new MethodTrampoline(0, (Parser target) => target.parseDottedName()),
'parseEmptyStatement_0':
- new MethodTrampoline(0, (Parser target) => target._parseEmptyStatement()),
+ new MethodTrampoline(0, (Parser target) => target.parseEmptyStatement()),
'parseEnumConstantDeclaration_0': new MethodTrampoline(
0, (Parser target) => target._parseEnumConstantDeclaration()),
'parseEnumDeclaration_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._parseEnumDeclaration(arg0)),
+ 1, (Parser target, arg0) => target.parseEnumDeclaration(arg0)),
'parseEqualityExpression_0': new MethodTrampoline(
0, (Parser target) => target._parseEqualityExpression()),
'parseExportDirective_1': new MethodTrampoline(
1, (Parser target, arg0) => target._parseExportDirective(arg0)),
'parseExpressionList_0':
- new MethodTrampoline(0, (Parser target) => target._parseExpressionList()),
+ new MethodTrampoline(0, (Parser target) => target.parseExpressionList()),
'parseFinalConstVarOrType_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._parseFinalConstVarOrType(arg0)),
+ 1, (Parser target, arg0) => target.parseFinalConstVarOrType(arg0)),
'parseFormalParameter_1': new MethodTrampoline(
1, (Parser target, arg0) => target._parseFormalParameter(arg0)),
'parseForStatement_0':
- new MethodTrampoline(0, (Parser target) => target._parseForStatement()),
+ new MethodTrampoline(0, (Parser target) => target.parseForStatement()),
'parseFunctionBody_3': new MethodTrampoline(
3,
(Parser target, arg0, arg1, arg2) =>
- target._parseFunctionBody(arg0, arg1, arg2)),
+ target.parseFunctionBody(arg0, arg1, arg2)),
'parseFunctionDeclaration_3': new MethodTrampoline(
3,
(Parser target, arg0, arg1, arg2) =>
target._parseFunctionDeclaration(arg0, arg1, arg2)),
'parseFunctionDeclarationStatement_0': new MethodTrampoline(
- 0, (Parser target) => target._parseFunctionDeclarationStatement()),
+ 0, (Parser target) => target.parseFunctionDeclarationStatement()),
'parseFunctionDeclarationStatementAfterReturnType_2': new MethodTrampoline(
2,
(Parser target, arg0, arg1) =>
@@ -275,7 +271,7 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
'parseIdentifierList_0':
new MethodTrampoline(0, (Parser target) => target._parseIdentifierList()),
'parseIfStatement_0':
- new MethodTrampoline(0, (Parser target) => target._parseIfStatement()),
+ new MethodTrampoline(0, (Parser target) => target.parseIfStatement()),
'parseImportDirective_1': new MethodTrampoline(
1, (Parser target, arg0) => target._parseImportDirective(arg0)),
'parseInitializedIdentifierList_4': new MethodTrampoline(
@@ -291,7 +287,7 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
'parseListLiteral_2': new MethodTrampoline(
2, (Parser target, arg0, arg1) => target._parseListLiteral(arg0, arg1)),
'parseListOrMapLiteral_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._parseListOrMapLiteral(arg0)),
+ 1, (Parser target, arg0) => target.parseListOrMapLiteral(arg0)),
'parseLogicalAndExpression_0': new MethodTrampoline(
0, (Parser target) => target._parseLogicalAndExpression()),
'parseMapLiteral_2': new MethodTrampoline(
@@ -308,7 +304,7 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
'parseModifiers_0':
new MethodTrampoline(0, (Parser target) => target._parseModifiers()),
'parseMultiplicativeExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parseMultiplicativeExpression()),
+ 0, (Parser target) => target.parseMultiplicativeExpression()),
'parseNativeClause_0':
new MethodTrampoline(0, (Parser target) => target._parseNativeClause()),
'parseNewExpression_0':
@@ -318,7 +314,7 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
'parseOperator_3': new MethodTrampoline(
3,
(Parser target, arg0, arg1, arg2) =>
- target._parseOperator(arg0, arg1, arg2)),
+ target.parseOperator(arg0, arg1, arg2)),
'parseOptionalReturnType_0': new MethodTrampoline(
0, (Parser target) => target._parseOptionalReturnType()),
'parsePartDirective_1': new MethodTrampoline(
@@ -326,65 +322,65 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
'parsePostfixExpression_0': new MethodTrampoline(
0, (Parser target) => target._parsePostfixExpression()),
'parsePrimaryExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parsePrimaryExpression()),
+ 0, (Parser target) => target.parsePrimaryExpression()),
'parseRedirectingConstructorInvocation_1': new MethodTrampoline(
1,
(Parser target, arg0) =>
target._parseRedirectingConstructorInvocation(arg0)),
'parseRelationalExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parseRelationalExpression()),
+ 0, (Parser target) => target.parseRelationalExpression()),
'parseRethrowExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parseRethrowExpression()),
- 'parseReturnStatement_0': new MethodTrampoline(
- 0, (Parser target) => target._parseReturnStatement()),
+ 0, (Parser target) => target.parseRethrowExpression()),
+ 'parseReturnStatement_0':
+ new MethodTrampoline(0, (Parser target) => target.parseReturnStatement()),
'parseSetter_4': new MethodTrampoline(
4,
(Parser target, arg0, arg1, arg2, arg3) =>
target._parseSetter(arg0, arg1, arg2, arg3)),
- 'parseShiftExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parseShiftExpression()),
+ 'parseShiftExpression_0':
+ new MethodTrampoline(0, (Parser target) => target.parseShiftExpression()),
'parseStatementList_0':
new MethodTrampoline(0, (Parser target) => target._parseStatementList()),
'parseStringInterpolation_1': new MethodTrampoline(
1, (Parser target, arg0) => target._parseStringInterpolation(arg0)),
'parseSuperConstructorInvocation_0': new MethodTrampoline(
- 0, (Parser target) => target._parseSuperConstructorInvocation()),
- 'parseSwitchStatement_0': new MethodTrampoline(
- 0, (Parser target) => target._parseSwitchStatement()),
+ 0, (Parser target) => target.parseSuperConstructorInvocation()),
+ 'parseSwitchStatement_0':
+ new MethodTrampoline(0, (Parser target) => target.parseSwitchStatement()),
'parseSymbolLiteral_0':
- new MethodTrampoline(0, (Parser target) => target._parseSymbolLiteral()),
- 'parseThrowExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parseThrowExpression()),
+ new MethodTrampoline(0, (Parser target) => target.parseSymbolLiteral()),
+ 'parseThrowExpression_0':
+ new MethodTrampoline(0, (Parser target) => target.parseThrowExpression()),
'parseThrowExpressionWithoutCascade_0': new MethodTrampoline(
- 0, (Parser target) => target._parseThrowExpressionWithoutCascade()),
+ 0, (Parser target) => target.parseThrowExpressionWithoutCascade()),
'parseTryStatement_0':
- new MethodTrampoline(0, (Parser target) => target._parseTryStatement()),
+ new MethodTrampoline(0, (Parser target) => target.parseTryStatement()),
'parseTypeAlias_1': new MethodTrampoline(
1, (Parser target, arg0) => target._parseTypeAlias(arg0)),
- 'parseUnaryExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parseUnaryExpression()),
+ 'parseUnaryExpression_0':
+ new MethodTrampoline(0, (Parser target) => target.parseUnaryExpression()),
'parseVariableDeclaration_0': new MethodTrampoline(
- 0, (Parser target) => target._parseVariableDeclaration()),
+ 0, (Parser target) => target.parseVariableDeclaration()),
'parseVariableDeclarationListAfterMetadata_1': new MethodTrampoline(
1,
(Parser target, arg0) =>
- target._parseVariableDeclarationListAfterMetadata(arg0)),
+ target.parseVariableDeclarationListAfterMetadata(arg0)),
'parseVariableDeclarationListAfterType_3': new MethodTrampoline(
3,
(Parser target, arg0, arg1, arg2) =>
- target._parseVariableDeclarationListAfterType(arg0, arg1, arg2)),
+ target.parseVariableDeclarationListAfterType(arg0, arg1, arg2)),
'parseVariableDeclarationStatementAfterMetadata_1': new MethodTrampoline(
1,
(Parser target, arg0) =>
- target._parseVariableDeclarationStatementAfterMetadata(arg0)),
+ target.parseVariableDeclarationStatementAfterMetadata(arg0)),
'parseVariableDeclarationStatementAfterType_3': new MethodTrampoline(
3,
(Parser target, arg0, arg1, arg2) =>
target._parseVariableDeclarationStatementAfterType(arg0, arg1, arg2)),
'parseWhileStatement_0':
- new MethodTrampoline(0, (Parser target) => target._parseWhileStatement()),
+ new MethodTrampoline(0, (Parser target) => target.parseWhileStatement()),
'parseYieldStatement_0':
- new MethodTrampoline(0, (Parser target) => target._parseYieldStatement()),
+ new MethodTrampoline(0, (Parser target) => target.parseYieldStatement()),
'peek_0': new MethodTrampoline(0, (Parser target) => target._peek()),
'peekAt_1':
new MethodTrampoline(1, (Parser target, arg0) => target._peekAt(arg0)),
@@ -411,19 +407,19 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
'skipPastMatchingToken_1': new MethodTrampoline(
1, (Parser target, arg0) => target._skipPastMatchingToken(arg0)),
'skipPrefixedIdentifier_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._skipPrefixedIdentifier(arg0)),
+ 1, (Parser target, arg0) => target.skipPrefixedIdentifier(arg0)),
'skipReturnType_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._skipReturnType(arg0)),
+ 1, (Parser target, arg0) => target.skipReturnType(arg0)),
'skipSimpleIdentifier_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._skipSimpleIdentifier(arg0)),
+ 1, (Parser target, arg0) => target.skipSimpleIdentifier(arg0)),
'skipStringInterpolation_1': new MethodTrampoline(
1, (Parser target, arg0) => target._skipStringInterpolation(arg0)),
'skipStringLiteral_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._skipStringLiteral(arg0)),
+ 1, (Parser target, arg0) => target.skipStringLiteral(arg0)),
'skipTypeArgumentList_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._skipTypeArgumentList(arg0)),
+ 1, (Parser target, arg0) => target.skipTypeArgumentList(arg0)),
'skipTypeName_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._skipTypeName(arg0)),
+ 1, (Parser target, arg0) => target.skipTypeName(arg0)),
'skipTypeParameterList_1': new MethodTrampoline(
1, (Parser target, arg0) => target._skipTypeParameterList(arg0)),
'tokenMatches_2': new MethodTrampoline(
@@ -434,10 +430,6 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
(Parser target, arg0, arg1) => target._tokenMatchesKeyword(arg0, arg1)),
'tokenMatchesString_2': new MethodTrampoline(
2, (Parser target, arg0, arg1) => target._tokenMatchesString(arg0, arg1)),
- 'translateCharacter_3': new MethodTrampoline(
- 3,
- (Parser target, arg0, arg1, arg2) =>
- target._translateCharacter(arg0, arg1, arg2)),
'unlockErrorListener_0':
new MethodTrampoline(0, (Parser target) => target._unlockErrorListener()),
'validateFormalParameterList_1': new MethodTrampoline(
@@ -477,8 +469,7 @@ Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
};
Object invokeParserMethodImpl(
- Parser parser, String methodName, List<Object> objects, Token tokenStream) {
- parser.currentToken = tokenStream;
+ Parser parser, String methodName, List<Object> objects) {
MethodTrampoline method =
methodTable_Parser['${methodName}_${objects.length}'];
if (method == null) {
@@ -749,6 +740,11 @@ class Parser {
Parser(this._source, this._errorListener);
/**
+ * Return the current token.
+ */
+ Token get currentToken => _currentToken;
+
+ /**
* Set the token with which the parse is to begin to the given [token].
*/
void set currentToken(Token token) {
@@ -791,7 +787,7 @@ class Parser {
*/
@deprecated
bool get hasReturnTypeInTypeAlias {
- Token next = _skipReturnType(_currentToken);
+ Token next = skipReturnType(_currentToken);
if (next == null) {
return false;
}
@@ -819,6 +815,59 @@ class Parser {
}
/**
+ * Return the content of a string with the given literal representation. The
+ * [lexeme] is the literal representation of the string. The flag [isFirst] is
+ * `true` if this is the first token in a string literal. The flag [isLast] is
+ * `true` if this is the last token in a string literal.
+ */
+ String computeStringValue(String lexeme, bool isFirst, bool isLast) {
+ StringLexemeHelper helper = new StringLexemeHelper(lexeme, isFirst, isLast);
+ int start = helper.start;
+ int end = helper.end;
+ bool stringEndsAfterStart = end >= start;
+ assert(stringEndsAfterStart);
+ if (!stringEndsAfterStart) {
+ AnalysisEngine.instance.logger.logError(
+ "Internal error: computeStringValue($lexeme, $isFirst, $isLast)");
+ return "";
+ }
+ if (helper.isRaw) {
+ return lexeme.substring(start, end);
+ }
+ StringBuffer buffer = new StringBuffer();
+ int index = start;
+ while (index < end) {
+ index = _translateCharacter(buffer, lexeme, index);
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Return a synthetic identifier.
+ */
+ SimpleIdentifier createSyntheticIdentifier({bool isDeclaration: false}) {
+ Token syntheticToken;
+ if (_currentToken.type == TokenType.KEYWORD) {
+ // Consider current keyword token as an identifier.
+ // It is not always true, e.g. "^is T" where "^" is place the place for
+ // synthetic identifier. By creating SyntheticStringToken we can
+ // distinguish a real identifier from synthetic. In the code completion
+ // behavior will depend on a cursor position - before or on "is".
+ syntheticToken = _injectToken(new SyntheticStringToken(
+ TokenType.IDENTIFIER, _currentToken.lexeme, _currentToken.offset));
+ } else {
+ syntheticToken = _createSyntheticToken(TokenType.IDENTIFIER);
+ }
+ return new SimpleIdentifier(syntheticToken, isDeclaration: isDeclaration);
+ }
+
+ /**
+ * Return a synthetic string literal.
+ */
+ SimpleStringLiteral createSyntheticStringLiteral() =>
+ new SimpleStringLiteral(_createSyntheticToken(TokenType.STRING), "");
+
+ /**
* Advance to the next token in the token stream, making it the new current
* token and return the token that was current before this method was invoked.
*/
@@ -829,6 +878,204 @@ class Parser {
}
/**
+ * Return `true` if the current token appears to be the beginning of a
+ * function declaration.
+ */
+ bool isFunctionDeclaration() {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.VOID) {
+ return true;
+ }
+ Token afterReturnType = skipTypeName(_currentToken);
+ if (afterReturnType == null) {
+ // There was no return type, but it is optional, so go back to where we
+ // started.
+ afterReturnType = _currentToken;
+ }
+ Token afterIdentifier = skipSimpleIdentifier(afterReturnType);
+ if (afterIdentifier == null) {
+ // It's possible that we parsed the function name as if it were a type
+ // name, so see whether it makes sense if we assume that there is no type.
+ afterIdentifier = skipSimpleIdentifier(_currentToken);
+ }
+ if (afterIdentifier == null) {
+ return false;
+ }
+ if (isFunctionExpression(afterIdentifier)) {
+ return true;
+ }
+ // 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 (keyword == Keyword.GET) {
+ Token afterName = skipSimpleIdentifier(_currentToken.next);
+ if (afterName == null) {
+ return false;
+ }
+ 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;
+ }
+ TokenType type = afterName.type;
+ return type == TokenType.FUNCTION || type == TokenType.OPEN_CURLY_BRACKET;
+ }
+ return false;
+ }
+
+ /**
+ * Return `true` if the given [token] appears to be the beginning of a
+ * function expression.
+ */
+ bool isFunctionExpression(Token token) {
+ // Function expressions aren't allowed in initializer lists.
+ if (_inInitializer) {
+ return false;
+ }
+ Token afterTypeParameters = _skipTypeParameterList(token);
+ if (afterTypeParameters == null) {
+ afterTypeParameters = token;
+ }
+ Token afterParameters = _skipFormalParameterList(afterTypeParameters);
+ if (afterParameters == null) {
+ return false;
+ }
+ if (afterParameters.matchesAny(
+ const <TokenType>[TokenType.OPEN_CURLY_BRACKET, TokenType.FUNCTION])) {
+ return true;
+ }
+ String lexeme = afterParameters.lexeme;
+ return lexeme == ASYNC || lexeme == SYNC;
+ }
+
+ /**
+ * Return `true` if the current token is the first token in an initialized
+ * variable declaration rather than an expression. This method assumes that we
+ * have already skipped past any metadata that might be associated with the
+ * declaration.
+ *
+ * initializedVariableDeclaration ::=
+ * declaredIdentifier ('=' expression)? (',' initializedIdentifier)*
+ *
+ * declaredIdentifier ::=
+ * metadata finalConstVarOrType identifier
+ *
+ * finalConstVarOrType ::=
+ * 'final' type?
+ * | 'const' type?
+ * | 'var'
+ * | type
+ *
+ * type ::=
+ * qualified typeArguments?
+ *
+ * initializedIdentifier ::=
+ * identifier ('=' expression)?
+ */
+ bool isInitializedVariableDeclaration() {
+ 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 (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>[
+ TokenType.LT,
+ TokenType.OPEN_CURLY_BRACKET,
+ TokenType.OPEN_SQUARE_BRACKET,
+ TokenType.INDEX
+ ]);
+ }
+ bool allowAdditionalTokens = true;
+ // We know that we have an identifier, and need to see whether it might be
+ // a type name.
+ if (_currentToken.type != TokenType.IDENTIFIER) {
+ allowAdditionalTokens = false;
+ }
+ Token token = skipTypeName(_currentToken);
+ if (token == null) {
+ // There was no type name, so this can't be a declaration.
+ return false;
+ }
+ if (token.type != TokenType.IDENTIFIER) {
+ allowAdditionalTokens = false;
+ }
+ token = skipSimpleIdentifier(token);
+ if (token == null) {
+ return false;
+ }
+ TokenType type = token.type;
+ // Usual cases in valid code:
+ // String v = '';
+ // String v, v2;
+ // String v;
+ // for (String item in items) {}
+ if (type == TokenType.EQ ||
+ type == TokenType.COMMA ||
+ type == TokenType.SEMICOLON ||
+ token.keyword == Keyword.IN) {
+ return true;
+ }
+ // It is OK to parse as a variable declaration in these cases:
+ // String v }
+ // String v if (true) print('OK');
+ // String v { print(42); }
+ // ...but not in these cases:
+ // get getterName {
+ // String get getterName
+ if (allowAdditionalTokens) {
+ if (type == TokenType.CLOSE_CURLY_BRACKET ||
+ type == TokenType.KEYWORD ||
+ type == TokenType.IDENTIFIER ||
+ type == TokenType.OPEN_CURLY_BRACKET) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return `true` if the current token appears to be the beginning of a switch
+ * member.
+ */
+ bool isSwitchMember() {
+ Token token = _currentToken;
+ while (_tokenMatches(token, TokenType.IDENTIFIER) &&
+ _tokenMatches(token.next, TokenType.COLON)) {
+ token = token.next.next;
+ }
+ Keyword keyword = token.keyword;
+ return keyword == Keyword.CASE || keyword == Keyword.DEFAULT;
+ }
+
+ /**
+ * Parse an additive expression. Return the additive expression that was
+ * parsed.
+ *
+ * additiveExpression ::=
+ * multiplicativeExpression (additiveOperator multiplicativeExpression)*
+ * | 'super' (additiveOperator multiplicativeExpression)+
+ */
+ Expression parseAdditiveExpression() {
+ Expression expression;
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type.isAdditiveOperator) {
+ expression = new SuperExpression(getAndAdvance());
+ } else {
+ expression = parseMultiplicativeExpression();
+ }
+ while (_currentToken.type.isAdditiveOperator) {
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), parseMultiplicativeExpression());
+ }
+ return expression;
+ }
+
+ /**
* Parse an annotation. Return the annotation that was parsed.
*
* This method assumes that the current token matches [TokenType.AT].
@@ -937,24 +1184,127 @@ class Parser {
}
/**
- * Parse a bitwise or expression. Return the bitwise or expression that was
- * parsed.
+ * Parse an assert statement. Return the assert statement.
*
- * bitwiseOrExpression ::=
- * bitwiseXorExpression ('|' bitwiseXorExpression)*
- * | 'super' ('|' bitwiseXorExpression)+
+ * This method assumes that the current token matches `Keyword.ASSERT`.
+ *
+ * assertStatement ::=
+ * 'assert' '(' expression [',' expression] ')' ';'
*/
- Expression parseBitwiseOrExpression() {
- Expression expression;
- if (_currentToken.keyword == Keyword.SUPER &&
- _currentToken.next.type == TokenType.BAR) {
- expression = new SuperExpression(getAndAdvance());
- } else {
- expression = _parseBitwiseXorExpression();
+ AssertStatement parseAssertStatement() {
+ Token keyword = getAndAdvance();
+ Token leftParen = _expect(TokenType.OPEN_PAREN);
+ Expression expression = parseExpression2();
+ Token comma;
+ Expression message;
+ if (_matches(TokenType.COMMA)) {
+ comma = getAndAdvance();
+ message = parseExpression2();
+ }
+ Token rightParen = _expect(TokenType.CLOSE_PAREN);
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new AssertStatement(
+ keyword, leftParen, expression, comma, message, rightParen, semicolon);
+ }
+
+ /**
+ * Parse an assignable expression. The [primaryAllowed] is `true` if the
+ * expression is allowed to be a primary without any assignable selector.
+ * Return the assignable expression that was parsed.
+ *
+ * assignableExpression ::=
+ * primary (arguments* assignableSelector)+
+ * | 'super' unconditionalAssignableSelector
+ * | identifier
+ */
+ Expression parseAssignableExpression(bool primaryAllowed) {
+ if (_matchesKeyword(Keyword.SUPER)) {
+ return _parseAssignableSelector(
+ new SuperExpression(getAndAdvance()), false,
+ allowConditional: false);
+ }
+ return _parseAssignableExpressionNotStartingWithSuper(primaryAllowed);
+ }
+
+ /**
+ * Parse a await expression. Return the await expression that was parsed.
+ *
+ * This method assumes that the current token matches `_AWAIT`.
+ *
+ * awaitExpression ::=
+ * 'await' unaryExpression
+ */
+ AwaitExpression parseAwaitExpression() {
+ Token awaitToken = getAndAdvance();
+ Expression expression = parseUnaryExpression();
+ return new AwaitExpression(awaitToken, expression);
+ }
+
+ /**
+ * Parse a bitwise and expression. Return the bitwise and expression that was
+ * parsed.
+ *
+ * bitwiseAndExpression ::=
+ * shiftExpression ('&' shiftExpression)*
+ * | 'super' ('&' shiftExpression)+
+ */
+ Expression parseBitwiseAndExpression() {
+ Expression expression;
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type == TokenType.AMPERSAND) {
+ expression = new SuperExpression(getAndAdvance());
+ } else {
+ expression = parseShiftExpression();
+ }
+ while (_currentToken.type == TokenType.AMPERSAND) {
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), parseShiftExpression());
+ }
+ return expression;
+ }
+
+ /**
+ * Parse a bitwise or expression. Return the bitwise or expression that was
+ * parsed.
+ *
+ * bitwiseOrExpression ::=
+ * bitwiseXorExpression ('|' bitwiseXorExpression)*
+ * | 'super' ('|' bitwiseXorExpression)+
+ */
+ Expression parseBitwiseOrExpression() {
+ Expression expression;
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type == TokenType.BAR) {
+ expression = new SuperExpression(getAndAdvance());
+ } else {
+ expression = parseBitwiseXorExpression();
}
while (_currentToken.type == TokenType.BAR) {
expression = new BinaryExpression(
- expression, getAndAdvance(), _parseBitwiseXorExpression());
+ expression, getAndAdvance(), parseBitwiseXorExpression());
+ }
+ return expression;
+ }
+
+ /**
+ * Parse a bitwise exclusive-or expression. Return the bitwise exclusive-or
+ * expression that was parsed.
+ *
+ * bitwiseXorExpression ::=
+ * bitwiseAndExpression ('^' bitwiseAndExpression)*
+ * | 'super' ('^' bitwiseAndExpression)+
+ */
+ Expression parseBitwiseXorExpression() {
+ Expression expression;
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type == TokenType.CARET) {
+ expression = new SuperExpression(getAndAdvance());
+ } else {
+ expression = parseBitwiseAndExpression();
+ }
+ while (_currentToken.type == TokenType.CARET) {
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), parseBitwiseAndExpression());
}
return expression;
}
@@ -998,117 +1348,370 @@ class Parser {
}
/**
- * Parse a class member. The [className] is the name of the class containing
- * the member being parsed. Return the class member that was parsed, or `null`
- * if what was found was not a valid class member.
+ * Parse a break statement. Return the break statement that was parsed.
*
- * classMemberDefinition ::=
- * declaration ';'
- * | methodSignature functionBody
+ * This method assumes that the current token matches `Keyword.BREAK`.
+ *
+ * breakStatement ::=
+ * 'break' identifier? ';'
*/
- ClassMember parseClassMember(String className) {
- CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
- Modifiers modifiers = _parseModifiers();
- 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 (keyword == Keyword.SET && isFollowedByIdentifier) {
- _validateModifiersForGetterOrSetterOrMethod(modifiers);
- return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
- modifiers.staticKeyword, returnType);
- } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
- _validateModifiersForOperator(modifiers);
- return _parseOperatorAfterKeyword(commentAndMetadata,
- modifiers.externalKeyword, returnType, getAndAdvance());
- } else if (_matchesIdentifier() &&
- _peek().matchesAny(const <TokenType>[
- TokenType.OPEN_PAREN,
- TokenType.OPEN_CURLY_BRACKET,
- TokenType.FUNCTION,
- TokenType.LT
- ])) {
- _validateModifiersForGetterOrSetterOrMethod(modifiers);
- return _parseMethodDeclarationAfterReturnType(commentAndMetadata,
- modifiers.externalKeyword, modifiers.staticKeyword, returnType);
- } else {
- //
- // We have found an error of some kind. Try to recover.
- //
- if (_matchesIdentifier()) {
- if (_peek().matchesAny(const <TokenType>[
- TokenType.EQ,
- TokenType.COMMA,
- TokenType.SEMICOLON
- ])) {
- //
- // We appear to have a variable declaration with a type of "void".
- //
- _reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType);
- return _parseInitializedIdentifierList(
- commentAndMetadata,
- modifiers.staticKeyword,
- _validateModifiersForField(modifiers),
- returnType);
- }
- }
- if (_isOperator(_currentToken)) {
- //
- // We appear to have found an operator declaration without the
- // 'operator' keyword.
- //
- _validateModifiersForOperator(modifiers);
- return _parseOperator(
- commentAndMetadata, modifiers.externalKeyword, returnType);
- }
- _reportErrorForToken(
- ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
- return null;
+ Statement parseBreakStatement() {
+ Token breakKeyword = getAndAdvance();
+ SimpleIdentifier label = null;
+ if (_matchesIdentifier()) {
+ label = _parseSimpleIdentifierUnchecked();
+ }
+ if (!_inLoop && !_inSwitch && label == null) {
+ _reportErrorForToken(ParserErrorCode.BREAK_OUTSIDE_OF_LOOP, breakKeyword);
+ }
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new BreakStatement(breakKeyword, label, semicolon);
+ }
+
+ /**
+ * 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?
+ *
+ * cascadeSelector ::=
+ * '[' expression ']'
+ * | identifier
+ *
+ * cascadeAssignment ::=
+ * assignmentOperator expressionWithoutCascade
+ */
+ Expression parseCascadeSection() {
+ Token period = getAndAdvance();
+ Expression expression = null;
+ SimpleIdentifier functionName = null;
+ if (_matchesIdentifier()) {
+ functionName = _parseSimpleIdentifierUnchecked();
+ } else if (_currentToken.type == TokenType.OPEN_SQUARE_BRACKET) {
+ Token leftBracket = getAndAdvance();
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = false;
+ try {
+ Expression index = parseExpression2();
+ Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
+ expression = new IndexExpression.forCascade(
+ period, leftBracket, index, rightBracket);
+ period = null;
+ } finally {
+ _inInitializer = wasInInitializer;
}
+ } else {
+ _reportErrorForToken(ParserErrorCode.MISSING_IDENTIFIER, _currentToken,
+ [_currentToken.lexeme]);
+ functionName = createSyntheticIdentifier();
}
- Token next = _peek();
- bool isFollowedByIdentifier = _tokenMatchesIdentifier(next);
- if (keyword == Keyword.GET && isFollowedByIdentifier) {
- _validateModifiersForGetterOrSetterOrMethod(modifiers);
- return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
- modifiers.staticKeyword, null);
- } else if (keyword == Keyword.SET && isFollowedByIdentifier) {
- _validateModifiersForGetterOrSetterOrMethod(modifiers);
- return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
- modifiers.staticKeyword, null);
- } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
- _validateModifiersForOperator(modifiers);
- return _parseOperatorAfterKeyword(
- commentAndMetadata, modifiers.externalKeyword, null, getAndAdvance());
- } else if (!_matchesIdentifier()) {
- //
- // Recover from an error.
- //
- if (_matchesKeyword(Keyword.CLASS)) {
- _reportErrorForCurrentToken(ParserErrorCode.CLASS_IN_CLASS);
- // TODO(brianwilkerson) We don't currently have any way to capture the
- // class that was parsed.
- _parseClassDeclaration(commentAndMetadata, null);
- return null;
- } else if (_matchesKeyword(Keyword.ABSTRACT) &&
- _tokenMatchesKeyword(_peek(), Keyword.CLASS)) {
- _reportErrorForToken(ParserErrorCode.CLASS_IN_CLASS, _peek());
- // TODO(brianwilkerson) We don't currently have any way to capture the
- // class that was parsed.
- _parseClassDeclaration(commentAndMetadata, getAndAdvance());
- return null;
- } else if (_matchesKeyword(Keyword.ENUM)) {
- _reportErrorForToken(ParserErrorCode.ENUM_IN_CLASS, _peek());
- // TODO(brianwilkerson) We don't currently have any way to capture the
- // enum that was parsed.
- _parseEnumDeclaration(commentAndMetadata);
+ assert((expression == null && functionName != null) ||
+ (expression != null && functionName == null));
+ if (_isLikelyArgumentList()) {
+ do {
+ TypeArgumentList typeArguments = _parseOptionalTypeArguments();
+ if (functionName != null) {
+ expression = new MethodInvocation(expression, period, functionName,
+ typeArguments, parseArgumentList());
+ period = null;
+ functionName = null;
+ } else if (expression == null) {
+ // It should not be possible to get here.
+ expression = new MethodInvocation(expression, period,
+ createSyntheticIdentifier(), typeArguments, parseArgumentList());
+ } else {
+ expression = new FunctionExpressionInvocation(
+ expression, typeArguments, parseArgumentList());
+ }
+ } while (_isLikelyArgumentList());
+ } else if (functionName != null) {
+ expression = new PropertyAccess(expression, period, functionName);
+ period = null;
+ }
+ assert(expression != null);
+ bool progress = true;
+ while (progress) {
+ progress = false;
+ Expression selector = _parseAssignableSelector(expression, true);
+ if (!identical(selector, expression)) {
+ expression = selector;
+ progress = true;
+ while (_isLikelyArgumentList()) {
+ TypeArgumentList typeArguments = _parseOptionalTypeArguments();
+ Expression currentExpression = expression;
+ if (currentExpression is PropertyAccess) {
+ expression = new MethodInvocation(
+ currentExpression.target,
+ currentExpression.operator,
+ currentExpression.propertyName,
+ typeArguments,
+ parseArgumentList());
+ } else {
+ expression = new FunctionExpressionInvocation(
+ expression, typeArguments, parseArgumentList());
+ }
+ }
+ }
+ }
+ if (_currentToken.type.isAssignmentOperator) {
+ Token operator = getAndAdvance();
+ _ensureAssignable(expression);
+ expression = new AssignmentExpression(
+ expression, operator, parseExpressionWithoutCascade());
+ }
+ return expression;
+ }
+
+ /**
+ * Parse a class declaration. The [commentAndMetadata] is the metadata to be
+ * associated with the member. The [abstractKeyword] is the token for the
+ * 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) {
+ //
+ // Parse the name and type parameters.
+ //
+ Token keyword = getAndAdvance();
+ SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
+ String className = name.name;
+ TypeParameterList typeParameters = null;
+ 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
+ // generate errors if they are not in the order required by the
+ // specification.
+ //
+ ExtendsClause extendsClause = null;
+ WithClause withClause = null;
+ ImplementsClause implementsClause = null;
+ bool foundClause = true;
+ while (foundClause) {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.EXTENDS) {
+ if (extendsClause == null) {
+ extendsClause = parseExtendsClause();
+ if (withClause != null) {
+ _reportErrorForToken(
+ ParserErrorCode.WITH_BEFORE_EXTENDS, withClause.withKeyword);
+ } else if (implementsClause != null) {
+ _reportErrorForToken(ParserErrorCode.IMPLEMENTS_BEFORE_EXTENDS,
+ implementsClause.implementsKeyword);
+ }
+ } else {
+ _reportErrorForToken(ParserErrorCode.MULTIPLE_EXTENDS_CLAUSES,
+ extendsClause.extendsKeyword);
+ parseExtendsClause();
+ }
+ } else if (keyword == Keyword.WITH) {
+ if (withClause == null) {
+ withClause = parseWithClause();
+ if (implementsClause != null) {
+ _reportErrorForToken(ParserErrorCode.IMPLEMENTS_BEFORE_WITH,
+ implementsClause.implementsKeyword);
+ }
+ } else {
+ _reportErrorForToken(
+ ParserErrorCode.MULTIPLE_WITH_CLAUSES, withClause.withKeyword);
+ parseWithClause();
+ // TODO(brianwilkerson) Should we merge the list of applied mixins
+ // into a single list?
+ }
+ } else if (keyword == Keyword.IMPLEMENTS) {
+ if (implementsClause == null) {
+ implementsClause = parseImplementsClause();
+ } else {
+ _reportErrorForToken(ParserErrorCode.MULTIPLE_IMPLEMENTS_CLAUSES,
+ implementsClause.implementsKeyword);
+ parseImplementsClause();
+ // TODO(brianwilkerson) Should we merge the list of implemented
+ // classes into a single list?
+ }
+ } else {
+ foundClause = false;
+ }
+ }
+ if (withClause != null && extendsClause == null) {
+ _reportErrorForToken(
+ ParserErrorCode.WITH_WITHOUT_EXTENDS, withClause.withKeyword);
+ }
+ //
+ // Look for and skip over the extra-lingual 'native' specification.
+ //
+ NativeClause nativeClause = null;
+ if (_matchesString(_NATIVE) && _tokenMatches(_peek(), TokenType.STRING)) {
+ nativeClause = _parseNativeClause();
+ }
+ //
+ // Parse the body of the class.
+ //
+ Token leftBracket = null;
+ List<ClassMember> members = null;
+ Token rightBracket = null;
+ if (_matches(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);
+ }
+ ClassDeclaration classDeclaration = new ClassDeclaration(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ abstractKeyword,
+ keyword,
+ name,
+ typeParameters,
+ extendsClause,
+ withClause,
+ implementsClause,
+ leftBracket,
+ members,
+ rightBracket);
+ classDeclaration.nativeClause = nativeClause;
+ return classDeclaration;
+ }
+
+ /**
+ * Parse a class member. The [className] is the name of the class containing
+ * the member being parsed. Return the class member that was parsed, or `null`
+ * if what was found was not a valid class member.
+ *
+ * classMemberDefinition ::=
+ * declaration ';'
+ * | methodSignature functionBody
+ */
+ ClassMember parseClassMember(String className) {
+ CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
+ Modifiers modifiers = _parseModifiers();
+ 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 (keyword == Keyword.SET && isFollowedByIdentifier) {
+ _validateModifiersForGetterOrSetterOrMethod(modifiers);
+ return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
+ modifiers.staticKeyword, returnType);
+ } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
+ _validateModifiersForOperator(modifiers);
+ return _parseOperatorAfterKeyword(commentAndMetadata,
+ modifiers.externalKeyword, returnType, getAndAdvance());
+ } else if (_matchesIdentifier() &&
+ _peek().matchesAny(const <TokenType>[
+ TokenType.OPEN_PAREN,
+ TokenType.OPEN_CURLY_BRACKET,
+ TokenType.FUNCTION,
+ TokenType.LT
+ ])) {
+ _validateModifiersForGetterOrSetterOrMethod(modifiers);
+ return _parseMethodDeclarationAfterReturnType(commentAndMetadata,
+ modifiers.externalKeyword, modifiers.staticKeyword, returnType);
+ } else {
+ //
+ // We have found an error of some kind. Try to recover.
+ //
+ if (_matchesIdentifier()) {
+ if (_peek().matchesAny(const <TokenType>[
+ TokenType.EQ,
+ TokenType.COMMA,
+ TokenType.SEMICOLON
+ ])) {
+ //
+ // We appear to have a variable declaration with a type of "void".
+ //
+ _reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType);
+ return _parseInitializedIdentifierList(
+ commentAndMetadata,
+ modifiers.staticKeyword,
+ _validateModifiersForField(modifiers),
+ returnType);
+ }
+ }
+ if (_isOperator(_currentToken)) {
+ //
+ // We appear to have found an operator declaration without the
+ // 'operator' keyword.
+ //
+ _validateModifiersForOperator(modifiers);
+ return parseOperator(
+ commentAndMetadata, modifiers.externalKeyword, returnType);
+ }
+ _reportErrorForToken(
+ ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
+ return null;
+ }
+ }
+ Token next = _peek();
+ bool isFollowedByIdentifier = _tokenMatchesIdentifier(next);
+ if (keyword == Keyword.GET && isFollowedByIdentifier) {
+ _validateModifiersForGetterOrSetterOrMethod(modifiers);
+ return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
+ modifiers.staticKeyword, null);
+ } else if (keyword == Keyword.SET && isFollowedByIdentifier) {
+ _validateModifiersForGetterOrSetterOrMethod(modifiers);
+ return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
+ modifiers.staticKeyword, null);
+ } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
+ _validateModifiersForOperator(modifiers);
+ return _parseOperatorAfterKeyword(
+ commentAndMetadata, modifiers.externalKeyword, null, getAndAdvance());
+ } else if (!_matchesIdentifier()) {
+ //
+ // Recover from an error.
+ //
+ if (_matchesKeyword(Keyword.CLASS)) {
+ _reportErrorForCurrentToken(ParserErrorCode.CLASS_IN_CLASS);
+ // TODO(brianwilkerson) We don't currently have any way to capture the
+ // class that was parsed.
+ parseClassDeclaration(commentAndMetadata, null);
+ return null;
+ } else if (_matchesKeyword(Keyword.ABSTRACT) &&
+ _tokenMatchesKeyword(_peek(), Keyword.CLASS)) {
+ _reportErrorForToken(ParserErrorCode.CLASS_IN_CLASS, _peek());
+ // TODO(brianwilkerson) We don't currently have any way to capture the
+ // class that was parsed.
+ parseClassDeclaration(commentAndMetadata, getAndAdvance());
+ return null;
+ } else if (_matchesKeyword(Keyword.ENUM)) {
+ _reportErrorForToken(ParserErrorCode.ENUM_IN_CLASS, _peek());
+ // TODO(brianwilkerson) We don't currently have any way to capture the
+ // enum that was parsed.
+ parseEnumDeclaration(commentAndMetadata);
return null;
} else if (_isOperator(_currentToken)) {
//
@@ -1116,7 +1719,7 @@ class Parser {
// 'operator' keyword.
//
_validateModifiersForOperator(modifiers);
- return _parseOperator(
+ return parseOperator(
commentAndMetadata, modifiers.externalKeyword, null);
}
Token keyword = modifiers.varKeyword;
@@ -1132,7 +1735,7 @@ class Parser {
//
_reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
VariableDeclaration variable =
- new VariableDeclaration(_createSyntheticIdentifier(), null, null);
+ new VariableDeclaration(createSyntheticIdentifier(), null, null);
List<VariableDeclaration> variables = <VariableDeclaration>[variable];
return new FieldDeclaration(
commentAndMetadata.comment,
@@ -1159,7 +1762,7 @@ class Parser {
null,
null,
null,
- _createSyntheticIdentifier(isDeclaration: true),
+ createSyntheticIdentifier(isDeclaration: true),
null,
new FormalParameterList(
null, <FormalParameter>[], null, null, null),
@@ -1267,7 +1870,7 @@ class Parser {
// 'operator' keyword.
//
_validateModifiersForOperator(modifiers);
- return _parseOperator(
+ return parseOperator(
commentAndMetadata, modifiers.externalKeyword, type);
}
//
@@ -1332,6 +1935,32 @@ class Parser {
}
/**
+ * 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. Return the class type alias that was parsed.
+ *
+ * This method assumes that the current token matches an identifier.
+ *
+ * classTypeAlias ::=
+ * identifier typeParameters? '=' 'abstract'? mixinApplication
+ *
+ * mixinApplication ::=
+ * type withClause implementsClause? ';'
+ */
+ ClassTypeAlias parseClassTypeAlias(CommentAndMetadata commentAndMetadata,
+ Token abstractKeyword, Token classKeyword) {
+ SimpleIdentifier className =
+ _parseSimpleIdentifierUnchecked(isDeclaration: true);
+ TypeParameterList typeParameters = null;
+ if (_matches(TokenType.LT)) {
+ typeParameters = parseTypeParameterList();
+ }
+ return _parseClassTypeAliasAfterName(commentAndMetadata, abstractKeyword,
+ classKeyword, className, typeParameters);
+ }
+
+ /**
* Parse a single combinator. Return the combinator that was parsed, or `null`
* if no combinator is found.
*
@@ -1349,6 +1978,162 @@ class Parser {
}
/**
+ * Parse a list of combinators in a directive. Return the combinators that
+ * were parsed, or `null` if there are no combinators.
+ *
+ * combinator ::=
+ * 'show' identifier (',' identifier)*
+ * | 'hide' identifier (',' identifier)*
+ */
+ List<Combinator> parseCombinators() {
+ List<Combinator> combinators = null;
+ while (true) {
+ Combinator combinator = parseCombinator();
+ if (combinator == null) {
+ break;
+ }
+ combinators ??= <Combinator>[];
+ combinators.add(combinator);
+ }
+ return combinators;
+ }
+
+ /**
+ * Parse the documentation comment and metadata preceding a declaration. This
+ * method allows any number of documentation comments to occur before, after
+ * or between the metadata, but only returns the last (right-most)
+ * documentation comment that is found. Return the documentation comment and
+ * metadata that were parsed.
+ *
+ * metadata ::=
+ * annotation*
+ */
+ CommentAndMetadata parseCommentAndMetadata() {
+ // TODO(brianwilkerson) Consider making the creation of documentation
+ // comments be lazy.
+ List<DocumentationCommentToken> tokens = parseDocumentationCommentTokens();
+ List<Annotation> metadata = null;
+ while (_matches(TokenType.AT)) {
+ metadata ??= <Annotation>[];
+ metadata.add(parseAnnotation());
+ List<DocumentationCommentToken> optionalTokens =
+ parseDocumentationCommentTokens();
+ if (optionalTokens != null) {
+ tokens = optionalTokens;
+ }
+ }
+ return new CommentAndMetadata(parseDocumentationComment(tokens), metadata);
+ }
+
+ /**
+ * Parse a comment reference from the source between square brackets. The
+ * [referenceSource] is the source occurring between the square brackets
+ * within a documentation comment. The [sourceOffset] is the offset of the
+ * first character of the reference source. Return the comment reference that
+ * was parsed, or `null` if no reference could be found.
+ *
+ * commentReference ::=
+ * 'new'? prefixedIdentifier
+ */
+ CommentReference parseCommentReference(
+ String referenceSource, int sourceOffset) {
+ // TODO(brianwilkerson) The errors are not getting the right offset/length
+ // and are being duplicated.
+ try {
+ BooleanErrorListener listener = new BooleanErrorListener();
+ Scanner scanner = new Scanner(
+ null, new SubSequenceReader(referenceSource, sourceOffset), listener);
+ scanner.setSourceStart(1, 1);
+ Token firstToken = scanner.tokenize();
+ if (listener.errorReported) {
+ return null;
+ }
+ if (firstToken.type == TokenType.EOF) {
+ Token syntheticToken =
+ new SyntheticStringToken(TokenType.IDENTIFIER, "", sourceOffset);
+ syntheticToken.setNext(firstToken);
+ return new CommentReference(null, new SimpleIdentifier(syntheticToken));
+ }
+ Token newKeyword = null;
+ if (_tokenMatchesKeyword(firstToken, Keyword.NEW)) {
+ newKeyword = firstToken;
+ firstToken = firstToken.next;
+ }
+ if (firstToken.isUserDefinableOperator) {
+ if (firstToken.next.type != TokenType.EOF) {
+ return null;
+ }
+ Identifier identifier = new SimpleIdentifier(firstToken);
+ return new CommentReference(null, identifier);
+ } else if (_tokenMatchesKeyword(firstToken, Keyword.OPERATOR)) {
+ Token secondToken = firstToken.next;
+ if (secondToken.isUserDefinableOperator) {
+ if (secondToken.next.type != TokenType.EOF) {
+ return null;
+ }
+ Identifier identifier = new SimpleIdentifier(secondToken);
+ return new CommentReference(null, identifier);
+ }
+ return null;
+ } else if (_tokenMatchesIdentifier(firstToken)) {
+ Token secondToken = firstToken.next;
+ Token thirdToken = secondToken.next;
+ Token nextToken;
+ Identifier identifier;
+ if (_tokenMatches(secondToken, TokenType.PERIOD)) {
+ if (thirdToken.isUserDefinableOperator) {
+ identifier = new PrefixedIdentifier(
+ new SimpleIdentifier(firstToken),
+ secondToken,
+ new SimpleIdentifier(thirdToken));
+ nextToken = thirdToken.next;
+ } else if (_tokenMatchesKeyword(thirdToken, Keyword.OPERATOR)) {
+ Token fourthToken = thirdToken.next;
+ if (fourthToken.isUserDefinableOperator) {
+ identifier = new PrefixedIdentifier(
+ new SimpleIdentifier(firstToken),
+ secondToken,
+ new SimpleIdentifier(fourthToken));
+ nextToken = fourthToken.next;
+ } else {
+ return null;
+ }
+ } else if (_tokenMatchesIdentifier(thirdToken)) {
+ identifier = new PrefixedIdentifier(
+ new SimpleIdentifier(firstToken),
+ secondToken,
+ new SimpleIdentifier(thirdToken));
+ nextToken = thirdToken.next;
+ }
+ } else {
+ identifier = new SimpleIdentifier(firstToken);
+ nextToken = firstToken.next;
+ }
+ if (nextToken.type != TokenType.EOF) {
+ return null;
+ }
+ return new CommentReference(newKeyword, identifier);
+ } 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.
+ }
+ return null;
+ }
+
+ /**
* Parse a compilation unit, starting with the given [token]. Return the
* compilation unit that was parsed.
*/
@@ -1394,7 +2179,7 @@ class Parser {
Token memberStart = _currentToken;
TokenType type = _currentToken.type;
while (type != TokenType.EOF) {
- CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
+ CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
Keyword keyword = _currentToken.keyword;
TokenType nextType = _currentToken.next.type;
if ((keyword == Keyword.IMPORT ||
@@ -1461,7 +2246,7 @@ class Parser {
_advance();
} else {
CompilationUnitMember member =
- _parseCompilationUnitMember(commentAndMetadata);
+ parseCompilationUnitMember(commentAndMetadata);
if (member != null) {
declarations.add(member);
}
@@ -1514,23 +2299,290 @@ class Parser {
}
/**
- * Parse a conditional expression. Return the conditional expression that was
- * parsed.
+ * Parse a compilation unit member. The [commentAndMetadata] is the metadata
+ * to be associated with the member. Return the compilation unit member that
+ * was parsed, or `null` if what was parsed could not be represented as a
+ * compilation unit member.
+ *
+ * compilationUnitMember ::=
+ * classDefinition
+ * | functionTypeAlias
+ * | external functionSignature
+ * | external getterSignature
+ * | external setterSignature
+ * | functionSignature functionBody
+ * | returnType? getOrSet identifier formalParameterList functionBody
+ * | (final | const) type? staticFinalDeclarationList ';'
+ * | variableDeclaration ';'
+ */
+ CompilationUnitMember parseCompilationUnitMember(
+ CommentAndMetadata commentAndMetadata) {
+ Modifiers modifiers = _parseModifiers();
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.CLASS) {
+ return parseClassDeclaration(
+ commentAndMetadata, _validateModifiersForClass(modifiers));
+ }
+ 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 (keyword == Keyword.ENUM) {
+ _validateModifiersForEnum(modifiers);
+ return parseEnumDeclaration(commentAndMetadata);
+ } 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 (keyword == Keyword.OPERATOR && _isOperator(next)) {
+ _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
+ return _convertToFunctionDeclaration(_parseOperatorAfterKeyword(
+ commentAndMetadata,
+ modifiers.externalKeyword,
+ returnType,
+ getAndAdvance()));
+ } else if (_matchesIdentifier() &&
+ next.matchesAny(const <TokenType>[
+ TokenType.OPEN_PAREN,
+ TokenType.OPEN_CURLY_BRACKET,
+ TokenType.FUNCTION,
+ TokenType.LT
+ ])) {
+ _validateModifiersForTopLevelFunction(modifiers);
+ return _parseFunctionDeclaration(
+ commentAndMetadata, modifiers.externalKeyword, returnType);
+ } else {
+ //
+ // We have found an error of some kind. Try to recover.
+ //
+ if (_matchesIdentifier()) {
+ if (next.matchesAny(const <TokenType>[
+ TokenType.EQ,
+ TokenType.COMMA,
+ TokenType.SEMICOLON
+ ])) {
+ //
+ // We appear to have a variable declaration with a type of "void".
+ //
+ _reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType);
+ return new TopLevelVariableDeclaration(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ parseVariableDeclarationListAfterType(null,
+ _validateModifiersForTopLevelVariable(modifiers), null),
+ _expect(TokenType.SEMICOLON));
+ }
+ }
+ _reportErrorForToken(
+ ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
+ return null;
+ }
+ } else if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
+ _tokenMatchesIdentifier(next)) {
+ _validateModifiersForTopLevelFunction(modifiers);
+ return _parseFunctionDeclaration(
+ commentAndMetadata, modifiers.externalKeyword, null);
+ } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
+ _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
+ return _convertToFunctionDeclaration(_parseOperatorAfterKeyword(
+ commentAndMetadata,
+ modifiers.externalKeyword,
+ null,
+ getAndAdvance()));
+ } else if (!_matchesIdentifier()) {
+ Token keyword = modifiers.varKeyword;
+ if (keyword == null) {
+ keyword = modifiers.finalKeyword;
+ }
+ if (keyword == null) {
+ keyword = modifiers.constKeyword;
+ }
+ if (keyword != null) {
+ //
+ // We appear to have found an incomplete top-level variable declaration.
+ //
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+ VariableDeclaration variable =
+ new VariableDeclaration(createSyntheticIdentifier(), null, null);
+ List<VariableDeclaration> variables = <VariableDeclaration>[variable];
+ return new TopLevelVariableDeclaration(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ new VariableDeclarationList(null, null, keyword, null, variables),
+ _expect(TokenType.SEMICOLON));
+ }
+ _reportErrorForToken(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
+ return null;
+ } else if (_isPeekGenericTypeParametersAndOpenParen()) {
+ return _parseFunctionDeclaration(
+ commentAndMetadata, modifiers.externalKeyword, null);
+ } else if (_tokenMatches(next, TokenType.OPEN_PAREN)) {
+ TypeName returnType = _parseOptionalTypeNameComment();
+ _validateModifiersForTopLevelFunction(modifiers);
+ return _parseFunctionDeclaration(
+ commentAndMetadata, modifiers.externalKeyword, returnType);
+ } else if (next.matchesAny(const <TokenType>[
+ TokenType.EQ,
+ TokenType.COMMA,
+ TokenType.SEMICOLON
+ ])) {
+ if (modifiers.constKeyword == null &&
+ modifiers.finalKeyword == null &&
+ modifiers.varKeyword == null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE);
+ }
+ return new TopLevelVariableDeclaration(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ parseVariableDeclarationListAfterType(
+ null, _validateModifiersForTopLevelVariable(modifiers), null),
+ _expect(TokenType.SEMICOLON));
+ }
+ TypeName returnType = parseReturnType();
+ keyword = _currentToken.keyword;
+ next = _peek();
+ if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
+ _tokenMatchesIdentifier(next)) {
+ _validateModifiersForTopLevelFunction(modifiers);
+ return _parseFunctionDeclaration(
+ commentAndMetadata, modifiers.externalKeyword, returnType);
+ } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
+ _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
+ return _convertToFunctionDeclaration(_parseOperatorAfterKeyword(
+ commentAndMetadata,
+ modifiers.externalKeyword,
+ returnType,
+ getAndAdvance()));
+ } else if (_matches(TokenType.AT)) {
+ return new TopLevelVariableDeclaration(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ parseVariableDeclarationListAfterType(null,
+ _validateModifiersForTopLevelVariable(modifiers), returnType),
+ _expect(TokenType.SEMICOLON));
+ } else if (!_matchesIdentifier()) {
+ // TODO(brianwilkerson) Generalize this error. We could also be parsing a
+ // top-level variable at this point.
+ _reportErrorForToken(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
+ Token semicolon;
+ if (_matches(TokenType.SEMICOLON)) {
+ semicolon = getAndAdvance();
+ } else {
+ semicolon = _createSyntheticToken(TokenType.SEMICOLON);
+ }
+ 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);
+ } else if (next.matchesAny(const <TokenType>[
+ TokenType.OPEN_PAREN,
+ TokenType.FUNCTION,
+ TokenType.OPEN_CURLY_BRACKET,
+ TokenType.LT
+ ])) {
+ _validateModifiersForTopLevelFunction(modifiers);
+ return _parseFunctionDeclaration(
+ commentAndMetadata, modifiers.externalKeyword, returnType);
+ }
+ return new TopLevelVariableDeclaration(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ parseVariableDeclarationListAfterType(
+ null, _validateModifiersForTopLevelVariable(modifiers), returnType),
+ _expect(TokenType.SEMICOLON));
+ }
+
+ /**
+ * Parse a conditional expression. Return the conditional expression that was
+ * parsed.
+ *
+ * conditionalExpression ::=
+ * ifNullExpression ('?' expressionWithoutCascade ':' expressionWithoutCascade)?
+ */
+ Expression parseConditionalExpression() {
+ Expression condition = parseIfNullExpression();
+ if (_currentToken.type != TokenType.QUESTION) {
+ return condition;
+ }
+ Token question = getAndAdvance();
+ Expression thenExpression = parseExpressionWithoutCascade();
+ Token colon = _expect(TokenType.COLON);
+ Expression elseExpression = parseExpressionWithoutCascade();
+ return new ConditionalExpression(
+ condition, question, thenExpression, colon, elseExpression);
+ }
+
+ /**
+ * Parse a configuration in either an import or export directive.
+ *
+ * This method assumes that the current token matches `Keyword.IF`.
+ *
+ * configuration ::=
+ * 'if' '(' test ')' uri
+ *
+ * test ::=
+ * dottedName ('==' stringLiteral)?
+ *
+ * dottedName ::=
+ * identifier ('.' identifier)*
+ */
+ Configuration parseConfiguration() {
+ Token ifKeyword = getAndAdvance();
+ Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+ DottedName name = parseDottedName();
+ Token equalToken = null;
+ StringLiteral value = null;
+ if (_matches(TokenType.EQ_EQ)) {
+ equalToken = getAndAdvance();
+ value = parseStringLiteral();
+ if (value is StringInterpolation) {
+ _reportErrorForNode(
+ ParserErrorCode.INVALID_LITERAL_IN_CONFIGURATION, value);
+ }
+ }
+ Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ StringLiteral libraryUri = _parseUri();
+ return new Configuration(ifKeyword, leftParenthesis, name, equalToken,
+ value, rightParenthesis, libraryUri);
+ }
+
+ /**
+ * Parse a const expression. Return the const expression that was parsed.
*
- * conditionalExpression ::=
- * ifNullExpression ('?' expressionWithoutCascade ':' expressionWithoutCascade)?
+ * This method assumes that the current token matches `Keyword.CONST`.
+ *
+ * constExpression ::=
+ * instanceCreationExpression
+ * | listLiteral
+ * | mapLiteral
*/
- Expression parseConditionalExpression() {
- Expression condition = parseIfNullExpression();
- if (_currentToken.type != TokenType.QUESTION) {
- return condition;
+ Expression parseConstExpression() {
+ Token keyword = getAndAdvance();
+ TokenType type = _currentToken.type;
+ if (type == TokenType.LT || _injectGenericCommentTypeList()) {
+ return parseListOrMapLiteral(keyword);
+ } else if (type == TokenType.OPEN_SQUARE_BRACKET ||
+ type == TokenType.INDEX) {
+ return _parseListLiteral(keyword, null);
+ } else if (type == TokenType.OPEN_CURLY_BRACKET) {
+ return _parseMapLiteral(keyword, null);
}
- Token question = getAndAdvance();
- Expression thenExpression = parseExpressionWithoutCascade();
- Token colon = _expect(TokenType.COLON);
- Expression elseExpression = parseExpressionWithoutCascade();
- return new ConditionalExpression(
- condition, question, thenExpression, colon, elseExpression);
+ return _parseInstanceCreationExpression(keyword);
}
/**
@@ -1552,6 +2604,32 @@ 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 = getAndAdvance();
+ if (!_inLoop && !_inSwitch) {
+ _reportErrorForToken(
+ ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP, continueKeyword);
+ }
+ SimpleIdentifier label = null;
+ if (_matchesIdentifier()) {
+ label = _parseSimpleIdentifierUnchecked();
+ }
+ if (_inSwitch && !_inLoop && label == null) {
+ _reportErrorForToken(
+ ParserErrorCode.CONTINUE_WITHOUT_LABEL_IN_CASE, continueKeyword);
+ }
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new ContinueStatement(continueKeyword, label, semicolon);
+ }
+
+ /**
* Parse the script tag and directives in a compilation unit, starting with
* the given [token], until the first non-directive is encountered. The
* remainder of the compilation unit will not be parsed. Specifically, if
@@ -1560,3600 +2638,3491 @@ class Parser {
*/
CompilationUnit parseDirectives(Token token) {
_currentToken = token;
- return _parseDirectives();
- }
-
- /**
- * Parse an expression, starting with the given [token]. Return the expression
- * that was parsed, or `null` if the tokens do not represent a recognizable
- * expression.
- */
- Expression parseExpression(Token token) {
- _currentToken = token;
- return parseExpression2();
+ return parseDirectives2();
}
/**
- * Parse an expression that might contain a cascade. Return the expression
- * that was parsed.
+ * Parse the script tag and directives in a compilation unit until the first
+ * non-directive is encountered. Return the compilation unit that was parsed.
*
- * expression ::=
- * assignableExpression assignmentOperator expression
- * | conditionalExpression cascadeSection*
- * | throwExpression
+ * compilationUnit ::=
+ * scriptTag? directive*
*/
- Expression parseExpression2() {
- Keyword keyword = _currentToken.keyword;
- if (keyword == Keyword.THROW) {
- return _parseThrowExpression();
- } else if (keyword == Keyword.RETHROW) {
- // TODO(brianwilkerson) Rethrow is a statement again.
- return _parseRethrowExpression();
+ CompilationUnit parseDirectives2() {
+ Token firstToken = _currentToken;
+ ScriptTag scriptTag = null;
+ if (_matches(TokenType.SCRIPT_TAG)) {
+ scriptTag = new ScriptTag(getAndAdvance());
}
- //
- // assignableExpression is a subset of conditionalExpression, so we can
- // parse a conditional expression and then determine whether it is followed
- // by an assignmentOperator, checking for conformance to the restricted
- // grammar after making that determination.
- //
- Expression expression = parseConditionalExpression();
- TokenType type = _currentToken.type;
- if (type == TokenType.PERIOD_PERIOD) {
- List<Expression> cascadeSections = <Expression>[];
- do {
- Expression section = _parseCascadeSection();
- if (section != null) {
- cascadeSections.add(section);
+ List<Directive> directives = <Directive>[];
+ while (!_matches(TokenType.EOF)) {
+ CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
+ 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();
+ } else {
+ while (!_matches(TokenType.EOF)) {
+ _advance();
}
- } while (_currentToken.type == TokenType.PERIOD_PERIOD);
- return new CascadeExpression(expression, cascadeSections);
- } else if (type.isAssignmentOperator) {
- Token operator = getAndAdvance();
- _ensureAssignable(expression);
- return new AssignmentExpression(expression, operator, parseExpression2());
- }
- return expression;
- }
-
- /**
- * Parse an expression that does not contain any cascades. Return the
- * expression that was parsed.
- *
- * expressionWithoutCascade ::=
- * assignableExpression assignmentOperator expressionWithoutCascade
- * | conditionalExpression
- * | throwExpressionWithoutCascade
- */
- Expression parseExpressionWithoutCascade() {
- if (_matchesKeyword(Keyword.THROW)) {
- return _parseThrowExpressionWithoutCascade();
- } else if (_matchesKeyword(Keyword.RETHROW)) {
- return _parseRethrowExpression();
- }
- //
- // assignableExpression is a subset of conditionalExpression, so we can
- // parse a conditional expression and then determine whether it is followed
- // by an assignmentOperator, checking for conformance to the restricted
- // grammar after making that determination.
- //
- Expression expression = parseConditionalExpression();
- if (_currentToken.type.isAssignmentOperator) {
- Token operator = getAndAdvance();
- _ensureAssignable(expression);
- expression = new AssignmentExpression(
- expression, operator, parseExpressionWithoutCascade());
+ return new CompilationUnit(
+ firstToken, scriptTag, directives, null, _currentToken);
+ }
}
- return expression;
- }
-
- /**
- * 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 = getAndAdvance();
- TypeName superclass = parseTypeName(false);
- return new ExtendsClause(keyword, superclass);
+ return new CompilationUnit(
+ firstToken, scriptTag, directives, null, _currentToken);
}
/**
- * Parse a list of formal parameters. Return the formal parameters that were
- * parsed.
- *
- * formalParameterList ::=
- * '(' ')'
- * | '(' normalFormalParameters (',' optionalFormalParameters)? ')'
- * | '(' optionalFormalParameters ')'
- *
- * normalFormalParameters ::=
- * normalFormalParameter (',' normalFormalParameter)*
- *
- * optionalFormalParameters ::=
- * optionalPositionalFormalParameters
- * | namedFormalParameters
- *
- * optionalPositionalFormalParameters ::=
- * '[' defaultFormalParameter (',' defaultFormalParameter)* ']'
+ * 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.
*
- * namedFormalParameters ::=
- * '{' defaultNamedParameter (',' defaultNamedParameter)* '}'
+ * documentationComment ::=
+ * multiLineComment?
+ * | singleLineComment*
*/
- FormalParameterList parseFormalParameterList() {
- if (_matches(TokenType.OPEN_PAREN)) {
- return _parseFormalParameterListUnchecked();
+ Comment parseDocumentationComment(List<DocumentationCommentToken> tokens) {
+ if (tokens == null) {
+ return null;
}
- // 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));
- }
-
- /**
- * Parse a function expression. Return the function expression that was
- * parsed.
- *
- * functionExpression ::=
- * typeParameters? formalParameterList functionExpressionBody
- */
- FunctionExpression parseFunctionExpression() {
- TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
- FormalParameterList parameters = parseFormalParameterList();
- _validateFormalParameterList(parameters);
- FunctionBody body =
- _parseFunctionBody(false, ParserErrorCode.MISSING_FUNCTION_BODY, true);
- return new FunctionExpression(typeParameters, parameters, body);
+ List<CommentReference> references = _parseCommentReferences(tokens);
+ return Comment.createDocumentationCommentWithReferences(tokens, references);
}
/**
- * Parse an if-null expression. Return the if-null expression that was
- * parsed.
+ * Parse a documentation comment. Return the documentation comment that was
+ * parsed, or `null` if there was no comment.
*
- * ifNullExpression ::= logicalOrExpression ('??' logicalOrExpression)*
+ * documentationComment ::=
+ * multiLineComment?
+ * | singleLineComment*
*/
- Expression parseIfNullExpression() {
- Expression expression = parseLogicalOrExpression();
- while (_currentToken.type == TokenType.QUESTION_QUESTION) {
- expression = new BinaryExpression(
- expression, getAndAdvance(), parseLogicalOrExpression());
+ List<DocumentationCommentToken> parseDocumentationCommentTokens() {
+ List<DocumentationCommentToken> tokens = <DocumentationCommentToken>[];
+ CommentToken commentToken = _currentToken.precedingComments;
+ while (commentToken != null) {
+ if (commentToken is DocumentationCommentToken) {
+ if (tokens.isNotEmpty) {
+ if (commentToken.type == TokenType.SINGLE_LINE_COMMENT) {
+ if (tokens[0].type != TokenType.SINGLE_LINE_COMMENT) {
+ tokens.clear();
+ }
+ } else {
+ tokens.clear();
+ }
+ }
+ tokens.add(commentToken);
+ }
+ commentToken = commentToken.next;
}
- return expression;
+ return tokens.isEmpty ? null : tokens;
}
/**
- * Parse an implements clause. Return the implements clause that was parsed.
+ * Parse a do statement. Return the do statement that was parsed.
*
- * This method assumes that the current token matches `Keyword.IMPLEMENTS`.
+ * This method assumes that the current token matches `Keyword.DO`.
*
- * implementsClause ::=
- * 'implements' type (',' type)*
+ * doStatement ::=
+ * 'do' statement 'while' '(' expression ')' ';'
*/
- ImplementsClause parseImplementsClause() {
- Token keyword = getAndAdvance();
- List<TypeName> interfaces = <TypeName>[];
- interfaces.add(parseTypeName(false));
- while (_optional(TokenType.COMMA)) {
- interfaces.add(parseTypeName(false));
+ Statement parseDoStatement() {
+ bool wasInLoop = _inLoop;
+ _inLoop = true;
+ try {
+ Token doKeyword = getAndAdvance();
+ Statement body = parseStatement2();
+ Token whileKeyword = _expectKeyword(Keyword.WHILE);
+ Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+ Expression condition = parseExpression2();
+ Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new DoStatement(doKeyword, body, whileKeyword, leftParenthesis,
+ condition, rightParenthesis, semicolon);
+ } finally {
+ _inLoop = wasInLoop;
}
- return new ImplementsClause(keyword, interfaces);
- }
-
- /**
- * 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 =
- _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration);
- Token colon = getAndAdvance();
- return new Label(label, colon);
}
/**
- * Parse a library identifier. Return the library identifier that was parsed.
+ * Parse a dotted name. Return the dotted name that was parsed.
*
- * libraryIdentifier ::=
+ * dottedName ::=
* identifier ('.' identifier)*
*/
- LibraryIdentifier parseLibraryIdentifier() {
- List<SimpleIdentifier> components = <SimpleIdentifier>[];
- components.add(parseSimpleIdentifier());
+ DottedName parseDottedName() {
+ List<SimpleIdentifier> components = <SimpleIdentifier>[
+ parseSimpleIdentifier()
+ ];
while (_optional(TokenType.PERIOD)) {
components.add(parseSimpleIdentifier());
}
- return new LibraryIdentifier(components);
+ return new DottedName(components);
}
/**
- * Parse a logical or expression. Return the logical or expression that was
- * parsed.
+ * Parse an empty statement. Return the empty statement that was parsed.
*
- * logicalOrExpression ::=
- * logicalAndExpression ('||' logicalAndExpression)*
- */
- Expression parseLogicalOrExpression() {
- Expression expression = _parseLogicalAndExpression();
- while (_currentToken.type == TokenType.BAR_BAR) {
- expression = new BinaryExpression(
- expression, getAndAdvance(), _parseLogicalAndExpression());
- }
- return expression;
- }
-
- /**
- * Parse a map literal entry. Return the map literal entry that was parsed.
+ * This method assumes that the current token matches `TokenType.SEMICOLON`.
*
- * mapLiteralEntry ::=
- * expression ':' expression
+ * emptyStatement ::=
+ * ';'
*/
- MapLiteralEntry parseMapLiteralEntry() {
- Expression key = parseExpression2();
- Token separator = _expect(TokenType.COLON);
- Expression value = parseExpression2();
- return new MapLiteralEntry(key, separator, value);
- }
+ Statement parseEmptyStatement() => new EmptyStatement(getAndAdvance());
/**
- * Parse a normal formal parameter. Return the normal formal parameter that
- * was parsed.
- *
- * normalFormalParameter ::=
- * functionSignature
- * | fieldFormalParameter
- * | simpleFormalParameter
- *
- * functionSignature:
- * metadata returnType? identifier typeParameters? formalParameterList
+ * Parse an enum declaration. The [commentAndMetadata] is the metadata to be
+ * associated with the member. Return the enum declaration that was parsed.
*
- * fieldFormalParameter ::=
- * metadata finalConstVarOrType? 'this' '.' identifier
+ * This method assumes that the current token matches `Keyword.ENUM`.
*
- * simpleFormalParameter ::=
- * declaredIdentifier
- * | metadata identifier
+ * enumType ::=
+ * metadata 'enum' id '{' id (',' id)* (',')? '}'
*/
- NormalFormalParameter parseNormalFormalParameter() {
- CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
- FinalConstVarOrType holder = _parseFinalConstVarOrType(true);
- Token thisKeyword = null;
- Token period = null;
- if (_matchesKeyword(Keyword.THIS)) {
- thisKeyword = getAndAdvance();
- period = _expect(TokenType.PERIOD);
- }
- SimpleIdentifier identifier = parseSimpleIdentifier();
- TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
- if (_matches(TokenType.OPEN_PAREN)) {
- FormalParameterList parameters = _parseFormalParameterListUnchecked();
- if (thisKeyword == null) {
- if (holder.keyword != null) {
- _reportErrorForToken(
- ParserErrorCode.FUNCTION_TYPED_PARAMETER_VAR, holder.keyword);
- }
- Token question = null;
- if (enableNnbd && _matches(TokenType.QUESTION)) {
- question = getAndAdvance();
- }
- return new FunctionTypedFormalParameter(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- holder.type,
- new SimpleIdentifier(identifier.token, isDeclaration: true),
- typeParameters,
- parameters,
- question: question);
+ EnumDeclaration parseEnumDeclaration(CommentAndMetadata commentAndMetadata) {
+ Token keyword = getAndAdvance();
+ SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
+ Token leftBracket = null;
+ List<EnumConstantDeclaration> constants = <EnumConstantDeclaration>[];
+ Token rightBracket = null;
+ if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+ leftBracket = getAndAdvance();
+ if (_matchesIdentifier() || _matches(TokenType.AT)) {
+ constants.add(_parseEnumConstantDeclaration());
+ } else if (_matches(TokenType.COMMA) &&
+ _tokenMatchesIdentifier(_peek())) {
+ constants.add(_parseEnumConstantDeclaration());
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
} else {
- return new FieldFormalParameter(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- holder.keyword,
- holder.type,
- thisKeyword,
- period,
- identifier,
- typeParameters,
- parameters);
+ constants.add(_parseEnumConstantDeclaration());
+ _reportErrorForCurrentToken(ParserErrorCode.EMPTY_ENUM_BODY);
}
- } else if (typeParameters != null) {
- // TODO(brianwilkerson) Report an error. It looks like a function-typed
- // parameter with no parameter list.
- //_reportErrorForToken(ParserErrorCode.MISSING_PARAMETERS, typeParameters.endToken);
- }
- TypeName type = holder.type;
- if (type != null) {
- if (_tokenMatchesKeyword(type.name.beginToken, Keyword.VOID)) {
- _reportErrorForToken(
- ParserErrorCode.VOID_PARAMETER, type.name.beginToken);
- } else if (holder.keyword != null &&
- _tokenMatchesKeyword(holder.keyword, Keyword.VAR)) {
- _reportErrorForToken(ParserErrorCode.VAR_AND_TYPE, holder.keyword);
+ while (_optional(TokenType.COMMA)) {
+ if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+ break;
+ }
+ constants.add(_parseEnumConstantDeclaration());
}
+ rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
+ } else {
+ leftBracket = _createSyntheticToken(TokenType.OPEN_CURLY_BRACKET);
+ rightBracket = _createSyntheticToken(TokenType.CLOSE_CURLY_BRACKET);
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_ENUM_BODY);
}
- if (thisKeyword != null) {
- // TODO(brianwilkerson) If there are type parameters but no parameters,
- // should we create a synthetic empty parameter list here so we can
- // capture the type parameters?
- return new FieldFormalParameter(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- holder.keyword,
- holder.type,
- thisKeyword,
- period,
- identifier,
- null,
- null);
- }
- return new SimpleFormalParameter(
+ return new EnumDeclaration(
commentAndMetadata.comment,
commentAndMetadata.metadata,
- holder.keyword,
- holder.type,
- new SimpleIdentifier(identifier.token, isDeclaration: true));
+ keyword,
+ name,
+ leftBracket,
+ constants,
+ rightBracket);
}
/**
- * Parse a prefixed identifier. Return the prefixed identifier that was
- * parsed.
- *
- * prefixedIdentifier ::=
- * identifier ('.' identifier)?
+ * Parse an expression, starting with the given [token]. Return the expression
+ * that was parsed, or `null` if the tokens do not represent a recognizable
+ * expression.
*/
- Identifier parsePrefixedIdentifier() {
- return _parsePrefixedIdentifierAfterIdentifier(parseSimpleIdentifier());
+ Expression parseExpression(Token token) {
+ _currentToken = token;
+ return parseExpression2();
}
/**
- * Parse a return type. Return the return type that was parsed.
+ * Parse an expression that might contain a cascade. Return the expression
+ * that was parsed.
*
- * returnType ::=
- * 'void'
- * | type
+ * expression ::=
+ * assignableExpression assignmentOperator expression
+ * | conditionalExpression cascadeSection*
+ * | throwExpression
*/
- TypeName parseReturnType() {
- if (_currentToken.keyword == Keyword.VOID) {
- return new TypeName(new SimpleIdentifier(getAndAdvance()), null);
- } else {
- return parseTypeName(false);
+ Expression parseExpression2() {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.THROW) {
+ return parseThrowExpression();
+ } else if (keyword == Keyword.RETHROW) {
+ // TODO(brianwilkerson) Rethrow is a statement again.
+ return parseRethrowExpression();
+ }
+ //
+ // assignableExpression is a subset of conditionalExpression, so we can
+ // parse a conditional expression and then determine whether it is followed
+ // by an assignmentOperator, checking for conformance to the restricted
+ // grammar after making that determination.
+ //
+ Expression expression = parseConditionalExpression();
+ TokenType type = _currentToken.type;
+ if (type == TokenType.PERIOD_PERIOD) {
+ List<Expression> cascadeSections = <Expression>[];
+ do {
+ Expression section = parseCascadeSection();
+ if (section != null) {
+ cascadeSections.add(section);
+ }
+ } while (_currentToken.type == TokenType.PERIOD_PERIOD);
+ return new CascadeExpression(expression, cascadeSections);
+ } else if (type.isAssignmentOperator) {
+ Token operator = getAndAdvance();
+ _ensureAssignable(expression);
+ return new AssignmentExpression(expression, operator, parseExpression2());
}
+ return expression;
}
/**
- * Parse a simple identifier. Return the simple identifier that was parsed.
+ * Parse a list of expressions. Return the expression that was parsed.
*
- * identifier ::=
- * IDENTIFIER
+ * expressionList ::=
+ * expression (',' expression)*
*/
- SimpleIdentifier parseSimpleIdentifier({bool isDeclaration: false}) {
- if (_matchesIdentifier()) {
- return _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration);
+ List<Expression> parseExpressionList() {
+ List<Expression> expressions = <Expression>[parseExpression2()];
+ while (_optional(TokenType.COMMA)) {
+ expressions.add(parseExpression2());
}
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
- return _createSyntheticIdentifier(isDeclaration: isDeclaration);
- }
-
- /**
- * Parse a statement, starting with the given [token]. Return the statement
- * that was parsed, or `null` if the tokens do not represent a recognizable
- * statement.
- */
- Statement parseStatement(Token token) {
- _currentToken = token;
- return parseStatement2();
+ return expressions;
}
/**
- * Parse a statement. Return the statement that was parsed.
+ * Parse an expression that does not contain any cascades. Return the
+ * expression that was parsed.
*
- * statement ::=
- * label* nonLabeledStatement
+ * expressionWithoutCascade ::=
+ * assignableExpression assignmentOperator expressionWithoutCascade
+ * | conditionalExpression
+ * | throwExpressionWithoutCascade
*/
- Statement parseStatement2() {
- List<Label> labels = null;
- while (_matchesIdentifier() && _currentToken.next.type == TokenType.COLON) {
- Label label = parseLabel(isDeclaration: true);
- if (labels == null) {
- labels = <Label>[label];
- } else {
- labels.add(label);
- }
+ Expression parseExpressionWithoutCascade() {
+ if (_matchesKeyword(Keyword.THROW)) {
+ return parseThrowExpressionWithoutCascade();
+ } else if (_matchesKeyword(Keyword.RETHROW)) {
+ return parseRethrowExpression();
}
- Statement statement = _parseNonLabeledStatement();
- if (labels == null) {
- return statement;
+ //
+ // assignableExpression is a subset of conditionalExpression, so we can
+ // parse a conditional expression and then determine whether it is followed
+ // by an assignmentOperator, checking for conformance to the restricted
+ // grammar after making that determination.
+ //
+ Expression expression = parseConditionalExpression();
+ if (_currentToken.type.isAssignmentOperator) {
+ Token operator = getAndAdvance();
+ _ensureAssignable(expression);
+ expression = new AssignmentExpression(
+ expression, operator, parseExpressionWithoutCascade());
}
- return new LabeledStatement(labels, statement);
+ return expression;
}
/**
- * Parse a sequence of statements, starting with the given [token]. Return the
- * statements that were parsed, or `null` if the tokens do not represent a
- * recognizable sequence of statements.
+ * 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
*/
- List<Statement> parseStatements(Token token) {
- _currentToken = token;
- return _parseStatementList();
+ ExtendsClause parseExtendsClause() {
+ Token keyword = getAndAdvance();
+ TypeName superclass = parseTypeName(false);
+ return new ExtendsClause(keyword, superclass);
}
/**
- * Parse a string literal. Return the string literal that was parsed.
+ * Parse the 'final', 'const', 'var' or type preceding a variable declaration.
+ * The [optional] is `true` if the keyword and type are optional. Return the
+ * 'final', 'const', 'var' or type that was parsed.
*
- * stringLiteral ::=
- * MULTI_LINE_STRING+
- * | SINGLE_LINE_STRING+
+ * finalConstVarOrType ::=
+ * 'final' type?
+ * | 'const' type?
+ * | 'var'
+ * | type
*/
- StringLiteral parseStringLiteral() {
- if (_matches(TokenType.STRING)) {
- return _parseStringLiteralUnchecked();
+ FinalConstVarOrType parseFinalConstVarOrType(bool optional) {
+ Token keywordToken = null;
+ TypeName type = null;
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.FINAL || keyword == Keyword.CONST) {
+ keywordToken = getAndAdvance();
+ if (_isTypedIdentifier(_currentToken)) {
+ type = parseTypeName(false);
+ } else {
+ // Support `final/*=T*/ x;`
+ type = _parseOptionalTypeNameComment();
+ }
+ } else if (keyword == Keyword.VAR) {
+ keywordToken = getAndAdvance();
+ // Support `var/*=T*/ x;`
+ type = _parseOptionalTypeNameComment();
+ if (type != null) {
+ // Clear the keyword to prevent an error.
+ keywordToken = null;
+ }
+ } else if (_isTypedIdentifier(_currentToken)) {
+ type = parseReturnType();
+ } else if (!optional) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE);
+ } else {
+ // Support parameters such as `(/*=K*/ key, /*=V*/ value)`
+ // This is not supported if the type is required.
+ type = _parseOptionalTypeNameComment();
}
- _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_STRING_LITERAL);
- return _createSyntheticStringLiteral();
+ return new FinalConstVarOrType(keywordToken, type);
}
/**
- * Parse a list of type arguments. Return the type argument list that was
+ * Parse a list of formal parameters. Return the formal parameters that were
* parsed.
*
- * This method assumes that the current token matches `TokenType.LT`.
+ * formalParameterList ::=
+ * '(' ')'
+ * | '(' normalFormalParameters (',' optionalFormalParameters)? ')'
+ * | '(' optionalFormalParameters ')'
*
- * typeArguments ::=
- * '<' typeList '>'
+ * normalFormalParameters ::=
+ * normalFormalParameter (',' normalFormalParameter)*
*
- * typeList ::=
- * type (',' type)*
- */
- TypeArgumentList parseTypeArgumentList() {
- Token leftBracket = getAndAdvance();
- List<TypeName> arguments = <TypeName>[parseTypeName(false)];
- while (_optional(TokenType.COMMA)) {
- arguments.add(parseTypeName(false));
- }
- Token rightBracket = _expectGt();
- return new TypeArgumentList(leftBracket, arguments, rightBracket);
- }
-
- /**
- * Parse a type name. Return the type name that was parsed.
+ * optionalFormalParameters ::=
+ * optionalPositionalFormalParameters
+ * | namedFormalParameters
*
- * type ::=
- * qualified typeArguments?
- */
- TypeName parseTypeName(bool inExpression) {
- TypeName realType = _parseTypeName(inExpression);
- // 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 ?? realType;
- }
-
- /**
- * Parse a type parameter. Return the type parameter that was parsed.
+ * optionalPositionalFormalParameters ::=
+ * '[' defaultFormalParameter (',' defaultFormalParameter)* ']'
*
- * typeParameter ::=
- * metadata name ('extends' bound)?
+ * namedFormalParameters ::=
+ * '{' defaultNamedParameter (',' defaultNamedParameter)* '}'
*/
- TypeParameter parseTypeParameter() {
- CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
- SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
- if (_matchesKeyword(Keyword.EXTENDS)) {
- Token keyword = getAndAdvance();
- TypeName bound = parseTypeName(false);
- return new TypeParameter(commentAndMetadata.comment,
- commentAndMetadata.metadata, name, keyword, bound);
+ FormalParameterList parseFormalParameterList() {
+ if (_matches(TokenType.OPEN_PAREN)) {
+ return _parseFormalParameterListUnchecked();
}
- return new TypeParameter(commentAndMetadata.comment,
- commentAndMetadata.metadata, name, null, null);
+ // 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));
}
/**
- * Parse a list of type parameters. Return the list of type parameters that
- * were parsed.
+ * Parse a for statement. Return the for statement that was parsed.
*
- * This method assumes that the current token matches `TokenType.LT`.
+ * forStatement ::=
+ * 'for' '(' forLoopParts ')' statement
*
- * typeParameterList ::=
- * '<' typeParameter (',' typeParameter)* '>'
+ * forLoopParts ::=
+ * forInitializerStatement expression? ';' expressionList?
+ * | declaredIdentifier 'in' expression
+ * | identifier 'in' expression
+ *
+ * forInitializerStatement ::=
+ * localVariableDeclaration ';'
+ * | expression? ';'
*/
- TypeParameterList parseTypeParameterList() {
- Token leftBracket = getAndAdvance();
- List<TypeParameter> typeParameters = <TypeParameter>[parseTypeParameter()];
- while (_optional(TokenType.COMMA)) {
- typeParameters.add(parseTypeParameter());
+ Statement parseForStatement() {
+ bool wasInLoop = _inLoop;
+ _inLoop = true;
+ try {
+ Token awaitKeyword = null;
+ if (_matchesString(_AWAIT)) {
+ awaitKeyword = getAndAdvance();
+ }
+ Token forKeyword = _expectKeyword(Keyword.FOR);
+ Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+ VariableDeclarationList variableList = null;
+ Expression initialization = null;
+ if (!_matches(TokenType.SEMICOLON)) {
+ CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
+ if (_matchesIdentifier() &&
+ (_tokenMatchesKeyword(_peek(), Keyword.IN) ||
+ _tokenMatches(_peek(), TokenType.COLON))) {
+ SimpleIdentifier variableName = _parseSimpleIdentifierUnchecked();
+ variableList = new VariableDeclarationList(commentAndMetadata.comment,
+ commentAndMetadata.metadata, null, null, <VariableDeclaration>[
+ new VariableDeclaration(variableName, null, null)
+ ]);
+ } else if (isInitializedVariableDeclaration()) {
+ variableList =
+ parseVariableDeclarationListAfterMetadata(commentAndMetadata);
+ } else {
+ initialization = parseExpression2();
+ }
+ 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;
+ SimpleIdentifier identifier = null;
+ if (variableList == null) {
+ // We found: <expression> 'in'
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MISSING_VARIABLE_IN_FOR_EACH);
+ } else {
+ NodeList<VariableDeclaration> variables = variableList.variables;
+ if (variables.length > 1) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MULTIPLE_VARIABLES_IN_FOR_EACH,
+ [variables.length.toString()]);
+ }
+ VariableDeclaration variable = variables[0];
+ if (variable.initializer != null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.INITIALIZED_VARIABLE_IN_FOR_EACH);
+ }
+ Token keyword = variableList.keyword;
+ TypeName type = variableList.type;
+ if (keyword != null || type != null) {
+ loopVariable = new DeclaredIdentifier(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ keyword,
+ type,
+ new SimpleIdentifier(variable.name.token,
+ isDeclaration: true));
+ } else {
+ if (commentAndMetadata.hasMetadata) {
+ // TODO(jwren) metadata isn't allowed before the identifier in
+ // "identifier in expression", add warning if commentAndMetadata
+ // has content
+ }
+ identifier = variable.name;
+ }
+ }
+ Token inKeyword = getAndAdvance();
+ Expression iterator = parseExpression2();
+ Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ Statement body = parseStatement2();
+ if (loopVariable == null) {
+ return new ForEachStatement.withReference(
+ awaitKeyword,
+ forKeyword,
+ leftParenthesis,
+ identifier,
+ inKeyword,
+ iterator,
+ rightParenthesis,
+ body);
+ }
+ return new ForEachStatement.withDeclaration(
+ awaitKeyword,
+ forKeyword,
+ leftParenthesis,
+ loopVariable,
+ inKeyword,
+ iterator,
+ rightParenthesis,
+ body);
+ }
+ }
+ if (awaitKeyword != null) {
+ _reportErrorForToken(
+ ParserErrorCode.INVALID_AWAIT_IN_FOR, awaitKeyword);
+ }
+ Token leftSeparator = _expect(TokenType.SEMICOLON);
+ Expression condition = null;
+ if (!_matches(TokenType.SEMICOLON)) {
+ condition = parseExpression2();
+ }
+ Token rightSeparator = _expect(TokenType.SEMICOLON);
+ List<Expression> updaters = null;
+ if (!_matches(TokenType.CLOSE_PAREN)) {
+ updaters = parseExpressionList();
+ }
+ Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ Statement body = parseStatement2();
+ return new ForStatement(
+ forKeyword,
+ leftParenthesis,
+ variableList,
+ initialization,
+ leftSeparator,
+ condition,
+ rightSeparator,
+ updaters,
+ rightParenthesis,
+ body);
+ } finally {
+ _inLoop = wasInLoop;
}
- Token rightBracket = _expectGt();
- return new TypeParameterList(leftBracket, typeParameters, rightBracket);
}
/**
- * Parse a with clause. Return the with clause that was parsed.
+ * Parse a function body. The [mayBeEmpty] is `true` if the function body is
+ * allowed to be empty. The [emptyErrorCode] is the error code to report if
+ * function body expected, but not found. The [inExpression] is `true` if the
+ * function body is being parsed as part of an expression and therefore does
+ * not have a terminating semicolon. Return the function body that was parsed.
*
- * This method assumes that the current token matches `Keyword.WITH`.
+ * functionBody ::=
+ * '=>' expression ';'
+ * | block
*
- * withClause ::=
- * 'with' typeName (',' typeName)*
- */
- WithClause parseWithClause() {
- Token withKeyword = getAndAdvance();
- List<TypeName> types = <TypeName>[parseTypeName(false)];
- while (_optional(TokenType.COMMA)) {
- types.add(parseTypeName(false));
- }
- return new WithClause(withKeyword, types);
- }
-
- /**
- * Advance to the next token in the token stream.
- */
- void _advance() {
- _currentToken = _currentToken.next;
- }
-
- /**
- * Append the character equivalent of the given [scalarValue] to the given
- * [builder]. Use the [startIndex] and [endIndex] to report an error, and
- * don't append anything to the builder, if the scalar value is invalid. The
- * [escapeSequence] is the escape sequence that was parsed to produce the
- * scalar value (used for error reporting).
- */
- void _appendScalarValue(StringBuffer buffer, String escapeSequence,
- int scalarValue, int startIndex, int endIndex) {
- if (scalarValue < 0 ||
- scalarValue > Character.MAX_CODE_POINT ||
- (scalarValue >= 0xD800 && scalarValue <= 0xDFFF)) {
- _reportErrorForCurrentToken(
- ParserErrorCode.INVALID_CODE_POINT, [escapeSequence]);
- return;
- }
- if (scalarValue < Character.MAX_VALUE) {
- buffer.writeCharCode(scalarValue);
- } else {
- buffer.write(Character.toChars(scalarValue));
- }
- }
-
- /**
- * Clone all token starting from the given [token] up to the end of the token
- * stream, and return the first token in the new token stream.
+ * functionExpressionBody ::=
+ * '=>' expression
+ * | block
*/
- Token _cloneTokens(Token token) {
- if (token == null) {
- return null;
- }
- token = token is CommentToken ? token.parent : token;
- Token head = new Token(TokenType.EOF, -1);
- head.setNext(head);
- Token current = head;
- while (token.type != TokenType.EOF) {
- Token clone = token.copy();
- current.setNext(clone);
- current = clone;
- token = token.next;
+ FunctionBody parseFunctionBody(
+ bool mayBeEmpty, ParserErrorCode emptyErrorCode, bool inExpression) {
+ bool wasInAsync = _inAsync;
+ bool wasInGenerator = _inGenerator;
+ bool wasInLoop = _inLoop;
+ bool wasInSwitch = _inSwitch;
+ _inAsync = false;
+ _inGenerator = false;
+ _inLoop = false;
+ _inSwitch = false;
+ try {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.SEMICOLON) {
+ if (!mayBeEmpty) {
+ _reportErrorForCurrentToken(emptyErrorCode);
+ }
+ return new EmptyFunctionBody(getAndAdvance());
+ }
+ Token keyword = null;
+ Token star = null;
+ 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 (type == TokenType.FUNCTION) {
+ if (keyword != null) {
+ if (!foundAsync) {
+ _reportErrorForToken(ParserErrorCode.INVALID_SYNC, keyword);
+ keyword = null;
+ } else if (star != null) {
+ _reportErrorForToken(
+ ParserErrorCode.INVALID_STAR_AFTER_ASYNC, star);
+ }
+ }
+ Token functionDefinition = getAndAdvance();
+ if (_matchesKeyword(Keyword.RETURN)) {
+ _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
+ [_currentToken.lexeme]);
+ _advance();
+ }
+ Expression expression = parseExpression2();
+ Token semicolon = null;
+ if (!inExpression) {
+ semicolon = _expect(TokenType.SEMICOLON);
+ }
+ if (!_parseFunctionBodies) {
+ return new EmptyFunctionBody(
+ _createSyntheticToken(TokenType.SEMICOLON));
+ }
+ return new ExpressionFunctionBody(
+ keyword, functionDefinition, expression, semicolon);
+ } else if (type == TokenType.OPEN_CURLY_BRACKET) {
+ if (keyword != null) {
+ if (foundSync && star == null) {
+ _reportErrorForToken(
+ ParserErrorCode.MISSING_STAR_AFTER_SYNC, keyword);
+ }
+ }
+ if (!_parseFunctionBodies) {
+ _skipBlock();
+ return new EmptyFunctionBody(
+ _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);
+ return new EmptyFunctionBody(
+ _createSyntheticToken(TokenType.SEMICOLON));
+ }
+ } finally {
+ _inAsync = wasInAsync;
+ _inGenerator = wasInGenerator;
+ _inLoop = wasInLoop;
+ _inSwitch = wasInSwitch;
}
- Token tail = new Token(TokenType.EOF, 0);
- tail.setNext(tail);
- current.setNext(tail);
- return head.next;
}
/**
- * Return the content of a string with the given literal representation. The
- * [lexeme] is the literal representation of the string. The flag [isFirst] is
- * `true` if this is the first token in a string literal. The flag [isLast] is
- * `true` if this is the last token in a string literal.
+ * Parse a function declaration statement. Return the function declaration
+ * statement that was parsed.
+ *
+ * functionDeclarationStatement ::=
+ * functionSignature functionBody
*/
- String _computeStringValue(String lexeme, bool isFirst, bool isLast) {
- StringLexemeHelper helper = new StringLexemeHelper(lexeme, isFirst, isLast);
- int start = helper.start;
- int end = helper.end;
- bool stringEndsAfterStart = end >= start;
- assert(stringEndsAfterStart);
- if (!stringEndsAfterStart) {
- AnalysisEngine.instance.logger.logError(
- "Internal error: computeStringValue($lexeme, $isFirst, $isLast)");
- return "";
- }
- if (helper.isRaw) {
- return lexeme.substring(start, end);
- }
- StringBuffer buffer = new StringBuffer();
- int index = start;
- while (index < end) {
- index = _translateCharacter(buffer, lexeme, index);
- }
- return buffer.toString();
+ Statement parseFunctionDeclarationStatement() {
+ Modifiers modifiers = _parseModifiers();
+ _validateModifiersForFunctionDeclarationStatement(modifiers);
+ return _parseFunctionDeclarationStatementAfterReturnType(
+ parseCommentAndMetadata(), _parseOptionalReturnType());
}
/**
- * Convert the given [method] declaration into the nearest valid top-level
- * function declaration (that is, the function declaration that most closely
- * captures the components of the given method declaration).
+ * Parse a function expression. Return the function expression that was
+ * parsed.
+ *
+ * functionExpression ::=
+ * typeParameters? formalParameterList functionExpressionBody
*/
- FunctionDeclaration _convertToFunctionDeclaration(MethodDeclaration method) =>
- new FunctionDeclaration(
- method.documentationComment,
- method.metadata,
- method.externalKeyword,
- method.returnType,
- method.propertyKeyword,
- method.name,
- new FunctionExpression(
- method.typeParameters, method.parameters, method.body));
+ FunctionExpression parseFunctionExpression() {
+ TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
+ FormalParameterList parameters = parseFormalParameterList();
+ _validateFormalParameterList(parameters);
+ FunctionBody body =
+ parseFunctionBody(false, ParserErrorCode.MISSING_FUNCTION_BODY, true);
+ return new FunctionExpression(typeParameters, parameters, body);
+ }
/**
- * Return `true` if the current token could be the start of a compilation unit
- * member. This method is used for recovery purposes to decide when to stop
- * skipping tokens after finding an error while parsing a compilation unit
- * member.
+ * Parse an if-null expression. Return the if-null expression that was
+ * parsed.
+ *
+ * ifNullExpression ::= logicalOrExpression ('??' logicalOrExpression)*
*/
- bool _couldBeStartOfCompilationUnitMember() {
- 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 (keyword == Keyword.CLASS) {
- // This looks like the start of a class definition
- return true;
- } else if (keyword == Keyword.TYPEDEF &&
- nextType != TokenType.PERIOD &&
- nextType != TokenType.LT) {
- // This looks like the start of a typedef
- return true;
- } 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 (nextType == TokenType.OPEN_PAREN) {
- // This looks like the start of a function
- return true;
- }
- Token token = _skipReturnType(_currentToken);
- if (token == null) {
- return false;
- }
- // 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;
- }
+ Expression parseIfNullExpression() {
+ Expression expression = parseLogicalOrExpression();
+ while (_currentToken.type == TokenType.QUESTION_QUESTION) {
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), parseLogicalOrExpression());
}
- return false;
+ return expression;
}
/**
- * Return a synthetic identifier.
+ * 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)?
*/
- SimpleIdentifier _createSyntheticIdentifier({bool isDeclaration: false}) {
- Token syntheticToken;
- if (_currentToken.type == TokenType.KEYWORD) {
- // Consider current keyword token as an identifier.
- // It is not always true, e.g. "^is T" where "^" is place the place for
- // synthetic identifier. By creating SyntheticStringToken we can
- // distinguish a real identifier from synthetic. In the code completion
- // behavior will depend on a cursor position - before or on "is".
- syntheticToken = _injectToken(new SyntheticStringToken(
- TokenType.IDENTIFIER, _currentToken.lexeme, _currentToken.offset));
- } else {
- syntheticToken = _createSyntheticToken(TokenType.IDENTIFIER);
+ Statement parseIfStatement() {
+ Token ifKeyword = getAndAdvance();
+ Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+ Expression condition = parseExpression2();
+ Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ Statement thenStatement = parseStatement2();
+ Token elseKeyword = null;
+ Statement elseStatement = null;
+ if (_matchesKeyword(Keyword.ELSE)) {
+ elseKeyword = getAndAdvance();
+ elseStatement = parseStatement2();
}
- return new SimpleIdentifier(syntheticToken, isDeclaration: isDeclaration);
+ return new IfStatement(ifKeyword, leftParenthesis, condition,
+ rightParenthesis, thenStatement, elseKeyword, elseStatement);
}
/**
- * Return a synthetic token representing the given [keyword].
- */
- Token _createSyntheticKeyword(Keyword keyword) => _injectToken(
- new Parser_SyntheticKeywordToken(keyword, _currentToken.offset));
-
- /**
- * Return a synthetic string literal.
- */
- SimpleStringLiteral _createSyntheticStringLiteral() =>
- new SimpleStringLiteral(_createSyntheticToken(TokenType.STRING), "");
-
- /**
- * Return a synthetic token with the given [type].
- */
- Token _createSyntheticToken(TokenType type) =>
- _injectToken(new StringToken(type, "", _currentToken.offset));
-
- /**
- * Create and return a new token with the given [type]. The token will replace
- * the first portion of the given [token], so it will have the same offset and
- * will have any comments that might have preceeded the token.
+ * 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)*
*/
- Token _createToken(Token token, TokenType type, {bool isBegin: false}) {
- CommentToken comments = token.precedingComments;
- if (comments == null) {
- if (isBegin) {
- return new BeginToken(type, token.offset);
- }
- return new Token(type, token.offset);
- } else if (isBegin) {
- return new BeginTokenWithComment(type, token.offset, comments);
+ ImplementsClause parseImplementsClause() {
+ Token keyword = getAndAdvance();
+ List<TypeName> interfaces = <TypeName>[];
+ interfaces.add(parseTypeName(false));
+ while (_optional(TokenType.COMMA)) {
+ interfaces.add(parseTypeName(false));
}
- return new TokenWithComment(type, token.offset, comments);
+ return new ImplementsClause(keyword, interfaces);
}
/**
- * Check that the given [expression] is assignable and report an error if it
- * isn't.
- *
- * assignableExpression ::=
- * primary (arguments* assignableSelector)+
- * | 'super' unconditionalAssignableSelector
- * | identifier
+ * Parse a label. Return the label that was parsed.
*
- * unconditionalAssignableSelector ::=
- * '[' expression ']'
- * | '.' identifier
+ * This method assumes that the current token matches an identifier and that
+ * the following token matches `TokenType.COLON`.
*
- * assignableSelector ::=
- * unconditionalAssignableSelector
- * | '?.' identifier
+ * label ::=
+ * identifier ':'
*/
- void _ensureAssignable(Expression expression) {
- if (expression != null && !expression.isAssignable) {
- _reportErrorForCurrentToken(
- ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE);
- }
+ Label parseLabel({bool isDeclaration: false}) {
+ SimpleIdentifier label =
+ _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration);
+ Token colon = getAndAdvance();
+ return new Label(label, colon);
}
/**
- * If the current token has the expected type, return it after advancing to
- * the next token. Otherwise report an error and return the current token
- * without advancing.
- *
- * Note that the method [_expectGt] should be used if the argument to this
- * method would be [TokenType.GT].
+ * Parse a library identifier. Return the library identifier that was parsed.
*
- * The [type] is the type of token that is expected.
- */
- Token _expect(TokenType type) {
- if (_matches(type)) {
- return getAndAdvance();
- }
- // Remove uses of this method in favor of matches?
- // Pass in the error code to use to report the error?
- if (type == TokenType.SEMICOLON) {
- if (_tokenMatches(_currentToken.next, TokenType.SEMICOLON)) {
- _reportErrorForCurrentToken(
- ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
- _advance();
- return getAndAdvance();
- }
- _reportErrorForToken(ParserErrorCode.EXPECTED_TOKEN,
- _currentToken.previous, [type.lexeme]);
- return _createSyntheticToken(TokenType.SEMICOLON);
+ * libraryIdentifier ::=
+ * identifier ('.' identifier)*
+ */
+ LibraryIdentifier parseLibraryIdentifier() {
+ List<SimpleIdentifier> components = <SimpleIdentifier>[];
+ components.add(parseSimpleIdentifier());
+ while (_optional(TokenType.PERIOD)) {
+ components.add(parseSimpleIdentifier());
}
- _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TOKEN, [type.lexeme]);
- return _createSyntheticToken(type);
+ return new LibraryIdentifier(components);
}
/**
- * If the current token has the type [TokenType.GT], return it after advancing
- * to the next token. Otherwise report an error and create a synthetic token.
+ * Parse a list or map literal. The [modifier] is the 'const' modifier
+ * appearing before the literal, or `null` if there is no modifier. Return the
+ * list or map literal that was parsed.
+ *
+ * listOrMapLiteral ::=
+ * listLiteral
+ * | mapLiteral
*/
- Token _expectGt() {
- if (_matchesGt()) {
- return getAndAdvance();
+ TypedLiteral parseListOrMapLiteral(Token modifier) {
+ TypeArgumentList typeArguments = _parseOptionalTypeArguments();
+ if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+ return _parseMapLiteral(modifier, typeArguments);
+ } else if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
+ _matches(TokenType.INDEX)) {
+ return _parseListLiteral(modifier, typeArguments);
}
- _reportErrorForCurrentToken(
- ParserErrorCode.EXPECTED_TOKEN, [TokenType.GT.lexeme]);
- return _createSyntheticToken(TokenType.GT);
+ _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL);
+ return new ListLiteral(
+ modifier,
+ typeArguments,
+ _createSyntheticToken(TokenType.OPEN_SQUARE_BRACKET),
+ null,
+ _createSyntheticToken(TokenType.CLOSE_SQUARE_BRACKET));
}
/**
- * If the current token is a keyword matching the given [keyword], return it
- * after advancing to the next token. Otherwise report an error and return the
- * current token without advancing.
+ * Parse a logical or expression. Return the logical or expression that was
+ * parsed.
+ *
+ * logicalOrExpression ::=
+ * logicalAndExpression ('||' logicalAndExpression)*
*/
- Token _expectKeyword(Keyword keyword) {
- if (_matchesKeyword(keyword)) {
- return getAndAdvance();
+ Expression parseLogicalOrExpression() {
+ Expression expression = _parseLogicalAndExpression();
+ while (_currentToken.type == TokenType.BAR_BAR) {
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), _parseLogicalAndExpression());
}
- // Remove uses of this method in favor of matches?
- // Pass in the error code to use to report the error?
- _reportErrorForCurrentToken(
- ParserErrorCode.EXPECTED_TOKEN, [keyword.syntax]);
- return _currentToken;
+ return expression;
}
/**
- * Search the given list of [ranges] for a range that contains the given
- * [index]. Return the range that was found, or `null` if none of the ranges
- * contain the index.
+ * Parse a map literal entry. Return the map literal entry that was parsed.
+ *
+ * mapLiteralEntry ::=
+ * expression ':' expression
*/
- List<int> _findRange(List<List<int>> ranges, int index) {
- int rangeCount = ranges.length;
- for (int i = 0; i < rangeCount; i++) {
- List<int> range = ranges[i];
- if (range[0] <= index && index <= range[1]) {
- return range;
- } else if (index < range[0]) {
- return null;
- }
- }
- return null;
+ MapLiteralEntry parseMapLiteralEntry() {
+ Expression key = parseExpression2();
+ Token separator = _expect(TokenType.COLON);
+ Expression value = parseExpression2();
+ return new MapLiteralEntry(key, separator, value);
}
/**
- * Return a list of the ranges of characters in the given [comment] that
- * should be treated as code blocks.
+ * Parse a multiplicative expression. Return the multiplicative expression
+ * that was parsed.
+ *
+ * multiplicativeExpression ::=
+ * unaryExpression (multiplicativeOperator unaryExpression)*
+ * | 'super' (multiplicativeOperator unaryExpression)+
*/
- List<List<int>> _getCodeBlockRanges(String comment) {
- List<List<int>> ranges = <List<int>>[];
- int length = comment.length;
- if (length < 3) {
- return ranges;
- }
- int index = 0;
- int firstChar = comment.codeUnitAt(0);
- if (firstChar == 0x2F) {
- int secondChar = comment.codeUnitAt(1);
- int thirdChar = comment.codeUnitAt(2);
- if ((secondChar == 0x2A && thirdChar == 0x2A) ||
- (secondChar == 0x2F && thirdChar == 0x2F)) {
- index = 3;
- }
- }
- if (StringUtilities.startsWith4(comment, index, 0x20, 0x20, 0x20, 0x20)) {
- int end = index + 4;
- while (end < length &&
- comment.codeUnitAt(end) != 0xD &&
- comment.codeUnitAt(end) != 0xA) {
- end = end + 1;
- }
- ranges.add(<int>[index, end]);
- index = end;
+ Expression parseMultiplicativeExpression() {
+ Expression expression;
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type.isMultiplicativeOperator) {
+ expression = new SuperExpression(getAndAdvance());
+ } else {
+ expression = parseUnaryExpression();
}
- while (index < length) {
- int currentChar = comment.codeUnitAt(index);
- if (currentChar == 0xD || currentChar == 0xA) {
- index = index + 1;
- while (index < length &&
- Character.isWhitespace(comment.codeUnitAt(index))) {
- index = index + 1;
- }
- if (StringUtilities.startsWith6(
- comment, index, 0x2A, 0x20, 0x20, 0x20, 0x20, 0x20)) {
- int end = index + 6;
- while (end < length &&
- comment.codeUnitAt(end) != 0xD &&
- comment.codeUnitAt(end) != 0xA) {
- end = end + 1;
- }
- ranges.add(<int>[index, end]);
- index = end;
- }
- } else if (index + 1 < length &&
- currentChar == 0x5B &&
- comment.codeUnitAt(index + 1) == 0x3A) {
- int end = StringUtilities.indexOf2(comment, index + 2, 0x3A, 0x5D);
- if (end < 0) {
- end = length;
- }
- ranges.add(<int>[index, end]);
- index = end + 1;
- } else {
- index = index + 1;
- }
+ while (_currentToken.type.isMultiplicativeOperator) {
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), parseUnaryExpression());
}
- return ranges;
+ return expression;
}
/**
- * Return the end token associated with the given [beginToken], or `null` if
- * either the given token is not a begin token or it does not have an end
- * token associated with it.
+ * Parse a normal formal parameter. Return the normal formal parameter that
+ * was parsed.
+ *
+ * normalFormalParameter ::=
+ * functionSignature
+ * | fieldFormalParameter
+ * | simpleFormalParameter
+ *
+ * functionSignature:
+ * metadata returnType? identifier typeParameters? formalParameterList
+ *
+ * fieldFormalParameter ::=
+ * metadata finalConstVarOrType? 'this' '.' identifier
+ *
+ * simpleFormalParameter ::=
+ * declaredIdentifier
+ * | metadata identifier
*/
- Token _getEndToken(Token beginToken) {
- if (beginToken is BeginToken) {
- return beginToken.endToken;
+ NormalFormalParameter parseNormalFormalParameter() {
+ CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
+ FinalConstVarOrType holder = parseFinalConstVarOrType(true);
+ Token thisKeyword = null;
+ Token period = null;
+ if (_matchesKeyword(Keyword.THIS)) {
+ thisKeyword = getAndAdvance();
+ period = _expect(TokenType.PERIOD);
}
- return null;
- }
-
- bool _injectGenericComment(TokenType type, int prefixLen) {
- if (parseGenericMethodComments) {
- CommentToken t = _currentToken.precedingComments;
- for (; t != null; t = t.next) {
- if (t.type == type) {
- String comment = t.lexeme.substring(prefixLen, t.lexeme.length - 2);
- Token list = _scanGenericMethodComment(comment, t.offset + prefixLen);
- if (list != null) {
- // Remove the token from the comment stream.
- t.remove();
- // Insert the tokens into the stream.
- _injectTokenList(list);
- return true;
- }
+ SimpleIdentifier identifier = parseSimpleIdentifier();
+ TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
+ if (_matches(TokenType.OPEN_PAREN)) {
+ FormalParameterList parameters = _parseFormalParameterListUnchecked();
+ if (thisKeyword == null) {
+ if (holder.keyword != null) {
+ _reportErrorForToken(
+ ParserErrorCode.FUNCTION_TYPED_PARAMETER_VAR, holder.keyword);
+ }
+ Token question = null;
+ if (enableNnbd && _matches(TokenType.QUESTION)) {
+ question = getAndAdvance();
}
+ return new FunctionTypedFormalParameter(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ holder.type,
+ new SimpleIdentifier(identifier.token, isDeclaration: true),
+ typeParameters,
+ parameters,
+ question: question);
+ } else {
+ return new FieldFormalParameter(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ holder.keyword,
+ holder.type,
+ thisKeyword,
+ period,
+ identifier,
+ typeParameters,
+ parameters);
+ }
+ } else if (typeParameters != null) {
+ // TODO(brianwilkerson) Report an error. It looks like a function-typed
+ // parameter with no parameter list.
+ //_reportErrorForToken(ParserErrorCode.MISSING_PARAMETERS, typeParameters.endToken);
+ }
+ TypeName type = holder.type;
+ if (type != null) {
+ if (_tokenMatchesKeyword(type.name.beginToken, Keyword.VOID)) {
+ _reportErrorForToken(
+ ParserErrorCode.VOID_PARAMETER, type.name.beginToken);
+ } else if (holder.keyword != null &&
+ _tokenMatchesKeyword(holder.keyword, Keyword.VAR)) {
+ _reportErrorForToken(ParserErrorCode.VAR_AND_TYPE, holder.keyword);
}
}
- return false;
+ if (thisKeyword != null) {
+ // TODO(brianwilkerson) If there are type parameters but no parameters,
+ // should we create a synthetic empty parameter list here so we can
+ // capture the type parameters?
+ return new FieldFormalParameter(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ holder.keyword,
+ holder.type,
+ thisKeyword,
+ period,
+ identifier,
+ null,
+ null);
+ }
+ return new SimpleFormalParameter(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ holder.keyword,
+ holder.type,
+ new SimpleIdentifier(identifier.token, isDeclaration: true));
}
/**
- * Matches a generic comment type substitution and injects it into the token
- * stream. Returns true if a match was injected, otherwise false.
+ * Parse an operator declaration. 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. Return the operator declaration that was parsed.
*
- * These comments are of the form `/*=T*/`, in other words, a [TypeName]
- * inside a slash-star comment, preceded by equals sign.
- */
- bool _injectGenericCommentTypeAssign() {
- return _injectGenericComment(TokenType.GENERIC_METHOD_TYPE_ASSIGN, 3);
- }
-
- /**
- * Matches a generic comment type parameters and injects them into the token
- * stream. Returns true if a match was injected, otherwise false.
+ * operatorDeclaration ::=
+ * operatorSignature (';' | functionBody)
*
- * These comments are of the form `/*<K, V>*/`, in other words, a
- * [TypeParameterList] or [TypeArgumentList] inside a slash-star comment.
- */
- bool _injectGenericCommentTypeList() {
- return _injectGenericComment(TokenType.GENERIC_METHOD_TYPE_LIST, 2);
- }
-
- /**
- * Inject the given [token] into the token stream immediately before the
- * current token.
+ * operatorSignature ::=
+ * 'external'? returnType? 'operator' operator formalParameterList
*/
- Token _injectToken(Token token) {
- Token previous = _currentToken.previous;
- token.setNext(_currentToken);
- previous.setNext(token);
- return token;
- }
-
- void _injectTokenList(Token firstToken) {
- // Scanner creates a cyclic EOF token.
- Token lastToken = firstToken;
- while (lastToken.next.type != TokenType.EOF) {
- lastToken = lastToken.next;
+ MethodDeclaration parseOperator(CommentAndMetadata commentAndMetadata,
+ Token externalKeyword, TypeName returnType) {
+ Token operatorKeyword;
+ if (_matchesKeyword(Keyword.OPERATOR)) {
+ operatorKeyword = getAndAdvance();
+ } else {
+ _reportErrorForToken(
+ ParserErrorCode.MISSING_KEYWORD_OPERATOR, _currentToken);
+ operatorKeyword = _createSyntheticKeyword(Keyword.OPERATOR);
}
- // Inject these new tokens into the stream.
- Token previous = _currentToken.previous;
- lastToken.setNext(_currentToken);
- previous.setNext(firstToken);
- _currentToken = firstToken;
+ return _parseOperatorAfterKeyword(
+ commentAndMetadata, externalKeyword, returnType, operatorKeyword);
}
/**
- * Return `true` if the current token could be the question mark in a
- * condition expression. The current token is assumed to be a question mark.
+ * Parse a prefixed identifier. Return the prefixed identifier that was
+ * parsed.
+ *
+ * prefixedIdentifier ::=
+ * identifier ('.' identifier)?
*/
- bool _isConditionalOperator() {
- void parseOperation(Parser parser) {
- parser.parseExpressionWithoutCascade();
- }
-
- Token token = _skip(_currentToken.next, parseOperation);
- if (token == null || !_tokenMatches(token, TokenType.COLON)) {
- return false;
- }
- token = _skip(token.next, parseOperation);
- return token != null;
+ Identifier parsePrefixedIdentifier() {
+ return _parsePrefixedIdentifierAfterIdentifier(parseSimpleIdentifier());
}
/**
- * Return `true` if the current token appears to be the beginning of a
- * function declaration.
+ * Parse a primary expression. Return the primary expression that was parsed.
+ *
+ * primary ::=
+ * thisExpression
+ * | 'super' unconditionalAssignableSelector
+ * | functionExpression
+ * | literal
+ * | identifier
+ * | newExpression
+ * | constObjectExpression
+ * | '(' expression ')'
+ * | argumentDefinitionTest
+ *
+ * literal ::=
+ * nullLiteral
+ * | booleanLiteral
+ * | numericLiteral
+ * | stringLiteral
+ * | symbolLiteral
+ * | mapLiteral
+ * | listLiteral
*/
- bool _isFunctionDeclaration() {
- Keyword keyword = _currentToken.keyword;
- if (keyword == Keyword.VOID) {
- return true;
- }
- Token afterReturnType = _skipTypeName(_currentToken);
- if (afterReturnType == null) {
- // There was no return type, but it is optional, so go back to where we
- // started.
- afterReturnType = _currentToken;
- }
- Token afterIdentifier = _skipSimpleIdentifier(afterReturnType);
- if (afterIdentifier == null) {
- // It's possible that we parsed the function name as if it were a type
- // name, so see whether it makes sense if we assume that there is no type.
- afterIdentifier = _skipSimpleIdentifier(_currentToken);
+ Expression parsePrimaryExpression() {
+ 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();
}
- if (afterIdentifier == null) {
- return false;
+ 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);
}
- if (_isFunctionExpression(afterIdentifier)) {
- return true;
+ 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 (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 (keyword == Keyword.FALSE) {
+ return new BooleanLiteral(getAndAdvance(), false);
+ } else if (keyword == Keyword.TRUE) {
+ return new BooleanLiteral(getAndAdvance(), true);
}
- // 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 (keyword == Keyword.GET) {
- Token afterName = _skipSimpleIdentifier(_currentToken.next);
- if (afterName == null) {
- return false;
+ if (type == TokenType.DOUBLE) {
+ Token token = getAndAdvance();
+ double value = 0.0;
+ try {
+ value = double.parse(token.lexeme);
+ } on FormatException {
+ // The invalid format should have been reported by the scanner.
}
- 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 new DoubleLiteral(token, value);
+ } else if (type == TokenType.HEXADECIMAL) {
+ Token token = getAndAdvance();
+ int value = null;
+ try {
+ value = int.parse(token.lexeme.substring(2), radix: 16);
+ } on FormatException {
+ // The invalid format should have been reported by the scanner.
}
- TokenType type = afterName.type;
- return type == TokenType.FUNCTION || type == TokenType.OPEN_CURLY_BRACKET;
- }
- return false;
- }
-
- /**
- * Return `true` if the given [token] appears to be the beginning of a
- * function expression.
- */
- bool _isFunctionExpression(Token token) {
- // Function expressions aren't allowed in initializer lists.
- if (_inInitializer) {
- return false;
- }
- Token afterTypeParameters = _skipTypeParameterList(token);
- if (afterTypeParameters == null) {
- afterTypeParameters = token;
- }
- Token afterParameters = _skipFormalParameterList(afterTypeParameters);
- if (afterParameters == null) {
- return false;
- }
- if (afterParameters.matchesAny(
- const <TokenType>[TokenType.OPEN_CURLY_BRACKET, TokenType.FUNCTION])) {
- return true;
+ return new IntegerLiteral(token, value);
+ } else if (keyword == Keyword.CONST) {
+ return parseConstExpression();
+ } else if (type == TokenType.OPEN_PAREN) {
+ if (isFunctionExpression(_currentToken)) {
+ return parseFunctionExpression();
+ }
+ Token leftParenthesis = getAndAdvance();
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = false;
+ try {
+ Expression expression = parseExpression2();
+ Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ return new ParenthesizedExpression(
+ leftParenthesis, expression, rightParenthesis);
+ } finally {
+ _inInitializer = wasInInitializer;
+ }
+ } else if (type == TokenType.LT || _injectGenericCommentTypeList()) {
+ return parseListOrMapLiteral(null);
+ } else if (type == TokenType.OPEN_CURLY_BRACKET) {
+ return _parseMapLiteral(null, null);
+ } else if (type == TokenType.OPEN_SQUARE_BRACKET ||
+ type == TokenType.INDEX) {
+ return _parseListLiteral(null, null);
+ } else if (type == TokenType.QUESTION &&
+ _tokenMatches(_peek(), TokenType.IDENTIFIER)) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
+ _advance();
+ return parsePrimaryExpression();
+ } else if (keyword == Keyword.VOID) {
+ //
+ // Recover from having a return type of "void" where a return type is not
+ // expected.
+ //
+ // TODO(brianwilkerson) Improve this error message.
+ _reportErrorForCurrentToken(
+ ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
+ _advance();
+ return parsePrimaryExpression();
+ } else if (type == TokenType.HASH) {
+ return parseSymbolLiteral();
+ } else {
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+ return createSyntheticIdentifier();
}
- String lexeme = afterParameters.lexeme;
- return lexeme == ASYNC || lexeme == SYNC;
}
/**
- * Return `true` if the given [character] is a valid hexadecimal digit.
+ * Parse a relational expression. Return the relational expression that was
+ * parsed.
+ *
+ * relationalExpression ::=
+ * bitwiseOrExpression ('is' '!'? type | 'as' type | relationalOperator bitwiseOrExpression)?
+ * | 'super' relationalOperator bitwiseOrExpression
*/
- bool _isHexDigit(int character) =>
- (0x30 <= character && character <= 0x39) ||
- (0x41 <= character && character <= 0x46) ||
- (0x61 <= character && character <= 0x66);
+ Expression parseRelationalExpression() {
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type.isRelationalOperator) {
+ Expression expression = new SuperExpression(getAndAdvance());
+ Token operator = getAndAdvance();
+ return new BinaryExpression(
+ expression, operator, parseBitwiseOrExpression());
+ }
+ Expression expression = parseBitwiseOrExpression();
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.AS) {
+ Token asOperator = getAndAdvance();
+ return new AsExpression(expression, asOperator, parseTypeName(true));
+ } else if (keyword == Keyword.IS) {
+ Token isOperator = getAndAdvance();
+ Token notOperator = null;
+ if (_matches(TokenType.BANG)) {
+ notOperator = getAndAdvance();
+ }
+ return new IsExpression(
+ expression, isOperator, notOperator, parseTypeName(true));
+ } else if (_currentToken.type.isRelationalOperator) {
+ Token operator = getAndAdvance();
+ return new BinaryExpression(
+ expression, operator, parseBitwiseOrExpression());
+ }
+ return expression;
+ }
/**
- * Return `true` if the current token is the first token in an initialized
- * variable declaration rather than an expression. This method assumes that we
- * have already skipped past any metadata that might be associated with the
- * declaration.
- *
- * initializedVariableDeclaration ::=
- * declaredIdentifier ('=' expression)? (',' initializedIdentifier)*
+ * Parse a rethrow expression. Return the rethrow expression that was parsed.
*
- * declaredIdentifier ::=
- * metadata finalConstVarOrType identifier
+ * This method assumes that the current token matches `Keyword.RETHROW`.
*
- * finalConstVarOrType ::=
- * 'final' type?
- * | 'const' type?
- * | 'var'
- * | type
+ * rethrowExpression ::=
+ * 'rethrow'
+ */
+ Expression parseRethrowExpression() => new RethrowExpression(getAndAdvance());
+
+ /**
+ * Parse a return statement. Return the return statement that was parsed.
*
- * type ::=
- * qualified typeArguments?
+ * This method assumes that the current token matches `Keyword.RETURN`.
*
- * initializedIdentifier ::=
- * identifier ('=' expression)?
+ * returnStatement ::=
+ * 'return' expression? ';'
*/
- bool _isInitializedVariableDeclaration() {
- 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 (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>[
- TokenType.LT,
- TokenType.OPEN_CURLY_BRACKET,
- TokenType.OPEN_SQUARE_BRACKET,
- TokenType.INDEX
- ]);
- }
- bool allowAdditionalTokens = true;
- // We know that we have an identifier, and need to see whether it might be
- // a type name.
- if (_currentToken.type != TokenType.IDENTIFIER) {
- allowAdditionalTokens = false;
- }
- Token token = _skipTypeName(_currentToken);
- if (token == null) {
- // There was no type name, so this can't be a declaration.
- return false;
- }
- if (token.type != TokenType.IDENTIFIER) {
- allowAdditionalTokens = false;
- }
- token = _skipSimpleIdentifier(token);
- if (token == null) {
- return false;
- }
- TokenType type = token.type;
- // Usual cases in valid code:
- // String v = '';
- // String v, v2;
- // String v;
- // for (String item in items) {}
- if (type == TokenType.EQ ||
- type == TokenType.COMMA ||
- type == TokenType.SEMICOLON ||
- token.keyword == Keyword.IN) {
- return true;
- }
- // It is OK to parse as a variable declaration in these cases:
- // String v }
- // String v if (true) print('OK');
- // String v { print(42); }
- // ...but not in these cases:
- // get getterName {
- // String get getterName
- if (allowAdditionalTokens) {
- if (type == TokenType.CLOSE_CURLY_BRACKET ||
- type == TokenType.KEYWORD ||
- type == TokenType.IDENTIFIER ||
- type == TokenType.OPEN_CURLY_BRACKET) {
- return true;
- }
+ Statement parseReturnStatement() {
+ Token returnKeyword = getAndAdvance();
+ if (_matches(TokenType.SEMICOLON)) {
+ return new ReturnStatement(returnKeyword, null, getAndAdvance());
}
- return false;
+ Expression expression = parseExpression2();
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new ReturnStatement(returnKeyword, expression, semicolon);
}
- bool _isLikelyArgumentList() {
- // Try to reduce the amount of lookahead required here before enabling
- // generic methods.
- if (_matches(TokenType.OPEN_PAREN)) {
- return true;
- }
- if (!parseGenericMethods) {
- return false;
+ /**
+ * Parse a return type. Return the return type that was parsed.
+ *
+ * returnType ::=
+ * 'void'
+ * | type
+ */
+ TypeName parseReturnType() {
+ if (_currentToken.keyword == Keyword.VOID) {
+ return new TypeName(new SimpleIdentifier(getAndAdvance()), null);
+ } else {
+ return parseTypeName(false);
}
- Token token = _skipTypeArgumentList(_currentToken);
- return token != null && _tokenMatches(token, TokenType.OPEN_PAREN);
}
/**
- * Given that we have just found bracketed text within the given [comment],
- * look to see whether that text is (a) followed by a parenthesized link
- * address, (b) followed by a colon, or (c) followed by optional whitespace
- * and another square bracket. The [rightIndex] is the index of the right
- * bracket. Return `true` if the bracketed text is followed by a link address.
+ * Parse a shift expression. Return the shift expression that was parsed.
*
- * This method uses the syntax described by the
- * <a href="http://daringfireball.net/projects/markdown/syntax">markdown</a>
- * project.
+ * shiftExpression ::=
+ * additiveExpression (shiftOperator additiveExpression)*
+ * | 'super' (shiftOperator additiveExpression)+
*/
- bool _isLinkText(String comment, int rightIndex) {
- int length = comment.length;
- int index = rightIndex + 1;
- if (index >= length) {
- return false;
- }
- int nextChar = comment.codeUnitAt(index);
- if (nextChar == 0x28 || nextChar == 0x3A) {
- return true;
+ Expression parseShiftExpression() {
+ Expression expression;
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type.isShiftOperator) {
+ expression = new SuperExpression(getAndAdvance());
+ } else {
+ expression = parseAdditiveExpression();
}
- while (Character.isWhitespace(nextChar)) {
- index = index + 1;
- if (index >= length) {
- return false;
- }
- nextChar = comment.codeUnitAt(index);
+ while (_currentToken.type.isShiftOperator) {
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), parseAdditiveExpression());
}
- return nextChar == 0x5B;
+ return expression;
}
/**
- * Return `true` if the given [startToken] appears to be the beginning of an
- * operator declaration.
+ * Parse a simple identifier. Return the simple identifier that was parsed.
+ *
+ * identifier ::=
+ * IDENTIFIER
*/
- bool _isOperator(Token startToken) {
- // Accept any operator here, even if it is not user definable.
- if (!startToken.isOperator) {
- return false;
- }
- // Token "=" means that it is actually a field initializer.
- if (startToken.type == TokenType.EQ) {
- return false;
- }
- // Consume all operator tokens.
- Token token = startToken.next;
- while (token.isOperator) {
- token = token.next;
+ SimpleIdentifier parseSimpleIdentifier({bool isDeclaration: false}) {
+ if (_matchesIdentifier()) {
+ return _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration);
}
- // Formal parameter list is expect now.
- return _tokenMatches(token, TokenType.OPEN_PAREN);
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+ return createSyntheticIdentifier(isDeclaration: isDeclaration);
}
- bool _isPeekGenericTypeParametersAndOpenParen() {
- if (!parseGenericMethods) {
- return false;
- }
- Token token = _skipTypeParameterList(_peek());
- return token != null && _tokenMatches(token, TokenType.OPEN_PAREN);
+ /**
+ * Parse a statement, starting with the given [token]. Return the statement
+ * that was parsed, or `null` if the tokens do not represent a recognizable
+ * statement.
+ */
+ Statement parseStatement(Token token) {
+ _currentToken = token;
+ return parseStatement2();
}
/**
- * Return `true` if the current token appears to be the beginning of a switch
- * member.
+ * Parse a statement. Return the statement that was parsed.
+ *
+ * statement ::=
+ * label* nonLabeledStatement
*/
- bool _isSwitchMember() {
- Token token = _currentToken;
- while (_tokenMatches(token, TokenType.IDENTIFIER) &&
- _tokenMatches(token.next, TokenType.COLON)) {
- token = token.next.next;
+ Statement parseStatement2() {
+ List<Label> labels = null;
+ while (_matchesIdentifier() && _currentToken.next.type == TokenType.COLON) {
+ Label label = parseLabel(isDeclaration: true);
+ if (labels == null) {
+ labels = <Label>[label];
+ } else {
+ labels.add(label);
+ }
+ }
+ Statement statement = _parseNonLabeledStatement();
+ if (labels == null) {
+ return statement;
}
- Keyword keyword = token.keyword;
- return keyword == Keyword.CASE || keyword == Keyword.DEFAULT;
+ return new LabeledStatement(labels, statement);
}
/**
- * Return `true` if the [startToken] appears to be the first token of a type
- * name that is followed by a variable or field formal parameter.
+ * Parse a sequence of statements, starting with the given [token]. Return the
+ * statements that were parsed, or `null` if the tokens do not represent a
+ * recognizable sequence of statements.
*/
- bool _isTypedIdentifier(Token startToken) {
- Token token = _skipReturnType(startToken);
- if (token == null) {
- return false;
- } else if (_tokenMatchesIdentifier(token)) {
- return true;
- } else if (_tokenMatchesKeyword(token, Keyword.THIS) &&
- _tokenMatches(token.next, TokenType.PERIOD) &&
- _tokenMatchesIdentifier(token.next.next)) {
- return true;
- } else if (_tokenMatchesKeyword(startToken, Keyword.VOID)) {
- // The keyword 'void' isn't a valid identifier, so it should be assumed to
- // be a type name.
- return true;
- } else if (startToken.next != token &&
- !_tokenMatches(token, TokenType.OPEN_PAREN)) {
- // The type is more than a simple identifier, so it should be assumed to
- // be a type name.
- return true;
- }
- return false;
+ List<Statement> parseStatements(Token token) {
+ _currentToken = token;
+ return _parseStatementList();
}
/**
- * Increments the error reporting lock level. If level is more than `0`, then
- * [reportError] wont report any error.
+ * Parse a string literal. Return the string literal that was parsed.
+ *
+ * stringLiteral ::=
+ * MULTI_LINE_STRING+
+ * | SINGLE_LINE_STRING+
*/
- void _lockErrorListener() {
- _errorListenerLock++;
+ StringLiteral parseStringLiteral() {
+ if (_matches(TokenType.STRING)) {
+ return _parseStringLiteralUnchecked();
+ }
+ _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_STRING_LITERAL);
+ return createSyntheticStringLiteral();
}
/**
- * Return `true` if the current token has the given [type]. Note that the
- * method [_matchesGt] should be used if the argument to this method would be
- * [TokenType.GT].
+ * 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
*/
- bool _matches(TokenType type) => _currentToken.type == type;
+ SuperConstructorInvocation parseSuperConstructorInvocation() {
+ Token keyword = getAndAdvance();
+ Token period = null;
+ SimpleIdentifier constructorName = null;
+ if (_matches(TokenType.PERIOD)) {
+ period = getAndAdvance();
+ constructorName = parseSimpleIdentifier();
+ }
+ ArgumentList argumentList = _parseArgumentListChecked();
+ return new SuperConstructorInvocation(
+ keyword, period, constructorName, argumentList);
+ }
/**
- * Return `true` if the current token has a type of [TokenType.GT]. Note that
- * this method, unlike other variants, will modify the token stream if
- * possible to match desired type. In particular, if the next token is either
- * a '>>' or '>>>', the token stream will be re-written and `true` will be
- * returned.
+ * Parse a switch statement. Return the switch statement that was parsed.
+ *
+ * switchStatement ::=
+ * 'switch' '(' expression ')' '{' switchCase* defaultCase? '}'
+ *
+ * switchCase ::=
+ * label* ('case' expression ':') statements
+ *
+ * defaultCase ::=
+ * label* 'default' ':' statements
*/
- bool _matchesGt() {
- TokenType currentType = _currentToken.type;
- if (currentType == TokenType.GT) {
- return true;
- } else if (currentType == TokenType.GT_GT) {
- Token first = _createToken(_currentToken, TokenType.GT);
- Token second = new Token(TokenType.GT, _currentToken.offset + 1);
- second.setNext(_currentToken.next);
- first.setNext(second);
- _currentToken.previous.setNext(first);
- _currentToken = first;
- return true;
- } else if (currentType == TokenType.GT_EQ) {
- Token first = _createToken(_currentToken, TokenType.GT);
- Token second = new Token(TokenType.EQ, _currentToken.offset + 1);
- second.setNext(_currentToken.next);
- first.setNext(second);
- _currentToken.previous.setNext(first);
- _currentToken = first;
- return true;
- } else if (currentType == TokenType.GT_GT_EQ) {
- int offset = _currentToken.offset;
- Token first = _createToken(_currentToken, TokenType.GT);
- Token second = new Token(TokenType.GT, offset + 1);
- Token third = new Token(TokenType.EQ, offset + 2);
- third.setNext(_currentToken.next);
- second.setNext(third);
- first.setNext(second);
- _currentToken.previous.setNext(first);
- _currentToken = first;
- return true;
+ SwitchStatement parseSwitchStatement() {
+ bool wasInSwitch = _inSwitch;
+ _inSwitch = true;
+ try {
+ HashSet<String> definedLabels = new HashSet<String>();
+ Token keyword = _expectKeyword(Keyword.SWITCH);
+ Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+ Expression expression = parseExpression2();
+ Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
+ Token defaultKeyword = null;
+ 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 =
+ _parseSimpleIdentifierUnchecked(isDeclaration: true);
+ String label = identifier.token.lexeme;
+ if (definedLabels.contains(label)) {
+ _reportErrorForToken(
+ ParserErrorCode.DUPLICATE_LABEL_IN_SWITCH_STATEMENT,
+ identifier.token,
+ [label]);
+ } else {
+ definedLabels.add(label);
+ }
+ Token colon = getAndAdvance();
+ labels.add(new Label(identifier, colon));
+ }
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.CASE) {
+ Token caseKeyword = getAndAdvance();
+ Expression caseExpression = parseExpression2();
+ Token colon = _expect(TokenType.COLON);
+ members.add(new SwitchCase(labels, caseKeyword, caseExpression, colon,
+ _parseStatementList()));
+ if (defaultKeyword != null) {
+ _reportErrorForToken(
+ ParserErrorCode.SWITCH_HAS_CASE_AFTER_DEFAULT_CASE,
+ caseKeyword);
+ }
+ } else if (keyword == Keyword.DEFAULT) {
+ if (defaultKeyword != null) {
+ _reportErrorForToken(
+ ParserErrorCode.SWITCH_HAS_MULTIPLE_DEFAULT_CASES, _peek());
+ }
+ defaultKeyword = getAndAdvance();
+ Token colon = _expect(TokenType.COLON);
+ members.add(new SwitchDefault(
+ labels, defaultKeyword, colon, _parseStatementList()));
+ } else {
+ // 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);
+ 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,
+ rightParenthesis, leftBracket, members, rightBracket);
+ } finally {
+ _inSwitch = wasInSwitch;
}
- return false;
}
/**
- * Return `true` if the current token is a valid identifier. Valid identifiers
- * include built-in identifiers (pseudo-keywords).
+ * Parse a symbol literal. Return the symbol literal that was parsed.
+ *
+ * This method assumes that the current token matches [TokenType.HASH].
+ *
+ * symbolLiteral ::=
+ * '#' identifier ('.' identifier)*
*/
- bool _matchesIdentifier() => _tokenMatchesIdentifier(_currentToken);
+ SymbolLiteral parseSymbolLiteral() {
+ Token poundSign = getAndAdvance();
+ List<Token> components = <Token>[];
+ if (_matchesIdentifier()) {
+ components.add(getAndAdvance());
+ while (_optional(TokenType.PERIOD)) {
+ if (_matchesIdentifier()) {
+ components.add(getAndAdvance());
+ } else {
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+ components.add(_createSyntheticToken(TokenType.IDENTIFIER));
+ break;
+ }
+ }
+ } else if (_currentToken.isOperator) {
+ components.add(getAndAdvance());
+ } else if (_matchesKeyword(Keyword.VOID)) {
+ components.add(getAndAdvance());
+ } else {
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+ components.add(_createSyntheticToken(TokenType.IDENTIFIER));
+ }
+ return new SymbolLiteral(poundSign, components);
+ }
/**
- * Return `true` if the current token matches the given [keyword].
+ * Parse a throw expression. Return the throw expression that was parsed.
+ *
+ * This method assumes that the current token matches [Keyword.THROW].
+ *
+ * throwExpression ::=
+ * 'throw' expression
*/
- bool _matchesKeyword(Keyword keyword) =>
- _tokenMatchesKeyword(_currentToken, keyword);
+ Expression parseThrowExpression() {
+ 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());
+ }
+ Expression expression = parseExpression2();
+ return new ThrowExpression(keyword, expression);
+ }
/**
- * Return `true` if the current token matches the given [identifier].
+ * Parse a throw expression. Return the throw expression that was parsed.
+ *
+ * This method assumes that the current token matches [Keyword.THROW].
+ *
+ * throwExpressionWithoutCascade ::=
+ * 'throw' expressionWithoutCascade
*/
- bool _matchesString(String identifier) =>
- _currentToken.type == TokenType.IDENTIFIER &&
- _currentToken.lexeme == identifier;
+ Expression parseThrowExpressionWithoutCascade() {
+ 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());
+ }
+ Expression expression = parseExpressionWithoutCascade();
+ return new ThrowExpression(keyword, expression);
+ }
/**
- * If the current token has the given [type], then advance to the next token
- * and return `true`. Otherwise, return `false` without advancing. This method
- * should not be invoked with an argument value of [TokenType.GT].
+ * 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)
+ *
+ * onPart ::=
+ * catchPart block
+ * | 'on' type catchPart? block
+ *
+ * catchPart ::=
+ * 'catch' '(' identifier (',' identifier)? ')'
+ *
+ * finallyPart ::=
+ * 'finally' block
*/
- bool _optional(TokenType type) {
- if (_currentToken.type == type) {
- _advance();
- return true;
+ Statement parseTryStatement() {
+ Token tryKeyword = getAndAdvance();
+ Block body = _parseBlockChecked();
+ List<CatchClause> catchClauses = <CatchClause>[];
+ Block finallyClause = null;
+ while (_matchesString(_ON) || _matchesKeyword(Keyword.CATCH)) {
+ Token onKeyword = null;
+ TypeName exceptionType = null;
+ if (_matchesString(_ON)) {
+ onKeyword = getAndAdvance();
+ exceptionType = parseTypeName(false);
+ }
+ Token catchKeyword = null;
+ Token leftParenthesis = null;
+ SimpleIdentifier exceptionParameter = null;
+ Token comma = null;
+ SimpleIdentifier stackTraceParameter = null;
+ Token rightParenthesis = null;
+ if (_matchesKeyword(Keyword.CATCH)) {
+ catchKeyword = getAndAdvance();
+ leftParenthesis = _expect(TokenType.OPEN_PAREN);
+ exceptionParameter = parseSimpleIdentifier(isDeclaration: true);
+ if (_matches(TokenType.COMMA)) {
+ comma = getAndAdvance();
+ stackTraceParameter = parseSimpleIdentifier(isDeclaration: true);
+ }
+ rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ }
+ Block catchBody = _parseBlockChecked();
+ catchClauses.add(new CatchClause(
+ onKeyword,
+ exceptionType,
+ catchKeyword,
+ leftParenthesis,
+ exceptionParameter,
+ comma,
+ stackTraceParameter,
+ rightParenthesis,
+ catchBody));
}
- return false;
+ Token finallyKeyword = null;
+ if (_matchesKeyword(Keyword.FINALLY)) {
+ finallyKeyword = getAndAdvance();
+ finallyClause = _parseBlockChecked();
+ } else if (catchClauses.isEmpty) {
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_CATCH_OR_FINALLY);
+ }
+ return new TryStatement(
+ tryKeyword, body, catchClauses, finallyKeyword, finallyClause);
}
/**
- * Parse an additive expression. Return the additive expression that was
+ * Parse a list of type arguments. Return the type argument list that was
* parsed.
*
- * additiveExpression ::=
- * multiplicativeExpression (additiveOperator multiplicativeExpression)*
- * | 'super' (additiveOperator multiplicativeExpression)+
+ * This method assumes that the current token matches `TokenType.LT`.
+ *
+ * typeArguments ::=
+ * '<' typeList '>'
+ *
+ * typeList ::=
+ * type (',' type)*
*/
- Expression _parseAdditiveExpression() {
- Expression expression;
- if (_currentToken.keyword == Keyword.SUPER &&
- _currentToken.next.type.isAdditiveOperator) {
- expression = new SuperExpression(getAndAdvance());
- } else {
- expression = _parseMultiplicativeExpression();
- }
- while (_currentToken.type.isAdditiveOperator) {
- expression = new BinaryExpression(
- expression, getAndAdvance(), _parseMultiplicativeExpression());
+ TypeArgumentList parseTypeArgumentList() {
+ Token leftBracket = getAndAdvance();
+ List<TypeName> arguments = <TypeName>[parseTypeName(false)];
+ while (_optional(TokenType.COMMA)) {
+ arguments.add(parseTypeName(false));
}
- return expression;
+ Token rightBracket = _expectGt();
+ return new TypeArgumentList(leftBracket, arguments, rightBracket);
}
/**
- * 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.
+ * Parse a type name. Return the type name that was parsed.
+ *
+ * type ::=
+ * qualified typeArguments?
*/
- 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));
+ TypeName parseTypeName(bool inExpression) {
+ TypeName realType = _parseTypeName(inExpression);
+ // 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 ?? realType;
}
/**
- * Parse an assert within a constructor's initializer list. Return the assert.
- *
- * This method assumes that the current token matches `Keyword.ASSERT`.
+ * Parse a type parameter. Return the type parameter that was parsed.
*
- * assertInitializer ::=
- * 'assert' '(' expression [',' expression] ')'
+ * typeParameter ::=
+ * metadata name ('extends' bound)?
*/
- void _parseAssertInitializer() {
- // TODO(brianwilkerson) Capture the syntax in the AST using a new class,
- // such as AssertInitializer
- Token keyword = getAndAdvance();
- Token leftParen = _expect(TokenType.OPEN_PAREN);
- Expression expression = parseExpression2();
- Token comma;
- Expression message;
- if (_matches(TokenType.COMMA)) {
- comma = getAndAdvance();
- message = parseExpression2();
+ TypeParameter parseTypeParameter() {
+ CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
+ SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
+ if (_matchesKeyword(Keyword.EXTENDS)) {
+ Token keyword = getAndAdvance();
+ TypeName bound = parseTypeName(false);
+ return new TypeParameter(commentAndMetadata.comment,
+ commentAndMetadata.metadata, name, keyword, bound);
}
- Token rightParen = _expect(TokenType.CLOSE_PAREN);
-// return new AssertInitializer(
-// keyword, leftParen, expression, comma, message, rightParen);
+ return new TypeParameter(commentAndMetadata.comment,
+ commentAndMetadata.metadata, name, null, null);
}
/**
- * Parse an assert statement. Return the assert statement.
+ * Parse a list of type parameters. Return the list of type parameters that
+ * were parsed.
*
- * This method assumes that the current token matches `Keyword.ASSERT`.
+ * This method assumes that the current token matches `TokenType.LT`.
*
- * assertStatement ::=
- * 'assert' '(' expression [',' expression] ')' ';'
+ * typeParameterList ::=
+ * '<' typeParameter (',' typeParameter)* '>'
*/
- AssertStatement _parseAssertStatement() {
- Token keyword = getAndAdvance();
- Token leftParen = _expect(TokenType.OPEN_PAREN);
- Expression expression = parseExpression2();
- Token comma;
- Expression message;
- if (_matches(TokenType.COMMA)) {
- comma = getAndAdvance();
- message = parseExpression2();
+ TypeParameterList parseTypeParameterList() {
+ Token leftBracket = getAndAdvance();
+ List<TypeParameter> typeParameters = <TypeParameter>[parseTypeParameter()];
+ while (_optional(TokenType.COMMA)) {
+ typeParameters.add(parseTypeParameter());
}
- Token rightParen = _expect(TokenType.CLOSE_PAREN);
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new AssertStatement(
- keyword, leftParen, expression, comma, message, rightParen, semicolon);
+ Token rightBracket = _expectGt();
+ return new TypeParameterList(leftBracket, typeParameters, rightBracket);
}
/**
- * Parse an assignable expression. The [primaryAllowed] is `true` if the
- * expression is allowed to be a primary without any assignable selector.
- * Return the assignable expression that was parsed.
+ * Parse a unary expression. Return the unary expression that was parsed.
*
- * assignableExpression ::=
- * primary (arguments* assignableSelector)+
- * | 'super' unconditionalAssignableSelector
- * | identifier
+ * unaryExpression ::=
+ * prefixOperator unaryExpression
+ * | awaitExpression
+ * | postfixExpression
+ * | unaryOperator 'super'
+ * | '-' 'super'
+ * | incrementOperator assignableExpression
*/
- Expression _parseAssignableExpression(bool primaryAllowed) {
- if (_matchesKeyword(Keyword.SUPER)) {
- return _parseAssignableSelector(
- new SuperExpression(getAndAdvance()), false,
- allowConditional: false);
+ Expression parseUnaryExpression() {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.MINUS ||
+ type == TokenType.BANG ||
+ type == TokenType.TILDE) {
+ Token operator = getAndAdvance();
+ if (_matchesKeyword(Keyword.SUPER)) {
+ TokenType nextType = _peek().type;
+ if (nextType == TokenType.OPEN_SQUARE_BRACKET ||
+ nextType == TokenType.PERIOD) {
+ // "prefixOperator unaryExpression"
+ // --> "prefixOperator postfixExpression"
+ // --> "prefixOperator primary selector*"
+ // --> "prefixOperator 'super' assignableSelector selector*"
+ return new PrefixExpression(operator, parseUnaryExpression());
+ }
+ return new PrefixExpression(
+ operator, new SuperExpression(getAndAdvance()));
+ }
+ return new PrefixExpression(operator, parseUnaryExpression());
+ } else if (_currentToken.type.isIncrementOperator) {
+ Token operator = getAndAdvance();
+ if (_matchesKeyword(Keyword.SUPER)) {
+ TokenType nextType = _peek().type;
+ if (nextType == TokenType.OPEN_SQUARE_BRACKET ||
+ nextType == TokenType.PERIOD) {
+ // --> "prefixOperator 'super' assignableSelector selector*"
+ return new PrefixExpression(operator, parseUnaryExpression());
+ }
+ //
+ // Even though it is not valid to use an incrementing operator
+ // ('++' or '--') before 'super', we can (and therefore must) interpret
+ // "--super" as semantically equivalent to "-(-super)". Unfortunately,
+ // we cannot do the same for "++super" because "+super" is also not
+ // valid.
+ //
+ if (type == TokenType.MINUS_MINUS) {
+ Token firstOperator = _createToken(operator, TokenType.MINUS);
+ Token secondOperator =
+ new Token(TokenType.MINUS, operator.offset + 1);
+ secondOperator.setNext(_currentToken);
+ firstOperator.setNext(secondOperator);
+ operator.previous.setNext(firstOperator);
+ return new PrefixExpression(
+ firstOperator,
+ new PrefixExpression(
+ secondOperator, 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, _parseAssignableExpressionNotStartingWithSuper(false));
+ } else if (type == TokenType.PLUS) {
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+ return createSyntheticIdentifier();
+ } else if (_inAsync && _matchesString(_AWAIT)) {
+ return parseAwaitExpression();
}
- return _parseAssignableExpressionNotStartingWithSuper(primaryAllowed);
+ return _parsePostfixExpression();
}
/**
- * 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
+ * Parse a variable declaration. Return the variable declaration that was
* parsed.
+ *
+ * variableDeclaration ::=
+ * identifier ('=' expression)?
*/
- 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
- // than an identifier and/or is followed by an assignableSelector.
- //
- Expression expression = _parsePrimaryExpression();
- bool isOptional = primaryAllowed || expression is SimpleIdentifier;
- while (true) {
- while (_isLikelyArgumentList()) {
- TypeArgumentList typeArguments = _parseOptionalTypeArguments();
- ArgumentList argumentList = parseArgumentList();
- Expression currentExpression = expression;
- if (currentExpression is SimpleIdentifier) {
- expression = new MethodInvocation(
- null, null, currentExpression, typeArguments, argumentList);
- } else if (currentExpression is PrefixedIdentifier) {
- expression = new MethodInvocation(
- currentExpression.prefix,
- currentExpression.period,
- currentExpression.identifier,
- typeArguments,
- argumentList);
- } else if (currentExpression is PropertyAccess) {
- expression = new MethodInvocation(
- currentExpression.target,
- currentExpression.operator,
- currentExpression.propertyName,
- typeArguments,
- argumentList);
- } else {
- expression = new FunctionExpressionInvocation(
- expression, typeArguments, argumentList);
- }
- if (!primaryAllowed) {
- isOptional = false;
- }
- }
- Expression selectorExpression = _parseAssignableSelector(
- expression, isOptional || (expression is PrefixedIdentifier));
- if (identical(selectorExpression, expression)) {
- if (!isOptional && (expression is PrefixedIdentifier)) {
- PrefixedIdentifier identifier = expression as PrefixedIdentifier;
- expression = new PropertyAccess(
- identifier.prefix, identifier.period, identifier.identifier);
- }
- return expression;
- }
- expression = selectorExpression;
- isOptional = true;
+ VariableDeclaration parseVariableDeclaration() {
+ // TODO(paulberry): prior to the fix for bug 23204, we permitted
+ // annotations before variable declarations (e.g. "String @deprecated s;").
+ // Although such constructions are prohibited by the spec, we may want to
+ // consider handling them anyway to allow for better parser recovery in the
+ // event that the user erroneously tries to use them. However, as a
+ // counterargument, this would likely degrade parser recovery in the event
+ // of a construct like "class C { int @deprecated foo() {} }" (i.e. the
+ // user is in the middle of inserting "int bar;" prior to
+ // "@deprecated foo() {}").
+ SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
+ Token equals = null;
+ Expression initializer = null;
+ if (_matches(TokenType.EQ)) {
+ equals = getAndAdvance();
+ initializer = parseExpression2();
}
+ return new VariableDeclaration(name, equals, initializer);
}
/**
- * Parse an assignable selector. The [prefix] is the expression preceding the
- * selector. The [optional] is `true` if the selector is optional. Return the
- * assignable selector that was parsed, or the original prefix if there was no
- * assignable selector. If [allowConditional] is false, then the '?.'
- * operator will still be parsed, but a parse error will be generated.
+ * Parse a variable declaration list. The [commentAndMetadata] is the metadata
+ * to be associated with the variable declaration list. Return the variable
+ * declaration list that was parsed.
*
- * unconditionalAssignableSelector ::=
- * '[' expression ']'
- * | '.' identifier
+ * variableDeclarationList ::=
+ * finalConstVarOrType variableDeclaration (',' variableDeclaration)*
+ */
+ VariableDeclarationList parseVariableDeclarationListAfterMetadata(
+ CommentAndMetadata commentAndMetadata) {
+ FinalConstVarOrType holder = parseFinalConstVarOrType(false);
+ return parseVariableDeclarationListAfterType(
+ commentAndMetadata, holder.keyword, holder.type);
+ }
+
+ /**
+ * Parse a variable declaration list. The [commentAndMetadata] is the metadata
+ * to be associated with the variable declaration list, or `null` if there is
+ * no attempt at parsing the comment and metadata. The [keyword] is the token
+ * representing the 'final', 'const' or 'var' keyword, or `null` if there is
+ * no keyword. The [type] is the type of the variables in the list. Return the
+ * variable declaration list that was parsed.
*
- * assignableSelector ::=
- * unconditionalAssignableSelector
- * | '?.' identifier
+ * variableDeclarationList ::=
+ * finalConstVarOrType variableDeclaration (',' variableDeclaration)*
*/
- Expression _parseAssignableSelector(Expression prefix, bool optional,
- {bool allowConditional: true}) {
- TokenType type = _currentToken.type;
- if (type == TokenType.OPEN_SQUARE_BRACKET) {
- Token leftBracket = getAndAdvance();
- bool wasInInitializer = _inInitializer;
- _inInitializer = false;
- try {
- Expression index = parseExpression2();
- Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
- return new IndexExpression.forTarget(
- prefix, leftBracket, index, rightBracket);
- } finally {
- _inInitializer = wasInInitializer;
- }
- } else {
- 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;
- }
+ VariableDeclarationList parseVariableDeclarationListAfterType(
+ CommentAndMetadata commentAndMetadata, Token keyword, TypeName type) {
+ if (type != null &&
+ keyword != null &&
+ _tokenMatchesKeyword(keyword, Keyword.VAR)) {
+ _reportErrorForToken(ParserErrorCode.VAR_AND_TYPE, keyword);
+ }
+ List<VariableDeclaration> variables = <VariableDeclaration>[
+ parseVariableDeclaration()
+ ];
+ while (_optional(TokenType.COMMA)) {
+ variables.add(parseVariableDeclaration());
}
+ return new VariableDeclarationList(commentAndMetadata?.comment,
+ commentAndMetadata?.metadata, keyword, type, variables);
}
/**
- * Parse a await expression. Return the await expression that was parsed.
- *
- * This method assumes that the current token matches `_AWAIT`.
+ * Parse a variable declaration statement. The [commentAndMetadata] is the
+ * metadata to be associated with the variable declaration statement, or
+ * `null` if there is no attempt at parsing the comment and metadata. Return
+ * the variable declaration statement that was parsed.
*
- * awaitExpression ::=
- * 'await' unaryExpression
+ * variableDeclarationStatement ::=
+ * variableDeclarationList ';'
*/
- AwaitExpression _parseAwaitExpression() {
- Token awaitToken = getAndAdvance();
- Expression expression = _parseUnaryExpression();
- return new AwaitExpression(awaitToken, expression);
+ VariableDeclarationStatement parseVariableDeclarationStatementAfterMetadata(
+ CommentAndMetadata commentAndMetadata) {
+ // Token startToken = currentToken;
+ VariableDeclarationList variableList =
+ parseVariableDeclarationListAfterMetadata(commentAndMetadata);
+// if (!matches(TokenType.SEMICOLON)) {
+// if (matches(startToken, Keyword.VAR) && isTypedIdentifier(startToken.getNext())) {
+// // TODO(brianwilkerson) This appears to be of the form "var type variable". We should do
+// // a better job of recovering in this case.
+// }
+// }
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new VariableDeclarationStatement(variableList, semicolon);
}
/**
- * Parse a bitwise and expression. Return the bitwise and expression that was
- * parsed.
+ * Parse a while statement. Return the while statement that was parsed.
*
- * bitwiseAndExpression ::=
- * shiftExpression ('&' shiftExpression)*
- * | 'super' ('&' shiftExpression)+
+ * This method assumes that the current token matches [Keyword.WHILE].
+ *
+ * whileStatement ::=
+ * 'while' '(' expression ')' statement
*/
- Expression _parseBitwiseAndExpression() {
- Expression expression;
- if (_currentToken.keyword == Keyword.SUPER &&
- _currentToken.next.type == TokenType.AMPERSAND) {
- expression = new SuperExpression(getAndAdvance());
- } else {
- expression = _parseShiftExpression();
- }
- while (_currentToken.type == TokenType.AMPERSAND) {
- expression = new BinaryExpression(
- expression, getAndAdvance(), _parseShiftExpression());
+ Statement parseWhileStatement() {
+ bool wasInLoop = _inLoop;
+ _inLoop = true;
+ try {
+ Token keyword = getAndAdvance();
+ Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
+ Expression condition = parseExpression2();
+ Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ Statement body = parseStatement2();
+ return new WhileStatement(
+ keyword, leftParenthesis, condition, rightParenthesis, body);
+ } finally {
+ _inLoop = wasInLoop;
}
- return expression;
}
/**
- * Parse a bitwise exclusive-or expression. Return the bitwise exclusive-or
- * expression that was parsed.
+ * Parse a with clause. Return the with clause that was parsed.
*
- * bitwiseXorExpression ::=
- * bitwiseAndExpression ('^' bitwiseAndExpression)*
- * | 'super' ('^' bitwiseAndExpression)+
+ * This method assumes that the current token matches `Keyword.WITH`.
+ *
+ * withClause ::=
+ * 'with' typeName (',' typeName)*
*/
- Expression _parseBitwiseXorExpression() {
- Expression expression;
- if (_currentToken.keyword == Keyword.SUPER &&
- _currentToken.next.type == TokenType.CARET) {
- expression = new SuperExpression(getAndAdvance());
- } else {
- expression = _parseBitwiseAndExpression();
- }
- while (_currentToken.type == TokenType.CARET) {
- expression = new BinaryExpression(
- expression, getAndAdvance(), _parseBitwiseAndExpression());
+ WithClause parseWithClause() {
+ Token withKeyword = getAndAdvance();
+ List<TypeName> types = <TypeName>[parseTypeName(false)];
+ while (_optional(TokenType.COMMA)) {
+ types.add(parseTypeName(false));
}
- return expression;
+ return new WithClause(withKeyword, types);
}
/**
- * 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.
+ * Parse a yield statement. Return the yield statement that was parsed.
*
- * block ::=
- * '{' statements '}'
+ * This method assumes that the current token matches [Keyword.YIELD].
+ *
+ * yieldStatement ::=
+ * 'yield' '*'? expression ';'
*/
- 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));
+ YieldStatement parseYieldStatement() {
+ Token yieldToken = getAndAdvance();
+ Token star = null;
+ if (_matches(TokenType.STAR)) {
+ star = getAndAdvance();
+ }
+ Expression expression = parseExpression2();
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new YieldStatement(yieldToken, star, expression, semicolon);
}
/**
- * Parse a break statement. Return the break statement that was parsed.
+ * Parse a prefixed identifier, starting at the [startToken], without actually
+ * creating a prefixed identifier or changing the current token. Return the
+ * token following the prefixed identifier that was parsed, or `null` if the
+ * given token is not the first token in a valid prefixed identifier.
*
- * This method assumes that the current token matches `Keyword.BREAK`.
+ * This method must be kept in sync with [parsePrefixedIdentifier].
*
- * breakStatement ::=
- * 'break' identifier? ';'
+ * prefixedIdentifier ::=
+ * identifier ('.' identifier)?
*/
- Statement _parseBreakStatement() {
- Token breakKeyword = getAndAdvance();
- SimpleIdentifier label = null;
- if (_matchesIdentifier()) {
- label = _parseSimpleIdentifierUnchecked();
+ Token skipPrefixedIdentifier(Token startToken) {
+ Token token = skipSimpleIdentifier(startToken);
+ if (token == null) {
+ return null;
+ } else if (!_tokenMatches(token, TokenType.PERIOD)) {
+ return token;
}
- if (!_inLoop && !_inSwitch && label == null) {
- _reportErrorForToken(ParserErrorCode.BREAK_OUTSIDE_OF_LOOP, breakKeyword);
+ token = token.next;
+ Token nextToken = skipSimpleIdentifier(token);
+ if (nextToken != null) {
+ return nextToken;
+ } else if (_tokenMatches(token, TokenType.CLOSE_PAREN) ||
+ _tokenMatches(token, TokenType.COMMA)) {
+ // If the `id.` is followed by something that cannot produce a valid
+ // structure then assume this is a prefixed identifier but missing the
+ // trailing identifier
+ return token;
}
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new BreakStatement(breakKeyword, label, semicolon);
+ return null;
}
/**
- * 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?
+ * Parse a return type, starting at the [startToken], without actually
+ * creating a return type or changing the current token. Return the token
+ * following the return type that was parsed, or `null` if the given token is
+ * not the first token in a valid return type.
*
- * cascadeSelector ::=
- * '[' expression ']'
- * | identifier
+ * This method must be kept in sync with [parseReturnType].
*
- * cascadeAssignment ::=
- * assignmentOperator expressionWithoutCascade
+ * returnType ::=
+ * 'void'
+ * | type
*/
- Expression _parseCascadeSection() {
- Token period = getAndAdvance();
- Expression expression = null;
- SimpleIdentifier functionName = null;
- if (_matchesIdentifier()) {
- functionName = _parseSimpleIdentifierUnchecked();
- } else if (_currentToken.type == TokenType.OPEN_SQUARE_BRACKET) {
- Token leftBracket = getAndAdvance();
- bool wasInInitializer = _inInitializer;
- _inInitializer = false;
- try {
- Expression index = parseExpression2();
- Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
- expression = new IndexExpression.forCascade(
- period, leftBracket, index, rightBracket);
- period = null;
- } finally {
- _inInitializer = wasInInitializer;
- }
+ Token skipReturnType(Token startToken) {
+ if (_tokenMatchesKeyword(startToken, Keyword.VOID)) {
+ return startToken.next;
} else {
- _reportErrorForToken(ParserErrorCode.MISSING_IDENTIFIER, _currentToken,
- [_currentToken.lexeme]);
- functionName = _createSyntheticIdentifier();
- }
- assert((expression == null && functionName != null) ||
- (expression != null && functionName == null));
- if (_isLikelyArgumentList()) {
- do {
- TypeArgumentList typeArguments = _parseOptionalTypeArguments();
- if (functionName != null) {
- expression = new MethodInvocation(expression, period, functionName,
- typeArguments, parseArgumentList());
- period = null;
- functionName = null;
- } else if (expression == null) {
- // It should not be possible to get here.
- expression = new MethodInvocation(expression, period,
- _createSyntheticIdentifier(), typeArguments, parseArgumentList());
- } else {
- expression = new FunctionExpressionInvocation(
- expression, typeArguments, parseArgumentList());
- }
- } while (_isLikelyArgumentList());
- } else if (functionName != null) {
- expression = new PropertyAccess(expression, period, functionName);
- period = null;
- }
- assert(expression != null);
- bool progress = true;
- while (progress) {
- progress = false;
- Expression selector = _parseAssignableSelector(expression, true);
- if (!identical(selector, expression)) {
- expression = selector;
- progress = true;
- while (_isLikelyArgumentList()) {
- TypeArgumentList typeArguments = _parseOptionalTypeArguments();
- Expression currentExpression = expression;
- if (currentExpression is PropertyAccess) {
- expression = new MethodInvocation(
- currentExpression.target,
- currentExpression.operator,
- currentExpression.propertyName,
- typeArguments,
- parseArgumentList());
- } else {
- expression = new FunctionExpressionInvocation(
- expression, typeArguments, parseArgumentList());
- }
- }
- }
+ return skipTypeName(startToken);
}
- if (_currentToken.type.isAssignmentOperator) {
- Token operator = getAndAdvance();
- _ensureAssignable(expression);
- expression = new AssignmentExpression(
- expression, operator, parseExpressionWithoutCascade());
- }
- return expression;
}
/**
- * Parse a class declaration. The [commentAndMetadata] is the metadata to be
- * associated with the member. The [abstractKeyword] is the token for the
- * keyword 'abstract', or `null` if the keyword was not given. Return the
- * class declaration that was parsed.
+ * Parse a simple identifier, starting at the [startToken], without actually
+ * creating a simple identifier or changing the current token. Return the
+ * token following the simple identifier that was parsed, or `null` if the
+ * given token is not the first token in a valid simple identifier.
*
- * This method assumes that the current token matches `Keyword.CLASS`.
+ * This method must be kept in sync with [parseSimpleIdentifier].
*
- * classDeclaration ::=
- * metadata 'abstract'? 'class' name typeParameterList? (extendsClause withClause?)? implementsClause? '{' classMembers '}' |
- * metadata 'abstract'? 'class' mixinApplicationClass
+ * identifier ::=
+ * IDENTIFIER
*/
- CompilationUnitMember _parseClassDeclaration(
- CommentAndMetadata commentAndMetadata, Token abstractKeyword) {
- //
- // Parse the name and type parameters.
- //
- Token keyword = getAndAdvance();
- SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
- String className = name.name;
- TypeParameterList typeParameters = null;
- 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
- // generate errors if they are not in the order required by the
- // specification.
- //
- ExtendsClause extendsClause = null;
- WithClause withClause = null;
- ImplementsClause implementsClause = null;
- bool foundClause = true;
- while (foundClause) {
- Keyword keyword = _currentToken.keyword;
- if (keyword == Keyword.EXTENDS) {
- if (extendsClause == null) {
- extendsClause = parseExtendsClause();
- if (withClause != null) {
- _reportErrorForToken(
- ParserErrorCode.WITH_BEFORE_EXTENDS, withClause.withKeyword);
- } else if (implementsClause != null) {
- _reportErrorForToken(ParserErrorCode.IMPLEMENTS_BEFORE_EXTENDS,
- implementsClause.implementsKeyword);
- }
- } else {
- _reportErrorForToken(ParserErrorCode.MULTIPLE_EXTENDS_CLAUSES,
- extendsClause.extendsKeyword);
- parseExtendsClause();
- }
- } else if (keyword == Keyword.WITH) {
- if (withClause == null) {
- withClause = parseWithClause();
- if (implementsClause != null) {
- _reportErrorForToken(ParserErrorCode.IMPLEMENTS_BEFORE_WITH,
- implementsClause.implementsKeyword);
- }
- } else {
- _reportErrorForToken(
- ParserErrorCode.MULTIPLE_WITH_CLAUSES, withClause.withKeyword);
- parseWithClause();
- // TODO(brianwilkerson) Should we merge the list of applied mixins
- // into a single list?
- }
- } else if (keyword == Keyword.IMPLEMENTS) {
- if (implementsClause == null) {
- implementsClause = parseImplementsClause();
- } else {
- _reportErrorForToken(ParserErrorCode.MULTIPLE_IMPLEMENTS_CLAUSES,
- implementsClause.implementsKeyword);
- parseImplementsClause();
- // TODO(brianwilkerson) Should we merge the list of implemented
- // classes into a single list?
- }
- } else {
- foundClause = false;
- }
- }
- if (withClause != null && extendsClause == null) {
- _reportErrorForToken(
- ParserErrorCode.WITH_WITHOUT_EXTENDS, withClause.withKeyword);
+ Token skipSimpleIdentifier(Token startToken) {
+ if (_tokenMatches(startToken, TokenType.IDENTIFIER) ||
+ _tokenMatchesPseudoKeyword(startToken)) {
+ return startToken.next;
}
- //
- // Look for and skip over the extra-lingual 'native' specification.
- //
- NativeClause nativeClause = null;
- if (_matchesString(_NATIVE) && _tokenMatches(_peek(), TokenType.STRING)) {
- nativeClause = _parseNativeClause();
+ return null;
+ }
+
+ /**
+ * Parse a string literal, starting at the [startToken], without actually
+ * creating a string literal or changing the current token. Return the token
+ * following the string literal that was parsed, or `null` if the given token
+ * is not the first token in a valid string literal.
+ *
+ * This method must be kept in sync with [parseStringLiteral].
+ *
+ * stringLiteral ::=
+ * MULTI_LINE_STRING+
+ * | SINGLE_LINE_STRING+
+ */
+ Token skipStringLiteral(Token startToken) {
+ Token token = startToken;
+ while (token != null && _tokenMatches(token, TokenType.STRING)) {
+ token = token.next;
+ TokenType type = token.type;
+ if (type == TokenType.STRING_INTERPOLATION_EXPRESSION ||
+ type == TokenType.STRING_INTERPOLATION_IDENTIFIER) {
+ token = _skipStringInterpolation(token);
+ }
}
- //
- // Parse the body of the class.
- //
- Token leftBracket = null;
- List<ClassMember> members = null;
- Token rightBracket = null;
- if (_matches(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);
+ if (identical(token, startToken)) {
+ return null;
}
- ClassDeclaration classDeclaration = new ClassDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- abstractKeyword,
- keyword,
- name,
- typeParameters,
- extendsClause,
- withClause,
- implementsClause,
- leftBracket,
- members,
- rightBracket);
- classDeclaration.nativeClause = nativeClause;
- return classDeclaration;
+ return token;
}
/**
- * Parse a list of class members. The [className] is the name of the class
- * whose members are being parsed. The [closingBracket] is the closing bracket
- * for the class, or `null` if the closing bracket is missing. Return the list
- * of class members that were parsed.
+ * Parse a list of type arguments, starting at the [startToken], without
+ * actually creating a type argument list or changing the current token.
+ * Return the token following the type argument list that was parsed, or
+ * `null` if the given token is not the first token in a valid type argument
+ * list.
*
- * classMembers ::=
- * (metadata memberDefinition)*
+ * This method must be kept in sync with [parseTypeArgumentList].
+ *
+ * typeArguments ::=
+ * '<' typeList '>'
+ *
+ * typeList ::=
+ * type (',' type)*
*/
- List<ClassMember> _parseClassMembers(String className, Token closingBracket) {
- List<ClassMember> members = <ClassMember>[];
- Token memberStart = _currentToken;
- TokenType type = _currentToken.type;
- Keyword keyword = _currentToken.keyword;
- while (type != TokenType.EOF &&
- type != TokenType.CLOSE_CURLY_BRACKET &&
- (closingBracket != null ||
- (keyword != Keyword.CLASS && keyword != Keyword.TYPEDEF))) {
- if (type == TokenType.SEMICOLON) {
- _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
- [_currentToken.lexeme]);
- _advance();
- } else {
- ClassMember member = parseClassMember(className);
- if (member != null) {
- members.add(member);
- }
+ Token skipTypeArgumentList(Token startToken) {
+ Token token = startToken;
+ if (!_tokenMatches(token, TokenType.LT) &&
+ !_injectGenericCommentTypeList()) {
+ return null;
+ }
+ token = skipTypeName(token.next);
+ if (token == null) {
+ // If the start token '<' is followed by '>'
+ // then assume this should be type argument list but is missing a type
+ token = startToken.next;
+ if (_tokenMatches(token, TokenType.GT)) {
+ return token.next;
}
- if (identical(_currentToken, memberStart)) {
- _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
- [_currentToken.lexeme]);
- _advance();
+ return null;
+ }
+ while (_tokenMatches(token, TokenType.COMMA)) {
+ token = skipTypeName(token.next);
+ if (token == null) {
+ return null;
}
- memberStart = _currentToken;
- type = _currentToken.type;
- keyword = _currentToken.keyword;
}
- return members;
+ if (token.type == TokenType.GT) {
+ return token.next;
+ } else if (token.type == TokenType.GT_GT) {
+ Token second = new Token(TokenType.GT, token.offset + 1);
+ second.setNextWithoutSettingPrevious(token.next);
+ return second;
+ }
+ return null;
}
/**
- * 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. Return the class type alias that was parsed.
- *
- * This method assumes that the current token matches an identifier.
+ * Parse a type name, starting at the [startToken], without actually creating
+ * a type name or changing the current token. Return the token following the
+ * type name that was parsed, or `null` if the given token is not the first
+ * token in a valid type name.
*
- * classTypeAlias ::=
- * identifier typeParameters? '=' 'abstract'? mixinApplication
+ * This method must be kept in sync with [parseTypeName].
*
- * mixinApplication ::=
- * type withClause implementsClause? ';'
+ * type ::=
+ * qualified typeArguments?
*/
- ClassTypeAlias _parseClassTypeAlias(CommentAndMetadata commentAndMetadata,
- Token abstractKeyword, Token classKeyword) {
- SimpleIdentifier className =
- _parseSimpleIdentifierUnchecked(isDeclaration: true);
- TypeParameterList typeParameters = null;
- if (_matches(TokenType.LT)) {
- typeParameters = parseTypeParameterList();
+ Token skipTypeName(Token startToken) {
+ Token token = skipPrefixedIdentifier(startToken);
+ if (token == null) {
+ return null;
}
- return _parseClassTypeAliasAfterName(commentAndMetadata, abstractKeyword,
- classKeyword, className, typeParameters);
+ if (_tokenMatches(token, TokenType.LT)) {
+ token = skipTypeArgumentList(token);
+ }
+ return token;
+ }
+
+ /**
+ * Advance to the next token in the token stream.
+ */
+ void _advance() {
+ _currentToken = _currentToken.next;
+ }
+
+ /**
+ * Append the character equivalent of the given [scalarValue] to the given
+ * [builder]. Use the [startIndex] and [endIndex] to report an error, and
+ * don't append anything to the builder, if the scalar value is invalid. The
+ * [escapeSequence] is the escape sequence that was parsed to produce the
+ * scalar value (used for error reporting).
+ */
+ void _appendScalarValue(StringBuffer buffer, String escapeSequence,
+ int scalarValue, int startIndex, int endIndex) {
+ if (scalarValue < 0 ||
+ scalarValue > Character.MAX_CODE_POINT ||
+ (scalarValue >= 0xD800 && scalarValue <= 0xDFFF)) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.INVALID_CODE_POINT, [escapeSequence]);
+ return;
+ }
+ if (scalarValue < Character.MAX_VALUE) {
+ buffer.writeCharCode(scalarValue);
+ } else {
+ buffer.write(Character.toChars(scalarValue));
+ }
+ }
+
+ /**
+ * Clone all token starting from the given [token] up to the end of the token
+ * stream, and return the first token in the new token stream.
+ */
+ Token _cloneTokens(Token token) {
+ if (token == null) {
+ return null;
+ }
+ token = token is CommentToken ? token.parent : token;
+ Token head = new Token(TokenType.EOF, -1);
+ head.setNext(head);
+ Token current = head;
+ while (token.type != TokenType.EOF) {
+ Token clone = token.copy();
+ current.setNext(clone);
+ current = clone;
+ token = token.next;
+ }
+ Token tail = new Token(TokenType.EOF, 0);
+ tail.setNext(tail);
+ current.setNext(tail);
+ return head.next;
+ }
+
+ /**
+ * Convert the given [method] declaration into the nearest valid top-level
+ * function declaration (that is, the function declaration that most closely
+ * captures the components of the given method declaration).
+ */
+ FunctionDeclaration _convertToFunctionDeclaration(MethodDeclaration method) =>
+ new FunctionDeclaration(
+ method.documentationComment,
+ method.metadata,
+ method.externalKeyword,
+ method.returnType,
+ method.propertyKeyword,
+ method.name,
+ new FunctionExpression(
+ method.typeParameters, method.parameters, method.body));
+
+ /**
+ * Return `true` if the current token could be the start of a compilation unit
+ * member. This method is used for recovery purposes to decide when to stop
+ * skipping tokens after finding an error while parsing a compilation unit
+ * member.
+ */
+ bool _couldBeStartOfCompilationUnitMember() {
+ 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 (keyword == Keyword.CLASS) {
+ // This looks like the start of a class definition
+ return true;
+ } else if (keyword == Keyword.TYPEDEF &&
+ nextType != TokenType.PERIOD &&
+ nextType != TokenType.LT) {
+ // This looks like the start of a typedef
+ return true;
+ } 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 (nextType == TokenType.OPEN_PAREN) {
+ // This looks like the start of a function
+ return true;
+ }
+ Token token = skipReturnType(_currentToken);
+ if (token == null) {
+ return false;
+ }
+ // 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;
+ }
+ }
+ return false;
}
/**
- * 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? ';'
+ * Return a synthetic token representing the given [keyword].
*/
- ClassTypeAlias _parseClassTypeAliasAfterName(
- CommentAndMetadata commentAndMetadata,
- Token abstractKeyword,
- Token classKeyword,
- SimpleIdentifier className,
- TypeParameterList typeParameters) {
- Token equals = _expect(TokenType.EQ);
- TypeName superclass = parseTypeName(false);
- WithClause withClause = null;
- if (_matchesKeyword(Keyword.WITH)) {
- withClause = parseWithClause();
- } else {
- _reportErrorForCurrentToken(
- ParserErrorCode.EXPECTED_TOKEN, [Keyword.WITH.syntax]);
- }
- ImplementsClause implementsClause = null;
- if (_matchesKeyword(Keyword.IMPLEMENTS)) {
- implementsClause = parseImplementsClause();
- }
- Token semicolon;
- if (_matches(TokenType.SEMICOLON)) {
- semicolon = getAndAdvance();
- } else {
- if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
- _reportErrorForCurrentToken(
- ParserErrorCode.EXPECTED_TOKEN, [TokenType.SEMICOLON.lexeme]);
- Token leftBracket = getAndAdvance();
- _parseClassMembers(className.name, _getEndToken(leftBracket));
- _expect(TokenType.CLOSE_CURLY_BRACKET);
- } else {
- _reportErrorForToken(ParserErrorCode.EXPECTED_TOKEN,
- _currentToken.previous, [TokenType.SEMICOLON.lexeme]);
+ Token _createSyntheticKeyword(Keyword keyword) => _injectToken(
+ new Parser_SyntheticKeywordToken(keyword, _currentToken.offset));
+
+ /**
+ * Return a synthetic token with the given [type].
+ */
+ Token _createSyntheticToken(TokenType type) =>
+ _injectToken(new StringToken(type, "", _currentToken.offset));
+
+ /**
+ * Create and return a new token with the given [type]. The token will replace
+ * the first portion of the given [token], so it will have the same offset and
+ * will have any comments that might have preceeded the token.
+ */
+ Token _createToken(Token token, TokenType type, {bool isBegin: false}) {
+ CommentToken comments = token.precedingComments;
+ if (comments == null) {
+ if (isBegin) {
+ return new BeginToken(type, token.offset);
}
- semicolon = _createSyntheticToken(TokenType.SEMICOLON);
+ return new Token(type, token.offset);
+ } else if (isBegin) {
+ return new BeginTokenWithComment(type, token.offset, comments);
}
- return new ClassTypeAlias(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- classKeyword,
- className,
- typeParameters,
- equals,
- abstractKeyword,
- superclass,
- withClause,
- implementsClause,
- semicolon);
+ return new TokenWithComment(type, token.offset, comments);
}
/**
- * Parse a list of combinators in a directive. Return the combinators that
- * were parsed, or `null` if there are no combinators.
+ * Check that the given [expression] is assignable and report an error if it
+ * isn't.
*
- * combinator ::=
- * 'show' identifier (',' identifier)*
- * | 'hide' identifier (',' identifier)*
+ * assignableExpression ::=
+ * primary (arguments* assignableSelector)+
+ * | 'super' unconditionalAssignableSelector
+ * | identifier
+ *
+ * unconditionalAssignableSelector ::=
+ * '[' expression ']'
+ * | '.' identifier
+ *
+ * assignableSelector ::=
+ * unconditionalAssignableSelector
+ * | '?.' identifier
*/
- List<Combinator> _parseCombinators() {
- List<Combinator> combinators = null;
- while (true) {
- Combinator combinator = parseCombinator();
- if (combinator == null) {
- break;
- }
- combinators ??= <Combinator>[];
- combinators.add(combinator);
+ void _ensureAssignable(Expression expression) {
+ if (expression != null && !expression.isAssignable) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE);
}
- return combinators;
}
/**
- * Parse the documentation comment and metadata preceding a declaration. This
- * method allows any number of documentation comments to occur before, after
- * or between the metadata, but only returns the last (right-most)
- * documentation comment that is found. Return the documentation comment and
- * metadata that were parsed.
+ * If the current token has the expected type, return it after advancing to
+ * the next token. Otherwise report an error and return the current token
+ * without advancing.
*
- * metadata ::=
- * annotation*
+ * Note that the method [_expectGt] should be used if the argument to this
+ * method would be [TokenType.GT].
+ *
+ * The [type] is the type of token that is expected.
*/
- CommentAndMetadata _parseCommentAndMetadata() {
- // TODO(brianwilkerson) Consider making the creation of documentation
- // comments be lazy.
- List<DocumentationCommentToken> tokens = _parseDocumentationCommentTokens();
- List<Annotation> metadata = null;
- while (_matches(TokenType.AT)) {
- metadata ??= <Annotation>[];
- metadata.add(parseAnnotation());
- List<DocumentationCommentToken> optionalTokens =
- _parseDocumentationCommentTokens();
- if (optionalTokens != null) {
- tokens = optionalTokens;
+ Token _expect(TokenType type) {
+ if (_matches(type)) {
+ return getAndAdvance();
+ }
+ // Remove uses of this method in favor of matches?
+ // Pass in the error code to use to report the error?
+ if (type == TokenType.SEMICOLON) {
+ if (_tokenMatches(_currentToken.next, TokenType.SEMICOLON)) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
+ _advance();
+ return getAndAdvance();
}
+ _reportErrorForToken(ParserErrorCode.EXPECTED_TOKEN,
+ _currentToken.previous, [type.lexeme]);
+ return _createSyntheticToken(TokenType.SEMICOLON);
}
- return new CommentAndMetadata(_parseDocumentationComment(tokens), metadata);
+ _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TOKEN, [type.lexeme]);
+ return _createSyntheticToken(type);
}
/**
- * Parse a comment reference from the source between square brackets. The
- * [referenceSource] is the source occurring between the square brackets
- * within a documentation comment. The [sourceOffset] is the offset of the
- * first character of the reference source. Return the comment reference that
- * was parsed, or `null` if no reference could be found.
- *
- * commentReference ::=
- * 'new'? prefixedIdentifier
- */
- CommentReference _parseCommentReference(
- String referenceSource, int sourceOffset) {
- // TODO(brianwilkerson) The errors are not getting the right offset/length
- // and are being duplicated.
- try {
- BooleanErrorListener listener = new BooleanErrorListener();
- Scanner scanner = new Scanner(
- null, new SubSequenceReader(referenceSource, sourceOffset), listener);
- scanner.setSourceStart(1, 1);
- Token firstToken = scanner.tokenize();
- if (listener.errorReported) {
- return null;
- }
- if (firstToken.type == TokenType.EOF) {
- Token syntheticToken =
- new SyntheticStringToken(TokenType.IDENTIFIER, "", sourceOffset);
- syntheticToken.setNext(firstToken);
- return new CommentReference(null, new SimpleIdentifier(syntheticToken));
- }
- Token newKeyword = null;
- if (_tokenMatchesKeyword(firstToken, Keyword.NEW)) {
- newKeyword = firstToken;
- firstToken = firstToken.next;
- }
- if (firstToken.isUserDefinableOperator) {
- if (firstToken.next.type != TokenType.EOF) {
- return null;
- }
- Identifier identifier = new SimpleIdentifier(firstToken);
- return new CommentReference(null, identifier);
- } else if (_tokenMatchesKeyword(firstToken, Keyword.OPERATOR)) {
- Token secondToken = firstToken.next;
- if (secondToken.isUserDefinableOperator) {
- if (secondToken.next.type != TokenType.EOF) {
- return null;
- }
- Identifier identifier = new SimpleIdentifier(secondToken);
- return new CommentReference(null, identifier);
- }
- return null;
- } else if (_tokenMatchesIdentifier(firstToken)) {
- Token secondToken = firstToken.next;
- Token thirdToken = secondToken.next;
- Token nextToken;
- Identifier identifier;
- if (_tokenMatches(secondToken, TokenType.PERIOD)) {
- if (thirdToken.isUserDefinableOperator) {
- identifier = new PrefixedIdentifier(
- new SimpleIdentifier(firstToken),
- secondToken,
- new SimpleIdentifier(thirdToken));
- nextToken = thirdToken.next;
- } else if (_tokenMatchesKeyword(thirdToken, Keyword.OPERATOR)) {
- Token fourthToken = thirdToken.next;
- if (fourthToken.isUserDefinableOperator) {
- identifier = new PrefixedIdentifier(
- new SimpleIdentifier(firstToken),
- secondToken,
- new SimpleIdentifier(fourthToken));
- nextToken = fourthToken.next;
- } else {
- return null;
- }
- } else if (_tokenMatchesIdentifier(thirdToken)) {
- identifier = new PrefixedIdentifier(
- new SimpleIdentifier(firstToken),
- secondToken,
- new SimpleIdentifier(thirdToken));
- nextToken = thirdToken.next;
- }
- } else {
- identifier = new SimpleIdentifier(firstToken);
- nextToken = firstToken.next;
- }
- if (nextToken.type != TokenType.EOF) {
- return null;
- }
- return new CommentReference(newKeyword, identifier);
- } 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.
+ * If the current token has the type [TokenType.GT], return it after advancing
+ * to the next token. Otherwise report an error and create a synthetic token.
+ */
+ Token _expectGt() {
+ if (_matchesGt()) {
+ return getAndAdvance();
}
- return null;
+ _reportErrorForCurrentToken(
+ ParserErrorCode.EXPECTED_TOKEN, [TokenType.GT.lexeme]);
+ return _createSyntheticToken(TokenType.GT);
}
/**
- * Parse all of the comment references occurring in the given array of
- * documentation comments. The [tokens] are the comment tokens representing
- * the documentation comments to be parsed. Return the comment references that
- * were parsed.
- *
- * commentReference ::=
- * '[' 'new'? qualified ']' libraryReference?
- *
- * libraryReference ::=
- * '(' stringLiteral ')'
+ * If the current token is a keyword matching the given [keyword], return it
+ * after advancing to the next token. Otherwise report an error and return the
+ * current token without advancing.
*/
- List<CommentReference> _parseCommentReferences(
- List<DocumentationCommentToken> tokens) {
- List<CommentReference> references = <CommentReference>[];
- bool isInGitHubCodeBlock = false;
- for (DocumentationCommentToken token in tokens) {
- String comment = token.lexeme;
- // Skip GitHub code blocks.
- // https://help.github.com/articles/creating-and-highlighting-code-blocks/
- if (tokens.length != 1) {
- if (comment.indexOf('```') != -1) {
- isInGitHubCodeBlock = !isInGitHubCodeBlock;
- }
- if (isInGitHubCodeBlock) {
- continue;
- }
- }
- // Remove GitHub include code.
- comment = _removeGitHubInlineCode(comment);
- // Find references.
- int length = comment.length;
- List<List<int>> codeBlockRanges = _getCodeBlockRanges(comment);
- int leftIndex = comment.indexOf('[');
- while (leftIndex >= 0 && leftIndex + 1 < length) {
- List<int> range = _findRange(codeBlockRanges, leftIndex);
- if (range == null) {
- int nameOffset = token.offset + leftIndex + 1;
- int rightIndex = comment.indexOf(']', leftIndex);
- if (rightIndex >= 0) {
- int firstChar = comment.codeUnitAt(leftIndex + 1);
- if (firstChar != 0x27 && firstChar != 0x22) {
- if (_isLinkText(comment, rightIndex)) {
- // TODO(brianwilkerson) Handle the case where there's a library
- // URI in the link text.
- } else {
- CommentReference reference = _parseCommentReference(
- comment.substring(leftIndex + 1, rightIndex), nameOffset);
- if (reference != null) {
- references.add(reference);
- token.references.add(reference.beginToken);
- }
- }
- }
- } else {
- // terminating ']' is not typed yet
- int charAfterLeft = comment.codeUnitAt(leftIndex + 1);
- Token nameToken;
- if (Character.isLetterOrDigit(charAfterLeft)) {
- int nameEnd = StringUtilities.indexOfFirstNotLetterDigit(
- comment, leftIndex + 1);
- String name = comment.substring(leftIndex + 1, nameEnd);
- nameToken =
- new StringToken(TokenType.IDENTIFIER, name, nameOffset);
- } else {
- nameToken = new SyntheticStringToken(
- TokenType.IDENTIFIER, '', nameOffset);
- }
- nameToken.setNext(new SimpleToken(TokenType.EOF, nameToken.end));
- references.add(
- new CommentReference(null, new SimpleIdentifier(nameToken)));
- token.references.add(nameToken);
- // next character
- rightIndex = leftIndex + 1;
- }
- leftIndex = comment.indexOf('[', rightIndex);
- } else {
- leftIndex = comment.indexOf('[', range[1]);
- }
- }
+ Token _expectKeyword(Keyword keyword) {
+ if (_matchesKeyword(keyword)) {
+ return getAndAdvance();
}
- return references;
+ // Remove uses of this method in favor of matches?
+ // Pass in the error code to use to report the error?
+ _reportErrorForCurrentToken(
+ ParserErrorCode.EXPECTED_TOKEN, [keyword.syntax]);
+ return _currentToken;
}
/**
- * Parse a compilation unit member. The [commentAndMetadata] is the metadata
- * to be associated with the member. Return the compilation unit member that
- * was parsed, or `null` if what was parsed could not be represented as a
- * compilation unit member.
- *
- * compilationUnitMember ::=
- * classDefinition
- * | functionTypeAlias
- * | external functionSignature
- * | external getterSignature
- * | external setterSignature
- * | functionSignature functionBody
- * | returnType? getOrSet identifier formalParameterList functionBody
- * | (final | const) type? staticFinalDeclarationList ';'
- * | variableDeclaration ';'
+ * Search the given list of [ranges] for a range that contains the given
+ * [index]. Return the range that was found, or `null` if none of the ranges
+ * contain the index.
*/
- CompilationUnitMember _parseCompilationUnitMember(
- CommentAndMetadata commentAndMetadata) {
- Modifiers modifiers = _parseModifiers();
- Keyword keyword = _currentToken.keyword;
- if (keyword == Keyword.CLASS) {
- return _parseClassDeclaration(
- commentAndMetadata, _validateModifiersForClass(modifiers));
- }
- 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 (keyword == Keyword.ENUM) {
- _validateModifiersForEnum(modifiers);
- return _parseEnumDeclaration(commentAndMetadata);
- } 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 (keyword == Keyword.OPERATOR && _isOperator(next)) {
- _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
- return _convertToFunctionDeclaration(_parseOperatorAfterKeyword(
- commentAndMetadata,
- modifiers.externalKeyword,
- returnType,
- getAndAdvance()));
- } else if (_matchesIdentifier() &&
- next.matchesAny(const <TokenType>[
- TokenType.OPEN_PAREN,
- TokenType.OPEN_CURLY_BRACKET,
- TokenType.FUNCTION,
- TokenType.LT
- ])) {
- _validateModifiersForTopLevelFunction(modifiers);
- return _parseFunctionDeclaration(
- commentAndMetadata, modifiers.externalKeyword, returnType);
- } else {
- //
- // We have found an error of some kind. Try to recover.
- //
- if (_matchesIdentifier()) {
- if (next.matchesAny(const <TokenType>[
- TokenType.EQ,
- TokenType.COMMA,
- TokenType.SEMICOLON
- ])) {
- //
- // We appear to have a variable declaration with a type of "void".
- //
- _reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType);
- return new TopLevelVariableDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- _parseVariableDeclarationListAfterType(null,
- _validateModifiersForTopLevelVariable(modifiers), null),
- _expect(TokenType.SEMICOLON));
- }
- }
- _reportErrorForToken(
- ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
+ List<int> _findRange(List<List<int>> ranges, int index) {
+ int rangeCount = ranges.length;
+ for (int i = 0; i < rangeCount; i++) {
+ List<int> range = ranges[i];
+ if (range[0] <= index && index <= range[1]) {
+ return range;
+ } else if (index < range[0]) {
return null;
}
- } else if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
- _tokenMatchesIdentifier(next)) {
- _validateModifiersForTopLevelFunction(modifiers);
- return _parseFunctionDeclaration(
- commentAndMetadata, modifiers.externalKeyword, null);
- } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
- _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
- return _convertToFunctionDeclaration(_parseOperatorAfterKeyword(
- commentAndMetadata,
- modifiers.externalKeyword,
- null,
- getAndAdvance()));
- } else if (!_matchesIdentifier()) {
- Token keyword = modifiers.varKeyword;
- if (keyword == null) {
- keyword = modifiers.finalKeyword;
- }
- if (keyword == null) {
- keyword = modifiers.constKeyword;
- }
- if (keyword != null) {
- //
- // We appear to have found an incomplete top-level variable declaration.
- //
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
- VariableDeclaration variable =
- new VariableDeclaration(_createSyntheticIdentifier(), null, null);
- List<VariableDeclaration> variables = <VariableDeclaration>[variable];
- return new TopLevelVariableDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- new VariableDeclarationList(null, null, keyword, null, variables),
- _expect(TokenType.SEMICOLON));
- }
- _reportErrorForToken(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
- return null;
- } else if (_isPeekGenericTypeParametersAndOpenParen()) {
- return _parseFunctionDeclaration(
- commentAndMetadata, modifiers.externalKeyword, null);
- } else if (_tokenMatches(next, TokenType.OPEN_PAREN)) {
- TypeName returnType = _parseOptionalTypeNameComment();
- _validateModifiersForTopLevelFunction(modifiers);
- return _parseFunctionDeclaration(
- commentAndMetadata, modifiers.externalKeyword, returnType);
- } else if (next.matchesAny(const <TokenType>[
- TokenType.EQ,
- TokenType.COMMA,
- TokenType.SEMICOLON
- ])) {
- if (modifiers.constKeyword == null &&
- modifiers.finalKeyword == null &&
- modifiers.varKeyword == null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE);
+ }
+ return null;
+ }
+
+ /**
+ * Return a list of the ranges of characters in the given [comment] that
+ * should be treated as code blocks.
+ */
+ List<List<int>> _getCodeBlockRanges(String comment) {
+ List<List<int>> ranges = <List<int>>[];
+ int length = comment.length;
+ if (length < 3) {
+ return ranges;
+ }
+ int index = 0;
+ int firstChar = comment.codeUnitAt(0);
+ if (firstChar == 0x2F) {
+ int secondChar = comment.codeUnitAt(1);
+ int thirdChar = comment.codeUnitAt(2);
+ if ((secondChar == 0x2A && thirdChar == 0x2A) ||
+ (secondChar == 0x2F && thirdChar == 0x2F)) {
+ index = 3;
}
- return new TopLevelVariableDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- _parseVariableDeclarationListAfterType(
- null, _validateModifiersForTopLevelVariable(modifiers), null),
- _expect(TokenType.SEMICOLON));
}
- TypeName returnType = parseReturnType();
- keyword = _currentToken.keyword;
- next = _peek();
- if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
- _tokenMatchesIdentifier(next)) {
- _validateModifiersForTopLevelFunction(modifiers);
- return _parseFunctionDeclaration(
- commentAndMetadata, modifiers.externalKeyword, returnType);
- } else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
- _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
- return _convertToFunctionDeclaration(_parseOperatorAfterKeyword(
- commentAndMetadata,
- modifiers.externalKeyword,
- returnType,
- getAndAdvance()));
- } else if (_matches(TokenType.AT)) {
- return new TopLevelVariableDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- _parseVariableDeclarationListAfterType(null,
- _validateModifiersForTopLevelVariable(modifiers), returnType),
- _expect(TokenType.SEMICOLON));
- } else if (!_matchesIdentifier()) {
- // TODO(brianwilkerson) Generalize this error. We could also be parsing a
- // top-level variable at this point.
- _reportErrorForToken(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
- Token semicolon;
- if (_matches(TokenType.SEMICOLON)) {
- semicolon = getAndAdvance();
+ if (StringUtilities.startsWith4(comment, index, 0x20, 0x20, 0x20, 0x20)) {
+ int end = index + 4;
+ while (end < length &&
+ comment.codeUnitAt(end) != 0xD &&
+ comment.codeUnitAt(end) != 0xA) {
+ end = end + 1;
+ }
+ ranges.add(<int>[index, end]);
+ index = end;
+ }
+ while (index < length) {
+ int currentChar = comment.codeUnitAt(index);
+ if (currentChar == 0xD || currentChar == 0xA) {
+ index = index + 1;
+ while (index < length &&
+ Character.isWhitespace(comment.codeUnitAt(index))) {
+ index = index + 1;
+ }
+ if (StringUtilities.startsWith6(
+ comment, index, 0x2A, 0x20, 0x20, 0x20, 0x20, 0x20)) {
+ int end = index + 6;
+ while (end < length &&
+ comment.codeUnitAt(end) != 0xD &&
+ comment.codeUnitAt(end) != 0xA) {
+ end = end + 1;
+ }
+ ranges.add(<int>[index, end]);
+ index = end;
+ }
+ } else if (index + 1 < length &&
+ currentChar == 0x5B &&
+ comment.codeUnitAt(index + 1) == 0x3A) {
+ int end = StringUtilities.indexOf2(comment, index + 2, 0x3A, 0x5D);
+ if (end < 0) {
+ end = length;
+ }
+ ranges.add(<int>[index, end]);
+ index = end + 1;
} else {
- semicolon = _createSyntheticToken(TokenType.SEMICOLON);
+ index = index + 1;
}
- 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);
- } else if (next.matchesAny(const <TokenType>[
- TokenType.OPEN_PAREN,
- TokenType.FUNCTION,
- TokenType.OPEN_CURLY_BRACKET,
- TokenType.LT
- ])) {
- _validateModifiersForTopLevelFunction(modifiers);
- return _parseFunctionDeclaration(
- commentAndMetadata, modifiers.externalKeyword, returnType);
}
- return new TopLevelVariableDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- _parseVariableDeclarationListAfterType(
- null, _validateModifiersForTopLevelVariable(modifiers), returnType),
- _expect(TokenType.SEMICOLON));
+ return ranges;
}
/**
- * Parse a configuration in either an import or export directive.
- *
- * This method assumes that the current token matches `Keyword.IF`.
+ * Return the end token associated with the given [beginToken], or `null` if
+ * either the given token is not a begin token or it does not have an end
+ * token associated with it.
+ */
+ Token _getEndToken(Token beginToken) {
+ if (beginToken is BeginToken) {
+ return beginToken.endToken;
+ }
+ return null;
+ }
+
+ bool _injectGenericComment(TokenType type, int prefixLen) {
+ if (parseGenericMethodComments) {
+ CommentToken t = _currentToken.precedingComments;
+ for (; t != null; t = t.next) {
+ if (t.type == type) {
+ String comment = t.lexeme.substring(prefixLen, t.lexeme.length - 2);
+ Token list = _scanGenericMethodComment(comment, t.offset + prefixLen);
+ if (list != null) {
+ // Remove the token from the comment stream.
+ t.remove();
+ // Insert the tokens into the stream.
+ _injectTokenList(list);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Matches a generic comment type substitution and injects it into the token
+ * stream. Returns true if a match was injected, otherwise false.
*
- * configuration ::=
- * 'if' '(' test ')' uri
+ * These comments are of the form `/*=T*/`, in other words, a [TypeName]
+ * inside a slash-star comment, preceded by equals sign.
+ */
+ bool _injectGenericCommentTypeAssign() {
+ return _injectGenericComment(TokenType.GENERIC_METHOD_TYPE_ASSIGN, 3);
+ }
+
+ /**
+ * Matches a generic comment type parameters and injects them into the token
+ * stream. Returns true if a match was injected, otherwise false.
*
- * test ::=
- * dottedName ('==' stringLiteral)?
+ * These comments are of the form `/*<K, V>*/`, in other words, a
+ * [TypeParameterList] or [TypeArgumentList] inside a slash-star comment.
+ */
+ bool _injectGenericCommentTypeList() {
+ return _injectGenericComment(TokenType.GENERIC_METHOD_TYPE_LIST, 2);
+ }
+
+ /**
+ * Inject the given [token] into the token stream immediately before the
+ * current token.
+ */
+ Token _injectToken(Token token) {
+ Token previous = _currentToken.previous;
+ token.setNext(_currentToken);
+ previous.setNext(token);
+ return token;
+ }
+
+ void _injectTokenList(Token firstToken) {
+ // Scanner creates a cyclic EOF token.
+ Token lastToken = firstToken;
+ while (lastToken.next.type != TokenType.EOF) {
+ lastToken = lastToken.next;
+ }
+ // Inject these new tokens into the stream.
+ Token previous = _currentToken.previous;
+ lastToken.setNext(_currentToken);
+ previous.setNext(firstToken);
+ _currentToken = firstToken;
+ }
+
+ /**
+ * Return `true` if the current token could be the question mark in a
+ * condition expression. The current token is assumed to be a question mark.
+ */
+ bool _isConditionalOperator() {
+ void parseOperation(Parser parser) {
+ parser.parseExpressionWithoutCascade();
+ }
+
+ Token token = _skip(_currentToken.next, parseOperation);
+ if (token == null || !_tokenMatches(token, TokenType.COLON)) {
+ return false;
+ }
+ token = _skip(token.next, parseOperation);
+ return token != null;
+ }
+
+ /**
+ * Return `true` if the given [character] is a valid hexadecimal digit.
+ */
+ bool _isHexDigit(int character) =>
+ (0x30 <= character && character <= 0x39) ||
+ (0x41 <= character && character <= 0x46) ||
+ (0x61 <= character && character <= 0x66);
+
+ bool _isLikelyArgumentList() {
+ // Try to reduce the amount of lookahead required here before enabling
+ // generic methods.
+ if (_matches(TokenType.OPEN_PAREN)) {
+ return true;
+ }
+ if (!parseGenericMethods) {
+ return false;
+ }
+ Token token = skipTypeArgumentList(_currentToken);
+ return token != null && _tokenMatches(token, TokenType.OPEN_PAREN);
+ }
+
+ /**
+ * Given that we have just found bracketed text within the given [comment],
+ * look to see whether that text is (a) followed by a parenthesized link
+ * address, (b) followed by a colon, or (c) followed by optional whitespace
+ * and another square bracket. The [rightIndex] is the index of the right
+ * bracket. Return `true` if the bracketed text is followed by a link address.
*
- * dottedName ::=
- * identifier ('.' identifier)*
+ * This method uses the syntax described by the
+ * <a href="http://daringfireball.net/projects/markdown/syntax">markdown</a>
+ * project.
*/
- Configuration _parseConfiguration() {
- Token ifKeyword = getAndAdvance();
- Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
- DottedName name = _parseDottedName();
- Token equalToken = null;
- StringLiteral value = null;
- if (_matches(TokenType.EQ_EQ)) {
- equalToken = getAndAdvance();
- value = parseStringLiteral();
- if (value is StringInterpolation) {
- _reportErrorForNode(
- ParserErrorCode.INVALID_LITERAL_IN_CONFIGURATION, value);
+ bool _isLinkText(String comment, int rightIndex) {
+ int length = comment.length;
+ int index = rightIndex + 1;
+ if (index >= length) {
+ return false;
+ }
+ int nextChar = comment.codeUnitAt(index);
+ if (nextChar == 0x28 || nextChar == 0x3A) {
+ return true;
+ }
+ while (Character.isWhitespace(nextChar)) {
+ index = index + 1;
+ if (index >= length) {
+ return false;
}
+ nextChar = comment.codeUnitAt(index);
}
- Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
- StringLiteral libraryUri = _parseUri();
- return new Configuration(ifKeyword, leftParenthesis, name, equalToken,
- value, rightParenthesis, libraryUri);
+ return nextChar == 0x5B;
}
/**
- * Parse a list of configurations. Return the configurations that were parsed,
- * or `null` if there are no configurations.
+ * Return `true` if the given [startToken] appears to be the beginning of an
+ * operator declaration.
*/
- List<Configuration> _parseConfigurations() {
- List<Configuration> configurations = null;
- while (_matchesKeyword(Keyword.IF)) {
- configurations ??= <Configuration>[];
- configurations.add(_parseConfiguration());
+ bool _isOperator(Token startToken) {
+ // Accept any operator here, even if it is not user definable.
+ if (!startToken.isOperator) {
+ return false;
}
- return configurations;
+ // Token "=" means that it is actually a field initializer.
+ if (startToken.type == TokenType.EQ) {
+ return false;
+ }
+ // Consume all operator tokens.
+ Token token = startToken.next;
+ while (token.isOperator) {
+ token = token.next;
+ }
+ // Formal parameter list is expect now.
+ return _tokenMatches(token, TokenType.OPEN_PAREN);
+ }
+
+ bool _isPeekGenericTypeParametersAndOpenParen() {
+ if (!parseGenericMethods) {
+ return false;
+ }
+ Token token = _skipTypeParameterList(_peek());
+ return token != null && _tokenMatches(token, TokenType.OPEN_PAREN);
}
/**
- * 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
+ * Return `true` if the [startToken] appears to be the first token of a type
+ * name that is followed by a variable or field formal parameter.
*/
- Expression _parseConstExpression() {
- Token keyword = getAndAdvance();
- TokenType type = _currentToken.type;
- if (type == TokenType.LT || _injectGenericCommentTypeList()) {
- return _parseListOrMapLiteral(keyword);
- } else if (type == TokenType.OPEN_SQUARE_BRACKET ||
- type == TokenType.INDEX) {
- return _parseListLiteral(keyword, null);
- } else if (type == TokenType.OPEN_CURLY_BRACKET) {
- return _parseMapLiteral(keyword, null);
+ bool _isTypedIdentifier(Token startToken) {
+ Token token = skipReturnType(startToken);
+ if (token == null) {
+ return false;
+ } else if (_tokenMatchesIdentifier(token)) {
+ return true;
+ } else if (_tokenMatchesKeyword(token, Keyword.THIS) &&
+ _tokenMatches(token.next, TokenType.PERIOD) &&
+ _tokenMatchesIdentifier(token.next.next)) {
+ return true;
+ } else if (_tokenMatchesKeyword(startToken, Keyword.VOID)) {
+ // The keyword 'void' isn't a valid identifier, so it should be assumed to
+ // be a type name.
+ return true;
+ } else if (startToken.next != token &&
+ !_tokenMatches(token, TokenType.OPEN_PAREN)) {
+ // The type is more than a simple identifier, so it should be assumed to
+ // be a type name.
+ return true;
}
- return _parseInstanceCreationExpression(keyword);
+ return false;
}
- ConstructorDeclaration _parseConstructor(
- CommentAndMetadata commentAndMetadata,
- Token externalKeyword,
- Token constKeyword,
- Token factoryKeyword,
- SimpleIdentifier returnType,
- Token period,
- SimpleIdentifier name,
- FormalParameterList parameters) {
- bool bodyAllowed = externalKeyword == null;
- Token separator = null;
- List<ConstructorInitializer> initializers = null;
- if (_matches(TokenType.COLON)) {
- separator = getAndAdvance();
- initializers = <ConstructorInitializer>[];
- do {
- Keyword keyword = _currentToken.keyword;
- if (keyword == Keyword.THIS) {
- TokenType nextType = _peek().type;
- if (nextType == TokenType.OPEN_PAREN) {
- bodyAllowed = false;
- initializers.add(_parseRedirectingConstructorInvocation(false));
- } else if (nextType == TokenType.PERIOD &&
- _tokenMatches(_peekAt(3), TokenType.OPEN_PAREN)) {
- bodyAllowed = false;
- initializers.add(_parseRedirectingConstructorInvocation(true));
- } else {
- initializers.add(_parseConstructorFieldInitializer(true));
- }
- } else if (keyword == Keyword.SUPER) {
- initializers.add(_parseSuperConstructorInvocation());
- } else if (_matches(TokenType.OPEN_CURLY_BRACKET) ||
- _matches(TokenType.FUNCTION)) {
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_INITIALIZER);
- } else if (_enableAssertInitializer &&
- _matchesKeyword(Keyword.ASSERT)) {
- _parseAssertInitializer();
- } else {
- initializers.add(_parseConstructorFieldInitializer(false));
- }
- } while (_optional(TokenType.COMMA));
- if (factoryKeyword != null) {
- _reportErrorForToken(
- ParserErrorCode.FACTORY_WITH_INITIALIZERS, factoryKeyword);
- }
- }
- ConstructorName redirectedConstructor = null;
- FunctionBody body;
- if (_matches(TokenType.EQ)) {
- separator = getAndAdvance();
- redirectedConstructor = parseConstructorName();
- body = new EmptyFunctionBody(_expect(TokenType.SEMICOLON));
- if (factoryKeyword == null) {
- _reportErrorForNode(
- ParserErrorCode.REDIRECTION_IN_NON_FACTORY_CONSTRUCTOR,
- redirectedConstructor);
- }
- } else {
- body = _parseFunctionBody(
- true, ParserErrorCode.MISSING_FUNCTION_BODY, false);
- if (constKeyword != null &&
- factoryKeyword != null &&
- externalKeyword == null) {
- _reportErrorForToken(ParserErrorCode.CONST_FACTORY, factoryKeyword);
- } else if (body is EmptyFunctionBody) {
- if (factoryKeyword != null &&
- externalKeyword == null &&
- _parseFunctionBodies) {
- _reportErrorForToken(
- ParserErrorCode.FACTORY_WITHOUT_BODY, factoryKeyword);
- }
- } else {
- if (constKeyword != null) {
- _reportErrorForNode(
- ParserErrorCode.CONST_CONSTRUCTOR_WITH_BODY, body);
- } else if (externalKeyword != null) {
- _reportErrorForNode(
- ParserErrorCode.EXTERNAL_CONSTRUCTOR_WITH_BODY, body);
- } else if (!bodyAllowed) {
- _reportErrorForNode(
- ParserErrorCode.REDIRECTING_CONSTRUCTOR_WITH_BODY, body);
- }
- }
- }
- return new ConstructorDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- externalKeyword,
- constKeyword,
- factoryKeyword,
- returnType,
- period,
- name,
- parameters,
- separator,
- initializers,
- redirectedConstructor,
- body);
+ /**
+ * Increments the error reporting lock level. If level is more than `0`, then
+ * [reportError] wont report any error.
+ */
+ void _lockErrorListener() {
+ _errorListenerLock++;
}
/**
- * 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*
+ * Return `true` if the current token has the given [type]. Note that the
+ * method [_matchesGt] should be used if the argument to this method would be
+ * [TokenType.GT].
*/
- ConstructorFieldInitializer _parseConstructorFieldInitializer(bool hasThis) {
- Token keywordToken = null;
- Token period = null;
- if (hasThis) {
- keywordToken = getAndAdvance();
- period = _expect(TokenType.PERIOD);
- }
- SimpleIdentifier fieldName = parseSimpleIdentifier();
- Token equals = null;
- TokenType type = _currentToken.type;
- if (type == TokenType.EQ) {
- equals = getAndAdvance();
- } else {
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_ASSIGNMENT_IN_INITIALIZER);
- 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();
- if (_matches(TokenType.PERIOD_PERIOD)) {
- List<Expression> cascadeSections = <Expression>[];
- do {
- Expression section = _parseCascadeSection();
- if (section != null) {
- cascadeSections.add(section);
- }
- } while (_matches(TokenType.PERIOD_PERIOD));
- expression = new CascadeExpression(expression, cascadeSections);
- }
- return new ConstructorFieldInitializer(
- keywordToken, period, fieldName, equals, expression);
- } finally {
- _inInitializer = wasInInitializer;
+ bool _matches(TokenType type) => _currentToken.type == type;
+
+ /**
+ * Return `true` if the current token has a type of [TokenType.GT]. Note that
+ * this method, unlike other variants, will modify the token stream if
+ * possible to match desired type. In particular, if the next token is either
+ * a '>>' or '>>>', the token stream will be re-written and `true` will be
+ * returned.
+ */
+ bool _matchesGt() {
+ TokenType currentType = _currentToken.type;
+ if (currentType == TokenType.GT) {
+ return true;
+ } else if (currentType == TokenType.GT_GT) {
+ Token first = _createToken(_currentToken, TokenType.GT);
+ Token second = new Token(TokenType.GT, _currentToken.offset + 1);
+ second.setNext(_currentToken.next);
+ first.setNext(second);
+ _currentToken.previous.setNext(first);
+ _currentToken = first;
+ return true;
+ } else if (currentType == TokenType.GT_EQ) {
+ Token first = _createToken(_currentToken, TokenType.GT);
+ Token second = new Token(TokenType.EQ, _currentToken.offset + 1);
+ second.setNext(_currentToken.next);
+ first.setNext(second);
+ _currentToken.previous.setNext(first);
+ _currentToken = first;
+ return true;
+ } else if (currentType == TokenType.GT_GT_EQ) {
+ int offset = _currentToken.offset;
+ Token first = _createToken(_currentToken, TokenType.GT);
+ Token second = new Token(TokenType.GT, offset + 1);
+ Token third = new Token(TokenType.EQ, offset + 2);
+ third.setNext(_currentToken.next);
+ second.setNext(third);
+ first.setNext(second);
+ _currentToken.previous.setNext(first);
+ _currentToken = first;
+ return true;
}
+ return false;
}
/**
- * Parse a continue statement. Return the continue statement that was parsed.
- *
- * This method assumes that the current token matches `Keyword.CONTINUE`.
- *
- * continueStatement ::=
- * 'continue' identifier? ';'
+ * Return `true` if the current token is a valid identifier. Valid identifiers
+ * include built-in identifiers (pseudo-keywords).
*/
- Statement _parseContinueStatement() {
- Token continueKeyword = getAndAdvance();
- if (!_inLoop && !_inSwitch) {
- _reportErrorForToken(
- ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP, continueKeyword);
- }
- SimpleIdentifier label = null;
- if (_matchesIdentifier()) {
- label = _parseSimpleIdentifierUnchecked();
- }
- if (_inSwitch && !_inLoop && label == null) {
- _reportErrorForToken(
- ParserErrorCode.CONTINUE_WITHOUT_LABEL_IN_CASE, continueKeyword);
+ bool _matchesIdentifier() => _tokenMatchesIdentifier(_currentToken);
+
+ /**
+ * Return `true` if the current token matches the given [keyword].
+ */
+ bool _matchesKeyword(Keyword keyword) =>
+ _tokenMatchesKeyword(_currentToken, keyword);
+
+ /**
+ * Return `true` if the current token matches the given [identifier].
+ */
+ bool _matchesString(String identifier) =>
+ _currentToken.type == TokenType.IDENTIFIER &&
+ _currentToken.lexeme == identifier;
+
+ /**
+ * If the current token has the given [type], then advance to the next token
+ * and return `true`. Otherwise, return `false` without advancing. This method
+ * should not be invoked with an argument value of [TokenType.GT].
+ */
+ bool _optional(TokenType type) {
+ if (_currentToken.type == type) {
+ _advance();
+ return true;
}
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new ContinueStatement(continueKeyword, label, semicolon);
+ return false;
}
/**
- * Parse a directive. The [commentAndMetadata] is the metadata to be
- * associated with the directive. Return the directive that was parsed.
- *
- * directive ::=
- * exportDirective
- * | libraryDirective
- * | importDirective
- * | partDirective
+ * 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.
*/
- Directive _parseDirective(CommentAndMetadata commentAndMetadata) {
- if (_matchesKeyword(Keyword.IMPORT)) {
- return _parseImportDirective(commentAndMetadata);
- } else if (_matchesKeyword(Keyword.EXPORT)) {
- return _parseExportDirective(commentAndMetadata);
- } else if (_matchesKeyword(Keyword.LIBRARY)) {
- return _parseLibraryDirective(commentAndMetadata);
- } else if (_matchesKeyword(Keyword.PART)) {
- 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.
- throw new StateError(
- "parseDirective invoked in an invalid state; currentToken = $_currentToken");
+ 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 the script tag and directives in a compilation unit until the first
- * non-directive is encountered. Return the compilation unit that was parsed.
+ * Parse an assert within a constructor's initializer list. Return the assert.
*
- * compilationUnit ::=
- * scriptTag? directive*
+ * This method assumes that the current token matches `Keyword.ASSERT`.
+ *
+ * assertInitializer ::=
+ * 'assert' '(' expression [',' expression] ')'
*/
- CompilationUnit _parseDirectives() {
- Token firstToken = _currentToken;
- ScriptTag scriptTag = null;
- if (_matches(TokenType.SCRIPT_TAG)) {
- scriptTag = new ScriptTag(getAndAdvance());
- }
- List<Directive> directives = <Directive>[];
- while (!_matches(TokenType.EOF)) {
- CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
- 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();
- } else {
- while (!_matches(TokenType.EOF)) {
- _advance();
- }
- return new CompilationUnit(
- firstToken, scriptTag, directives, null, _currentToken);
- }
+ void _parseAssertInitializer() {
+ // TODO(brianwilkerson) Capture the syntax in the AST using a new class,
+ // such as AssertInitializer
+ Token keyword = getAndAdvance();
+ Token leftParen = _expect(TokenType.OPEN_PAREN);
+ Expression expression = parseExpression2();
+ Token comma;
+ Expression message;
+ if (_matches(TokenType.COMMA)) {
+ comma = getAndAdvance();
+ message = parseExpression2();
}
- return new CompilationUnit(
- firstToken, scriptTag, directives, null, _currentToken);
+ Token rightParen = _expect(TokenType.CLOSE_PAREN);
+// return new AssertInitializer(
+// keyword, leftParen, expression, comma, message, rightParen);
}
/**
- * 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*
+ * 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.
*/
- Comment _parseDocumentationComment(List<DocumentationCommentToken> tokens) {
- if (tokens == null) {
- return null;
+ 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
+ // than an identifier and/or is followed by an assignableSelector.
+ //
+ Expression expression = parsePrimaryExpression();
+ bool isOptional = primaryAllowed || expression is SimpleIdentifier;
+ while (true) {
+ while (_isLikelyArgumentList()) {
+ TypeArgumentList typeArguments = _parseOptionalTypeArguments();
+ ArgumentList argumentList = parseArgumentList();
+ Expression currentExpression = expression;
+ if (currentExpression is SimpleIdentifier) {
+ expression = new MethodInvocation(
+ null, null, currentExpression, typeArguments, argumentList);
+ } else if (currentExpression is PrefixedIdentifier) {
+ expression = new MethodInvocation(
+ currentExpression.prefix,
+ currentExpression.period,
+ currentExpression.identifier,
+ typeArguments,
+ argumentList);
+ } else if (currentExpression is PropertyAccess) {
+ expression = new MethodInvocation(
+ currentExpression.target,
+ currentExpression.operator,
+ currentExpression.propertyName,
+ typeArguments,
+ argumentList);
+ } else {
+ expression = new FunctionExpressionInvocation(
+ expression, typeArguments, argumentList);
+ }
+ if (!primaryAllowed) {
+ isOptional = false;
+ }
+ }
+ Expression selectorExpression = _parseAssignableSelector(
+ expression, isOptional || (expression is PrefixedIdentifier));
+ if (identical(selectorExpression, expression)) {
+ if (!isOptional && (expression is PrefixedIdentifier)) {
+ PrefixedIdentifier identifier = expression as PrefixedIdentifier;
+ expression = new PropertyAccess(
+ identifier.prefix, identifier.period, identifier.identifier);
+ }
+ return expression;
+ }
+ expression = selectorExpression;
+ isOptional = true;
}
- List<CommentReference> references = _parseCommentReferences(tokens);
- return Comment.createDocumentationCommentWithReferences(tokens, references);
}
/**
- * Parse a documentation comment. Return the documentation comment that was
- * parsed, or `null` if there was no comment.
+ * Parse an assignable selector. The [prefix] is the expression preceding the
+ * selector. The [optional] is `true` if the selector is optional. Return the
+ * assignable selector that was parsed, or the original prefix if there was no
+ * assignable selector. If [allowConditional] is false, then the '?.'
+ * operator will still be parsed, but a parse error will be generated.
*
- * documentationComment ::=
- * multiLineComment?
- * | singleLineComment*
+ * unconditionalAssignableSelector ::=
+ * '[' expression ']'
+ * | '.' identifier
+ *
+ * assignableSelector ::=
+ * unconditionalAssignableSelector
+ * | '?.' identifier
*/
- List<DocumentationCommentToken> _parseDocumentationCommentTokens() {
- List<DocumentationCommentToken> tokens = <DocumentationCommentToken>[];
- CommentToken commentToken = _currentToken.precedingComments;
- while (commentToken != null) {
- if (commentToken is DocumentationCommentToken) {
- if (tokens.isNotEmpty) {
- if (commentToken.type == TokenType.SINGLE_LINE_COMMENT) {
- if (tokens[0].type != TokenType.SINGLE_LINE_COMMENT) {
- tokens.clear();
- }
- } else {
- tokens.clear();
- }
+ Expression _parseAssignableSelector(Expression prefix, bool optional,
+ {bool allowConditional: true}) {
+ TokenType type = _currentToken.type;
+ if (type == TokenType.OPEN_SQUARE_BRACKET) {
+ Token leftBracket = getAndAdvance();
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = false;
+ try {
+ Expression index = parseExpression2();
+ Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
+ return new IndexExpression.forTarget(
+ prefix, leftBracket, index, rightBracket);
+ } finally {
+ _inInitializer = wasInInitializer;
+ }
+ } else {
+ bool isQuestionPeriod = type == TokenType.QUESTION_PERIOD;
+ if (type == TokenType.PERIOD || isQuestionPeriod) {
+ if (isQuestionPeriod && !allowConditional) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.INVALID_OPERATOR_FOR_SUPER,
+ [_currentToken.lexeme]);
}
- tokens.add(commentToken);
+ Token operator = getAndAdvance();
+ return new PropertyAccess(prefix, operator, parseSimpleIdentifier());
+ } else {
+ if (!optional) {
+ // Report the missing selector.
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR);
+ }
+ return prefix;
}
- commentToken = commentToken.next;
}
- 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`.
+ * 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.
*
- * doStatement ::=
- * 'do' statement 'while' '(' expression ')' ';'
+ * block ::=
+ * '{' statements '}'
*/
- Statement _parseDoStatement() {
- bool wasInLoop = _inLoop;
- _inLoop = true;
- try {
- Token doKeyword = getAndAdvance();
- Statement body = parseStatement2();
- Token whileKeyword = _expectKeyword(Keyword.WHILE);
- Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
- Expression condition = parseExpression2();
- Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new DoStatement(doKeyword, body, whileKeyword, leftParenthesis,
- condition, rightParenthesis, semicolon);
- } finally {
- _inLoop = wasInLoop;
+ 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 dotted name. Return the dotted name that was parsed.
+ * Parse a list of class members. The [className] is the name of the class
+ * whose members are being parsed. The [closingBracket] is the closing bracket
+ * for the class, or `null` if the closing bracket is missing. Return the list
+ * of class members that were parsed.
*
- * dottedName ::=
- * identifier ('.' identifier)*
+ * classMembers ::=
+ * (metadata memberDefinition)*
*/
- DottedName _parseDottedName() {
- List<SimpleIdentifier> components = <SimpleIdentifier>[
- parseSimpleIdentifier()
- ];
- while (_optional(TokenType.PERIOD)) {
- components.add(parseSimpleIdentifier());
+ List<ClassMember> _parseClassMembers(String className, Token closingBracket) {
+ List<ClassMember> members = <ClassMember>[];
+ Token memberStart = _currentToken;
+ TokenType type = _currentToken.type;
+ Keyword keyword = _currentToken.keyword;
+ while (type != TokenType.EOF &&
+ type != TokenType.CLOSE_CURLY_BRACKET &&
+ (closingBracket != null ||
+ (keyword != Keyword.CLASS && keyword != Keyword.TYPEDEF))) {
+ if (type == TokenType.SEMICOLON) {
+ _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
+ [_currentToken.lexeme]);
+ _advance();
+ } else {
+ ClassMember member = parseClassMember(className);
+ if (member != null) {
+ members.add(member);
+ }
+ }
+ if (identical(_currentToken, memberStart)) {
+ _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
+ [_currentToken.lexeme]);
+ _advance();
+ }
+ memberStart = _currentToken;
+ type = _currentToken.type;
+ keyword = _currentToken.keyword;
}
- return new DottedName(components);
+ return members;
}
/**
- * 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
+ * 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.
*
- * Actual:
+ * classTypeAlias ::=
+ * identifier typeParameters? '=' 'abstract'? mixinApplication
*
- * enumConstant ::=
- * metadata id
+ * mixinApplication ::=
+ * type withClause implementsClause? ';'
*/
- EnumConstantDeclaration _parseEnumConstantDeclaration() {
- CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
- SimpleIdentifier name;
- if (_matchesIdentifier()) {
- name = _parseSimpleIdentifierUnchecked(isDeclaration: true);
+ ClassTypeAlias _parseClassTypeAliasAfterName(
+ CommentAndMetadata commentAndMetadata,
+ Token abstractKeyword,
+ Token classKeyword,
+ SimpleIdentifier className,
+ TypeParameterList typeParameters) {
+ Token equals = _expect(TokenType.EQ);
+ TypeName superclass = parseTypeName(false);
+ WithClause withClause = null;
+ if (_matchesKeyword(Keyword.WITH)) {
+ withClause = parseWithClause();
} else {
- name = _createSyntheticIdentifier();
+ _reportErrorForCurrentToken(
+ ParserErrorCode.EXPECTED_TOKEN, [Keyword.WITH.syntax]);
}
- if (commentAndMetadata.hasMetadata) {
- _reportErrorForNode(ParserErrorCode.ANNOTATION_ON_ENUM_CONSTANT,
- commentAndMetadata.metadata[0]);
+ ImplementsClause implementsClause = null;
+ if (_matchesKeyword(Keyword.IMPLEMENTS)) {
+ implementsClause = parseImplementsClause();
}
- return new EnumConstantDeclaration(
- commentAndMetadata.comment, commentAndMetadata.metadata, name);
+ Token semicolon;
+ if (_matches(TokenType.SEMICOLON)) {
+ semicolon = getAndAdvance();
+ } else {
+ if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.EXPECTED_TOKEN, [TokenType.SEMICOLON.lexeme]);
+ Token leftBracket = getAndAdvance();
+ _parseClassMembers(className.name, _getEndToken(leftBracket));
+ _expect(TokenType.CLOSE_CURLY_BRACKET);
+ } else {
+ _reportErrorForToken(ParserErrorCode.EXPECTED_TOKEN,
+ _currentToken.previous, [TokenType.SEMICOLON.lexeme]);
+ }
+ semicolon = _createSyntheticToken(TokenType.SEMICOLON);
+ }
+ return new ClassTypeAlias(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ classKeyword,
+ className,
+ typeParameters,
+ equals,
+ abstractKeyword,
+ superclass,
+ withClause,
+ implementsClause,
+ semicolon);
}
/**
- * Parse an enum declaration. The [commentAndMetadata] is the metadata to be
- * associated with the member. Return the enum declaration that was parsed.
+ * Parse all of the comment references occurring in the given array of
+ * documentation comments. The [tokens] are the comment tokens representing
+ * the documentation comments to be parsed. Return the comment references that
+ * were parsed.
*
- * This method assumes that the current token matches `Keyword.ENUM`.
+ * commentReference ::=
+ * '[' 'new'? qualified ']' libraryReference?
*
- * enumType ::=
- * metadata 'enum' id '{' id (',' id)* (',')? '}'
+ * libraryReference ::=
+ * '(' stringLiteral ')'
*/
- EnumDeclaration _parseEnumDeclaration(CommentAndMetadata commentAndMetadata) {
- Token keyword = getAndAdvance();
- SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
- Token leftBracket = null;
- List<EnumConstantDeclaration> constants = <EnumConstantDeclaration>[];
- Token rightBracket = null;
- if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
- leftBracket = getAndAdvance();
- if (_matchesIdentifier() || _matches(TokenType.AT)) {
- constants.add(_parseEnumConstantDeclaration());
- } else if (_matches(TokenType.COMMA) &&
- _tokenMatchesIdentifier(_peek())) {
- constants.add(_parseEnumConstantDeclaration());
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
- } else {
- constants.add(_parseEnumConstantDeclaration());
- _reportErrorForCurrentToken(ParserErrorCode.EMPTY_ENUM_BODY);
+ List<CommentReference> _parseCommentReferences(
+ List<DocumentationCommentToken> tokens) {
+ List<CommentReference> references = <CommentReference>[];
+ bool isInGitHubCodeBlock = false;
+ for (DocumentationCommentToken token in tokens) {
+ String comment = token.lexeme;
+ // Skip GitHub code blocks.
+ // https://help.github.com/articles/creating-and-highlighting-code-blocks/
+ if (tokens.length != 1) {
+ if (comment.indexOf('```') != -1) {
+ isInGitHubCodeBlock = !isInGitHubCodeBlock;
+ }
+ if (isInGitHubCodeBlock) {
+ continue;
+ }
}
- while (_optional(TokenType.COMMA)) {
- if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
- break;
+ // Remove GitHub include code.
+ comment = _removeGitHubInlineCode(comment);
+ // Find references.
+ int length = comment.length;
+ List<List<int>> codeBlockRanges = _getCodeBlockRanges(comment);
+ int leftIndex = comment.indexOf('[');
+ while (leftIndex >= 0 && leftIndex + 1 < length) {
+ List<int> range = _findRange(codeBlockRanges, leftIndex);
+ if (range == null) {
+ int nameOffset = token.offset + leftIndex + 1;
+ int rightIndex = comment.indexOf(']', leftIndex);
+ if (rightIndex >= 0) {
+ int firstChar = comment.codeUnitAt(leftIndex + 1);
+ if (firstChar != 0x27 && firstChar != 0x22) {
+ if (_isLinkText(comment, rightIndex)) {
+ // TODO(brianwilkerson) Handle the case where there's a library
+ // URI in the link text.
+ } else {
+ CommentReference reference = parseCommentReference(
+ comment.substring(leftIndex + 1, rightIndex), nameOffset);
+ if (reference != null) {
+ references.add(reference);
+ token.references.add(reference.beginToken);
+ }
+ }
+ }
+ } else {
+ // terminating ']' is not typed yet
+ int charAfterLeft = comment.codeUnitAt(leftIndex + 1);
+ Token nameToken;
+ if (Character.isLetterOrDigit(charAfterLeft)) {
+ int nameEnd = StringUtilities.indexOfFirstNotLetterDigit(
+ comment, leftIndex + 1);
+ String name = comment.substring(leftIndex + 1, nameEnd);
+ nameToken =
+ new StringToken(TokenType.IDENTIFIER, name, nameOffset);
+ } else {
+ nameToken = new SyntheticStringToken(
+ TokenType.IDENTIFIER, '', nameOffset);
+ }
+ nameToken.setNext(new SimpleToken(TokenType.EOF, nameToken.end));
+ references.add(
+ new CommentReference(null, new SimpleIdentifier(nameToken)));
+ token.references.add(nameToken);
+ // next character
+ rightIndex = leftIndex + 1;
+ }
+ leftIndex = comment.indexOf('[', rightIndex);
+ } else {
+ leftIndex = comment.indexOf('[', range[1]);
}
- constants.add(_parseEnumConstantDeclaration());
}
- rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
- } else {
- leftBracket = _createSyntheticToken(TokenType.OPEN_CURLY_BRACKET);
- rightBracket = _createSyntheticToken(TokenType.CLOSE_CURLY_BRACKET);
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_ENUM_BODY);
}
- return new EnumDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- keyword,
- name,
- leftBracket,
- constants,
- rightBracket);
+ return references;
}
/**
- * Parse an equality expression. Return the equality expression that was
- * parsed.
- *
- * equalityExpression ::=
- * relationalExpression (equalityOperator relationalExpression)?
- * | 'super' equalityOperator relationalExpression
+ * Parse a list of configurations. Return the configurations that were parsed,
+ * or `null` if there are no configurations.
*/
- Expression _parseEqualityExpression() {
- Expression expression;
- if (_currentToken.keyword == Keyword.SUPER &&
- _currentToken.next.type.isEqualityOperator) {
- expression = new SuperExpression(getAndAdvance());
- } else {
- expression = _parseRelationalExpression();
+ List<Configuration> _parseConfigurations() {
+ List<Configuration> configurations = null;
+ while (_matchesKeyword(Keyword.IF)) {
+ configurations ??= <Configuration>[];
+ configurations.add(parseConfiguration());
}
- bool leftEqualityExpression = false;
- while (_currentToken.type.isEqualityOperator) {
- if (leftEqualityExpression) {
+ return configurations;
+ }
+
+ ConstructorDeclaration _parseConstructor(
+ CommentAndMetadata commentAndMetadata,
+ Token externalKeyword,
+ Token constKeyword,
+ Token factoryKeyword,
+ SimpleIdentifier returnType,
+ Token period,
+ SimpleIdentifier name,
+ FormalParameterList parameters) {
+ bool bodyAllowed = externalKeyword == null;
+ Token separator = null;
+ List<ConstructorInitializer> initializers = null;
+ if (_matches(TokenType.COLON)) {
+ separator = getAndAdvance();
+ initializers = <ConstructorInitializer>[];
+ do {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.THIS) {
+ TokenType nextType = _peek().type;
+ if (nextType == TokenType.OPEN_PAREN) {
+ bodyAllowed = false;
+ initializers.add(_parseRedirectingConstructorInvocation(false));
+ } else if (nextType == TokenType.PERIOD &&
+ _tokenMatches(_peekAt(3), TokenType.OPEN_PAREN)) {
+ bodyAllowed = false;
+ initializers.add(_parseRedirectingConstructorInvocation(true));
+ } else {
+ initializers.add(_parseConstructorFieldInitializer(true));
+ }
+ } else if (keyword == Keyword.SUPER) {
+ initializers.add(parseSuperConstructorInvocation());
+ } else if (_matches(TokenType.OPEN_CURLY_BRACKET) ||
+ _matches(TokenType.FUNCTION)) {
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_INITIALIZER);
+ } else if (_enableAssertInitializer &&
+ _matchesKeyword(Keyword.ASSERT)) {
+ _parseAssertInitializer();
+ } else {
+ initializers.add(_parseConstructorFieldInitializer(false));
+ }
+ } while (_optional(TokenType.COMMA));
+ if (factoryKeyword != null) {
+ _reportErrorForToken(
+ ParserErrorCode.FACTORY_WITH_INITIALIZERS, factoryKeyword);
+ }
+ }
+ ConstructorName redirectedConstructor = null;
+ FunctionBody body;
+ if (_matches(TokenType.EQ)) {
+ separator = getAndAdvance();
+ redirectedConstructor = parseConstructorName();
+ body = new EmptyFunctionBody(_expect(TokenType.SEMICOLON));
+ if (factoryKeyword == null) {
_reportErrorForNode(
- ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND, expression);
+ ParserErrorCode.REDIRECTION_IN_NON_FACTORY_CONSTRUCTOR,
+ redirectedConstructor);
+ }
+ } else {
+ body =
+ parseFunctionBody(true, ParserErrorCode.MISSING_FUNCTION_BODY, false);
+ if (constKeyword != null &&
+ factoryKeyword != null &&
+ externalKeyword == null) {
+ _reportErrorForToken(ParserErrorCode.CONST_FACTORY, factoryKeyword);
+ } else if (body is EmptyFunctionBody) {
+ if (factoryKeyword != null &&
+ externalKeyword == null &&
+ _parseFunctionBodies) {
+ _reportErrorForToken(
+ ParserErrorCode.FACTORY_WITHOUT_BODY, factoryKeyword);
+ }
+ } else {
+ if (constKeyword != null) {
+ _reportErrorForNode(
+ ParserErrorCode.CONST_CONSTRUCTOR_WITH_BODY, body);
+ } else if (externalKeyword != null) {
+ _reportErrorForNode(
+ ParserErrorCode.EXTERNAL_CONSTRUCTOR_WITH_BODY, body);
+ } else if (!bodyAllowed) {
+ _reportErrorForNode(
+ ParserErrorCode.REDIRECTING_CONSTRUCTOR_WITH_BODY, body);
+ }
}
- expression = new BinaryExpression(
- expression, getAndAdvance(), _parseRelationalExpression());
- leftEqualityExpression = true;
}
- return expression;
- }
-
- /**
- * 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 = getAndAdvance();
- StringLiteral libraryUri = _parseUri();
- List<Configuration> configurations = _parseConfigurations();
- List<Combinator> combinators = _parseCombinators();
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new ExportDirective(
+ return new ConstructorDeclaration(
commentAndMetadata.comment,
commentAndMetadata.metadata,
- exportKeyword,
- libraryUri,
- configurations,
- combinators,
- semicolon);
- }
-
- /**
- * Parse a list of expressions. Return the expression that was parsed.
- *
- * expressionList ::=
- * expression (',' expression)*
- */
- List<Expression> _parseExpressionList() {
- List<Expression> expressions = <Expression>[parseExpression2()];
- while (_optional(TokenType.COMMA)) {
- expressions.add(parseExpression2());
- }
- return expressions;
+ externalKeyword,
+ constKeyword,
+ factoryKeyword,
+ returnType,
+ period,
+ name,
+ parameters,
+ separator,
+ initializers,
+ redirectedConstructor,
+ body);
}
/**
- * Parse the 'final', 'const', 'var' or type preceding a variable declaration.
- * The [optional] is `true` if the keyword and type are optional. Return the
- * 'final', 'const', 'var' or type 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.
*
- * finalConstVarOrType ::=
- * 'final' type?
- * | 'const' type?
- * | 'var'
- * | type
+ * fieldInitializer:
+ * ('this' '.')? identifier '=' conditionalExpression cascadeSection*
*/
- FinalConstVarOrType _parseFinalConstVarOrType(bool optional) {
+ ConstructorFieldInitializer _parseConstructorFieldInitializer(bool hasThis) {
Token keywordToken = null;
- TypeName type = null;
- Keyword keyword = _currentToken.keyword;
- if (keyword == Keyword.FINAL || keyword == Keyword.CONST) {
+ Token period = null;
+ if (hasThis) {
keywordToken = getAndAdvance();
- if (_isTypedIdentifier(_currentToken)) {
- type = parseTypeName(false);
+ period = _expect(TokenType.PERIOD);
+ }
+ SimpleIdentifier fieldName = parseSimpleIdentifier();
+ Token equals = null;
+ TokenType type = _currentToken.type;
+ if (type == TokenType.EQ) {
+ equals = getAndAdvance();
+ } else {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MISSING_ASSIGNMENT_IN_INITIALIZER);
+ Keyword keyword = _currentToken.keyword;
+ if (keyword != Keyword.THIS &&
+ keyword != Keyword.SUPER &&
+ type != TokenType.OPEN_CURLY_BRACKET &&
+ type != TokenType.FUNCTION) {
+ equals = _createSyntheticToken(TokenType.EQ);
} else {
- // Support `final/*=T*/ x;`
- type = _parseOptionalTypeNameComment();
+ return new ConstructorFieldInitializer(keywordToken, period, fieldName,
+ _createSyntheticToken(TokenType.EQ), createSyntheticIdentifier());
}
- } else if (keyword == Keyword.VAR) {
- keywordToken = getAndAdvance();
- // Support `var/*=T*/ x;`
- type = _parseOptionalTypeNameComment();
- if (type != null) {
- // Clear the keyword to prevent an error.
- keywordToken = null;
+ }
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = true;
+ try {
+ Expression expression = parseConditionalExpression();
+ if (_matches(TokenType.PERIOD_PERIOD)) {
+ List<Expression> cascadeSections = <Expression>[];
+ do {
+ Expression section = parseCascadeSection();
+ if (section != null) {
+ cascadeSections.add(section);
+ }
+ } while (_matches(TokenType.PERIOD_PERIOD));
+ expression = new CascadeExpression(expression, cascadeSections);
}
- } else if (_isTypedIdentifier(_currentToken)) {
- type = parseReturnType();
- } else if (!optional) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE);
- } else {
- // Support parameters such as `(/*=K*/ key, /*=V*/ value)`
- // This is not supported if the type is required.
- type = _parseOptionalTypeNameComment();
+ return new ConstructorFieldInitializer(
+ keywordToken, period, fieldName, equals, expression);
+ } finally {
+ _inInitializer = wasInInitializer;
}
- return new FinalConstVarOrType(keywordToken, type);
}
/**
- * Parse a formal parameter. At most one of `isOptional` and `isNamed` can be
- * `true`. The [kind] is the kind of parameter being expected based on the
- * presence or absence of group delimiters. Return the formal parameter that
- * was parsed.
- *
- * defaultFormalParameter ::=
- * normalFormalParameter ('=' expression)?
+ * Parse a directive. The [commentAndMetadata] is the metadata to be
+ * associated with the directive. Return the directive that was parsed.
*
- * defaultNamedParameter ::=
- * normalFormalParameter (':' expression)?
+ * directive ::=
+ * exportDirective
+ * | libraryDirective
+ * | importDirective
+ * | partDirective
*/
- FormalParameter _parseFormalParameter(ParameterKind kind) {
- NormalFormalParameter parameter = parseNormalFormalParameter();
- 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, separator);
- } else if (kind == ParameterKind.REQUIRED) {
- _reportErrorForNode(
- ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP, parameter);
- }
- return new DefaultFormalParameter(
- 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,
- separator);
- } else if (kind == ParameterKind.REQUIRED) {
- _reportErrorForNode(
- ParserErrorCode.NAMED_PARAMETER_OUTSIDE_GROUP, parameter);
- }
- return new DefaultFormalParameter(
- parameter, kind, separator, defaultValue);
- } else if (kind != ParameterKind.REQUIRED) {
- return new DefaultFormalParameter(parameter, kind, null, null);
+ Directive _parseDirective(CommentAndMetadata commentAndMetadata) {
+ if (_matchesKeyword(Keyword.IMPORT)) {
+ return _parseImportDirective(commentAndMetadata);
+ } else if (_matchesKeyword(Keyword.EXPORT)) {
+ return _parseExportDirective(commentAndMetadata);
+ } else if (_matchesKeyword(Keyword.LIBRARY)) {
+ return _parseLibraryDirective(commentAndMetadata);
+ } else if (_matchesKeyword(Keyword.PART)) {
+ 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.
+ throw new StateError(
+ "parseDirective invoked in an invalid state; currentToken = $_currentToken");
}
- return parameter;
}
/**
- * Parse a list of formal parameters given that the list starts with the given
- * [leftParenthesis]. Return the formal parameters that were parsed.
+ * Parse an enum constant declaration. Return the enum constant declaration
+ * that was parsed.
+ *
+ * Specified:
+ *
+ * enumConstant ::=
+ * id
+ *
+ * Actual:
+ *
+ * enumConstant ::=
+ * metadata id
*/
- FormalParameterList _parseFormalParameterListAfterParen(
- Token leftParenthesis) {
- if (_matches(TokenType.CLOSE_PAREN)) {
- return new FormalParameterList(
- leftParenthesis, null, null, null, getAndAdvance());
+ EnumConstantDeclaration _parseEnumConstantDeclaration() {
+ CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
+ SimpleIdentifier name;
+ if (_matchesIdentifier()) {
+ name = _parseSimpleIdentifierUnchecked(isDeclaration: true);
+ } else {
+ name = createSyntheticIdentifier();
}
- //
- // 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;
-
- // Advance past trailing commas as appropriate.
- if (type == TokenType.COMMA) {
- // Only parse commas trailing normal (non-positional/named) params.
- if (rightSquareBracket == null && rightCurlyBracket == null) {
- Token next = _peek();
- if (next.type == TokenType.CLOSE_PAREN ||
- next.type == TokenType.CLOSE_CURLY_BRACKET ||
- next.type == TokenType.CLOSE_SQUARE_BRACKET) {
- _advance();
- 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 (commentAndMetadata.hasMetadata) {
+ _reportErrorForNode(ParserErrorCode.ANNOTATION_ON_ENUM_CONSTANT,
+ commentAndMetadata.metadata[0]);
}
- if (leftCurlyBracket != null && rightCurlyBracket == null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
+ return new EnumConstantDeclaration(
+ commentAndMetadata.comment, commentAndMetadata.metadata, name);
+ }
+
+ /**
+ * Parse an equality expression. Return the equality expression that was
+ * parsed.
+ *
+ * equalityExpression ::=
+ * relationalExpression (equalityOperator relationalExpression)?
+ * | 'super' equalityOperator relationalExpression
+ */
+ Expression _parseEqualityExpression() {
+ Expression expression;
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type.isEqualityOperator) {
+ expression = new SuperExpression(getAndAdvance());
+ } else {
+ expression = parseRelationalExpression();
}
- //
- // Build the parameter list.
- //
- leftSquareBracket ??= leftCurlyBracket;
- rightSquareBracket ??= rightCurlyBracket;
- return new FormalParameterList(leftParenthesis, parameters,
- leftSquareBracket, rightSquareBracket, rightParenthesis);
+ bool leftEqualityExpression = false;
+ while (_currentToken.type.isEqualityOperator) {
+ if (leftEqualityExpression) {
+ _reportErrorForNode(
+ ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND, expression);
+ }
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), parseRelationalExpression());
+ leftEqualityExpression = true;
+ }
+ return expression;
}
/**
- * Parse a list of formal parameters. Return the formal parameters that were
- * parsed.
+ * 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 `TokenType.OPEN_PAREN`.
+ * This method assumes that the current token matches `Keyword.EXPORT`.
+ *
+ * exportDirective ::=
+ * metadata 'export' stringLiteral configuration* combinator*';'
*/
- FormalParameterList _parseFormalParameterListUnchecked() {
- return _parseFormalParameterListAfterParen(getAndAdvance());
+ ExportDirective _parseExportDirective(CommentAndMetadata commentAndMetadata) {
+ Token exportKeyword = getAndAdvance();
+ StringLiteral libraryUri = _parseUri();
+ List<Configuration> configurations = _parseConfigurations();
+ List<Combinator> combinators = parseCombinators();
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new ExportDirective(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ exportKeyword,
+ libraryUri,
+ configurations,
+ combinators,
+ semicolon);
}
/**
- * Parse a for statement. Return the for statement that was parsed.
- *
- * forStatement ::=
- * 'for' '(' forLoopParts ')' statement
+ * Parse a formal parameter. At most one of `isOptional` and `isNamed` can be
+ * `true`. The [kind] is the kind of parameter being expected based on the
+ * presence or absence of group delimiters. Return the formal parameter that
+ * was parsed.
*
- * forLoopParts ::=
- * forInitializerStatement expression? ';' expressionList?
- * | declaredIdentifier 'in' expression
- * | identifier 'in' expression
+ * defaultFormalParameter ::=
+ * normalFormalParameter ('=' expression)?
*
- * forInitializerStatement ::=
- * localVariableDeclaration ';'
- * | expression? ';'
+ * defaultNamedParameter ::=
+ * normalFormalParameter (':' expression)?
*/
- Statement _parseForStatement() {
- bool wasInLoop = _inLoop;
- _inLoop = true;
- try {
- Token awaitKeyword = null;
- if (_matchesString(_AWAIT)) {
- awaitKeyword = getAndAdvance();
- }
- Token forKeyword = _expectKeyword(Keyword.FOR);
- Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
- VariableDeclarationList variableList = null;
- Expression initialization = null;
- if (!_matches(TokenType.SEMICOLON)) {
- CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
- if (_matchesIdentifier() &&
- (_tokenMatchesKeyword(_peek(), Keyword.IN) ||
- _tokenMatches(_peek(), TokenType.COLON))) {
- SimpleIdentifier variableName = _parseSimpleIdentifierUnchecked();
- variableList = new VariableDeclarationList(commentAndMetadata.comment,
- commentAndMetadata.metadata, null, null, <VariableDeclaration>[
- new VariableDeclaration(variableName, null, null)
- ]);
- } else if (_isInitializedVariableDeclaration()) {
- variableList =
- _parseVariableDeclarationListAfterMetadata(commentAndMetadata);
- } else {
- initialization = parseExpression2();
- }
- 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;
- SimpleIdentifier identifier = null;
- if (variableList == null) {
- // We found: <expression> 'in'
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_VARIABLE_IN_FOR_EACH);
- } else {
- NodeList<VariableDeclaration> variables = variableList.variables;
- if (variables.length > 1) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MULTIPLE_VARIABLES_IN_FOR_EACH,
- [variables.length.toString()]);
- }
- VariableDeclaration variable = variables[0];
- if (variable.initializer != null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.INITIALIZED_VARIABLE_IN_FOR_EACH);
- }
- Token keyword = variableList.keyword;
- TypeName type = variableList.type;
- if (keyword != null || type != null) {
- loopVariable = new DeclaredIdentifier(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- keyword,
- type,
- new SimpleIdentifier(variable.name.token,
- isDeclaration: true));
- } else {
- if (commentAndMetadata.hasMetadata) {
- // TODO(jwren) metadata isn't allowed before the identifier in
- // "identifier in expression", add warning if commentAndMetadata
- // has content
- }
- identifier = variable.name;
- }
- }
- Token inKeyword = getAndAdvance();
- Expression iterator = parseExpression2();
- Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
- Statement body = parseStatement2();
- if (loopVariable == null) {
- return new ForEachStatement.withReference(
- awaitKeyword,
- forKeyword,
- leftParenthesis,
- identifier,
- inKeyword,
- iterator,
- rightParenthesis,
- body);
- }
- return new ForEachStatement.withDeclaration(
- awaitKeyword,
- forKeyword,
- leftParenthesis,
- loopVariable,
- inKeyword,
- iterator,
- rightParenthesis,
- body);
- }
- }
- if (awaitKeyword != null) {
+ FormalParameter _parseFormalParameter(ParameterKind kind) {
+ NormalFormalParameter parameter = parseNormalFormalParameter();
+ TokenType type = _currentToken.type;
+ if (type == TokenType.EQ) {
+ Token separator = getAndAdvance();
+ Expression defaultValue = parseExpression2();
+ if (kind == ParameterKind.NAMED) {
_reportErrorForToken(
- ParserErrorCode.INVALID_AWAIT_IN_FOR, awaitKeyword);
- }
- Token leftSeparator = _expect(TokenType.SEMICOLON);
- Expression condition = null;
- if (!_matches(TokenType.SEMICOLON)) {
- condition = parseExpression2();
+ ParserErrorCode.WRONG_SEPARATOR_FOR_NAMED_PARAMETER, separator);
+ } else if (kind == ParameterKind.REQUIRED) {
+ _reportErrorForNode(
+ ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP, parameter);
}
- Token rightSeparator = _expect(TokenType.SEMICOLON);
- List<Expression> updaters = null;
- if (!_matches(TokenType.CLOSE_PAREN)) {
- updaters = _parseExpressionList();
+ return new DefaultFormalParameter(
+ 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,
+ separator);
+ } else if (kind == ParameterKind.REQUIRED) {
+ _reportErrorForNode(
+ ParserErrorCode.NAMED_PARAMETER_OUTSIDE_GROUP, parameter);
}
- Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
- Statement body = parseStatement2();
- return new ForStatement(
- forKeyword,
- leftParenthesis,
- variableList,
- initialization,
- leftSeparator,
- condition,
- rightSeparator,
- updaters,
- rightParenthesis,
- body);
- } finally {
- _inLoop = wasInLoop;
+ return new DefaultFormalParameter(
+ parameter, kind, separator, defaultValue);
+ } else if (kind != ParameterKind.REQUIRED) {
+ return new DefaultFormalParameter(parameter, kind, null, null);
}
+ return parameter;
}
/**
- * Parse a function body. The [mayBeEmpty] is `true` if the function body is
- * allowed to be empty. The [emptyErrorCode] is the error code to report if
- * function body expected, but not found. The [inExpression] is `true` if the
- * function body is being parsed as part of an expression and therefore does
- * not have a terminating semicolon. Return the function body that was parsed.
- *
- * functionBody ::=
- * '=>' expression ';'
- * | block
- *
- * functionExpressionBody ::=
- * '=>' expression
- * | block
- */
- FunctionBody _parseFunctionBody(
- bool mayBeEmpty, ParserErrorCode emptyErrorCode, bool inExpression) {
- bool wasInAsync = _inAsync;
- bool wasInGenerator = _inGenerator;
- bool wasInLoop = _inLoop;
- bool wasInSwitch = _inSwitch;
- _inAsync = false;
- _inGenerator = false;
- _inLoop = false;
- _inSwitch = false;
- try {
- TokenType type = _currentToken.type;
- if (type == TokenType.SEMICOLON) {
- if (!mayBeEmpty) {
- _reportErrorForCurrentToken(emptyErrorCode);
- }
- return new EmptyFunctionBody(getAndAdvance());
- }
- Token keyword = null;
- Token star = null;
- 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;
+ * 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;
}
}
- if (type == TokenType.FUNCTION) {
- if (keyword != null) {
- if (!foundAsync) {
- _reportErrorForToken(ParserErrorCode.INVALID_SYNC, keyword);
- keyword = null;
- } else if (star != null) {
- _reportErrorForToken(
- ParserErrorCode.INVALID_STAR_AFTER_ASYNC, star);
- }
+ 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;
}
- Token functionDefinition = getAndAdvance();
- if (_matchesKeyword(Keyword.RETURN)) {
- _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
- [_currentToken.lexeme]);
- _advance();
+ if (leftCurlyBracket != null && !reportedMixedGroups) {
+ _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
+ reportedMixedGroups = true;
}
- Expression expression = parseExpression2();
- Token semicolon = null;
- if (!inExpression) {
- semicolon = _expect(TokenType.SEMICOLON);
+ 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 (!_parseFunctionBodies) {
- return new EmptyFunctionBody(
- _createSyntheticToken(TokenType.SEMICOLON));
+ if (leftSquareBracket != null && !reportedMixedGroups) {
+ _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
+ reportedMixedGroups = true;
}
- return new ExpressionFunctionBody(
- keyword, functionDefinition, expression, semicolon);
- } else if (type == TokenType.OPEN_CURLY_BRACKET) {
- if (keyword != null) {
- if (foundSync && star == null) {
- _reportErrorForToken(
- ParserErrorCode.MISSING_STAR_AFTER_SYNC, keyword);
+ 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;
+
+ // Advance past trailing commas as appropriate.
+ if (type == TokenType.COMMA) {
+ // Only parse commas trailing normal (non-positional/named) params.
+ if (rightSquareBracket == null && rightCurlyBracket == null) {
+ Token next = _peek();
+ if (next.type == TokenType.CLOSE_PAREN ||
+ next.type == TokenType.CLOSE_CURLY_BRACKET ||
+ next.type == TokenType.CLOSE_SQUARE_BRACKET) {
+ _advance();
+ type = _currentToken.type;
}
}
- if (!_parseFunctionBodies) {
- _skipBlock();
- return new EmptyFunctionBody(
- _createSyntheticToken(TokenType.SEMICOLON));
+ }
+
+ 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,
+ ["["]);
+ }
}
- return new BlockFunctionBody(keyword, star, parseBlock());
- } else if (_matchesString(_NATIVE)) {
- Token nativeToken = getAndAdvance();
- StringLiteral stringLiteral = null;
- if (_matches(TokenType.STRING)) {
- stringLiteral = _parseStringLiteralUnchecked();
+ 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,
+ ["{"]);
+ }
}
- return new NativeFunctionBody(
- nativeToken, stringLiteral, _expect(TokenType.SEMICOLON));
- } else {
- // Invalid function body
- _reportErrorForCurrentToken(emptyErrorCode);
- return new EmptyFunctionBody(
- _createSyntheticToken(TokenType.SEMICOLON));
+ kind = ParameterKind.REQUIRED;
}
- } finally {
- _inAsync = wasInAsync;
- _inGenerator = wasInGenerator;
- _inLoop = wasInLoop;
- _inSwitch = wasInSwitch;
+ } 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());
}
/**
@@ -5212,7 +6181,7 @@ class Parser {
}
FunctionBody body;
if (externalKeyword == null) {
- body = _parseFunctionBody(
+ body = parseFunctionBody(
false, ParserErrorCode.MISSING_FUNCTION_BODY, false);
} else {
body = new EmptyFunctionBody(_expect(TokenType.SEMICOLON));
@@ -5233,20 +6202,6 @@ class Parser {
}
/**
- * Parse a function declaration statement. Return the function declaration
- * statement that was parsed.
- *
- * functionDeclarationStatement ::=
- * functionSignature functionBody
- */
- Statement _parseFunctionDeclarationStatement() {
- Modifiers modifiers = _parseModifiers();
- _validateModifiersForFunctionDeclarationStatement(modifiers);
- return _parseFunctionDeclarationStatementAfterReturnType(
- _parseCommentAndMetadata(), _parseOptionalReturnType());
- }
-
- /**
* Parse a function declaration statement. The [commentAndMetadata] is the
* documentation comment and metadata to be associated with the declaration.
* The [returnType] is the return type, or `null` if there is no return type.
@@ -5401,7 +6356,7 @@ class Parser {
_advance();
_advance();
}
- FunctionBody body = _parseFunctionBody(
+ FunctionBody body = parseFunctionBody(
externalKeyword != null || staticKeyword == null,
ParserErrorCode.STATIC_GETTER_WITHOUT_BODY,
false);
@@ -5440,30 +6395,6 @@ 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 = getAndAdvance();
- Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
- Expression condition = parseExpression2();
- Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
- Statement thenStatement = parseStatement2();
- Token elseKeyword = null;
- Statement elseStatement = null;
- if (_matchesKeyword(Keyword.ELSE)) {
- elseKeyword = getAndAdvance();
- elseStatement = parseStatement2();
- }
- return new IfStatement(ifKeyword, leftParenthesis, condition,
- rightParenthesis, thenStatement, elseKeyword, elseStatement);
- }
-
- /**
* Parse an import directive. The [commentAndMetadata] is the metadata to be
* associated with the directive. Return the import directive that was parsed.
*
@@ -5504,7 +6435,7 @@ class Parser {
}
}
}
- List<Combinator> combinators = _parseCombinators();
+ List<Combinator> combinators = parseCombinators();
Token semicolon = _expect(TokenType.SEMICOLON);
return new ImportDirective(
commentAndMetadata.comment,
@@ -5544,7 +6475,7 @@ class Parser {
Token keyword,
TypeName type) {
VariableDeclarationList fieldList =
- _parseVariableDeclarationListAfterType(null, keyword, type);
+ parseVariableDeclarationListAfterType(null, keyword, type);
return new FieldDeclaration(
commentAndMetadata.comment,
commentAndMetadata.metadata,
@@ -5610,7 +6541,7 @@ class Parser {
_reportErrorForToken(missingNameError, missingNameToken);
}
return new LibraryIdentifier(
- <SimpleIdentifier>[_createSyntheticIdentifier()]);
+ <SimpleIdentifier>[createSyntheticIdentifier()]);
}
/**
@@ -5667,32 +6598,6 @@ class Parser {
}
/**
- * Parse a list or map literal. The [modifier] is the 'const' modifier
- * appearing before the literal, or `null` if there is no modifier. Return the
- * list or map literal that was parsed.
- *
- * listOrMapLiteral ::=
- * listLiteral
- * | mapLiteral
- */
- TypedLiteral _parseListOrMapLiteral(Token modifier) {
- TypeArgumentList typeArguments = _parseOptionalTypeArguments();
- if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
- return _parseMapLiteral(modifier, typeArguments);
- } else if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
- _matches(TokenType.INDEX)) {
- return _parseListLiteral(modifier, typeArguments);
- }
- _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL);
- return new ListLiteral(
- modifier,
- typeArguments,
- _createSyntheticToken(TokenType.OPEN_SQUARE_BRACKET),
- null,
- _createSyntheticToken(TokenType.CLOSE_SQUARE_BRACKET));
- }
-
- /**
* Parse a logical and expression. Return the logical and expression that was
* parsed.
*
@@ -5766,7 +6671,7 @@ class Parser {
SimpleIdentifier name,
TypeParameterList typeParameters,
FormalParameterList parameters) {
- FunctionBody body = _parseFunctionBody(
+ FunctionBody body = parseFunctionBody(
externalKeyword != null || staticKeyword == null,
ParserErrorCode.MISSING_FUNCTION_BODY,
false);
@@ -5926,29 +6831,6 @@ class Parser {
}
/**
- * Parse a multiplicative expression. Return the multiplicative expression
- * that was parsed.
- *
- * multiplicativeExpression ::=
- * unaryExpression (multiplicativeOperator unaryExpression)*
- * | 'super' (multiplicativeOperator unaryExpression)+
- */
- Expression _parseMultiplicativeExpression() {
- Expression expression;
- if (_currentToken.keyword == Keyword.SUPER &&
- _currentToken.next.type.isMultiplicativeOperator) {
- expression = new SuperExpression(getAndAdvance());
- } else {
- expression = _parseUnaryExpression();
- }
- while (_currentToken.type.isMultiplicativeOperator) {
- expression = new BinaryExpression(
- expression, getAndAdvance(), _parseUnaryExpression());
- }
- return expression;
- }
-
- /**
* Parse a class native clause. Return the native clause that was parsed.
*
* This method assumes that the current token matches `_NATIVE`.
@@ -5995,11 +6877,11 @@ class Parser {
*/
Statement _parseNonLabeledStatement() {
// TODO(brianwilkerson) Pass the comment and metadata on where appropriate.
- CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata();
+ CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
TokenType type = _currentToken.type;
if (type == TokenType.OPEN_CURLY_BRACKET) {
if (_tokenMatches(_peek(), TokenType.STRING)) {
- Token afterString = _skipStringLiteral(_currentToken.next);
+ Token afterString = skipStringLiteral(_currentToken.next);
if (afterString != null && afterString.type == TokenType.COLON) {
return new ExpressionStatement(
parseExpression2(), _expect(TokenType.SEMICOLON));
@@ -6012,33 +6894,33 @@ class Parser {
// TODO(jwren) compute some metrics to figure out a better order for this
// if-then sequence to optimize performance
if (keyword == Keyword.ASSERT) {
- return _parseAssertStatement();
+ return parseAssertStatement();
} else if (keyword == Keyword.BREAK) {
- return _parseBreakStatement();
+ return parseBreakStatement();
} else if (keyword == Keyword.CONTINUE) {
- return _parseContinueStatement();
+ return parseContinueStatement();
} else if (keyword == Keyword.DO) {
- return _parseDoStatement();
+ return parseDoStatement();
} else if (keyword == Keyword.FOR) {
- return _parseForStatement();
+ return parseForStatement();
} else if (keyword == Keyword.IF) {
- return _parseIfStatement();
+ return parseIfStatement();
} else if (keyword == Keyword.RETHROW) {
return new ExpressionStatement(
- _parseRethrowExpression(), _expect(TokenType.SEMICOLON));
+ parseRethrowExpression(), _expect(TokenType.SEMICOLON));
} else if (keyword == Keyword.RETURN) {
- return _parseReturnStatement();
+ return parseReturnStatement();
} else if (keyword == Keyword.SWITCH) {
- return _parseSwitchStatement();
+ return parseSwitchStatement();
} else if (keyword == Keyword.THROW) {
return new ExpressionStatement(
- _parseThrowExpression(), _expect(TokenType.SEMICOLON));
+ parseThrowExpression(), _expect(TokenType.SEMICOLON));
} else if (keyword == Keyword.TRY) {
- return _parseTryStatement();
+ return parseTryStatement();
} else if (keyword == Keyword.WHILE) {
- return _parseWhileStatement();
+ return parseWhileStatement();
} else if (keyword == Keyword.VAR || keyword == Keyword.FINAL) {
- return _parseVariableDeclarationStatementAfterMetadata(
+ return parseVariableDeclarationStatementAfterMetadata(
commentAndMetadata);
} else if (keyword == Keyword.VOID) {
TypeName returnType =
@@ -6067,7 +6949,7 @@ class Parser {
// We appear to have a variable declaration with a type of "void".
//
_reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType);
- return _parseVariableDeclarationStatementAfterMetadata(
+ return parseVariableDeclarationStatementAfterMetadata(
commentAndMetadata);
}
} else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
@@ -6093,7 +6975,7 @@ class Parser {
return new ExpressionStatement(
parseExpression2(), _expect(TokenType.SEMICOLON));
} else if (_tokenMatches(next, TokenType.IDENTIFIER)) {
- Token afterType = _skipTypeName(next);
+ Token afterType = skipTypeName(next);
if (afterType != null) {
if (_tokenMatches(afterType, TokenType.OPEN_PAREN) ||
(_tokenMatches(afterType, TokenType.PERIOD) &&
@@ -6104,7 +6986,7 @@ class Parser {
}
}
}
- return _parseVariableDeclarationStatementAfterMetadata(
+ return parseVariableDeclarationStatementAfterMetadata(
commentAndMetadata);
} else if (keyword == Keyword.NEW ||
keyword == Keyword.TRUE ||
@@ -6122,29 +7004,28 @@ class Parser {
return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
}
} else if (_inGenerator && _matchesString(_YIELD)) {
- return _parseYieldStatement();
+ return parseYieldStatement();
} else if (_inAsync && _matchesString(_AWAIT)) {
if (_tokenMatchesKeyword(_peek(), Keyword.FOR)) {
- return _parseForStatement();
+ return parseForStatement();
}
return new ExpressionStatement(
parseExpression2(), _expect(TokenType.SEMICOLON));
} else if (_matchesString(_AWAIT) &&
_tokenMatchesKeyword(_peek(), Keyword.FOR)) {
Token awaitToken = _currentToken;
- Statement statement = _parseForStatement();
+ Statement statement = parseForStatement();
if (statement is! ForStatement) {
_reportErrorForToken(
CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT, awaitToken);
}
return statement;
} else if (type == TokenType.SEMICOLON) {
- return _parseEmptyStatement();
- } else if (_isInitializedVariableDeclaration()) {
- return _parseVariableDeclarationStatementAfterMetadata(
- commentAndMetadata);
- } else if (_isFunctionDeclaration()) {
- return _parseFunctionDeclarationStatement();
+ return parseEmptyStatement();
+ } else if (isInitializedVariableDeclaration()) {
+ return parseVariableDeclarationStatementAfterMetadata(commentAndMetadata);
+ } else if (isFunctionDeclaration()) {
+ return parseFunctionDeclarationStatement();
} else if (type == TokenType.CLOSE_CURLY_BRACKET) {
_reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT);
return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
@@ -6155,33 +7036,6 @@ class Parser {
}
/**
- * Parse an operator declaration. 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. Return the operator declaration that was parsed.
- *
- * operatorDeclaration ::=
- * operatorSignature (';' | functionBody)
- *
- * operatorSignature ::=
- * 'external'? returnType? 'operator' operator formalParameterList
- */
- MethodDeclaration _parseOperator(CommentAndMetadata commentAndMetadata,
- Token externalKeyword, TypeName returnType) {
- Token operatorKeyword;
- if (_matchesKeyword(Keyword.OPERATOR)) {
- operatorKeyword = getAndAdvance();
- } else {
- _reportErrorForToken(
- 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'
@@ -6219,7 +7073,7 @@ class Parser {
FormalParameterList parameters = parseFormalParameterList();
_validateFormalParameterList(parameters);
FunctionBody body =
- _parseFunctionBody(true, ParserErrorCode.MISSING_FUNCTION_BODY, false);
+ parseFunctionBody(true, ParserErrorCode.MISSING_FUNCTION_BODY, false);
if (externalKeyword != null && body is! EmptyFunctionBody) {
_reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_OPERATOR_WITH_BODY);
}
@@ -6364,7 +7218,7 @@ class Parser {
* | argumentList
*/
Expression _parsePostfixExpression() {
- Expression operand = _parseAssignableExpression(true);
+ Expression operand = parseAssignableExpression(true);
TokenType type = _currentToken.type;
if (type == TokenType.OPEN_SQUARE_BRACKET ||
type == TokenType.PERIOD ||
@@ -6433,238 +7287,37 @@ class Parser {
*/
Identifier _parsePrefixedIdentifierUnchecked() {
return _parsePrefixedIdentifierAfterIdentifier(
- _parseSimpleIdentifierUnchecked());
- }
-
- /**
- * Parse a primary expression. Return the primary expression that was parsed.
- *
- * primary ::=
- * thisExpression
- * | 'super' unconditionalAssignableSelector
- * | functionExpression
- * | literal
- * | identifier
- * | newExpression
- * | constObjectExpression
- * | '(' expression ')'
- * | argumentDefinitionTest
- *
- * literal ::=
- * nullLiteral
- * | booleanLiteral
- * | numericLiteral
- * | stringLiteral
- * | symbolLiteral
- * | mapLiteral
- * | listLiteral
- */
- Expression _parsePrimaryExpression() {
- 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 (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 (keyword == Keyword.FALSE) {
- return new BooleanLiteral(getAndAdvance(), false);
- } else if (keyword == Keyword.TRUE) {
- return new BooleanLiteral(getAndAdvance(), true);
- }
- if (type == TokenType.DOUBLE) {
- Token token = getAndAdvance();
- double value = 0.0;
- try {
- value = double.parse(token.lexeme);
- } on FormatException {
- // The invalid format should have been reported by the scanner.
- }
- return new DoubleLiteral(token, value);
- } else if (type == TokenType.HEXADECIMAL) {
- Token token = getAndAdvance();
- int value = null;
- try {
- value = int.parse(token.lexeme.substring(2), radix: 16);
- } on FormatException {
- // The invalid format should have been reported by the scanner.
- }
- return new IntegerLiteral(token, value);
- } else if (keyword == Keyword.CONST) {
- return _parseConstExpression();
- } else if (type == TokenType.OPEN_PAREN) {
- if (_isFunctionExpression(_currentToken)) {
- return parseFunctionExpression();
- }
- Token leftParenthesis = getAndAdvance();
- bool wasInInitializer = _inInitializer;
- _inInitializer = false;
- try {
- Expression expression = parseExpression2();
- Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
- return new ParenthesizedExpression(
- leftParenthesis, expression, rightParenthesis);
- } finally {
- _inInitializer = wasInInitializer;
- }
- } else if (type == TokenType.LT || _injectGenericCommentTypeList()) {
- return _parseListOrMapLiteral(null);
- } else if (type == TokenType.OPEN_CURLY_BRACKET) {
- return _parseMapLiteral(null, null);
- } else if (type == TokenType.OPEN_SQUARE_BRACKET ||
- type == TokenType.INDEX) {
- return _parseListLiteral(null, null);
- } else if (type == TokenType.QUESTION &&
- _tokenMatches(_peek(), TokenType.IDENTIFIER)) {
- _reportErrorForCurrentToken(
- ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
- _advance();
- return _parsePrimaryExpression();
- } else if (keyword == Keyword.VOID) {
- //
- // Recover from having a return type of "void" where a return type is not
- // expected.
- //
- // TODO(brianwilkerson) Improve this error message.
- _reportErrorForCurrentToken(
- ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]);
- _advance();
- return _parsePrimaryExpression();
- } else if (type == TokenType.HASH) {
- return _parseSymbolLiteral();
- } else {
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
- return _createSyntheticIdentifier();
- }
- }
-
- /**
- * 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(
- bool hasPeriod) {
- Token keyword = getAndAdvance();
- Token period = null;
- SimpleIdentifier constructorName = null;
- if (hasPeriod) {
- period = getAndAdvance();
- if (_matchesIdentifier()) {
- constructorName = _parseSimpleIdentifierUnchecked(isDeclaration: false);
- } else {
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
- constructorName = _createSyntheticIdentifier(isDeclaration: false);
- _advance();
- }
- }
- ArgumentList argumentList = _parseArgumentListChecked();
- return new RedirectingConstructorInvocation(
- keyword, period, constructorName, argumentList);
- }
-
- /**
- * Parse a relational expression. Return the relational expression that was
- * parsed.
- *
- * relationalExpression ::=
- * bitwiseOrExpression ('is' '!'? type | 'as' type | relationalOperator bitwiseOrExpression)?
- * | 'super' relationalOperator bitwiseOrExpression
- */
- Expression _parseRelationalExpression() {
- if (_currentToken.keyword == Keyword.SUPER &&
- _currentToken.next.type.isRelationalOperator) {
- Expression expression = new SuperExpression(getAndAdvance());
- Token operator = getAndAdvance();
- return new BinaryExpression(
- expression, operator, parseBitwiseOrExpression());
- }
- Expression expression = parseBitwiseOrExpression();
- Keyword keyword = _currentToken.keyword;
- if (keyword == Keyword.AS) {
- Token asOperator = getAndAdvance();
- return new AsExpression(expression, asOperator, parseTypeName(true));
- } else if (keyword == Keyword.IS) {
- Token isOperator = getAndAdvance();
- Token notOperator = null;
- if (_matches(TokenType.BANG)) {
- notOperator = getAndAdvance();
- }
- return new IsExpression(
- expression, isOperator, notOperator, parseTypeName(true));
- } else if (_currentToken.type.isRelationalOperator) {
- Token operator = getAndAdvance();
- return new BinaryExpression(
- expression, operator, parseBitwiseOrExpression());
- }
- return expression;
- }
-
- /**
- * 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(getAndAdvance());
+ _parseSimpleIdentifierUnchecked());
+ }
/**
- * Parse a return statement. Return the return statement that was parsed.
+ * 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.RETURN`.
+ * This method assumes that the current token matches `Keyword.THIS`.
*
- * returnStatement ::=
- * 'return' expression? ';'
+ * redirectingConstructorInvocation ::=
+ * 'this' ('.' identifier)? arguments
*/
- Statement _parseReturnStatement() {
- Token returnKeyword = getAndAdvance();
- if (_matches(TokenType.SEMICOLON)) {
- return new ReturnStatement(returnKeyword, null, getAndAdvance());
+ RedirectingConstructorInvocation _parseRedirectingConstructorInvocation(
+ bool hasPeriod) {
+ Token keyword = getAndAdvance();
+ Token period = null;
+ SimpleIdentifier constructorName = null;
+ if (hasPeriod) {
+ period = getAndAdvance();
+ if (_matchesIdentifier()) {
+ constructorName = _parseSimpleIdentifierUnchecked(isDeclaration: false);
+ } else {
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+ constructorName = createSyntheticIdentifier(isDeclaration: false);
+ _advance();
+ }
}
- Expression expression = parseExpression2();
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new ReturnStatement(returnKeyword, expression, semicolon);
+ ArgumentList argumentList = _parseArgumentListChecked();
+ return new RedirectingConstructorInvocation(
+ keyword, period, constructorName, argumentList);
}
/**
@@ -6689,7 +7342,7 @@ class Parser {
SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
FormalParameterList parameters = parseFormalParameterList();
_validateFormalParameterList(parameters);
- FunctionBody body = _parseFunctionBody(
+ FunctionBody body = parseFunctionBody(
externalKeyword != null || staticKeyword == null,
ParserErrorCode.STATIC_SETTER_WITHOUT_BODY,
false);
@@ -6711,28 +7364,6 @@ class Parser {
}
/**
- * Parse a shift expression. Return the shift expression that was parsed.
- *
- * shiftExpression ::=
- * additiveExpression (shiftOperator additiveExpression)*
- * | 'super' (shiftOperator additiveExpression)+
- */
- Expression _parseShiftExpression() {
- Expression expression;
- if (_currentToken.keyword == Keyword.SUPER &&
- _currentToken.next.type.isShiftOperator) {
- expression = new SuperExpression(getAndAdvance());
- } else {
- expression = _parseAdditiveExpression();
- }
- while (_currentToken.type.isShiftOperator) {
- expression = new BinaryExpression(
- 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.
@@ -6764,7 +7395,7 @@ class Parser {
TokenType type = _currentToken.type;
while (type != TokenType.EOF &&
type != TokenType.CLOSE_CURLY_BRACKET &&
- !_isSwitchMember()) {
+ !isSwitchMember()) {
statements.add(parseStatement2());
if (identical(_currentToken, statementStart)) {
_reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken,
@@ -6788,7 +7419,7 @@ class Parser {
StringInterpolation _parseStringInterpolation(Token string) {
List<InterpolationElement> elements = <InterpolationElement>[
new InterpolationString(
- string, _computeStringValue(string.lexeme, true, false))
+ string, computeStringValue(string.lexeme, true, false))
];
bool hasMore = true;
bool isExpression = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION);
@@ -6821,292 +7452,36 @@ class Parser {
hasMore =
isExpression || _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER);
elements.add(new InterpolationString(
- string, _computeStringValue(string.lexeme, false, !hasMore)));
+ string, computeStringValue(string.lexeme, false, !hasMore)));
} else {
hasMore = false;
- }
- }
- return new StringInterpolation(elements);
- }
-
- /**
- * 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 = getAndAdvance();
- Token period = null;
- SimpleIdentifier constructorName = null;
- if (_matches(TokenType.PERIOD)) {
- period = getAndAdvance();
- constructorName = parseSimpleIdentifier();
- }
- ArgumentList argumentList = _parseArgumentListChecked();
- return new SuperConstructorInvocation(
- keyword, period, constructorName, argumentList);
- }
-
- /**
- * Parse a switch statement. Return the switch statement that was parsed.
- *
- * switchStatement ::=
- * 'switch' '(' expression ')' '{' switchCase* defaultCase? '}'
- *
- * switchCase ::=
- * label* ('case' expression ':') statements
- *
- * defaultCase ::=
- * label* 'default' ':' statements
- */
- SwitchStatement _parseSwitchStatement() {
- bool wasInSwitch = _inSwitch;
- _inSwitch = true;
- try {
- HashSet<String> definedLabels = new HashSet<String>();
- Token keyword = _expectKeyword(Keyword.SWITCH);
- Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
- Expression expression = parseExpression2();
- Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
- Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET);
- Token defaultKeyword = null;
- 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 =
- _parseSimpleIdentifierUnchecked(isDeclaration: true);
- String label = identifier.token.lexeme;
- if (definedLabels.contains(label)) {
- _reportErrorForToken(
- ParserErrorCode.DUPLICATE_LABEL_IN_SWITCH_STATEMENT,
- identifier.token,
- [label]);
- } else {
- definedLabels.add(label);
- }
- Token colon = getAndAdvance();
- labels.add(new Label(identifier, colon));
- }
- Keyword keyword = _currentToken.keyword;
- if (keyword == Keyword.CASE) {
- Token caseKeyword = getAndAdvance();
- Expression caseExpression = parseExpression2();
- Token colon = _expect(TokenType.COLON);
- members.add(new SwitchCase(labels, caseKeyword, caseExpression, colon,
- _parseStatementList()));
- if (defaultKeyword != null) {
- _reportErrorForToken(
- ParserErrorCode.SWITCH_HAS_CASE_AFTER_DEFAULT_CASE,
- caseKeyword);
- }
- } else if (keyword == Keyword.DEFAULT) {
- if (defaultKeyword != null) {
- _reportErrorForToken(
- ParserErrorCode.SWITCH_HAS_MULTIPLE_DEFAULT_CASES, _peek());
- }
- defaultKeyword = getAndAdvance();
- Token colon = _expect(TokenType.COLON);
- members.add(new SwitchDefault(
- labels, defaultKeyword, colon, _parseStatementList()));
- } else {
- // 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);
- 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,
- rightParenthesis, leftBracket, members, rightBracket);
- } finally {
- _inSwitch = wasInSwitch;
- }
- }
-
- /**
- * 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 = <Token>[];
- if (_matchesIdentifier()) {
- components.add(getAndAdvance());
- while (_optional(TokenType.PERIOD)) {
- if (_matchesIdentifier()) {
- components.add(getAndAdvance());
- } else {
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
- components.add(_createSyntheticToken(TokenType.IDENTIFIER));
- break;
- }
- }
- } else if (_currentToken.isOperator) {
- components.add(getAndAdvance());
- } else if (_matchesKeyword(Keyword.VOID)) {
- components.add(getAndAdvance());
- } else {
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
- components.add(_createSyntheticToken(TokenType.IDENTIFIER));
- }
- return new SymbolLiteral(poundSign, components);
- }
-
- /**
- * 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 = 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());
- }
- Expression expression = parseExpression2();
- return new ThrowExpression(keyword, expression);
- }
-
- /**
- * 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 = 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());
+ }
}
- Expression expression = parseExpressionWithoutCascade();
- return new ThrowExpression(keyword, expression);
+ return new StringInterpolation(elements);
}
/**
- * 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)
- *
- * onPart ::=
- * catchPart block
- * | 'on' type catchPart? block
+ * Parse a string literal. Return the string literal that was parsed.
*
- * catchPart ::=
- * 'catch' '(' identifier (',' identifier)? ')'
+ * This method assumes that the current token matches `TokenType.STRING`.
*
- * finallyPart ::=
- * 'finally' block
+ * stringLiteral ::=
+ * MULTI_LINE_STRING+
+ * | SINGLE_LINE_STRING+
*/
- Statement _parseTryStatement() {
- Token tryKeyword = getAndAdvance();
- Block body = _parseBlockChecked();
- List<CatchClause> catchClauses = <CatchClause>[];
- Block finallyClause = null;
- while (_matchesString(_ON) || _matchesKeyword(Keyword.CATCH)) {
- Token onKeyword = null;
- TypeName exceptionType = null;
- if (_matchesString(_ON)) {
- onKeyword = getAndAdvance();
- exceptionType = parseTypeName(false);
- }
- Token catchKeyword = null;
- Token leftParenthesis = null;
- SimpleIdentifier exceptionParameter = null;
- Token comma = null;
- SimpleIdentifier stackTraceParameter = null;
- Token rightParenthesis = null;
- if (_matchesKeyword(Keyword.CATCH)) {
- catchKeyword = getAndAdvance();
- leftParenthesis = _expect(TokenType.OPEN_PAREN);
- exceptionParameter = parseSimpleIdentifier(isDeclaration: true);
- if (_matches(TokenType.COMMA)) {
- comma = getAndAdvance();
- stackTraceParameter = parseSimpleIdentifier(isDeclaration: true);
- }
- rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+ 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)));
}
- Block catchBody = _parseBlockChecked();
- catchClauses.add(new CatchClause(
- onKeyword,
- exceptionType,
- catchKeyword,
- leftParenthesis,
- exceptionParameter,
- comma,
- stackTraceParameter,
- rightParenthesis,
- catchBody));
- }
- Token finallyKeyword = null;
- if (_matchesKeyword(Keyword.FINALLY)) {
- finallyKeyword = getAndAdvance();
- finallyClause = _parseBlockChecked();
- } else if (catchClauses.isEmpty) {
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_CATCH_OR_FINALLY);
- }
- return new TryStatement(
- tryKeyword, body, catchClauses, finallyKeyword, finallyClause);
+ } while (_matches(TokenType.STRING));
+ return strings.length == 1 ? strings[0] : new AdjacentStrings(strings);
}
/**
@@ -7142,14 +7517,14 @@ class Parser {
next = _skipTypeParameterList(next);
if (next != null && _tokenMatches(next, TokenType.EQ)) {
TypeAlias typeAlias =
- _parseClassTypeAlias(commentAndMetadata, null, keyword);
+ parseClassTypeAlias(commentAndMetadata, null, keyword);
_reportErrorForToken(
ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword);
return typeAlias;
}
} else if (_tokenMatches(next, TokenType.EQ)) {
TypeAlias typeAlias =
- _parseClassTypeAlias(commentAndMetadata, null, keyword);
+ parseClassTypeAlias(commentAndMetadata, null, keyword);
_reportErrorForToken(
ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword);
return typeAlias;
@@ -7166,7 +7541,7 @@ class Parser {
_reportErrorForCurrentToken(ParserErrorCode.VAR_AS_TYPE_NAME);
typeName = new SimpleIdentifier(getAndAdvance());
} else {
- typeName = _createSyntheticIdentifier();
+ typeName = createSyntheticIdentifier();
_reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TYPE_NAME);
}
TypeArgumentList typeArguments = _parseOptionalTypeArguments();
@@ -7199,82 +7574,6 @@ class Parser {
}
/**
- * Parse a unary expression. Return the unary expression that was parsed.
- *
- * unaryExpression ::=
- * prefixOperator unaryExpression
- * | awaitExpression
- * | postfixExpression
- * | unaryOperator 'super'
- * | '-' 'super'
- * | incrementOperator assignableExpression
- */
- Expression _parseUnaryExpression() {
- TokenType type = _currentToken.type;
- if (type == TokenType.MINUS ||
- type == TokenType.BANG ||
- type == TokenType.TILDE) {
- Token operator = getAndAdvance();
- if (_matchesKeyword(Keyword.SUPER)) {
- TokenType nextType = _peek().type;
- if (nextType == TokenType.OPEN_SQUARE_BRACKET ||
- nextType == TokenType.PERIOD) {
- // "prefixOperator unaryExpression"
- // --> "prefixOperator postfixExpression"
- // --> "prefixOperator primary selector*"
- // --> "prefixOperator 'super' assignableSelector selector*"
- return new PrefixExpression(operator, _parseUnaryExpression());
- }
- return new PrefixExpression(
- operator, new SuperExpression(getAndAdvance()));
- }
- return new PrefixExpression(operator, _parseUnaryExpression());
- } else if (_currentToken.type.isIncrementOperator) {
- Token operator = getAndAdvance();
- if (_matchesKeyword(Keyword.SUPER)) {
- TokenType nextType = _peek().type;
- if (nextType == TokenType.OPEN_SQUARE_BRACKET ||
- nextType == TokenType.PERIOD) {
- // --> "prefixOperator 'super' assignableSelector selector*"
- return new PrefixExpression(operator, _parseUnaryExpression());
- }
- //
- // Even though it is not valid to use an incrementing operator
- // ('++' or '--') before 'super', we can (and therefore must) interpret
- // "--super" as semantically equivalent to "-(-super)". Unfortunately,
- // we cannot do the same for "++super" because "+super" is also not
- // valid.
- //
- if (type == TokenType.MINUS_MINUS) {
- Token firstOperator = _createToken(operator, TokenType.MINUS);
- Token secondOperator =
- new Token(TokenType.MINUS, operator.offset + 1);
- secondOperator.setNext(_currentToken);
- firstOperator.setNext(secondOperator);
- operator.previous.setNext(firstOperator);
- return new PrefixExpression(
- firstOperator,
- new PrefixExpression(
- secondOperator, 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, _parseAssignableExpressionNotStartingWithSuper(false));
- } else if (type == TokenType.PLUS) {
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
- return _createSyntheticIdentifier();
- } else if (_inAsync && _matchesString(_AWAIT)) {
- return _parseAwaitExpression();
- }
- return _parsePostfixExpression();
- }
-
- /**
* Parse a string literal representing a URI. Return the string literal that
* was parsed.
*/
@@ -7291,141 +7590,47 @@ class Parser {
!isKeywordAfterUri(_currentToken)) {
// Attempt to recover in the case where the URI was not enclosed in
// quotes.
- Token token = _currentToken;
- 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)) {
- Token endToken = token.previous;
- token = _currentToken;
- int endOffset = token.end;
- StringBuffer buffer = new StringBuffer();
- buffer.write(token.lexeme);
- while (token != endToken) {
- token = token.next;
- if (token.offset != endOffset || token.precedingComments != null) {
- return parseStringLiteral();
- }
- buffer.write(token.lexeme);
- endOffset = token.end;
- }
- String value = buffer.toString();
- Token newToken =
- new StringToken(TokenType.STRING, "'$value'", _currentToken.offset);
- _reportErrorForToken(
- ParserErrorCode.NON_STRING_LITERAL_AS_URI, newToken);
- _currentToken = endToken.next;
- return new SimpleStringLiteral(newToken, value);
- }
- }
- return parseStringLiteral();
- }
-
- /**
- * Parse a variable declaration. Return the variable declaration that was
- * parsed.
- *
- * variableDeclaration ::=
- * identifier ('=' expression)?
- */
- VariableDeclaration _parseVariableDeclaration() {
- // TODO(paulberry): prior to the fix for bug 23204, we permitted
- // annotations before variable declarations (e.g. "String @deprecated s;").
- // Although such constructions are prohibited by the spec, we may want to
- // consider handling them anyway to allow for better parser recovery in the
- // event that the user erroneously tries to use them. However, as a
- // counterargument, this would likely degrade parser recovery in the event
- // of a construct like "class C { int @deprecated foo() {} }" (i.e. the
- // user is in the middle of inserting "int bar;" prior to
- // "@deprecated foo() {}").
- SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
- Token equals = null;
- Expression initializer = null;
- if (_matches(TokenType.EQ)) {
- equals = getAndAdvance();
- initializer = parseExpression2();
- }
- return new VariableDeclaration(name, equals, initializer);
- }
-
- /**
- * Parse a variable declaration list. The [commentAndMetadata] is the metadata
- * to be associated with the variable declaration list. Return the variable
- * declaration list that was parsed.
- *
- * variableDeclarationList ::=
- * finalConstVarOrType variableDeclaration (',' variableDeclaration)*
- */
- VariableDeclarationList _parseVariableDeclarationListAfterMetadata(
- CommentAndMetadata commentAndMetadata) {
- FinalConstVarOrType holder = _parseFinalConstVarOrType(false);
- return _parseVariableDeclarationListAfterType(
- commentAndMetadata, holder.keyword, holder.type);
- }
-
- /**
- * Parse a variable declaration list. The [commentAndMetadata] is the metadata
- * to be associated with the variable declaration list, or `null` if there is
- * no attempt at parsing the comment and metadata. The [keyword] is the token
- * representing the 'final', 'const' or 'var' keyword, or `null` if there is
- * no keyword. The [type] is the type of the variables in the list. Return the
- * variable declaration list that was parsed.
- *
- * variableDeclarationList ::=
- * finalConstVarOrType variableDeclaration (',' variableDeclaration)*
- */
- VariableDeclarationList _parseVariableDeclarationListAfterType(
- CommentAndMetadata commentAndMetadata, Token keyword, TypeName type) {
- if (type != null &&
- keyword != null &&
- _tokenMatchesKeyword(keyword, Keyword.VAR)) {
- _reportErrorForToken(ParserErrorCode.VAR_AND_TYPE, keyword);
- }
- List<VariableDeclaration> variables = <VariableDeclaration>[
- _parseVariableDeclaration()
- ];
- while (_optional(TokenType.COMMA)) {
- variables.add(_parseVariableDeclaration());
- }
- return new VariableDeclarationList(commentAndMetadata?.comment,
- commentAndMetadata?.metadata, keyword, type, variables);
- }
+ Token token = _currentToken;
+ 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;
+ }
- /**
- * Parse a variable declaration statement. The [commentAndMetadata] is the
- * metadata to be associated with the variable declaration statement, or
- * `null` if there is no attempt at parsing the comment and metadata. Return
- * the variable declaration statement that was parsed.
- *
- * variableDeclarationStatement ::=
- * variableDeclarationList ';'
- */
- VariableDeclarationStatement _parseVariableDeclarationStatementAfterMetadata(
- CommentAndMetadata commentAndMetadata) {
- // Token startToken = currentToken;
- VariableDeclarationList variableList =
- _parseVariableDeclarationListAfterMetadata(commentAndMetadata);
-// if (!matches(TokenType.SEMICOLON)) {
-// if (matches(startToken, Keyword.VAR) && isTypedIdentifier(startToken.getNext())) {
-// // TODO(brianwilkerson) This appears to be of the form "var type variable". We should do
-// // a better job of recovering in this case.
-// }
-// }
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new VariableDeclarationStatement(variableList, semicolon);
+ while ((_tokenMatchesIdentifier(token) && !isKeywordAfterUri(token)) ||
+ isValidInUri(token)) {
+ token = token.next;
+ }
+ if (_tokenMatches(token, TokenType.SEMICOLON) ||
+ isKeywordAfterUri(token)) {
+ Token endToken = token.previous;
+ token = _currentToken;
+ int endOffset = token.end;
+ StringBuffer buffer = new StringBuffer();
+ buffer.write(token.lexeme);
+ while (token != endToken) {
+ token = token.next;
+ if (token.offset != endOffset || token.precedingComments != null) {
+ return parseStringLiteral();
+ }
+ buffer.write(token.lexeme);
+ endOffset = token.end;
+ }
+ String value = buffer.toString();
+ Token newToken =
+ new StringToken(TokenType.STRING, "'$value'", _currentToken.offset);
+ _reportErrorForToken(
+ ParserErrorCode.NON_STRING_LITERAL_AS_URI, newToken);
+ _currentToken = endToken.next;
+ return new SimpleStringLiteral(newToken, value);
+ }
+ }
+ return parseStringLiteral();
}
/**
@@ -7442,56 +7647,13 @@ class Parser {
VariableDeclarationStatement _parseVariableDeclarationStatementAfterType(
CommentAndMetadata commentAndMetadata, Token keyword, TypeName type) {
VariableDeclarationList variableList =
- _parseVariableDeclarationListAfterType(
+ parseVariableDeclarationListAfterType(
commentAndMetadata, keyword, type);
Token semicolon = _expect(TokenType.SEMICOLON);
return new VariableDeclarationStatement(variableList, semicolon);
}
/**
- * 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
- */
- Statement _parseWhileStatement() {
- bool wasInLoop = _inLoop;
- _inLoop = true;
- try {
- Token keyword = getAndAdvance();
- Token leftParenthesis = _expect(TokenType.OPEN_PAREN);
- Expression condition = parseExpression2();
- Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
- Statement body = parseStatement2();
- return new WhileStatement(
- keyword, leftParenthesis, condition, rightParenthesis, body);
- } finally {
- _inLoop = wasInLoop;
- }
- }
-
- /**
- * Parse a yield statement. Return the yield statement that was parsed.
- *
- * This method assumes that the current token matches [Keyword.YIELD].
- *
- * yieldStatement ::=
- * 'yield' '*'? expression ';'
- */
- YieldStatement _parseYieldStatement() {
- Token yieldToken = getAndAdvance();
- Token star = null;
- if (_matches(TokenType.STAR)) {
- star = getAndAdvance();
- }
- Expression expression = parseExpression2();
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new YieldStatement(yieldToken, star, expression, semicolon);
- }
-
- /**
* Return the token that is immediately after the current token. This is
* equivalent to [_peekAt](1).
*/
@@ -7663,7 +7825,7 @@ class Parser {
if (_tokenMatchesIdentifier(next2) ||
_tokenMatches(next2, TokenType.LT) ||
_tokenMatches(next2, TokenType.PERIOD)) {
- return _skipTypeName(next);
+ return skipTypeName(next);
}
// "parameter"
return next;
@@ -7680,7 +7842,7 @@ class Parser {
(_tokenMatchesIdentifier(next.next.next) ||
_tokenMatches(next.next.next, TokenType.LT) ||
_tokenMatchesKeyword(next.next.next, Keyword.THIS)))) {
- return _skipReturnType(startToken);
+ return skipReturnType(startToken);
}
}
return null;
@@ -7764,7 +7926,7 @@ class Parser {
if (afterType == null) {
return null;
}
- if (_skipSimpleIdentifier(afterType) == null) {
+ if (skipSimpleIdentifier(afterType) == null) {
return null;
}
return _skipPastMatchingToken(startToken);
@@ -7786,77 +7948,6 @@ class Parser {
}
/**
- * Parse a prefixed identifier, starting at the [startToken], without actually
- * creating a prefixed identifier or changing the current token. Return the
- * token following the prefixed identifier that was parsed, or `null` if the
- * given token is not the first token in a valid prefixed identifier.
- *
- * This method must be kept in sync with [parsePrefixedIdentifier].
- *
- * prefixedIdentifier ::=
- * identifier ('.' identifier)?
- */
- Token _skipPrefixedIdentifier(Token startToken) {
- Token token = _skipSimpleIdentifier(startToken);
- if (token == null) {
- return null;
- } else if (!_tokenMatches(token, TokenType.PERIOD)) {
- return token;
- }
- token = token.next;
- Token nextToken = _skipSimpleIdentifier(token);
- if (nextToken != null) {
- return nextToken;
- } else if (_tokenMatches(token, TokenType.CLOSE_PAREN) ||
- _tokenMatches(token, TokenType.COMMA)) {
- // If the `id.` is followed by something that cannot produce a valid
- // structure then assume this is a prefixed identifier but missing the
- // trailing identifier
- return token;
- }
- return null;
- }
-
- /**
- * Parse a return type, starting at the [startToken], without actually
- * creating a return type or changing the current token. Return the token
- * following the return type that was parsed, or `null` if the given token is
- * not the first token in a valid return type.
- *
- * This method must be kept in sync with [parseReturnType].
- *
- * returnType ::=
- * 'void'
- * | type
- */
- Token _skipReturnType(Token startToken) {
- if (_tokenMatchesKeyword(startToken, Keyword.VOID)) {
- return startToken.next;
- } else {
- return _skipTypeName(startToken);
- }
- }
-
- /**
- * Parse a simple identifier, starting at the [startToken], without actually
- * creating a simple identifier or changing the current token. Return the
- * token following the simple identifier that was parsed, or `null` if the
- * given token is not the first token in a valid simple identifier.
- *
- * This method must be kept in sync with [parseSimpleIdentifier].
- *
- * identifier ::=
- * IDENTIFIER
- */
- Token _skipSimpleIdentifier(Token startToken) {
- if (_tokenMatches(startToken, TokenType.IDENTIFIER) ||
- _tokenMatchesPseudoKeyword(startToken)) {
- return startToken.next;
- }
- return null;
- }
-
- /**
* Parse a string literal that contains interpolations, starting at the
* [startToken], without actually creating a string literal or changing the
* current token. Return the token following the string literal that was
@@ -7889,7 +7980,7 @@ class Parser {
bracketNestingLevel--;
token = token.next;
} else if (type == TokenType.STRING) {
- token = _skipStringLiteral(token);
+ token = skipStringLiteral(token);
if (token == null) {
return null;
}
@@ -7917,103 +8008,6 @@ class Parser {
}
/**
- * Parse a string literal, starting at the [startToken], without actually
- * creating a string literal or changing the current token. Return the token
- * following the string literal that was parsed, or `null` if the given token
- * is not the first token in a valid string literal.
- *
- * This method must be kept in sync with [parseStringLiteral].
- *
- * stringLiteral ::=
- * MULTI_LINE_STRING+
- * | SINGLE_LINE_STRING+
- */
- Token _skipStringLiteral(Token startToken) {
- Token token = startToken;
- while (token != null && _tokenMatches(token, TokenType.STRING)) {
- token = token.next;
- TokenType type = token.type;
- if (type == TokenType.STRING_INTERPOLATION_EXPRESSION ||
- type == TokenType.STRING_INTERPOLATION_IDENTIFIER) {
- token = _skipStringInterpolation(token);
- }
- }
- if (identical(token, startToken)) {
- return null;
- }
- return token;
- }
-
- /**
- * Parse a list of type arguments, starting at the [startToken], without
- * actually creating a type argument list or changing the current token.
- * Return the token following the type argument list that was parsed, or
- * `null` if the given token is not the first token in a valid type argument
- * list.
- *
- * This method must be kept in sync with [parseTypeArgumentList].
- *
- * typeArguments ::=
- * '<' typeList '>'
- *
- * typeList ::=
- * type (',' type)*
- */
- Token _skipTypeArgumentList(Token startToken) {
- Token token = startToken;
- if (!_tokenMatches(token, TokenType.LT) &&
- !_injectGenericCommentTypeList()) {
- return null;
- }
- token = _skipTypeName(token.next);
- if (token == null) {
- // If the start token '<' is followed by '>'
- // then assume this should be type argument list but is missing a type
- token = startToken.next;
- if (_tokenMatches(token, TokenType.GT)) {
- return token.next;
- }
- return null;
- }
- while (_tokenMatches(token, TokenType.COMMA)) {
- token = _skipTypeName(token.next);
- if (token == null) {
- return null;
- }
- }
- if (token.type == TokenType.GT) {
- return token.next;
- } else if (token.type == TokenType.GT_GT) {
- Token second = new Token(TokenType.GT, token.offset + 1);
- second.setNextWithoutSettingPrevious(token.next);
- return second;
- }
- return null;
- }
-
- /**
- * Parse a type name, starting at the [startToken], without actually creating
- * a type name or changing the current token. Return the token following the
- * type name that was parsed, or `null` if the given token is not the first
- * token in a valid type name.
- *
- * This method must be kept in sync with [parseTypeName].
- *
- * type ::=
- * qualified typeArguments?
- */
- Token _skipTypeName(Token startToken) {
- Token token = _skipPrefixedIdentifier(startToken);
- if (token == null) {
- return null;
- }
- if (_tokenMatches(token, TokenType.LT)) {
- token = _skipTypeArgumentList(token);
- }
- return token;
- }
-
- /**
* Parse a list of type parameters, starting at the [startToken], without
* actually creating a type parameter list or changing the current token.
* Return the token following the type parameter list that was parsed, or
« 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