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()) { |