Linter Demo Errors: 5Warnings: 150File: /home/fstrocco/Dart/dart/benchmark/analyzer/lib/src/generated/parser.dart // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. // This code was auto-generated, is not intended to be edited, and is subject to // significant change. Please see the README file for more information. library engine.parser; import "dart:math" as math; import 'dart:collection'; import 'ast.dart'; import 'engine.dart' show AnalysisEngine, AnalysisOptionsImpl; import 'error.dart'; import 'java_core.dart'; import 'java_engine.dart'; import 'scanner.dart'; import 'source.dart'; import 'utilities_collection.dart' show TokenMap; import 'utilities_dart.dart'; Map methodTable_Parser = { '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()), '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)), 'computeStringValue_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._computeStringValue(arg0, arg1, arg2)), 'convertToFunctionDeclaration_1': new MethodTrampoline( 1, (Parser target, arg0) => target._convertToFunctionDeclaration(arg0)), 'couldBeStartOfCompilationUnitMember_0': new MethodTrampoline( 0, (Parser target) => target._couldBeStartOfCompilationUnitMember()), 'createSyntheticIdentifier_0': new MethodTrampoline( 0, (Parser target) => target._createSyntheticIdentifier()), '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)), 'expectSemicolon_0': new MethodTrampoline(0, (Parser target) => target._expectSemicolon()), 'findRange_2': new MethodTrampoline( 2, (Parser target, arg0, 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, arg0) => target._parseCommentReferences(arg0)), 'parseCompilationUnitMember_1': new MethodTrampoline( 1, (Parser target, arg0) => target._parseCompilationUnitMember(arg0)), '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_0': new MethodTrampoline( 0, (Parser target) => target._parseConstructorFieldInitializer()), 'parseContinueStatement_0': new MethodTrampoline( 0, (Parser target) => target._parseContinueStatement()), 'parseDirective_1': new MethodTrampoline( 1, (Parser target, arg0) => target._parseDirective(arg0)), 'parseDirectives_0': new MethodTrampoline(0, (Parser target) => target._parseDirectives()), 'parseDocumentationComment_0': new MethodTrampoline( 0, (Parser target) => target._parseDocumentationComment()), 'parseDoStatement_0': new MethodTrampoline(0, (Parser target) => target._parseDoStatement()), '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_6': new MethodTrampoline(6, (Parser target, arg0, arg1, arg2, arg3, arg4, arg5) => target ._parseMethodDeclarationAfterParameters( arg0, arg1, arg2, arg3, arg4, arg5)), '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._parsePartDirective(arg0)), 'parsePostfixExpression_0': new MethodTrampoline( 0, (Parser target) => target._parsePostfixExpression()), 'parsePrimaryExpression_0': new MethodTrampoline( 0, (Parser target) => target._parsePrimaryExpression()), 'parseRedirectingConstructorInvocation_0': new MethodTrampoline( 0, (Parser target) => target._parseRedirectingConstructorInvocation()), '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)), 'translateCharacter_3': new MethodTrampoline(3, (Parser target, arg0, arg1, arg2) => target._translateCharacter(arg0, arg1, arg2)), 'unlockErrorListener_0': new MethodTrampoline(0, (Parser target) => target._unlockErrorListener()), 'validateFormalParameterList_1': new MethodTrampoline( 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 objects, Token tokenStream) { parser.currentToken = tokenStream; MethodTrampoline method = methodTable_Parser['${methodName}_${objects.length}']; return method.invoke(parser, objects); } /** * A simple data-holder for a method that needs to return multiple values. */ class CommentAndMetadata { /** * The documentation comment that was parsed, or `null` if none was given. */ final Comment comment; /** * The metadata that was parsed. */ final List metadata; /** * Initialize a newly created holder with the given [comment] and [metadata]. */ CommentAndMetadata(this.comment, this.metadata); } /** * A simple data-holder for a method that needs to return multiple values. */ class FinalConstVarOrType { /** * The 'final', 'const' or 'var' keyword, or `null` if none was given. */ final Token keyword; /** * The type, of `null` if no type was specified. */ final TypeName type; /** * Initialize a newly created holder with the given [keyword] and [type]. */ FinalConstVarOrType(this.keyword, this.type); } /** * A dispatcher that will invoke the right parse method when re-parsing a * specified child of the visited node. All of the methods in this class assume * that the parser is positioned to parse the replacement for the node. All of * the methods will throw an [IncrementalParseException] if the node could not * be parsed for some reason. */ class IncrementalParseDispatcher implements AstVisitor { /** * The parser used to parse the replacement for the node. */ final Parser _parser; /** * The node that is to be replaced. */ final AstNode _oldNode; /** * Initialize a newly created dispatcher to parse a single node that will * use the [_parser] to replace the [_oldNode]. */ IncrementalParseDispatcher(this._parser, this._oldNode); @override AstNode visitAdjacentStrings(AdjacentStrings node) { if (node.strings.contains(_oldNode)) { return _parser.parseStringLiteral(); } return _notAChild(node); } @override AstNode visitAnnotation(Annotation node) { if (identical(_oldNode, node.name)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.constructorName)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.arguments)) { return _parser.parseArgumentList(); } return _notAChild(node); } @override AstNode visitArgumentList(ArgumentList node) { if (node.arguments.contains(_oldNode)) { return _parser.parseArgument(); } return _notAChild(node); } @override AstNode visitAsExpression(AsExpression node) { if (identical(_oldNode, node.expression)) { return _parser.parseBitwiseOrExpression(); } else if (identical(_oldNode, node.type)) { return _parser.parseTypeName(); } return _notAChild(node); } @override AstNode visitAssertStatement(AssertStatement node) { if (identical(_oldNode, node.condition)) { return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitAssignmentExpression(AssignmentExpression node) { if (identical(_oldNode, node.leftHandSide)) { // TODO(brianwilkerson) If the assignment is part of a cascade section, // then we don't have a single parse method that will work. // Otherwise, we can parse a conditional expression, but need to ensure // that the resulting expression is assignable. // return parser.parseConditionalExpression(); throw new InsufficientContextException(); } else if (identical(_oldNode, node.rightHandSide)) { if (_isCascadeAllowedInAssignment(node)) { return _parser.parseExpression2(); } return _parser.parseExpressionWithoutCascade(); } return _notAChild(node); } @override AstNode visitAwaitExpression(AwaitExpression node) { if (identical(_oldNode, node.expression)) { // TODO(brianwilkerson) Depending on precedence, // this might not be sufficient. return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitBinaryExpression(BinaryExpression node) { if (identical(_oldNode, node.leftOperand)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.rightOperand)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitBlock(Block node) { if (node.statements.contains(_oldNode)) { return _parser.parseStatement2(); } return _notAChild(node); } @override AstNode visitBlockFunctionBody(BlockFunctionBody node) { if (identical(_oldNode, node.block)) { return _parser.parseBlock(); } return _notAChild(node); } @override AstNode visitBooleanLiteral(BooleanLiteral node) => _notAChild(node); @override AstNode visitBreakStatement(BreakStatement node) { if (identical(_oldNode, node.label)) { return _parser.parseSimpleIdentifier(); } return _notAChild(node); } @override AstNode visitCascadeExpression(CascadeExpression node) { if (identical(_oldNode, node.target)) { return _parser.parseConditionalExpression(); } else if (node.cascadeSections.contains(_oldNode)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitCatchClause(CatchClause node) { if (identical(_oldNode, node.exceptionType)) { return _parser.parseTypeName(); } else if (identical(_oldNode, node.exceptionParameter)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.stackTraceParameter)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.body)) { return _parser.parseBlock(); } return _notAChild(node); } @override AstNode visitClassDeclaration(ClassDeclaration node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.name)) { // Changing the class name changes whether a member is interpreted as a // constructor or not, so we'll just have to re-parse the entire class. throw new InsufficientContextException(); } else if (identical(_oldNode, node.typeParameters)) { return _parser.parseTypeParameterList(); } else if (identical(_oldNode, node.extendsClause)) { return _parser.parseExtendsClause(); } else if (identical(_oldNode, node.withClause)) { return _parser.parseWithClause(); } else if (identical(_oldNode, node.implementsClause)) { return _parser.parseImplementsClause(); } else if (node.members.contains(_oldNode)) { ClassMember member = _parser.parseClassMember(node.name.name); if (member == null) { throw new InsufficientContextException(); } return member; } return _notAChild(node); } @override AstNode visitClassTypeAlias(ClassTypeAlias node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.name)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.typeParameters)) { return _parser.parseTypeParameterList(); } else if (identical(_oldNode, node.superclass)) { return _parser.parseTypeName(); } else if (identical(_oldNode, node.withClause)) { return _parser.parseWithClause(); } else if (identical(_oldNode, node.implementsClause)) { return _parser.parseImplementsClause(); } return _notAChild(node); } @override AstNode visitComment(Comment node) { throw new InsufficientContextException(); } @override AstNode visitCommentReference(CommentReference node) { if (identical(_oldNode, node.identifier)) { return _parser.parsePrefixedIdentifier(); } return _notAChild(node); } @override AstNode visitCompilationUnit(CompilationUnit node) { throw new InsufficientContextException(); } @override AstNode visitConditionalExpression(ConditionalExpression node) { if (identical(_oldNode, node.condition)) { return _parser.parseIfNullExpression(); } else if (identical(_oldNode, node.thenExpression)) { return _parser.parseExpressionWithoutCascade(); } else if (identical(_oldNode, node.elseExpression)) { return _parser.parseExpressionWithoutCascade(); } return _notAChild(node); } @override AstNode visitConstructorDeclaration(ConstructorDeclaration node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.returnType)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.name)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.parameters)) { return _parser.parseFormalParameterList(); } else if (identical(_oldNode, node.redirectedConstructor)) { throw new InsufficientContextException(); } else if (node.initializers.contains(_oldNode)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.body)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitConstructorFieldInitializer(ConstructorFieldInitializer node) { if (identical(_oldNode, node.fieldName)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.expression)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitConstructorName(ConstructorName node) { if (identical(_oldNode, node.type)) { return _parser.parseTypeName(); } else if (identical(_oldNode, node.name)) { return _parser.parseSimpleIdentifier(); } return _notAChild(node); } @override AstNode visitContinueStatement(ContinueStatement node) { if (identical(_oldNode, node.label)) { return _parser.parseSimpleIdentifier(); } return _notAChild(node); } @override AstNode visitDeclaredIdentifier(DeclaredIdentifier node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.type)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.identifier)) { return _parser.parseSimpleIdentifier(); } return _notAChild(node); } @override AstNode visitDefaultFormalParameter(DefaultFormalParameter node) { if (identical(_oldNode, node.parameter)) { return _parser.parseNormalFormalParameter(); } else if (identical(_oldNode, node.defaultValue)) { return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitDoStatement(DoStatement node) { if (identical(_oldNode, node.body)) { return _parser.parseStatement2(); } else if (identical(_oldNode, node.condition)) { return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitDoubleLiteral(DoubleLiteral node) => _notAChild(node); @override AstNode visitEmptyFunctionBody(EmptyFunctionBody node) => _notAChild(node); @override AstNode visitEmptyStatement(EmptyStatement node) => _notAChild(node); @override AstNode visitEnumConstantDeclaration(EnumConstantDeclaration node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.name)) { return _parser.parseSimpleIdentifier(); } return _notAChild(node); } @override AstNode visitEnumDeclaration(EnumDeclaration node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.name)) { return _parser.parseSimpleIdentifier(); } else if (node.constants.contains(_oldNode)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitExportDirective(ExportDirective node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.uri)) { return _parser.parseStringLiteral(); } else if (node.combinators.contains(_oldNode)) { throw new IncrementalParseException(); //return parser.parseCombinator(); } return _notAChild(node); } @override AstNode visitExpressionFunctionBody(ExpressionFunctionBody node) { if (identical(_oldNode, node.expression)) { return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitExpressionStatement(ExpressionStatement node) { if (identical(_oldNode, node.expression)) { return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitExtendsClause(ExtendsClause node) { if (identical(_oldNode, node.superclass)) { return _parser.parseTypeName(); } return _notAChild(node); } @override AstNode visitFieldDeclaration(FieldDeclaration node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.fields)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitFieldFormalParameter(FieldFormalParameter node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.type)) { return _parser.parseTypeName(); } else if (identical(_oldNode, node.identifier)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.parameters)) { return _parser.parseFormalParameterList(); } return _notAChild(node); } @override AstNode visitForEachStatement(ForEachStatement node) { if (identical(_oldNode, node.loopVariable)) { throw new InsufficientContextException(); //return parser.parseDeclaredIdentifier(); } else if (identical(_oldNode, node.identifier)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.body)) { return _parser.parseStatement2(); } return _notAChild(node); } @override AstNode visitFormalParameterList(FormalParameterList node) { // We don't know which kind of parameter to parse. throw new InsufficientContextException(); } @override AstNode visitForStatement(ForStatement node) { if (identical(_oldNode, node.variables)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.initialization)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.condition)) { return _parser.parseExpression2(); } else if (node.updaters.contains(_oldNode)) { return _parser.parseExpression2(); } else if (identical(_oldNode, node.body)) { return _parser.parseStatement2(); } return _notAChild(node); } @override AstNode visitFunctionDeclaration(FunctionDeclaration node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.returnType)) { return _parser.parseReturnType(); } else if (identical(_oldNode, node.name)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.functionExpression)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitFunctionDeclarationStatement(FunctionDeclarationStatement node) { if (identical(_oldNode, node.functionDeclaration)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitFunctionExpression(FunctionExpression node) { if (identical(_oldNode, node.parameters)) { return _parser.parseFormalParameterList(); } else if (identical(_oldNode, node.body)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { if (identical(_oldNode, node.function)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.argumentList)) { return _parser.parseArgumentList(); } return _notAChild(node); } @override AstNode visitFunctionTypeAlias(FunctionTypeAlias node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.returnType)) { return _parser.parseReturnType(); } else if (identical(_oldNode, node.name)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.typeParameters)) { return _parser.parseTypeParameterList(); } else if (identical(_oldNode, node.parameters)) { return _parser.parseFormalParameterList(); } return _notAChild(node); } @override AstNode visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.returnType)) { return _parser.parseReturnType(); } else if (identical(_oldNode, node.identifier)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.parameters)) { return _parser.parseFormalParameterList(); } return _notAChild(node); } @override AstNode visitHideCombinator(HideCombinator node) { if (node.hiddenNames.contains(_oldNode)) { return _parser.parseSimpleIdentifier(); } return _notAChild(node); } @override AstNode visitIfStatement(IfStatement node) { if (identical(_oldNode, node.condition)) { return _parser.parseExpression2(); } else if (identical(_oldNode, node.thenStatement)) { return _parser.parseStatement2(); } else if (identical(_oldNode, node.elseStatement)) { return _parser.parseStatement2(); } return _notAChild(node); } @override AstNode visitImplementsClause(ImplementsClause node) { if (node.interfaces.contains(node)) { return _parser.parseTypeName(); } return _notAChild(node); } @override AstNode visitImportDirective(ImportDirective node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.uri)) { return _parser.parseStringLiteral(); } else if (identical(_oldNode, node.prefix)) { return _parser.parseSimpleIdentifier(); } else if (node.combinators.contains(_oldNode)) { return _parser.parseCombinator(); } return _notAChild(node); } @override AstNode visitIndexExpression(IndexExpression node) { if (identical(_oldNode, node.target)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.index)) { return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitInstanceCreationExpression(InstanceCreationExpression node) { if (identical(_oldNode, node.constructorName)) { return _parser.parseConstructorName(); } else if (identical(_oldNode, node.argumentList)) { return _parser.parseArgumentList(); } return _notAChild(node); } @override AstNode visitIntegerLiteral(IntegerLiteral node) => _notAChild(node); @override AstNode visitInterpolationExpression(InterpolationExpression node) { if (identical(_oldNode, node.expression)) { if (node.leftBracket == null) { throw new InsufficientContextException(); //return parser.parseThisOrSimpleIdentifier(); } return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitInterpolationString(InterpolationString node) { throw new InsufficientContextException(); } @override AstNode visitIsExpression(IsExpression node) { if (identical(_oldNode, node.expression)) { return _parser.parseBitwiseOrExpression(); } else if (identical(_oldNode, node.type)) { return _parser.parseTypeName(); } return _notAChild(node); } @override AstNode visitLabel(Label node) { if (identical(_oldNode, node.label)) { return _parser.parseSimpleIdentifier(); } return _notAChild(node); } @override AstNode visitLabeledStatement(LabeledStatement node) { if (node.labels.contains(_oldNode)) { return _parser.parseLabel(); } else if (identical(_oldNode, node.statement)) { return _parser.parseStatement2(); } return _notAChild(node); } @override AstNode visitLibraryDirective(LibraryDirective node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.name)) { return _parser.parseLibraryIdentifier(); } return _notAChild(node); } @override AstNode visitLibraryIdentifier(LibraryIdentifier node) { if (node.components.contains(_oldNode)) { return _parser.parseSimpleIdentifier(); } return _notAChild(node); } @override AstNode visitListLiteral(ListLiteral node) { if (identical(_oldNode, node.typeArguments)) { return _parser.parseTypeArgumentList(); } else if (node.elements.contains(_oldNode)) { return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitMapLiteral(MapLiteral node) { if (identical(_oldNode, node.typeArguments)) { return _parser.parseTypeArgumentList(); } else if (node.entries.contains(_oldNode)) { return _parser.parseMapLiteralEntry(); } return _notAChild(node); } @override AstNode visitMapLiteralEntry(MapLiteralEntry node) { if (identical(_oldNode, node.key)) { return _parser.parseExpression2(); } else if (identical(_oldNode, node.value)) { return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitMethodDeclaration(MethodDeclaration node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.returnType)) { throw new InsufficientContextException(); //return parser.parseTypeName(); //return parser.parseReturnType(); } else if (identical(_oldNode, node.name)) { if (node.operatorKeyword != null) { throw new InsufficientContextException(); } return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.body)) { //return parser.parseFunctionBody(); throw new InsufficientContextException(); } else if (identical(_oldNode, node.parameters)) { // TODO(paulberry): if we want errors to be correct, we'll need to also // call _validateFormalParameterList, and sometimes // _validateModifiersForGetterOrSetterOrMethod. return _parser.parseFormalParameterList(); } return _notAChild(node); } @override AstNode visitMethodInvocation(MethodInvocation node) { if (identical(_oldNode, node.target)) { throw new IncrementalParseException(); } else if (identical(_oldNode, node.methodName)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.argumentList)) { return _parser.parseArgumentList(); } return _notAChild(node); } @override AstNode visitNamedExpression(NamedExpression node) { if (identical(_oldNode, node.name)) { return _parser.parseLabel(); } else if (identical(_oldNode, node.expression)) { return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitNativeClause(NativeClause node) { if (identical(_oldNode, node.name)) { return _parser.parseStringLiteral(); } return _notAChild(node); } @override AstNode visitNativeFunctionBody(NativeFunctionBody node) { if (identical(_oldNode, node.stringLiteral)) { return _parser.parseStringLiteral(); } return _notAChild(node); } @override AstNode visitNullLiteral(NullLiteral node) => _notAChild(node); @override AstNode visitParenthesizedExpression(ParenthesizedExpression node) { if (identical(_oldNode, node.expression)) { return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitPartDirective(PartDirective node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.uri)) { return _parser.parseStringLiteral(); } return _notAChild(node); } @override AstNode visitPartOfDirective(PartOfDirective node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.libraryName)) { return _parser.parseLibraryIdentifier(); } return _notAChild(node); } @override AstNode visitPostfixExpression(PostfixExpression node) { if (identical(_oldNode, node.operand)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitPrefixedIdentifier(PrefixedIdentifier node) { if (identical(_oldNode, node.prefix)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.identifier)) { return _parser.parseSimpleIdentifier(); } return _notAChild(node); } @override AstNode visitPrefixExpression(PrefixExpression node) { if (identical(_oldNode, node.operand)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitPropertyAccess(PropertyAccess node) { if (identical(_oldNode, node.target)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.propertyName)) { return _parser.parseSimpleIdentifier(); } return _notAChild(node); } @override AstNode visitRedirectingConstructorInvocation( RedirectingConstructorInvocation node) { if (identical(_oldNode, node.constructorName)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.argumentList)) { return _parser.parseArgumentList(); } return _notAChild(node); } @override AstNode visitRethrowExpression(RethrowExpression node) => _notAChild(node); @override AstNode visitReturnStatement(ReturnStatement node) { if (identical(_oldNode, node.expression)) { return _parser.parseExpression2(); } return _notAChild(node); } @override AstNode visitScriptTag(ScriptTag node) => _notAChild(node); @override AstNode visitShowCombinator(ShowCombinator node) { if (node.shownNames.contains(_oldNode)) { return _parser.parseSimpleIdentifier(); } return _notAChild(node); } @override AstNode visitSimpleFormalParameter(SimpleFormalParameter node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.type)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.identifier)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitSimpleIdentifier(SimpleIdentifier node) => _notAChild(node); @override AstNode visitSimpleStringLiteral(SimpleStringLiteral node) => _notAChild(node); @override AstNode visitStringInterpolation(StringInterpolation node) { if (node.elements.contains(_oldNode)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitSuperConstructorInvocation(SuperConstructorInvocation node) { if (identical(_oldNode, node.constructorName)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.argumentList)) { return _parser.parseArgumentList(); } return _notAChild(node); } @override AstNode visitSuperExpression(SuperExpression node) => _notAChild(node); @override AstNode visitSwitchCase(SwitchCase node) { if (node.labels.contains(_oldNode)) { return _parser.parseLabel(); } else if (identical(_oldNode, node.expression)) { return _parser.parseExpression2(); } else if (node.statements.contains(_oldNode)) { return _parser.parseStatement2(); } return _notAChild(node); } @override AstNode visitSwitchDefault(SwitchDefault node) { if (node.labels.contains(_oldNode)) { return _parser.parseLabel(); } else if (node.statements.contains(_oldNode)) { return _parser.parseStatement2(); } return _notAChild(node); } @override AstNode visitSwitchStatement(SwitchStatement node) { if (identical(_oldNode, node.expression)) { return _parser.parseExpression2(); } else if (node.members.contains(_oldNode)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitSymbolLiteral(SymbolLiteral node) => _notAChild(node); @override AstNode visitThisExpression(ThisExpression node) => _notAChild(node); @override AstNode visitThrowExpression(ThrowExpression node) { if (identical(_oldNode, node.expression)) { if (_isCascadeAllowedInThrow(node)) { return _parser.parseExpression2(); } return _parser.parseExpressionWithoutCascade(); } return _notAChild(node); } @override AstNode visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.variables)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitTryStatement(TryStatement node) { if (identical(_oldNode, node.body)) { return _parser.parseBlock(); } else if (node.catchClauses.contains(_oldNode)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.finallyBlock)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitTypeArgumentList(TypeArgumentList node) { if (node.arguments.contains(_oldNode)) { return _parser.parseTypeName(); } return _notAChild(node); } @override AstNode visitTypeName(TypeName node) { if (identical(_oldNode, node.name)) { return _parser.parsePrefixedIdentifier(); } else if (identical(_oldNode, node.typeArguments)) { return _parser.parseTypeArgumentList(); } return _notAChild(node); } @override AstNode visitTypeParameter(TypeParameter node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.name)) { return _parser.parseSimpleIdentifier(); } else if (identical(_oldNode, node.bound)) { return _parser.parseTypeName(); } return _notAChild(node); } @override AstNode visitTypeParameterList(TypeParameterList node) { if (node.typeParameters.contains(node)) { return _parser.parseTypeParameter(); } return _notAChild(node); } @override AstNode visitVariableDeclaration(VariableDeclaration node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.name)) { throw new InsufficientContextException(); } else if (identical(_oldNode, node.initializer)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitVariableDeclarationList(VariableDeclarationList node) { if (identical(_oldNode, node.documentationComment)) { throw new InsufficientContextException(); } else if (node.metadata.contains(_oldNode)) { return _parser.parseAnnotation(); } else if (identical(_oldNode, node.type)) { // There is not enough context to know whether we should reparse the type // using parseReturnType() (which allows 'void') or parseTypeName() // (which doesn't). Note that even though the language disallows // variables of type 'void', the parser sometimes accepts them in the // course of error recovery (e.g. "class C { void v; }" throw new InsufficientContextException(); } else if (node.variables.contains(_oldNode)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitVariableDeclarationStatement(VariableDeclarationStatement node) { if (identical(_oldNode, node.variables)) { throw new InsufficientContextException(); } return _notAChild(node); } @override AstNode visitWhileStatement(WhileStatement node) { if (identical(_oldNode, node.condition)) { return _parser.parseExpression2(); } else if (identical(_oldNode, node.body)) { return _parser.parseStatement2(); } return _notAChild(node); } @override AstNode visitWithClause(WithClause node) { if (node.mixinTypes.contains(node)) { return _parser.parseTypeName(); } return _notAChild(node); } @override AstNode visitYieldStatement(YieldStatement node) { if (identical(_oldNode, node.expression)) { return _parser.parseExpression2(); } return _notAChild(node); } /** * Return `true` if the given assignment [expression] can have a cascade * expression on the right-hand side. */ bool _isCascadeAllowedInAssignment(AssignmentExpression expression) { // TODO(brianwilkerson) Implement this method. throw new InsufficientContextException(); } /** * Return `true` if the given throw [expression] can have a cascade * expression. */ bool _isCascadeAllowedInThrow(ThrowExpression expression) { // TODO(brianwilkerson) Implement this method. throw new InsufficientContextException(); } /** * Throw an exception indicating that the visited [node] was not the parent of * the node to be replaced. */ AstNode _notAChild(AstNode node) { throw new IncrementalParseException.con1( "Internal error: the visited node (a ${node.runtimeType}) was not the parent of the node to be replaced (a ${_oldNode.runtimeType})"); } } /** * An exception that occurred while attempting to parse a replacement for a * specified node in an existing AST structure. */ class IncrementalParseException extends RuntimeException { /** * Initialize a newly created exception to have no message and to be its own * cause. */ IncrementalParseException() : super(); /** * Initialize a newly created exception to have the given [message] and to be * its own cause. */ IncrementalParseException.con1(String message) : super(message: message); /** * Initialize a newly created exception to have no message and to have the * given [cause]. */ IncrementalParseException.con2(Exception cause) : super(cause: cause); } /** * An object used to re-parse a single AST structure within a larger AST * structure. */ class IncrementalParser { /** * The source being parsed. */ final Source _source; /** * A map from old tokens to new tokens used during the cloning process. */ final TokenMap _tokenMap; /** * The error listener that will be informed of any errors that are found * during the parse. */ final AnalysisErrorListener _errorListener; /** * The node in the AST structure that contains the revised content. */ AstNode _updatedNode; /** * Initialize a newly created incremental parser to parse a portion of the * content of the given [_source]. The [_tokenMap] is a map from old tokens to * new tokens that is used during the cloning process. The [_errorListener] * will be informed of any errors that are found during the parse. */ IncrementalParser(this._source, this._tokenMap, this._errorListener); /** * Return the node in the AST structure that contains the revised content. */ AstNode get updatedNode => _updatedNode; /** * Given a range of tokens that were re-scanned, re-parse the minimum number * of tokens to produce a consistent AST structure. The range is represented * by the first and last tokens in the range. * * More specifically, the [leftToken] is the token in the new token stream * immediately to the left of the range of tokens that were inserted and the * [rightToken] is the token in the new token stream immediately to the right * of the range of tokens that were inserted. The [originalStart] and * [originalEnd] are the offsets in the original source of the first and last * characters that were modified. * * The tokens are assumed to be contained in the same token stream. */ AstNode reparse(AstNode originalStructure, Token leftToken, Token rightToken, int originalStart, int originalEnd) { AstNode oldNode = null; AstNode newNode = null; // // Find the first token that needs to be re-parsed. // Token firstToken = leftToken.next; if (identical(firstToken, rightToken)) { // If there are no new tokens, then we need to include at least one copied // node in the range. firstToken = leftToken; } // // Find the smallest AST node that encompasses the range of re-scanned // tokens. // if (originalEnd < originalStart) { oldNode = new NodeLocator.con1(originalStart).searchWithin(originalStructure); } else { oldNode = new NodeLocator.con2(originalStart, originalEnd) .searchWithin(originalStructure); } // // Find the token at which parsing is to begin. // int originalOffset = oldNode.offset; Token parseToken = _findTokenAt(firstToken, originalOffset); if (parseToken == null) { return null; } // // Parse the appropriate AST structure starting at the appropriate place. // Parser parser = new Parser(_source, _errorListener); parser.currentToken = parseToken; while (newNode == null) { AstNode parent = oldNode.parent; if (parent == null) { parseToken = _findFirstToken(parseToken); parser.currentToken = parseToken; return parser.parseCompilationUnit2(); } bool advanceToParent = false; try { IncrementalParseDispatcher dispatcher = new IncrementalParseDispatcher(parser, oldNode); IncrementalParseStateBuilder contextBuilder = new IncrementalParseStateBuilder(parser); contextBuilder.buildState(oldNode); newNode = parent.accept(dispatcher); // // Validate that the new node can replace the old node. // Token mappedToken = _tokenMap.get(oldNode.endToken.next); if (mappedToken == null || newNode == null || mappedToken.offset != newNode.endToken.next.offset || newNode.offset != oldNode.offset) { advanceToParent = true; } } on InsufficientContextException { advanceToParent = true; } catch (exception) { return null; } if (advanceToParent) { newNode = null; oldNode = parent; originalOffset = oldNode.offset; parseToken = _findTokenAt(parseToken, originalOffset); parser.currentToken = parseToken; } } _updatedNode = newNode; // // Replace the old node with the new node in a copy of the original AST // structure. // if (identical(oldNode, originalStructure)) { // We ended up re-parsing the whole structure, so there's no need for a // copy. ResolutionCopier.copyResolutionData(oldNode, newNode); return newNode; } ResolutionCopier.copyResolutionData(oldNode, newNode); IncrementalAstCloner cloner = new IncrementalAstCloner(oldNode, newNode, _tokenMap); return originalStructure.accept(cloner) as AstNode; } /** * Return the first (non-EOF) token in the token stream containing the * [firstToken]. */ Token _findFirstToken(Token firstToken) { while (firstToken.type != TokenType.EOF) { firstToken = firstToken.previous; } return firstToken.next; } /** * Find the token at or before the [firstToken] with the given [offset], or * `null` if there is no such token. */ Token _findTokenAt(Token firstToken, int offset) { while (firstToken.offset > offset && firstToken.type != TokenType.EOF) { firstToken = firstToken.previous; } return firstToken; } } /** * A visitor capable of inferring the correct parser state for incremental * parsing. This visitor visits each parent/child relationship in the chain of * ancestors of the node to be replaced (starting with the root of the parse * tree), updating the parser to the correct state for parsing the child of the * given parent. Once it has visited all of these relationships, the parser * will be in the correct state for reparsing the node to be replaced. */ class IncrementalParseStateBuilder extends SimpleAstVisitor { // TODO(paulberry): add support for other pieces of parser state (_inAsync, // _inGenerator, _inLoop, and _inSwitch). Note that _inLoop and _inSwitch // only affect error message generation. /** * The parser whose state should be built. */ final Parser _parser; /** * The child node in the parent/child relationship currently being visited. * (The corresponding parent is the node passed to the visit...() function.) */ AstNode _childNode; /** * Create an IncrementalParseStateBuilder which will build the correct state * for [_parser]. */ IncrementalParseStateBuilder(this._parser); /** * Build the correct parser state for parsing a replacement for [node]. */ void buildState(AstNode node) { List ancestors = []; while (node != null) { ancestors.add(node); node = node.parent; } _parser._inInitializer = false; for (int i = ancestors.length - 2; i >= 0; i--) { _childNode = ancestors[i]; ancestors[i + 1].accept(this); } } @override void visitArgumentList(ArgumentList node) { _parser._inInitializer = false; } @override void visitConstructorFieldInitializer(ConstructorFieldInitializer node) { if (identical(_childNode, node.expression)) { _parser._inInitializer = true; } } @override void visitIndexExpression(IndexExpression node) { if (identical(_childNode, node.index)) { _parser._inInitializer = false; } } @override void visitInterpolationExpression(InterpolationExpression node) { if (identical(_childNode, node.expression)) { _parser._inInitializer = false; } } @override void visitListLiteral(ListLiteral node) { if (node.elements.contains(_childNode)) { _parser._inInitializer = false; } } @override void visitMapLiteral(MapLiteral node) { if (node.entries.contains(_childNode)) { _parser._inInitializer = false; } } @override void visitParenthesizedExpression(ParenthesizedExpression node) { if (identical(_childNode, node.expression)) { _parser._inInitializer = false; } } } /** * An exception indicating that an AST node cannot be re-parsed because there is * not enough context to know how to re-parse the node. Clients can attempt to * re-parse the parent of the node. */ class InsufficientContextException extends IncrementalParseException { /** * Initialize a newly created exception to have no message and to be its own * cause. */ InsufficientContextException() : super(); /** * Initialize a newly created exception to have the given [message] and to be * its own cause. */ InsufficientContextException.con1(String message) : super.con1(message); /** * Initialize a newly created exception to have no message and to have the * given [cause]. */ InsufficientContextException.con2(Exception cause) : super.con2(cause); } /** * Wrapper around [Function] which should be called with "target" and * "arguments". */ class MethodTrampoline { int parameterCount; Function trampoline; MethodTrampoline(this.parameterCount, this.trampoline); Object invoke(target, List arguments) { if (arguments.length != parameterCount) { throw new IllegalArgumentException( "${arguments.length} != $parameterCount"); } switch (parameterCount) { case 0: return trampoline(target); case 1: return trampoline(target, arguments[0]); case 2: return trampoline(target, arguments[0], arguments[1]); case 3: return trampoline(target, arguments[0], arguments[1], arguments[2]); case 4: return trampoline( target, arguments[0], arguments[1], arguments[2], arguments[3]); default: throw new IllegalArgumentException("Not implemented for > 4 arguments"); } } } /** * A simple data-holder for a method that needs to return multiple values. */ class Modifiers { /** * The token representing the keyword 'abstract', or `null` if the keyword was * not found. */ Token abstractKeyword; /** * The token representing the keyword 'const', or `null` if the keyword was * not found. */ Token constKeyword; /** * The token representing the keyword 'external', or `null` if the keyword was * not found. */ Token externalKeyword; /** * The token representing the keyword 'factory', or `null` if the keyword was * not found. */ Token factoryKeyword; /** * The token representing the keyword 'final', or `null` if the keyword was * not found. */ Token finalKeyword; /** * The token representing the keyword 'static', or `null` if the keyword was * not found. */ Token staticKeyword; /** * The token representing the keyword 'var', or `null` if the keyword was not * found. */ Token varKeyword; @override String toString() { StringBuffer buffer = new StringBuffer(); bool needsSpace = _appendKeyword(buffer, false, abstractKeyword); needsSpace = _appendKeyword(buffer, needsSpace, constKeyword); needsSpace = _appendKeyword(buffer, needsSpace, externalKeyword); needsSpace = _appendKeyword(buffer, needsSpace, factoryKeyword); needsSpace = _appendKeyword(buffer, needsSpace, finalKeyword); needsSpace = _appendKeyword(buffer, needsSpace, staticKeyword); _appendKeyword(buffer, needsSpace, varKeyword); return buffer.toString(); } /** * If the given [keyword] is not `null`, append it to the given [builder], * prefixing it with a space if [needsSpace] is `true`. Return `true` if * subsequent keywords need to be prefixed with a space. */ bool _appendKeyword(StringBuffer buffer, bool needsSpace, Token keyword) { if (keyword != null) { if (needsSpace) { buffer.writeCharCode(0x20); } buffer.write(keyword.lexeme); return true; } return needsSpace; } } /** * A parser used to parse tokens into an AST structure. */ class Parser { static String ASYNC = "async"; static String _AWAIT = "await"; static String _HIDE = "hide"; static String _OF = "of"; static String _ON = "on"; static String _NATIVE = "native"; static String _SHOW = "show"; static String SYNC = "sync"; static String _YIELD = "yield"; /** * The source being parsed. */ final Source _source; /** * The error listener that will be informed of any errors that are found * during the parse. */ final AnalysisErrorListener _errorListener; /** * An [errorListener] lock, if more than `0`, then errors are not reported. */ int _errorListenerLock = 0; /** * A flag indicating whether parser is to parse function bodies. */ bool _parseFunctionBodies = true; /** * The next token to be parsed. */ Token _currentToken; /** * A flag indicating whether the parser is currently in a function body marked * as being 'async'. */ bool _inAsync = false; /** * A flag indicating whether the parser is currently in a function body marked * as being 'async'. */ bool _inGenerator = false; /** * A flag indicating whether the parser is currently in the body of a loop. */ bool _inLoop = false; /** * A flag indicating whether the parser is currently in a switch statement. */ bool _inSwitch = false; /** * A flag indicating whether the parser is currently in a constructor field * initializer, with no intervening parens, braces, or brackets. */ bool _inInitializer = false; /** * Initialize a newly created parser to parse the content of the given * [_source] and to report any errors that are found to the given * [_errorListener]. */ Parser(this._source, this._errorListener); void set currentToken(Token currentToken) { this._currentToken = currentToken; } /** * Return `true` if the current token is the first token of a return type that * is followed by an identifier, possibly followed by a list of type * parameters, followed by a left-parenthesis. This is used by * [_parseTypeAlias] to determine whether or not to parse a return type. */ bool get hasReturnTypeInTypeAlias { Token next = _skipReturnType(_currentToken); if (next == null) { return false; } return _tokenMatchesIdentifier(next); } /** * Set whether the parser is to parse the async support. */ @deprecated void set parseAsync(bool parseAsync) { // Async support cannot be disabled } /** * Set whether the parser is to parse deferred libraries. */ @deprecated void set parseDeferredLibraries(bool parseDeferredLibraries) { // Deferred libraries support cannot be disabled } /** * Set whether the parser is to parse enum declarations. */ @deprecated void set parseEnum(bool parseEnum) { // Enum support cannot be disabled } /** * Set whether parser is to parse function bodies. */ void set parseFunctionBodies(bool parseFunctionBodies) { this._parseFunctionBodies = parseFunctionBodies; } /** * Advance to the next token in the token stream, making it the new current * token and return the token that was current before this method was invoked. */ Token getAndAdvance() { Token token = _currentToken; _advance(); return token; } /** * Parse an annotation. Return the annotation that was parsed. * * annotation ::= * '@' qualified ('.' identifier)? arguments? * */ Annotation parseAnnotation() { Token atSign = _expect(TokenType.AT); Identifier name = parsePrefixedIdentifier(); Token period = null; SimpleIdentifier constructorName = null; if (_matches(TokenType.PERIOD)) { period = getAndAdvance(); constructorName = parseSimpleIdentifier(); } ArgumentList arguments = null; if (_matches(TokenType.OPEN_PAREN)) { arguments = parseArgumentList(); } return new Annotation(atSign, name, period, constructorName, arguments); } /** * Parse an argument. Return the argument that was parsed. * * argument ::= * namedArgument * | expression * * namedArgument ::= * label expression */ Expression parseArgument() { // // Both namedArgument and expression can start with an identifier, but only // namedArgument can have an identifier followed by a colon. // if (_matchesIdentifier() && _tokenMatches(_peek(), TokenType.COLON)) { return new NamedExpression(parseLabel(), parseExpression2()); } else { return parseExpression2(); } } /** * Parse a list of arguments. Return the argument list that was parsed. * * arguments ::= * '(' argumentList? ')' * * argumentList ::= * namedArgument (',' namedArgument)* * | expressionList (',' namedArgument)* */ ArgumentList parseArgumentList() { Token leftParenthesis = _expect(TokenType.OPEN_PAREN); List arguments = new List(); if (_matches(TokenType.CLOSE_PAREN)) { return new ArgumentList(leftParenthesis, arguments, getAndAdvance()); } // // Even though unnamed arguments must all appear before any named arguments, // we allow them to appear in any order so that we can recover faster. // bool wasInInitializer = _inInitializer; _inInitializer = false; try { Expression argument = parseArgument(); arguments.add(argument); bool foundNamedArgument = argument is NamedExpression; bool generatedError = false; while (_optional(TokenType.COMMA)) { argument = parseArgument(); arguments.add(argument); if (foundNamedArgument) { bool blankArgument = argument is SimpleIdentifier && argument.name.isEmpty; if (!generatedError && !(argument is NamedExpression && !blankArgument)) { // Report the error, once, but allow the arguments to be in any // order in the AST. _reportErrorForCurrentToken( ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT); generatedError = true; } } else if (argument is NamedExpression) { foundNamedArgument = true; } } // TODO(brianwilkerson) Recovery: Look at the left parenthesis to see // whether there is a matching right parenthesis. If there is, then we're // more likely missing a comma and should go back to parsing arguments. Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); return new ArgumentList(leftParenthesis, arguments, rightParenthesis); } finally { _inInitializer = wasInInitializer; } } /** * Parse a bitwise or expression. Return the bitwise or expression that was * parsed. * * bitwiseOrExpression ::= * bitwiseXorExpression ('|' bitwiseXorExpression)* * | 'super' ('|' bitwiseXorExpression)+ */ Expression parseBitwiseOrExpression() { Expression expression; if (_matchesKeyword(Keyword.SUPER) && _tokenMatches(_peek(), TokenType.BAR)) { expression = new SuperExpression(getAndAdvance()); } else { expression = _parseBitwiseXorExpression(); } while (_matches(TokenType.BAR)) { Token operator = getAndAdvance(); expression = new BinaryExpression( expression, operator, _parseBitwiseXorExpression()); } return expression; } /** * Parse a block. Return the block that was parsed. * * block ::= * '{' statements '}' */ Block parseBlock() { Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET); List statements = new List(); Token statementStart = _currentToken; while ( !_matches(TokenType.EOF) && !_matches(TokenType.CLOSE_CURLY_BRACKET)) { Statement statement = parseStatement2(); if (statement != null) { statements.add(statement); } if (identical(_currentToken, statementStart)) { // Ensure that we are making progress and report an error if we're not. _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]); _advance(); } statementStart = _currentToken; } Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET); return new Block(leftBracket, statements, rightBracket); } /** * Parse a class member. The [className] is the name of the class containing * the member being parsed. Return the class member that was parsed, or `null` * if what was found was not a valid class member. * * classMemberDefinition ::= * declaration ';' * | methodSignature functionBody */ ClassMember parseClassMember(String className) { CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata(); Modifiers modifiers = _parseModifiers(); if (_matchesKeyword(Keyword.VOID)) { TypeName returnType = parseReturnType(); if (_matchesKeyword(Keyword.GET) && _tokenMatchesIdentifier(_peek())) { _validateModifiersForGetterOrSetterOrMethod(modifiers); return _parseGetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, returnType); } else if (_matchesKeyword(Keyword.SET) && _tokenMatchesIdentifier(_peek())) { _validateModifiersForGetterOrSetterOrMethod(modifiers); return _parseSetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, returnType); } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) { _validateModifiersForOperator(modifiers); return _parseOperator( commentAndMetadata, modifiers.externalKeyword, returnType); } else if (_matchesIdentifier() && _peek().matchesAny([ TokenType.OPEN_PAREN, TokenType.OPEN_CURLY_BRACKET, TokenType.FUNCTION ])) { _validateModifiersForGetterOrSetterOrMethod(modifiers); return _parseMethodDeclarationAfterReturnType(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, returnType); } else { // // We have found an error of some kind. Try to recover. // if (_matchesIdentifier()) { if (_peek().matchesAny( [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) { // // We appear to have a variable declaration with a type of "void". // _reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType); return _parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, _validateModifiersForField(modifiers), returnType); } } if (_isOperator(_currentToken)) { // // We appear to have found an operator declaration without the // 'operator' keyword. // _validateModifiersForOperator(modifiers); return _parseOperator( commentAndMetadata, modifiers.externalKeyword, returnType); } _reportErrorForToken( ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken); return null; } } else if (_matchesKeyword(Keyword.GET) && _tokenMatchesIdentifier(_peek())) { _validateModifiersForGetterOrSetterOrMethod(modifiers); return _parseGetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, null); } else if (_matchesKeyword(Keyword.SET) && _tokenMatchesIdentifier(_peek())) { _validateModifiersForGetterOrSetterOrMethod(modifiers); return _parseSetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, null); } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) { _validateModifiersForOperator(modifiers); return _parseOperator( commentAndMetadata, modifiers.externalKeyword, null); } else if (!_matchesIdentifier()) { // // Recover from an error. // if (_matchesKeyword(Keyword.CLASS)) { _reportErrorForCurrentToken(ParserErrorCode.CLASS_IN_CLASS); // TODO(brianwilkerson) We don't currently have any way to capture the // class that was parsed. _parseClassDeclaration(commentAndMetadata, null); return null; } else if (_matchesKeyword(Keyword.ABSTRACT) && _tokenMatchesKeyword(_peek(), Keyword.CLASS)) { _reportErrorForToken(ParserErrorCode.CLASS_IN_CLASS, _peek()); // TODO(brianwilkerson) We don't currently have any way to capture the // class that was parsed. _parseClassDeclaration(commentAndMetadata, getAndAdvance()); return null; } else if (_matchesKeyword(Keyword.ENUM)) { _reportErrorForToken(ParserErrorCode.ENUM_IN_CLASS, _peek()); // TODO(brianwilkerson) We don't currently have any way to capture the // enum that was parsed. _parseEnumDeclaration(commentAndMetadata); return null; } else if (_isOperator(_currentToken)) { // // We appear to have found an operator declaration without the // 'operator' keyword. // _validateModifiersForOperator(modifiers); return _parseOperator( commentAndMetadata, modifiers.externalKeyword, null); } Token keyword = modifiers.varKeyword; if (keyword == null) { keyword = modifiers.finalKeyword; } if (keyword == null) { keyword = modifiers.constKeyword; } if (keyword != null) { // // We appear to have found an incomplete field declaration. // _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); List variables = new List(); variables.add(new VariableDeclaration( null, null, _createSyntheticIdentifier(), null, null)); return new FieldDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, null, new VariableDeclarationList(null, null, keyword, null, variables), _expectSemicolon()); } _reportErrorForToken( ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken); if (commentAndMetadata.comment != null || !commentAndMetadata.metadata.isEmpty) { // // We appear to have found an incomplete declaration at the end of the // class. At this point it consists of a metadata, which we don't want // to loose, so we'll treat it as a method declaration with a missing // name, parameters and empty body. // return new MethodDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, null, null, null, null, null, _createSyntheticIdentifier(), new FormalParameterList( null, new List(), null, null, null), new EmptyFunctionBody(_createSyntheticToken(TokenType.SEMICOLON))); } return null; } else if (_tokenMatches(_peek(), TokenType.PERIOD) && _tokenMatchesIdentifier(_peekAt(2)) && _tokenMatches(_peekAt(3), TokenType.OPEN_PAREN)) { return _parseConstructor(commentAndMetadata, modifiers.externalKeyword, _validateModifiersForConstructor(modifiers), modifiers.factoryKeyword, parseSimpleIdentifier(), getAndAdvance(), parseSimpleIdentifier(), parseFormalParameterList()); } else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) { SimpleIdentifier methodName = parseSimpleIdentifier(); FormalParameterList parameters = parseFormalParameterList(); if (_matches(TokenType.COLON) || modifiers.factoryKeyword != null || methodName.name == className) { return _parseConstructor(commentAndMetadata, modifiers.externalKeyword, _validateModifiersForConstructor(modifiers), modifiers.factoryKeyword, methodName, null, null, parameters); } _validateModifiersForGetterOrSetterOrMethod(modifiers); _validateFormalParameterList(parameters); return _parseMethodDeclarationAfterParameters(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, null, methodName, parameters); } else if (_peek() .matchesAny([TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) { if (modifiers.constKeyword == null && modifiers.finalKeyword == null && modifiers.varKeyword == null) { _reportErrorForCurrentToken( ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE); } return _parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, _validateModifiersForField(modifiers), null); } else if (_matchesKeyword(Keyword.TYPEDEF)) { _reportErrorForCurrentToken(ParserErrorCode.TYPEDEF_IN_CLASS); // TODO(brianwilkerson) We don't currently have any way to capture the // function type alias that was parsed. _parseFunctionTypeAlias(commentAndMetadata, getAndAdvance()); return null; } TypeName type = parseTypeName(); if (_matchesKeyword(Keyword.GET) && _tokenMatchesIdentifier(_peek())) { _validateModifiersForGetterOrSetterOrMethod(modifiers); return _parseGetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, type); } else if (_matchesKeyword(Keyword.SET) && _tokenMatchesIdentifier(_peek())) { _validateModifiersForGetterOrSetterOrMethod(modifiers); return _parseSetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, type); } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) { _validateModifiersForOperator(modifiers); return _parseOperator( commentAndMetadata, modifiers.externalKeyword, type); } else if (!_matchesIdentifier()) { if (_matches(TokenType.CLOSE_CURLY_BRACKET)) { // // We appear to have found an incomplete declaration at the end of the // 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(commentAndMetadata, modifiers.staticKeyword, _validateModifiersForField(modifiers), type); } if (_isOperator(_currentToken)) { // // We appear to have found an operator declaration without the // 'operator' keyword. // _validateModifiersForOperator(modifiers); return _parseOperator( commentAndMetadata, modifiers.externalKeyword, type); } // // We appear to have found an incomplete declaration before another // declaration. 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. // _reportErrorForToken( ParserErrorCode.EXPECTED_CLASS_MEMBER, _currentToken); try { _lockErrorListener(); return _parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, _validateModifiersForField(modifiers), type); } finally { _unlockErrorListener(); } } else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) { SimpleIdentifier methodName = parseSimpleIdentifier(); FormalParameterList parameters = parseFormalParameterList(); if (methodName.name == className) { _reportErrorForNode(ParserErrorCode.CONSTRUCTOR_WITH_RETURN_TYPE, type); return _parseConstructor(commentAndMetadata, modifiers.externalKeyword, _validateModifiersForConstructor(modifiers), modifiers.factoryKeyword, methodName, null, null, parameters); } _validateModifiersForGetterOrSetterOrMethod(modifiers); _validateFormalParameterList(parameters); return _parseMethodDeclarationAfterParameters(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, type, methodName, parameters); } else if (_tokenMatches(_peek(), TokenType.OPEN_CURLY_BRACKET)) { // We have found "TypeName identifier {", and are guessing that this is a // getter without the keyword 'get'. _validateModifiersForGetterOrSetterOrMethod(modifiers); _reportErrorForCurrentToken(ParserErrorCode.MISSING_GET); _currentToken = _injectToken( new Parser_SyntheticKeywordToken(Keyword.GET, _currentToken.offset)); return _parseGetter(commentAndMetadata, modifiers.externalKeyword, modifiers.staticKeyword, type); } return _parseInitializedIdentifierList(commentAndMetadata, modifiers.staticKeyword, _validateModifiersForField(modifiers), type); } /** * Parse a single combinator. Return the combinator that was parsed, or `null` * if no combinator is found. * * combinator ::= * 'show' identifier (',' identifier)* * | 'hide' identifier (',' identifier)* */ Combinator parseCombinator() { if (_matchesString(_SHOW) || _matchesString(_HIDE)) { Token keyword = getAndAdvance(); List names = _parseIdentifierList(); if (keyword.lexeme == _SHOW) { return new ShowCombinator(keyword, names); } else { return new HideCombinator(keyword, names); } } return null; } /** * Parse a compilation unit, starting with the given [token]. Return the * compilation unit that was parsed. */ CompilationUnit parseCompilationUnit(Token token) { _currentToken = token; return parseCompilationUnit2(); } /** * Parse a compilation unit. Return the compilation unit that was parsed. * * Specified: * * compilationUnit ::= * scriptTag? directive* topLevelDeclaration* * * Actual: * * compilationUnit ::= * scriptTag? topLevelElement* * * topLevelElement ::= * directive * | topLevelDeclaration */ CompilationUnit parseCompilationUnit2() { Token firstToken = _currentToken; ScriptTag scriptTag = null; if (_matches(TokenType.SCRIPT_TAG)) { scriptTag = new ScriptTag(getAndAdvance()); } // // Even though all directives must appear before declarations and must occur // in a given order, we allow directives and declarations to occur in any // order so that we can recover better. // bool libraryDirectiveFound = false; bool partOfDirectiveFound = false; bool partDirectiveFound = false; bool directiveFoundAfterDeclaration = false; List directives = new List(); List declarations = new List(); Token memberStart = _currentToken; while (!_matches(TokenType.EOF)) { CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata(); if ((_matchesKeyword(Keyword.IMPORT) || _matchesKeyword(Keyword.EXPORT) || _matchesKeyword(Keyword.LIBRARY) || _matchesKeyword(Keyword.PART)) && !_tokenMatches(_peek(), TokenType.PERIOD) && !_tokenMatches(_peek(), TokenType.LT) && !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) { Directive directive = _parseDirective(commentAndMetadata); if (declarations.length > 0 && !directiveFoundAfterDeclaration) { _reportErrorForToken(ParserErrorCode.DIRECTIVE_AFTER_DECLARATION, directive.beginToken); directiveFoundAfterDeclaration = true; } if (directive is LibraryDirective) { if (libraryDirectiveFound) { _reportErrorForCurrentToken( ParserErrorCode.MULTIPLE_LIBRARY_DIRECTIVES); } else { if (directives.length > 0) { _reportErrorForToken(ParserErrorCode.LIBRARY_DIRECTIVE_NOT_FIRST, directive.libraryKeyword); } libraryDirectiveFound = true; } } else if (directive is PartDirective) { partDirectiveFound = true; } else if (partDirectiveFound) { if (directive is ExportDirective) { _reportErrorForToken( ParserErrorCode.EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE, directive.keyword); } else if (directive is ImportDirective) { _reportErrorForToken( ParserErrorCode.IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE, directive.keyword); } } if (directive is PartOfDirective) { if (partOfDirectiveFound) { _reportErrorForCurrentToken( ParserErrorCode.MULTIPLE_PART_OF_DIRECTIVES); } else { int directiveCount = directives.length; for (int i = 0; i < directiveCount; i++) { _reportErrorForToken( ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART, directives[i].keyword); } partOfDirectiveFound = true; } } else { if (partOfDirectiveFound) { _reportErrorForToken(ParserErrorCode.NON_PART_OF_DIRECTIVE_IN_PART, directive.keyword); } } directives.add(directive); } else if (_matches(TokenType.SEMICOLON)) { _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]); _advance(); } else { CompilationUnitMember member = _parseCompilationUnitMember(commentAndMetadata); if (member != null) { declarations.add(member); } } if (identical(_currentToken, memberStart)) { _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]); _advance(); while (!_matches(TokenType.EOF) && !_couldBeStartOfCompilationUnitMember()) { _advance(); } } memberStart = _currentToken; } return new CompilationUnit( firstToken, scriptTag, directives, declarations, _currentToken); } /** * Parse a conditional expression. Return the conditional expression that was * parsed. * * conditionalExpression ::= * ifNullExpression ('?' expressionWithoutCascade ':' expressionWithoutCascade)? */ Expression parseConditionalExpression() { Expression condition = parseIfNullExpression(); if (!_matches(TokenType.QUESTION)) { return condition; } Token question = getAndAdvance(); Expression thenExpression = parseExpressionWithoutCascade(); Token colon = _expect(TokenType.COLON); Expression elseExpression = parseExpressionWithoutCascade(); return new ConditionalExpression( condition, question, thenExpression, colon, elseExpression); } /** * Parse the name of a constructor. Return the constructor name that was * parsed. * * constructorName: * type ('.' identifier)? */ ConstructorName parseConstructorName() { TypeName type = parseTypeName(); Token period = null; SimpleIdentifier name = null; if (_matches(TokenType.PERIOD)) { period = getAndAdvance(); name = parseSimpleIdentifier(); } return new ConstructorName(type, period, name); } /** * 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 * there are directives later in the file, they will not be parsed. Return the * compilation unit that was parsed. */ CompilationUnit parseDirectives(Token token) { _currentToken = token; return _parseDirectives(); } /** * Parse an expression, starting with the given [token]. Return the expression * that was parsed, or `null` if the tokens do not represent a recognizable * expression. */ Expression parseExpression(Token token) { _currentToken = token; return parseExpression2(); } /** * Parse an expression that might contain a cascade. Return the expression * that was parsed. * * expression ::= * assignableExpression assignmentOperator expression * | conditionalExpression cascadeSection* * | throwExpression */ Expression parseExpression2() { if (_matchesKeyword(Keyword.THROW)) { return _parseThrowExpression(); } else if (_matchesKeyword(Keyword.RETHROW)) { // TODO(brianwilkerson) Rethrow is a statement again. return _parseRethrowExpression(); } // // assignableExpression is a subset of conditionalExpression, so we can // parse a conditional expression and then determine whether it is followed // by an assignmentOperator, checking for conformance to the restricted // grammar after making that determination. // Expression expression = parseConditionalExpression(); TokenType tokenType = _currentToken.type; if (tokenType == TokenType.PERIOD_PERIOD) { List cascadeSections = new List(); while (tokenType == TokenType.PERIOD_PERIOD) { Expression section = _parseCascadeSection(); if (section != null) { cascadeSections.add(section); } tokenType = _currentToken.type; } return new CascadeExpression(expression, cascadeSections); } else if (tokenType.isAssignmentOperator) { Token operator = getAndAdvance(); _ensureAssignable(expression); return new AssignmentExpression(expression, operator, parseExpression2()); } return expression; } /** * Parse an expression that does not contain any cascades. Return the * expression that was parsed. * * expressionWithoutCascade ::= * assignableExpression assignmentOperator expressionWithoutCascade * | conditionalExpression * | throwExpressionWithoutCascade */ Expression parseExpressionWithoutCascade() { if (_matchesKeyword(Keyword.THROW)) { return _parseThrowExpressionWithoutCascade(); } else if (_matchesKeyword(Keyword.RETHROW)) { return _parseRethrowExpression(); } // // assignableExpression is a subset of conditionalExpression, so we can // parse a conditional expression and then determine whether it is followed // by an assignmentOperator, checking for conformance to the restricted // grammar after making that determination. // Expression expression = parseConditionalExpression(); if (_currentToken.type.isAssignmentOperator) { Token operator = getAndAdvance(); _ensureAssignable(expression); expression = new AssignmentExpression( expression, operator, parseExpressionWithoutCascade()); } return expression; } /** * Parse a class extends clause. Return the class extends clause that was * parsed. * * classExtendsClause ::= * 'extends' type */ ExtendsClause parseExtendsClause() { Token keyword = _expectKeyword(Keyword.EXTENDS); TypeName superclass = parseTypeName(); return new ExtendsClause(keyword, superclass); } /** * Parse a list of formal parameters. Return the formal parameters that were * parsed. * * formalParameterList ::= * '(' ')' * | '(' normalFormalParameters (',' optionalFormalParameters)? ')' * | '(' optionalFormalParameters ')' * * normalFormalParameters ::= * normalFormalParameter (',' normalFormalParameter)* * * optionalFormalParameters ::= * optionalPositionalFormalParameters * | namedFormalParameters * * optionalPositionalFormalParameters ::= * '[' defaultFormalParameter (',' defaultFormalParameter)* ']' * * namedFormalParameters ::= * '{' defaultNamedParameter (',' defaultNamedParameter)* '}' */ FormalParameterList parseFormalParameterList() { Token leftParenthesis = _expect(TokenType.OPEN_PAREN); if (_matches(TokenType.CLOSE_PAREN)) { return new FormalParameterList( leftParenthesis, null, null, null, getAndAdvance()); } // // Even though it is invalid to have default parameters outside of brackets, // required parameters inside of brackets, or multiple groups of default and // named parameters, we allow all of these cases so that we can recover // better. // List parameters = new List(); List normalParameters = new List(); List positionalParameters = new List(); List namedParameters = new List(); List currentParameters = normalParameters; Token leftSquareBracket = null; Token rightSquareBracket = null; Token leftCurlyBracket = null; Token rightCurlyBracket = null; ParameterKind kind = ParameterKind.REQUIRED; bool firstParameter = true; bool reportedMuliplePositionalGroups = false; bool reportedMulipleNamedGroups = false; bool reportedMixedGroups = false; bool wasOptionalParameter = false; Token initialToken = null; do { if (firstParameter) { firstParameter = false; } else if (!_optional(TokenType.COMMA)) { // TODO(brianwilkerson) The token is wrong, we need to recover from this // case. if (_getEndToken(leftParenthesis) != null) { _reportErrorForCurrentToken( ParserErrorCode.EXPECTED_TOKEN, [TokenType.COMMA.lexeme]); } else { _reportErrorForToken(ParserErrorCode.MISSING_CLOSING_PARENTHESIS, _currentToken.previous); break; } } initialToken = _currentToken; // // Handle the beginning of parameter groups. // if (_matches(TokenType.OPEN_SQUARE_BRACKET)) { wasOptionalParameter = true; if (leftSquareBracket != null && !reportedMuliplePositionalGroups) { _reportErrorForCurrentToken( ParserErrorCode.MULTIPLE_POSITIONAL_PARAMETER_GROUPS); reportedMuliplePositionalGroups = true; } if (leftCurlyBracket != null && !reportedMixedGroups) { _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS); reportedMixedGroups = true; } leftSquareBracket = getAndAdvance(); currentParameters = positionalParameters; kind = ParameterKind.POSITIONAL; } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) { wasOptionalParameter = true; if (leftCurlyBracket != null && !reportedMulipleNamedGroups) { _reportErrorForCurrentToken( ParserErrorCode.MULTIPLE_NAMED_PARAMETER_GROUPS); reportedMulipleNamedGroups = true; } if (leftSquareBracket != null && !reportedMixedGroups) { _reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS); reportedMixedGroups = true; } leftCurlyBracket = getAndAdvance(); currentParameters = namedParameters; kind = ParameterKind.NAMED; } // // Parse and record the parameter. // FormalParameter parameter = _parseFormalParameter(kind); parameters.add(parameter); currentParameters.add(parameter); if (kind == ParameterKind.REQUIRED && wasOptionalParameter) { _reportErrorForNode( ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS, parameter); } // // Handle the end of parameter groups. // // TODO(brianwilkerson) Improve the detection and reporting of missing and // mismatched delimiters. if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) { rightSquareBracket = getAndAdvance(); currentParameters = normalParameters; if (leftSquareBracket == null) { if (leftCurlyBracket != null) { _reportErrorForCurrentToken( ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]); rightCurlyBracket = rightSquareBracket; rightSquareBracket = null; } else { _reportErrorForCurrentToken( ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP, ["["]); } } kind = ParameterKind.REQUIRED; } else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) { rightCurlyBracket = getAndAdvance(); currentParameters = normalParameters; if (leftCurlyBracket == null) { if (leftSquareBracket != null) { _reportErrorForCurrentToken( ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]); rightSquareBracket = rightCurlyBracket; rightCurlyBracket = null; } else { _reportErrorForCurrentToken( ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP, ["{"]); } } kind = ParameterKind.REQUIRED; } } while (!_matches(TokenType.CLOSE_PAREN) && !identical(initialToken, _currentToken)); Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); // // Check that the groups were closed correctly. // if (leftSquareBracket != null && rightSquareBracket == null) { _reportErrorForCurrentToken( ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["]"]); } if (leftCurlyBracket != null && rightCurlyBracket == null) { _reportErrorForCurrentToken( ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, ["}"]); } // // Build the parameter list. // if (leftSquareBracket == null) { leftSquareBracket = leftCurlyBracket; } if (rightSquareBracket == null) { rightSquareBracket = rightCurlyBracket; } return new FormalParameterList(leftParenthesis, parameters, leftSquareBracket, rightSquareBracket, rightParenthesis); } /** * Parse a function expression. Return the function expression that was * parsed. * * functionExpression ::= * formalParameterList functionExpressionBody */ FunctionExpression parseFunctionExpression() { FormalParameterList parameters = parseFormalParameterList(); _validateFormalParameterList(parameters); FunctionBody body = _parseFunctionBody(false, ParserErrorCode.MISSING_FUNCTION_BODY, true); return new FunctionExpression(parameters, body); } /** * Parse an if-null expression. Return the if-null expression that was * parsed. * * ifNullExpression ::= logicalOrExpression ('??' logicalOrExpression)* */ Expression parseIfNullExpression() { Expression expression = parseLogicalOrExpression(); while (_matches(TokenType.QUESTION_QUESTION)) { Token operator = getAndAdvance(); expression = new BinaryExpression( expression, operator, parseLogicalOrExpression()); } return expression; } /** * Parse an implements clause. Return the implements clause that was parsed. * * implementsClause ::= * 'implements' type (',' type)* */ ImplementsClause parseImplementsClause() { Token keyword = _expectKeyword(Keyword.IMPLEMENTS); List interfaces = new List(); interfaces.add(parseTypeName()); while (_optional(TokenType.COMMA)) { interfaces.add(parseTypeName()); } return new ImplementsClause(keyword, interfaces); } /** * Parse a label. Return the label that was parsed. * * label ::= * identifier ':' */ Label parseLabel() { SimpleIdentifier label = parseSimpleIdentifier(); Token colon = _expect(TokenType.COLON); return new Label(label, colon); } /** * Parse a library identifier. Return the library identifier that was parsed. * * libraryIdentifier ::= * identifier ('.' identifier)* */ LibraryIdentifier parseLibraryIdentifier() { List components = new List(); components.add(parseSimpleIdentifier()); while (_matches(TokenType.PERIOD)) { _advance(); components.add(parseSimpleIdentifier()); } return new LibraryIdentifier(components); } /** * Parse a logical or expression. Return the logical or expression that was * parsed. * * logicalOrExpression ::= * logicalAndExpression ('||' logicalAndExpression)* */ Expression parseLogicalOrExpression() { Expression expression = _parseLogicalAndExpression(); while (_matches(TokenType.BAR_BAR)) { Token operator = getAndAdvance(); expression = new BinaryExpression( expression, operator, _parseLogicalAndExpression()); } return expression; } /** * Parse a map literal entry. Return the map literal entry that was parsed. * * mapLiteralEntry ::= * expression ':' expression */ MapLiteralEntry parseMapLiteralEntry() { Expression key = parseExpression2(); Token separator = _expect(TokenType.COLON); Expression value = parseExpression2(); return new MapLiteralEntry(key, separator, value); } /** * Parse a normal formal parameter. Return the normal formal parameter that * was parsed. * * normalFormalParameter ::= * functionSignature * | fieldFormalParameter * | simpleFormalParameter * * functionSignature: * metadata returnType? identifier 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(); if (_matches(TokenType.OPEN_PAREN)) { FormalParameterList parameters = parseFormalParameterList(); if (thisKeyword == null) { if (holder.keyword != null) { _reportErrorForToken( ParserErrorCode.FUNCTION_TYPED_PARAMETER_VAR, holder.keyword); } return new FunctionTypedFormalParameter(commentAndMetadata.comment, commentAndMetadata.metadata, holder.type, identifier, parameters); } else { return new FieldFormalParameter(commentAndMetadata.comment, commentAndMetadata.metadata, holder.keyword, holder.type, thisKeyword, period, identifier, parameters); } } 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) { return new FieldFormalParameter(commentAndMetadata.comment, commentAndMetadata.metadata, holder.keyword, holder.type, thisKeyword, period, identifier, null); } return new SimpleFormalParameter(commentAndMetadata.comment, commentAndMetadata.metadata, holder.keyword, holder.type, identifier); } /** * Parse a prefixed identifier. Return the prefixed identifier that was * parsed. * * prefixedIdentifier ::= * identifier ('.' identifier)? */ Identifier parsePrefixedIdentifier() { SimpleIdentifier qualifier = parseSimpleIdentifier(); if (!_matches(TokenType.PERIOD)) { return qualifier; } Token period = getAndAdvance(); SimpleIdentifier qualified = parseSimpleIdentifier(); return new PrefixedIdentifier(qualifier, period, qualified); } /** * Parse a return type. Return the return type that was parsed. * * returnType ::= * 'void' * | type */ TypeName parseReturnType() { if (_matchesKeyword(Keyword.VOID)) { return new TypeName(new SimpleIdentifier(getAndAdvance()), null); } else { return parseTypeName(); } } /** * Parse a simple identifier. Return the simple identifier that was parsed. * * identifier ::= * IDENTIFIER */ SimpleIdentifier parseSimpleIdentifier() { if (_matchesIdentifier()) { String lexeme = _currentToken.lexeme; if ((_inAsync || _inGenerator) && (lexeme == 'async' || lexeme == 'await' || lexeme == 'yield')) { _reportErrorForCurrentToken( ParserErrorCode.ASYNC_KEYWORD_USED_AS_IDENTIFIER); } return new SimpleIdentifier(getAndAdvance()); } _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); return _createSyntheticIdentifier(); } /** * 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 labels = new List(); while (_matchesIdentifier() && _tokenMatches(_peek(), TokenType.COLON)) { labels.add(parseLabel()); } Statement statement = _parseNonLabeledStatement(); if (labels.isEmpty) { 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 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() { List strings = new List(); while (_matches(TokenType.STRING)) { Token string = getAndAdvance(); if (_matches(TokenType.STRING_INTERPOLATION_EXPRESSION) || _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER)) { strings.add(_parseStringInterpolation(string)); } else { strings.add(new SimpleStringLiteral( string, _computeStringValue(string.lexeme, true, true))); } } if (strings.length < 1) { _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_STRING_LITERAL); return _createSyntheticStringLiteral(); } else if (strings.length == 1) { return strings[0]; } else { return new AdjacentStrings(strings); } } /** * Parse a list of type arguments. Return the type argument list that was * parsed. * * typeArguments ::= * '<' typeList '>' * * typeList ::= * type (',' type)* */ TypeArgumentList parseTypeArgumentList() { Token leftBracket = _expect(TokenType.LT); List arguments = new List(); arguments.add(parseTypeName()); while (_optional(TokenType.COMMA)) { arguments.add(parseTypeName()); } 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() { Identifier typeName; if (_matchesKeyword(Keyword.VAR)) { _reportErrorForCurrentToken(ParserErrorCode.VAR_AS_TYPE_NAME); typeName = new SimpleIdentifier(getAndAdvance()); } else if (_matchesIdentifier()) { typeName = parsePrefixedIdentifier(); } else { typeName = _createSyntheticIdentifier(); _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TYPE_NAME); } TypeArgumentList typeArguments = null; if (_matches(TokenType.LT)) { typeArguments = parseTypeArgumentList(); } return new TypeName(typeName, typeArguments); } /** * Parse a type parameter. Return the type parameter that was parsed. * * typeParameter ::= * metadata name ('extends' bound)? */ TypeParameter parseTypeParameter() { CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata(); SimpleIdentifier name = parseSimpleIdentifier(); if (_matchesKeyword(Keyword.EXTENDS)) { Token keyword = getAndAdvance(); TypeName bound = parseTypeName(); 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. * * typeParameterList ::= * '<' typeParameter (',' typeParameter)* '>' */ TypeParameterList parseTypeParameterList() { Token leftBracket = _expect(TokenType.LT); List typeParameters = new List(); typeParameters.add(parseTypeParameter()); while (_optional(TokenType.COMMA)) { typeParameters.add(parseTypeParameter()); } Token rightBracket = _expectGt(); return new TypeParameterList(leftBracket, typeParameters, rightBracket); } /** * Parse a with clause. Return the with clause that was parsed. * * withClause ::= * 'with' typeName (',' typeName)* */ WithClause parseWithClause() { Token with2 = _expectKeyword(Keyword.WITH); List types = new List(); types.add(parseTypeName()); while (_optional(TokenType.COMMA)) { types.add(parseTypeName()); } return new WithClause(with2, types); } /** * Advance to the next token in the token stream. */ void _advance() { _currentToken = _currentToken.next; } /** * Append the character equivalent of the given [scalarValue] to the given * [builder]. Use the [startIndex] and [endIndex] to report an error, and * don't append anything to the builder, if the scalar value is invalid. The * [escapeSequence] is the escape sequence that was parsed to produce the * scalar value (used for error reporting). */ void _appendScalarValue(StringBuffer buffer, String escapeSequence, int scalarValue, int startIndex, int endIndex) { if (scalarValue < 0 || scalarValue > Character.MAX_CODE_POINT || (scalarValue >= 0xD800 && scalarValue <= 0xDFFF)) { _reportErrorForCurrentToken( ParserErrorCode.INVALID_CODE_POINT, [escapeSequence]); return; } if (scalarValue < Character.MAX_VALUE) { buffer.writeCharCode(scalarValue); } else { buffer.write(Character.toChars(scalarValue)); } } /** * Return the content of a string with the given literal representation. The * [lexeme] is the literal representation of the string. The flag [isFirst] is * `true` if this is the first token in a string literal. The flag [isLast] is * `true` if this is the last token in a string literal. */ String _computeStringValue(String lexeme, bool isFirst, bool isLast) { StringLexemeHelper helper = new StringLexemeHelper(lexeme, isFirst, isLast); int start = helper.start; int end = helper.end; bool stringEndsAfterStart = end >= start; assert(stringEndsAfterStart); if (!stringEndsAfterStart) { AnalysisEngine.instance.logger.logError( "Internal error: computeStringValue($lexeme, $isFirst, $isLast)"); return ""; } if (helper.isRaw) { return lexeme.substring(start, end); } StringBuffer buffer = new StringBuffer(); int index = start; while (index < end) { index = _translateCharacter(buffer, lexeme, index); } return buffer.toString(); } /** * 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.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() { if ((_matchesKeyword(Keyword.IMPORT) || _matchesKeyword(Keyword.EXPORT) || _matchesKeyword(Keyword.LIBRARY) || _matchesKeyword(Keyword.PART)) && !_tokenMatches(_peek(), TokenType.PERIOD) && !_tokenMatches(_peek(), TokenType.LT)) { // This looks like the start of a directive return true; } else if (_matchesKeyword(Keyword.CLASS)) { // This looks like the start of a class definition return true; } else if (_matchesKeyword(Keyword.TYPEDEF) && !_tokenMatches(_peek(), TokenType.PERIOD) && !_tokenMatches(_peek(), TokenType.LT)) { // This looks like the start of a typedef return true; } else if (_matchesKeyword(Keyword.VOID) || ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) && _tokenMatchesIdentifier(_peek())) || (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek()))) { // This looks like the start of a function return true; } else if (_matchesIdentifier()) { if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) { // This looks like the start of a function return true; } Token token = _skipReturnType(_currentToken); if (token == null) { return false; } if (_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET) || (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) || _matchesIdentifier()) { return true; } } return false; } /** * Return a synthetic identifier. */ SimpleIdentifier _createSyntheticIdentifier() { Token syntheticToken; if (_currentToken.type == TokenType.KEYWORD) { // Consider current keyword token as an identifier. // It is not always true, e.g. "^is T" where "^" is place the place for // synthetic identifier. By creating SyntheticStringToken we can // distinguish a real identifier from synthetic. In the code completion // behavior will depend on a cursor position - before or on "is". syntheticToken = _injectToken(new SyntheticStringToken( TokenType.IDENTIFIER, _currentToken.lexeme, _currentToken.offset)); } else { syntheticToken = _createSyntheticToken(TokenType.IDENTIFIER); } return new SimpleIdentifier(syntheticToken); } /** * Return a synthetic token representing the given [keyword]. */ Token _createSyntheticKeyword(Keyword keyword) => _injectToken( new Parser_SyntheticKeywordToken(keyword, _currentToken.offset)); /** * Return a synthetic string literal. */ SimpleStringLiteral _createSyntheticStringLiteral() => new SimpleStringLiteral(_createSyntheticToken(TokenType.STRING), ""); /** * Return a synthetic token with the given [type]. */ Token _createSyntheticToken(TokenType type) => _injectToken(new StringToken(type, "", _currentToken.offset)); /** * Create and return a new token with the given [type]. The token will replace * the first portion of the given [token], so it will have the same offset and * will have any comments that might have preceeded the token. */ 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); } return new TokenWithComment(type, token.offset, comments); } /** * Check that the given [expression] is assignable and report an error if it * isn't. * * assignableExpression ::= * primary (arguments* assignableSelector)+ * | 'super' unconditionalAssignableSelector * | identifier * * unconditionalAssignableSelector ::= * '[' expression ']' * | '.' identifier * * assignableSelector ::= * unconditionalAssignableSelector * | '?.' identifier */ void _ensureAssignable(Expression expression) { if (expression != null && !expression.isAssignable) { _reportErrorForCurrentToken( ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE); } } /** * 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]. * * The [type] is the type of token that is expected. */ Token _expect(TokenType type) { if (_matches(type)) { return getAndAdvance(); } // Remove uses of this method in favor of matches? // Pass in the error code to use to report the error? if (type == TokenType.SEMICOLON) { if (_tokenMatches(_currentToken.next, TokenType.SEMICOLON)) { _reportErrorForCurrentToken( ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]); _advance(); return getAndAdvance(); } _reportErrorForToken(ParserErrorCode.EXPECTED_TOKEN, _currentToken.previous, [type.lexeme]); } else { _reportErrorForCurrentToken( ParserErrorCode.EXPECTED_TOKEN, [type.lexeme]); } return _currentToken; } /** * If the current token has the type [TokenType.GT], return it after advancing * to the next token. Otherwise report an error and return the current token * without advancing. */ Token _expectGt() { if (_matchesGt()) { return getAndAdvance(); } _reportErrorForCurrentToken( ParserErrorCode.EXPECTED_TOKEN, [TokenType.GT.lexeme]); return _currentToken; } /** * 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(); } // 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; } /** * If the current token is a semicolon, return it after advancing to the next * token. Otherwise report an error and create a synthetic semicolon. */ Token _expectSemicolon() { // TODO(scheglov) consider pushing this behavior into [_expect] if (_matches(TokenType.SEMICOLON)) { return getAndAdvance(); } else { _reportErrorForToken( ParserErrorCode.EXPECTED_TOKEN, _currentToken.previous, [";"]); return _createSyntheticToken(TokenType.SEMICOLON); } } /** * 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. */ List _findRange(List> ranges, int index) { int rangeCount = ranges.length; for (int i = 0; i < rangeCount; i++) { List range = ranges[i]; if (range[0] <= index && index <= range[1]) { return range; } else if (index < range[0]) { return null; } } return null; } /** * Return a list of the ranges of characters in the given [comment] that * should be treated as code blocks. */ List> _getCodeBlockRanges(String comment) { List> ranges = new List>(); 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; } } 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([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([index, end]); index = end + 1; } else { index = index + 1; } } return ranges; } /** * Return the end token associated with the given [beginToken], or `null` if * either the given token is not a begin token or it does not have an end * token associated with it. */ Token _getEndToken(Token beginToken) { if (beginToken is BeginToken) { return beginToken.endToken; } return null; } /** * 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; } /** * Return `true` if the current token appears to be the beginning of a * function declaration. */ bool _isFunctionDeclaration() { if (_matchesKeyword(Keyword.VOID)) { return true; } Token afterReturnType = _skipTypeName(_currentToken); if (afterReturnType == null) { // There was no return type, but it is optional, so go back to where we // started. afterReturnType = _currentToken; } Token afterIdentifier = _skipSimpleIdentifier(afterReturnType); if (afterIdentifier == null) { // It's possible that we parsed the function name as if it were a type // name, so see whether it makes sense if we assume that there is no type. afterIdentifier = _skipSimpleIdentifier(_currentToken); } if (afterIdentifier == null) { return false; } if (_isFunctionExpression(afterIdentifier)) { return true; } // It's possible that we have found a getter. While this isn't valid at this // point we test for it in order to recover better. if (_matchesKeyword(Keyword.GET)) { Token afterName = _skipSimpleIdentifier(_currentToken.next); if (afterName == null) { return false; } return _tokenMatches(afterName, TokenType.FUNCTION) || _tokenMatches(afterName, TokenType.OPEN_CURLY_BRACKET); } else if (_tokenMatchesKeyword(afterReturnType, Keyword.GET)) { Token afterName = _skipSimpleIdentifier(afterReturnType.next); if (afterName == null) { return false; } return _tokenMatches(afterName, TokenType.FUNCTION) || _tokenMatches(afterName, TokenType.OPEN_CURLY_BRACKET); } return false; } /** * Return `true` if the given [token] appears to be the beginning of a * function expression. */ bool _isFunctionExpression(Token token) { // Function expressions aren't allowed in initializer lists. if (_inInitializer) { return false; } Token afterParameters = _skipFormalParameterList(token); if (afterParameters == null) { return false; } if (afterParameters .matchesAny([TokenType.OPEN_CURLY_BRACKET, TokenType.FUNCTION])) { return true; } String lexeme = afterParameters.lexeme; return lexeme == ASYNC || lexeme == SYNC; } /** * Return `true` if the given [character] is a valid hexadecimal digit. */ bool _isHexDigit(int character) => (0x30 <= character && character <= 0x39) || (0x41 <= character && character <= 0x46) || (0x61 <= character && character <= 0x66); /** * Return `true` if the current token is the first token in an initialized * variable declaration rather than an expression. This method assumes that we * have already skipped past any metadata that might be associated with the * declaration. * * initializedVariableDeclaration ::= * declaredIdentifier ('=' expression)? (',' initializedIdentifier)* * * declaredIdentifier ::= * metadata finalConstVarOrType identifier * * finalConstVarOrType ::= * 'final' type? * | 'const' type? * | 'var' * | type * * type ::= * qualified typeArguments? * * initializedIdentifier ::= * identifier ('=' expression)? */ bool _isInitializedVariableDeclaration() { if (_matchesKeyword(Keyword.FINAL) || _matchesKeyword(Keyword.VAR)) { // An expression cannot start with a keyword other than 'const', // 'rethrow', or 'throw'. return true; } if (_matchesKeyword(Keyword.CONST)) { // Look to see whether we might be at the start of a list or map literal, // otherwise this should be the start of a variable declaration. return !_peek().matchesAny([ TokenType.LT, TokenType.OPEN_CURLY_BRACKET, TokenType.OPEN_SQUARE_BRACKET, TokenType.INDEX ]); } // We know that we have an identifier, and need to see whether it might be // a type name. Token token = _skipTypeName(_currentToken); if (token == null) { // There was no type name, so this can't be a declaration. return false; } token = _skipSimpleIdentifier(token); if (token == null) { return false; } TokenType type = token.type; return type == TokenType.EQ || type == TokenType.COMMA || type == TokenType.SEMICOLON || _tokenMatchesKeyword(token, Keyword.IN); } /** * 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. * * This method uses the syntax described by the * markdown * project. */ bool _isLinkText(String comment, int rightIndex) { int length = comment.length; int index = rightIndex + 1; if (index >= length) { return false; } int nextChar = comment.codeUnitAt(index); if (nextChar == 0x28 || nextChar == 0x3A) { return true; } while (Character.isWhitespace(nextChar)) { index = index + 1; if (index >= length) { return false; } nextChar = comment.codeUnitAt(index); } return nextChar == 0x5B; } /** * Return `true` if the given [startToken] appears to be the beginning of an * operator declaration. */ 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 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); } /** * Return `true` if the current token appears to be the beginning of a switch * member. */ bool _isSwitchMember() { Token token = _currentToken; while (_tokenMatches(token, TokenType.IDENTIFIER) && _tokenMatches(token.next, TokenType.COLON)) { token = token.next.next; } if (token.type == TokenType.KEYWORD) { Keyword keyword = (token as KeywordToken).keyword; return keyword == Keyword.CASE || keyword == Keyword.DEFAULT; } return false; } /** * 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. */ 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) { // The type is more than a simple identifier, so it should be assumed to // be a type name. return true; } 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; } /** * Return `true` if the current token is a valid identifier. Valid identifiers * include built-in identifiers (pseudo-keywords). */ bool _matchesIdentifier() => _tokenMatchesIdentifier(_currentToken); /** * Return `true` if the current token matches the given [keyword]. */ bool _matchesKeyword(Keyword keyword) => _tokenMatchesKeyword(_currentToken, keyword); /** * Return `true` if the current token matches the given [identifier]. */ bool _matchesString(String identifier) => _currentToken.type == TokenType.IDENTIFIER && _currentToken.lexeme == identifier; /** * If the current token has the given [type], then advance to the next token * and return `true`. Otherwise, return `false` without advancing. This method * should not be invoked with an argument value of [TokenType.GT]. */ bool _optional(TokenType type) { if (_matches(type)) { _advance(); return true; } return false; } /** * Parse an additive expression. Return the additive expression that was * parsed. * * additiveExpression ::= * multiplicativeExpression (additiveOperator multiplicativeExpression)* * | 'super' (additiveOperator multiplicativeExpression)+ */ Expression _parseAdditiveExpression() { Expression expression; if (_matchesKeyword(Keyword.SUPER) && _currentToken.next.type.isAdditiveOperator) { expression = new SuperExpression(getAndAdvance()); } else { expression = _parseMultiplicativeExpression(); } while (_currentToken.type.isAdditiveOperator) { Token operator = getAndAdvance(); expression = new BinaryExpression( expression, operator, _parseMultiplicativeExpression()); } return expression; } /** * Parse an assert statement. Return the assert statement. * * assertStatement ::= * 'assert' '(' conditionalExpression ')' ';' */ AssertStatement _parseAssertStatement() { Token keyword = _expectKeyword(Keyword.ASSERT); Token leftParen = _expect(TokenType.OPEN_PAREN); Expression expression = parseExpression2(); if (expression is AssignmentExpression) { _reportErrorForNode( ParserErrorCode.ASSERT_DOES_NOT_TAKE_ASSIGNMENT, expression); } else if (expression is CascadeExpression) { _reportErrorForNode( ParserErrorCode.ASSERT_DOES_NOT_TAKE_CASCADE, expression); } else if (expression is ThrowExpression) { _reportErrorForNode( ParserErrorCode.ASSERT_DOES_NOT_TAKE_THROW, expression); } else if (expression is RethrowExpression) { _reportErrorForNode( ParserErrorCode.ASSERT_DOES_NOT_TAKE_RETHROW, expression); } Token rightParen = _expect(TokenType.CLOSE_PAREN); Token semicolon = _expect(TokenType.SEMICOLON); return new AssertStatement( keyword, leftParen, expression, rightParen, semicolon); } /** * Parse an assignable expression. The [primaryAllowed] is `true` if the * expression is allowed to be a primary without any assignable selector. * Return the assignable expression that was parsed. * * assignableExpression ::= * primary (arguments* assignableSelector)+ * | 'super' unconditionalAssignableSelector * | identifier */ Expression _parseAssignableExpression(bool primaryAllowed) { if (_matchesKeyword(Keyword.SUPER)) { return _parseAssignableSelector( new SuperExpression(getAndAdvance()), false, allowConditional: false); } // // 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 (_matches(TokenType.OPEN_PAREN)) { ArgumentList argumentList = parseArgumentList(); if (expression is SimpleIdentifier) { expression = new MethodInvocation( null, null, expression as SimpleIdentifier, argumentList); } else if (expression is PrefixedIdentifier) { PrefixedIdentifier identifier = expression as PrefixedIdentifier; expression = new MethodInvocation(identifier.prefix, identifier.period, identifier.identifier, argumentList); } else if (expression is PropertyAccess) { PropertyAccess access = expression as PropertyAccess; expression = new MethodInvocation(access.target, access.operator, access.propertyName, argumentList); } else { expression = new FunctionExpressionInvocation(expression, argumentList); } if (!primaryAllowed) { isOptional = false; } } Expression selectorExpression = _parseAssignableSelector( expression, isOptional || (expression is PrefixedIdentifier)); if (identical(selectorExpression, expression)) { if (!isOptional && (expression is PrefixedIdentifier)) { PrefixedIdentifier identifier = expression as PrefixedIdentifier; expression = new PropertyAccess( identifier.prefix, identifier.period, identifier.identifier); } return expression; } expression = selectorExpression; isOptional = true; } } /** * 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}) { if (_matches(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 if (_matches(TokenType.PERIOD) || _matches(TokenType.QUESTION_PERIOD)) { if (_matches(TokenType.QUESTION_PERIOD) && !allowConditional) { _reportErrorForCurrentToken( ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [_currentToken.lexeme]); } Token operator = getAndAdvance(); return new PropertyAccess(prefix, operator, parseSimpleIdentifier()); } else { if (!optional) { // Report the missing selector. _reportErrorForCurrentToken( ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR); } return prefix; } } /** * Parse a await expression. Return the await expression that was parsed. * * awaitExpression ::= * 'await' unaryExpression */ AwaitExpression _parseAwaitExpression() { Token awaitToken = getAndAdvance(); Expression expression = _parseUnaryExpression(); return new AwaitExpression(awaitToken, expression); } /** * Parse a bitwise and expression. Return the bitwise and expression that was * parsed. * * bitwiseAndExpression ::= * shiftExpression ('&' shiftExpression)* * | 'super' ('&' shiftExpression)+ */ Expression _parseBitwiseAndExpression() { Expression expression; if (_matchesKeyword(Keyword.SUPER) && _tokenMatches(_peek(), TokenType.AMPERSAND)) { expression = new SuperExpression(getAndAdvance()); } else { expression = _parseShiftExpression(); } while (_matches(TokenType.AMPERSAND)) { Token operator = getAndAdvance(); expression = new BinaryExpression(expression, operator, _parseShiftExpression()); } return expression; } /** * Parse a bitwise exclusive-or expression. Return the bitwise exclusive-or * expression that was parsed. * * bitwiseXorExpression ::= * bitwiseAndExpression ('^' bitwiseAndExpression)* * | 'super' ('^' bitwiseAndExpression)+ */ Expression _parseBitwiseXorExpression() { Expression expression; if (_matchesKeyword(Keyword.SUPER) && _tokenMatches(_peek(), TokenType.CARET)) { expression = new SuperExpression(getAndAdvance()); } else { expression = _parseBitwiseAndExpression(); } while (_matches(TokenType.CARET)) { Token operator = getAndAdvance(); expression = new BinaryExpression( expression, operator, _parseBitwiseAndExpression()); } return expression; } /** * Parse a break statement. Return the break statement that was parsed. * * breakStatement ::= * 'break' identifier? ';' */ Statement _parseBreakStatement() { Token breakKeyword = _expectKeyword(Keyword.BREAK); SimpleIdentifier label = null; if (_matchesIdentifier()) { label = parseSimpleIdentifier(); } if (!_inLoop && !_inSwitch && label == null) { _reportErrorForToken(ParserErrorCode.BREAK_OUTSIDE_OF_LOOP, breakKeyword); } Token semicolon = _expect(TokenType.SEMICOLON); return new BreakStatement(breakKeyword, label, semicolon); } /** * Parse a cascade section. Return the expression representing the cascaded * method invocation. * * cascadeSection ::= * '..' (cascadeSelector arguments*) (assignableSelector arguments*)* cascadeAssignment? * * cascadeSelector ::= * '[' expression ']' * | identifier * * cascadeAssignment ::= * assignmentOperator expressionWithoutCascade */ Expression _parseCascadeSection() { Token period = _expect(TokenType.PERIOD_PERIOD); Expression expression = null; SimpleIdentifier functionName = null; if (_matchesIdentifier()) { functionName = parseSimpleIdentifier(); } else if (_currentToken.type == TokenType.OPEN_SQUARE_BRACKET) { Token leftBracket = getAndAdvance(); bool wasInInitializer = _inInitializer; _inInitializer = false; try { Expression index = parseExpression2(); Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET); expression = new IndexExpression.forCascade( period, leftBracket, index, rightBracket); period = null; } finally { _inInitializer = wasInInitializer; } } else { _reportErrorForToken(ParserErrorCode.MISSING_IDENTIFIER, _currentToken, [_currentToken.lexeme]); functionName = _createSyntheticIdentifier(); } assert((expression == null && functionName != null) || (expression != null && functionName == null)); if (_currentToken.type == TokenType.OPEN_PAREN) { while (_currentToken.type == TokenType.OPEN_PAREN) { if (functionName != null) { expression = new MethodInvocation( expression, period, functionName, parseArgumentList()); period = null; functionName = null; } else if (expression == null) { // It should not be possible to get here. expression = new MethodInvocation(expression, period, _createSyntheticIdentifier(), parseArgumentList()); } else { expression = new FunctionExpressionInvocation(expression, parseArgumentList()); } } } else if (functionName != null) { expression = new PropertyAccess(expression, period, functionName); period = null; } assert(expression != null); bool progress = true; while (progress) { progress = false; Expression selector = _parseAssignableSelector(expression, true); if (!identical(selector, expression)) { expression = selector; progress = true; while (_currentToken.type == TokenType.OPEN_PAREN) { if (expression is PropertyAccess) { PropertyAccess propertyAccess = expression as PropertyAccess; expression = new MethodInvocation(propertyAccess.target, propertyAccess.operator, propertyAccess.propertyName, parseArgumentList()); } else { expression = new FunctionExpressionInvocation( expression, parseArgumentList()); } } } } if (_currentToken.type.isAssignmentOperator) { Token operator = getAndAdvance(); _ensureAssignable(expression); expression = new AssignmentExpression( expression, operator, parseExpressionWithoutCascade()); } return expression; } /** * Parse a class declaration. The [commentAndMetadata] is the metadata to be * associated with the member. The [abstractKeyword] is the token for the * keyword 'abstract', or `null` if the keyword was not given. Return the * class declaration that was parsed. * * classDeclaration ::= * metadata 'abstract'? 'class' name typeParameterList? (extendsClause withClause?)? implementsClause? '{' classMembers '}' | * metadata 'abstract'? 'class' mixinApplicationClass */ CompilationUnitMember _parseClassDeclaration( CommentAndMetadata commentAndMetadata, Token abstractKeyword) { Token keyword = _expectKeyword(Keyword.CLASS); if (_matchesIdentifier()) { Token next = _peek(); if (_tokenMatches(next, TokenType.LT)) { next = _skipTypeParameterList(next); if (next != null && _tokenMatches(next, TokenType.EQ)) { return _parseClassTypeAlias( commentAndMetadata, abstractKeyword, keyword); } } else if (_tokenMatches(next, TokenType.EQ)) { return _parseClassTypeAlias( commentAndMetadata, abstractKeyword, keyword); } } SimpleIdentifier name = parseSimpleIdentifier(); String className = name.name; TypeParameterList typeParameters = null; if (_matches(TokenType.LT)) { typeParameters = parseTypeParameterList(); } // // Parse the clauses. The parser accepts clauses in any order, but will // generate errors if they are not in the order required by the // specification. // ExtendsClause extendsClause = null; WithClause withClause = null; ImplementsClause implementsClause = null; bool foundClause = true; while (foundClause) { if (_matchesKeyword(Keyword.EXTENDS)) { if (extendsClause == null) { extendsClause = parseExtendsClause(); if (withClause != null) { _reportErrorForToken( ParserErrorCode.WITH_BEFORE_EXTENDS, withClause.withKeyword); } else if (implementsClause != null) { _reportErrorForToken(ParserErrorCode.IMPLEMENTS_BEFORE_EXTENDS, implementsClause.implementsKeyword); } } else { _reportErrorForToken(ParserErrorCode.MULTIPLE_EXTENDS_CLAUSES, extendsClause.extendsKeyword); parseExtendsClause(); } } else if (_matchesKeyword(Keyword.WITH)) { if (withClause == null) { withClause = parseWithClause(); if (implementsClause != null) { _reportErrorForToken(ParserErrorCode.IMPLEMENTS_BEFORE_WITH, implementsClause.implementsKeyword); } } else { _reportErrorForToken( ParserErrorCode.MULTIPLE_WITH_CLAUSES, withClause.withKeyword); parseWithClause(); // TODO(brianwilkerson) Should we merge the list of applied mixins // into a single list? } } else if (_matchesKeyword(Keyword.IMPLEMENTS)) { if (implementsClause == null) { implementsClause = parseImplementsClause(); } else { _reportErrorForToken(ParserErrorCode.MULTIPLE_IMPLEMENTS_CLAUSES, implementsClause.implementsKeyword); parseImplementsClause(); // TODO(brianwilkerson) Should we merge the list of implemented // classes into a single list? } } else { foundClause = false; } } if (withClause != null && extendsClause == null) { _reportErrorForToken( ParserErrorCode.WITH_WITHOUT_EXTENDS, withClause.withKeyword); } // // Look for and skip over the extra-lingual 'native' specification. // NativeClause nativeClause = null; if (_matchesString(_NATIVE) && _tokenMatches(_peek(), TokenType.STRING)) { nativeClause = _parseNativeClause(); } // // Parse the body of the class. // Token leftBracket = null; List members = null; Token rightBracket = null; if (_matches(TokenType.OPEN_CURLY_BRACKET)) { leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET); members = _parseClassMembers(className, _getEndToken(leftBracket)); rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET); } else { leftBracket = _createSyntheticToken(TokenType.OPEN_CURLY_BRACKET); rightBracket = _createSyntheticToken(TokenType.CLOSE_CURLY_BRACKET); _reportErrorForCurrentToken(ParserErrorCode.MISSING_CLASS_BODY); } ClassDeclaration classDeclaration = new ClassDeclaration( commentAndMetadata.comment, commentAndMetadata.metadata, abstractKeyword, keyword, name, typeParameters, extendsClause, withClause, implementsClause, leftBracket, members, rightBracket); classDeclaration.nativeClause = nativeClause; return classDeclaration; } /** * Parse a 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. * * classMembers ::= * (metadata memberDefinition)* */ List _parseClassMembers(String className, Token closingBracket) { List members = new List(); Token memberStart = _currentToken; while (!_matches(TokenType.EOF) && !_matches(TokenType.CLOSE_CURLY_BRACKET) && (closingBracket != null || (!_matchesKeyword(Keyword.CLASS) && !_matchesKeyword(Keyword.TYPEDEF)))) { if (_matches(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; } return members; } /** * Parse a class type alias. The [commentAndMetadata] is the metadata to be * associated with the member. The [abstractKeyword] is the token representing * the 'abstract' keyword. The [classKeyword] is the token representing the * 'class' keyword. Return the class type alias that was parsed. * * classTypeAlias ::= * identifier typeParameters? '=' 'abstract'? mixinApplication * * mixinApplication ::= * type withClause implementsClause? ';' */ ClassTypeAlias _parseClassTypeAlias(CommentAndMetadata commentAndMetadata, Token abstractKeyword, Token classKeyword) { SimpleIdentifier className = parseSimpleIdentifier(); TypeParameterList typeParameters = null; if (_matches(TokenType.LT)) { typeParameters = parseTypeParameterList(); } Token equals = _expect(TokenType.EQ); TypeName superclass = parseTypeName(); WithClause withClause = null; if (_matchesKeyword(Keyword.WITH)) { withClause = parseWithClause(); } else { _reportErrorForCurrentToken( ParserErrorCode.EXPECTED_TOKEN, [Keyword.WITH.syntax]); } ImplementsClause implementsClause = null; if (_matchesKeyword(Keyword.IMPLEMENTS)) { implementsClause = parseImplementsClause(); } Token semicolon; if (_matches(TokenType.SEMICOLON)) { semicolon = getAndAdvance(); } else { if (_matches(TokenType.OPEN_CURLY_BRACKET)) { _reportErrorForCurrentToken( ParserErrorCode.EXPECTED_TOKEN, [TokenType.SEMICOLON.lexeme]); Token leftBracket = getAndAdvance(); _parseClassMembers(className.name, _getEndToken(leftBracket)); _expect(TokenType.CLOSE_CURLY_BRACKET); } else { _reportErrorForToken(ParserErrorCode.EXPECTED_TOKEN, _currentToken.previous, [TokenType.SEMICOLON.lexeme]); } semicolon = _createSyntheticToken(TokenType.SEMICOLON); } return new ClassTypeAlias(commentAndMetadata.comment, commentAndMetadata.metadata, classKeyword, className, typeParameters, equals, abstractKeyword, superclass, withClause, implementsClause, semicolon); } /** * Parse a list of combinators in a directive. Return the combinators that * were parsed. * * combinator ::= * 'show' identifier (',' identifier)* * | 'hide' identifier (',' identifier)* */ List _parseCombinators() { List combinators = new List(); while (true) { Combinator combinator = parseCombinator(); if (combinator == null) { break; } combinators.add(combinator); } return combinators; } /** * Parse the documentation comment and metadata preceding a declaration. This * method allows any number of documentation comments to occur before, after * or between the metadata, but only returns the last (right-most) * documentation comment that is found. Return the documentation comment and * metadata that were parsed. * * metadata ::= * annotation* */ CommentAndMetadata _parseCommentAndMetadata() { Comment comment = _parseDocumentationComment(); List metadata = new List(); while (_matches(TokenType.AT)) { metadata.add(parseAnnotation()); Comment optionalComment = _parseDocumentationComment(); if (optionalComment != null) { comment = optionalComment; } } return new CommentAndMetadata(comment, metadata); } /** * Parse a comment reference from the source between square brackets. The * [referenceSource] is the source occurring between the square brackets * within a documentation comment. The [sourceOffset] is the offset of the * first character of the reference source. Return the comment reference that * was parsed, or `null` if no reference could be found. * * commentReference ::= * 'new'? prefixedIdentifier */ CommentReference _parseCommentReference( String referenceSource, int sourceOffset) { // TODO(brianwilkerson) The errors are not getting the right offset/length // and are being duplicated. if (referenceSource.length == 0) { Token syntheticToken = new SyntheticStringToken(TokenType.IDENTIFIER, "", sourceOffset); return new CommentReference(null, new SimpleIdentifier(syntheticToken)); } try { BooleanErrorListener listener = new BooleanErrorListener(); Scanner scanner = new Scanner( null, new SubSequenceReader(referenceSource, sourceOffset), listener); scanner.setSourceStart(1, 1); Token firstToken = scanner.tokenize(); if (listener.errorReported) { return null; } Token newKeyword = null; if (_tokenMatchesKeyword(firstToken, Keyword.NEW)) { newKeyword = firstToken; firstToken = firstToken.next; } if (_tokenMatchesIdentifier(firstToken)) { Token secondToken = firstToken.next; Token thirdToken = secondToken.next; Token nextToken; Identifier identifier; if (_tokenMatches(secondToken, TokenType.PERIOD) && _tokenMatchesIdentifier(thirdToken)) { identifier = new PrefixedIdentifier(new SimpleIdentifier(firstToken), secondToken, new SimpleIdentifier(thirdToken)); nextToken = thirdToken.next; } else { identifier = new SimpleIdentifier(firstToken); nextToken = firstToken.next; } if (nextToken.type != TokenType.EOF) { return null; } return new CommentReference(newKeyword, identifier); } else if (_tokenMatchesKeyword(firstToken, Keyword.THIS) || _tokenMatchesKeyword(firstToken, Keyword.NULL) || _tokenMatchesKeyword(firstToken, Keyword.TRUE) || _tokenMatchesKeyword(firstToken, Keyword.FALSE)) { // TODO(brianwilkerson) If we want to support this we will need to // extend the definition of CommentReference to take an expression // rather than an identifier. For now we just ignore it to reduce the // number of errors produced, but that's probably not a valid long term // approach. return null; } } catch (exception) { // Ignored because we assume that it wasn't a real comment reference. } 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. * * commentReference ::= * '[' 'new'? qualified ']' libraryReference? * * libraryReference ::= * '(' stringLiteral ')' */ List _parseCommentReferences( List tokens) { List references = new List(); for (DocumentationCommentToken token in tokens) { String comment = token.lexeme; int length = comment.length; List> codeBlockRanges = _getCodeBlockRanges(comment); int leftIndex = comment.indexOf('['); while (leftIndex >= 0 && leftIndex + 1 < length) { List range = _findRange(codeBlockRanges, leftIndex); if (range == null) { int nameOffset = token.offset + leftIndex + 1; int rightIndex = JavaString.indexOf(comment, ']', 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); if (Character.isLetterOrDigit(charAfterLeft)) { int nameEnd = StringUtilities.indexOfFirstNotLetterDigit( comment, leftIndex + 1); String name = comment.substring(leftIndex + 1, nameEnd); Token nameToken = new StringToken(TokenType.IDENTIFIER, name, nameOffset); references.add( new CommentReference(null, new SimpleIdentifier(nameToken))); } else { Token nameToken = new SyntheticStringToken( TokenType.IDENTIFIER, "", nameOffset); references.add( new CommentReference(null, new SimpleIdentifier(nameToken))); } // next character rightIndex = leftIndex + 1; } leftIndex = JavaString.indexOf(comment, '[', rightIndex); } else { leftIndex = JavaString.indexOf(comment, '[', range[1] + 1); } } } return references; } /** * Parse a compilation unit member. The [commentAndMetadata] is the metadata * to be associated with the member. Return the compilation unit member that * was parsed, or `null` if what was parsed could not be represented as a * compilation unit member. * * compilationUnitMember ::= * classDefinition * | functionTypeAlias * | external functionSignature * | external getterSignature * | external setterSignature * | functionSignature functionBody * | returnType? getOrSet identifier formalParameterList functionBody * | (final | const) type? staticFinalDeclarationList ';' * | variableDeclaration ';' */ CompilationUnitMember _parseCompilationUnitMember( CommentAndMetadata commentAndMetadata) { Modifiers modifiers = _parseModifiers(); if (_matchesKeyword(Keyword.CLASS)) { return _parseClassDeclaration( commentAndMetadata, _validateModifiersForClass(modifiers)); } else if (_matchesKeyword(Keyword.TYPEDEF) && !_tokenMatches(_peek(), TokenType.PERIOD) && !_tokenMatches(_peek(), TokenType.LT) && !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) { _validateModifiersForTypedef(modifiers); return _parseTypeAlias(commentAndMetadata); } else if (_matchesKeyword(Keyword.ENUM)) { _validateModifiersForEnum(modifiers); return _parseEnumDeclaration(commentAndMetadata); } if (_matchesKeyword(Keyword.VOID)) { TypeName returnType = parseReturnType(); if ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) && _tokenMatchesIdentifier(_peek())) { _validateModifiersForTopLevelFunction(modifiers); return _parseFunctionDeclaration( commentAndMetadata, modifiers.externalKeyword, returnType); } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) { _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken); return _convertToFunctionDeclaration(_parseOperator( commentAndMetadata, modifiers.externalKeyword, returnType)); } else if (_matchesIdentifier() && _peek().matchesAny([ TokenType.OPEN_PAREN, TokenType.OPEN_CURLY_BRACKET, TokenType.FUNCTION ])) { _validateModifiersForTopLevelFunction(modifiers); return _parseFunctionDeclaration( commentAndMetadata, modifiers.externalKeyword, returnType); } else { // // We have found an error of some kind. Try to recover. // if (_matchesIdentifier()) { if (_peek().matchesAny( [TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) { // // We appear to have a variable declaration with a type of "void". // _reportErrorForNode(ParserErrorCode.VOID_VARIABLE, returnType); return new TopLevelVariableDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, _parseVariableDeclarationListAfterType(null, _validateModifiersForTopLevelVariable(modifiers), null), _expect(TokenType.SEMICOLON)); } } _reportErrorForToken( ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken); return null; } } else if ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) && _tokenMatchesIdentifier(_peek())) { _validateModifiersForTopLevelFunction(modifiers); return _parseFunctionDeclaration( commentAndMetadata, modifiers.externalKeyword, null); } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) { _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken); return _convertToFunctionDeclaration( _parseOperator(commentAndMetadata, modifiers.externalKeyword, null)); } else if (!_matchesIdentifier()) { Token keyword = modifiers.varKeyword; if (keyword == null) { keyword = modifiers.finalKeyword; } if (keyword == null) { keyword = modifiers.constKeyword; } if (keyword != null) { // // We appear to have found an incomplete top-level variable declaration. // _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); List variables = new List(); variables.add(new VariableDeclaration( null, null, _createSyntheticIdentifier(), null, null)); return new TopLevelVariableDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, new VariableDeclarationList(null, null, keyword, null, variables), _expectSemicolon()); } _reportErrorForToken(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken); return null; } else if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) { _validateModifiersForTopLevelFunction(modifiers); return _parseFunctionDeclaration( commentAndMetadata, modifiers.externalKeyword, null); } else if (_peek() .matchesAny([TokenType.EQ, TokenType.COMMA, TokenType.SEMICOLON])) { if (modifiers.constKeyword == null && modifiers.finalKeyword == null && modifiers.varKeyword == null) { _reportErrorForCurrentToken( ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE); } return new TopLevelVariableDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, _parseVariableDeclarationListAfterType( null, _validateModifiersForTopLevelVariable(modifiers), null), _expect(TokenType.SEMICOLON)); } TypeName returnType = parseReturnType(); if ((_matchesKeyword(Keyword.GET) || _matchesKeyword(Keyword.SET)) && _tokenMatchesIdentifier(_peek())) { _validateModifiersForTopLevelFunction(modifiers); return _parseFunctionDeclaration( commentAndMetadata, modifiers.externalKeyword, returnType); } else if (_matchesKeyword(Keyword.OPERATOR) && _isOperator(_peek())) { _reportErrorForToken(ParserErrorCode.TOP_LEVEL_OPERATOR, _currentToken); return _convertToFunctionDeclaration(_parseOperator( commentAndMetadata, modifiers.externalKeyword, returnType)); } else if (_matches(TokenType.AT)) { return new TopLevelVariableDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, _parseVariableDeclarationListAfterType( null, _validateModifiersForTopLevelVariable(modifiers), returnType), _expect(TokenType.SEMICOLON)); } else if (!_matchesIdentifier()) { // TODO(brianwilkerson) Generalize this error. We could also be parsing a // top-level variable at this point. _reportErrorForToken(ParserErrorCode.EXPECTED_EXECUTABLE, _currentToken); Token semicolon; if (_matches(TokenType.SEMICOLON)) { semicolon = getAndAdvance(); } else { semicolon = _createSyntheticToken(TokenType.SEMICOLON); } List variables = new List(); variables.add(new VariableDeclaration( null, null, _createSyntheticIdentifier(), null, null)); return new TopLevelVariableDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, new VariableDeclarationList(null, null, null, returnType, variables), semicolon); } if (_peek().matchesAny([ TokenType.OPEN_PAREN, TokenType.FUNCTION, TokenType.OPEN_CURLY_BRACKET ])) { _validateModifiersForTopLevelFunction(modifiers); return _parseFunctionDeclaration( commentAndMetadata, modifiers.externalKeyword, returnType); } return new TopLevelVariableDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, _parseVariableDeclarationListAfterType( null, _validateModifiersForTopLevelVariable(modifiers), returnType), _expect(TokenType.SEMICOLON)); } /** * Parse a const expression. Return the const expression that was parsed. * * constExpression ::= * instanceCreationExpression * | listLiteral * | mapLiteral */ Expression _parseConstExpression() { Token keyword = _expectKeyword(Keyword.CONST); if (_matches(TokenType.OPEN_SQUARE_BRACKET) || _matches(TokenType.INDEX)) { return _parseListLiteral(keyword, null); } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) { return _parseMapLiteral(keyword, null); } else if (_matches(TokenType.LT)) { return _parseListOrMapLiteral(keyword); } return _parseInstanceCreationExpression(keyword); } ConstructorDeclaration _parseConstructor( CommentAndMetadata commentAndMetadata, Token externalKeyword, Token constKeyword, Token factoryKeyword, SimpleIdentifier returnType, Token period, SimpleIdentifier name, FormalParameterList parameters) { bool bodyAllowed = externalKeyword == null; Token separator = null; List initializers = null; if (_matches(TokenType.COLON)) { separator = getAndAdvance(); initializers = new List(); do { if (_matchesKeyword(Keyword.THIS)) { if (_tokenMatches(_peek(), TokenType.OPEN_PAREN)) { bodyAllowed = false; initializers.add(_parseRedirectingConstructorInvocation()); } else if (_tokenMatches(_peek(), TokenType.PERIOD) && _tokenMatches(_peekAt(3), TokenType.OPEN_PAREN)) { bodyAllowed = false; initializers.add(_parseRedirectingConstructorInvocation()); } else { initializers.add(_parseConstructorFieldInitializer()); } } else if (_matchesKeyword(Keyword.SUPER)) { initializers.add(_parseSuperConstructorInvocation()); } else if (_matches(TokenType.OPEN_CURLY_BRACKET) || _matches(TokenType.FUNCTION)) { _reportErrorForCurrentToken(ParserErrorCode.MISSING_INITIALIZER); } else { initializers.add(_parseConstructorFieldInitializer()); } } 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) { _reportErrorForToken( ParserErrorCode.FACTORY_WITHOUT_BODY, factoryKeyword); } } else { if (constKeyword != null) { _reportErrorForNode( ParserErrorCode.CONST_CONSTRUCTOR_WITH_BODY, body); } else if (!bodyAllowed) { _reportErrorForNode( ParserErrorCode.EXTERNAL_CONSTRUCTOR_WITH_BODY, body); } } } return new ConstructorDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, constKeyword, factoryKeyword, returnType, period, name, parameters, separator, initializers, redirectedConstructor, body); } /** * Parse a field initializer within a constructor. Return the field * initializer that was parsed. * * fieldInitializer: * ('this' '.')? identifier '=' conditionalExpression cascadeSection* */ ConstructorFieldInitializer _parseConstructorFieldInitializer() { Token keyword = null; Token period = null; if (_matchesKeyword(Keyword.THIS)) { keyword = getAndAdvance(); period = _expect(TokenType.PERIOD); } SimpleIdentifier fieldName = parseSimpleIdentifier(); Token equals = null; if (_matches(TokenType.EQ)) { equals = getAndAdvance(); } else if (!_matchesKeyword(Keyword.THIS) && !_matchesKeyword(Keyword.SUPER) && !_matches(TokenType.OPEN_CURLY_BRACKET) && !_matches(TokenType.FUNCTION)) { _reportErrorForCurrentToken( ParserErrorCode.MISSING_ASSIGNMENT_IN_INITIALIZER); equals = _createSyntheticToken(TokenType.EQ); } else { _reportErrorForCurrentToken( ParserErrorCode.MISSING_ASSIGNMENT_IN_INITIALIZER); return new ConstructorFieldInitializer(keyword, period, fieldName, _createSyntheticToken(TokenType.EQ), _createSyntheticIdentifier()); } bool wasInInitializer = _inInitializer; _inInitializer = true; try { Expression expression = parseConditionalExpression(); TokenType tokenType = _currentToken.type; if (tokenType == TokenType.PERIOD_PERIOD) { List cascadeSections = new List(); while (tokenType == TokenType.PERIOD_PERIOD) { Expression section = _parseCascadeSection(); if (section != null) { cascadeSections.add(section); } tokenType = _currentToken.type; } expression = new CascadeExpression(expression, cascadeSections); } return new ConstructorFieldInitializer( keyword, period, fieldName, equals, expression); } finally { _inInitializer = wasInInitializer; } } /** * Parse a continue statement. Return the continue statement that was parsed. * * continueStatement ::= * 'continue' identifier? ';' */ Statement _parseContinueStatement() { Token continueKeyword = _expectKeyword(Keyword.CONTINUE); if (!_inLoop && !_inSwitch) { _reportErrorForToken( ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP, continueKeyword); } SimpleIdentifier label = null; if (_matchesIdentifier()) { label = parseSimpleIdentifier(); } if (_inSwitch && !_inLoop && label == null) { _reportErrorForToken( ParserErrorCode.CONTINUE_WITHOUT_LABEL_IN_CASE, continueKeyword); } Token semicolon = _expect(TokenType.SEMICOLON); return new ContinueStatement(continueKeyword, label, semicolon); } /** * Parse 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 _parsePartDirective(commentAndMetadata); } else { // Internal error: this method should not have been invoked if the current // token was something other than one of the above. throw new IllegalStateException( "parseDirective invoked in an invalid state; currentToken = $_currentToken"); } } /** * Parse the script tag and directives in a compilation unit until the first * non-directive is encountered. Return the compilation unit that was parsed. * * compilationUnit ::= * scriptTag? directive* */ CompilationUnit _parseDirectives() { Token firstToken = _currentToken; ScriptTag scriptTag = null; if (_matches(TokenType.SCRIPT_TAG)) { scriptTag = new ScriptTag(getAndAdvance()); } List directives = new List(); while (!_matches(TokenType.EOF)) { CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata(); if ((_matchesKeyword(Keyword.IMPORT) || _matchesKeyword(Keyword.EXPORT) || _matchesKeyword(Keyword.LIBRARY) || _matchesKeyword(Keyword.PART)) && !_tokenMatches(_peek(), TokenType.PERIOD) && !_tokenMatches(_peek(), TokenType.LT) && !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) { directives.add(_parseDirective(commentAndMetadata)); } else if (_matches(TokenType.SEMICOLON)) { _advance(); } else { while (!_matches(TokenType.EOF)) { _advance(); } return new CompilationUnit(firstToken, scriptTag, directives, new List(), _currentToken); } } return new CompilationUnit(firstToken, scriptTag, directives, new List(), _currentToken); } /** * Parse a documentation comment. Return the documentation comment that was * parsed, or `null` if there was no comment. * * documentationComment ::= * multiLineComment? * | singleLineComment* */ Comment _parseDocumentationComment() { List documentationTokens = []; CommentToken commentToken = _currentToken.precedingComments; while (commentToken != null) { if (commentToken is DocumentationCommentToken) { if (documentationTokens.isNotEmpty) { if (commentToken.type == TokenType.SINGLE_LINE_COMMENT) { if (documentationTokens[0].type != TokenType.SINGLE_LINE_COMMENT) { documentationTokens.clear(); } } else { documentationTokens.clear(); } } documentationTokens.add(commentToken); } commentToken = commentToken.next; } if (documentationTokens.isEmpty) { return null; } List references = _parseCommentReferences(documentationTokens); return Comment.createDocumentationCommentWithReferences( documentationTokens, references); } /** * Parse a do statement. Return the do statement that was parsed. * * doStatement ::= * 'do' statement 'while' '(' expression ')' ';' */ Statement _parseDoStatement() { bool wasInLoop = _inLoop; _inLoop = true; try { Token doKeyword = _expectKeyword(Keyword.DO); Statement body = parseStatement2(); Token whileKeyword = _expectKeyword(Keyword.WHILE); Token leftParenthesis = _expect(TokenType.OPEN_PAREN); Expression condition = parseExpression2(); Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); Token semicolon = _expect(TokenType.SEMICOLON); return new DoStatement(doKeyword, body, whileKeyword, leftParenthesis, condition, rightParenthesis, semicolon); } finally { _inLoop = wasInLoop; } } /** * Parse an empty statement. Return the empty statement that was parsed. * * emptyStatement ::= * ';' */ Statement _parseEmptyStatement() => new EmptyStatement(getAndAdvance()); EnumConstantDeclaration _parseEnumConstantDeclaration() { CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata(); SimpleIdentifier name; if (_matchesIdentifier()) { name = parseSimpleIdentifier(); } else { name = _createSyntheticIdentifier(); } if (commentAndMetadata.metadata.isNotEmpty) { _reportErrorForNode(ParserErrorCode.ANNOTATION_ON_ENUM_CONSTANT, commentAndMetadata.metadata[0]); } return new EnumConstantDeclaration( commentAndMetadata.comment, commentAndMetadata.metadata, name); } /** * Parse an enum declaration. The [commentAndMetadata] is the metadata to be * associated with the member. Return the enum declaration that was parsed. * * enumType ::= * metadata 'enum' id '{' id (',' id)* (',')? '}' */ EnumDeclaration _parseEnumDeclaration(CommentAndMetadata commentAndMetadata) { Token keyword = _expectKeyword(Keyword.ENUM); SimpleIdentifier name = parseSimpleIdentifier(); Token leftBracket = null; List constants = new List(); Token rightBracket = null; if (_matches(TokenType.OPEN_CURLY_BRACKET)) { leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET); if (_matchesIdentifier() || _matches(TokenType.AT)) { constants.add(_parseEnumConstantDeclaration()); } else if (_matches(TokenType.COMMA) && _tokenMatchesIdentifier(_peek())) { constants.add(_parseEnumConstantDeclaration()); _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); } else { constants.add(_parseEnumConstantDeclaration()); _reportErrorForCurrentToken(ParserErrorCode.EMPTY_ENUM_BODY); } while (_optional(TokenType.COMMA)) { if (_matches(TokenType.CLOSE_CURLY_BRACKET)) { break; } constants.add(_parseEnumConstantDeclaration()); } rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET); } else { leftBracket = _createSyntheticToken(TokenType.OPEN_CURLY_BRACKET); rightBracket = _createSyntheticToken(TokenType.CLOSE_CURLY_BRACKET); _reportErrorForCurrentToken(ParserErrorCode.MISSING_ENUM_BODY); } return new EnumDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, name, leftBracket, constants, rightBracket); } /** * Parse an equality expression. Return the equality expression that was * parsed. * * equalityExpression ::= * relationalExpression (equalityOperator relationalExpression)? * | 'super' equalityOperator relationalExpression */ Expression _parseEqualityExpression() { Expression expression; if (_matchesKeyword(Keyword.SUPER) && _currentToken.next.type.isEqualityOperator) { expression = new SuperExpression(getAndAdvance()); } else { expression = _parseRelationalExpression(); } bool leftEqualityExpression = false; while (_currentToken.type.isEqualityOperator) { Token operator = getAndAdvance(); if (leftEqualityExpression) { _reportErrorForNode( ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND, expression); } expression = new BinaryExpression( expression, operator, _parseRelationalExpression()); leftEqualityExpression = true; } return expression; } /** * Parse an export directive. The [commentAndMetadata] is the metadata to be * associated with the directive. Return the export directive that was parsed. * * exportDirective ::= * metadata 'export' stringLiteral combinator*';' */ ExportDirective _parseExportDirective(CommentAndMetadata commentAndMetadata) { Token exportKeyword = _expectKeyword(Keyword.EXPORT); StringLiteral libraryUri = _parseUri(); List combinators = _parseCombinators(); Token semicolon = _expectSemicolon(); return new ExportDirective(commentAndMetadata.comment, commentAndMetadata.metadata, exportKeyword, libraryUri, combinators, semicolon); } /** * Parse a list of expressions. Return the expression that was parsed. * * expressionList ::= * expression (',' expression)* */ List _parseExpressionList() { List expressions = new List(); expressions.add(parseExpression2()); while (_optional(TokenType.COMMA)) { expressions.add(parseExpression2()); } return expressions; } /** * Parse the 'final', 'const', 'var' or type preceding a variable declaration. * The [optional] is `true` if the keyword and type are optional. Return the * 'final', 'const', 'var' or type that was parsed. * * finalConstVarOrType ::= * 'final' type? * | 'const' type? * | 'var' * | type */ FinalConstVarOrType _parseFinalConstVarOrType(bool optional) { Token keyword = null; TypeName type = null; if (_matchesKeyword(Keyword.FINAL) || _matchesKeyword(Keyword.CONST)) { keyword = getAndAdvance(); if (_isTypedIdentifier(_currentToken)) { type = parseTypeName(); } } else if (_matchesKeyword(Keyword.VAR)) { keyword = getAndAdvance(); } else { if (_isTypedIdentifier(_currentToken)) { type = parseReturnType(); } else if (!optional) { _reportErrorForCurrentToken( ParserErrorCode.MISSING_CONST_FINAL_VAR_OR_TYPE); } } return new FinalConstVarOrType(keyword, type); } /** * Parse a formal parameter. At most one of `isOptional` and `isNamed` can be * `true`. The [kind] is the kind of parameter being expected based on the * presence or absence of group delimiters. Return the formal parameter that * was parsed. * * defaultFormalParameter ::= * normalFormalParameter ('=' expression)? * * defaultNamedParameter ::= * normalFormalParameter (':' expression)? */ FormalParameter _parseFormalParameter(ParameterKind kind) { NormalFormalParameter parameter = parseNormalFormalParameter(); if (_matches(TokenType.EQ)) { Token seperator = getAndAdvance(); Expression defaultValue = parseExpression2(); if (kind == ParameterKind.NAMED) { _reportErrorForToken( ParserErrorCode.WRONG_SEPARATOR_FOR_NAMED_PARAMETER, seperator); } else if (kind == ParameterKind.REQUIRED) { _reportErrorForNode( ParserErrorCode.POSITIONAL_PARAMETER_OUTSIDE_GROUP, parameter); } return new DefaultFormalParameter( parameter, kind, seperator, defaultValue); } else if (_matches(TokenType.COLON)) { Token seperator = getAndAdvance(); Expression defaultValue = parseExpression2(); if (kind == ParameterKind.POSITIONAL) { _reportErrorForToken( ParserErrorCode.WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER, seperator); } else if (kind == ParameterKind.REQUIRED) { _reportErrorForNode( ParserErrorCode.NAMED_PARAMETER_OUTSIDE_GROUP, parameter); } return new DefaultFormalParameter( parameter, kind, seperator, defaultValue); } else if (kind != ParameterKind.REQUIRED) { return new DefaultFormalParameter(parameter, kind, null, null); } return parameter; } /** * Parse a for statement. Return the for statement that was parsed. * * forStatement ::= * 'for' '(' forLoopParts ')' statement * * forLoopParts ::= * forInitializerStatement expression? ';' expressionList? * | declaredIdentifier 'in' expression * | identifier 'in' expression * * forInitializerStatement ::= * localVariableDeclaration ';' * | expression? ';' */ Statement _parseForStatement() { bool wasInLoop = _inLoop; _inLoop = true; try { Token awaitKeyword = null; if (_matchesString(_AWAIT)) { awaitKeyword = getAndAdvance(); } Token forKeyword = _expectKeyword(Keyword.FOR); Token leftParenthesis = _expect(TokenType.OPEN_PAREN); VariableDeclarationList variableList = null; Expression initialization = null; if (!_matches(TokenType.SEMICOLON)) { CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata(); if (_matchesIdentifier() && (_tokenMatchesKeyword(_peek(), Keyword.IN) || _tokenMatches(_peek(), TokenType.COLON))) { List variables = new List(); SimpleIdentifier variableName = parseSimpleIdentifier(); variables.add( new VariableDeclaration(null, null, variableName, null, null)); variableList = new VariableDeclarationList(commentAndMetadata.comment, commentAndMetadata.metadata, null, null, variables); } else if (_isInitializedVariableDeclaration()) { variableList = _parseVariableDeclarationListAfterMetadata(commentAndMetadata); } else { initialization = parseExpression2(); } if (_matchesKeyword(Keyword.IN) || _matches(TokenType.COLON)) { if (_matches(TokenType.COLON)) { _reportErrorForCurrentToken(ParserErrorCode.COLON_IN_PLACE_OF_IN); } DeclaredIdentifier loopVariable = null; SimpleIdentifier identifier = null; if (variableList == null) { // We found: 'in' _reportErrorForCurrentToken( ParserErrorCode.MISSING_VARIABLE_IN_FOR_EACH); } else { NodeList variables = variableList.variables; if (variables.length > 1) { _reportErrorForCurrentToken( ParserErrorCode.MULTIPLE_VARIABLES_IN_FOR_EACH, [variables.length.toString()]); } VariableDeclaration variable = variables[0]; if (variable.initializer != null) { _reportErrorForCurrentToken( ParserErrorCode.INITIALIZED_VARIABLE_IN_FOR_EACH); } Token keyword = variableList.keyword; TypeName type = variableList.type; if (keyword != null || type != null) { loopVariable = new DeclaredIdentifier(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, type, variable.name); } else { if (!commentAndMetadata.metadata.isEmpty) { // TODO(jwren) metadata isn't allowed before the identifier in // "identifier in expression", add warning if commentAndMetadata // has content } identifier = variable.name; } } Token inKeyword = getAndAdvance(); Expression iterator = parseExpression2(); Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); Statement body = parseStatement2(); if (loopVariable == null) { return new ForEachStatement.con2(awaitKeyword, forKeyword, leftParenthesis, identifier, inKeyword, iterator, rightParenthesis, body); } return new ForEachStatement.con1(awaitKeyword, forKeyword, leftParenthesis, loopVariable, inKeyword, iterator, rightParenthesis, body); } } if (awaitKeyword != null) { _reportErrorForToken( ParserErrorCode.INVALID_AWAIT_IN_FOR, awaitKeyword); } Token leftSeparator = _expect(TokenType.SEMICOLON); Expression condition = null; if (!_matches(TokenType.SEMICOLON)) { condition = parseExpression2(); } Token rightSeparator = _expect(TokenType.SEMICOLON); List updaters = null; if (!_matches(TokenType.CLOSE_PAREN)) { updaters = _parseExpressionList(); } Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); Statement body = parseStatement2(); return new ForStatement(forKeyword, leftParenthesis, variableList, initialization, leftSeparator, condition, rightSeparator, updaters, rightParenthesis, body); } finally { _inLoop = wasInLoop; } } /** * Parse a function body. The [mayBeEmpty] is `true` if the function body is * allowed to be empty. The [emptyErrorCode] is the error code to report if * function body expected, but not found. The [inExpression] is `true` if the * function body is being parsed as part of an expression and therefore does * not have a terminating semicolon. Return the function body that was parsed. * * functionBody ::= * '=>' expression ';' * | block * * functionExpressionBody ::= * '=>' expression * | block */ FunctionBody _parseFunctionBody( bool mayBeEmpty, ParserErrorCode emptyErrorCode, bool inExpression) { bool wasInAsync = _inAsync; bool wasInGenerator = _inGenerator; bool wasInLoop = _inLoop; bool wasInSwitch = _inSwitch; _inAsync = false; _inGenerator = false; _inLoop = false; _inSwitch = false; try { if (_matches(TokenType.SEMICOLON)) { if (!mayBeEmpty) { _reportErrorForCurrentToken(emptyErrorCode); } return new EmptyFunctionBody(getAndAdvance()); } else if (_matchesString(_NATIVE)) { Token nativeToken = getAndAdvance(); StringLiteral stringLiteral = null; if (_matches(TokenType.STRING)) { stringLiteral = parseStringLiteral(); } return new NativeFunctionBody( nativeToken, stringLiteral, _expect(TokenType.SEMICOLON)); } Token keyword = null; Token star = null; if (_matchesString(ASYNC)) { keyword = getAndAdvance(); if (_matches(TokenType.STAR)) { star = getAndAdvance(); _inGenerator = true; } _inAsync = true; } else if (_matchesString(SYNC)) { keyword = getAndAdvance(); if (_matches(TokenType.STAR)) { star = getAndAdvance(); _inGenerator = true; } } if (_matches(TokenType.FUNCTION)) { if (keyword != null) { if (!_tokenMatchesString(keyword, ASYNC)) { _reportErrorForToken(ParserErrorCode.INVALID_SYNC, keyword); keyword = null; } else if (star != null) { _reportErrorForToken( ParserErrorCode.INVALID_STAR_AFTER_ASYNC, star); } } Token functionDefinition = getAndAdvance(); if (_matchesKeyword(Keyword.RETURN)) { _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]); _advance(); } Expression expression = parseExpression2(); Token semicolon = null; if (!inExpression) { semicolon = _expect(TokenType.SEMICOLON); } if (!_parseFunctionBodies) { return new EmptyFunctionBody( _createSyntheticToken(TokenType.SEMICOLON)); } return new ExpressionFunctionBody( keyword, functionDefinition, expression, semicolon); } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) { if (keyword != null) { if (_tokenMatchesString(keyword, SYNC) && star == null) { _reportErrorForToken( ParserErrorCode.MISSING_STAR_AFTER_SYNC, keyword); } } if (!_parseFunctionBodies) { _skipBlock(); return new EmptyFunctionBody( _createSyntheticToken(TokenType.SEMICOLON)); } return new BlockFunctionBody(keyword, star, parseBlock()); } else { // Invalid function body _reportErrorForCurrentToken(emptyErrorCode); return new EmptyFunctionBody( _createSyntheticToken(TokenType.SEMICOLON)); } } finally { _inAsync = wasInAsync; _inGenerator = wasInGenerator; _inLoop = wasInLoop; _inSwitch = wasInSwitch; } } /** * 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 keyword = null; bool isGetter = false; if (_matchesKeyword(Keyword.GET) && !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) { keyword = getAndAdvance(); isGetter = true; } else if (_matchesKeyword(Keyword.SET) && !_tokenMatches(_peek(), TokenType.OPEN_PAREN)) { keyword = getAndAdvance(); } SimpleIdentifier name = parseSimpleIdentifier(); FormalParameterList parameters = null; if (!isGetter) { if (_matches(TokenType.OPEN_PAREN)) { parameters = parseFormalParameterList(); _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); parseFormalParameterList(); } 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, keyword, name, new FunctionExpression(parameters, body)); } /** * Parse a function declaration statement. Return the function declaration * statement that was parsed. * * functionDeclarationStatement ::= * functionSignature functionBody */ Statement _parseFunctionDeclarationStatement() { Modifiers modifiers = _parseModifiers(); _validateModifiersForFunctionDeclarationStatement(modifiers); return _parseFunctionDeclarationStatementAfterReturnType( _parseCommentAndMetadata(), _parseOptionalReturnType()); } /** * Parse a function declaration statement. The [commentAndMetadata] is the * documentation comment and metadata to be associated with the declaration. * The [returnType] is the return type, or `null` if there is no return type. * 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 as KeywordToken).keyword == Keyword.GET) { _reportErrorForToken( ParserErrorCode.GETTER_IN_FUNCTION, propertyKeyword); } else { _reportErrorForToken( ParserErrorCode.SETTER_IN_FUNCTION, propertyKeyword); } } return new FunctionDeclarationStatement(declaration); } /** * 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 */ FunctionTypeAlias _parseFunctionTypeAlias( CommentAndMetadata commentAndMetadata, Token keyword) { TypeName returnType = null; if (hasReturnTypeInTypeAlias) { returnType = parseReturnType(); } SimpleIdentifier name = parseSimpleIdentifier(); TypeParameterList typeParameters = null; if (_matches(TokenType.LT)) { typeParameters = parseTypeParameterList(); } if (_matches(TokenType.SEMICOLON) || _matches(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 (!_matches(TokenType.OPEN_PAREN)) { _reportErrorForCurrentToken(ParserErrorCode.MISSING_TYPEDEF_PARAMETERS); // TODO(brianwilkerson) Recover from this error. At the very least we // should skip to the start of the next valid compilation unit member, // allowing for the possibility of finding the typedef parameters before // that point. 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)); } FormalParameterList parameters = parseFormalParameterList(); _validateFormalParameterList(parameters); Token semicolon = _expect(TokenType.SEMICOLON); return new FunctionTypeAlias(commentAndMetadata.comment, commentAndMetadata.metadata, keyword, returnType, name, typeParameters, parameters, semicolon); } /** * 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. * * getter ::= * getterSignature functionBody? * * getterSignature ::= * 'external'? 'static'? returnType? 'get' identifier */ MethodDeclaration _parseGetter(CommentAndMetadata commentAndMetadata, Token externalKeyword, Token staticKeyword, TypeName returnType) { Token propertyKeyword = _expectKeyword(Keyword.GET); SimpleIdentifier name = parseSimpleIdentifier(); 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, body); } /** * Parse a list of identifiers. Return the list of identifiers that were * parsed. * * identifierList ::= * identifier (',' identifier)* */ List _parseIdentifierList() { List identifiers = new List(); identifiers.add(parseSimpleIdentifier()); while (_matches(TokenType.COMMA)) { _advance(); identifiers.add(parseSimpleIdentifier()); } return identifiers; } /** * Parse an if statement. Return the if statement that was parsed. * * ifStatement ::= * 'if' '(' expression ')' statement ('else' statement)? */ Statement _parseIfStatement() { Token ifKeyword = _expectKeyword(Keyword.IF); Token leftParenthesis = _expect(TokenType.OPEN_PAREN); Expression condition = parseExpression2(); Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); Statement thenStatement = parseStatement2(); Token elseKeyword = null; Statement elseStatement = null; if (_matchesKeyword(Keyword.ELSE)) { elseKeyword = getAndAdvance(); elseStatement = parseStatement2(); } return new IfStatement(ifKeyword, leftParenthesis, condition, rightParenthesis, thenStatement, elseKeyword, elseStatement); } /** * Parse an import directive. The [commentAndMetadata] is the metadata to be * associated with the directive. Return the import directive that was parsed. * * importDirective ::= * metadata 'import' stringLiteral (deferred)? ('as' identifier)? combinator*';' */ ImportDirective _parseImportDirective(CommentAndMetadata commentAndMetadata) { Token importKeyword = _expectKeyword(Keyword.IMPORT); StringLiteral libraryUri = _parseUri(); Token deferredToken = null; Token asToken = null; SimpleIdentifier prefix = null; if (_matchesKeyword(Keyword.DEFERRED)) { deferredToken = getAndAdvance(); } if (_matchesKeyword(Keyword.AS)) { asToken = getAndAdvance(); prefix = parseSimpleIdentifier(); } 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(); } } } List combinators = _parseCombinators(); Token semicolon = _expectSemicolon(); return new ImportDirective(commentAndMetadata.comment, commentAndMetadata.metadata, importKeyword, libraryUri, 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 = parseArgumentList(); return new InstanceCreationExpression( keyword, constructorName, argumentList); } /** * Parse a library directive. The [commentAndMetadata] is the metadata to be * associated with the directive. Return the library directive that was * parsed. * * libraryDirective ::= * metadata 'library' identifier ';' */ LibraryDirective _parseLibraryDirective( CommentAndMetadata commentAndMetadata) { Token keyword = _expectKeyword(Keyword.LIBRARY); 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 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. * * libraryName ::= * libraryIdentifier */ LibraryIdentifier _parseLibraryName( ParserErrorCode missingNameError, Token missingNameToken) { if (_matchesIdentifier()) { return parseLibraryIdentifier(); } else if (_matches(TokenType.STRING)) { // TODO(brianwilkerson) Recovery: This should be extended to handle // arbitrary tokens until we can find a token that can start a compilation // unit member. StringLiteral string = parseStringLiteral(); _reportErrorForNode(ParserErrorCode.NON_IDENTIFIER_LIBRARY_NAME, string); } else { _reportErrorForToken(missingNameError, missingNameToken); } List components = new List(); components.add(_createSyntheticIdentifier()); 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. * * listLiteral ::= * 'const'? typeArguments? '[' (expressionList ','?)? ']' */ ListLiteral _parseListLiteral( Token modifier, TypeArgumentList typeArguments) { // may be empty list literal if (_matches(TokenType.INDEX)) { 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); } // open Token leftBracket = _expect(TokenType.OPEN_SQUARE_BRACKET); if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) { return new ListLiteral( modifier, typeArguments, leftBracket, null, getAndAdvance()); } bool wasInInitializer = _inInitializer; _inInitializer = false; try { List elements = new List(); elements.add(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; } } /** * Parse a list or map literal. The [modifier] is the 'const' modifier * appearing before the literal, or `null` if there is no modifier. Return the * list or map literal that was parsed. * * listOrMapLiteral ::= * listLiteral * | mapLiteral */ TypedLiteral _parseListOrMapLiteral(Token modifier) { TypeArgumentList typeArguments = null; if (_matches(TokenType.LT)) { typeArguments = parseTypeArgumentList(); } if (_matches(TokenType.OPEN_CURLY_BRACKET)) { return _parseMapLiteral(modifier, typeArguments); } else if (_matches(TokenType.OPEN_SQUARE_BRACKET) || _matches(TokenType.INDEX)) { return _parseListLiteral(modifier, typeArguments); } _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL); return new ListLiteral(modifier, typeArguments, _createSyntheticToken(TokenType.OPEN_SQUARE_BRACKET), null, _createSyntheticToken(TokenType.CLOSE_SQUARE_BRACKET)); } /** * Parse a logical and expression. Return the logical and expression that was * parsed. * * logicalAndExpression ::= * equalityExpression ('&&' equalityExpression)* */ Expression _parseLogicalAndExpression() { Expression expression = _parseEqualityExpression(); while (_matches(TokenType.AMPERSAND_AMPERSAND)) { Token operator = getAndAdvance(); expression = new BinaryExpression( expression, operator, _parseEqualityExpression()); } 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. * * mapLiteral ::= * 'const'? typeArguments? '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}' */ MapLiteral _parseMapLiteral(Token modifier, TypeArgumentList typeArguments) { Token leftBracket = _expect(TokenType.OPEN_CURLY_BRACKET); List entries = new List(); if (_matches(TokenType.CLOSE_CURLY_BRACKET)) { return new MapLiteral( modifier, typeArguments, leftBracket, entries, getAndAdvance()); } bool wasInInitializer = _inInitializer; _inInitializer = false; try { entries.add(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 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. * * functionDeclaration ::= * ('external' 'static'?)? functionSignature functionBody * | 'external'? functionSignature ';' */ MethodDeclaration _parseMethodDeclarationAfterParameters( CommentAndMetadata commentAndMetadata, Token externalKeyword, Token staticKeyword, TypeName returnType, SimpleIdentifier name, FormalParameterList parameters) { FunctionBody body = _parseFunctionBody( externalKeyword != null || staticKeyword == null, ParserErrorCode.MISSING_FUNCTION_BODY, false); if (externalKeyword != null) { if (body is! EmptyFunctionBody) { _reportErrorForNode(ParserErrorCode.EXTERNAL_METHOD_WITH_BODY, body); } } else if (staticKeyword != null) { if (body is EmptyFunctionBody) { _reportErrorForNode(ParserErrorCode.ABSTRACT_STATIC_METHOD, body); } } return new MethodDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, staticKeyword, returnType, null, null, name, 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(); FormalParameterList parameters; if (!_matches(TokenType.OPEN_PAREN) && (_matches(TokenType.OPEN_CURLY_BRACKET) || _matches(TokenType.FUNCTION))) { _reportErrorForToken( ParserErrorCode.MISSING_METHOD_PARAMETERS, _currentToken.previous); parameters = new FormalParameterList( _createSyntheticToken(TokenType.OPEN_PAREN), null, null, null, _createSyntheticToken(TokenType.CLOSE_PAREN)); } else { parameters = parseFormalParameterList(); } _validateFormalParameterList(parameters); return _parseMethodDeclarationAfterParameters(commentAndMetadata, externalKeyword, staticKeyword, returnType, methodName, 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. * * modifiers ::= * ('abstract' | 'const' | 'external' | 'factory' | 'final' | 'static' | 'var')* */ Modifiers _parseModifiers() { Modifiers modifiers = new Modifiers(); bool progress = true; while (progress) { if (_tokenMatches(_peek(), TokenType.PERIOD) || _tokenMatches(_peek(), TokenType.LT) || _tokenMatches(_peek(), TokenType.OPEN_PAREN)) { return modifiers; } if (_matchesKeyword(Keyword.ABSTRACT)) { if (modifiers.abstractKeyword != null) { _reportErrorForCurrentToken( ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]); _advance(); } else { modifiers.abstractKeyword = getAndAdvance(); } } else if (_matchesKeyword(Keyword.CONST)) { if (modifiers.constKeyword != null) { _reportErrorForCurrentToken( ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]); _advance(); } else { modifiers.constKeyword = getAndAdvance(); } } else if (_matchesKeyword(Keyword.EXTERNAL) && !_tokenMatches(_peek(), TokenType.PERIOD) && !_tokenMatches(_peek(), TokenType.LT)) { if (modifiers.externalKeyword != null) { _reportErrorForCurrentToken( ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]); _advance(); } else { modifiers.externalKeyword = getAndAdvance(); } } else if (_matchesKeyword(Keyword.FACTORY) && !_tokenMatches(_peek(), TokenType.PERIOD) && !_tokenMatches(_peek(), TokenType.LT)) { if (modifiers.factoryKeyword != null) { _reportErrorForCurrentToken( ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]); _advance(); } else { modifiers.factoryKeyword = getAndAdvance(); } } else if (_matchesKeyword(Keyword.FINAL)) { if (modifiers.finalKeyword != null) { _reportErrorForCurrentToken( ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]); _advance(); } else { modifiers.finalKeyword = getAndAdvance(); } } else if (_matchesKeyword(Keyword.STATIC) && !_tokenMatches(_peek(), TokenType.PERIOD) && !_tokenMatches(_peek(), TokenType.LT)) { if (modifiers.staticKeyword != null) { _reportErrorForCurrentToken( ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]); _advance(); } else { modifiers.staticKeyword = getAndAdvance(); } } else if (_matchesKeyword(Keyword.VAR)) { if (modifiers.varKeyword != null) { _reportErrorForCurrentToken( ParserErrorCode.DUPLICATED_MODIFIER, [_currentToken.lexeme]); _advance(); } else { modifiers.varKeyword = getAndAdvance(); } } else { progress = false; } } return modifiers; } /** * Parse a multiplicative expression. Return the multiplicative expression * that was parsed. * * multiplicativeExpression ::= * unaryExpression (multiplicativeOperator unaryExpression)* * | 'super' (multiplicativeOperator unaryExpression)+ */ Expression _parseMultiplicativeExpression() { Expression expression; if (_matchesKeyword(Keyword.SUPER) && _currentToken.next.type.isMultiplicativeOperator) { expression = new SuperExpression(getAndAdvance()); } else { expression = _parseUnaryExpression(); } while (_currentToken.type.isMultiplicativeOperator) { Token operator = getAndAdvance(); expression = new BinaryExpression(expression, operator, _parseUnaryExpression()); } return expression; } /** * Parse a class native clause. Return the native clause that was parsed. * * classNativeClause ::= * 'native' name */ NativeClause _parseNativeClause() { Token keyword = getAndAdvance(); StringLiteral name = parseStringLiteral(); return new NativeClause(keyword, name); } /** * Parse a new expression. Return the new expression that was parsed. * * newExpression ::= * instanceCreationExpression */ InstanceCreationExpression _parseNewExpression() => _parseInstanceCreationExpression(_expectKeyword(Keyword.NEW)); /** * Parse a non-labeled statement. Return the non-labeled statement that was * parsed. * * nonLabeledStatement ::= * block * | assertStatement * | breakStatement * | continueStatement * | doStatement * | forStatement * | ifStatement * | returnStatement * | switchStatement * | tryStatement * | whileStatement * | variableDeclarationList ';' * | expressionStatement * | functionSignature functionBody */ Statement _parseNonLabeledStatement() { // TODO(brianwilkerson) Pass the comment and metadata on where appropriate. CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata(); if (_matches(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 (_matches(TokenType.KEYWORD) && !(_currentToken as KeywordToken).keyword.isPseudoKeyword) { Keyword keyword = (_currentToken as KeywordToken).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 = parseReturnType(); if (_matchesIdentifier() && _peek().matchesAny([ TokenType.OPEN_PAREN, TokenType.OPEN_CURLY_BRACKET, TokenType.FUNCTION ])) { return _parseFunctionDeclarationStatementAfterReturnType( commentAndMetadata, returnType); } else { // // We have found an error of some kind. Try to recover. // if (_matchesIdentifier()) { if (_peek().matchesAny( [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) { if (_peek().matchesAny([ TokenType.LT, TokenType.OPEN_CURLY_BRACKET, TokenType.OPEN_SQUARE_BRACKET, TokenType.INDEX ])) { return new ExpressionStatement( parseExpression2(), _expect(TokenType.SEMICOLON)); } else if (_tokenMatches(_peek(), TokenType.IDENTIFIER)) { Token afterType = _skipTypeName(_peek()); 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(); } 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); } return statement; } else if (_matches(TokenType.SEMICOLON)) { return _parseEmptyStatement(); } else if (_isInitializedVariableDeclaration()) { return _parseVariableDeclarationStatementAfterMetadata( commentAndMetadata); } else if (_isFunctionDeclaration()) { return _parseFunctionDeclarationStatement(); } else if (_matches(TokenType.CLOSE_CURLY_BRACKET)) { _reportErrorForCurrentToken(ParserErrorCode.MISSING_STATEMENT); return new EmptyStatement(_createSyntheticToken(TokenType.SEMICOLON)); } else { return new ExpressionStatement( parseExpression2(), _expect(TokenType.SEMICOLON)); } } /** * Parse an operator declaration. The [commentAndMetadata] is the * documentation comment and metadata to be associated with the declaration. * The [externalKeyword] is the 'external' token. The [returnType] is the * return type that has already been parsed, or `null` if there was no return * type. Return the operator declaration that was parsed. * * operatorDeclaration ::= * operatorSignature (';' | functionBody) * * operatorSignature ::= * 'external'? returnType? 'operator' operator formalParameterList */ MethodDeclaration _parseOperator(CommentAndMetadata commentAndMetadata, Token externalKeyword, TypeName returnType) { Token operatorKeyword; if (_matchesKeyword(Keyword.OPERATOR)) { operatorKeyword = getAndAdvance(); } else { _reportErrorForToken( ParserErrorCode.MISSING_KEYWORD_OPERATOR, _currentToken); operatorKeyword = _createSyntheticKeyword(Keyword.OPERATOR); } if (!_currentToken.isUserDefinableOperator) { _reportErrorForCurrentToken( ParserErrorCode.NON_USER_DEFINABLE_OPERATOR, [_currentToken.lexeme]); } SimpleIdentifier name = new SimpleIdentifier(getAndAdvance()); if (_matches(TokenType.EQ)) { Token previous = _currentToken.previous; if ((_tokenMatches(previous, TokenType.EQ_EQ) || _tokenMatches(previous, TokenType.BANG_EQ)) && _currentToken.offset == previous.offset + 2) { _reportErrorForCurrentToken(ParserErrorCode.INVALID_OPERATOR, ["${previous.lexeme}${_currentToken.lexeme}"]); _advance(); } } FormalParameterList parameters = parseFormalParameterList(); _validateFormalParameterList(parameters); FunctionBody body = _parseFunctionBody(true, ParserErrorCode.MISSING_FUNCTION_BODY, false); if (externalKeyword != null && body is! EmptyFunctionBody) { _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_OPERATOR_WITH_BODY); } return new MethodDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, null, returnType, null, operatorKeyword, name, parameters, body); } /** * Parse a return type if one is given, otherwise return `null` without * advancing. Return the return type that was parsed. */ TypeName _parseOptionalReturnType() { if (_matchesKeyword(Keyword.VOID)) { return parseReturnType(); } else if (_matchesIdentifier() && !_matchesKeyword(Keyword.GET) && !_matchesKeyword(Keyword.SET) && !_matchesKeyword(Keyword.OPERATOR) && (_tokenMatchesIdentifier(_peek()) || _tokenMatches(_peek(), TokenType.LT))) { return parseReturnType(); } else if (_matchesIdentifier() && _tokenMatches(_peek(), TokenType.PERIOD) && _tokenMatchesIdentifier(_peekAt(2)) && (_tokenMatchesIdentifier(_peekAt(3)) || _tokenMatches(_peekAt(3), TokenType.LT))) { return parseReturnType(); } return null; } /** * 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. * * partDirective ::= * metadata 'part' stringLiteral ';' * * partOfDirective ::= * metadata 'part' 'of' identifier ';' */ Directive _parsePartDirective(CommentAndMetadata commentAndMetadata) { Token partKeyword = _expectKeyword(Keyword.PART); if (_matchesString(_OF)) { Token ofKeyword = getAndAdvance(); LibraryIdentifier libraryName = _parseLibraryName( ParserErrorCode.MISSING_NAME_IN_PART_OF_DIRECTIVE, ofKeyword); Token semicolon = _expect(TokenType.SEMICOLON); return new PartOfDirective(commentAndMetadata.comment, commentAndMetadata.metadata, partKeyword, ofKeyword, libraryName, semicolon); } StringLiteral partUri = _parseUri(); Token semicolon = _expect(TokenType.SEMICOLON); return new PartDirective(commentAndMetadata.comment, commentAndMetadata.metadata, partKeyword, partUri, semicolon); } /** * 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); if (_matches(TokenType.OPEN_SQUARE_BRACKET) || _matches(TokenType.PERIOD) || _matches(TokenType.QUESTION_PERIOD) || _matches(TokenType.OPEN_PAREN)) { do { if (_matches(TokenType.OPEN_PAREN)) { ArgumentList argumentList = parseArgumentList(); if (operand is PropertyAccess) { PropertyAccess access = operand as PropertyAccess; operand = new MethodInvocation(access.target, access.operator, access.propertyName, argumentList); } else { operand = new FunctionExpressionInvocation(operand, argumentList); } } else { operand = _parseAssignableSelector(operand, true); } } while (_matches(TokenType.OPEN_SQUARE_BRACKET) || _matches(TokenType.PERIOD) || _matches(TokenType.QUESTION_PERIOD) || _matches(TokenType.OPEN_PAREN)); return operand; } if (!_currentToken.type.isIncrementOperator) { return operand; } _ensureAssignable(operand); Token operator = getAndAdvance(); return new PostfixExpression(operand, operator); } /** * Parse a primary expression. Return the primary expression that was parsed. * * primary ::= * thisExpression * | 'super' unconditionalAssignableSelector * | functionExpression * | literal * | identifier * | newExpression * | constObjectExpression * | '(' expression ')' * | argumentDefinitionTest * * literal ::= * nullLiteral * | booleanLiteral * | numericLiteral * | stringLiteral * | symbolLiteral * | mapLiteral * | listLiteral */ Expression _parsePrimaryExpression() { if (_matchesKeyword(Keyword.THIS)) { return new ThisExpression(getAndAdvance()); } else if (_matchesKeyword(Keyword.SUPER)) { // TODO(paulberry): verify with Gilad that "super" must be followed by // unconditionalAssignableSelector in this case. return _parseAssignableSelector( new SuperExpression(getAndAdvance()), false, allowConditional: false); } else if (_matchesKeyword(Keyword.NULL)) { return new NullLiteral(getAndAdvance()); } else if (_matchesKeyword(Keyword.FALSE)) { return new BooleanLiteral(getAndAdvance(), false); } else if (_matchesKeyword(Keyword.TRUE)) { return new BooleanLiteral(getAndAdvance(), true); } else if (_matches(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 (_matches(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 (_matches(TokenType.INT)) { Token token = getAndAdvance(); int value = null; try { value = int.parse(token.lexeme); } on FormatException { // The invalid format should have been reported by the scanner. } return new IntegerLiteral(token, value); } else if (_matches(TokenType.STRING)) { return parseStringLiteral(); } else if (_matches(TokenType.OPEN_CURLY_BRACKET)) { return _parseMapLiteral(null, null); } else if (_matches(TokenType.OPEN_SQUARE_BRACKET) || _matches(TokenType.INDEX)) { return _parseListLiteral(null, null); } else if (_matchesIdentifier()) { // TODO(brianwilkerson) The code below was an attempt to recover from an // error case, but it needs to be applied as a recovery only after we // know that parsing it as an identifier doesn't work. Leaving the code as // a reminder of how to recover. // if (isFunctionExpression(peek())) { // // // // Function expressions were allowed to have names at one point, but this is now illegal. // // // reportError(ParserErrorCode.NAMED_FUNCTION_EXPRESSION, getAndAdvance()); // return parseFunctionExpression(); // } return parsePrefixedIdentifier(); } else if (_matchesKeyword(Keyword.NEW)) { return _parseNewExpression(); } else if (_matchesKeyword(Keyword.CONST)) { return _parseConstExpression(); } else if (_matches(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 (_matches(TokenType.LT)) { return _parseListOrMapLiteral(null); } else if (_matches(TokenType.QUESTION) && _tokenMatches(_peek(), TokenType.IDENTIFIER)) { _reportErrorForCurrentToken( ParserErrorCode.UNEXPECTED_TOKEN, [_currentToken.lexeme]); _advance(); return _parsePrimaryExpression(); } else if (_matchesKeyword(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 (_matches(TokenType.HASH)) { return _parseSymbolLiteral(); } else { _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); return _createSyntheticIdentifier(); } } /** * Parse a redirecting constructor invocation. Return the redirecting * constructor invocation that was parsed. * * redirectingConstructorInvocation ::= * 'this' ('.' identifier)? arguments */ RedirectingConstructorInvocation _parseRedirectingConstructorInvocation() { Token keyword = _expectKeyword(Keyword.THIS); Token period = null; SimpleIdentifier constructorName = null; if (_matches(TokenType.PERIOD)) { period = getAndAdvance(); constructorName = parseSimpleIdentifier(); } ArgumentList argumentList = parseArgumentList(); return new RedirectingConstructorInvocation( keyword, period, constructorName, argumentList); } /** * Parse a relational expression. Return the relational expression that was * parsed. * * relationalExpression ::= * bitwiseOrExpression ('is' '!'? type | 'as' type | relationalOperator bitwiseOrExpression)? * | 'super' relationalOperator bitwiseOrExpression */ Expression _parseRelationalExpression() { if (_matchesKeyword(Keyword.SUPER) && _currentToken.next.type.isRelationalOperator) { Expression expression = new SuperExpression(getAndAdvance()); Token operator = getAndAdvance(); expression = new BinaryExpression( expression, operator, parseBitwiseOrExpression()); return expression; } Expression expression = parseBitwiseOrExpression(); if (_matchesKeyword(Keyword.AS)) { Token asOperator = getAndAdvance(); expression = new AsExpression(expression, asOperator, parseTypeName()); } else if (_matchesKeyword(Keyword.IS)) { Token isOperator = getAndAdvance(); Token notOperator = null; if (_matches(TokenType.BANG)) { notOperator = getAndAdvance(); } expression = new IsExpression( expression, isOperator, notOperator, parseTypeName()); } else if (_currentToken.type.isRelationalOperator) { Token operator = getAndAdvance(); expression = new BinaryExpression( expression, operator, parseBitwiseOrExpression()); } return expression; } /** * Parse a rethrow expression. Return the rethrow expression that was parsed. * * rethrowExpression ::= * 'rethrow' */ Expression _parseRethrowExpression() => new RethrowExpression(_expectKeyword(Keyword.RETHROW)); /** * Parse a return statement. Return the return statement that was parsed. * * returnStatement ::= * 'return' expression? ';' */ Statement _parseReturnStatement() { Token returnKeyword = _expectKeyword(Keyword.RETURN); 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 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. * * setter ::= * setterSignature functionBody? * * setterSignature ::= * 'external'? 'static'? returnType? 'set' identifier formalParameterList */ MethodDeclaration _parseSetter(CommentAndMetadata commentAndMetadata, Token externalKeyword, Token staticKeyword, TypeName returnType) { Token propertyKeyword = _expectKeyword(Keyword.SET); SimpleIdentifier name = parseSimpleIdentifier(); FormalParameterList parameters = parseFormalParameterList(); _validateFormalParameterList(parameters); FunctionBody body = _parseFunctionBody( externalKeyword != null || staticKeyword == null, ParserErrorCode.STATIC_SETTER_WITHOUT_BODY, false); if (externalKeyword != null && body is! EmptyFunctionBody) { _reportErrorForCurrentToken(ParserErrorCode.EXTERNAL_SETTER_WITH_BODY); } return new MethodDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, externalKeyword, staticKeyword, returnType, propertyKeyword, null, name, parameters, body); } /** * Parse a shift expression. Return the shift expression that was parsed. * * shiftExpression ::= * additiveExpression (shiftOperator additiveExpression)* * | 'super' (shiftOperator additiveExpression)+ */ Expression _parseShiftExpression() { Expression expression; if (_matchesKeyword(Keyword.SUPER) && _currentToken.next.type.isShiftOperator) { expression = new SuperExpression(getAndAdvance()); } else { expression = _parseAdditiveExpression(); } while (_currentToken.type.isShiftOperator) { Token operator = getAndAdvance(); expression = new BinaryExpression( expression, operator, _parseAdditiveExpression()); } return expression; } /** * Parse a list of statements within a switch statement. Return the statements * that were parsed. * * statements ::= * statement* */ List _parseStatementList() { List statements = new List(); Token statementStart = _currentToken; while (!_matches(TokenType.EOF) && !_matches(TokenType.CLOSE_CURLY_BRACKET) && !_isSwitchMember()) { statements.add(parseStatement2()); if (identical(_currentToken, statementStart)) { _reportErrorForToken(ParserErrorCode.UNEXPECTED_TOKEN, _currentToken, [_currentToken.lexeme]); _advance(); } statementStart = _currentToken; } return statements; } /** * Parse a string literal that contains interpolations. Return the string * literal that was parsed. */ StringInterpolation _parseStringInterpolation(Token string) { List elements = new List(); bool hasMore = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION) || _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER); elements.add(new InterpolationString( string, _computeStringValue(string.lexeme, true, !hasMore))); while (hasMore) { if (_matches(TokenType.STRING_INTERPOLATION_EXPRESSION)) { Token openToken = getAndAdvance(); bool wasInInitializer = _inInitializer; _inInitializer = false; try { Expression expression = parseExpression2(); Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET); elements.add( new InterpolationExpression(openToken, expression, rightBracket)); } finally { _inInitializer = wasInInitializer; } } else { Token openToken = getAndAdvance(); Expression expression = null; if (_matchesKeyword(Keyword.THIS)) { expression = new ThisExpression(getAndAdvance()); } else { expression = parseSimpleIdentifier(); } elements.add(new InterpolationExpression(openToken, expression, null)); } if (_matches(TokenType.STRING)) { string = getAndAdvance(); hasMore = _matches(TokenType.STRING_INTERPOLATION_EXPRESSION) || _matches(TokenType.STRING_INTERPOLATION_IDENTIFIER); elements.add(new InterpolationString( string, _computeStringValue(string.lexeme, false, !hasMore))); } else { hasMore = false; } } return new StringInterpolation(elements); } /** * Parse a super constructor invocation. Return the super constructor * invocation that was parsed. * * superConstructorInvocation ::= * 'super' ('.' identifier)? arguments */ SuperConstructorInvocation _parseSuperConstructorInvocation() { Token keyword = _expectKeyword(Keyword.SUPER); Token period = null; SimpleIdentifier constructorName = null; if (_matches(TokenType.PERIOD)) { period = getAndAdvance(); constructorName = parseSimpleIdentifier(); } ArgumentList argumentList = parseArgumentList(); 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 definedLabels = new HashSet(); 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 members = new List(); while (!_matches(TokenType.EOF) && !_matches(TokenType.CLOSE_CURLY_BRACKET)) { List labels = new List(); while ( _matchesIdentifier() && _tokenMatches(_peek(), TokenType.COLON)) { SimpleIdentifier identifier = parseSimpleIdentifier(); 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 = _expect(TokenType.COLON); labels.add(new Label(identifier, colon)); } if (_matchesKeyword(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 (_matchesKeyword(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); while (!_matches(TokenType.EOF) && !_matches(TokenType.CLOSE_CURLY_BRACKET) && !_matchesKeyword(Keyword.CASE) && !_matchesKeyword(Keyword.DEFAULT)) { _advance(); } } } 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. * * symbolLiteral ::= * '#' identifier ('.' identifier)* */ SymbolLiteral _parseSymbolLiteral() { Token poundSign = getAndAdvance(); List components = new List(); if (_matchesIdentifier()) { components.add(getAndAdvance()); while (_matches(TokenType.PERIOD)) { _advance(); 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 (_tokenMatchesKeyword(_currentToken, 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. * * throwExpression ::= * 'throw' expression */ Expression _parseThrowExpression() { Token keyword = _expectKeyword(Keyword.THROW); if (_matches(TokenType.SEMICOLON) || _matches(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. * * throwExpressionWithoutCascade ::= * 'throw' expressionWithoutCascade */ Expression _parseThrowExpressionWithoutCascade() { Token keyword = _expectKeyword(Keyword.THROW); if (_matches(TokenType.SEMICOLON) || _matches(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. * * tryStatement ::= * 'try' block (onPart+ finallyPart? | finallyPart) * * onPart ::= * catchPart block * | 'on' type catchPart? block * * catchPart ::= * 'catch' '(' identifier (',' identifier)? ')' * * finallyPart ::= * 'finally' block */ Statement _parseTryStatement() { Token tryKeyword = _expectKeyword(Keyword.TRY); Block body = parseBlock(); List catchClauses = new List(); Block finallyClause = null; while (_matchesString(_ON) || _matchesKeyword(Keyword.CATCH)) { Token onKeyword = null; TypeName exceptionType = null; if (_matchesString(_ON)) { onKeyword = getAndAdvance(); exceptionType = parseTypeName(); } 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(); if (_matches(TokenType.COMMA)) { comma = getAndAdvance(); stackTraceParameter = parseSimpleIdentifier(); } rightParenthesis = _expect(TokenType.CLOSE_PAREN); } Block catchBody = parseBlock(); catchClauses.add(new CatchClause(onKeyword, exceptionType, catchKeyword, leftParenthesis, exceptionParameter, comma, stackTraceParameter, rightParenthesis, catchBody)); } Token finallyKeyword = null; if (_matchesKeyword(Keyword.FINALLY)) { finallyKeyword = getAndAdvance(); finallyClause = parseBlock(); } else { if (catchClauses.isEmpty) { _reportErrorForCurrentToken(ParserErrorCode.MISSING_CATCH_OR_FINALLY); } } return new TryStatement( tryKeyword, body, catchClauses, finallyKeyword, finallyClause); } /** * Parse a type alias. The [commentAndMetadata] is the metadata to be * associated with the member. Return the type alias that was parsed. * * 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 = _expectKeyword(Keyword.TYPEDEF); 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); } /** * Parse a unary expression. Return the unary expression that was parsed. * * unaryExpression ::= * prefixOperator unaryExpression * | awaitExpression * | postfixExpression * | unaryOperator 'super' * | '-' 'super' * | incrementOperator assignableExpression */ Expression _parseUnaryExpression() { if (_matches(TokenType.MINUS) || _matches(TokenType.BANG) || _matches(TokenType.TILDE)) { Token operator = getAndAdvance(); if (_matchesKeyword(Keyword.SUPER)) { if (_tokenMatches(_peek(), TokenType.OPEN_SQUARE_BRACKET) || _tokenMatches(_peek(), 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)) { if (_tokenMatches(_peek(), TokenType.OPEN_SQUARE_BRACKET) || _tokenMatches(_peek(), 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 (operator.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()))); } else { // Invalid operator before 'super' _reportErrorForCurrentToken( ParserErrorCode.INVALID_OPERATOR_FOR_SUPER, [operator.lexeme]); return new PrefixExpression( operator, new SuperExpression(getAndAdvance())); } } return new PrefixExpression(operator, _parseAssignableExpression(false)); } else if (_matches(TokenType.PLUS)) { _reportErrorForCurrentToken(ParserErrorCode.MISSING_IDENTIFIER); return _createSyntheticIdentifier(); } else if (_inAsync && _matchesString(_AWAIT)) { return _parseAwaitExpression(); } return _parsePostfixExpression(); } /** * Parse a string literal representing a URI. Return the string literal that * was parsed. */ StringLiteral _parseUri() { bool iskeywordAfterUri(Token token) => token.lexeme == Keyword.AS.syntax || token.lexeme == _HIDE || token.lexeme == _SHOW; if (!_matches(TokenType.STRING) && !_matches(TokenType.SEMICOLON) && !iskeywordAfterUri(_currentToken)) { // Attempt to recover in the case where the URI was not enclosed in // quotes. Token token = _currentToken; while ((_tokenMatchesIdentifier(token) && !iskeywordAfterUri(token)) || _tokenMatches(token, TokenType.COLON) || _tokenMatches(token, TokenType.SLASH) || _tokenMatches(token, TokenType.PERIOD) || _tokenMatches(token, TokenType.PERIOD_PERIOD) || _tokenMatches(token, TokenType.PERIOD_PERIOD_PERIOD) || _tokenMatches(token, TokenType.INT) || _tokenMatches(token, TokenType.DOUBLE)) { token = token.next; } if (_tokenMatches(token, TokenType.SEMICOLON) || iskeywordAfterUri(token)) { Token endToken = token.previous; token = _currentToken; int endOffset = token.end; StringBuffer buffer = new StringBuffer(); buffer.write(token.lexeme); while (token != endToken) { token = token.next; if (token.offset != endOffset || token.precedingComments != null) { return parseStringLiteral(); } buffer.write(token.lexeme); endOffset = token.end; } String value = buffer.toString(); Token newToken = new StringToken(TokenType.STRING, "'$value'", _currentToken.offset); _reportErrorForToken( ParserErrorCode.NON_STRING_LITERAL_AS_URI, newToken); _currentToken = endToken.next; return new SimpleStringLiteral(newToken, value); } } return parseStringLiteral(); } /** * Parse a variable declaration. Return the variable declaration that was * parsed. * * variableDeclaration ::= * identifier ('=' expression)? */ VariableDeclaration _parseVariableDeclaration() { CommentAndMetadata commentAndMetadata = _parseCommentAndMetadata(); SimpleIdentifier name = parseSimpleIdentifier(); Token equals = null; Expression initializer = null; if (_matches(TokenType.EQ)) { equals = getAndAdvance(); initializer = parseExpression2(); } return new VariableDeclaration(commentAndMetadata.comment, commentAndMetadata.metadata, 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 variables = new List(); variables.add(_parseVariableDeclaration()); while (_matches(TokenType.COMMA)) { _advance(); variables.add(_parseVariableDeclaration()); } return new VariableDeclarationList( commentAndMetadata != null ? commentAndMetadata.comment : null, commentAndMetadata != null ? commentAndMetadata.metadata : null, 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 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. 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 statement that was parsed. * * variableDeclarationStatement ::= * variableDeclarationList ';' */ VariableDeclarationStatement _parseVariableDeclarationStatementAfterType( CommentAndMetadata commentAndMetadata, Token keyword, TypeName type) { VariableDeclarationList variableList = _parseVariableDeclarationListAfterType( commentAndMetadata, keyword, type); Token semicolon = _expect(TokenType.SEMICOLON); return new VariableDeclarationStatement(variableList, semicolon); } /** * Parse a while statement. Return the while statement that was parsed. * * whileStatement ::= * 'while' '(' expression ')' statement */ Statement _parseWhileStatement() { bool wasInLoop = _inLoop; _inLoop = true; try { Token keyword = _expectKeyword(Keyword.WHILE); Token leftParenthesis = _expect(TokenType.OPEN_PAREN); Expression condition = parseExpression2(); Token rightParenthesis = _expect(TokenType.CLOSE_PAREN); Statement body = parseStatement2(); return new WhileStatement( keyword, leftParenthesis, condition, rightParenthesis, body); } finally { _inLoop = wasInLoop; } } /** * Parse a yield statement. Return the yield statement that was parsed. * * yieldStatement ::= * 'yield' '*'? expression ';' */ YieldStatement _parseYieldStatement() { Token yieldToken = getAndAdvance(); Token star = null; if (_matches(TokenType.STAR)) { star = getAndAdvance(); } Expression expression = parseExpression2(); Token semicolon = _expect(TokenType.SEMICOLON); return new YieldStatement(yieldToken, star, expression, semicolon); } /** * Return the token that is immediately after the current token. This is * equivalent to [_peekAt](1). */ Token _peek() => _currentToken.next; /** * Return the token that is the given [distance] after the current token, * where the distance is the number of tokens to look ahead. A distance of `0` * is the current token, `1` is the next token, etc. */ Token _peekAt(int distance) { Token token = _currentToken; for (int i = 0; i < distance; i++) { token = token.next; } return token; } /** * Report the given [error]. */ void _reportError(AnalysisError error) { if (_errorListenerLock != 0) { return; } _errorListener.onError(error); } /** * Report an error with the given [errorCode] and [arguments] associated with * the current token. */ void _reportErrorForCurrentToken(ParserErrorCode errorCode, [List arguments]) { _reportErrorForToken(errorCode, _currentToken, arguments); } /** * Report an error with the given [errorCode] and [arguments] associated with * the given [node]. */ void _reportErrorForNode(ParserErrorCode errorCode, AstNode node, [List arguments]) { _reportError(new AnalysisError.con2( _source, node.offset, node.length, errorCode, arguments)); } /** * Report an error with the given [errorCode] and [arguments] associated with * the given [token]. */ void _reportErrorForToken(ErrorCode errorCode, Token token, [List arguments]) { if (token.type == TokenType.EOF) { token = token.previous; } _reportError(new AnalysisError.con2(_source, token.offset, math.max(token.length, 1), errorCode, arguments)); } /** * Skips a block with all containing blocks. */ void _skipBlock() { Token endToken = (_currentToken as BeginToken).endToken; if (endToken == null) { endToken = _currentToken.next; while (!identical(endToken, _currentToken)) { _currentToken = endToken; endToken = _currentToken.next; } _reportErrorForToken( ParserErrorCode.EXPECTED_TOKEN, _currentToken.previous, ["}"]); } else { _currentToken = endToken.next; } } /** * Parse the 'final', 'const', 'var' or type preceding a variable declaration, * starting at the given token, without actually creating a type or changing * the current token. Return the token following the type that was parsed, or * `null` if the given token is not the first token in a valid type. The * [startToken] is the token at which parsing is to begin. Return the token * following the type that was parsed. * * finalConstVarOrType ::= * | 'final' type? * | 'const' type? * | 'var' * | type */ Token _skipFinalConstVarOrType(Token startToken) { if (_tokenMatchesKeyword(startToken, Keyword.FINAL) || _tokenMatchesKeyword(startToken, Keyword.CONST)) { Token next = startToken.next; if (_tokenMatchesIdentifier(next)) { Token next2 = next.next; // "Type parameter" or "Type<" or "prefix.Type" if (_tokenMatchesIdentifier(next2) || _tokenMatches(next2, TokenType.LT) || _tokenMatches(next2, TokenType.PERIOD)) { return _skipTypeName(next); } // "parameter" return next; } } else if (_tokenMatchesKeyword(startToken, Keyword.VAR)) { return startToken.next; } else if (_tokenMatchesIdentifier(startToken)) { Token next = startToken.next; if (_tokenMatchesIdentifier(next) || _tokenMatches(next, TokenType.LT) || _tokenMatchesKeyword(next, Keyword.THIS) || (_tokenMatches(next, TokenType.PERIOD) && _tokenMatchesIdentifier(next.next) && (_tokenMatchesIdentifier(next.next.next) || _tokenMatches(next.next.next, TokenType.LT) || _tokenMatchesKeyword(next.next.next, Keyword.THIS)))) { return _skipReturnType(startToken); } } return null; } /** * Parse a list of formal parameters, starting at the [startToken], without * actually creating a formal parameter list or changing the current token. * Return the token following the formal parameter list that was parsed, or * `null` if the given token is not the first token in a valid list of formal * parameter. * * Note that unlike other skip methods, this method uses a heuristic. In the * worst case, the parameters could be prefixed by metadata, which would * require us to be able to skip arbitrary expressions. Rather than duplicate * the logic of most of the parse methods we simply look for something that is * likely to be a list of parameters and then skip to returning the token * after the closing parenthesis. * * This method must be kept in sync with [parseFormalParameterList]. * * formalParameterList ::= * '(' ')' * | '(' normalFormalParameters (',' optionalFormalParameters)? ')' * | '(' optionalFormalParameters ')' * * normalFormalParameters ::= * normalFormalParameter (',' normalFormalParameter)* * * optionalFormalParameters ::= * optionalPositionalFormalParameters * | namedFormalParameters * * optionalPositionalFormalParameters ::= * '[' defaultFormalParameter (',' defaultFormalParameter)* ']' * * namedFormalParameters ::= * '{' defaultNamedParameter (',' defaultNamedParameter)* '}' */ Token _skipFormalParameterList(Token startToken) { if (!_tokenMatches(startToken, TokenType.OPEN_PAREN)) { return null; } Token next = startToken.next; if (_tokenMatches(next, TokenType.CLOSE_PAREN)) { return next.next; } // // Look to see whether the token after the open parenthesis is something // that should only occur at the beginning of a parameter list. // if (next.matchesAny([ TokenType.AT, TokenType.OPEN_SQUARE_BRACKET, TokenType.OPEN_CURLY_BRACKET ]) || _tokenMatchesKeyword(next, Keyword.VOID) || (_tokenMatchesIdentifier(next) && (next.next.matchesAny([TokenType.COMMA, TokenType.CLOSE_PAREN])))) { return _skipPastMatchingToken(startToken); } // // Look to see whether the first parameter is a function typed parameter // without a return type. // if (_tokenMatchesIdentifier(next) && _tokenMatches(next.next, TokenType.OPEN_PAREN)) { Token afterParameters = _skipFormalParameterList(next.next); if (afterParameters != null && (afterParameters .matchesAny([TokenType.COMMA, TokenType.CLOSE_PAREN]))) { return _skipPastMatchingToken(startToken); } } // // Look to see whether the first parameter has a type or is a function typed // parameter with a return type. // Token afterType = _skipFinalConstVarOrType(next); if (afterType == null) { return null; } if (_skipSimpleIdentifier(afterType) == null) { return null; } return _skipPastMatchingToken(startToken); } /** * If the [startToken] is a begin token with an associated end token, then * return the token following the end token. Otherwise, return `null`. */ Token _skipPastMatchingToken(Token startToken) { if (startToken is! BeginToken) { return null; } Token closeParen = (startToken as BeginToken).endToken; if (closeParen == null) { return null; } return closeParen.next; } /** * Parse a prefixed identifier, starting at the [startToken], without actually * creating a prefixed identifier or changing the current token. Return the * token following the prefixed identifier that was parsed, or `null` if the * given token is not the first token in a valid prefixed identifier. * * This method must be kept in sync with [parsePrefixedIdentifier]. * * prefixedIdentifier ::= * identifier ('.' identifier)? */ Token _skipPrefixedIdentifier(Token startToken) { Token token = _skipSimpleIdentifier(startToken); if (token == null) { return null; } else if (!_tokenMatches(token, TokenType.PERIOD)) { return token; } token = token.next; Token nextToken = _skipSimpleIdentifier(token); if (nextToken != null) { return nextToken; } else if (_tokenMatches(token, TokenType.CLOSE_PAREN) || _tokenMatches(token, TokenType.COMMA)) { // If the `id.` is followed by something that cannot produce a valid // structure then assume this is a prefixed identifier but missing the // trailing identifier return token; } return null; } /** * Parse a return type, starting at the [startToken], without actually * creating a return type or changing the current token. Return the token * following the return type that was parsed, or `null` if the given token is * not the first token in a valid return type. * * This method must be kept in sync with [parseReturnType]. * * returnType ::= * 'void' * | type */ Token _skipReturnType(Token startToken) { if (_tokenMatchesKeyword(startToken, Keyword.VOID)) { return startToken.next; } else { return _skipTypeName(startToken); } } /** * Parse a simple identifier, starting at the [startToken], without actually * creating a simple identifier or changing the current token. Return the * token following the simple identifier that was parsed, or `null` if the * given token is not the first token in a valid simple identifier. * * This method must be kept in sync with [parseSimpleIdentifier]. * * identifier ::= * IDENTIFIER */ Token _skipSimpleIdentifier(Token startToken) { if (_tokenMatches(startToken, TokenType.IDENTIFIER) || (_tokenMatches(startToken, TokenType.KEYWORD) && (startToken as KeywordToken).keyword.isPseudoKeyword)) { return startToken.next; } return null; } /** * Parse a string literal that contains interpolations, starting at the * [startToken], without actually creating a string literal or changing the * current token. Return the token following the string literal that was * parsed, or `null` if the given token is not the first token in a valid * string literal. * * This method must be kept in sync with [parseStringInterpolation]. */ Token _skipStringInterpolation(Token startToken) { Token token = startToken; TokenType type = token.type; while (type == TokenType.STRING_INTERPOLATION_EXPRESSION || type == TokenType.STRING_INTERPOLATION_IDENTIFIER) { if (type == TokenType.STRING_INTERPOLATION_EXPRESSION) { token = token.next; type = token.type; // // Rather than verify that the following tokens represent a valid // expression, we simply skip tokens until we reach the end of the // interpolation, being careful to handle nested string literals. // int bracketNestingLevel = 1; while (bracketNestingLevel > 0) { if (type == TokenType.EOF) { return null; } else if (type == TokenType.OPEN_CURLY_BRACKET) { bracketNestingLevel++; } else if (type == TokenType.CLOSE_CURLY_BRACKET) { bracketNestingLevel--; } else if (type == TokenType.STRING) { token = _skipStringLiteral(token); if (token == null) { return null; } } else { token = token.next; } type = token.type; } token = token.next; type = token.type; } else { token = token.next; if (token.type != TokenType.IDENTIFIER) { return null; } token = token.next; } type = token.type; if (type == TokenType.STRING) { token = token.next; type = token.type; } } return token; } /** * Parse a string literal, starting at the [startToken], without actually * creating a string literal or changing the current token. Return the token * following the string literal that was parsed, or `null` if the given token * is not the first token in a valid string literal. * * This method must be kept in sync with [parseStringLiteral]. * * stringLiteral ::= * MULTI_LINE_STRING+ * | SINGLE_LINE_STRING+ */ Token _skipStringLiteral(Token startToken) { Token token = startToken; while (token != null && _tokenMatches(token, TokenType.STRING)) { token = token.next; TokenType type = token.type; if (type == TokenType.STRING_INTERPOLATION_EXPRESSION || type == TokenType.STRING_INTERPOLATION_IDENTIFIER) { token = _skipStringInterpolation(token); } } if (identical(token, startToken)) { return null; } return token; } /** * Parse a list of type arguments, starting at the [startToken], without * actually creating a type argument list or changing the current token. * Return the token following the type argument list that was parsed, or * `null` if the given token is not the first token in a valid type argument * list. * * This method must be kept in sync with [parseTypeArgumentList]. * * typeArguments ::= * '<' typeList '>' * * typeList ::= * type (',' type)* */ Token _skipTypeArgumentList(Token startToken) { Token token = startToken; if (!_tokenMatches(token, TokenType.LT)) { return null; } token = _skipTypeName(token.next); if (token == null) { // If the start token '<' is followed by '>' // then assume this should be type argument list but is missing a type token = startToken.next; if (_tokenMatches(token, TokenType.GT)) { return token.next; } return null; } while (_tokenMatches(token, TokenType.COMMA)) { token = _skipTypeName(token.next); if (token == null) { return null; } } if (token.type == TokenType.GT) { return token.next; } else if (token.type == TokenType.GT_GT) { Token second = new Token(TokenType.GT, token.offset + 1); second.setNextWithoutSettingPrevious(token.next); return second; } return null; } /** * Parse a type name, starting at the [startToken], without actually creating * a type name or changing the current token. Return the token following the * type name that was parsed, or `null` if the given token is not the first * token in a valid type name. * * This method must be kept in sync with [parseTypeName]. * * type ::= * qualified typeArguments? */ Token _skipTypeName(Token startToken) { Token token = _skipPrefixedIdentifier(startToken); if (token == null) { return null; } if (_tokenMatches(token, TokenType.LT)) { token = _skipTypeArgumentList(token); } return token; } /** * Parse a list of type parameters, starting at the [startToken], without * actually creating a type parameter list or changing the current token. * Return the token following the type parameter list that was parsed, or * `null` if the given token is not the first token in a valid type parameter * list. * * This method must be kept in sync with [parseTypeParameterList]. * * typeParameterList ::= * '<' typeParameter (',' typeParameter)* '>' */ Token _skipTypeParameterList(Token startToken) { if (!_tokenMatches(startToken, TokenType.LT)) { return null; } // // We can't skip a type parameter because it can be preceeded by metadata, // so we just assume that everything before the matching end token is valid. // int depth = 1; Token next = startToken.next; while (depth > 0) { if (_tokenMatches(next, TokenType.EOF)) { return null; } else if (_tokenMatches(next, TokenType.LT)) { depth++; } else if (_tokenMatches(next, TokenType.GT)) { depth--; } else if (_tokenMatches(next, TokenType.GT_EQ)) { if (depth == 1) { Token fakeEquals = new Token(TokenType.EQ, next.offset + 2); fakeEquals.setNextWithoutSettingPrevious(next.next); return fakeEquals; } depth--; } else if (_tokenMatches(next, TokenType.GT_GT)) { depth -= 2; } else if (_tokenMatches(next, TokenType.GT_GT_EQ)) { if (depth < 2) { return null; } else if (depth == 2) { Token fakeEquals = new Token(TokenType.EQ, next.offset + 2); fakeEquals.setNextWithoutSettingPrevious(next.next); return fakeEquals; } depth -= 2; } next = next.next; } return next; } /** * Return `true` if the given [token] has the given [type]. */ bool _tokenMatches(Token token, TokenType type) => token.type == type; /** * Return `true` if the given [token] is a valid identifier. Valid identifiers * include built-in identifiers (pseudo-keywords). */ bool _tokenMatchesIdentifier(Token token) => _tokenMatches(token, TokenType.IDENTIFIER) || (_tokenMatches(token, TokenType.KEYWORD) && (token as KeywordToken).keyword.isPseudoKeyword); /** * Return `true` if the given [token] matches the given [keyword]. */ bool _tokenMatchesKeyword(Token token, Keyword keyword) => token.type == TokenType.KEYWORD && (token as KeywordToken).keyword == keyword; /** * Return `true` if the given [token] matches the given [identifier]. */ bool _tokenMatchesString(Token token, String identifier) => token.type == TokenType.IDENTIFIER && token.lexeme == identifier; /** * Translate the characters at the given [index] in the given [lexeme], * appending the translated character to the given [buffer]. The index is * assumed to be valid. */ int _translateCharacter(StringBuffer buffer, String lexeme, int index) { int currentChar = lexeme.codeUnitAt(index); if (currentChar != 0x5C) { buffer.writeCharCode(currentChar); return index + 1; } // // We have found an escape sequence, so we parse the string to determine // what kind of escape sequence and what character to add to the builder. // int length = lexeme.length; int currentIndex = index + 1; if (currentIndex >= length) { // Illegal escape sequence: no char after escape. // This cannot actually happen because it would require the escape // character to be the last character in the string, but if it were it // would escape the closing quote, leaving the string unclosed. // reportError(ParserErrorCode.MISSING_CHAR_IN_ESCAPE_SEQUENCE); return length; } currentChar = lexeme.codeUnitAt(currentIndex); if (currentChar == 0x6E) { buffer.writeCharCode(0xA); // newline } else if (currentChar == 0x72) { buffer.writeCharCode(0xD); // carriage return } else if (currentChar == 0x66) { buffer.writeCharCode(0xC); // form feed } else if (currentChar == 0x62) { buffer.writeCharCode(0x8); // backspace } else if (currentChar == 0x74) { buffer.writeCharCode(0x9); // tab } else if (currentChar == 0x76) { buffer.writeCharCode(0xB); // vertical tab } else if (currentChar == 0x78) { if (currentIndex + 2 >= length) { // Illegal escape sequence: not enough hex digits _reportErrorForCurrentToken(ParserErrorCode.INVALID_HEX_ESCAPE); return length; } int firstDigit = lexeme.codeUnitAt(currentIndex + 1); int secondDigit = lexeme.codeUnitAt(currentIndex + 2); if (!_isHexDigit(firstDigit) || !_isHexDigit(secondDigit)) { // Illegal escape sequence: invalid hex digit _reportErrorForCurrentToken(ParserErrorCode.INVALID_HEX_ESCAPE); } else { int charCode = (Character.digit(firstDigit, 16) << 4) + Character.digit(secondDigit, 16); buffer.writeCharCode(charCode); } return currentIndex + 3; } else if (currentChar == 0x75) { currentIndex++; if (currentIndex >= length) { // Illegal escape sequence: not enough hex digits _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE); return length; } currentChar = lexeme.codeUnitAt(currentIndex); if (currentChar == 0x7B) { currentIndex++; if (currentIndex >= length) { // Illegal escape sequence: incomplete escape _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE); return length; } currentChar = lexeme.codeUnitAt(currentIndex); int digitCount = 0; int value = 0; while (currentChar != 0x7D) { if (!_isHexDigit(currentChar)) { // Illegal escape sequence: invalid hex digit _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE); currentIndex++; while (currentIndex < length && lexeme.codeUnitAt(currentIndex) != 0x7D) { currentIndex++; } return currentIndex + 1; } digitCount++; value = (value << 4) + Character.digit(currentChar, 16); currentIndex++; if (currentIndex >= length) { // Illegal escape sequence: incomplete escape _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE); return length; } currentChar = lexeme.codeUnitAt(currentIndex); } if (digitCount < 1 || digitCount > 6) { // Illegal escape sequence: not enough or too many hex digits _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE); } _appendScalarValue(buffer, lexeme.substring(index, currentIndex + 1), value, index, currentIndex); return currentIndex + 1; } else { if (currentIndex + 3 >= length) { // Illegal escape sequence: not enough hex digits _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE); return length; } int firstDigit = currentChar; int secondDigit = lexeme.codeUnitAt(currentIndex + 1); int thirdDigit = lexeme.codeUnitAt(currentIndex + 2); int fourthDigit = lexeme.codeUnitAt(currentIndex + 3); if (!_isHexDigit(firstDigit) || !_isHexDigit(secondDigit) || !_isHexDigit(thirdDigit) || !_isHexDigit(fourthDigit)) { // Illegal escape sequence: invalid hex digits _reportErrorForCurrentToken(ParserErrorCode.INVALID_UNICODE_ESCAPE); } else { _appendScalarValue( buffer, lexeme .substring( index, currentIndex + 1), (((((Character.digit(firstDigit, 16) << 4) + Character.digit(secondDigit, 16)) << 4) + Character.digit(thirdDigit, 16)) << 4) + Character .digit(fourthDigit, 16), index, currentIndex + 3); } return currentIndex + 4; } } else { buffer.writeCharCode(currentChar); } return currentIndex + 1; } /** * Decrements the error reporting lock level. If level is more than `0`, then * [reportError] wont report any error. */ void _unlockErrorListener() { if (_errorListenerLock == 0) { throw new IllegalStateException( "Attempt to unlock not locked error listener."); } _errorListenerLock--; } /** * Validate that the given [parameterList] does not contain any field * initializers. */ void _validateFormalParameterList(FormalParameterList parameterList) { for (FormalParameter parameter in parameterList.parameters) { if (parameter is FieldFormalParameter) { _reportErrorForNode( ParserErrorCode.FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR, parameter.identifier); } } } /** * Validate that the given set of [modifiers] is appropriate for a class and * return the 'abstract' keyword if there is one. */ Token _validateModifiersForClass(Modifiers modifiers) { _validateModifiersForTopLevelDeclaration(modifiers); if (modifiers.constKeyword != null) { _reportErrorForToken(ParserErrorCode.CONST_CLASS, modifiers.constKeyword); } if (modifiers.externalKeyword != null) { _reportErrorForToken( ParserErrorCode.EXTERNAL_CLASS, modifiers.externalKeyword); } if (modifiers.finalKeyword != null) { _reportErrorForToken(ParserErrorCode.FINAL_CLASS, modifiers.finalKeyword); } if (modifiers.varKeyword != null) { _reportErrorForToken(ParserErrorCode.VAR_CLASS, modifiers.varKeyword); } return modifiers.abstractKeyword; } /** * Validate that the given set of [modifiers] is appropriate for a constructor * and return the 'const' keyword if there is one. */ Token _validateModifiersForConstructor(Modifiers modifiers) { if (modifiers.abstractKeyword != null) { _reportErrorForToken( ParserErrorCode.ABSTRACT_CLASS_MEMBER, modifiers.abstractKeyword); } if (modifiers.finalKeyword != null) { _reportErrorForToken( ParserErrorCode.FINAL_CONSTRUCTOR, modifiers.finalKeyword); } if (modifiers.staticKeyword != null) { _reportErrorForToken( ParserErrorCode.STATIC_CONSTRUCTOR, modifiers.staticKeyword); } if (modifiers.varKeyword != null) { _reportErrorForToken( ParserErrorCode.CONSTRUCTOR_WITH_RETURN_TYPE, modifiers.varKeyword); } Token externalKeyword = modifiers.externalKeyword; Token constKeyword = modifiers.constKeyword; Token factoryKeyword = modifiers.factoryKeyword; if (externalKeyword != null && constKeyword != null && constKeyword.offset < externalKeyword.offset) { _reportErrorForToken( ParserErrorCode.EXTERNAL_AFTER_CONST, externalKeyword); } if (externalKeyword != null && factoryKeyword != null && factoryKeyword.offset < externalKeyword.offset) { _reportErrorForToken( ParserErrorCode.EXTERNAL_AFTER_FACTORY, externalKeyword); } return constKeyword; } /** * Validate that the given set of [modifiers] is appropriate for a class and * return the 'abstract' keyword if there is one. */ void _validateModifiersForEnum(Modifiers modifiers) { _validateModifiersForTopLevelDeclaration(modifiers); if (modifiers.abstractKeyword != null) { _reportErrorForToken( ParserErrorCode.ABSTRACT_ENUM, modifiers.abstractKeyword); } if (modifiers.constKeyword != null) { _reportErrorForToken(ParserErrorCode.CONST_ENUM, modifiers.constKeyword); } if (modifiers.externalKeyword != null) { _reportErrorForToken( ParserErrorCode.EXTERNAL_ENUM, modifiers.externalKeyword); } if (modifiers.finalKeyword != null) { _reportErrorForToken(ParserErrorCode.FINAL_ENUM, modifiers.finalKeyword); } if (modifiers.varKeyword != null) { _reportErrorForToken(ParserErrorCode.VAR_ENUM, modifiers.varKeyword); } } /** * Validate that the given set of [modifiers] is appropriate for a field and * return the 'final', 'const' or 'var' keyword if there is one. */ Token _validateModifiersForField(Modifiers modifiers) { if (modifiers.abstractKeyword != null) { _reportErrorForCurrentToken(ParserErrorCode.ABSTRACT_CLASS_MEMBER); } if (modifiers.externalKeyword != null) { _reportErrorForToken( ParserErrorCode.EXTERNAL_FIELD, modifiers.externalKeyword); } if (modifiers.factoryKeyword != null) { _reportErrorForToken( ParserErrorCode.NON_CONSTRUCTOR_FACTORY, modifiers.factoryKeyword); } Token staticKeyword = modifiers.staticKeyword; Token constKeyword = modifiers.constKeyword; Token finalKeyword = modifiers.finalKeyword; Token varKeyword = modifiers.varKeyword; if (constKeyword != null) { if (finalKeyword != null) { _reportErrorForToken(ParserErrorCode.CONST_AND_FINAL, finalKeyword); } if (varKeyword != null) { _reportErrorForToken(ParserErrorCode.CONST_AND_VAR, varKeyword); } if (staticKeyword != null && constKeyword.offset < staticKeyword.offset) { _reportErrorForToken(ParserErrorCode.STATIC_AFTER_CONST, staticKeyword); } } else if (finalKeyword != null) { if (varKeyword != null) { _reportErrorForToken(ParserErrorCode.FINAL_AND_VAR, varKeyword); } if (staticKeyword != null && finalKeyword.offset < staticKeyword.offset) { _reportErrorForToken(ParserErrorCode.STATIC_AFTER_FINAL, staticKeyword); } } else if (varKeyword != null && staticKeyword != null && varKeyword.offset < staticKeyword.offset) { _reportErrorForToken(ParserErrorCode.STATIC_AFTER_VAR, staticKeyword); } return Token.lexicallyFirst([constKeyword, finalKeyword, varKeyword]); } /** * Validate that the given set of [modifiers] is appropriate for a local * function. */ void _validateModifiersForFunctionDeclarationStatement(Modifiers modifiers) { if (modifiers.abstractKeyword != null || modifiers.constKeyword != null || modifiers.externalKeyword != null || modifiers.factoryKeyword != null || modifiers.finalKeyword != null || modifiers.staticKeyword != null || modifiers.varKeyword != null) { _reportErrorForCurrentToken( ParserErrorCode.LOCAL_FUNCTION_DECLARATION_MODIFIER); } } /** * Validate that the given set of [modifiers] is appropriate for a getter, * setter, or method. */ void _validateModifiersForGetterOrSetterOrMethod(Modifiers modifiers) { if (modifiers.abstractKeyword != null) { _reportErrorForCurrentToken(ParserErrorCode.ABSTRACT_CLASS_MEMBER); } if (modifiers.constKeyword != null) { _reportErrorForToken( ParserErrorCode.CONST_METHOD, modifiers.constKeyword); } if (modifiers.factoryKeyword != null) { _reportErrorForToken( ParserErrorCode.NON_CONSTRUCTOR_FACTORY, modifiers.factoryKeyword); } if (modifiers.finalKeyword != null) { _reportErrorForToken( ParserErrorCode.FINAL_METHOD, modifiers.finalKeyword); } if (modifiers.varKeyword != null) { _reportErrorForToken( ParserErrorCode.VAR_RETURN_TYPE, modifiers.varKeyword); } Token externalKeyword = modifiers.externalKeyword; Token staticKeyword = modifiers.staticKeyword; if (externalKeyword != null && staticKeyword != null && staticKeyword.offset < externalKeyword.offset) { _reportErrorForToken( ParserErrorCode.EXTERNAL_AFTER_STATIC, externalKeyword); } } /** * Validate that the given set of [modifiers] is appropriate for a getter, * setter, or method. */ void _validateModifiersForOperator(Modifiers modifiers) { if (modifiers.abstractKeyword != null) { _reportErrorForCurrentToken(ParserErrorCode.ABSTRACT_CLASS_MEMBER); } if (modifiers.constKeyword != null) { _reportErrorForToken( ParserErrorCode.CONST_METHOD, modifiers.constKeyword); } if (modifiers.factoryKeyword != null) { _reportErrorForToken( ParserErrorCode.NON_CONSTRUCTOR_FACTORY, modifiers.factoryKeyword); } if (modifiers.finalKeyword != null) { _reportErrorForToken( ParserErrorCode.FINAL_METHOD, modifiers.finalKeyword); } if (modifiers.staticKeyword != null) { _reportErrorForToken( ParserErrorCode.STATIC_OPERATOR, modifiers.staticKeyword); } if (modifiers.varKeyword != null) { _reportErrorForToken( ParserErrorCode.VAR_RETURN_TYPE, modifiers.varKeyword); } } /** * Validate that the given set of [modifiers] is appropriate for a top-level * declaration. */ void _validateModifiersForTopLevelDeclaration(Modifiers modifiers) { if (modifiers.factoryKeyword != null) { _reportErrorForToken(ParserErrorCode.FACTORY_TOP_LEVEL_DECLARATION, modifiers.factoryKeyword); } if (modifiers.staticKeyword != null) { _reportErrorForToken(ParserErrorCode.STATIC_TOP_LEVEL_DECLARATION, modifiers.staticKeyword); } } /** * Validate that the given set of [modifiers] is appropriate for a top-level * function. */ void _validateModifiersForTopLevelFunction(Modifiers modifiers) { _validateModifiersForTopLevelDeclaration(modifiers); if (modifiers.abstractKeyword != null) { _reportErrorForCurrentToken(ParserErrorCode.ABSTRACT_TOP_LEVEL_FUNCTION); } if (modifiers.constKeyword != null) { _reportErrorForToken(ParserErrorCode.CONST_CLASS, modifiers.constKeyword); } if (modifiers.finalKeyword != null) { _reportErrorForToken(ParserErrorCode.FINAL_CLASS, modifiers.finalKeyword); } if (modifiers.varKeyword != null) { _reportErrorForToken( ParserErrorCode.VAR_RETURN_TYPE, modifiers.varKeyword); } } /** * Validate that the given set of [modifiers] is appropriate for a field and * return the 'final', 'const' or 'var' keyword if there is one. */ Token _validateModifiersForTopLevelVariable(Modifiers modifiers) { _validateModifiersForTopLevelDeclaration(modifiers); if (modifiers.abstractKeyword != null) { _reportErrorForCurrentToken(ParserErrorCode.ABSTRACT_TOP_LEVEL_VARIABLE); } if (modifiers.externalKeyword != null) { _reportErrorForToken( ParserErrorCode.EXTERNAL_FIELD, modifiers.externalKeyword); } Token constKeyword = modifiers.constKeyword; Token finalKeyword = modifiers.finalKeyword; Token varKeyword = modifiers.varKeyword; if (constKeyword != null) { if (finalKeyword != null) { _reportErrorForToken(ParserErrorCode.CONST_AND_FINAL, finalKeyword); } if (varKeyword != null) { _reportErrorForToken(ParserErrorCode.CONST_AND_VAR, varKeyword); } } else if (finalKeyword != null) { if (varKeyword != null) { _reportErrorForToken(ParserErrorCode.FINAL_AND_VAR, varKeyword); } } return Token.lexicallyFirst([constKeyword, finalKeyword, varKeyword]); } /** * Validate that the given set of [modifiers] is appropriate for a class and * return the 'abstract' keyword if there is one. */ void _validateModifiersForTypedef(Modifiers modifiers) { _validateModifiersForTopLevelDeclaration(modifiers); if (modifiers.abstractKeyword != null) { _reportErrorForToken( ParserErrorCode.ABSTRACT_TYPEDEF, modifiers.abstractKeyword); } if (modifiers.constKeyword != null) { _reportErrorForToken( ParserErrorCode.CONST_TYPEDEF, modifiers.constKeyword); } if (modifiers.externalKeyword != null) { _reportErrorForToken( ParserErrorCode.EXTERNAL_TYPEDEF, modifiers.externalKeyword); } if (modifiers.finalKeyword != null) { _reportErrorForToken( ParserErrorCode.FINAL_TYPEDEF, modifiers.finalKeyword); } if (modifiers.varKeyword != null) { _reportErrorForToken(ParserErrorCode.VAR_TYPEDEF, modifiers.varKeyword); } } } /** * A synthetic keyword token. */ class Parser_SyntheticKeywordToken extends KeywordToken { /** * Initialize a newly created token to represent the given [keyword] at the * given [offset]. */ Parser_SyntheticKeywordToken(Keyword keyword, int offset) : super(keyword, offset); @override int get length => 0; @override Token copy() => new Parser_SyntheticKeywordToken(keyword, offset); } /** * The error codes used for errors detected by the parser. The convention for * this class is for the name of the error code to indicate the problem that * caused the error to be generated and for the error message to explain what * is wrong and, when appropriate, how the problem can be corrected. */ class ParserErrorCode extends ErrorCode { static const ParserErrorCode ABSTRACT_CLASS_MEMBER = const ParserErrorCode( 'ABSTRACT_CLASS_MEMBER', "Members of classes cannot be declared to be 'abstract'"); static const ParserErrorCode ABSTRACT_ENUM = const ParserErrorCode( 'ABSTRACT_ENUM', "Enums cannot be declared to be 'abstract'"); static const ParserErrorCode ABSTRACT_STATIC_METHOD = const ParserErrorCode( 'ABSTRACT_STATIC_METHOD', "Static methods cannot be declared to be 'abstract'"); static const ParserErrorCode ABSTRACT_TOP_LEVEL_FUNCTION = const ParserErrorCode('ABSTRACT_TOP_LEVEL_FUNCTION', "Top-level functions cannot be declared to be 'abstract'"); static const ParserErrorCode ABSTRACT_TOP_LEVEL_VARIABLE = const ParserErrorCode('ABSTRACT_TOP_LEVEL_VARIABLE', "Top-level variables cannot be declared to be 'abstract'"); static const ParserErrorCode ABSTRACT_TYPEDEF = const ParserErrorCode( 'ABSTRACT_TYPEDEF', "Type aliases cannot be declared to be 'abstract'"); static const ParserErrorCode ANNOTATION_ON_ENUM_CONSTANT = const ParserErrorCode('ANNOTATION_ON_ENUM_CONSTANT', "Enum constants cannot have annotations"); static const ParserErrorCode ASSERT_DOES_NOT_TAKE_ASSIGNMENT = const ParserErrorCode('ASSERT_DOES_NOT_TAKE_ASSIGNMENT', "Assert cannot be called on an assignment"); static const ParserErrorCode ASSERT_DOES_NOT_TAKE_CASCADE = const ParserErrorCode( 'ASSERT_DOES_NOT_TAKE_CASCADE', "Assert cannot be called on cascade"); static const ParserErrorCode ASSERT_DOES_NOT_TAKE_THROW = const ParserErrorCode( 'ASSERT_DOES_NOT_TAKE_THROW', "Assert cannot be called on throws"); static const ParserErrorCode ASSERT_DOES_NOT_TAKE_RETHROW = const ParserErrorCode('ASSERT_DOES_NOT_TAKE_RETHROW', "Assert cannot be called on rethrows"); /** * 16.32 Identifier Reference: It is a compile-time error if any of the * identifiers async, await, or yield is used as an identifier in a function * body marked with either async, async*, or sync*. */ static const ParserErrorCode ASYNC_KEYWORD_USED_AS_IDENTIFIER = const ParserErrorCode('ASYNC_KEYWORD_USED_AS_IDENTIFIER', "The keywords 'async', 'await', and 'yield' may not be used as identifiers in an asynchronous or generator function."); static const ParserErrorCode BREAK_OUTSIDE_OF_LOOP = const ParserErrorCode( 'BREAK_OUTSIDE_OF_LOOP', "A break statement cannot be used outside of a loop or switch statement"); static const ParserErrorCode CLASS_IN_CLASS = const ParserErrorCode( 'CLASS_IN_CLASS', "Classes cannot be declared inside other classes"); static const ParserErrorCode COLON_IN_PLACE_OF_IN = const ParserErrorCode( 'COLON_IN_PLACE_OF_IN', "For-in loops use 'in' rather than a colon"); static const ParserErrorCode CONST_AND_FINAL = const ParserErrorCode( 'CONST_AND_FINAL', "Members cannot be declared to be both 'const' and 'final'"); static const ParserErrorCode CONST_AND_VAR = const ParserErrorCode( 'CONST_AND_VAR', "Members cannot be declared to be both 'const' and 'var'"); static const ParserErrorCode CONST_CLASS = const ParserErrorCode( 'CONST_CLASS', "Classes cannot be declared to be 'const'"); static const ParserErrorCode CONST_CONSTRUCTOR_WITH_BODY = const ParserErrorCode('CONST_CONSTRUCTOR_WITH_BODY', "'const' constructors cannot have a body"); static const ParserErrorCode CONST_ENUM = const ParserErrorCode( 'CONST_ENUM', "Enums cannot be declared to be 'const'"); static const ParserErrorCode CONST_FACTORY = const ParserErrorCode( 'CONST_FACTORY', "Only redirecting factory constructors can be declared to be 'const'"); static const ParserErrorCode CONST_METHOD = const ParserErrorCode( 'CONST_METHOD', "Getters, setters and methods cannot be declared to be 'const'"); static const ParserErrorCode CONST_TYPEDEF = const ParserErrorCode( 'CONST_TYPEDEF', "Type aliases cannot be declared to be 'const'"); static const ParserErrorCode CONSTRUCTOR_WITH_RETURN_TYPE = const ParserErrorCode('CONSTRUCTOR_WITH_RETURN_TYPE', "Constructors cannot have a return type"); static const ParserErrorCode CONTINUE_OUTSIDE_OF_LOOP = const ParserErrorCode( 'CONTINUE_OUTSIDE_OF_LOOP', "A continue statement cannot be used outside of a loop or switch statement"); static const ParserErrorCode CONTINUE_WITHOUT_LABEL_IN_CASE = const ParserErrorCode('CONTINUE_WITHOUT_LABEL_IN_CASE', "A continue statement in a switch statement must have a label as a target"); static const ParserErrorCode DEPRECATED_CLASS_TYPE_ALIAS = const ParserErrorCode('DEPRECATED_CLASS_TYPE_ALIAS', "The 'typedef' mixin application was replaced with 'class'"); static const ParserErrorCode DIRECTIVE_AFTER_DECLARATION = const ParserErrorCode('DIRECTIVE_AFTER_DECLARATION', "Directives must appear before any declarations"); static const ParserErrorCode DUPLICATE_LABEL_IN_SWITCH_STATEMENT = const ParserErrorCode('DUPLICATE_LABEL_IN_SWITCH_STATEMENT', "The label {0} was already used in this switch statement"); static const ParserErrorCode DUPLICATED_MODIFIER = const ParserErrorCode( 'DUPLICATED_MODIFIER', "The modifier '{0}' was already specified."); static const ParserErrorCode EMPTY_ENUM_BODY = const ParserErrorCode( 'EMPTY_ENUM_BODY', "An enum must declare at least one constant name"); static const ParserErrorCode ENUM_IN_CLASS = const ParserErrorCode( 'ENUM_IN_CLASS', "Enums cannot be declared inside classes"); static const ParserErrorCode EQUALITY_CANNOT_BE_EQUALITY_OPERAND = const ParserErrorCode('EQUALITY_CANNOT_BE_EQUALITY_OPERAND', "Equality expression cannot be operand of another equality expression."); static const ParserErrorCode EXPECTED_CASE_OR_DEFAULT = const ParserErrorCode( 'EXPECTED_CASE_OR_DEFAULT', "Expected 'case' or 'default'"); static const ParserErrorCode EXPECTED_CLASS_MEMBER = const ParserErrorCode('EXPECTED_CLASS_MEMBER', "Expected a class member"); static const ParserErrorCode EXPECTED_EXECUTABLE = const ParserErrorCode( 'EXPECTED_EXECUTABLE', "Expected a method, getter, setter or operator declaration"); static const ParserErrorCode EXPECTED_LIST_OR_MAP_LITERAL = const ParserErrorCode( 'EXPECTED_LIST_OR_MAP_LITERAL', "Expected a list or map literal"); static const ParserErrorCode EXPECTED_STRING_LITERAL = const ParserErrorCode( 'EXPECTED_STRING_LITERAL', "Expected a string literal"); static const ParserErrorCode EXPECTED_TOKEN = const ParserErrorCode('EXPECTED_TOKEN', "Expected to find '{0}'"); static const ParserErrorCode EXPECTED_TYPE_NAME = const ParserErrorCode('EXPECTED_TYPE_NAME', "Expected a type name"); static const ParserErrorCode EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE = const ParserErrorCode('EXPORT_DIRECTIVE_AFTER_PART_DIRECTIVE', "Export directives must preceed part directives"); static const ParserErrorCode EXTERNAL_AFTER_CONST = const ParserErrorCode( 'EXTERNAL_AFTER_CONST', "The modifier 'external' should be before the modifier 'const'"); static const ParserErrorCode EXTERNAL_AFTER_FACTORY = const ParserErrorCode( 'EXTERNAL_AFTER_FACTORY', "The modifier 'external' should be before the modifier 'factory'"); static const ParserErrorCode EXTERNAL_AFTER_STATIC = const ParserErrorCode( 'EXTERNAL_AFTER_STATIC', "The modifier 'external' should be before the modifier 'static'"); static const ParserErrorCode EXTERNAL_CLASS = const ParserErrorCode( 'EXTERNAL_CLASS', "Classes cannot be declared to be 'external'"); static const ParserErrorCode EXTERNAL_CONSTRUCTOR_WITH_BODY = const ParserErrorCode('EXTERNAL_CONSTRUCTOR_WITH_BODY', "External constructors cannot have a body"); static const ParserErrorCode EXTERNAL_ENUM = const ParserErrorCode( 'EXTERNAL_ENUM', "Enums cannot be declared to be 'external'"); static const ParserErrorCode EXTERNAL_FIELD = const ParserErrorCode( 'EXTERNAL_FIELD', "Fields cannot be declared to be 'external'"); static const ParserErrorCode EXTERNAL_GETTER_WITH_BODY = const ParserErrorCode( 'EXTERNAL_GETTER_WITH_BODY', "External getters cannot have a body"); static const ParserErrorCode EXTERNAL_METHOD_WITH_BODY = const ParserErrorCode( 'EXTERNAL_METHOD_WITH_BODY', "External methods cannot have a body"); static const ParserErrorCode EXTERNAL_OPERATOR_WITH_BODY = const ParserErrorCode('EXTERNAL_OPERATOR_WITH_BODY', "External operators cannot have a body"); static const ParserErrorCode EXTERNAL_SETTER_WITH_BODY = const ParserErrorCode( 'EXTERNAL_SETTER_WITH_BODY', "External setters cannot have a body"); static const ParserErrorCode EXTERNAL_TYPEDEF = const ParserErrorCode( 'EXTERNAL_TYPEDEF', "Type aliases cannot be declared to be 'external'"); static const ParserErrorCode FACTORY_TOP_LEVEL_DECLARATION = const ParserErrorCode('FACTORY_TOP_LEVEL_DECLARATION', "Top-level declarations cannot be declared to be 'factory'"); static const ParserErrorCode FACTORY_WITH_INITIALIZERS = const ParserErrorCode('FACTORY_WITH_INITIALIZERS', "A 'factory' constructor cannot have initializers", "Either remove the 'factory' keyword to make this a generative " "constructor or remove the initializers."); static const ParserErrorCode FACTORY_WITHOUT_BODY = const ParserErrorCode( 'FACTORY_WITHOUT_BODY', "A non-redirecting 'factory' constructor must have a body"); static const ParserErrorCode FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR = const ParserErrorCode('FIELD_INITIALIZER_OUTSIDE_CONSTRUCTOR', "Field initializers can only be used in a constructor"); static const ParserErrorCode FINAL_AND_VAR = const ParserErrorCode( 'FINAL_AND_VAR', "Members cannot be declared to be both 'final' and 'var'"); static const ParserErrorCode FINAL_CLASS = const ParserErrorCode( 'FINAL_CLASS', "Classes cannot be declared to be 'final'"); static const ParserErrorCode FINAL_CONSTRUCTOR = const ParserErrorCode( 'FINAL_CONSTRUCTOR', "A constructor cannot be declared to be 'final'"); static const ParserErrorCode FINAL_ENUM = const ParserErrorCode( 'FINAL_ENUM', "Enums cannot be declared to be 'final'"); static const ParserErrorCode FINAL_METHOD = const ParserErrorCode( 'FINAL_METHOD', "Getters, setters and methods cannot be declared to be 'final'"); static const ParserErrorCode FINAL_TYPEDEF = const ParserErrorCode( 'FINAL_TYPEDEF', "Type aliases cannot be declared to be 'final'"); static const ParserErrorCode FUNCTION_TYPED_PARAMETER_VAR = const ParserErrorCode( 'FUNCTION_TYPED_PARAMETER_VAR', "Function typed parameters cannot specify 'const', 'final' or 'var' instead of return type"); static const ParserErrorCode GETTER_IN_FUNCTION = const ParserErrorCode( 'GETTER_IN_FUNCTION', "Getters cannot be defined within methods or functions"); static const ParserErrorCode GETTER_WITH_PARAMETERS = const ParserErrorCode( 'GETTER_WITH_PARAMETERS', "Getter should be declared without a parameter list"); static const ParserErrorCode ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE = const ParserErrorCode('ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE', "Illegal assignment to non-assignable expression"); static const ParserErrorCode IMPLEMENTS_BEFORE_EXTENDS = const ParserErrorCode('IMPLEMENTS_BEFORE_EXTENDS', "The extends clause must be before the implements clause"); static const ParserErrorCode IMPLEMENTS_BEFORE_WITH = const ParserErrorCode( 'IMPLEMENTS_BEFORE_WITH', "The with clause must be before the implements clause"); static const ParserErrorCode IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE = const ParserErrorCode('IMPORT_DIRECTIVE_AFTER_PART_DIRECTIVE', "Import directives must preceed part directives"); static const ParserErrorCode INITIALIZED_VARIABLE_IN_FOR_EACH = const ParserErrorCode('INITIALIZED_VARIABLE_IN_FOR_EACH', "The loop variable in a for-each loop cannot be initialized"); static const ParserErrorCode INVALID_AWAIT_IN_FOR = const ParserErrorCode( 'INVALID_AWAIT_IN_FOR', "The modifier 'await' is not allowed for a normal 'for' statement", "Remove the keyword or use a for-each statement."); static const ParserErrorCode INVALID_CODE_POINT = const ParserErrorCode( 'INVALID_CODE_POINT', "The escape sequence '{0}' is not a valid code point"); static const ParserErrorCode INVALID_COMMENT_REFERENCE = const ParserErrorCode( 'INVALID_COMMENT_REFERENCE', "Comment references should contain a possibly prefixed identifier and can start with 'new', but should not contain anything else"); static const ParserErrorCode INVALID_HEX_ESCAPE = const ParserErrorCode( 'INVALID_HEX_ESCAPE', "An escape sequence starting with '\\x' must be followed by 2 hexidecimal digits"); static const ParserErrorCode INVALID_OPERATOR = const ParserErrorCode( 'INVALID_OPERATOR', "The string '{0}' is not a valid operator"); static const ParserErrorCode INVALID_OPERATOR_FOR_SUPER = const ParserErrorCode('INVALID_OPERATOR_FOR_SUPER', "The operator '{0}' cannot be used with 'super'"); static const ParserErrorCode INVALID_STAR_AFTER_ASYNC = const ParserErrorCode( 'INVALID_STAR_AFTER_ASYNC', "The modifier 'async*' is not allowed for an expression function body", "Convert the body to a block."); static const ParserErrorCode INVALID_SYNC = const ParserErrorCode( 'INVALID_SYNC', "The modifier 'sync' is not allowed for an exrpression function body", "Convert the body to a block."); static const ParserErrorCode INVALID_UNICODE_ESCAPE = const ParserErrorCode( 'INVALID_UNICODE_ESCAPE', "An escape sequence starting with '\\u' must be followed by 4 hexidecimal digits or from 1 to 6 digits between '{' and '}'"); static const ParserErrorCode LIBRARY_DIRECTIVE_NOT_FIRST = const ParserErrorCode('LIBRARY_DIRECTIVE_NOT_FIRST', "The library directive must appear before all other directives"); static const ParserErrorCode LOCAL_FUNCTION_DECLARATION_MODIFIER = const ParserErrorCode('LOCAL_FUNCTION_DECLARATION_MODIFIER', "Local function declarations cannot specify any modifier"); static const ParserErrorCode MISSING_ASSIGNABLE_SELECTOR = const ParserErrorCode('MISSING_ASSIGNABLE_SELECTOR', "Missing selector such as \".\" or \"[0]\""); static const ParserErrorCode MISSING_ASSIGNMENT_IN_INITIALIZER = const ParserErrorCode('MISSING_ASSIGNMENT_IN_INITIALIZER', "Expected an assignment after the field name"); static const ParserErrorCode MISSING_CATCH_OR_FINALLY = const ParserErrorCode( 'MISSING_CATCH_OR_FINALLY', "A try statement must have either a catch or finally clause"); static const ParserErrorCode MISSING_CLASS_BODY = const ParserErrorCode( 'MISSING_CLASS_BODY', "A class definition must have a body, even if it is empty"); static const ParserErrorCode MISSING_CLOSING_PARENTHESIS = const ParserErrorCode( 'MISSING_CLOSING_PARENTHESIS', "The closing parenthesis is missing"); static const ParserErrorCode MISSING_CONST_FINAL_VAR_OR_TYPE = const ParserErrorCode('MISSING_CONST_FINAL_VAR_OR_TYPE', "Variables must be declared using the keywords 'const', 'final', 'var' or a type name"); static const ParserErrorCode MISSING_ENUM_BODY = const ParserErrorCode( 'MISSING_ENUM_BODY', "An enum definition must have a body with at least one constant name"); static const ParserErrorCode MISSING_EXPRESSION_IN_INITIALIZER = const ParserErrorCode('MISSING_EXPRESSION_IN_INITIALIZER', "Expected an expression after the assignment operator"); static const ParserErrorCode MISSING_EXPRESSION_IN_THROW = const ParserErrorCode('MISSING_EXPRESSION_IN_THROW', "Throw expressions must compute the object to be thrown"); static const ParserErrorCode MISSING_FUNCTION_BODY = const ParserErrorCode( 'MISSING_FUNCTION_BODY', "A function body must be provided"); static const ParserErrorCode MISSING_FUNCTION_PARAMETERS = const ParserErrorCode('MISSING_FUNCTION_PARAMETERS', "Functions must have an explicit list of parameters"); static const ParserErrorCode MISSING_METHOD_PARAMETERS = const ParserErrorCode('MISSING_METHOD_PARAMETERS', "Methods must have an explicit list of parameters"); static const ParserErrorCode MISSING_GET = const ParserErrorCode( 'MISSING_GET', "Getters must have the keyword 'get' before the getter name"); static const ParserErrorCode MISSING_IDENTIFIER = const ParserErrorCode('MISSING_IDENTIFIER', "Expected an identifier"); static const ParserErrorCode MISSING_INITIALIZER = const ParserErrorCode('MISSING_INITIALIZER', "Expected an initializer"); static const ParserErrorCode MISSING_KEYWORD_OPERATOR = const ParserErrorCode( 'MISSING_KEYWORD_OPERATOR', "Operator declarations must be preceeded by the keyword 'operator'"); static const ParserErrorCode MISSING_NAME_IN_LIBRARY_DIRECTIVE = const ParserErrorCode('MISSING_NAME_IN_LIBRARY_DIRECTIVE', "Library directives must include a library name"); static const ParserErrorCode MISSING_NAME_IN_PART_OF_DIRECTIVE = const ParserErrorCode('MISSING_NAME_IN_PART_OF_DIRECTIVE', "Library directives must include a library name"); static const ParserErrorCode MISSING_PREFIX_IN_DEFERRED_IMPORT = const ParserErrorCode('MISSING_PREFIX_IN_DEFERRED_IMPORT', "Deferred imports must have a prefix"); static const ParserErrorCode MISSING_STAR_AFTER_SYNC = const ParserErrorCode( 'MISSING_STAR_AFTER_SYNC', "The modifier 'sync' must be followed by a star ('*')", "Remove the modifier or add a star."); static const ParserErrorCode MISSING_STATEMENT = const ParserErrorCode('MISSING_STATEMENT', "Expected a statement"); static const ParserErrorCode MISSING_TERMINATOR_FOR_PARAMETER_GROUP = const ParserErrorCode('MISSING_TERMINATOR_FOR_PARAMETER_GROUP', "There is no '{0}' to close the parameter group"); static const ParserErrorCode MISSING_TYPEDEF_PARAMETERS = const ParserErrorCode('MISSING_TYPEDEF_PARAMETERS', "Type aliases for functions must have an explicit list of parameters"); static const ParserErrorCode MISSING_VARIABLE_IN_FOR_EACH = const ParserErrorCode( 'MISSING_VARIABLE_IN_FOR_EACH', "A loop variable must be declared in a for-each loop before the 'in', but none were found"); static const ParserErrorCode MIXED_PARAMETER_GROUPS = const ParserErrorCode( 'MIXED_PARAMETER_GROUPS', "Cannot have both positional and named parameters in a single parameter list"); static const ParserErrorCode MULTIPLE_EXTENDS_CLAUSES = const ParserErrorCode( 'MULTIPLE_EXTENDS_CLAUSES', "Each class definition can have at most one extends clause"); static const ParserErrorCode MULTIPLE_IMPLEMENTS_CLAUSES = const ParserErrorCode('MULTIPLE_IMPLEMENTS_CLAUSES', "Each class definition can have at most one implements clause"); static const ParserErrorCode MULTIPLE_LIBRARY_DIRECTIVES = const ParserErrorCode('MULTIPLE_LIBRARY_DIRECTIVES', "Only one library directive may be declared in a file"); static const ParserErrorCode MULTIPLE_NAMED_PARAMETER_GROUPS = const ParserErrorCode('MULTIPLE_NAMED_PARAMETER_GROUPS', "Cannot have multiple groups of named parameters in a single parameter list"); static const ParserErrorCode MULTIPLE_PART_OF_DIRECTIVES = const ParserErrorCode('MULTIPLE_PART_OF_DIRECTIVES', "Only one part-of directive may be declared in a file"); static const ParserErrorCode MULTIPLE_POSITIONAL_PARAMETER_GROUPS = const ParserErrorCode('MULTIPLE_POSITIONAL_PARAMETER_GROUPS', "Cannot have multiple groups of positional parameters in a single parameter list"); static const ParserErrorCode MULTIPLE_VARIABLES_IN_FOR_EACH = const ParserErrorCode('MULTIPLE_VARIABLES_IN_FOR_EACH', "A single loop variable must be declared in a for-each loop before the 'in', but {0} were found"); static const ParserErrorCode MULTIPLE_WITH_CLAUSES = const ParserErrorCode( 'MULTIPLE_WITH_CLAUSES', "Each class definition can have at most one with clause"); static const ParserErrorCode NAMED_FUNCTION_EXPRESSION = const ParserErrorCode( 'NAMED_FUNCTION_EXPRESSION', "Function expressions cannot be named"); static const ParserErrorCode NAMED_PARAMETER_OUTSIDE_GROUP = const ParserErrorCode('NAMED_PARAMETER_OUTSIDE_GROUP', "Named parameters must be enclosed in curly braces ('{' and '}')"); static const ParserErrorCode NATIVE_CLAUSE_IN_NON_SDK_CODE = const ParserErrorCode('NATIVE_CLAUSE_IN_NON_SDK_CODE', "Native clause can only be used in the SDK and code that is loaded through native extensions"); static const ParserErrorCode NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE = const ParserErrorCode('NATIVE_FUNCTION_BODY_IN_NON_SDK_CODE', "Native functions can only be declared in the SDK and code that is loaded through native extensions"); static const ParserErrorCode NON_CONSTRUCTOR_FACTORY = const ParserErrorCode( 'NON_CONSTRUCTOR_FACTORY', "Only constructors can be declared to be a 'factory'"); static const ParserErrorCode NON_IDENTIFIER_LIBRARY_NAME = const ParserErrorCode('NON_IDENTIFIER_LIBRARY_NAME', "The name of a library must be an identifier"); static const ParserErrorCode NON_PART_OF_DIRECTIVE_IN_PART = const ParserErrorCode('NON_PART_OF_DIRECTIVE_IN_PART', "The part-of directive must be the only directive in a part"); static const ParserErrorCode NON_STRING_LITERAL_AS_URI = const ParserErrorCode('NON_STRING_LITERAL_AS_URI', "The URI must be a string literal", "Enclose the URI in either single or double quotes."); static const ParserErrorCode NON_USER_DEFINABLE_OPERATOR = const ParserErrorCode('NON_USER_DEFINABLE_OPERATOR', "The operator '{0}' is not user definable"); static const ParserErrorCode NORMAL_BEFORE_OPTIONAL_PARAMETERS = const ParserErrorCode('NORMAL_BEFORE_OPTIONAL_PARAMETERS', "Normal parameters must occur before optional parameters"); static const ParserErrorCode POSITIONAL_AFTER_NAMED_ARGUMENT = const ParserErrorCode('POSITIONAL_AFTER_NAMED_ARGUMENT', "Positional arguments must occur before named arguments"); static const ParserErrorCode POSITIONAL_PARAMETER_OUTSIDE_GROUP = const ParserErrorCode('POSITIONAL_PARAMETER_OUTSIDE_GROUP', "Positional parameters must be enclosed in square brackets ('[' and ']')"); static const ParserErrorCode REDIRECTION_IN_NON_FACTORY_CONSTRUCTOR = const ParserErrorCode('REDIRECTION_IN_NON_FACTORY_CONSTRUCTOR', "Only factory constructor can specify '=' redirection."); static const ParserErrorCode SETTER_IN_FUNCTION = const ParserErrorCode( 'SETTER_IN_FUNCTION', "Setters cannot be defined within methods or functions"); static const ParserErrorCode STATIC_AFTER_CONST = const ParserErrorCode( 'STATIC_AFTER_CONST', "The modifier 'static' should be before the modifier 'const'"); static const ParserErrorCode STATIC_AFTER_FINAL = const ParserErrorCode( 'STATIC_AFTER_FINAL', "The modifier 'static' should be before the modifier 'final'"); static const ParserErrorCode STATIC_AFTER_VAR = const ParserErrorCode( 'STATIC_AFTER_VAR', "The modifier 'static' should be before the modifier 'var'"); static const ParserErrorCode STATIC_CONSTRUCTOR = const ParserErrorCode( 'STATIC_CONSTRUCTOR', "Constructors cannot be static"); static const ParserErrorCode STATIC_GETTER_WITHOUT_BODY = const ParserErrorCode( 'STATIC_GETTER_WITHOUT_BODY', "A 'static' getter must have a body"); static const ParserErrorCode STATIC_OPERATOR = const ParserErrorCode('STATIC_OPERATOR', "Operators cannot be static"); static const ParserErrorCode STATIC_SETTER_WITHOUT_BODY = const ParserErrorCode( 'STATIC_SETTER_WITHOUT_BODY', "A 'static' setter must have a body"); static const ParserErrorCode STATIC_TOP_LEVEL_DECLARATION = const ParserErrorCode('STATIC_TOP_LEVEL_DECLARATION', "Top-level declarations cannot be declared to be 'static'"); static const ParserErrorCode SWITCH_HAS_CASE_AFTER_DEFAULT_CASE = const ParserErrorCode('SWITCH_HAS_CASE_AFTER_DEFAULT_CASE', "The 'default' case should be the last case in a switch statement"); static const ParserErrorCode SWITCH_HAS_MULTIPLE_DEFAULT_CASES = const ParserErrorCode('SWITCH_HAS_MULTIPLE_DEFAULT_CASES', "The 'default' case can only be declared once"); static const ParserErrorCode TOP_LEVEL_OPERATOR = const ParserErrorCode( 'TOP_LEVEL_OPERATOR', "Operators must be declared within a class"); static const ParserErrorCode TYPEDEF_IN_CLASS = const ParserErrorCode( 'TYPEDEF_IN_CLASS', "Function type aliases cannot be declared inside classes"); static const ParserErrorCode UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP = const ParserErrorCode('UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP', "There is no '{0}' to open a parameter group"); static const ParserErrorCode UNEXPECTED_TOKEN = const ParserErrorCode('UNEXPECTED_TOKEN', "Unexpected token '{0}'"); static const ParserErrorCode WITH_BEFORE_EXTENDS = const ParserErrorCode( 'WITH_BEFORE_EXTENDS', "The extends clause must be before the with clause"); static const ParserErrorCode WITH_WITHOUT_EXTENDS = const ParserErrorCode( 'WITH_WITHOUT_EXTENDS', "The with clause cannot be used without an extends clause"); static const ParserErrorCode WRONG_SEPARATOR_FOR_NAMED_PARAMETER = const ParserErrorCode('WRONG_SEPARATOR_FOR_NAMED_PARAMETER', "The default value of a named parameter should be preceeded by ':'"); static const ParserErrorCode WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER = const ParserErrorCode('WRONG_SEPARATOR_FOR_POSITIONAL_PARAMETER', "The default value of a positional parameter should be preceeded by '='"); static const ParserErrorCode WRONG_TERMINATOR_FOR_PARAMETER_GROUP = const ParserErrorCode('WRONG_TERMINATOR_FOR_PARAMETER_GROUP', "Expected '{0}' to close parameter group"); static const ParserErrorCode VAR_AND_TYPE = const ParserErrorCode( 'VAR_AND_TYPE', "Variables cannot be declared using both 'var' and a type name; remove the 'var'"); static const ParserErrorCode VAR_AS_TYPE_NAME = const ParserErrorCode( 'VAR_AS_TYPE_NAME', "The keyword 'var' cannot be used as a type name"); static const ParserErrorCode VAR_CLASS = const ParserErrorCode( 'VAR_CLASS', "Classes cannot be declared to be 'var'"); static const ParserErrorCode VAR_ENUM = const ParserErrorCode('VAR_ENUM', "Enums cannot be declared to be 'var'"); static const ParserErrorCode VAR_RETURN_TYPE = const ParserErrorCode( 'VAR_RETURN_TYPE', "The return type cannot be 'var'"); static const ParserErrorCode VAR_TYPEDEF = const ParserErrorCode( 'VAR_TYPEDEF', "Type aliases cannot be declared to be 'var'"); static const ParserErrorCode VOID_PARAMETER = const ParserErrorCode( 'VOID_PARAMETER', "Parameters cannot have a type of 'void'"); static const ParserErrorCode VOID_VARIABLE = const ParserErrorCode( 'VOID_VARIABLE', "Variables cannot have a type of 'void'"); /** * Initialize a newly created error code to have the given [name]. The message * associated with the error will be created from the given [message] * template. The correction associated with the error will be created from the * given [correction] template. */ const ParserErrorCode(String name, String message, [String correction]) : super(name, message, correction); @override ErrorSeverity get errorSeverity => ErrorSeverity.ERROR; @override ErrorType get type => ErrorType.SYNTACTIC_ERROR; } /** * An object that copies resolution information from one AST structure to * another as long as the structures of the corresponding children of a pair of * nodes are the same. */ class ResolutionCopier implements AstVisitor { /** * The AST node with which the node being visited is to be compared. This is * only valid at the beginning of each visit method (until [isEqualNodes] is * invoked). */ AstNode _toNode; @override bool visitAdjacentStrings(AdjacentStrings node) { AdjacentStrings toNode = this._toNode as AdjacentStrings; return _isEqualNodeLists(node.strings, toNode.strings); } @override bool visitAnnotation(Annotation node) { Annotation toNode = this._toNode as Annotation; if (_and(_isEqualTokens(node.atSign, toNode.atSign), _isEqualNodes(node.name, toNode.name), _isEqualTokens(node.period, toNode.period), _isEqualNodes(node.constructorName, toNode.constructorName), _isEqualNodes(node.arguments, toNode.arguments))) { toNode.element = node.element; return true; } return false; } @override bool visitArgumentList(ArgumentList node) { ArgumentList toNode = this._toNode as ArgumentList; return _and(_isEqualTokens(node.leftParenthesis, toNode.leftParenthesis), _isEqualNodeLists(node.arguments, toNode.arguments), _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis)); } @override bool visitAsExpression(AsExpression node) { AsExpression toNode = this._toNode as AsExpression; if (_and(_isEqualNodes(node.expression, toNode.expression), _isEqualTokens(node.asOperator, toNode.asOperator), _isEqualNodes(node.type, toNode.type))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitAssertStatement(AssertStatement node) { AssertStatement toNode = this._toNode as AssertStatement; return _and(_isEqualTokens(node.assertKeyword, toNode.assertKeyword), _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis), _isEqualNodes(node.condition, toNode.condition), _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis), _isEqualTokens(node.semicolon, toNode.semicolon)); } @override bool visitAssignmentExpression(AssignmentExpression node) { AssignmentExpression toNode = this._toNode as AssignmentExpression; if (_and(_isEqualNodes(node.leftHandSide, toNode.leftHandSide), _isEqualTokens(node.operator, toNode.operator), _isEqualNodes(node.rightHandSide, toNode.rightHandSide))) { toNode.propagatedElement = node.propagatedElement; toNode.propagatedType = node.propagatedType; toNode.staticElement = node.staticElement; toNode.staticType = node.staticType; return true; } return false; } @override bool visitAwaitExpression(AwaitExpression node) { AwaitExpression toNode = this._toNode as AwaitExpression; return _and(_isEqualTokens(node.awaitKeyword, toNode.awaitKeyword), _isEqualNodes(node.expression, toNode.expression)); } @override bool visitBinaryExpression(BinaryExpression node) { BinaryExpression toNode = this._toNode as BinaryExpression; if (_and(_isEqualNodes(node.leftOperand, toNode.leftOperand), _isEqualTokens(node.operator, toNode.operator), _isEqualNodes(node.rightOperand, toNode.rightOperand))) { toNode.propagatedElement = node.propagatedElement; toNode.propagatedType = node.propagatedType; toNode.staticElement = node.staticElement; toNode.staticType = node.staticType; return true; } return false; } @override bool visitBlock(Block node) { Block toNode = this._toNode as Block; return _and(_isEqualTokens(node.leftBracket, toNode.leftBracket), _isEqualNodeLists(node.statements, toNode.statements), _isEqualTokens(node.rightBracket, toNode.rightBracket)); } @override bool visitBlockFunctionBody(BlockFunctionBody node) { BlockFunctionBody toNode = this._toNode as BlockFunctionBody; return _isEqualNodes(node.block, toNode.block); } @override bool visitBooleanLiteral(BooleanLiteral node) { BooleanLiteral toNode = this._toNode as BooleanLiteral; if (_and(_isEqualTokens(node.literal, toNode.literal), node.value == toNode.value)) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitBreakStatement(BreakStatement node) { BreakStatement toNode = this._toNode as BreakStatement; if (_and(_isEqualTokens(node.breakKeyword, toNode.breakKeyword), _isEqualNodes(node.label, toNode.label), _isEqualTokens(node.semicolon, toNode.semicolon))) { // TODO(paulberry): map node.target to toNode.target. return true; } return false; } @override bool visitCascadeExpression(CascadeExpression node) { CascadeExpression toNode = this._toNode as CascadeExpression; if (_and(_isEqualNodes(node.target, toNode.target), _isEqualNodeLists(node.cascadeSections, toNode.cascadeSections))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitCatchClause(CatchClause node) { CatchClause toNode = this._toNode as CatchClause; return _and(_isEqualTokens(node.onKeyword, toNode.onKeyword), _isEqualNodes(node.exceptionType, toNode.exceptionType), _isEqualTokens(node.catchKeyword, toNode.catchKeyword), _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis), _isEqualNodes(node.exceptionParameter, toNode.exceptionParameter), _isEqualTokens(node.comma, toNode.comma), _isEqualNodes(node.stackTraceParameter, toNode.stackTraceParameter), _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis), _isEqualNodes(node.body, toNode.body)); } @override bool visitClassDeclaration(ClassDeclaration node) { ClassDeclaration toNode = this._toNode as ClassDeclaration; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.abstractKeyword, toNode.abstractKeyword), _isEqualTokens(node.classKeyword, toNode.classKeyword), _isEqualNodes(node.name, toNode.name), _isEqualNodes(node.typeParameters, toNode.typeParameters), _isEqualNodes(node.extendsClause, toNode.extendsClause), _isEqualNodes(node.withClause, toNode.withClause), _isEqualNodes(node.implementsClause, toNode.implementsClause), _isEqualTokens(node.leftBracket, toNode.leftBracket), _isEqualNodeLists(node.members, toNode.members), _isEqualTokens(node.rightBracket, toNode.rightBracket)); } @override bool visitClassTypeAlias(ClassTypeAlias node) { ClassTypeAlias toNode = this._toNode as ClassTypeAlias; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.typedefKeyword, toNode.typedefKeyword), _isEqualNodes(node.name, toNode.name), _isEqualNodes(node.typeParameters, toNode.typeParameters), _isEqualTokens(node.equals, toNode.equals), _isEqualTokens(node.abstractKeyword, toNode.abstractKeyword), _isEqualNodes(node.superclass, toNode.superclass), _isEqualNodes(node.withClause, toNode.withClause), _isEqualNodes(node.implementsClause, toNode.implementsClause), _isEqualTokens(node.semicolon, toNode.semicolon)); } @override bool visitComment(Comment node) { Comment toNode = this._toNode as Comment; return _isEqualNodeLists(node.references, toNode.references); } @override bool visitCommentReference(CommentReference node) { CommentReference toNode = this._toNode as CommentReference; return _and(_isEqualTokens(node.newKeyword, toNode.newKeyword), _isEqualNodes(node.identifier, toNode.identifier)); } @override bool visitCompilationUnit(CompilationUnit node) { CompilationUnit toNode = this._toNode as CompilationUnit; if (_and(_isEqualTokens(node.beginToken, toNode.beginToken), _isEqualNodes(node.scriptTag, toNode.scriptTag), _isEqualNodeLists(node.directives, toNode.directives), _isEqualNodeLists(node.declarations, toNode.declarations), _isEqualTokens(node.endToken, toNode.endToken))) { toNode.element = node.element; return true; } return false; } @override bool visitConditionalExpression(ConditionalExpression node) { ConditionalExpression toNode = this._toNode as ConditionalExpression; if (_and(_isEqualNodes(node.condition, toNode.condition), _isEqualTokens(node.question, toNode.question), _isEqualNodes(node.thenExpression, toNode.thenExpression), _isEqualTokens(node.colon, toNode.colon), _isEqualNodes(node.elseExpression, toNode.elseExpression))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitConstructorDeclaration(ConstructorDeclaration node) { ConstructorDeclaration toNode = this._toNode as ConstructorDeclaration; if (_and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.externalKeyword, toNode.externalKeyword), _isEqualTokens(node.constKeyword, toNode.constKeyword), _isEqualTokens(node.factoryKeyword, toNode.factoryKeyword), _isEqualNodes(node.returnType, toNode.returnType), _isEqualTokens(node.period, toNode.period), _isEqualNodes(node.name, toNode.name), _isEqualNodes(node.parameters, toNode.parameters), _isEqualTokens(node.separator, toNode.separator), _isEqualNodeLists(node.initializers, toNode.initializers), _isEqualNodes(node.redirectedConstructor, toNode.redirectedConstructor), _isEqualNodes(node.body, toNode.body))) { toNode.element = node.element; return true; } return false; } @override bool visitConstructorFieldInitializer(ConstructorFieldInitializer node) { ConstructorFieldInitializer toNode = this._toNode as ConstructorFieldInitializer; return _and(_isEqualTokens(node.thisKeyword, toNode.thisKeyword), _isEqualTokens(node.period, toNode.period), _isEqualNodes(node.fieldName, toNode.fieldName), _isEqualTokens(node.equals, toNode.equals), _isEqualNodes(node.expression, toNode.expression)); } @override bool visitConstructorName(ConstructorName node) { ConstructorName toNode = this._toNode as ConstructorName; if (_and(_isEqualNodes(node.type, toNode.type), _isEqualTokens(node.period, toNode.period), _isEqualNodes(node.name, toNode.name))) { toNode.staticElement = node.staticElement; return true; } return false; } @override bool visitContinueStatement(ContinueStatement node) { ContinueStatement toNode = this._toNode as ContinueStatement; if (_and(_isEqualTokens(node.continueKeyword, toNode.continueKeyword), _isEqualNodes(node.label, toNode.label), _isEqualTokens(node.semicolon, toNode.semicolon))) { // TODO(paulberry): map node.target to toNode.target. return true; } return false; } @override bool visitDeclaredIdentifier(DeclaredIdentifier node) { DeclaredIdentifier toNode = this._toNode as DeclaredIdentifier; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.keyword, toNode.keyword), _isEqualNodes(node.type, toNode.type), _isEqualNodes(node.identifier, toNode.identifier)); } @override bool visitDefaultFormalParameter(DefaultFormalParameter node) { DefaultFormalParameter toNode = this._toNode as DefaultFormalParameter; return _and(_isEqualNodes(node.parameter, toNode.parameter), node.kind == toNode.kind, _isEqualTokens(node.separator, toNode.separator), _isEqualNodes(node.defaultValue, toNode.defaultValue)); } @override bool visitDoStatement(DoStatement node) { DoStatement toNode = this._toNode as DoStatement; return _and(_isEqualTokens(node.doKeyword, toNode.doKeyword), _isEqualNodes(node.body, toNode.body), _isEqualTokens(node.whileKeyword, toNode.whileKeyword), _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis), _isEqualNodes(node.condition, toNode.condition), _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis), _isEqualTokens(node.semicolon, toNode.semicolon)); } @override bool visitDoubleLiteral(DoubleLiteral node) { DoubleLiteral toNode = this._toNode as DoubleLiteral; if (_and(_isEqualTokens(node.literal, toNode.literal), node.value == toNode.value)) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitEmptyFunctionBody(EmptyFunctionBody node) { EmptyFunctionBody toNode = this._toNode as EmptyFunctionBody; return _isEqualTokens(node.semicolon, toNode.semicolon); } @override bool visitEmptyStatement(EmptyStatement node) { EmptyStatement toNode = this._toNode as EmptyStatement; return _isEqualTokens(node.semicolon, toNode.semicolon); } @override bool visitEnumConstantDeclaration(EnumConstantDeclaration node) { EnumConstantDeclaration toNode = this._toNode as EnumConstantDeclaration; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualNodes(node.name, toNode.name)); } @override bool visitEnumDeclaration(EnumDeclaration node) { EnumDeclaration toNode = this._toNode as EnumDeclaration; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.enumKeyword, toNode.enumKeyword), _isEqualNodes(node.name, toNode.name), _isEqualTokens(node.leftBracket, toNode.leftBracket), _isEqualNodeLists(node.constants, toNode.constants), _isEqualTokens(node.rightBracket, toNode.rightBracket)); } @override bool visitExportDirective(ExportDirective node) { ExportDirective toNode = this._toNode as ExportDirective; if (_and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.keyword, toNode.keyword), _isEqualNodes(node.uri, toNode.uri), _isEqualNodeLists(node.combinators, toNode.combinators), _isEqualTokens(node.semicolon, toNode.semicolon))) { toNode.element = node.element; return true; } return false; } @override bool visitExpressionFunctionBody(ExpressionFunctionBody node) { ExpressionFunctionBody toNode = this._toNode as ExpressionFunctionBody; return _and( _isEqualTokens(node.functionDefinition, toNode.functionDefinition), _isEqualNodes(node.expression, toNode.expression), _isEqualTokens(node.semicolon, toNode.semicolon)); } @override bool visitExpressionStatement(ExpressionStatement node) { ExpressionStatement toNode = this._toNode as ExpressionStatement; return _and(_isEqualNodes(node.expression, toNode.expression), _isEqualTokens(node.semicolon, toNode.semicolon)); } @override bool visitExtendsClause(ExtendsClause node) { ExtendsClause toNode = this._toNode as ExtendsClause; return _and(_isEqualTokens(node.extendsKeyword, toNode.extendsKeyword), _isEqualNodes(node.superclass, toNode.superclass)); } @override bool visitFieldDeclaration(FieldDeclaration node) { FieldDeclaration toNode = this._toNode as FieldDeclaration; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.staticKeyword, toNode.staticKeyword), _isEqualNodes(node.fields, toNode.fields), _isEqualTokens(node.semicolon, toNode.semicolon)); } @override bool visitFieldFormalParameter(FieldFormalParameter node) { FieldFormalParameter toNode = this._toNode as FieldFormalParameter; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.keyword, toNode.keyword), _isEqualNodes(node.type, toNode.type), _isEqualTokens(node.thisKeyword, toNode.thisKeyword), _isEqualTokens(node.period, toNode.period), _isEqualNodes(node.identifier, toNode.identifier)); } @override bool visitForEachStatement(ForEachStatement node) { ForEachStatement toNode = this._toNode as ForEachStatement; return _and(_isEqualTokens(node.forKeyword, toNode.forKeyword), _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis), _isEqualNodes(node.loopVariable, toNode.loopVariable), _isEqualTokens(node.inKeyword, toNode.inKeyword), _isEqualNodes(node.iterable, toNode.iterable), _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis), _isEqualNodes(node.body, toNode.body)); } @override bool visitFormalParameterList(FormalParameterList node) { FormalParameterList toNode = this._toNode as FormalParameterList; return _and(_isEqualTokens(node.leftParenthesis, toNode.leftParenthesis), _isEqualNodeLists(node.parameters, toNode.parameters), _isEqualTokens(node.leftDelimiter, toNode.leftDelimiter), _isEqualTokens(node.rightDelimiter, toNode.rightDelimiter), _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis)); } @override bool visitForStatement(ForStatement node) { ForStatement toNode = this._toNode as ForStatement; return _and(_isEqualTokens(node.forKeyword, toNode.forKeyword), _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis), _isEqualNodes(node.variables, toNode.variables), _isEqualNodes(node.initialization, toNode.initialization), _isEqualTokens(node.leftSeparator, toNode.leftSeparator), _isEqualNodes(node.condition, toNode.condition), _isEqualTokens(node.rightSeparator, toNode.rightSeparator), _isEqualNodeLists(node.updaters, toNode.updaters), _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis), _isEqualNodes(node.body, toNode.body)); } @override bool visitFunctionDeclaration(FunctionDeclaration node) { FunctionDeclaration toNode = this._toNode as FunctionDeclaration; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.externalKeyword, toNode.externalKeyword), _isEqualNodes(node.returnType, toNode.returnType), _isEqualTokens(node.propertyKeyword, toNode.propertyKeyword), _isEqualNodes(node.name, toNode.name), _isEqualNodes(node.functionExpression, toNode.functionExpression)); } @override bool visitFunctionDeclarationStatement(FunctionDeclarationStatement node) { FunctionDeclarationStatement toNode = this._toNode as FunctionDeclarationStatement; return _isEqualNodes(node.functionDeclaration, toNode.functionDeclaration); } @override bool visitFunctionExpression(FunctionExpression node) { FunctionExpression toNode = this._toNode as FunctionExpression; if (_and(_isEqualNodes(node.parameters, toNode.parameters), _isEqualNodes(node.body, toNode.body))) { toNode.element = node.element; toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { FunctionExpressionInvocation toNode = this._toNode as FunctionExpressionInvocation; if (_and(_isEqualNodes(node.function, toNode.function), _isEqualNodes(node.argumentList, toNode.argumentList))) { toNode.propagatedElement = node.propagatedElement; toNode.propagatedType = node.propagatedType; toNode.staticElement = node.staticElement; toNode.staticType = node.staticType; return true; } return false; } @override bool visitFunctionTypeAlias(FunctionTypeAlias node) { FunctionTypeAlias toNode = this._toNode as FunctionTypeAlias; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.typedefKeyword, toNode.typedefKeyword), _isEqualNodes(node.returnType, toNode.returnType), _isEqualNodes(node.name, toNode.name), _isEqualNodes(node.typeParameters, toNode.typeParameters), _isEqualNodes(node.parameters, toNode.parameters), _isEqualTokens(node.semicolon, toNode.semicolon)); } @override bool visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { FunctionTypedFormalParameter toNode = this._toNode as FunctionTypedFormalParameter; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualNodes(node.returnType, toNode.returnType), _isEqualNodes(node.identifier, toNode.identifier), _isEqualNodes(node.parameters, toNode.parameters)); } @override bool visitHideCombinator(HideCombinator node) { HideCombinator toNode = this._toNode as HideCombinator; return _and(_isEqualTokens(node.keyword, toNode.keyword), _isEqualNodeLists(node.hiddenNames, toNode.hiddenNames)); } @override bool visitIfStatement(IfStatement node) { IfStatement toNode = this._toNode as IfStatement; return _and(_isEqualTokens(node.ifKeyword, toNode.ifKeyword), _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis), _isEqualNodes(node.condition, toNode.condition), _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis), _isEqualNodes(node.thenStatement, toNode.thenStatement), _isEqualTokens(node.elseKeyword, toNode.elseKeyword), _isEqualNodes(node.elseStatement, toNode.elseStatement)); } @override bool visitImplementsClause(ImplementsClause node) { ImplementsClause toNode = this._toNode as ImplementsClause; return _and( _isEqualTokens(node.implementsKeyword, toNode.implementsKeyword), _isEqualNodeLists(node.interfaces, toNode.interfaces)); } @override bool visitImportDirective(ImportDirective node) { ImportDirective toNode = this._toNode as ImportDirective; if (_and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.keyword, toNode.keyword), _isEqualNodes(node.uri, toNode.uri), _isEqualTokens(node.asKeyword, toNode.asKeyword), _isEqualNodes(node.prefix, toNode.prefix), _isEqualNodeLists(node.combinators, toNode.combinators), _isEqualTokens(node.semicolon, toNode.semicolon))) { toNode.element = node.element; return true; } return false; } @override bool visitIndexExpression(IndexExpression node) { IndexExpression toNode = this._toNode as IndexExpression; if (_and(_isEqualNodes(node.target, toNode.target), _isEqualTokens(node.leftBracket, toNode.leftBracket), _isEqualNodes(node.index, toNode.index), _isEqualTokens(node.rightBracket, toNode.rightBracket))) { toNode.auxiliaryElements = node.auxiliaryElements; toNode.propagatedElement = node.propagatedElement; toNode.propagatedType = node.propagatedType; toNode.staticElement = node.staticElement; toNode.staticType = node.staticType; return true; } return false; } @override bool visitInstanceCreationExpression(InstanceCreationExpression node) { InstanceCreationExpression toNode = this._toNode as InstanceCreationExpression; if (_and(_isEqualTokens(node.keyword, toNode.keyword), _isEqualNodes(node.constructorName, toNode.constructorName), _isEqualNodes(node.argumentList, toNode.argumentList))) { toNode.propagatedType = node.propagatedType; toNode.staticElement = node.staticElement; toNode.staticType = node.staticType; return true; } return false; } @override bool visitIntegerLiteral(IntegerLiteral node) { IntegerLiteral toNode = this._toNode as IntegerLiteral; if (_and(_isEqualTokens(node.literal, toNode.literal), node.value == toNode.value)) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitInterpolationExpression(InterpolationExpression node) { InterpolationExpression toNode = this._toNode as InterpolationExpression; return _and(_isEqualTokens(node.leftBracket, toNode.leftBracket), _isEqualNodes(node.expression, toNode.expression), _isEqualTokens(node.rightBracket, toNode.rightBracket)); } @override bool visitInterpolationString(InterpolationString node) { InterpolationString toNode = this._toNode as InterpolationString; return _and(_isEqualTokens(node.contents, toNode.contents), node.value == toNode.value); } @override bool visitIsExpression(IsExpression node) { IsExpression toNode = this._toNode as IsExpression; if (_and(_isEqualNodes(node.expression, toNode.expression), _isEqualTokens(node.isOperator, toNode.isOperator), _isEqualTokens(node.notOperator, toNode.notOperator), _isEqualNodes(node.type, toNode.type))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitLabel(Label node) { Label toNode = this._toNode as Label; return _and(_isEqualNodes(node.label, toNode.label), _isEqualTokens(node.colon, toNode.colon)); } @override bool visitLabeledStatement(LabeledStatement node) { LabeledStatement toNode = this._toNode as LabeledStatement; return _and(_isEqualNodeLists(node.labels, toNode.labels), _isEqualNodes(node.statement, toNode.statement)); } @override bool visitLibraryDirective(LibraryDirective node) { LibraryDirective toNode = this._toNode as LibraryDirective; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.libraryKeyword, toNode.libraryKeyword), _isEqualNodes(node.name, toNode.name), _isEqualTokens(node.semicolon, toNode.semicolon)); } @override bool visitLibraryIdentifier(LibraryIdentifier node) { LibraryIdentifier toNode = this._toNode as LibraryIdentifier; if (_isEqualNodeLists(node.components, toNode.components)) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitListLiteral(ListLiteral node) { ListLiteral toNode = this._toNode as ListLiteral; if (_and(_isEqualTokens(node.constKeyword, toNode.constKeyword), _isEqualNodes(node.typeArguments, toNode.typeArguments), _isEqualTokens(node.leftBracket, toNode.leftBracket), _isEqualNodeLists(node.elements, toNode.elements), _isEqualTokens(node.rightBracket, toNode.rightBracket))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitMapLiteral(MapLiteral node) { MapLiteral toNode = this._toNode as MapLiteral; if (_and(_isEqualTokens(node.constKeyword, toNode.constKeyword), _isEqualNodes(node.typeArguments, toNode.typeArguments), _isEqualTokens(node.leftBracket, toNode.leftBracket), _isEqualNodeLists(node.entries, toNode.entries), _isEqualTokens(node.rightBracket, toNode.rightBracket))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitMapLiteralEntry(MapLiteralEntry node) { MapLiteralEntry toNode = this._toNode as MapLiteralEntry; return _and(_isEqualNodes(node.key, toNode.key), _isEqualTokens(node.separator, toNode.separator), _isEqualNodes(node.value, toNode.value)); } @override bool visitMethodDeclaration(MethodDeclaration node) { MethodDeclaration toNode = this._toNode as MethodDeclaration; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.externalKeyword, toNode.externalKeyword), _isEqualTokens(node.modifierKeyword, toNode.modifierKeyword), _isEqualNodes(node.returnType, toNode.returnType), _isEqualTokens(node.propertyKeyword, toNode.propertyKeyword), _isEqualTokens(node.propertyKeyword, toNode.propertyKeyword), _isEqualNodes(node.name, toNode.name), _isEqualNodes(node.parameters, toNode.parameters), _isEqualNodes(node.body, toNode.body)); } @override bool visitMethodInvocation(MethodInvocation node) { MethodInvocation toNode = this._toNode as MethodInvocation; if (_and(_isEqualNodes(node.target, toNode.target), _isEqualTokens(node.operator, toNode.operator), _isEqualNodes(node.methodName, toNode.methodName), _isEqualNodes(node.argumentList, toNode.argumentList))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitNamedExpression(NamedExpression node) { NamedExpression toNode = this._toNode as NamedExpression; if (_and(_isEqualNodes(node.name, toNode.name), _isEqualNodes(node.expression, toNode.expression))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitNativeClause(NativeClause node) { NativeClause toNode = this._toNode as NativeClause; return _and(_isEqualTokens(node.nativeKeyword, toNode.nativeKeyword), _isEqualNodes(node.name, toNode.name)); } @override bool visitNativeFunctionBody(NativeFunctionBody node) { NativeFunctionBody toNode = this._toNode as NativeFunctionBody; return _and(_isEqualTokens(node.nativeKeyword, toNode.nativeKeyword), _isEqualNodes(node.stringLiteral, toNode.stringLiteral), _isEqualTokens(node.semicolon, toNode.semicolon)); } @override bool visitNullLiteral(NullLiteral node) { NullLiteral toNode = this._toNode as NullLiteral; if (_isEqualTokens(node.literal, toNode.literal)) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitParenthesizedExpression(ParenthesizedExpression node) { ParenthesizedExpression toNode = this._toNode as ParenthesizedExpression; if (_and(_isEqualTokens(node.leftParenthesis, toNode.leftParenthesis), _isEqualNodes(node.expression, toNode.expression), _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitPartDirective(PartDirective node) { PartDirective toNode = this._toNode as PartDirective; if (_and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.partKeyword, toNode.partKeyword), _isEqualNodes(node.uri, toNode.uri), _isEqualTokens(node.semicolon, toNode.semicolon))) { toNode.element = node.element; return true; } return false; } @override bool visitPartOfDirective(PartOfDirective node) { PartOfDirective toNode = this._toNode as PartOfDirective; if (_and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.partKeyword, toNode.partKeyword), _isEqualTokens(node.ofKeyword, toNode.ofKeyword), _isEqualNodes(node.libraryName, toNode.libraryName), _isEqualTokens(node.semicolon, toNode.semicolon))) { toNode.element = node.element; return true; } return false; } @override bool visitPostfixExpression(PostfixExpression node) { PostfixExpression toNode = this._toNode as PostfixExpression; if (_and(_isEqualNodes(node.operand, toNode.operand), _isEqualTokens(node.operator, toNode.operator))) { toNode.propagatedElement = node.propagatedElement; toNode.propagatedType = node.propagatedType; toNode.staticElement = node.staticElement; toNode.staticType = node.staticType; return true; } return false; } @override bool visitPrefixedIdentifier(PrefixedIdentifier node) { PrefixedIdentifier toNode = this._toNode as PrefixedIdentifier; if (_and(_isEqualNodes(node.prefix, toNode.prefix), _isEqualTokens(node.period, toNode.period), _isEqualNodes(node.identifier, toNode.identifier))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitPrefixExpression(PrefixExpression node) { PrefixExpression toNode = this._toNode as PrefixExpression; if (_and(_isEqualTokens(node.operator, toNode.operator), _isEqualNodes(node.operand, toNode.operand))) { toNode.propagatedElement = node.propagatedElement; toNode.propagatedType = node.propagatedType; toNode.staticElement = node.staticElement; toNode.staticType = node.staticType; return true; } return false; } @override bool visitPropertyAccess(PropertyAccess node) { PropertyAccess toNode = this._toNode as PropertyAccess; if (_and(_isEqualNodes(node.target, toNode.target), _isEqualTokens(node.operator, toNode.operator), _isEqualNodes(node.propertyName, toNode.propertyName))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitRedirectingConstructorInvocation( RedirectingConstructorInvocation node) { RedirectingConstructorInvocation toNode = this._toNode as RedirectingConstructorInvocation; if (_and(_isEqualTokens(node.thisKeyword, toNode.thisKeyword), _isEqualTokens(node.period, toNode.period), _isEqualNodes(node.constructorName, toNode.constructorName), _isEqualNodes(node.argumentList, toNode.argumentList))) { toNode.staticElement = node.staticElement; return true; } return false; } @override bool visitRethrowExpression(RethrowExpression node) { RethrowExpression toNode = this._toNode as RethrowExpression; if (_isEqualTokens(node.rethrowKeyword, toNode.rethrowKeyword)) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitReturnStatement(ReturnStatement node) { ReturnStatement toNode = this._toNode as ReturnStatement; return _and(_isEqualTokens(node.returnKeyword, toNode.returnKeyword), _isEqualNodes(node.expression, toNode.expression), _isEqualTokens(node.semicolon, toNode.semicolon)); } @override bool visitScriptTag(ScriptTag node) { ScriptTag toNode = this._toNode as ScriptTag; return _isEqualTokens(node.scriptTag, toNode.scriptTag); } @override bool visitShowCombinator(ShowCombinator node) { ShowCombinator toNode = this._toNode as ShowCombinator; return _and(_isEqualTokens(node.keyword, toNode.keyword), _isEqualNodeLists(node.shownNames, toNode.shownNames)); } @override bool visitSimpleFormalParameter(SimpleFormalParameter node) { SimpleFormalParameter toNode = this._toNode as SimpleFormalParameter; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.keyword, toNode.keyword), _isEqualNodes(node.type, toNode.type), _isEqualNodes(node.identifier, toNode.identifier)); } @override bool visitSimpleIdentifier(SimpleIdentifier node) { SimpleIdentifier toNode = this._toNode as SimpleIdentifier; if (_isEqualTokens(node.token, toNode.token)) { toNode.staticElement = node.staticElement; toNode.staticType = node.staticType; toNode.propagatedElement = node.propagatedElement; toNode.propagatedType = node.propagatedType; toNode.auxiliaryElements = node.auxiliaryElements; return true; } return false; } @override bool visitSimpleStringLiteral(SimpleStringLiteral node) { SimpleStringLiteral toNode = this._toNode as SimpleStringLiteral; if (_and(_isEqualTokens(node.literal, toNode.literal), node.value == toNode.value)) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitStringInterpolation(StringInterpolation node) { StringInterpolation toNode = this._toNode as StringInterpolation; if (_isEqualNodeLists(node.elements, toNode.elements)) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitSuperConstructorInvocation(SuperConstructorInvocation node) { SuperConstructorInvocation toNode = this._toNode as SuperConstructorInvocation; if (_and(_isEqualTokens(node.superKeyword, toNode.superKeyword), _isEqualTokens(node.period, toNode.period), _isEqualNodes(node.constructorName, toNode.constructorName), _isEqualNodes(node.argumentList, toNode.argumentList))) { toNode.staticElement = node.staticElement; return true; } return false; } @override bool visitSuperExpression(SuperExpression node) { SuperExpression toNode = this._toNode as SuperExpression; if (_isEqualTokens(node.superKeyword, toNode.superKeyword)) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitSwitchCase(SwitchCase node) { SwitchCase toNode = this._toNode as SwitchCase; return _and(_isEqualNodeLists(node.labels, toNode.labels), _isEqualTokens(node.keyword, toNode.keyword), _isEqualNodes(node.expression, toNode.expression), _isEqualTokens(node.colon, toNode.colon), _isEqualNodeLists(node.statements, toNode.statements)); } @override bool visitSwitchDefault(SwitchDefault node) { SwitchDefault toNode = this._toNode as SwitchDefault; return _and(_isEqualNodeLists(node.labels, toNode.labels), _isEqualTokens(node.keyword, toNode.keyword), _isEqualTokens(node.colon, toNode.colon), _isEqualNodeLists(node.statements, toNode.statements)); } @override bool visitSwitchStatement(SwitchStatement node) { SwitchStatement toNode = this._toNode as SwitchStatement; return _and(_isEqualTokens(node.switchKeyword, toNode.switchKeyword), _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis), _isEqualNodes(node.expression, toNode.expression), _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis), _isEqualTokens(node.leftBracket, toNode.leftBracket), _isEqualNodeLists(node.members, toNode.members), _isEqualTokens(node.rightBracket, toNode.rightBracket)); } @override bool visitSymbolLiteral(SymbolLiteral node) { SymbolLiteral toNode = this._toNode as SymbolLiteral; if (_and(_isEqualTokens(node.poundSign, toNode.poundSign), _isEqualTokenLists(node.components, toNode.components))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitThisExpression(ThisExpression node) { ThisExpression toNode = this._toNode as ThisExpression; if (_isEqualTokens(node.thisKeyword, toNode.thisKeyword)) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitThrowExpression(ThrowExpression node) { ThrowExpression toNode = this._toNode as ThrowExpression; if (_and(_isEqualTokens(node.throwKeyword, toNode.throwKeyword), _isEqualNodes(node.expression, toNode.expression))) { toNode.propagatedType = node.propagatedType; toNode.staticType = node.staticType; return true; } return false; } @override bool visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { TopLevelVariableDeclaration toNode = this._toNode as TopLevelVariableDeclaration; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualNodes(node.variables, toNode.variables), _isEqualTokens(node.semicolon, toNode.semicolon)); } @override bool visitTryStatement(TryStatement node) { TryStatement toNode = this._toNode as TryStatement; return _and(_isEqualTokens(node.tryKeyword, toNode.tryKeyword), _isEqualNodes(node.body, toNode.body), _isEqualNodeLists(node.catchClauses, toNode.catchClauses), _isEqualTokens(node.finallyKeyword, toNode.finallyKeyword), _isEqualNodes(node.finallyBlock, toNode.finallyBlock)); } @override bool visitTypeArgumentList(TypeArgumentList node) { TypeArgumentList toNode = this._toNode as TypeArgumentList; return _and(_isEqualTokens(node.leftBracket, toNode.leftBracket), _isEqualNodeLists(node.arguments, toNode.arguments), _isEqualTokens(node.rightBracket, toNode.rightBracket)); } @override bool visitTypeName(TypeName node) { TypeName toNode = this._toNode as TypeName; if (_and(_isEqualNodes(node.name, toNode.name), _isEqualNodes(node.typeArguments, toNode.typeArguments))) { toNode.type = node.type; return true; } return false; } @override bool visitTypeParameter(TypeParameter node) { TypeParameter toNode = this._toNode as TypeParameter; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualNodes(node.name, toNode.name), _isEqualTokens(node.extendsKeyword, toNode.extendsKeyword), _isEqualNodes(node.bound, toNode.bound)); } @override bool visitTypeParameterList(TypeParameterList node) { TypeParameterList toNode = this._toNode as TypeParameterList; return _and(_isEqualTokens(node.leftBracket, toNode.leftBracket), _isEqualNodeLists(node.typeParameters, toNode.typeParameters), _isEqualTokens(node.rightBracket, toNode.rightBracket)); } @override bool visitVariableDeclaration(VariableDeclaration node) { VariableDeclaration toNode = this._toNode as VariableDeclaration; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualNodes(node.name, toNode.name), _isEqualTokens(node.equals, toNode.equals), _isEqualNodes(node.initializer, toNode.initializer)); } @override bool visitVariableDeclarationList(VariableDeclarationList node) { VariableDeclarationList toNode = this._toNode as VariableDeclarationList; return _and( _isEqualNodes(node.documentationComment, toNode.documentationComment), _isEqualNodeLists(node.metadata, toNode.metadata), _isEqualTokens(node.keyword, toNode.keyword), _isEqualNodes(node.type, toNode.type), _isEqualNodeLists(node.variables, toNode.variables)); } @override bool visitVariableDeclarationStatement(VariableDeclarationStatement node) { VariableDeclarationStatement toNode = this._toNode as VariableDeclarationStatement; return _and(_isEqualNodes(node.variables, toNode.variables), _isEqualTokens(node.semicolon, toNode.semicolon)); } @override bool visitWhileStatement(WhileStatement node) { WhileStatement toNode = this._toNode as WhileStatement; return _and(_isEqualTokens(node.whileKeyword, toNode.whileKeyword), _isEqualTokens(node.leftParenthesis, toNode.leftParenthesis), _isEqualNodes(node.condition, toNode.condition), _isEqualTokens(node.rightParenthesis, toNode.rightParenthesis), _isEqualNodes(node.body, toNode.body)); } @override bool visitWithClause(WithClause node) { WithClause toNode = this._toNode as WithClause; return _and(_isEqualTokens(node.withKeyword, toNode.withKeyword), _isEqualNodeLists(node.mixinTypes, toNode.mixinTypes)); } @override bool visitYieldStatement(YieldStatement node) { YieldStatement toNode = this._toNode as YieldStatement; return _and(_isEqualTokens(node.yieldKeyword, toNode.yieldKeyword), _isEqualNodes(node.expression, toNode.expression), _isEqualTokens(node.semicolon, toNode.semicolon)); } /** * Return `true` if all of the parameters are `true`. */ bool _and(bool b1, bool b2, [bool b3 = true, bool b4 = true, bool b5 = true, bool b6 = true, bool b7 = true, bool b8 = true, bool b9 = true, bool b10 = true, bool b11 = true, bool b12 = true, bool b13 = true]) { // TODO(brianwilkerson) Inline this method. return b1 && b2 && b3 && b4 && b5 && b6 && b7 && b8 && b9 && b10 && b11 && b12 && b13; } /** * Return `true` if the [first] and [second] lists of AST nodes have the same * size and corresponding elements are equal. */ bool _isEqualNodeLists(NodeList first, NodeList second) { if (first == null) { return second == null; } else if (second == null) { return false; } int size = first.length; if (second.length != size) { return false; } bool equal = true; for (int i = 0; i < size; i++) { if (!_isEqualNodes(first[i], second[i])) { equal = false; } } return equal; } /** * Return `true` if the [fromNode] and [toNode] have the same structure. As a * side-effect, if the nodes do have the same structure, any resolution data * from the first node will be copied to the second node. */ bool _isEqualNodes(AstNode fromNode, AstNode toNode) { if (fromNode == null) { return toNode == null; } else if (toNode == null) { return false; } else if (fromNode.runtimeType == toNode.runtimeType) { this._toNode = toNode; return fromNode.accept(this); } // // Check for a simple transformation caused by entering a period. // if (toNode is PrefixedIdentifier) { SimpleIdentifier prefix = toNode.prefix; if (fromNode.runtimeType == prefix.runtimeType) { this._toNode = prefix; return fromNode.accept(this); } } else if (toNode is PropertyAccess) { Expression target = toNode.target; if (fromNode.runtimeType == target.runtimeType) { this._toNode = target; return fromNode.accept(this); } } return false; } /** * Return `true` if the [first] and [second] arrays of tokens have the same * length and corresponding elements are equal. */ bool _isEqualTokenLists(List first, List second) { int length = first.length; if (second.length != length) { return false; } for (int i = 0; i < length; i++) { if (!_isEqualTokens(first[i], second[i])) { return false; } } return true; } /** * Return `true` if the [first] and [second] tokens have the same structure. */ bool _isEqualTokens(Token first, Token second) { if (first == null) { return second == null; } else if (second == null) { return false; } return first.lexeme == second.lexeme; } /** * Copy resolution data from the [fromNode] to the [toNode]. */ static void copyResolutionData(AstNode fromNode, AstNode toNode) { ResolutionCopier copier = new ResolutionCopier(); copier._isEqualNodes(fromNode, toNode); } }