@@ -7393,7 +7393,11 @@ class Parser {
73937393 onlyParseVariableDeclarationStart);
73947394 }
73957395
7396- /// See [parseExpressionStatementOrDeclaration]
7396+ /// See [parseExpressionStatementOrDeclaration] .
7397+ ///
7398+ /// If `start.next` is an `@` token (i.e. this is a declaration with metadata)
7399+ /// then the caller should parse it before calling this method; otherwise,
7400+ /// this method will handle the lack of metadata appropriately.
73977401 Token parseExpressionStatementOrDeclarationAfterModifiers (
73987402 Token beforeType,
73997403 Token start,
@@ -7418,6 +7422,26 @@ class Parser {
74187422 start = context.parseVariableDeclarationModifiers (beforeType);
74197423 varFinalOrConst = context.varFinalOrConst;
74207424 }
7425+
7426+ // TODO(paulberry): maybe some of the conditions in this `if` test should be
7427+ // removed to allow for better error recovery.
7428+ if (allowPatterns &&
7429+ lateToken == null &&
7430+ varFinalOrConst != null &&
7431+ (optional ('var' , varFinalOrConst) ||
7432+ optional ('final' , varFinalOrConst)) &&
7433+ ! onlyParseVariableDeclarationStart &&
7434+ looksLikePatternVariableDeclaration (beforeType)) {
7435+ // If there was any metadata, then the caller was responsible for parsing
7436+ // it; if not, then we need to let the listener know there wasn't any.
7437+ if (! optional ('@' , start.next! )) {
7438+ listener.beginMetadataStar (start.next! );
7439+ listener.endMetadataStar (/* count = */ 0 );
7440+ }
7441+ return parsePatternVariableDeclarationStatement (
7442+ beforeType, start, varFinalOrConst);
7443+ }
7444+
74217445 typeInfo ?? = computeType (beforeType, /* required = */ false );
74227446
74237447 Token token = typeInfo.skipType (beforeType);
@@ -7438,6 +7462,9 @@ class Parser {
74387462 reportRecoverableErrorWithToken (
74397463 lateToken, codes.templateExtraneousModifier);
74407464 }
7465+ // If there was any metadata, then the caller was responsible for
7466+ // parsing it; if not, then we need to let the listener know there
7467+ // wasn't any.
74417468 if (! optional ('@' , start.next! )) {
74427469 listener.beginMetadataStar (start.next! );
74437470 listener.endMetadataStar (/* count = */ 0 );
@@ -7551,6 +7578,8 @@ class Parser {
75517578 }
75527579 }
75537580
7581+ // If there was any metadata, then the caller was responsible for parsing
7582+ // it; if not, then we need to let the listener know there wasn't any.
75547583 if (! optional ('@' , start.next! )) {
75557584 listener.beginMetadataStar (start.next! );
75567585 listener.endMetadataStar (/* count = */ 0 );
@@ -9208,7 +9237,8 @@ class Parser {
92089237 listener.endBinaryPattern (next);
92099238 break ;
92109239 default :
9211- throw new UnimplementedError ('TODO(paulberry): ${next .lexeme }' );
9240+ // Some other operator that doesn't belong in a pattern
9241+ return token;
92129242 }
92139243 }
92149244 }
@@ -9355,10 +9385,15 @@ class Parser {
93559385 isRefutableContext: isRefutableContext);
93569386 listener.handleExtractorPattern (firstIdentifier, dot, secondIdentifier);
93579387 return token;
9358- } else if (firstIdentifier.lexeme == '_' && dot == null ) {
9359- // It's a wildcard pattern with no preceding type, so parse it as a
9360- // variable pattern.
9361- return parseVariablePattern (beforeFirstIdentifier, typeInfo: typeInfo);
9388+ } else if (dot == null ) {
9389+ // It's a single identifier. If it's a wildcard pattern or we're in an
9390+ // irrefutable context, parse it as a variable pattern.
9391+ if (! isRefutableContext || firstIdentifier.lexeme == '_' ) {
9392+ // It's a wildcard pattern with no preceding type, so parse it as a
9393+ // variable pattern.
9394+ return parseVariablePattern (beforeFirstIdentifier,
9395+ typeInfo: typeInfo);
9396+ }
93629397 }
93639398 // It's not an extractor pattern so parse it as an expression.
93649399 token = beforeFirstIdentifier;
@@ -9389,8 +9424,7 @@ class Parser {
93899424 typeInfo = computeVariablePatternType (token);
93909425 token = typeInfo.parseType (token, this );
93919426 } else {
9392- // Bare wildcard pattern
9393- assert (next.lexeme == '_' );
9427+ // Bare identifier pattern
93949428 listener.handleNoType (token);
93959429 }
93969430 }
@@ -9666,6 +9700,88 @@ class Parser {
96669700 listener.handleExtractorPatternFields (argumentCount, begin, token);
96679701 return token;
96689702 }
9703+
9704+ /// Returns `true` if the given [token] should be treated like the start of
9705+ /// a pattern variable declaration.
9706+ ///
9707+ /// patternVariableDeclaration ::= ( 'final' | 'var' ) outerPattern '='
9708+ /// expression
9709+ bool looksLikePatternVariableDeclaration (Token token) {
9710+ Token ? afterOuterPattern = skipOuterPattern (token);
9711+ if (afterOuterPattern == null ) return false ;
9712+ return optional ('=' , afterOuterPattern.next! );
9713+ }
9714+
9715+ /// Tries to advance beyond an "outer pattern" starting from [token] . If the
9716+ /// next construct after [token] is not an outer pattern, returns `null` .
9717+ ///
9718+ /// outerPattern ::= parenthesizedPattern
9719+ /// | listPattern
9720+ /// | mapPattern
9721+ /// | recordPattern
9722+ /// | extractorPattern
9723+ Token ? skipOuterPattern (Token token) {
9724+ Token next = token.next! ;
9725+ if (next.isIdentifier) {
9726+ token = next;
9727+ next = token.next! ;
9728+ if (! optional ('.' , next)) {
9729+ return skipExtractorPatternRest (token);
9730+ }
9731+ token = next;
9732+ next = token.next! ;
9733+ if (next.isIdentifier) {
9734+ return skipExtractorPatternRest (next);
9735+ } else {
9736+ throw new UnimplementedError ('TODO(paulberry)' );
9737+ }
9738+ }
9739+ TypeParamOrArgInfo typeParamOrArg = computeTypeParamOrArg (token);
9740+ token = typeParamOrArg.skip (token);
9741+ next = token.next! ;
9742+ if (optional ('[]' , next)) {
9743+ // Empty list pattern
9744+ return next;
9745+ }
9746+ if (optional ('[' , next) || optional ('{' , next)) {
9747+ // List or map pattern
9748+ return next.endGroup;
9749+ }
9750+ if (typeParamOrArg == noTypeParamOrArg && optional ('(' , next)) {
9751+ // Record or parenthesized pattern
9752+ return next.endGroup;
9753+ }
9754+ throw new UnimplementedError ('TODO(paulberry)' );
9755+ }
9756+
9757+ /// Tries to advance through an extractor pattern, where [token] is the last
9758+ /// token of the extractor pattern's type name. If the tokens following
9759+ /// [token] don't look like the rest of an extractor pattern, returns `null` .
9760+ ///
9761+ /// extractorPattern ::= typeName typeArguments? '(' patternFields? ')'
9762+ Token ? skipExtractorPatternRest (Token token) {
9763+ TypeParamOrArgInfo typeParamOrArg = computeTypeParamOrArg (token);
9764+ token = typeParamOrArg.skip (token);
9765+ Token ? next = token.next;
9766+ if (next == null ) return null ;
9767+ if (! optional ('(' , next)) return null ;
9768+ return next.endGroup;
9769+ }
9770+
9771+ /// patternVariableDeclaration ::= ( 'final' | 'var' ) outerPattern '='
9772+ /// expression
9773+ Token parsePatternVariableDeclarationStatement (
9774+ Token keyword, Token start, Token varOrFinal) {
9775+ Token token = parsePattern (keyword, isRefutableContext: false );
9776+ Token equals = token.next! ;
9777+ // Caller should have assured that the pattern was followed by an `=`.
9778+ assert (optional ('=' , equals));
9779+ token = parseExpression (equals);
9780+ Token semicolon = ensureSemicolon (token);
9781+ listener.handlePatternVariableDeclarationStatement (
9782+ keyword, equals, semicolon);
9783+ return semicolon;
9784+ }
96699785}
96709786
96719787// TODO(ahe): Remove when analyzer supports generalized function syntax.
0 commit comments