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

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

Issue 2356653003: Finish 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 672f761f6fae7fd37641fdde925d4bb845fc466b..7615c1bb946841298fe506ccf597f29f6bdc11f0 100644
--- a/pkg/analyzer/lib/src/generated/parser.dart
+++ b/pkg/analyzer/lib/src/generated/parser.dart
@@ -26,458 +26,6 @@ import 'package:analyzer/src/generated/utilities_dart.dart';
export 'package:analyzer/src/dart/ast/utilities.dart' show ResolutionCopier;
-Map<String, MethodTrampoline> methodTable_Parser = <String, MethodTrampoline>{
- 'parseCompilationUnit_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target.parseCompilationUnit(arg0)),
- 'parseDirectives_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target.parseDirectives(arg0)),
- 'parseExpression_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target.parseExpression(arg0)),
- 'parseStatement_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target.parseStatement(arg0)),
- 'parseStatements_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target.parseStatements(arg0)),
- 'parseAnnotation_0':
- new MethodTrampoline(0, (Parser target) => target.parseAnnotation()),
- 'parseArgument_0':
- new MethodTrampoline(0, (Parser target) => target.parseArgument()),
- 'parseArgumentList_0':
- new MethodTrampoline(0, (Parser target) => target.parseArgumentList()),
- 'parseBitwiseOrExpression_0': new MethodTrampoline(
- 0, (Parser target) => target.parseBitwiseOrExpression()),
- 'parseBlock_0':
- new MethodTrampoline(0, (Parser target) => target.parseBlock()),
- 'parseClassMember_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target.parseClassMember(arg0)),
- 'parseCompilationUnit_0': new MethodTrampoline(
- 0, (Parser target) => target.parseCompilationUnit2()),
- 'parseConditionalExpression_0': new MethodTrampoline(
- 0, (Parser target) => target.parseConditionalExpression()),
- 'parseConstructorName_0':
- new MethodTrampoline(0, (Parser target) => target.parseConstructorName()),
- 'parseExpression_0':
- new MethodTrampoline(0, (Parser target) => target.parseExpression2()),
- 'parseExpressionWithoutCascade_0': new MethodTrampoline(
- 0, (Parser target) => target.parseExpressionWithoutCascade()),
- 'parseExtendsClause_0':
- new MethodTrampoline(0, (Parser target) => target.parseExtendsClause()),
- 'parseFormalParameterList_0': new MethodTrampoline(
- 0, (Parser target) => target.parseFormalParameterList()),
- 'parseFunctionExpression_0': new MethodTrampoline(
- 0, (Parser target) => target.parseFunctionExpression()),
- 'parseImplementsClause_0': new MethodTrampoline(
- 0, (Parser target) => target.parseImplementsClause()),
- 'parseLabel_0':
- new MethodTrampoline(0, (Parser target) => target.parseLabel()),
- 'parseLibraryIdentifier_0': new MethodTrampoline(
- 0, (Parser target) => target.parseLibraryIdentifier()),
- 'parseLogicalOrExpression_0': new MethodTrampoline(
- 0, (Parser target) => target.parseLogicalOrExpression()),
- 'parseMapLiteralEntry_0':
- new MethodTrampoline(0, (Parser target) => target.parseMapLiteralEntry()),
- 'parseNormalFormalParameter_0': new MethodTrampoline(
- 0, (Parser target) => target.parseNormalFormalParameter()),
- 'parsePrefixedIdentifier_0': new MethodTrampoline(
- 0, (Parser target) => target.parsePrefixedIdentifier()),
- 'parseReturnType_0':
- new MethodTrampoline(0, (Parser target) => target.parseReturnType()),
- 'parseSimpleIdentifier_0': new MethodTrampoline(
- 0, (Parser target) => target.parseSimpleIdentifier()),
- 'parseStatement_0':
- new MethodTrampoline(0, (Parser target) => target.parseStatement2()),
- 'parseStringLiteral_0':
- new MethodTrampoline(0, (Parser target) => target.parseStringLiteral()),
- 'parseTypeArgumentList_0': new MethodTrampoline(
- 0, (Parser target) => target.parseTypeArgumentList()),
- 'parseTypeName_0':
- new MethodTrampoline(0, (Parser target) => target.parseTypeName(false)),
- 'parseTypeParameter_0':
- new MethodTrampoline(0, (Parser target) => target.parseTypeParameter()),
- 'parseTypeParameterList_0': new MethodTrampoline(
- 0, (Parser target) => target.parseTypeParameterList()),
- 'parseWithClause_0':
- new MethodTrampoline(0, (Parser target) => target.parseWithClause()),
- 'advance_0': new MethodTrampoline(0, (Parser target) => target._advance()),
- 'appendScalarValue_5': new MethodTrampoline(
- 5,
- (Parser target, arg0, arg1, arg2, arg3, arg4) =>
- target._appendScalarValue(arg0, arg1, arg2, arg3, arg4)),
- '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()),
- 'createSyntheticKeyword_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._createSyntheticKeyword(arg0)),
- 'createSyntheticStringLiteral_0': new MethodTrampoline(
- 0, (Parser target) => target.createSyntheticStringLiteral()),
- 'createSyntheticToken_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._createSyntheticToken(arg0)),
- 'ensureAssignable_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._ensureAssignable(arg0)),
- 'expect_1':
- new MethodTrampoline(1, (Parser target, arg0) => target._expect(arg0)),
- 'expectGt_0': new MethodTrampoline(0, (Parser target) => target._expectGt()),
- 'expectKeyword_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._expectKeyword(arg0)),
- 'findRange_2': new MethodTrampoline(
- 2,
- (Parser target, List<List<int>> arg0, int arg1) =>
- target._findRange(arg0, arg1)),
- 'getCodeBlockRanges_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._getCodeBlockRanges(arg0)),
- 'getEndToken_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._getEndToken(arg0)),
- 'injectToken_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._injectToken(arg0)),
- 'isFunctionDeclaration_0': new MethodTrampoline(
- 0, (Parser target) => target.isFunctionDeclaration()),
- 'isFunctionExpression_1': new MethodTrampoline(
- 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()),
- '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()),
- 'isTypedIdentifier_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._isTypedIdentifier(arg0)),
- 'lockErrorListener_0':
- new MethodTrampoline(0, (Parser target) => target._lockErrorListener()),
- 'matches_1':
- new MethodTrampoline(1, (Parser target, arg0) => target._matches(arg0)),
- 'matchesGt_0':
- new MethodTrampoline(0, (Parser target) => target._matchesGt()),
- 'matchesIdentifier_0':
- new MethodTrampoline(0, (Parser target) => target._matchesIdentifier()),
- 'matchesKeyword_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._matchesKeyword(arg0)),
- 'matchesString_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._matchesString(arg0)),
- '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()),
- 'parseAssignableExpression_1': new MethodTrampoline(
- 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()),
- 'parseBitwiseAndExpression_0': new MethodTrampoline(
- 0, (Parser target) => target.parseBitwiseAndExpression()),
- 'parseBitwiseXorExpression_0': new MethodTrampoline(
- 0, (Parser target) => target.parseBitwiseXorExpression()),
- 'parseBreakStatement_0':
- new MethodTrampoline(0, (Parser target) => target.parseBreakStatement()),
- 'parseCascadeSection_0':
- new MethodTrampoline(0, (Parser target) => target.parseCascadeSection()),
- 'parseClassDeclaration_2': new MethodTrampoline(2,
- (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)),
- 'parseCombinator_0':
- new MethodTrampoline(0, (Parser target) => target.parseCombinator()),
- 'parseCombinators_0':
- new MethodTrampoline(0, (Parser target) => target.parseCombinators()),
- 'parseCommentAndMetadata_0': new MethodTrampoline(
- 0, (Parser target) => target.parseCommentAndMetadata()),
- 'parseCommentReference_2': new MethodTrampoline(2,
- (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)),
- 'parseConfiguration_0':
- 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
- ._parseConstructor(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)),
- 'parseConstructorFieldInitializer_1': new MethodTrampoline(1,
- (Parser target, arg0) => target._parseConstructorFieldInitializer(arg0)),
- 'parseContinueStatement_0': new MethodTrampoline(
- 0, (Parser target) => target.parseContinueStatement()),
- 'parseDirective_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._parseDirective(arg0)),
- 'parseDirectives_0':
- new MethodTrampoline(0, (Parser target) => target.parseDirectives2()),
- 'parseDocumentationComment_0': new MethodTrampoline(0, (Parser target) {
- List<DocumentationCommentToken> tokens =
- target.parseDocumentationCommentTokens();
- return target.parseDocumentationComment(tokens);
- }),
- 'parseDoStatement_0':
- new MethodTrampoline(0, (Parser target) => target.parseDoStatement()),
- 'parseDottedName_0':
- new MethodTrampoline(0, (Parser target) => target.parseDottedName()),
- 'parseEmptyStatement_0':
- 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)),
- '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()),
- 'parseFinalConstVarOrType_1': new MethodTrampoline(
- 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()),
- 'parseFunctionBody_3': new MethodTrampoline(
- 3,
- (Parser target, 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()),
- 'parseFunctionDeclarationStatementAfterReturnType_2': new MethodTrampoline(
- 2,
- (Parser target, arg0, arg1) =>
- target._parseFunctionDeclarationStatementAfterReturnType(arg0, arg1)),
- 'parseFunctionTypeAlias_2': new MethodTrampoline(
- 2,
- (Parser target, arg0, arg1) =>
- target._parseFunctionTypeAlias(arg0, arg1)),
- 'parseGetter_4': new MethodTrampoline(
- 4,
- (Parser target, arg0, arg1, arg2, arg3) =>
- target._parseGetter(arg0, arg1, arg2, arg3)),
- 'parseIdentifierList_0':
- new MethodTrampoline(0, (Parser target) => target._parseIdentifierList()),
- 'parseIfStatement_0':
- new MethodTrampoline(0, (Parser target) => target.parseIfStatement()),
- 'parseImportDirective_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._parseImportDirective(arg0)),
- 'parseInitializedIdentifierList_4': new MethodTrampoline(
- 4,
- (Parser target, arg0, arg1, arg2, arg3) =>
- target._parseInitializedIdentifierList(arg0, arg1, arg2, arg3)),
- 'parseInstanceCreationExpression_1': new MethodTrampoline(1,
- (Parser target, arg0) => target._parseInstanceCreationExpression(arg0)),
- 'parseLibraryDirective_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._parseLibraryDirective(arg0)),
- 'parseLibraryName_2': new MethodTrampoline(
- 2, (Parser target, arg0, arg1) => target._parseLibraryName(arg0, arg1)),
- 'parseListLiteral_2': new MethodTrampoline(
- 2, (Parser target, arg0, arg1) => target._parseListLiteral(arg0, arg1)),
- 'parseListOrMapLiteral_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target.parseListOrMapLiteral(arg0)),
- 'parseLogicalAndExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parseLogicalAndExpression()),
- 'parseMapLiteral_2': new MethodTrampoline(
- 2, (Parser target, arg0, arg1) => target._parseMapLiteral(arg0, arg1)),
- 'parseMethodDeclarationAfterParameters_7': new MethodTrampoline(
- 7,
- (Parser target, arg0, arg1, arg2, arg3, arg4, arg5, arg6) =>
- target._parseMethodDeclarationAfterParameters(
- arg0, arg1, arg2, arg3, arg4, arg5, arg6)),
- 'parseMethodDeclarationAfterReturnType_4': new MethodTrampoline(
- 4,
- (Parser target, arg0, arg1, arg2, arg3) => target
- ._parseMethodDeclarationAfterReturnType(arg0, arg1, arg2, arg3)),
- 'parseModifiers_0':
- new MethodTrampoline(0, (Parser target) => target._parseModifiers()),
- 'parseMultiplicativeExpression_0': new MethodTrampoline(
- 0, (Parser target) => target.parseMultiplicativeExpression()),
- 'parseNativeClause_0':
- new MethodTrampoline(0, (Parser target) => target._parseNativeClause()),
- 'parseNewExpression_0':
- new MethodTrampoline(0, (Parser target) => target._parseNewExpression()),
- 'parseNonLabeledStatement_0': new MethodTrampoline(
- 0, (Parser target) => target._parseNonLabeledStatement()),
- 'parseOperator_3': new MethodTrampoline(
- 3,
- (Parser target, arg0, arg1, arg2) =>
- target.parseOperator(arg0, arg1, arg2)),
- 'parseOptionalReturnType_0': new MethodTrampoline(
- 0, (Parser target) => target._parseOptionalReturnType()),
- 'parsePartDirective_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._parsePartOrPartOfDirective(arg0)),
- 'parsePostfixExpression_0': new MethodTrampoline(
- 0, (Parser target) => target._parsePostfixExpression()),
- 'parsePrimaryExpression_0': new MethodTrampoline(
- 0, (Parser target) => target.parsePrimaryExpression()),
- 'parseRedirectingConstructorInvocation_1': new MethodTrampoline(
- 1,
- (Parser target, arg0) =>
- target._parseRedirectingConstructorInvocation(arg0)),
- 'parseRelationalExpression_0': new MethodTrampoline(
- 0, (Parser target) => target.parseRelationalExpression()),
- 'parseRethrowExpression_0': new MethodTrampoline(
- 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()),
- '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()),
- 'parseSymbolLiteral_0':
- 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()),
- 'parseTryStatement_0':
- 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()),
- 'parseVariableDeclaration_0': new MethodTrampoline(
- 0, (Parser target) => target.parseVariableDeclaration()),
- 'parseVariableDeclarationListAfterMetadata_1': new MethodTrampoline(
- 1,
- (Parser target, arg0) =>
- target.parseVariableDeclarationListAfterMetadata(arg0)),
- 'parseVariableDeclarationListAfterType_3': new MethodTrampoline(
- 3,
- (Parser target, arg0, arg1, arg2) =>
- target.parseVariableDeclarationListAfterType(arg0, arg1, arg2)),
- 'parseVariableDeclarationStatementAfterMetadata_1': new MethodTrampoline(
- 1,
- (Parser target, 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()),
- 'parseYieldStatement_0':
- 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)),
- 'reportError_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._reportError(arg0)),
- 'reportErrorForCurrentToken_2': new MethodTrampoline(
- 2,
- (Parser target, arg0, arg1) =>
- target._reportErrorForCurrentToken(arg0, arg1)),
- 'reportErrorForNode_3': new MethodTrampoline(
- 3,
- (Parser target, arg0, arg1, arg2) =>
- target._reportErrorForNode(arg0, arg1, arg2)),
- 'reportErrorForToken_3': new MethodTrampoline(
- 3,
- (Parser target, arg0, arg1, arg2) =>
- target._reportErrorForToken(arg0, arg1, arg2)),
- 'skipBlock_0':
- new MethodTrampoline(0, (Parser target) => target._skipBlock()),
- 'skipFinalConstVarOrType_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._skipFinalConstVarOrType(arg0)),
- 'skipFormalParameterList_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._skipFormalParameterList(arg0)),
- 'skipPastMatchingToken_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._skipPastMatchingToken(arg0)),
- 'skipPrefixedIdentifier_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target.skipPrefixedIdentifier(arg0)),
- 'skipReturnType_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target.skipReturnType(arg0)),
- 'skipSimpleIdentifier_1': new MethodTrampoline(
- 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)),
- 'skipTypeArgumentList_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target.skipTypeArgumentList(arg0)),
- 'skipTypeName_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target.skipTypeName(arg0)),
- 'skipTypeParameterList_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._skipTypeParameterList(arg0)),
- 'tokenMatches_2': new MethodTrampoline(
- 2, (Parser target, arg0, arg1) => target._tokenMatches(arg0, arg1)),
- 'tokenMatchesIdentifier_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._tokenMatchesIdentifier(arg0)),
- 'tokenMatchesKeyword_2': new MethodTrampoline(2,
- (Parser target, arg0, arg1) => target._tokenMatchesKeyword(arg0, arg1)),
- 'tokenMatchesString_2': new MethodTrampoline(
- 2, (Parser target, arg0, arg1) => target._tokenMatchesString(arg0, arg1)),
- 'unlockErrorListener_0':
- new MethodTrampoline(0, (Parser target) => target._unlockErrorListener()),
- 'validateFormalParameterList_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._validateFormalParameterList(arg0)),
- 'validateModifiersForClass_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._validateModifiersForClass(arg0)),
- 'validateModifiersForConstructor_1': new MethodTrampoline(1,
- (Parser target, arg0) => target._validateModifiersForConstructor(arg0)),
- 'validateModifiersForEnum_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._validateModifiersForEnum(arg0)),
- 'validateModifiersForField_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._validateModifiersForField(arg0)),
- 'validateModifiersForFunctionDeclarationStatement_1': new MethodTrampoline(
- 1,
- (Parser target, arg0) =>
- target._validateModifiersForFunctionDeclarationStatement(arg0)),
- 'validateModifiersForGetterOrSetterOrMethod_1': new MethodTrampoline(
- 1,
- (Parser target, arg0) =>
- target._validateModifiersForGetterOrSetterOrMethod(arg0)),
- 'validateModifiersForOperator_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._validateModifiersForOperator(arg0)),
- 'validateModifiersForTopLevelDeclaration_1': new MethodTrampoline(
- 1,
- (Parser target, arg0) =>
- target._validateModifiersForTopLevelDeclaration(arg0)),
- 'validateModifiersForTopLevelFunction_1': new MethodTrampoline(
- 1,
- (Parser target, arg0) =>
- target._validateModifiersForTopLevelFunction(arg0)),
- 'validateModifiersForTopLevelVariable_1': new MethodTrampoline(
- 1,
- (Parser target, arg0) =>
- target._validateModifiersForTopLevelVariable(arg0)),
- 'validateModifiersForTypedef_1': new MethodTrampoline(
- 1, (Parser target, arg0) => target._validateModifiersForTypedef(arg0)),
-};
-
-Object invokeParserMethodImpl(
- Parser parser, String methodName, List<Object> objects) {
- MethodTrampoline method =
- methodTable_Parser['${methodName}_${objects.length}'];
- if (method == null) {
- throw new ArgumentError('There is no method named $methodName');
- }
- return method.invoke(parser, objects);
-}
-
/**
* A simple data-holder for a method that needs to return multiple values.
*/
@@ -783,7 +331,7 @@ class Parser {
* Return `true` if the current token is the first token of a return type that
* is followed by an identifier, possibly followed by a list of type
* parameters, followed by a left-parenthesis. This is used by
- * [_parseTypeAlias] to determine whether or not to parse a return type.
+ * [parseTypeAlias] to determine whether or not to parse a return type.
*/
@deprecated
bool get hasReturnTypeInTypeAlias {
@@ -1219,7 +767,7 @@ class Parser {
*/
Expression parseAssignableExpression(bool primaryAllowed) {
if (_matchesKeyword(Keyword.SUPER)) {
- return _parseAssignableSelector(
+ return parseAssignableSelector(
new SuperExpression(getAndAdvance()), false,
allowConditional: false);
}
@@ -1227,6 +775,57 @@ class Parser {
}
/**
+ * 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.
+ *
+ * unconditionalAssignableSelector ::=
+ * '[' expression ']'
+ * | '.' identifier
+ *
+ * assignableSelector ::=
+ * unconditionalAssignableSelector
+ * | '?.' identifier
+ */
+ 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;
+ }
+ }
+ }
+
+ /**
* Parse a await expression. Return the await expression that was parsed.
*
* This method assumes that the current token matches `_AWAIT`.
@@ -1437,7 +1036,7 @@ class Parser {
bool progress = true;
while (progress) {
progress = false;
- Expression selector = _parseAssignableSelector(expression, true);
+ Expression selector = parseAssignableSelector(expression, true);
if (!identical(selector, expression)) {
expression = selector;
progress = true;
@@ -1611,7 +1210,7 @@ class Parser {
*/
ClassMember parseClassMember(String className) {
CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
- Modifiers modifiers = _parseModifiers();
+ Modifiers modifiers = parseModifiers();
Keyword keyword = _currentToken.keyword;
if (keyword == Keyword.VOID) {
TypeName returnType =
@@ -1621,11 +1220,11 @@ class Parser {
bool isFollowedByIdentifier = _tokenMatchesIdentifier(next);
if (keyword == Keyword.GET && isFollowedByIdentifier) {
_validateModifiersForGetterOrSetterOrMethod(modifiers);
- return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
+ return parseGetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, returnType);
} else if (keyword == Keyword.SET && isFollowedByIdentifier) {
_validateModifiersForGetterOrSetterOrMethod(modifiers);
- return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
+ return parseSetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, returnType);
} else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
_validateModifiersForOperator(modifiers);
@@ -1655,7 +1254,7 @@ class Parser {
// We appear to have a variable declaration with a type of "void".
//
_reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType);
- return _parseInitializedIdentifierList(
+ return parseInitializedIdentifierList(
commentAndMetadata,
modifiers.staticKeyword,
_validateModifiersForField(modifiers),
@@ -1680,11 +1279,11 @@ class Parser {
bool isFollowedByIdentifier = _tokenMatchesIdentifier(next);
if (keyword == Keyword.GET && isFollowedByIdentifier) {
_validateModifiersForGetterOrSetterOrMethod(modifiers);
- return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
+ return parseGetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, null);
} else if (keyword == Keyword.SET && isFollowedByIdentifier) {
_validateModifiersForGetterOrSetterOrMethod(modifiers);
- return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
+ return parseSetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, null);
} else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
_validateModifiersForOperator(modifiers);
@@ -1820,7 +1419,7 @@ class Parser {
_reportErrorForCurrentToken(
ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE);
}
- return _parseInitializedIdentifierList(commentAndMetadata,
+ return parseInitializedIdentifierList(commentAndMetadata,
modifiers.staticKeyword, _validateModifiersForField(modifiers), null);
} else if (keyword == Keyword.TYPEDEF) {
_reportErrorForCurrentToken(ParserErrorCode.TYPEDEF_IN_CLASS);
@@ -1841,11 +1440,11 @@ class Parser {
isFollowedByIdentifier = _tokenMatchesIdentifier(next);
if (keyword == Keyword.GET && isFollowedByIdentifier) {
_validateModifiersForGetterOrSetterOrMethod(modifiers);
- return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
+ return parseGetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, type);
} else if (keyword == Keyword.SET && isFollowedByIdentifier) {
_validateModifiersForGetterOrSetterOrMethod(modifiers);
- return _parseSetter(commentAndMetadata, modifiers.externalKeyword,
+ return parseSetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, type);
} else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
_validateModifiersForOperator(modifiers);
@@ -1858,7 +1457,7 @@ class Parser {
// class. At this point it consists of a type name, so we'll treat it as
// a field declaration with a missing field name and semicolon.
//
- return _parseInitializedIdentifierList(
+ return parseInitializedIdentifierList(
commentAndMetadata,
modifiers.staticKeyword,
_validateModifiersForField(modifiers),
@@ -1882,7 +1481,7 @@ class Parser {
ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken);
try {
_lockErrorListener();
- return _parseInitializedIdentifierList(
+ return parseInitializedIdentifierList(
commentAndMetadata,
modifiers.staticKeyword,
_validateModifiersForField(modifiers),
@@ -1927,10 +1526,10 @@ class Parser {
_reportErrorForCurrentToken(ParserErrorCode.MISSING_GET);
_currentToken = _injectToken(
new Parser_SyntheticKeywordToken(Keyword.GET, _currentToken.offset));
- return _parseGetter(commentAndMetadata, modifiers.externalKeyword,
+ return parseGetter(commentAndMetadata, modifiers.externalKeyword,
modifiers.staticKeyword, type);
}
- return _parseInitializedIdentifierList(commentAndMetadata,
+ return parseInitializedIdentifierList(commentAndMetadata,
modifiers.staticKeyword, _validateModifiersForField(modifiers), type);
}
@@ -1970,9 +1569,9 @@ class Parser {
*/
Combinator parseCombinator() {
if (_matchesString(_SHOW)) {
- return new ShowCombinator(getAndAdvance(), _parseIdentifierList());
+ return new ShowCombinator(getAndAdvance(), parseIdentifierList());
} else if (_matchesString(_HIDE)) {
- return new HideCombinator(getAndAdvance(), _parseIdentifierList());
+ return new HideCombinator(getAndAdvance(), parseIdentifierList());
}
return null;
}
@@ -2134,6 +1733,90 @@ class Parser {
}
/**
+ * 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 ')'
+ */
+ 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]);
+ }
+ }
+ }
+ return references;
+ }
+
+ /**
* Parse a compilation unit, starting with the given [token]. Return the
* compilation unit that was parsed.
*/
@@ -2195,13 +1878,13 @@ class Parser {
_reportErrorForCurrentToken(
ParserErrorCode.IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE);
}
- return _parseImportDirective(commentAndMetadata);
+ return parseImportDirective(commentAndMetadata);
} else if (keyword == Keyword.EXPORT) {
if (partDirectiveFound) {
_reportErrorForCurrentToken(
ParserErrorCode.EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE);
}
- return _parseExportDirective(commentAndMetadata);
+ return parseExportDirective(commentAndMetadata);
} else if (keyword == Keyword.LIBRARY) {
if (libraryDirectiveFound) {
_reportErrorForCurrentToken(
@@ -2213,7 +1896,7 @@ class Parser {
}
libraryDirectiveFound = true;
}
- return _parseLibraryDirective(commentAndMetadata);
+ return parseLibraryDirective(commentAndMetadata);
} else if (keyword == Keyword.PART) {
if (_tokenMatchesString(_peek(), _OF)) {
partOfDirectiveFound = true;
@@ -2317,7 +2000,7 @@ class Parser {
*/
CompilationUnitMember parseCompilationUnitMember(
CommentAndMetadata commentAndMetadata) {
- Modifiers modifiers = _parseModifiers();
+ Modifiers modifiers = parseModifiers();
Keyword keyword = _currentToken.keyword;
if (keyword == Keyword.CLASS) {
return parseClassDeclaration(
@@ -2330,7 +2013,7 @@ class Parser {
nextType != TokenType.LT &&
nextType != TokenType.OPEN_PAREN) {
_validateModifiersForTypedef(modifiers);
- return _parseTypeAlias(commentAndMetadata);
+ return parseTypeAlias(commentAndMetadata);
} else if (keyword == Keyword.ENUM) {
_validateModifiersForEnum(modifiers);
return parseEnumDeclaration(commentAndMetadata);
@@ -2342,7 +2025,7 @@ class Parser {
if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
_tokenMatchesIdentifier(next)) {
_validateModifiersForTopLevelFunction(modifiers);
- return _parseFunctionDeclaration(
+ return parseFunctionDeclaration(
commentAndMetadata, modifiers.externalKeyword, returnType);
} else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
_reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
@@ -2359,7 +2042,7 @@ class Parser {
TokenType.LT
])) {
_validateModifiersForTopLevelFunction(modifiers);
- return _parseFunctionDeclaration(
+ return parseFunctionDeclaration(
commentAndMetadata, modifiers.externalKeyword, returnType);
} else {
//
@@ -2390,7 +2073,7 @@ class Parser {
} else if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
_tokenMatchesIdentifier(next)) {
_validateModifiersForTopLevelFunction(modifiers);
- return _parseFunctionDeclaration(
+ return parseFunctionDeclaration(
commentAndMetadata, modifiers.externalKeyword, null);
} else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
_reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
@@ -2424,12 +2107,12 @@ class Parser {
_reportErrorForToken(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken);
return null;
} else if (_isPeekGenericTypeParametersAndOpenParen()) {
- return _parseFunctionDeclaration(
+ return parseFunctionDeclaration(
commentAndMetadata, modifiers.externalKeyword, null);
} else if (_tokenMatches(next, TokenType.OPEN_PAREN)) {
TypeName returnType = _parseOptionalTypeNameComment();
_validateModifiersForTopLevelFunction(modifiers);
- return _parseFunctionDeclaration(
+ return parseFunctionDeclaration(
commentAndMetadata, modifiers.externalKeyword, returnType);
} else if (next.matchesAny(const <TokenType>[
TokenType.EQ,
@@ -2455,7 +2138,7 @@ class Parser {
if ((keyword == Keyword.GET || keyword == Keyword.SET) &&
_tokenMatchesIdentifier(next)) {
_validateModifiersForTopLevelFunction(modifiers);
- return _parseFunctionDeclaration(
+ return parseFunctionDeclaration(
commentAndMetadata, modifiers.externalKeyword, returnType);
} else if (keyword == Keyword.OPERATOR && _isOperator(next)) {
_reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken);
@@ -2496,7 +2179,7 @@ class Parser {
TokenType.LT
])) {
_validateModifiersForTopLevelFunction(modifiers);
- return _parseFunctionDeclaration(
+ return parseFunctionDeclaration(
commentAndMetadata, modifiers.externalKeyword, returnType);
}
return new TopLevelVariableDeclaration(
@@ -2578,11 +2261,66 @@ class Parser {
return parseListOrMapLiteral(keyword);
} else if (type == TokenType.OPEN_SQUARE_BRACKET ||
type == TokenType.INDEX) {
- return _parseListLiteral(keyword, null);
+ return parseListLiteral(keyword, null);
} else if (type == TokenType.OPEN_CURLY_BRACKET) {
- return _parseMapLiteral(keyword, null);
+ return parseMapLiteral(keyword, null);
+ }
+ return parseInstanceCreationExpression(keyword);
+ }
+
+ /**
+ * Parse a field initializer within a constructor. The flag [hasThis] should
+ * be true if the current token is `this`. Return the field initializer that
+ * was parsed.
+ *
+ * fieldInitializer:
+ * ('this' '.')? identifier '=' conditionalExpression cascadeSection*
+ */
+ ConstructorFieldInitializer parseConstructorFieldInitializer(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;
}
- return _parseInstanceCreationExpression(keyword);
}
/**
@@ -2630,6 +2368,33 @@ class Parser {
}
/**
+ * 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
+ */
+ 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");
+ }
+ }
+
+ /**
* 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
@@ -2666,7 +2431,7 @@ class Parser {
type != TokenType.PERIOD &&
type != TokenType.LT &&
type != TokenType.OPEN_PAREN) {
- directives.add(_parseDirective(commentAndMetadata));
+ directives.add(parseDirective(commentAndMetadata));
} else if (_matches(TokenType.SEMICOLON)) {
_advance();
} else {
@@ -2694,7 +2459,7 @@ class Parser {
if (tokens == null) {
return null;
}
- List<CommentReference> references = _parseCommentReferences(tokens);
+ List<CommentReference> references = parseCommentReferences(tokens);
return Comment.createDocumentationCommentWithReferences(tokens, references);
}
@@ -2829,27 +2594,81 @@ class Parser {
}
/**
- * 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.
+ * Parse an equality expression. Return the equality expression that was
+ * parsed.
+ *
+ * equalityExpression ::=
+ * relationalExpression (equalityOperator relationalExpression)?
+ * | 'super' equalityOperator relationalExpression
*/
- Expression parseExpression(Token token) {
- _currentToken = token;
- return parseExpression2();
+ Expression parseEqualityExpression() {
+ Expression expression;
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type.isEqualityOperator) {
+ expression = new SuperExpression(getAndAdvance());
+ } else {
+ expression = parseRelationalExpression();
+ }
+ 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 an expression that might contain a cascade. Return the expression
- * that was parsed.
+ * Parse an export directive. The [commentAndMetadata] is the metadata to be
+ * associated with the directive. Return the export directive that was parsed.
*
- * expression ::=
- * assignableExpression assignmentOperator expression
- * | conditionalExpression cascadeSection*
- * | throwExpression
+ * This method assumes that the current token matches `Keyword.EXPORT`.
+ *
+ * exportDirective ::=
+ * metadata 'export' stringLiteral configuration* combinator*';'
*/
- Expression parseExpression2() {
- Keyword keyword = _currentToken.keyword;
- if (keyword == Keyword.THROW) {
+ 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 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();
+ }
+
+ /**
+ * Parse an expression that might contain a cascade. Return the expression
+ * that was parsed.
+ *
+ * expression ::=
+ * assignableExpression assignmentOperator expression
+ * | conditionalExpression cascadeSection*
+ * | throwExpression
+ */
+ Expression parseExpression2() {
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.THROW) {
return parseThrowExpression();
} else if (keyword == Keyword.RETHROW) {
// TODO(brianwilkerson) Rethrow is a statement again.
@@ -2985,6 +2804,52 @@ class Parser {
}
/**
+ * 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)?
+ *
+ * defaultNamedParameter ::=
+ * normalFormalParameter (':' expression)?
+ */
+ 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);
+ }
+ return parameter;
+ }
+
+ /**
* Parse a list of formal parameters. Return the formal parameters that were
* parsed.
*
@@ -3289,6 +3154,82 @@ class Parser {
}
/**
+ * Parse a function declaration. The [commentAndMetadata] is the documentation
+ * comment and metadata to be associated with the declaration. The
+ * [externalKeyword] is the 'external' keyword, or `null` if the function is
+ * not external. The [returnType] is the return type, or `null` if there is no
+ * return type. The [isStatement] is `true` if the function declaration is
+ * being parsed as a statement. Return the function declaration that was
+ * parsed.
+ *
+ * functionDeclaration ::=
+ * functionSignature functionBody
+ * | returnType? getOrSet identifier formalParameterList functionBody
+ */
+ FunctionDeclaration parseFunctionDeclaration(
+ CommentAndMetadata commentAndMetadata,
+ Token externalKeyword,
+ TypeName returnType) {
+ Token keywordToken = null;
+ bool isGetter = false;
+ Keyword keyword = _currentToken.keyword;
+ SimpleIdentifier name = null;
+ if (keyword == Keyword.GET) {
+ keywordToken = getAndAdvance();
+ isGetter = true;
+ } else if (keyword == Keyword.SET) {
+ keywordToken = getAndAdvance();
+ }
+ if (keywordToken != null && _matches(TokenType.OPEN_PAREN)) {
+ name = new SimpleIdentifier(keywordToken, isDeclaration: true);
+ keywordToken = null;
+ isGetter = false;
+ } else {
+ name = parseSimpleIdentifier(isDeclaration: true);
+ }
+ TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
+ FormalParameterList parameters = null;
+ if (!isGetter) {
+ if (_matches(TokenType.OPEN_PAREN)) {
+ parameters = _parseFormalParameterListUnchecked();
+ _validateFormalParameterList(parameters);
+ } else {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MISSING_FUNCTION_PARAMETERS);
+ parameters = new FormalParameterList(
+ _createSyntheticToken(TokenType.OPEN_PAREN),
+ null,
+ null,
+ null,
+ _createSyntheticToken(TokenType.CLOSE_PAREN));
+ }
+ } else if (_matches(TokenType.OPEN_PAREN)) {
+ _reportErrorForCurrentToken(ParserErrorCode.GETTER_WITH_PARAMETERS);
+ _parseFormalParameterListUnchecked();
+ }
+ FunctionBody body;
+ if (externalKeyword == null) {
+ body = parseFunctionBody(
+ false, ParserErrorCode.MISSING_FUNCTION_BODY, false);
+ } else {
+ body = new EmptyFunctionBody(_expect(TokenType.SEMICOLON));
+ }
+// if (!isStatement && matches(TokenType.SEMICOLON)) {
+// // TODO(brianwilkerson) Improve this error message.
+// reportError(ParserErrorCode.UNEXPECTED_TOKEN, currentToken.getLexeme());
+// advance();
+// }
+ return new FunctionDeclaration(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ externalKeyword,
+ returnType,
+ keywordToken,
+ name,
+ new FunctionExpression(typeParameters, parameters, body));
+ }
+
+ /**
* Parse a function declaration statement. Return the function declaration
* statement that was parsed.
*
@@ -3296,7 +3237,7 @@ class Parser {
* functionSignature functionBody
*/
Statement parseFunctionDeclarationStatement() {
- Modifiers modifiers = _parseModifiers();
+ Modifiers modifiers = parseModifiers();
_validateModifiersForFunctionDeclarationStatement(modifiers);
return _parseFunctionDeclarationStatementAfterReturnType(
parseCommentAndMetadata(), _parseOptionalReturnType());
@@ -3319,6 +3260,70 @@ class Parser {
}
/**
+ * Parse a getter. The [commentAndMetadata] is the documentation comment and
+ * metadata to be associated with the declaration. The externalKeyword] is the
+ * 'external' token. The staticKeyword] is the static keyword, or `null` if
+ * the getter is not static. The [returnType] the return type that has already
+ * been parsed, or `null` if there was no return type. Return the getter that
+ * was parsed.
+ *
+ * This method assumes that the current token matches `Keyword.GET`.
+ *
+ * getter ::=
+ * getterSignature functionBody?
+ *
+ * getterSignature ::=
+ * 'external'? 'static'? returnType? 'get' identifier
+ */
+ MethodDeclaration parseGetter(CommentAndMetadata commentAndMetadata,
+ Token externalKeyword, Token staticKeyword, TypeName returnType) {
+ Token propertyKeyword = getAndAdvance();
+ SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
+ if (_matches(TokenType.OPEN_PAREN) &&
+ _tokenMatches(_peek(), TokenType.CLOSE_PAREN)) {
+ _reportErrorForCurrentToken(ParserErrorCode.GETTER_WITH_PARAMETERS);
+ _advance();
+ _advance();
+ }
+ FunctionBody body = parseFunctionBody(
+ externalKeyword != null || staticKeyword == null,
+ ParserErrorCode.STATIC_GETTER_WITHOUT_BODY,
+ false);
+ if (externalKeyword != null && body is! EmptyFunctionBody) {
+ _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_GETTER_WITH_BODY);
+ }
+ return new MethodDeclaration(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ externalKeyword,
+ staticKeyword,
+ returnType,
+ propertyKeyword,
+ null,
+ name,
+ null,
+ null,
+ body);
+ }
+
+ /**
+ * Parse a list of identifiers. Return the list of identifiers that were
+ * parsed.
+ *
+ * identifierList ::=
+ * identifier (',' identifier)*
+ */
+ List<SimpleIdentifier> parseIdentifierList() {
+ List<SimpleIdentifier> identifiers = <SimpleIdentifier>[
+ parseSimpleIdentifier()
+ ];
+ while (_optional(TokenType.COMMA)) {
+ identifiers.add(parseSimpleIdentifier());
+ }
+ return identifiers;
+ }
+
+ /**
* Parse an if-null expression. Return the if-null expression that was
* parsed.
*
@@ -3376,34 +3381,211 @@ class Parser {
}
/**
- * Parse a label. Return the label that was parsed.
- *
- * This method assumes that the current token matches an identifier and that
- * the following token matches `TokenType.COLON`.
+ * Parse an import directive. The [commentAndMetadata] is the metadata to be
+ * associated with the directive. Return the import directive that was parsed.
*
- * 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.
+ * This method assumes that the current token matches `Keyword.IMPORT`.
*
- * libraryIdentifier ::=
- * identifier ('.' identifier)*
+ * importDirective ::=
+ * metadata 'import' stringLiteral configuration* (deferred)? ('as' identifier)? combinator*';'
*/
- LibraryIdentifier parseLibraryIdentifier() {
- List<SimpleIdentifier> components = <SimpleIdentifier>[];
- components.add(parseSimpleIdentifier());
- while (_optional(TokenType.PERIOD)) {
- components.add(parseSimpleIdentifier());
+ ImportDirective parseImportDirective(CommentAndMetadata commentAndMetadata) {
+ Token importKeyword = getAndAdvance();
+ StringLiteral libraryUri = _parseUri();
+ List<Configuration> configurations = _parseConfigurations();
+ Token deferredToken = null;
+ Token asToken = null;
+ SimpleIdentifier prefix = null;
+ if (_matchesKeyword(Keyword.DEFERRED)) {
+ deferredToken = getAndAdvance();
+ }
+ if (_matchesKeyword(Keyword.AS)) {
+ asToken = getAndAdvance();
+ prefix = parseSimpleIdentifier(isDeclaration: true);
+ } else if (deferredToken != null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.MISSING_PREFIX_IN_DEFERRED_IMPORT);
+ } else if (!_matches(TokenType.SEMICOLON) &&
+ !_matchesString(_SHOW) &&
+ !_matchesString(_HIDE)) {
+ Token nextToken = _peek();
+ if (_tokenMatchesKeyword(nextToken, Keyword.AS) ||
+ _tokenMatchesString(nextToken, _SHOW) ||
+ _tokenMatchesString(nextToken, _HIDE)) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken]);
+ _advance();
+ if (_matchesKeyword(Keyword.AS)) {
+ asToken = getAndAdvance();
+ prefix = parseSimpleIdentifier(isDeclaration: true);
+ }
+ }
+ }
+ List<Combinator> combinators = parseCombinators();
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new ImportDirective(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ importKeyword,
+ libraryUri,
+ configurations,
+ deferredToken,
+ asToken,
+ prefix,
+ combinators,
+ semicolon);
+ }
+
+ /**
+ * Parse a list of initialized identifiers. The [commentAndMetadata] is the
+ * documentation comment and metadata to be associated with the declaration.
+ * The [staticKeyword] is the static keyword, or `null` if the getter is not
+ * static. The [keyword] is the token representing the 'final', 'const' or
+ * 'var' keyword, or `null` if there is no keyword. The [type] is the type
+ * that has already been parsed, or `null` if 'var' was provided. Return the
+ * getter that was parsed.
+ *
+ * ?? ::=
+ * 'static'? ('var' | type) initializedIdentifierList ';'
+ * | 'final' type? initializedIdentifierList ';'
+ *
+ * initializedIdentifierList ::=
+ * initializedIdentifier (',' initializedIdentifier)*
+ *
+ * initializedIdentifier ::=
+ * identifier ('=' expression)?
+ */
+ FieldDeclaration parseInitializedIdentifierList(
+ CommentAndMetadata commentAndMetadata,
+ Token staticKeyword,
+ Token keyword,
+ TypeName type) {
+ VariableDeclarationList fieldList =
+ parseVariableDeclarationListAfterType(null, keyword, type);
+ return new FieldDeclaration(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ staticKeyword,
+ fieldList,
+ _expect(TokenType.SEMICOLON));
+ }
+
+ /**
+ * Parse an instance creation expression. The [keyword] is the 'new' or
+ * 'const' keyword that introduces the expression. Return the instance
+ * creation expression that was parsed.
+ *
+ * instanceCreationExpression ::=
+ * ('new' | 'const') type ('.' identifier)? argumentList
+ */
+ InstanceCreationExpression parseInstanceCreationExpression(Token keyword) {
+ ConstructorName constructorName = parseConstructorName();
+ ArgumentList argumentList = _parseArgumentListChecked();
+ return new InstanceCreationExpression(
+ keyword, constructorName, argumentList);
+ }
+
+ /**
+ * 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 directive. The [commentAndMetadata] is the metadata to be
+ * associated with the directive. Return the library directive that was
+ * parsed.
+ *
+ * This method assumes that the current token matches `Keyword.LIBRARY`.
+ *
+ * libraryDirective ::=
+ * metadata 'library' identifier ';'
+ */
+ LibraryDirective parseLibraryDirective(
+ CommentAndMetadata commentAndMetadata) {
+ Token keyword = getAndAdvance();
+ LibraryIdentifier libraryName = _parseLibraryName(
+ ParserErrorCode.MISSING_NAME_IN_LIBRARY_DIRECTIVE, keyword);
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new LibraryDirective(commentAndMetadata.comment,
+ commentAndMetadata.metadata, keyword, libraryName, semicolon);
+ }
+
+ /**
+ * Parse a library identifier. Return the library identifier that was parsed.
+ *
+ * libraryIdentifier ::=
+ * identifier ('.' identifier)*
+ */
+ LibraryIdentifier parseLibraryIdentifier() {
+ List<SimpleIdentifier> components = <SimpleIdentifier>[];
+ components.add(parseSimpleIdentifier());
+ while (_optional(TokenType.PERIOD)) {
+ components.add(parseSimpleIdentifier());
+ }
+ return new LibraryIdentifier(components);
+ }
+
+ /**
+ * Parse a list literal. The [modifier] is the 'const' modifier appearing
+ * before the literal, or `null` if there is no modifier. The [typeArguments]
+ * is the type arguments appearing before the literal, or `null` if there are
+ * no type arguments. Return the list literal that was parsed.
+ *
+ * This method assumes that the current token matches either
+ * `TokenType.OPEN_SQUARE_BRACKET` or `TokenType.INDEX`.
+ *
+ * listLiteral ::=
+ * 'const'? typeArguments? '[' (expressionList ','?)? ']'
+ */
+ ListLiteral parseListLiteral(Token modifier, TypeArgumentList typeArguments) {
+ if (_matches(TokenType.INDEX)) {
+ // Split the token into two separate tokens.
+ BeginToken leftBracket = _createToken(
+ _currentToken, TokenType.OPEN_SQUARE_BRACKET,
+ isBegin: true);
+ Token rightBracket =
+ new Token(TokenType.CLOSE_SQUARE_BRACKET, _currentToken.offset + 1);
+ leftBracket.endToken = rightBracket;
+ rightBracket.setNext(_currentToken.next);
+ leftBracket.setNext(rightBracket);
+ _currentToken.previous.setNext(leftBracket);
+ _currentToken = _currentToken.next;
+ return new ListLiteral(
+ modifier, typeArguments, leftBracket, null, rightBracket);
+ }
+ Token leftBracket = getAndAdvance();
+ if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
+ return new ListLiteral(
+ modifier, typeArguments, leftBracket, null, getAndAdvance());
+ }
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = false;
+ try {
+ List<Expression> elements = <Expression>[parseExpression2()];
+ while (_optional(TokenType.COMMA)) {
+ if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
+ return new ListLiteral(
+ modifier, typeArguments, leftBracket, elements, getAndAdvance());
+ }
+ elements.add(parseExpression2());
+ }
+ Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
+ return new ListLiteral(
+ modifier, typeArguments, leftBracket, elements, rightBracket);
+ } finally {
+ _inInitializer = wasInInitializer;
}
- return new LibraryIdentifier(components);
}
/**
@@ -3418,10 +3600,10 @@ class Parser {
TypedLiteral parseListOrMapLiteral(Token modifier) {
TypeArgumentList typeArguments = _parseOptionalTypeArguments();
if (_matches(TokenType.OPEN_CURLY_BRACKET)) {
- return _parseMapLiteral(modifier, typeArguments);
+ return parseMapLiteral(modifier, typeArguments);
} else if (_matches(TokenType.OPEN_SQUARE_BRACKET) ||
_matches(TokenType.INDEX)) {
- return _parseListLiteral(modifier, typeArguments);
+ return parseListLiteral(modifier, typeArguments);
}
_reportErrorForCurrentToken(ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL);
return new ListLiteral(
@@ -3433,6 +3615,22 @@ class Parser {
}
/**
+ * Parse a logical and expression. Return the logical and expression that was
+ * parsed.
+ *
+ * logicalAndExpression ::=
+ * equalityExpression ('&&' equalityExpression)*
+ */
+ Expression parseLogicalAndExpression() {
+ Expression expression = parseEqualityExpression();
+ while (_currentToken.type == TokenType.AMPERSAND_AMPERSAND) {
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), parseEqualityExpression());
+ }
+ return expression;
+ }
+
+ /**
* Parse a logical or expression. Return the logical or expression that was
* parsed.
*
@@ -3440,15 +3638,52 @@ class Parser {
* logicalAndExpression ('||' logicalAndExpression)*
*/
Expression parseLogicalOrExpression() {
- Expression expression = _parseLogicalAndExpression();
+ Expression expression = parseLogicalAndExpression();
while (_currentToken.type == TokenType.BAR_BAR) {
expression = new BinaryExpression(
- expression, getAndAdvance(), _parseLogicalAndExpression());
+ expression, getAndAdvance(), parseLogicalAndExpression());
}
return expression;
}
/**
+ * Parse a map literal. The [modifier] is the 'const' modifier appearing
+ * before the literal, or `null` if there is no modifier. The [typeArguments]
+ * is the type arguments that were declared, or `null` if there are no type
+ * arguments. Return the map literal that was parsed.
+ *
+ * This method assumes that the current token matches
+ * `TokenType.OPEN_CURLY_BRACKET`.
+ *
+ * mapLiteral ::=
+ * 'const'? typeArguments? '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}'
+ */
+ MapLiteral parseMapLiteral(Token modifier, TypeArgumentList typeArguments) {
+ Token leftBracket = getAndAdvance();
+ if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+ return new MapLiteral(
+ modifier, typeArguments, leftBracket, null, getAndAdvance());
+ }
+ bool wasInInitializer = _inInitializer;
+ _inInitializer = false;
+ try {
+ List<MapLiteralEntry> entries = <MapLiteralEntry>[parseMapLiteralEntry()];
+ while (_optional(TokenType.COMMA)) {
+ if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+ return new MapLiteral(
+ modifier, typeArguments, leftBracket, entries, getAndAdvance());
+ }
+ entries.add(parseMapLiteralEntry());
+ }
+ Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
+ return new MapLiteral(
+ modifier, typeArguments, leftBracket, entries, rightBracket);
+ } finally {
+ _inInitializer = wasInInitializer;
+ }
+ }
+
+ /**
* Parse a map literal entry. Return the map literal entry that was parsed.
*
* mapLiteralEntry ::=
@@ -3462,3577 +3697,3084 @@ class Parser {
}
/**
- * Parse a multiplicative expression. Return the multiplicative expression
- * that was parsed.
+ * Parse the modifiers preceding a declaration. This method allows the
+ * modifiers to appear in any order but does generate errors for duplicated
+ * modifiers. Checks for other problems, such as having the modifiers appear
+ * in the wrong order or specifying both 'const' and 'final', are reported in
+ * one of the methods whose name is prefixed with `validateModifiersFor`.
+ * Return the modifiers that were parsed.
*
- * multiplicativeExpression ::=
- * unaryExpression (multiplicativeOperator unaryExpression)*
- * | 'super' (multiplicativeOperator unaryExpression)+
+ * modifiers ::=
+ * ('abstract' | 'const' | 'external' | 'factory' | 'final' | 'static' | 'var')*
*/
- 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 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
- */
- 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);
+ Modifiers parseModifiers() {
+ Modifiers modifiers = new Modifiers();
+ bool progress = true;
+ while (progress) {
+ TokenType nextType = _peek().type;
+ if (nextType == TokenType.PERIOD ||
+ nextType == TokenType.LT ||
+ nextType == TokenType.OPEN_PAREN) {
+ return modifiers;
+ }
+ Keyword keyword = _currentToken.keyword;
+ if (keyword == Keyword.ABSTRACT) {
+ if (modifiers.abstractKeyword != null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+ _advance();
+ } else {
+ modifiers.abstractKeyword = getAndAdvance();
}
- Token question = null;
- if (enableNnbd && _matches(TokenType.QUESTION)) {
- question = getAndAdvance();
+ } else if (keyword == Keyword.CONST) {
+ if (modifiers.constKeyword != null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+ _advance();
+ } else {
+ modifiers.constKeyword = getAndAdvance();
+ }
+ } else if (keyword == Keyword.EXTERNAL) {
+ if (modifiers.externalKeyword != null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+ _advance();
+ } else {
+ modifiers.externalKeyword = getAndAdvance();
+ }
+ } else if (keyword == Keyword.FACTORY) {
+ if (modifiers.factoryKeyword != null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+ _advance();
+ } else {
+ modifiers.factoryKeyword = getAndAdvance();
+ }
+ } else if (keyword == Keyword.FINAL) {
+ if (modifiers.finalKeyword != null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+ _advance();
+ } else {
+ modifiers.finalKeyword = getAndAdvance();
+ }
+ } else if (keyword == Keyword.STATIC) {
+ if (modifiers.staticKeyword != null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+ _advance();
+ } else {
+ modifiers.staticKeyword = getAndAdvance();
+ }
+ } else if (keyword == Keyword.VAR) {
+ if (modifiers.varKeyword != null) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
+ _advance();
+ } else {
+ modifiers.varKeyword = 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);
+ progress = 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));
+ return modifiers;
}
/**
- * 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)
+ * Parse a multiplicative expression. Return the multiplicative expression
+ * that was parsed.
*
- * operatorSignature ::=
- * 'external'? returnType? 'operator' operator formalParameterList
+ * multiplicativeExpression ::=
+ * unaryExpression (multiplicativeOperator unaryExpression)*
+ * | 'super' (multiplicativeOperator unaryExpression)+
*/
- MethodDeclaration parseOperator(CommentAndMetadata commentAndMetadata,
- Token externalKeyword, TypeName returnType) {
- Token operatorKeyword;
- if (_matchesKeyword(Keyword.OPERATOR)) {
- operatorKeyword = getAndAdvance();
+ Expression parseMultiplicativeExpression() {
+ Expression expression;
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type.isMultiplicativeOperator) {
+ expression = new SuperExpression(getAndAdvance());
} else {
- _reportErrorForToken(
- ParserErrorCode.MISSING_KEYWORD_OPERATOR, _currentToken);
- operatorKeyword = _createSyntheticKeyword(Keyword.OPERATOR);
+ expression = parseUnaryExpression();
}
- return _parseOperatorAfterKeyword(
- commentAndMetadata, externalKeyword, returnType, operatorKeyword);
+ while (_currentToken.type.isMultiplicativeOperator) {
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), parseUnaryExpression());
+ }
+ return expression;
}
/**
- * Parse a prefixed identifier. Return the prefixed identifier that was
- * parsed.
+ * Parse a new expression. Return the new expression that was parsed.
*
- * prefixedIdentifier ::=
- * identifier ('.' identifier)?
+ * This method assumes that the current token matches `Keyword.NEW`.
+ *
+ * newExpression ::=
+ * instanceCreationExpression
*/
- Identifier parsePrefixedIdentifier() {
- return _parsePrefixedIdentifierAfterIdentifier(parseSimpleIdentifier());
- }
+ InstanceCreationExpression parseNewExpression() =>
+ parseInstanceCreationExpression(getAndAdvance());
/**
- * Parse a primary expression. Return the primary expression that was parsed.
- *
- * primary ::=
- * thisExpression
- * | 'super' unconditionalAssignableSelector
- * | functionExpression
- * | literal
- * | identifier
- * | newExpression
- * | constObjectExpression
- * | '(' expression ')'
- * | argumentDefinitionTest
+ * Parse a non-labeled statement. Return the non-labeled statement that was
+ * parsed.
*
- * literal ::=
- * nullLiteral
- * | booleanLiteral
- * | numericLiteral
- * | stringLiteral
- * | symbolLiteral
- * | mapLiteral
- * | listLiteral
+ * nonLabeledStatement ::=
+ * block
+ * | assertStatement
+ * | breakStatement
+ * | continueStatement
+ * | doStatement
+ * | forStatement
+ * | ifStatement
+ * | returnStatement
+ * | switchStatement
+ * | tryStatement
+ * | whileStatement
+ * | variableDeclarationList ';'
+ * | expressionStatement
+ * | functionSignature functionBody
*/
- 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();
- }
+ Statement parseNonLabeledStatement() {
+ // TODO(brianwilkerson) Pass the comment and metadata on where appropriate.
+ CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
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.
+ if (type == TokenType.OPEN_CURLY_BRACKET) {
+ if (_tokenMatches(_peek(), TokenType.STRING)) {
+ Token afterString = skipStringLiteral(_currentToken.next);
+ if (afterString != null && afterString.type == TokenType.COLON) {
+ return new ExpressionStatement(
+ parseExpression2(), _expect(TokenType.SEMICOLON));
+ }
}
- 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 parseBlock();
+ } else if (type == TokenType.KEYWORD &&
+ !_currentToken.keyword.isPseudoKeyword) {
+ Keyword keyword = _currentToken.keyword;
+ // TODO(jwren) compute some metrics to figure out a better order for this
+ // if-then sequence to optimize performance
+ if (keyword == Keyword.ASSERT) {
+ return parseAssertStatement();
+ } else if (keyword == Keyword.BREAK) {
+ return parseBreakStatement();
+ } else if (keyword == Keyword.CONTINUE) {
+ return parseContinueStatement();
+ } else if (keyword == Keyword.DO) {
+ return parseDoStatement();
+ } else if (keyword == Keyword.FOR) {
+ return parseForStatement();
+ } else if (keyword == Keyword.IF) {
+ return parseIfStatement();
+ } else if (keyword == Keyword.RETHROW) {
+ return new ExpressionStatement(
+ parseRethrowExpression(), _expect(TokenType.SEMICOLON));
+ } else if (keyword == Keyword.RETURN) {
+ return parseReturnStatement();
+ } else if (keyword == Keyword.SWITCH) {
+ return parseSwitchStatement();
+ } else if (keyword == Keyword.THROW) {
+ return new ExpressionStatement(
+ parseThrowExpression(), _expect(TokenType.SEMICOLON));
+ } else if (keyword == Keyword.TRY) {
+ return parseTryStatement();
+ } else if (keyword == Keyword.WHILE) {
+ return parseWhileStatement();
+ } else if (keyword == Keyword.VAR || keyword == Keyword.FINAL) {
+ return parseVariableDeclarationStatementAfterMetadata(
+ commentAndMetadata);
+ } else if (keyword == Keyword.VOID) {
+ TypeName returnType =
+ new TypeName(new SimpleIdentifier(getAndAdvance()), null);
+ Token next = _currentToken.next;
+ if (_matchesIdentifier() &&
+ next.matchesAny(const <TokenType>[
+ TokenType.OPEN_PAREN,
+ TokenType.OPEN_CURLY_BRACKET,
+ TokenType.FUNCTION,
+ TokenType.LT
+ ])) {
+ return _parseFunctionDeclarationStatementAfterReturnType(
+ commentAndMetadata, 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 parseVariableDeclarationStatementAfterMetadata(
+ commentAndMetadata);
+ }
+ } else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+ //
+ // We appear to have found an incomplete statement at the end of a
+ // block. Parse it as a variable declaration.
+ //
+ return _parseVariableDeclarationStatementAfterType(
+ commentAndMetadata, null, returnType);
+ }
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT);
+ // TODO(brianwilkerson) Recover from this error.
+ return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
+ }
+ } else if (keyword == Keyword.CONST) {
+ Token next = _currentToken.next;
+ if (next.matchesAny(const <TokenType>[
+ TokenType.LT,
+ TokenType.OPEN_CURLY_BRACKET,
+ TokenType.OPEN_SQUARE_BRACKET,
+ TokenType.INDEX
+ ])) {
+ return new ExpressionStatement(
+ parseExpression2(), _expect(TokenType.SEMICOLON));
+ } else if (_tokenMatches(next, TokenType.IDENTIFIER)) {
+ Token afterType = skipTypeName(next);
+ if (afterType != null) {
+ if (_tokenMatches(afterType, TokenType.OPEN_PAREN) ||
+ (_tokenMatches(afterType, TokenType.PERIOD) &&
+ _tokenMatches(afterType.next, TokenType.IDENTIFIER) &&
+ _tokenMatches(afterType.next.next, TokenType.OPEN_PAREN))) {
+ return new ExpressionStatement(
+ parseExpression2(), _expect(TokenType.SEMICOLON));
+ }
+ }
+ }
+ return parseVariableDeclarationStatementAfterMetadata(
+ commentAndMetadata);
+ } else if (keyword == Keyword.NEW ||
+ keyword == Keyword.TRUE ||
+ keyword == Keyword.FALSE ||
+ keyword == Keyword.NULL ||
+ keyword == Keyword.SUPER ||
+ keyword == Keyword.THIS) {
+ return new ExpressionStatement(
+ parseExpression2(), _expect(TokenType.SEMICOLON));
+ } else {
+ //
+ // We have found an error of some kind. Try to recover.
+ //
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT);
+ return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
}
- return new IntegerLiteral(token, value);
- } else if (keyword == Keyword.CONST) {
- return parseConstExpression();
- } else if (type == TokenType.OPEN_PAREN) {
- if (isFunctionExpression(_currentToken)) {
- return parseFunctionExpression();
+ } else if (_inGenerator && _matchesString(_YIELD)) {
+ return parseYieldStatement();
+ } else if (_inAsync && _matchesString(_AWAIT)) {
+ if (_tokenMatchesKeyword(_peek(), Keyword.FOR)) {
+ return parseForStatement();
}
- 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;
+ return new ExpressionStatement(
+ parseExpression2(), _expect(TokenType.SEMICOLON));
+ } else if (_matchesString(_AWAIT) &&
+ _tokenMatchesKeyword(_peek(), Keyword.FOR)) {
+ Token awaitToken = _currentToken;
+ Statement statement = parseForStatement();
+ if (statement is! ForStatement) {
+ _reportErrorForToken(
+ CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT, awaitToken);
}
- } 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();
+ return statement;
+ } else if (type == TokenType.SEMICOLON) {
+ 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));
} else {
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
- return createSyntheticIdentifier();
+ return new ExpressionStatement(
+ parseExpression2(), _expect(TokenType.SEMICOLON));
}
}
/**
- * 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());
-
- /**
- * Parse a return statement. Return the return statement that was parsed.
- *
- * This method assumes that the current token matches `Keyword.RETURN`.
- *
- * returnStatement ::=
- * 'return' expression? ';'
- */
- Statement parseReturnStatement() {
- Token returnKeyword = getAndAdvance();
- if (_matches(TokenType.SEMICOLON)) {
- return new ReturnStatement(returnKeyword, null, getAndAdvance());
- }
- Expression expression = parseExpression2();
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new ReturnStatement(returnKeyword, expression, semicolon);
- }
-
- /**
- * 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);
- }
- }
-
- /**
- * 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.
- *
- * identifier ::=
- * IDENTIFIER
- */
- SimpleIdentifier parseSimpleIdentifier({bool isDeclaration: false}) {
- if (_matchesIdentifier()) {
- return _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration);
- }
- _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();
- }
-
- /**
- * Parse a statement. Return the statement that was parsed.
- *
- * statement ::=
- * label* nonLabeledStatement
- */
- 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;
- }
- return new LabeledStatement(labels, statement);
- }
-
- /**
- * 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.
- */
- List<Statement> parseStatements(Token token) {
- _currentToken = token;
- return _parseStatementList();
- }
-
- /**
- * Parse a string literal. Return the string literal that was parsed.
- *
- * stringLiteral ::=
- * MULTI_LINE_STRING+
- * | SINGLE_LINE_STRING+
- */
- StringLiteral parseStringLiteral() {
- if (_matches(TokenType.STRING)) {
- return _parseStringLiteralUnchecked();
- }
- _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_STRING_LITERAL);
- return createSyntheticStringLiteral();
- }
-
- /**
- * 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);
- }
-
- /**
- * 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
- */
- 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));
- }
- 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 a list of type arguments. Return the type argument list that was
- * parsed.
- *
- * This method assumes that the current token matches `TokenType.LT`.
- *
- * typeArguments ::=
- * '<' typeList '>'
- *
- * 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.
- *
- * 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.
- *
- * typeParameter ::=
- * metadata name ('extends' bound)?
- */
- 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);
- }
- return new TypeParameter(commentAndMetadata.comment,
- commentAndMetadata.metadata, name, null, null);
- }
-
- /**
- * Parse a list of type parameters. Return the list of type parameters that
- * were parsed.
- *
- * This method assumes that the current token matches `TokenType.LT`.
- *
- * typeParameterList ::=
- * '<' typeParameter (',' typeParameter)* '>'
- */
- TypeParameterList parseTypeParameterList() {
- Token leftBracket = getAndAdvance();
- List<TypeParameter> typeParameters = <TypeParameter>[parseTypeParameter()];
- while (_optional(TokenType.COMMA)) {
- typeParameters.add(parseTypeParameter());
- }
- Token rightBracket = _expectGt();
- return new TypeParameterList(leftBracket, typeParameters, rightBracket);
- }
-
- /**
- * 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 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);
- }
-
- /**
- * 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);
- }
-
- /**
- * 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 with clause. Return the with clause that was parsed.
- *
- * This method assumes that the current token matches `Keyword.WITH`.
- *
- * withClause ::=
- * 'with' typeName (',' typeName)*
- */
- WithClause parseWithClause() {
- Token withKeyword = getAndAdvance();
- List<TypeName> types = <TypeName>[parseTypeName(false)];
- while (_optional(TokenType.COMMA)) {
- types.add(parseTypeName(false));
- }
- return new WithClause(withKeyword, types);
- }
-
- /**
- * 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);
- }
-
- /**
- * 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.
+ * Parse a normal formal parameter. Return the normal formal parameter that
+ * was parsed.
*
- * This method must be kept in sync with [parsePrefixedIdentifier].
+ * normalFormalParameter ::=
+ * functionSignature
+ * | fieldFormalParameter
+ * | simpleFormalParameter
*
- * prefixedIdentifier ::=
- * identifier ('.' identifier)?
+ * functionSignature:
+ * metadata returnType? identifier typeParameters? formalParameterList
+ *
+ * fieldFormalParameter ::=
+ * metadata finalConstVarOrType? 'this' '.' identifier
+ *
+ * simpleFormalParameter ::=
+ * declaredIdentifier
+ * | metadata identifier
*/
- Token skipPrefixedIdentifier(Token startToken) {
- Token token = skipSimpleIdentifier(startToken);
- if (token == null) {
- return null;
- } else if (!_tokenMatches(token, TokenType.PERIOD)) {
- return token;
+ 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);
}
- 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;
+ 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);
}
- return null;
+ 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);
+ }
+ }
+ 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));
}
/**
- * 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.
+ * 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.
*
- * This method must be kept in sync with [parseReturnType].
+ * operatorDeclaration ::=
+ * operatorSignature (';' | functionBody)
*
- * returnType ::=
- * 'void'
- * | type
+ * operatorSignature ::=
+ * 'external'? returnType? 'operator' operator formalParameterList
*/
- Token skipReturnType(Token startToken) {
- if (_tokenMatchesKeyword(startToken, Keyword.VOID)) {
- return startToken.next;
+ MethodDeclaration parseOperator(CommentAndMetadata commentAndMetadata,
+ Token externalKeyword, TypeName returnType) {
+ Token operatorKeyword;
+ if (_matchesKeyword(Keyword.OPERATOR)) {
+ operatorKeyword = getAndAdvance();
} else {
- return skipTypeName(startToken);
+ _reportErrorForToken(
+ ParserErrorCode.MISSING_KEYWORD_OPERATOR, _currentToken);
+ operatorKeyword = _createSyntheticKeyword(Keyword.OPERATOR);
}
+ return _parseOperatorAfterKeyword(
+ commentAndMetadata, externalKeyword, returnType, operatorKeyword);
}
/**
- * 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].
+ * Parse a part or part-of directive. The [commentAndMetadata] is the metadata
+ * to be associated with the directive. Return the part or part-of directive
+ * that was parsed.
*
- * identifier ::=
- * IDENTIFIER
- */
- Token skipSimpleIdentifier(Token startToken) {
- if (_tokenMatches(startToken, TokenType.IDENTIFIER) ||
- _tokenMatchesPseudoKeyword(startToken)) {
- return startToken.next;
- }
- 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 assumes that the current token matches `Keyword.PART`.
*
- * This method must be kept in sync with [parseStringLiteral].
+ * partDirective ::=
+ * metadata 'part' stringLiteral ';'
*
- * stringLiteral ::=
- * MULTI_LINE_STRING+
- * | SINGLE_LINE_STRING+
+ * partOfDirective ::=
+ * metadata 'part' 'of' identifier ';'
*/
- 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;
+ Directive parsePartOrPartOfDirective(CommentAndMetadata commentAndMetadata) {
+ if (_tokenMatchesString(_peek(), _OF)) {
+ return _parsePartOfDirective(commentAndMetadata);
}
- return token;
+ return _parsePartDirective(commentAndMetadata);
}
/**
- * 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].
+ * Parse a postfix expression. Return the postfix expression that was parsed.
*
- * typeArguments ::=
- * '<' typeList '>'
+ * postfixExpression ::=
+ * assignableExpression postfixOperator
+ * | primary selector*
*
- * typeList ::=
- * type (',' type)*
+ * selector ::=
+ * assignableSelector
+ * | argumentList
*/
- 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;
- }
+ Expression parsePostfixExpression() {
+ Expression operand = parseAssignableExpression(true);
+ TokenType type = _currentToken.type;
+ if (type == TokenType.OPEN_SQUARE_BRACKET ||
+ type == TokenType.PERIOD ||
+ type == TokenType.QUESTION_PERIOD ||
+ type == TokenType.OPEN_PAREN ||
+ (parseGenericMethods && type == TokenType.LT)) {
+ do {
+ if (_isLikelyArgumentList()) {
+ TypeArgumentList typeArguments = _parseOptionalTypeArguments();
+ ArgumentList argumentList = parseArgumentList();
+ Expression currentOperand = operand;
+ if (currentOperand is PropertyAccess) {
+ operand = new MethodInvocation(
+ currentOperand.target,
+ currentOperand.operator,
+ currentOperand.propertyName,
+ typeArguments,
+ argumentList);
+ } else {
+ operand = new FunctionExpressionInvocation(
+ operand, typeArguments, argumentList);
+ }
+ } else {
+ operand = parseAssignableSelector(operand, true);
+ }
+ type = _currentToken.type;
+ } while (type == TokenType.OPEN_SQUARE_BRACKET ||
+ type == TokenType.PERIOD ||
+ type == TokenType.QUESTION_PERIOD ||
+ type == TokenType.OPEN_PAREN);
+ return operand;
}
- 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;
+ if (!_currentToken.type.isIncrementOperator) {
+ return operand;
}
- return null;
+ _ensureAssignable(operand);
+ Token operator = getAndAdvance();
+ return new PostfixExpression(operand, operator);
}
/**
- * 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].
+ * Parse a prefixed identifier. Return the prefixed identifier that was
+ * parsed.
*
- * 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;
- }
-
- /**
- * Advance to the next token in the token stream.
+ * prefixedIdentifier ::=
+ * identifier ('.' identifier)?
*/
- void _advance() {
- _currentToken = _currentToken.next;
+ Identifier parsePrefixedIdentifier() {
+ return _parsePrefixedIdentifierAfterIdentifier(parseSimpleIdentifier());
}
/**
- * 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).
+ * 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
*/
- 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;
+ 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 (scalarValue < Character.MAX_VALUE) {
- buffer.writeCharCode(scalarValue);
+ 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 {
- buffer.write(Character.toChars(scalarValue));
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+ return createSyntheticIdentifier();
}
}
/**
- * 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.
+ * 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
*/
- 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;
+ 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();
+ }
}
- Token tail = new Token(TokenType.EOF, 0);
- tail.setNext(tail);
- current.setNext(tail);
- return head.next;
+ ArgumentList argumentList = _parseArgumentListChecked();
+ return new RedirectingConstructorInvocation(
+ keyword, period, constructorName, argumentList);
}
/**
- * 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.
+ * Parse a relational expression. Return the relational expression that was
+ * parsed.
+ *
+ * relationalExpression ::=
+ * bitwiseOrExpression ('is' '!'? type | 'as' type | relationalOperator bitwiseOrExpression)?
+ * | 'super' relationalOperator bitwiseOrExpression
*/
- bool _couldBeStartOfCompilationUnitMember() {
+ 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;
- 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;
+ 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 false;
+ return expression;
}
/**
- * Return a synthetic token representing the given [keyword].
+ * Parse a rethrow expression. Return the rethrow expression that was parsed.
+ *
+ * This method assumes that the current token matches `Keyword.RETHROW`.
+ *
+ * rethrowExpression ::=
+ * 'rethrow'
*/
- Token _createSyntheticKeyword(Keyword keyword) => _injectToken(
- new Parser_SyntheticKeywordToken(keyword, _currentToken.offset));
+ Expression parseRethrowExpression() => new RethrowExpression(getAndAdvance());
/**
- * Return a synthetic token with the given [type].
+ * Parse a return statement. Return the return statement that was parsed.
+ *
+ * This method assumes that the current token matches `Keyword.RETURN`.
+ *
+ * returnStatement ::=
+ * 'return' expression? ';'
*/
- Token _createSyntheticToken(TokenType type) =>
- _injectToken(new StringToken(type, "", _currentToken.offset));
+ Statement parseReturnStatement() {
+ Token returnKeyword = getAndAdvance();
+ if (_matches(TokenType.SEMICOLON)) {
+ return new ReturnStatement(returnKeyword, null, getAndAdvance());
+ }
+ Expression expression = parseExpression2();
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new ReturnStatement(returnKeyword, expression, semicolon);
+ }
/**
- * 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 a return type. Return the return type that was parsed.
+ *
+ * returnType ::=
+ * 'void'
+ * | 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);
+ TypeName parseReturnType() {
+ if (_currentToken.keyword == Keyword.VOID) {
+ return new TypeName(new SimpleIdentifier(getAndAdvance()), null);
+ } else {
+ return parseTypeName(false);
}
- return new TokenWithComment(type, token.offset, comments);
}
/**
- * Check that the given [expression] is assignable and report an error if it
- * isn't.
+ * Parse a setter. The [commentAndMetadata] is the documentation comment and
+ * metadata to be associated with the declaration. The [externalKeyword] is
+ * the 'external' token. The [staticKeyword] is the static keyword, or `null`
+ * if the setter is not static. The [returnType] is the return type that has
+ * already been parsed, or `null` if there was no return type. Return the
+ * setter that was parsed.
*
- * assignableExpression ::=
- * primary (arguments* assignableSelector)+
- * | 'super' unconditionalAssignableSelector
- * | identifier
+ * This method assumes that the current token matches `Keyword.SET`.
*
- * unconditionalAssignableSelector ::=
- * '[' expression ']'
- * | '.' identifier
+ * setter ::=
+ * setterSignature functionBody?
*
- * assignableSelector ::=
- * unconditionalAssignableSelector
- * | '?.' identifier
+ * setterSignature ::=
+ * 'external'? 'static'? returnType? 'set' identifier formalParameterList
*/
- void _ensureAssignable(Expression expression) {
- if (expression != null && !expression.isAssignable) {
- _reportErrorForCurrentToken(
- ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE);
+ MethodDeclaration parseSetter(CommentAndMetadata commentAndMetadata,
+ Token externalKeyword, Token staticKeyword, TypeName returnType) {
+ Token propertyKeyword = getAndAdvance();
+ SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
+ FormalParameterList parameters = parseFormalParameterList();
+ _validateFormalParameterList(parameters);
+ FunctionBody body = parseFunctionBody(
+ externalKeyword != null || staticKeyword == null,
+ ParserErrorCode.STATIC_SETTER_WITHOUT_BODY,
+ false);
+ if (externalKeyword != null && body is! EmptyFunctionBody) {
+ _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_SETTER_WITH_BODY);
}
+ return new MethodDeclaration(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ externalKeyword,
+ staticKeyword,
+ returnType,
+ propertyKeyword,
+ null,
+ name,
+ null,
+ parameters,
+ body);
}
/**
- * 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 shift expression. Return the shift expression that was parsed.
*
- * The [type] is the type of token that is expected.
+ * shiftExpression ::=
+ * additiveExpression (shiftOperator additiveExpression)*
+ * | 'super' (shiftOperator additiveExpression)+
*/
- Token _expect(TokenType type) {
- if (_matches(type)) {
- return getAndAdvance();
+ Expression parseShiftExpression() {
+ Expression expression;
+ if (_currentToken.keyword == Keyword.SUPER &&
+ _currentToken.next.type.isShiftOperator) {
+ expression = new SuperExpression(getAndAdvance());
+ } else {
+ expression = parseAdditiveExpression();
}
- // 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);
+ while (_currentToken.type.isShiftOperator) {
+ expression = new BinaryExpression(
+ expression, getAndAdvance(), parseAdditiveExpression());
}
- _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TOKEN, [type.lexeme]);
- return _createSyntheticToken(type);
+ return expression;
}
/**
- * 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 simple identifier. Return the simple identifier that was parsed.
+ *
+ * identifier ::=
+ * IDENTIFIER
*/
- Token _expectGt() {
- if (_matchesGt()) {
- return getAndAdvance();
+ SimpleIdentifier parseSimpleIdentifier({bool isDeclaration: false}) {
+ if (_matchesIdentifier()) {
+ return _parseSimpleIdentifierUnchecked(isDeclaration: isDeclaration);
}
- _reportErrorForCurrentToken(
- ParserErrorCode.EXPECTED_TOKEN, [TokenType.GT.lexeme]);
- return _createSyntheticToken(TokenType.GT);
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER);
+ return createSyntheticIdentifier(isDeclaration: isDeclaration);
}
/**
- * 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 statement, starting with the given [token]. Return the statement
+ * that was parsed, or `null` if the tokens do not represent a recognizable
+ * statement.
*/
- Token _expectKeyword(Keyword keyword) {
- if (_matchesKeyword(keyword)) {
- return getAndAdvance();
+ Statement parseStatement(Token token) {
+ _currentToken = token;
+ return parseStatement2();
+ }
+
+ /**
+ * Parse a statement. Return the statement that was parsed.
+ *
+ * statement ::=
+ * label* nonLabeledStatement
+ */
+ 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);
+ }
}
- // 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;
+ Statement statement = parseNonLabeledStatement();
+ if (labels == null) {
+ return statement;
+ }
+ return new LabeledStatement(labels, statement);
}
/**
- * 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 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.
*/
- 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;
+ List<Statement> parseStatements(Token token) {
+ _currentToken = token;
+ return _parseStatementList();
}
/**
- * Return a list of the ranges of characters in the given [comment] that
- * should be treated as code blocks.
+ * Parse a string literal. Return the string literal that was parsed.
+ *
+ * stringLiteral ::=
+ * MULTI_LINE_STRING+
+ * | SINGLE_LINE_STRING+
*/
- 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;
- }
- 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;
- }
+ StringLiteral parseStringLiteral() {
+ if (_matches(TokenType.STRING)) {
+ return _parseStringLiteralUnchecked();
}
- return ranges;
+ _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_STRING_LITERAL);
+ return createSyntheticStringLiteral();
}
/**
- * 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 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
*/
- Token _getEndToken(Token beginToken) {
- if (beginToken is BeginToken) {
- return beginToken.endToken;
+ SuperConstructorInvocation parseSuperConstructorInvocation() {
+ Token keyword = getAndAdvance();
+ Token period = null;
+ SimpleIdentifier constructorName = null;
+ if (_matches(TokenType.PERIOD)) {
+ period = getAndAdvance();
+ constructorName = parseSimpleIdentifier();
}
- return null;
+ ArgumentList argumentList = _parseArgumentListChecked();
+ return new SuperConstructorInvocation(
+ keyword, period, constructorName, argumentList);
}
- 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;
+ /**
+ * 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;
}
- return false;
}
/**
- * Matches a generic comment type substitution and injects it into the token
- * stream. Returns true if a match was injected, otherwise false.
+ * Parse a symbol literal. Return the symbol literal that was parsed.
*
- * These comments are of the form `/*=T*/`, in other words, a [TypeName]
- * inside a slash-star comment, preceded by equals sign.
+ * This method assumes that the current token matches [TokenType.HASH].
+ *
+ * symbolLiteral ::=
+ * '#' identifier ('.' identifier)*
*/
- bool _injectGenericCommentTypeAssign() {
- return _injectGenericComment(TokenType.GENERIC_METHOD_TYPE_ASSIGN, 3);
+ 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);
}
/**
- * Matches a generic comment type parameters and injects them into the token
- * stream. Returns true if a match was injected, otherwise false.
+ * Parse a throw expression. Return the throw expression that was parsed.
*
- * 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.
+ * This method assumes that the current token matches [Keyword.THROW].
+ *
+ * throwExpression ::=
+ * 'throw' expression
*/
- 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;
+ 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());
}
- // Inject these new tokens into the stream.
- Token previous = _currentToken.previous;
- lastToken.setNext(_currentToken);
- previous.setNext(firstToken);
- _currentToken = firstToken;
+ Expression expression = parseExpression2();
+ return new ThrowExpression(keyword, expression);
}
/**
- * 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 throw expression. Return the throw expression that was parsed.
+ *
+ * This method assumes that the current token matches [Keyword.THROW].
+ *
+ * throwExpressionWithoutCascade ::=
+ * 'throw' expressionWithoutCascade
*/
- bool _isConditionalOperator() {
- void parseOperation(Parser parser) {
- parser.parseExpressionWithoutCascade();
- }
-
- Token token = _skip(_currentToken.next, parseOperation);
- if (token == null || !_tokenMatches(token, TokenType.COLON)) {
- return false;
+ 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());
}
- token = _skip(token.next, parseOperation);
- return token != null;
+ Expression expression = parseExpressionWithoutCascade();
+ return new ThrowExpression(keyword, expression);
}
/**
- * Return `true` if the given [character] is a valid hexadecimal digit.
+ * 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 _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;
+ 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));
}
- if (!parseGenericMethods) {
- return false;
+ Token finallyKeyword = null;
+ if (_matchesKeyword(Keyword.FINALLY)) {
+ finallyKeyword = getAndAdvance();
+ finallyClause = _parseBlockChecked();
+ } else if (catchClauses.isEmpty) {
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_CATCH_OR_FINALLY);
}
- Token token = skipTypeArgumentList(_currentToken);
- return token != null && _tokenMatches(token, TokenType.OPEN_PAREN);
+ return new TryStatement(
+ tryKeyword, body, catchClauses, finallyKeyword, finallyClause);
}
/**
- * 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 type alias. The [commentAndMetadata] is the metadata to be
+ * associated with the member. Return the type alias that was parsed.
*
- * This method uses the syntax described by the
- * <a href="http://daringfireball.net/projects/markdown/syntax">markdown</a>
- * project.
+ * This method assumes that the current token matches [Keyword.TYPEDEF].
+ *
+ * typeAlias ::=
+ * 'typedef' typeAliasBody
+ *
+ * typeAliasBody ::=
+ * classTypeAlias
+ * | functionTypeAlias
+ *
+ * classTypeAlias ::=
+ * identifier typeParameters? '=' 'abstract'? mixinApplication
+ *
+ * mixinApplication ::=
+ * qualified withClause implementsClause? ';'
+ *
+ * functionTypeAlias ::=
+ * functionPrefix typeParameterList? formalParameterList ';'
+ *
+ * functionPrefix ::=
+ * returnType? name
*/
- 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;
+ TypeAlias parseTypeAlias(CommentAndMetadata commentAndMetadata) {
+ Token keyword = getAndAdvance();
+ if (_matchesIdentifier()) {
+ Token next = _peek();
+ if (_tokenMatches(next, TokenType.LT)) {
+ next = _skipTypeParameterList(next);
+ if (next != null && _tokenMatches(next, TokenType.EQ)) {
+ TypeAlias typeAlias =
+ 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);
+ _reportErrorForToken(
+ ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword);
+ return typeAlias;
}
- nextChar = comment.codeUnitAt(index);
}
- return nextChar == 0x5B;
+ return _parseFunctionTypeAlias(commentAndMetadata, keyword);
}
/**
- * Return `true` if the given [startToken] appears to be the beginning of an
- * operator declaration.
+ * Parse a list of type arguments. Return the type argument list that was
+ * parsed.
+ *
+ * This method assumes that the current token matches `TokenType.LT`.
+ *
+ * typeArguments ::=
+ * '<' typeList '>'
+ *
+ * typeList ::=
+ * type (',' type)*
*/
- 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;
+ TypeArgumentList parseTypeArgumentList() {
+ Token leftBracket = getAndAdvance();
+ List<TypeName> arguments = <TypeName>[parseTypeName(false)];
+ while (_optional(TokenType.COMMA)) {
+ arguments.add(parseTypeName(false));
}
- // Formal parameter list is expect now.
- return _tokenMatches(token, TokenType.OPEN_PAREN);
+ Token rightBracket = _expectGt();
+ return new TypeArgumentList(leftBracket, arguments, rightBracket);
}
- bool _isPeekGenericTypeParametersAndOpenParen() {
- if (!parseGenericMethods) {
- return false;
- }
- Token token = _skipTypeParameterList(_peek());
- return token != null && _tokenMatches(token, TokenType.OPEN_PAREN);
+ /**
+ * Parse a type name. Return the type name that was parsed.
+ *
+ * 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;
}
/**
- * 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 type parameter. Return the type parameter that was parsed.
+ *
+ * typeParameter ::=
+ * metadata name ('extends' bound)?
*/
- 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;
+ 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);
}
- return false;
+ return new TypeParameter(commentAndMetadata.comment,
+ commentAndMetadata.metadata, name, null, null);
}
/**
- * Increments the error reporting lock level. If level is more than `0`, then
- * [reportError] wont report any error.
+ * Parse a list of type parameters. Return the list of type parameters that
+ * were parsed.
+ *
+ * This method assumes that the current token matches `TokenType.LT`.
+ *
+ * typeParameterList ::=
+ * '<' typeParameter (',' typeParameter)* '>'
*/
- void _lockErrorListener() {
- _errorListenerLock++;
+ TypeParameterList parseTypeParameterList() {
+ Token leftBracket = getAndAdvance();
+ List<TypeParameter> typeParameters = <TypeParameter>[parseTypeParameter()];
+ while (_optional(TokenType.COMMA)) {
+ typeParameters.add(parseTypeParameter());
+ }
+ Token rightBracket = _expectGt();
+ return new TypeParameterList(leftBracket, typeParameters, rightBracket);
}
/**
- * 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 unary expression. Return the unary expression that was parsed.
+ *
+ * unaryExpression ::=
+ * prefixOperator unaryExpression
+ * | awaitExpression
+ * | postfixExpression
+ * | unaryOperator 'super'
+ * | '-' 'super'
+ * | incrementOperator assignableExpression
*/
- bool _matches(TokenType type) => _currentToken.type == type;
+ 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();
+ }
/**
- * 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 variable declaration. Return the variable declaration that was
+ * parsed.
+ *
+ * variableDeclaration ::=
+ * identifier ('=' expression)?
*/
- 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;
+ 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 false;
+ return new VariableDeclaration(name, equals, initializer);
}
/**
- * Return `true` if the current token is a valid identifier. Valid identifiers
- * include built-in identifiers (pseudo-keywords).
+ * 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)*
*/
- bool _matchesIdentifier() => _tokenMatchesIdentifier(_currentToken);
+ VariableDeclarationList parseVariableDeclarationListAfterMetadata(
+ CommentAndMetadata commentAndMetadata) {
+ FinalConstVarOrType holder = parseFinalConstVarOrType(false);
+ return parseVariableDeclarationListAfterType(
+ commentAndMetadata, holder.keyword, holder.type);
+ }
/**
- * Return `true` if the current token matches the given [keyword].
+ * 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)*
*/
- bool _matchesKeyword(Keyword keyword) =>
- _tokenMatchesKeyword(_currentToken, keyword);
+ 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);
+ }
/**
- * Return `true` if the current token matches the given [identifier].
- */
- bool _matchesString(String identifier) =>
- _currentToken.type == TokenType.IDENTIFIER &&
- _currentToken.lexeme == identifier;
+ * 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);
+ }
/**
- * 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 while statement. Return the while statement that was parsed.
+ *
+ * This method assumes that the current token matches [Keyword.WHILE].
+ *
+ * whileStatement ::=
+ * 'while' '(' expression ')' statement
*/
- bool _optional(TokenType type) {
- if (_currentToken.type == type) {
- _advance();
- return true;
+ 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 false;
}
/**
- * 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 with clause. Return the with clause that was parsed.
+ *
+ * This method assumes that the current token matches `Keyword.WITH`.
+ *
+ * withClause ::=
+ * 'with' typeName (',' typeName)*
*/
- ArgumentList _parseArgumentListChecked() {
- if (_matches(TokenType.OPEN_PAREN)) {
- return parseArgumentList();
+ WithClause parseWithClause() {
+ Token withKeyword = getAndAdvance();
+ List<TypeName> types = <TypeName>[parseTypeName(false)];
+ while (_optional(TokenType.COMMA)) {
+ types.add(parseTypeName(false));
}
- _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));
+ return new WithClause(withKeyword, types);
}
/**
- * Parse an assert within a constructor's initializer list. Return the assert.
+ * Parse a yield statement. Return the yield statement that was parsed.
*
- * This method assumes that the current token matches `Keyword.ASSERT`.
+ * This method assumes that the current token matches [Keyword.YIELD].
*
- * assertInitializer ::=
- * 'assert' '(' expression [',' expression] ')'
+ * yieldStatement ::=
+ * 'yield' '*'? expression ';'
*/
- 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();
+ YieldStatement parseYieldStatement() {
+ Token yieldToken = getAndAdvance();
+ Token star = null;
+ if (_matches(TokenType.STAR)) {
+ star = getAndAdvance();
}
- Token rightParen = _expect(TokenType.CLOSE_PAREN);
-// return new AssertInitializer(
-// keyword, leftParen, expression, comma, message, rightParen);
+ Expression expression = parseExpression2();
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new YieldStatement(yieldToken, star, expression, semicolon);
}
/**
- * 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.
+ * 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)?
*/
- 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;
+ 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 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 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.
*
- * unconditionalAssignableSelector ::=
- * '[' expression ']'
- * | '.' identifier
+ * This method must be kept in sync with [parseReturnType].
*
- * assignableSelector ::=
- * unconditionalAssignableSelector
- * | '?.' identifier
+ * returnType ::=
+ * 'void'
+ * | type
*/
- 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;
- }
+ Token skipReturnType(Token startToken) {
+ if (_tokenMatchesKeyword(startToken, Keyword.VOID)) {
+ return startToken.next;
} 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;
- }
+ return skipTypeName(startToken);
}
}
/**
- * 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 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.
*
- * block ::=
- * '{' statements '}'
+ * This method must be kept in sync with [parseSimpleIdentifier].
+ *
+ * identifier ::=
+ * IDENTIFIER
*/
- 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));
+ Token skipSimpleIdentifier(Token startToken) {
+ if (_tokenMatches(startToken, TokenType.IDENTIFIER) ||
+ _tokenMatchesPseudoKeyword(startToken)) {
+ return startToken.next;
+ }
+ return null;
}
/**
- * 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 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.
*
- * classMembers ::=
- * (metadata memberDefinition)*
+ * This method must be kept in sync with [parseStringLiteral].
+ *
+ * stringLiteral ::=
+ * MULTI_LINE_STRING+
+ * | SINGLE_LINE_STRING+
*/
- 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();
+ 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);
}
- memberStart = _currentToken;
- type = _currentToken.type;
- keyword = _currentToken.keyword;
}
- return members;
+ if (identical(token, startToken)) {
+ return null;
+ }
+ return token;
}
/**
- * 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.
+ * 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.
*
- * classTypeAlias ::=
- * identifier typeParameters? '=' 'abstract'? mixinApplication
+ * This method must be kept in sync with [parseTypeArgumentList].
*
- * mixinApplication ::=
- * type withClause implementsClause? ';'
+ * typeArguments ::=
+ * '<' typeList '>'
+ *
+ * typeList ::=
+ * type (',' type)*
*/
- 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]);
+ Token skipTypeArgumentList(Token startToken) {
+ Token token = startToken;
+ if (!_tokenMatches(token, TokenType.LT) &&
+ !_injectGenericCommentTypeList()) {
+ return null;
}
- ImplementsClause implementsClause = null;
- if (_matchesKeyword(Keyword.IMPLEMENTS)) {
- implementsClause = parseImplementsClause();
+ 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;
}
- 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]);
+ while (_tokenMatches(token, TokenType.COMMA)) {
+ token = skipTypeName(token.next);
+ if (token == null) {
+ return null;
}
- semicolon = _createSyntheticToken(TokenType.SEMICOLON);
}
- return new ClassTypeAlias(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- classKeyword,
- className,
- typeParameters,
- equals,
- abstractKeyword,
- superclass,
- withClause,
- implementsClause,
- semicolon);
+ 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 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.
+ * 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.
*
- * commentReference ::=
- * '[' 'new'? qualified ']' libraryReference?
+ * This method must be kept in sync with [parseTypeName].
*
- * libraryReference ::=
- * '(' stringLiteral ')'
+ * type ::=
+ * qualified typeArguments?
*/
- 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 skipTypeName(Token startToken) {
+ Token token = skipPrefixedIdentifier(startToken);
+ if (token == null) {
+ return null;
+ }
+ 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));
}
- return references;
}
/**
- * Parse a list of configurations. Return the configurations that were parsed,
- * or `null` if there are no configurations.
+ * 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.
*/
- List<Configuration> _parseConfigurations() {
- List<Configuration> configurations = null;
- while (_matchesKeyword(Keyword.IF)) {
- configurations ??= <Configuration>[];
- configurations.add(parseConfiguration());
+ Token _cloneTokens(Token token) {
+ if (token == null) {
+ return null;
}
- return configurations;
+ 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;
}
- 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);
+ /**
+ * 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;
}
- }
- 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);
+ Token token = skipReturnType(_currentToken);
+ if (token == null) {
+ return false;
}
- } 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);
- }
+ // 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 new ConstructorDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- externalKeyword,
- constKeyword,
- factoryKeyword,
- returnType,
- period,
- name,
- parameters,
- separator,
- initializers,
- redirectedConstructor,
- body);
+ return false;
}
/**
- * 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 a synthetic token representing the given [keyword].
*/
- 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);
+ 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);
}
- return new ConstructorFieldInitializer(
- keywordToken, period, fieldName, equals, expression);
- } finally {
- _inInitializer = wasInInitializer;
+ return new Token(type, token.offset);
+ } else if (isBegin) {
+ return new BeginTokenWithComment(type, token.offset, comments);
}
+ return new TokenWithComment(type, token.offset, comments);
}
/**
- * Parse a directive. The [commentAndMetadata] is the metadata to be
- * associated with the directive. Return the directive that was parsed.
+ * Check that the given [expression] is assignable and report an error if it
+ * isn't.
*
- * directive ::=
- * exportDirective
- * | libraryDirective
- * | importDirective
- * | partDirective
+ * assignableExpression ::=
+ * primary (arguments* assignableSelector)+
+ * | 'super' unconditionalAssignableSelector
+ * | identifier
+ *
+ * unconditionalAssignableSelector ::=
+ * '[' expression ']'
+ * | '.' identifier
+ *
+ * assignableSelector ::=
+ * unconditionalAssignableSelector
+ * | '?.' identifier
*/
- 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");
+ void _ensureAssignable(Expression expression) {
+ if (expression != null && !expression.isAssignable) {
+ _reportErrorForCurrentToken(
+ ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE);
}
}
/**
- * Parse an enum constant declaration. Return the enum constant declaration
- * that was parsed.
- *
- * Specified:
- *
- * enumConstant ::=
- * id
+ * 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.
*
- * Actual:
+ * Note that the method [_expectGt] should be used if the argument to this
+ * method would be [TokenType.GT].
*
- * enumConstant ::=
- * metadata id
+ * The [type] is the type of token that is expected.
*/
- EnumConstantDeclaration _parseEnumConstantDeclaration() {
- CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
- SimpleIdentifier name;
- if (_matchesIdentifier()) {
- name = _parseSimpleIdentifierUnchecked(isDeclaration: true);
- } else {
- name = createSyntheticIdentifier();
+ Token _expect(TokenType type) {
+ if (_matches(type)) {
+ return getAndAdvance();
}
- if (commentAndMetadata.hasMetadata) {
- _reportErrorForNode(ParserErrorCode.ANNOTATION_ON_ENUM_CONSTANT,
- commentAndMetadata.metadata[0]);
+ // 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 EnumConstantDeclaration(
- commentAndMetadata.comment, commentAndMetadata.metadata, name);
+ _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TOKEN, [type.lexeme]);
+ return _createSyntheticToken(type);
}
/**
- * Parse an equality expression. Return the equality expression that was
- * parsed.
- *
- * equalityExpression ::=
- * relationalExpression (equalityOperator relationalExpression)?
- * | 'super' equalityOperator relationalExpression
+ * 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.
*/
- Expression _parseEqualityExpression() {
- Expression expression;
- if (_currentToken.keyword == Keyword.SUPER &&
- _currentToken.next.type.isEqualityOperator) {
- expression = new SuperExpression(getAndAdvance());
- } else {
- expression = parseRelationalExpression();
+ Token _expectGt() {
+ if (_matchesGt()) {
+ return getAndAdvance();
}
- 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;
+ _reportErrorForCurrentToken(
+ ParserErrorCode.EXPECTED_TOKEN, [TokenType.GT.lexeme]);
+ return _createSyntheticToken(TokenType.GT);
+ }
+
+ /**
+ * 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.
+ */
+ Token _expectKeyword(Keyword keyword) {
+ if (_matchesKeyword(keyword)) {
+ return getAndAdvance();
}
- return expression;
+ // 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 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*';'
+ * 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.
*/
- 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);
+ 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;
}
/**
- * 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)?
- *
- * defaultNamedParameter ::=
- * normalFormalParameter (':' expression)?
+ * Return a list of the ranges of characters in the given [comment] that
+ * should be treated as code blocks.
*/
- 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);
+ 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 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);
+ }
+ 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 {
+ index = index + 1;
}
- return new DefaultFormalParameter(
- parameter, kind, separator, defaultValue);
- } else if (kind != ParameterKind.REQUIRED) {
- return new DefaultFormalParameter(parameter, kind, null, null);
}
- return parameter;
+ return ranges;
}
/**
- * Parse a list of formal parameters given that the list starts with the given
- * [leftParenthesis]. Return the formal parameters that were parsed.
+ * 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.
*/
- FormalParameterList _parseFormalParameterListAfterParen(
- Token leftParenthesis) {
- if (_matches(TokenType.CLOSE_PAREN)) {
- return new FormalParameterList(
- leftParenthesis, null, null, null, getAndAdvance());
+ Token _getEndToken(Token beginToken) {
+ if (beginToken is BeginToken) {
+ return beginToken.endToken;
}
- //
- // 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;
+ return null;
+ }
- // 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;
+ 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;
+ }
- 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, ["]"]);
+ /**
+ * Matches a generic comment type substitution and injects it into the token
+ * stream. Returns true if a match was injected, otherwise false.
+ *
+ * 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.
+ *
+ * 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;
}
- if (leftCurlyBracket != null && rightCurlyBracket == null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]);
+ // 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();
}
- //
- // Build the parameter list.
- //
- leftSquareBracket ??= leftCurlyBracket;
- rightSquareBracket ??= rightCurlyBracket;
- return new FormalParameterList(leftParenthesis, parameters,
- leftSquareBracket, rightSquareBracket, rightParenthesis);
+
+ Token token = _skip(_currentToken.next, parseOperation);
+ if (token == null || !_tokenMatches(token, TokenType.COLON)) {
+ return false;
+ }
+ token = _skip(token.next, parseOperation);
+ return token != null;
}
/**
- * Parse a list of formal parameters. Return the formal parameters that were
- * parsed.
- *
- * This method assumes that the current token matches `TokenType.OPEN_PAREN`.
+ * Return `true` if the given [character] is a valid hexadecimal digit.
*/
- FormalParameterList _parseFormalParameterListUnchecked() {
- return _parseFormalParameterListAfterParen(getAndAdvance());
+ 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);
}
/**
- * Parse a function declaration. The [commentAndMetadata] is the documentation
- * comment and metadata to be associated with the declaration. The
- * [externalKeyword] is the 'external' keyword, or `null` if the function is
- * not external. The [returnType] is the return type, or `null` if there is no
- * return type. The [isStatement] is `true` if the function declaration is
- * being parsed as a statement. Return the function declaration that was
- * parsed.
+ * 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.
*
- * functionDeclaration ::=
- * functionSignature functionBody
- * | returnType? getOrSet identifier formalParameterList functionBody
+ * This method uses the syntax described by the
+ * <a href="http://daringfireball.net/projects/markdown/syntax">markdown</a>
+ * project.
*/
- FunctionDeclaration _parseFunctionDeclaration(
- CommentAndMetadata commentAndMetadata,
- Token externalKeyword,
- TypeName returnType) {
- Token keywordToken = null;
- bool isGetter = false;
- Keyword keyword = _currentToken.keyword;
- SimpleIdentifier name = null;
- if (keyword == Keyword.GET) {
- keywordToken = getAndAdvance();
- isGetter = true;
- } else if (keyword == Keyword.SET) {
- keywordToken = getAndAdvance();
+ bool _isLinkText(String comment, int rightIndex) {
+ int length = comment.length;
+ int index = rightIndex + 1;
+ if (index >= length) {
+ return false;
}
- if (keywordToken != null && _matches(TokenType.OPEN_PAREN)) {
- name = new SimpleIdentifier(keywordToken, isDeclaration: true);
- keywordToken = null;
- isGetter = false;
- } else {
- name = parseSimpleIdentifier(isDeclaration: true);
+ int nextChar = comment.codeUnitAt(index);
+ if (nextChar == 0x28 || nextChar == 0x3A) {
+ return true;
}
- TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
- FormalParameterList parameters = null;
- if (!isGetter) {
- if (_matches(TokenType.OPEN_PAREN)) {
- parameters = _parseFormalParameterListUnchecked();
- _validateFormalParameterList(parameters);
- } else {
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_FUNCTION_PARAMETERS);
- parameters = new FormalParameterList(
- _createSyntheticToken(TokenType.OPEN_PAREN),
- null,
- null,
- null,
- _createSyntheticToken(TokenType.CLOSE_PAREN));
+ while (Character.isWhitespace(nextChar)) {
+ index = index + 1;
+ if (index >= length) {
+ return false;
}
- } else if (_matches(TokenType.OPEN_PAREN)) {
- _reportErrorForCurrentToken(ParserErrorCode.GETTER_WITH_PARAMETERS);
- _parseFormalParameterListUnchecked();
- }
- FunctionBody body;
- if (externalKeyword == null) {
- body = parseFunctionBody(
- false, ParserErrorCode.MISSING_FUNCTION_BODY, false);
- } else {
- body = new EmptyFunctionBody(_expect(TokenType.SEMICOLON));
+ nextChar = comment.codeUnitAt(index);
}
-// if (!isStatement && matches(TokenType.SEMICOLON)) {
-// // TODO(brianwilkerson) Improve this error message.
-// reportError(ParserErrorCode.UNEXPECTED_TOKEN, currentToken.getLexeme());
-// advance();
-// }
- return new FunctionDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- externalKeyword,
- returnType,
- keywordToken,
- name,
- new FunctionExpression(typeParameters, parameters, body));
+ return nextChar == 0x5B;
}
/**
- * 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.
- * Return the function declaration statement that was parsed.
- *
- * functionDeclarationStatement ::=
- * functionSignature functionBody
+ * Return `true` if the given [startToken] appears to be the beginning of an
+ * operator declaration.
*/
- Statement _parseFunctionDeclarationStatementAfterReturnType(
- CommentAndMetadata commentAndMetadata, TypeName returnType) {
- FunctionDeclaration declaration =
- _parseFunctionDeclaration(commentAndMetadata, null, returnType);
- Token propertyKeyword = declaration.propertyKeyword;
- if (propertyKeyword != null) {
- if (propertyKeyword.keyword == Keyword.GET) {
- _reportErrorForToken(
- ParserErrorCode.GETTER_IN_FUNCTION, propertyKeyword);
- } else {
- _reportErrorForToken(
- ParserErrorCode.SETTER_IN_FUNCTION, propertyKeyword);
- }
+ bool _isOperator(Token startToken) {
+ // Accept any operator here, even if it is not user definable.
+ if (!startToken.isOperator) {
+ return false;
}
- return new FunctionDeclarationStatement(declaration);
+ // 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 function type alias. The [commentAndMetadata] is the metadata to be
- * associated with the member. The [keyword] is the token representing the
- * 'typedef' keyword. Return the function type alias that was parsed.
- *
- * functionTypeAlias ::=
- * functionPrefix typeParameterList? formalParameterList ';'
- *
- * functionPrefix ::=
- * returnType? name
+ * 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.
*/
- FunctionTypeAlias _parseFunctionTypeAlias(
- CommentAndMetadata commentAndMetadata, Token keyword) {
- TypeName returnType = null;
- if (hasReturnTypeInTypeAlias) {
- returnType = parseReturnType();
- }
- SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
- TypeParameterList typeParameters = null;
- if (_matches(TokenType.LT)) {
- typeParameters = parseTypeParameterList();
+ 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;
}
- TokenType type = _currentToken.type;
- if (type == TokenType.SEMICOLON || type == TokenType.EOF) {
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS);
- FormalParameterList parameters = new FormalParameterList(
- _createSyntheticToken(TokenType.OPEN_PAREN),
- null,
- null,
- null,
- _createSyntheticToken(TokenType.CLOSE_PAREN));
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new FunctionTypeAlias(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- keyword,
- returnType,
- name,
- typeParameters,
- parameters,
- semicolon);
- } else if (type == TokenType.OPEN_PAREN) {
- FormalParameterList parameters = _parseFormalParameterListUnchecked();
- _validateFormalParameterList(parameters);
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new FunctionTypeAlias(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- keyword,
- returnType,
- name,
- typeParameters,
- parameters,
- semicolon);
- } else {
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS);
- // Recovery: At the very least we should skip to the start of the next
- // valid compilation unit member, allowing for the possibility of finding
- // the typedef parameters before that point.
- return new FunctionTypeAlias(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- keyword,
- returnType,
- name,
- typeParameters,
- new FormalParameterList(_createSyntheticToken(TokenType.OPEN_PAREN),
- null, null, null, _createSyntheticToken(TokenType.CLOSE_PAREN)),
- _createSyntheticToken(TokenType.SEMICOLON));
+ return false;
+ }
+
+ /**
+ * Increments the error reporting lock level. If level is more than `0`, then
+ * [reportError] wont report any error.
+ */
+ void _lockErrorListener() {
+ _errorListenerLock++;
+ }
+
+ /**
+ * 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].
+ */
+ 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;
}
/**
- * Parses generic type parameters from a comment.
- *
- * Normally this is handled by [_parseGenericMethodTypeParameters], but if the
- * code already handles the normal generic type parameters, the comment
- * matcher can be called directly. For example, we may have already tried
- * matching `<` (less than sign) in a method declaration, and be currently
- * on the `(` (open paren) because we didn't find it. In that case, this
- * function will parse the preceding comment such as `/*<T, R>*/`.
+ * Return `true` if the current token is a valid identifier. Valid identifiers
+ * include built-in identifiers (pseudo-keywords).
*/
- TypeParameterList _parseGenericCommentTypeParameters() {
- if (_injectGenericCommentTypeList()) {
- return parseTypeParameterList();
- }
- return null;
- }
+ bool _matchesIdentifier() => _tokenMatchesIdentifier(_currentToken);
/**
- * Parse the generic method or function's type parameters.
- *
- * For backwards compatibility this can optionally use comments.
- * See [parseGenericMethodComments].
+ * Return `true` if the current token matches the given [keyword].
*/
- TypeParameterList _parseGenericMethodTypeParameters() {
- if (parseGenericMethods && _matches(TokenType.LT) ||
- _injectGenericCommentTypeList()) {
- return parseTypeParameterList();
- }
- return null;
- }
+ bool _matchesKeyword(Keyword keyword) =>
+ _tokenMatchesKeyword(_currentToken, keyword);
/**
- * Parse a getter. The [commentAndMetadata] is the documentation comment and
- * metadata to be associated with the declaration. The externalKeyword] is the
- * 'external' token. The staticKeyword] is the static keyword, or `null` if
- * the getter is not static. The [returnType] the return type that has already
- * been parsed, or `null` if there was no return type. Return the getter that
- * was parsed.
- *
- * This method assumes that the current token matches `Keyword.GET`.
- *
- * getter ::=
- * getterSignature functionBody?
- *
- * getterSignature ::=
- * 'external'? 'static'? returnType? 'get' identifier
+ * Return `true` if the current token matches the given [identifier].
*/
- MethodDeclaration _parseGetter(CommentAndMetadata commentAndMetadata,
- Token externalKeyword, Token staticKeyword, TypeName returnType) {
- Token propertyKeyword = getAndAdvance();
- SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
- if (_matches(TokenType.OPEN_PAREN) &&
- _tokenMatches(_peek(), TokenType.CLOSE_PAREN)) {
- _reportErrorForCurrentToken(ParserErrorCode.GETTER_WITH_PARAMETERS);
- _advance();
+ 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;
}
- FunctionBody body = parseFunctionBody(
- externalKeyword != null || staticKeyword == null,
- ParserErrorCode.STATIC_GETTER_WITHOUT_BODY,
- false);
- if (externalKeyword != null && body is! EmptyFunctionBody) {
- _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_GETTER_WITH_BODY);
- }
- return new MethodDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- externalKeyword,
- staticKeyword,
- returnType,
- propertyKeyword,
- null,
- name,
- null,
- null,
- body);
+ return false;
}
/**
- * Parse a list of identifiers. Return the list of identifiers that were
- * parsed.
- *
- * identifierList ::=
- * identifier (',' identifier)*
+ * 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.
*/
- List<SimpleIdentifier> _parseIdentifierList() {
- List<SimpleIdentifier> identifiers = <SimpleIdentifier>[
- parseSimpleIdentifier()
- ];
- while (_optional(TokenType.COMMA)) {
- identifiers.add(parseSimpleIdentifier());
+ ArgumentList _parseArgumentListChecked() {
+ if (_matches(TokenType.OPEN_PAREN)) {
+ return parseArgumentList();
}
- return identifiers;
+ _reportErrorForCurrentToken(
+ ParserErrorCode.EXPECTED_TOKEN, [TokenType.OPEN_PAREN.lexeme]);
+ // Recovery: Look to see whether there is a close paren that isn't matched
+ // to an open paren and if so parse the list of arguments as normal.
+ return new ArgumentList(_createSyntheticToken(TokenType.OPEN_PAREN), null,
+ _createSyntheticToken(TokenType.CLOSE_PAREN));
}
/**
- * Parse an import directive. The [commentAndMetadata] is the metadata to be
- * associated with the directive. Return the import directive that was parsed.
+ * Parse an assert within a constructor's initializer list. Return the assert.
*
- * This method assumes that the current token matches `Keyword.IMPORT`.
+ * This method assumes that the current token matches `Keyword.ASSERT`.
*
- * importDirective ::=
- * metadata 'import' stringLiteral configuration* (deferred)? ('as' identifier)? combinator*';'
+ * assertInitializer ::=
+ * 'assert' '(' expression [',' expression] ')'
*/
- ImportDirective _parseImportDirective(CommentAndMetadata commentAndMetadata) {
- Token importKeyword = getAndAdvance();
- StringLiteral libraryUri = _parseUri();
- List<Configuration> configurations = _parseConfigurations();
- Token deferredToken = null;
- Token asToken = null;
- SimpleIdentifier prefix = null;
- if (_matchesKeyword(Keyword.DEFERRED)) {
- deferredToken = getAndAdvance();
- }
- if (_matchesKeyword(Keyword.AS)) {
- asToken = getAndAdvance();
- prefix = parseSimpleIdentifier(isDeclaration: true);
- } else if (deferredToken != null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.MISSING_PREFIX_IN_DEFERRED_IMPORT);
- } else if (!_matches(TokenType.SEMICOLON) &&
- !_matchesString(_SHOW) &&
- !_matchesString(_HIDE)) {
- Token nextToken = _peek();
- if (_tokenMatchesKeyword(nextToken, Keyword.AS) ||
- _tokenMatchesString(nextToken, _SHOW) ||
- _tokenMatchesString(nextToken, _HIDE)) {
- _reportErrorForCurrentToken(
- ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken]);
- _advance();
- if (_matchesKeyword(Keyword.AS)) {
- asToken = getAndAdvance();
- prefix = parseSimpleIdentifier(isDeclaration: true);
- }
- }
+ 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();
}
- List<Combinator> combinators = parseCombinators();
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new ImportDirective(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- importKeyword,
- libraryUri,
- configurations,
- deferredToken,
- asToken,
- prefix,
- combinators,
- semicolon);
+ Token rightParen = _expect(TokenType.CLOSE_PAREN);
+// return new AssertInitializer(
+// keyword, leftParen, expression, comma, message, rightParen);
}
/**
- * Parse a list of initialized identifiers. The [commentAndMetadata] is the
- * documentation comment and metadata to be associated with the declaration.
- * The [staticKeyword] is the static keyword, or `null` if the getter is not
- * static. The [keyword] is the token representing the 'final', 'const' or
- * 'var' keyword, or `null` if there is no keyword. The [type] is the type
- * that has already been parsed, or `null` if 'var' was provided. Return the
- * getter that was parsed.
- *
- * ?? ::=
- * 'static'? ('var' | type) initializedIdentifierList ';'
- * | 'final' type? initializedIdentifierList ';'
- *
- * initializedIdentifierList ::=
- * initializedIdentifier (',' initializedIdentifier)*
- *
- * initializedIdentifier ::=
- * identifier ('=' expression)?
+ * 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.
*/
- FieldDeclaration _parseInitializedIdentifierList(
- CommentAndMetadata commentAndMetadata,
- Token staticKeyword,
- Token keyword,
- TypeName type) {
- VariableDeclarationList fieldList =
- parseVariableDeclarationListAfterType(null, keyword, type);
- return new FieldDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- staticKeyword,
- fieldList,
- _expect(TokenType.SEMICOLON));
+ 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;
+ }
}
/**
- * Parse an instance creation expression. The [keyword] is the 'new' or
- * 'const' keyword that introduces the expression. Return the instance
- * creation expression that was parsed.
+ * 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.
*
- * instanceCreationExpression ::=
- * ('new' | 'const') type ('.' identifier)? argumentList
+ * block ::=
+ * '{' statements '}'
*/
- InstanceCreationExpression _parseInstanceCreationExpression(Token keyword) {
- ConstructorName constructorName = parseConstructorName();
- ArgumentList argumentList = _parseArgumentListChecked();
- return new InstanceCreationExpression(
- keyword, constructorName, argumentList);
+ 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 library directive. The [commentAndMetadata] is the metadata to be
- * associated with the directive. Return the library directive that was
- * parsed.
- *
- * This method assumes that the current token matches `Keyword.LIBRARY`.
+ * 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.
*
- * libraryDirective ::=
- * metadata 'library' identifier ';'
+ * classMembers ::=
+ * (metadata memberDefinition)*
*/
- LibraryDirective _parseLibraryDirective(
- CommentAndMetadata commentAndMetadata) {
- Token keyword = getAndAdvance();
- LibraryIdentifier libraryName = _parseLibraryName(
- ParserErrorCode.MISSING_NAME_IN_LIBRARY_DIRECTIVE, keyword);
- Token semicolon = _expect(TokenType.SEMICOLON);
- return new LibraryDirective(commentAndMetadata.comment,
- commentAndMetadata.metadata, keyword, libraryName, semicolon);
+ 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 members;
}
/**
- * Parse a library name. The [missingNameError] is the error code to be used
- * if the library name is missing. The [missingNameToken] is the token
- * associated with the error produced if the library name is missing. Return
- * the library name that was parsed.
+ * 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.
*
- * libraryName ::=
- * libraryIdentifier
+ * classTypeAlias ::=
+ * identifier typeParameters? '=' 'abstract'? mixinApplication
+ *
+ * mixinApplication ::=
+ * type withClause implementsClause? ';'
*/
- LibraryIdentifier _parseLibraryName(
- ParserErrorCode missingNameError, Token missingNameToken) {
- if (_matchesIdentifier()) {
- return parseLibraryIdentifier();
- } else if (_matches(TokenType.STRING)) {
- // Recovery: This should be extended to handle arbitrary tokens until we
- // can find a token that can start a compilation unit member.
- StringLiteral string = parseStringLiteral();
- _reportErrorForNode(ParserErrorCode.NON_IDENTIFIER_LIBRARY_NAME, string);
+ 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 {
- _reportErrorForToken(missingNameError, missingNameToken);
+ _reportErrorForCurrentToken(
+ ParserErrorCode.EXPECTED_TOKEN, [Keyword.WITH.syntax]);
}
- return new LibraryIdentifier(
- <SimpleIdentifier>[createSyntheticIdentifier()]);
+ 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]);
+ }
+ semicolon = _createSyntheticToken(TokenType.SEMICOLON);
+ }
+ return new ClassTypeAlias(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ classKeyword,
+ className,
+ typeParameters,
+ equals,
+ abstractKeyword,
+ superclass,
+ withClause,
+ implementsClause,
+ semicolon);
}
/**
- * Parse a list literal. The [modifier] is the 'const' modifier appearing
- * before the literal, or `null` if there is no modifier. The [typeArguments]
- * is the type arguments appearing before the literal, or `null` if there are
- * no type arguments. Return the list literal that was parsed.
- *
- * This method assumes that the current token matches either
- * `TokenType.OPEN_SQUARE_BRACKET` or `TokenType.INDEX`.
- *
- * listLiteral ::=
- * 'const'? typeArguments? '[' (expressionList ','?)? ']'
+ * Parse a list of configurations. Return the configurations that were parsed,
+ * or `null` if there are no configurations.
*/
- ListLiteral _parseListLiteral(
- Token modifier, TypeArgumentList typeArguments) {
- if (_matches(TokenType.INDEX)) {
- // Split the token into two separate tokens.
- BeginToken leftBracket = _createToken(
- _currentToken, TokenType.OPEN_SQUARE_BRACKET,
- isBegin: true);
- Token rightBracket =
- new Token(TokenType.CLOSE_SQUARE_BRACKET, _currentToken.offset + 1);
- leftBracket.endToken = rightBracket;
- rightBracket.setNext(_currentToken.next);
- leftBracket.setNext(rightBracket);
- _currentToken.previous.setNext(leftBracket);
- _currentToken = _currentToken.next;
- return new ListLiteral(
- modifier, typeArguments, leftBracket, null, rightBracket);
- }
- Token leftBracket = getAndAdvance();
- if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
- return new ListLiteral(
- modifier, typeArguments, leftBracket, null, getAndAdvance());
+ List<Configuration> _parseConfigurations() {
+ List<Configuration> configurations = null;
+ while (_matchesKeyword(Keyword.IF)) {
+ configurations ??= <Configuration>[];
+ configurations.add(parseConfiguration());
}
- bool wasInInitializer = _inInitializer;
- _inInitializer = false;
- try {
- List<Expression> elements = <Expression>[parseExpression2()];
- while (_optional(TokenType.COMMA)) {
- if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
- return new ListLiteral(
- modifier, typeArguments, leftBracket, elements, getAndAdvance());
+ 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.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);
}
- elements.add(parseExpression2());
}
- Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
- return new ListLiteral(
- modifier, typeArguments, leftBracket, elements, rightBracket);
- } finally {
- _inInitializer = wasInInitializer;
}
+ return new ConstructorDeclaration(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ externalKeyword,
+ constKeyword,
+ factoryKeyword,
+ returnType,
+ period,
+ name,
+ parameters,
+ separator,
+ initializers,
+ redirectedConstructor,
+ body);
}
/**
- * Parse a logical and expression. Return the logical and expression that was
- * parsed.
+ * Parse an enum constant declaration. Return the enum constant declaration
+ * that was parsed.
*
- * logicalAndExpression ::=
- * equalityExpression ('&&' equalityExpression)*
+ * Specified:
+ *
+ * enumConstant ::=
+ * id
+ *
+ * Actual:
+ *
+ * enumConstant ::=
+ * metadata id
*/
- Expression _parseLogicalAndExpression() {
- Expression expression = _parseEqualityExpression();
- while (_currentToken.type == TokenType.AMPERSAND_AMPERSAND) {
- expression = new BinaryExpression(
- expression, getAndAdvance(), _parseEqualityExpression());
+ EnumConstantDeclaration _parseEnumConstantDeclaration() {
+ CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
+ SimpleIdentifier name;
+ if (_matchesIdentifier()) {
+ name = _parseSimpleIdentifierUnchecked(isDeclaration: true);
+ } else {
+ name = createSyntheticIdentifier();
}
- return expression;
+ if (commentAndMetadata.hasMetadata) {
+ _reportErrorForNode(ParserErrorCode.ANNOTATION_ON_ENUM_CONSTANT,
+ commentAndMetadata.metadata[0]);
+ }
+ return new EnumConstantDeclaration(
+ commentAndMetadata.comment, commentAndMetadata.metadata, name);
}
/**
- * Parse a map literal. The [modifier] is the 'const' modifier appearing
- * before the literal, or `null` if there is no modifier. The [typeArguments]
- * is the type arguments that were declared, or `null` if there are no type
- * arguments. Return the map literal that was parsed.
- *
- * This method assumes that the current token matches
- * `TokenType.OPEN_CURLY_BRACKET`.
- *
- * mapLiteral ::=
- * 'const'? typeArguments? '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}'
+ * Parse a list of formal parameters given that the list starts with the given
+ * [leftParenthesis]. Return the formal parameters that were parsed.
*/
- MapLiteral _parseMapLiteral(Token modifier, TypeArgumentList typeArguments) {
- Token leftBracket = getAndAdvance();
- if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
- return new MapLiteral(
- modifier, typeArguments, leftBracket, null, getAndAdvance());
+ FormalParameterList _parseFormalParameterListAfterParen(
+ Token leftParenthesis) {
+ if (_matches(TokenType.CLOSE_PAREN)) {
+ return new FormalParameterList(
+ leftParenthesis, null, null, null, getAndAdvance());
}
- bool wasInInitializer = _inInitializer;
- _inInitializer = false;
- try {
- List<MapLiteralEntry> entries = <MapLiteralEntry>[parseMapLiteralEntry()];
- while (_optional(TokenType.COMMA)) {
- if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
- return new MapLiteral(
- modifier, typeArguments, leftBracket, entries, 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;
}
- entries.add(parseMapLiteralEntry());
}
- Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
- return new MapLiteral(
- modifier, typeArguments, leftBracket, entries, rightBracket);
- } finally {
- _inInitializer = wasInInitializer;
+ 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 (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 method declaration. The [commentAndMetadata] is the documentation
- * comment and metadata to be associated with the declaration. The
- * [externalKeyword] is the 'external' token. The [staticKeyword] is the
- * static keyword, or `null` if the getter is not static. The [returnType] is
- * the return type of the method. The [name] is the name of the method. The
- * [parameters] is the parameters to the method. Return the method declaration
- * that was parsed.
+ * Parse a list of formal parameters. Return the formal parameters that were
+ * parsed.
*
- * functionDeclaration ::=
- * ('external' 'static'?)? functionSignature functionBody
- * | 'external'? functionSignature ';'
+ * This method assumes that the current token matches `TokenType.OPEN_PAREN`.
*/
- MethodDeclaration _parseMethodDeclarationAfterParameters(
- CommentAndMetadata commentAndMetadata,
- Token externalKeyword,
- Token staticKeyword,
- TypeName returnType,
- SimpleIdentifier name,
- TypeParameterList typeParameters,
- FormalParameterList parameters) {
- FunctionBody body = parseFunctionBody(
- externalKeyword != null || staticKeyword == null,
- ParserErrorCode.MISSING_FUNCTION_BODY,
- false);
- if (externalKeyword != null) {
- if (body is! EmptyFunctionBody) {
- _reportErrorForNode(ParserErrorCode.EXTERNAL_METHOD_WITH_BODY, body);
- }
- } else if (staticKeyword != null) {
- if (body is EmptyFunctionBody && _parseFunctionBodies) {
- _reportErrorForNode(ParserErrorCode.ABSTRACT_STATIC_METHOD, body);
+ FormalParameterList _parseFormalParameterListUnchecked() {
+ return _parseFormalParameterListAfterParen(getAndAdvance());
+ }
+
+ /**
+ * 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.
+ * Return the function declaration statement that was parsed.
+ *
+ * functionDeclarationStatement ::=
+ * functionSignature functionBody
+ */
+ Statement _parseFunctionDeclarationStatementAfterReturnType(
+ CommentAndMetadata commentAndMetadata, TypeName returnType) {
+ FunctionDeclaration declaration =
+ parseFunctionDeclaration(commentAndMetadata, null, returnType);
+ Token propertyKeyword = declaration.propertyKeyword;
+ if (propertyKeyword != null) {
+ if (propertyKeyword.keyword == Keyword.GET) {
+ _reportErrorForToken(
+ ParserErrorCode.GETTER_IN_FUNCTION, propertyKeyword);
+ } else {
+ _reportErrorForToken(
+ ParserErrorCode.SETTER_IN_FUNCTION, propertyKeyword);
}
}
- return new MethodDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- externalKeyword,
- staticKeyword,
- returnType,
- null,
- null,
- name,
- typeParameters,
- parameters,
- body);
+ return new FunctionDeclarationStatement(declaration);
}
/**
- * Parse a method declaration. The [commentAndMetadata] is the documentation
- * comment and metadata to be associated with the declaration. The
- * [externalKeyword] is the 'external' token. The [staticKeyword] is the
- * static keyword, or `null` if the getter is not static. The [returnType] is
- * the return type of the method. Return the method declaration that was
- * parsed.
+ * Parse a function type alias. The [commentAndMetadata] is the metadata to be
+ * associated with the member. The [keyword] is the token representing the
+ * 'typedef' keyword. Return the function type alias that was parsed.
*
- * functionDeclaration ::=
- * 'external'? 'static'? functionSignature functionBody
- * | 'external'? functionSignature ';'
+ * functionTypeAlias ::=
+ * functionPrefix typeParameterList? formalParameterList ';'
+ *
+ * functionPrefix ::=
+ * returnType? name
*/
- MethodDeclaration _parseMethodDeclarationAfterReturnType(
- CommentAndMetadata commentAndMetadata,
- Token externalKeyword,
- Token staticKeyword,
- TypeName returnType) {
- SimpleIdentifier methodName = parseSimpleIdentifier(isDeclaration: true);
- TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
- FormalParameterList parameters;
+ FunctionTypeAlias _parseFunctionTypeAlias(
+ CommentAndMetadata commentAndMetadata, Token keyword) {
+ TypeName returnType = null;
+ if (hasReturnTypeInTypeAlias) {
+ returnType = parseReturnType();
+ }
+ SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
+ TypeParameterList typeParameters = null;
+ if (_matches(TokenType.LT)) {
+ typeParameters = parseTypeParameterList();
+ }
TokenType type = _currentToken.type;
- // TODO(brianwilkerson) Figure out why we care what the current token is if
- // it isn't a paren.
- if (type != TokenType.OPEN_PAREN &&
- (type == TokenType.OPEN_CURLY_BRACKET || type == TokenType.FUNCTION)) {
- _reportErrorForToken(
- ParserErrorCode.MISSING_METHOD_PARAMETERS, _currentToken.previous);
- parameters = new FormalParameterList(
+ if (type == TokenType.SEMICOLON || type == TokenType.EOF) {
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS);
+ FormalParameterList parameters = new FormalParameterList(
_createSyntheticToken(TokenType.OPEN_PAREN),
null,
null,
null,
_createSyntheticToken(TokenType.CLOSE_PAREN));
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new FunctionTypeAlias(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ keyword,
+ returnType,
+ name,
+ typeParameters,
+ parameters,
+ semicolon);
+ } else if (type == TokenType.OPEN_PAREN) {
+ FormalParameterList parameters = _parseFormalParameterListUnchecked();
+ _validateFormalParameterList(parameters);
+ Token semicolon = _expect(TokenType.SEMICOLON);
+ return new FunctionTypeAlias(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ keyword,
+ returnType,
+ name,
+ typeParameters,
+ parameters,
+ semicolon);
} else {
- parameters = parseFormalParameterList();
+ _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS);
+ // Recovery: At the very least we should skip to the start of the next
+ // valid compilation unit member, allowing for the possibility of finding
+ // the typedef parameters before that point.
+ return new FunctionTypeAlias(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ keyword,
+ returnType,
+ name,
+ typeParameters,
+ new FormalParameterList(_createSyntheticToken(TokenType.OPEN_PAREN),
+ null, null, null, _createSyntheticToken(TokenType.CLOSE_PAREN)),
+ _createSyntheticToken(TokenType.SEMICOLON));
}
- _validateFormalParameterList(parameters);
- return _parseMethodDeclarationAfterParameters(
- commentAndMetadata,
- externalKeyword,
- staticKeyword,
- returnType,
- methodName,
- typeParameters,
- parameters);
}
/**
- * Parse the modifiers preceding a declaration. This method allows the
- * modifiers to appear in any order but does generate errors for duplicated
- * modifiers. Checks for other problems, such as having the modifiers appear
- * in the wrong order or specifying both 'const' and 'final', are reported in
- * one of the methods whose name is prefixed with `validateModifiersFor`.
- * Return the modifiers that were parsed.
+ * Parses generic type parameters from a comment.
*
- * modifiers ::=
- * ('abstract' | 'const' | 'external' | 'factory' | 'final' | 'static' | 'var')*
- */
- Modifiers _parseModifiers() {
- Modifiers modifiers = new Modifiers();
- bool progress = true;
- while (progress) {
- TokenType nextType = _peek().type;
- if (nextType == TokenType.PERIOD ||
- nextType == TokenType.LT ||
- nextType == TokenType.OPEN_PAREN) {
- return modifiers;
- }
- Keyword keyword = _currentToken.keyword;
- if (keyword == Keyword.ABSTRACT) {
- if (modifiers.abstractKeyword != null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
- _advance();
- } else {
- modifiers.abstractKeyword = getAndAdvance();
- }
- } else if (keyword == Keyword.CONST) {
- if (modifiers.constKeyword != null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
- _advance();
- } else {
- modifiers.constKeyword = getAndAdvance();
- }
- } else if (keyword == Keyword.EXTERNAL) {
- if (modifiers.externalKeyword != null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
- _advance();
- } else {
- modifiers.externalKeyword = getAndAdvance();
- }
- } else if (keyword == Keyword.FACTORY) {
- if (modifiers.factoryKeyword != null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
- _advance();
- } else {
- modifiers.factoryKeyword = getAndAdvance();
- }
- } else if (keyword == Keyword.FINAL) {
- if (modifiers.finalKeyword != null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
- _advance();
- } else {
- modifiers.finalKeyword = getAndAdvance();
- }
- } else if (keyword == Keyword.STATIC) {
- if (modifiers.staticKeyword != null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
- _advance();
- } else {
- modifiers.staticKeyword = getAndAdvance();
- }
- } else if (keyword == Keyword.VAR) {
- if (modifiers.varKeyword != null) {
- _reportErrorForCurrentToken(
- ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]);
- _advance();
- } else {
- modifiers.varKeyword = getAndAdvance();
- }
- } else {
- progress = false;
- }
+ * Normally this is handled by [_parseGenericMethodTypeParameters], but if the
+ * code already handles the normal generic type parameters, the comment
+ * matcher can be called directly. For example, we may have already tried
+ * matching `<` (less than sign) in a method declaration, and be currently
+ * on the `(` (open paren) because we didn't find it. In that case, this
+ * function will parse the preceding comment such as `/*<T, R>*/`.
+ */
+ TypeParameterList _parseGenericCommentTypeParameters() {
+ if (_injectGenericCommentTypeList()) {
+ return parseTypeParameterList();
}
- return modifiers;
+ return null;
}
/**
- * Parse a class native clause. Return the native clause that was parsed.
- *
- * This method assumes that the current token matches `_NATIVE`.
+ * Parse the generic method or function's type parameters.
*
- * classNativeClause ::=
- * 'native' name
+ * For backwards compatibility this can optionally use comments.
+ * See [parseGenericMethodComments].
*/
- NativeClause _parseNativeClause() {
- Token keyword = getAndAdvance();
- StringLiteral name = parseStringLiteral();
- return new NativeClause(keyword, name);
+ TypeParameterList _parseGenericMethodTypeParameters() {
+ if (parseGenericMethods && _matches(TokenType.LT) ||
+ _injectGenericCommentTypeList()) {
+ return parseTypeParameterList();
+ }
+ return null;
}
/**
- * Parse a new expression. Return the new expression that was parsed.
- *
- * This method assumes that the current token matches `Keyword.NEW`.
+ * Parse a library name. The [missingNameError] is the error code to be used
+ * if the library name is missing. The [missingNameToken] is the token
+ * associated with the error produced if the library name is missing. Return
+ * the library name that was parsed.
*
- * newExpression ::=
- * instanceCreationExpression
+ * libraryName ::=
+ * libraryIdentifier
*/
- InstanceCreationExpression _parseNewExpression() =>
- _parseInstanceCreationExpression(getAndAdvance());
+ LibraryIdentifier _parseLibraryName(
+ ParserErrorCode missingNameError, Token missingNameToken) {
+ if (_matchesIdentifier()) {
+ return parseLibraryIdentifier();
+ } else if (_matches(TokenType.STRING)) {
+ // Recovery: This should be extended to handle arbitrary tokens until we
+ // can find a token that can start a compilation unit member.
+ StringLiteral string = parseStringLiteral();
+ _reportErrorForNode(ParserErrorCode.NON_IDENTIFIER_LIBRARY_NAME, string);
+ } else {
+ _reportErrorForToken(missingNameError, missingNameToken);
+ }
+ return new LibraryIdentifier(
+ <SimpleIdentifier>[createSyntheticIdentifier()]);
+ }
/**
- * Parse a non-labeled statement. Return the non-labeled statement that was
- * parsed.
+ * Parse a method declaration. The [commentAndMetadata] is the documentation
+ * comment and metadata to be associated with the declaration. The
+ * [externalKeyword] is the 'external' token. The [staticKeyword] is the
+ * static keyword, or `null` if the getter is not static. The [returnType] is
+ * the return type of the method. The [name] is the name of the method. The
+ * [parameters] is the parameters to the method. Return the method declaration
+ * that was parsed.
*
- * nonLabeledStatement ::=
- * block
- * | assertStatement
- * | breakStatement
- * | continueStatement
- * | doStatement
- * | forStatement
- * | ifStatement
- * | returnStatement
- * | switchStatement
- * | tryStatement
- * | whileStatement
- * | variableDeclarationList ';'
- * | expressionStatement
- * | functionSignature functionBody
+ * functionDeclaration ::=
+ * ('external' 'static'?)? functionSignature functionBody
+ * | 'external'? functionSignature ';'
*/
- Statement _parseNonLabeledStatement() {
- // TODO(brianwilkerson) Pass the comment and metadata on where appropriate.
- CommentAndMetadata commentAndMetadata = parseCommentAndMetadata();
- TokenType type = _currentToken.type;
- if (type == TokenType.OPEN_CURLY_BRACKET) {
- if (_tokenMatches(_peek(), TokenType.STRING)) {
- Token afterString = skipStringLiteral(_currentToken.next);
- if (afterString != null && afterString.type == TokenType.COLON) {
- return new ExpressionStatement(
- parseExpression2(), _expect(TokenType.SEMICOLON));
- }
- }
- return parseBlock();
- } else if (type == TokenType.KEYWORD &&
- !_currentToken.keyword.isPseudoKeyword) {
- Keyword keyword = _currentToken.keyword;
- // TODO(jwren) compute some metrics to figure out a better order for this
- // if-then sequence to optimize performance
- if (keyword == Keyword.ASSERT) {
- return parseAssertStatement();
- } else if (keyword == Keyword.BREAK) {
- return parseBreakStatement();
- } else if (keyword == Keyword.CONTINUE) {
- return parseContinueStatement();
- } else if (keyword == Keyword.DO) {
- return parseDoStatement();
- } else if (keyword == Keyword.FOR) {
- return parseForStatement();
- } else if (keyword == Keyword.IF) {
- return parseIfStatement();
- } else if (keyword == Keyword.RETHROW) {
- return new ExpressionStatement(
- parseRethrowExpression(), _expect(TokenType.SEMICOLON));
- } else if (keyword == Keyword.RETURN) {
- return parseReturnStatement();
- } else if (keyword == Keyword.SWITCH) {
- return parseSwitchStatement();
- } else if (keyword == Keyword.THROW) {
- return new ExpressionStatement(
- parseThrowExpression(), _expect(TokenType.SEMICOLON));
- } else if (keyword == Keyword.TRY) {
- return parseTryStatement();
- } else if (keyword == Keyword.WHILE) {
- return parseWhileStatement();
- } else if (keyword == Keyword.VAR || keyword == Keyword.FINAL) {
- return parseVariableDeclarationStatementAfterMetadata(
- commentAndMetadata);
- } else if (keyword == Keyword.VOID) {
- TypeName returnType =
- new TypeName(new SimpleIdentifier(getAndAdvance()), null);
- Token next = _currentToken.next;
- if (_matchesIdentifier() &&
- next.matchesAny(const <TokenType>[
- TokenType.OPEN_PAREN,
- TokenType.OPEN_CURLY_BRACKET,
- TokenType.FUNCTION,
- TokenType.LT
- ])) {
- return _parseFunctionDeclarationStatementAfterReturnType(
- commentAndMetadata, 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 parseVariableDeclarationStatementAfterMetadata(
- commentAndMetadata);
- }
- } else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
- //
- // We appear to have found an incomplete statement at the end of a
- // block. Parse it as a variable declaration.
- //
- return _parseVariableDeclarationStatementAfterType(
- commentAndMetadata, null, returnType);
- }
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT);
- // TODO(brianwilkerson) Recover from this error.
- return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
- }
- } else if (keyword == Keyword.CONST) {
- Token next = _currentToken.next;
- if (next.matchesAny(const <TokenType>[
- TokenType.LT,
- TokenType.OPEN_CURLY_BRACKET,
- TokenType.OPEN_SQUARE_BRACKET,
- TokenType.INDEX
- ])) {
- return new ExpressionStatement(
- parseExpression2(), _expect(TokenType.SEMICOLON));
- } else if (_tokenMatches(next, TokenType.IDENTIFIER)) {
- Token afterType = skipTypeName(next);
- if (afterType != null) {
- if (_tokenMatches(afterType, TokenType.OPEN_PAREN) ||
- (_tokenMatches(afterType, TokenType.PERIOD) &&
- _tokenMatches(afterType.next, TokenType.IDENTIFIER) &&
- _tokenMatches(afterType.next.next, TokenType.OPEN_PAREN))) {
- return new ExpressionStatement(
- parseExpression2(), _expect(TokenType.SEMICOLON));
- }
- }
- }
- return parseVariableDeclarationStatementAfterMetadata(
- commentAndMetadata);
- } else if (keyword == Keyword.NEW ||
- keyword == Keyword.TRUE ||
- keyword == Keyword.FALSE ||
- keyword == Keyword.NULL ||
- keyword == Keyword.SUPER ||
- keyword == Keyword.THIS) {
- return new ExpressionStatement(
- parseExpression2(), _expect(TokenType.SEMICOLON));
- } else {
- //
- // We have found an error of some kind. Try to recover.
- //
- _reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT);
- return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON));
- }
- } else if (_inGenerator && _matchesString(_YIELD)) {
- return parseYieldStatement();
- } else if (_inAsync && _matchesString(_AWAIT)) {
- if (_tokenMatchesKeyword(_peek(), Keyword.FOR)) {
- return parseForStatement();
+ MethodDeclaration _parseMethodDeclarationAfterParameters(
+ CommentAndMetadata commentAndMetadata,
+ Token externalKeyword,
+ Token staticKeyword,
+ TypeName returnType,
+ SimpleIdentifier name,
+ TypeParameterList typeParameters,
+ FormalParameterList parameters) {
+ FunctionBody body = parseFunctionBody(
+ externalKeyword != null || staticKeyword == null,
+ ParserErrorCode.MISSING_FUNCTION_BODY,
+ false);
+ if (externalKeyword != null) {
+ if (body is! EmptyFunctionBody) {
+ _reportErrorForNode(ParserErrorCode.EXTERNAL_METHOD_WITH_BODY, body);
}
- return new ExpressionStatement(
- parseExpression2(), _expect(TokenType.SEMICOLON));
- } else if (_matchesString(_AWAIT) &&
- _tokenMatchesKeyword(_peek(), Keyword.FOR)) {
- Token awaitToken = _currentToken;
- Statement statement = parseForStatement();
- if (statement is! ForStatement) {
- _reportErrorForToken(
- CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT, awaitToken);
+ } else if (staticKeyword != null) {
+ if (body is EmptyFunctionBody && _parseFunctionBodies) {
+ _reportErrorForNode(ParserErrorCode.ABSTRACT_STATIC_METHOD, body);
}
- return statement;
- } else if (type == TokenType.SEMICOLON) {
- 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));
+ }
+ return new MethodDeclaration(
+ commentAndMetadata.comment,
+ commentAndMetadata.metadata,
+ externalKeyword,
+ staticKeyword,
+ returnType,
+ null,
+ null,
+ name,
+ typeParameters,
+ parameters,
+ body);
+ }
+
+ /**
+ * Parse a method declaration. The [commentAndMetadata] is the documentation
+ * comment and metadata to be associated with the declaration. The
+ * [externalKeyword] is the 'external' token. The [staticKeyword] is the
+ * static keyword, or `null` if the getter is not static. The [returnType] is
+ * the return type of the method. Return the method declaration that was
+ * parsed.
+ *
+ * functionDeclaration ::=
+ * 'external'? 'static'? functionSignature functionBody
+ * | 'external'? functionSignature ';'
+ */
+ MethodDeclaration _parseMethodDeclarationAfterReturnType(
+ CommentAndMetadata commentAndMetadata,
+ Token externalKeyword,
+ Token staticKeyword,
+ TypeName returnType) {
+ SimpleIdentifier methodName = parseSimpleIdentifier(isDeclaration: true);
+ TypeParameterList typeParameters = _parseGenericMethodTypeParameters();
+ FormalParameterList parameters;
+ TokenType type = _currentToken.type;
+ // TODO(brianwilkerson) Figure out why we care what the current token is if
+ // it isn't a paren.
+ if (type != TokenType.OPEN_PAREN &&
+ (type == TokenType.OPEN_CURLY_BRACKET || type == TokenType.FUNCTION)) {
+ _reportErrorForToken(
+ ParserErrorCode.MISSING_METHOD_PARAMETERS, _currentToken.previous);
+ parameters = new FormalParameterList(
+ _createSyntheticToken(TokenType.OPEN_PAREN),
+ null,
+ null,
+ null,
+ _createSyntheticToken(TokenType.CLOSE_PAREN));
} else {
- return new ExpressionStatement(
- parseExpression2(), _expect(TokenType.SEMICOLON));
+ parameters = parseFormalParameterList();
}
+ _validateFormalParameterList(parameters);
+ return _parseMethodDeclarationAfterParameters(
+ commentAndMetadata,
+ externalKeyword,
+ staticKeyword,
+ returnType,
+ methodName,
+ typeParameters,
+ parameters);
+ }
+
+ /**
+ * Parse a class native clause. Return the native clause that was parsed.
+ *
+ * This method assumes that the current token matches `_NATIVE`.
+ *
+ * classNativeClause ::=
+ * 'native' name
+ */
+ NativeClause _parseNativeClause() {
+ Token keyword = getAndAdvance();
+ StringLiteral name = parseStringLiteral();
+ return new NativeClause(keyword, name);
}
/**
@@ -7187,79 +6929,6 @@ class Parser {
}
/**
- * Parse a part or part-of directive. The [commentAndMetadata] is the metadata
- * to be associated with the directive. Return the part or part-of directive
- * that was parsed.
- *
- * This method assumes that the current token matches `Keyword.PART`.
- *
- * partDirective ::=
- * metadata 'part' stringLiteral ';'
- *
- * partOfDirective ::=
- * metadata 'part' 'of' identifier ';'
- */
- Directive _parsePartOrPartOfDirective(CommentAndMetadata commentAndMetadata) {
- if (_tokenMatchesString(_peek(), _OF)) {
- return _parsePartOfDirective(commentAndMetadata);
- }
- return _parsePartDirective(commentAndMetadata);
- }
-
- /**
- * Parse a postfix expression. Return the postfix expression that was parsed.
- *
- * postfixExpression ::=
- * assignableExpression postfixOperator
- * | primary selector*
- *
- * selector ::=
- * assignableSelector
- * | argumentList
- */
- Expression _parsePostfixExpression() {
- Expression operand = parseAssignableExpression(true);
- TokenType type = _currentToken.type;
- if (type == TokenType.OPEN_SQUARE_BRACKET ||
- type == TokenType.PERIOD ||
- type == TokenType.QUESTION_PERIOD ||
- type == TokenType.OPEN_PAREN ||
- (parseGenericMethods && type == TokenType.LT)) {
- do {
- if (_isLikelyArgumentList()) {
- TypeArgumentList typeArguments = _parseOptionalTypeArguments();
- ArgumentList argumentList = parseArgumentList();
- Expression currentOperand = operand;
- if (currentOperand is PropertyAccess) {
- operand = new MethodInvocation(
- currentOperand.target,
- currentOperand.operator,
- currentOperand.propertyName,
- typeArguments,
- argumentList);
- } else {
- operand = new FunctionExpressionInvocation(
- operand, typeArguments, argumentList);
- }
- } else {
- operand = _parseAssignableSelector(operand, true);
- }
- type = _currentToken.type;
- } while (type == TokenType.OPEN_SQUARE_BRACKET ||
- type == TokenType.PERIOD ||
- type == TokenType.QUESTION_PERIOD ||
- type == TokenType.OPEN_PAREN);
- return operand;
- }
- if (!_currentToken.type.isIncrementOperator) {
- return operand;
- }
- _ensureAssignable(operand);
- Token operator = getAndAdvance();
- return new PostfixExpression(operand, operator);
- }
-
- /**
* Parse a prefixed identifier given that the given [qualifier] was already
* parsed. Return the prefixed identifier that was parsed.
*
@@ -7291,79 +6960,6 @@ class Parser {
}
/**
- * 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 setter. The [commentAndMetadata] is the documentation comment and
- * metadata to be associated with the declaration. The [externalKeyword] is
- * the 'external' token. The [staticKeyword] is the static keyword, or `null`
- * if the setter is not static. The [returnType] is the return type that has
- * already been parsed, or `null` if there was no return type. Return the
- * setter that was parsed.
- *
- * This method assumes that the current token matches `Keyword.SET`.
- *
- * setter ::=
- * setterSignature functionBody?
- *
- * setterSignature ::=
- * 'external'? 'static'? returnType? 'set' identifier formalParameterList
- */
- MethodDeclaration _parseSetter(CommentAndMetadata commentAndMetadata,
- Token externalKeyword, Token staticKeyword, TypeName returnType) {
- Token propertyKeyword = getAndAdvance();
- SimpleIdentifier name = parseSimpleIdentifier(isDeclaration: true);
- FormalParameterList parameters = parseFormalParameterList();
- _validateFormalParameterList(parameters);
- FunctionBody body = parseFunctionBody(
- externalKeyword != null || staticKeyword == null,
- ParserErrorCode.STATIC_SETTER_WITHOUT_BODY,
- false);
- if (externalKeyword != null && body is! EmptyFunctionBody) {
- _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_SETTER_WITH_BODY);
- }
- return new MethodDeclaration(
- commentAndMetadata.comment,
- commentAndMetadata.metadata,
- externalKeyword,
- staticKeyword,
- returnType,
- propertyKeyword,
- null,
- name,
- null,
- parameters,
- body);
- }
-
- /**
* Parse a simple identifier. Return the simple identifier that was parsed.
*
* This method assumes that the current token matches an identifier.
@@ -7484,55 +7080,6 @@ class Parser {
return strings.length == 1 ? strings[0] : new AdjacentStrings(strings);
}
- /**
- * Parse a type alias. The [commentAndMetadata] is the metadata to be
- * associated with the member. Return the type alias that was parsed.
- *
- * This method assumes that the current token matches [Keyword.TYPEDEF].
- *
- * typeAlias ::=
- * 'typedef' typeAliasBody
- *
- * typeAliasBody ::=
- * classTypeAlias
- * | functionTypeAlias
- *
- * classTypeAlias ::=
- * identifier typeParameters? '=' 'abstract'? mixinApplication
- *
- * mixinApplication ::=
- * qualified withClause implementsClause? ';'
- *
- * functionTypeAlias ::=
- * functionPrefix typeParameterList? formalParameterList ';'
- *
- * functionPrefix ::=
- * returnType? name
- */
- TypeAlias _parseTypeAlias(CommentAndMetadata commentAndMetadata) {
- Token keyword = getAndAdvance();
- if (_matchesIdentifier()) {
- Token next = _peek();
- if (_tokenMatches(next, TokenType.LT)) {
- next = _skipTypeParameterList(next);
- if (next != null && _tokenMatches(next, TokenType.EQ)) {
- TypeAlias typeAlias =
- 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);
- _reportErrorForToken(
- ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS, keyword);
- return typeAlias;
- }
- }
- return _parseFunctionTypeAlias(commentAndMetadata, keyword);
- }
-
TypeName _parseTypeName(bool inExpression) {
Identifier typeName;
if (_matchesIdentifier()) {
« 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