OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library fasta.parser.parser; | 5 library fasta.parser.parser; |
6 | 6 |
7 import '../fasta_codes.dart' | 7 import '../fasta_codes.dart' |
8 show | 8 show |
9 FastaCode, | 9 FastaCode, |
10 FastaMessage, | 10 FastaMessage, |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
58 | 58 |
59 import '../scanner/recover.dart' show closeBraceFor, skipToEof; | 59 import '../scanner/recover.dart' show closeBraceFor, skipToEof; |
60 | 60 |
61 import '../scanner/keyword.dart' show Keyword; | 61 import '../scanner/keyword.dart' show Keyword; |
62 | 62 |
63 import '../scanner/precedence.dart' | 63 import '../scanner/precedence.dart' |
64 show | 64 show |
65 ASSIGNMENT_PRECEDENCE, | 65 ASSIGNMENT_PRECEDENCE, |
66 AS_INFO, | 66 AS_INFO, |
67 CASCADE_PRECEDENCE, | 67 CASCADE_PRECEDENCE, |
68 EOF_INFO, | |
69 EQUALITY_PRECEDENCE, | 68 EQUALITY_PRECEDENCE, |
70 GENERIC_METHOD_TYPE_ASSIGN, | |
71 GENERIC_METHOD_TYPE_LIST, | |
72 GT_INFO, | 69 GT_INFO, |
73 IS_INFO, | 70 IS_INFO, |
74 MINUS_MINUS_INFO, | 71 MINUS_MINUS_INFO, |
75 OPEN_PAREN_INFO, | 72 OPEN_PAREN_INFO, |
76 OPEN_SQUARE_BRACKET_INFO, | 73 OPEN_SQUARE_BRACKET_INFO, |
77 PERIOD_INFO, | 74 PERIOD_INFO, |
78 PLUS_PLUS_INFO, | 75 PLUS_PLUS_INFO, |
79 POSTFIX_PRECEDENCE, | 76 POSTFIX_PRECEDENCE, |
80 PrecedenceInfo, | 77 PrecedenceInfo, |
81 QUESTION_INFO, | 78 QUESTION_INFO, |
82 QUESTION_PERIOD_INFO, | 79 QUESTION_PERIOD_INFO, |
83 RELATIONAL_PRECEDENCE, | 80 RELATIONAL_PRECEDENCE, |
84 SCRIPT_INFO; | 81 SCRIPT_INFO; |
85 | 82 |
86 import '../scanner/token.dart' | 83 import '../scanner/token.dart' |
87 show | 84 show |
88 BeginGroupToken, | 85 BeginGroupToken, |
89 CommentToken, | |
90 KeywordToken, | 86 KeywordToken, |
91 SymbolToken, | 87 SymbolToken, |
92 Token, | 88 Token, |
93 isUserDefinableOperator; | 89 isUserDefinableOperator; |
94 | 90 |
95 import '../scanner/token_constants.dart' | 91 import '../scanner/token_constants.dart' |
96 show | 92 show |
97 COMMA_TOKEN, | 93 COMMA_TOKEN, |
98 DOUBLE_TOKEN, | 94 DOUBLE_TOKEN, |
99 EOF_TOKEN, | 95 EOF_TOKEN, |
(...skipping 10 matching lines...) Expand all Loading... |
110 OPEN_CURLY_BRACKET_TOKEN, | 106 OPEN_CURLY_BRACKET_TOKEN, |
111 OPEN_PAREN_TOKEN, | 107 OPEN_PAREN_TOKEN, |
112 OPEN_SQUARE_BRACKET_TOKEN, | 108 OPEN_SQUARE_BRACKET_TOKEN, |
113 PERIOD_TOKEN, | 109 PERIOD_TOKEN, |
114 SEMICOLON_TOKEN, | 110 SEMICOLON_TOKEN, |
115 STRING_INTERPOLATION_IDENTIFIER_TOKEN, | 111 STRING_INTERPOLATION_IDENTIFIER_TOKEN, |
116 STRING_INTERPOLATION_TOKEN, | 112 STRING_INTERPOLATION_TOKEN, |
117 STRING_TOKEN; | 113 STRING_TOKEN; |
118 | 114 |
119 import '../scanner/characters.dart' show $CLOSE_CURLY_BRACKET; | 115 import '../scanner/characters.dart' show $CLOSE_CURLY_BRACKET; |
120 import '../scanner/string_scanner.dart'; | |
121 | 116 |
122 import '../util/link.dart' show Link; | 117 import '../util/link.dart' show Link; |
123 | 118 |
124 import 'async_modifier.dart' show AsyncModifier; | 119 import 'async_modifier.dart' show AsyncModifier; |
125 | 120 |
126 import 'listener.dart' show Listener; | 121 import 'listener.dart' show Listener; |
127 | 122 |
128 import 'identifier_context.dart' show IdentifierContext; | 123 import 'identifier_context.dart' show IdentifierContext; |
129 | 124 |
130 /// Returns true if [token] is the symbol or keyword [value]. | 125 /// Returns true if [token] is the symbol or keyword [value]. |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
182 /// | 177 /// |
183 /// Historically, we over-used identical, and when identical is used on other | 178 /// Historically, we over-used identical, and when identical is used on other |
184 /// objects than strings, it can often be replaced by `==`. | 179 /// objects than strings, it can often be replaced by `==`. |
185 class Parser { | 180 class Parser { |
186 final Listener listener; | 181 final Listener listener; |
187 | 182 |
188 Uri get uri => listener.uri; | 183 Uri get uri => listener.uri; |
189 | 184 |
190 bool mayParseFunctionExpressions = true; | 185 bool mayParseFunctionExpressions = true; |
191 | 186 |
192 bool parseGenericMethodComments = false; | |
193 | |
194 /// Represents parser state: what asynchronous syntax is allowed in the | 187 /// Represents parser state: what asynchronous syntax is allowed in the |
195 /// function being currently parsed. In rare situations, this can be set by | 188 /// function being currently parsed. In rare situations, this can be set by |
196 /// external clients, for example, to parse an expression outside a function. | 189 /// external clients, for example, to parse an expression outside a function. |
197 AsyncModifier asyncState = AsyncModifier.Sync; | 190 AsyncModifier asyncState = AsyncModifier.Sync; |
198 | 191 |
199 Parser(this.listener); | 192 Parser(this.listener); |
200 | 193 |
201 bool get inGenerator { | 194 bool get inGenerator { |
202 return asyncState == AsyncModifier.AsyncStar || | 195 return asyncState == AsyncModifier.AsyncStar || |
203 asyncState == AsyncModifier.SyncStar; | 196 asyncState == AsyncModifier.SyncStar; |
(...skipping 429 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
633 token = expect('.', token.next); | 626 token = expect('.', token.next); |
634 nameToken = token; | 627 nameToken = token; |
635 token = parseIdentifier(token, IdentifierContext.fieldInitializer); | 628 token = parseIdentifier(token, IdentifierContext.fieldInitializer); |
636 } else { | 629 } else { |
637 nameToken = token; | 630 nameToken = token; |
638 token = parseIdentifier( | 631 token = parseIdentifier( |
639 token, IdentifierContext.formalParameterDeclaration); | 632 token, IdentifierContext.formalParameterDeclaration); |
640 } | 633 } |
641 } | 634 } |
642 | 635 |
643 token = _injectGenericCommentTypeList(token); | 636 token = listener.injectGenericCommentTypeList(token); |
644 if (optional('(', token)) { | 637 if (optional('(', token)) { |
645 Token inlineFunctionTypeStart = token; | 638 Token inlineFunctionTypeStart = token; |
646 listener.beginFunctionTypedFormalParameter(token); | 639 listener.beginFunctionTypedFormalParameter(token); |
647 listener.handleNoTypeVariables(token); | 640 listener.handleNoTypeVariables(token); |
648 token = parseFormalParameters(token); | 641 token = parseFormalParameters(token); |
649 listener.endFunctionTypedFormalParameter( | 642 listener.endFunctionTypedFormalParameter( |
650 covariantKeyword, thisKeyword, kind); | 643 covariantKeyword, thisKeyword, kind); |
651 // Generalized function types don't allow inline function types. | 644 // Generalized function types don't allow inline function types. |
652 // The following isn't allowed: | 645 // The following isn't allowed: |
653 // int Function(int bar(String x)). | 646 // int Function(int bar(String x)). |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
719 } else { | 712 } else { |
720 return expect(']', token); | 713 return expect(']', token); |
721 } | 714 } |
722 } | 715 } |
723 | 716 |
724 Token parseTypeOpt(Token token) { | 717 Token parseTypeOpt(Token token) { |
725 if (isGeneralizedFunctionType(token)) { | 718 if (isGeneralizedFunctionType(token)) { |
726 // Function type without return type. | 719 // Function type without return type. |
727 return parseType(token); | 720 return parseType(token); |
728 } | 721 } |
| 722 token = listener.injectGenericCommentTypeAssign(token); |
729 Token peek = peekAfterIfType(token); | 723 Token peek = peekAfterIfType(token); |
730 if (peek != null && (peek.isIdentifier() || optional('this', peek))) { | 724 if (peek != null && (peek.isIdentifier() || optional('this', peek))) { |
731 return parseType(token); | 725 return parseType(token); |
732 } | 726 } |
733 listener.handleNoType(token); | 727 listener.handleNoType(token); |
734 return token; | 728 return token; |
735 } | 729 } |
736 | 730 |
737 bool isValidTypeReference(Token token) { | 731 bool isValidTypeReference(Token token) { |
738 final kind = token.kind; | 732 final kind = token.kind; |
(...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1111 token, | 1105 token, |
1112 (t) => listener.beginTypeVariables(t), | 1106 (t) => listener.beginTypeVariables(t), |
1113 (t) => parseTypeVariable(t), | 1107 (t) => parseTypeVariable(t), |
1114 (c, bt, et) => listener.endTypeVariables(c, bt, et), | 1108 (c, bt, et) => listener.endTypeVariables(c, bt, et), |
1115 (t) => listener.handleNoTypeVariables(t)); | 1109 (t) => listener.handleNoTypeVariables(t)); |
1116 } | 1110 } |
1117 | 1111 |
1118 /// TODO(ahe): Clean this up. | 1112 /// TODO(ahe): Clean this up. |
1119 Token parseStuff(Token token, Function beginStuff, Function stuffParser, | 1113 Token parseStuff(Token token, Function beginStuff, Function stuffParser, |
1120 Function endStuff, Function handleNoStuff) { | 1114 Function endStuff, Function handleNoStuff) { |
1121 token = _injectGenericCommentTypeList(token); | 1115 token = listener.injectGenericCommentTypeList(token); |
1122 if (optional('<', token)) { | 1116 if (optional('<', token)) { |
1123 Token begin = token; | 1117 Token begin = token; |
1124 beginStuff(begin); | 1118 beginStuff(begin); |
1125 int count = 0; | 1119 int count = 0; |
1126 do { | 1120 do { |
1127 token = stuffParser(token.next); | 1121 token = stuffParser(token.next); |
1128 ++count; | 1122 ++count; |
1129 } while (optional(',', token)); | 1123 } while (optional(',', token)); |
1130 Token next = token.next; | 1124 Token next = token.next; |
1131 if (identical(token.stringValue, '>>')) { | 1125 if (identical(token.stringValue, '>>')) { |
(...skipping 1266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2398 } | 2392 } |
2399 | 2393 |
2400 Token parseExpressionStatementOrDeclaration(Token token) { | 2394 Token parseExpressionStatementOrDeclaration(Token token) { |
2401 if (!inPlainSync && optional("await", token)) { | 2395 if (!inPlainSync && optional("await", token)) { |
2402 return parseExpressionStatement(token); | 2396 return parseExpressionStatement(token); |
2403 } | 2397 } |
2404 assert(token.isIdentifier() || identical(token.stringValue, 'void')); | 2398 assert(token.isIdentifier() || identical(token.stringValue, 'void')); |
2405 Token identifier = peekIdentifierAfterType(token); | 2399 Token identifier = peekIdentifierAfterType(token); |
2406 if (identifier != null) { | 2400 if (identifier != null) { |
2407 assert(identifier.isIdentifier()); | 2401 assert(identifier.isIdentifier()); |
| 2402 |
| 2403 // If the identifier token has a type substitution comment /*=T*/, |
| 2404 // then the set of tokens type tokens should be replaced with the |
| 2405 // tokens parsed from the comment. |
| 2406 token = |
| 2407 listener.replaceTokenWithGenericCommentTypeAssign(token, identifier); |
| 2408 |
2408 Token afterId = identifier.next; | 2409 Token afterId = identifier.next; |
2409 int afterIdKind = afterId.kind; | 2410 int afterIdKind = afterId.kind; |
2410 if (identical(afterIdKind, EQ_TOKEN) || | 2411 if (identical(afterIdKind, EQ_TOKEN) || |
2411 identical(afterIdKind, SEMICOLON_TOKEN) || | 2412 identical(afterIdKind, SEMICOLON_TOKEN) || |
2412 identical(afterIdKind, COMMA_TOKEN)) { | 2413 identical(afterIdKind, COMMA_TOKEN)) { |
2413 // We are looking at "type identifier" followed by '=', ';', ','. | 2414 // We are looking at "type identifier" followed by '=', ';', ','. |
2414 return parseVariablesDeclaration(token); | 2415 return parseVariablesDeclaration(token); |
2415 } else if (identical(afterIdKind, OPEN_PAREN_TOKEN)) { | 2416 } else if (identical(afterIdKind, OPEN_PAREN_TOKEN)) { |
2416 // We are looking at "type identifier '('". | 2417 // We are looking at "type identifier '('". |
2417 BeginGroupToken beginParen = afterId; | 2418 BeginGroupToken beginParen = afterId; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2482 } | 2483 } |
2483 } | 2484 } |
2484 return parseExpressionStatement(token); | 2485 return parseExpressionStatement(token); |
2485 } | 2486 } |
2486 | 2487 |
2487 Token parseExpressionStatementOrConstDeclaration(Token token) { | 2488 Token parseExpressionStatementOrConstDeclaration(Token token) { |
2488 assert(identical(token.stringValue, 'const')); | 2489 assert(identical(token.stringValue, 'const')); |
2489 if (isModifier(token.next)) { | 2490 if (isModifier(token.next)) { |
2490 return parseVariablesDeclaration(token); | 2491 return parseVariablesDeclaration(token); |
2491 } | 2492 } |
| 2493 listener.injectGenericCommentTypeAssign(token.next); |
2492 Token identifier = peekIdentifierAfterOptionalType(token.next); | 2494 Token identifier = peekIdentifierAfterOptionalType(token.next); |
2493 if (identifier != null) { | 2495 if (identifier != null) { |
2494 assert(identifier.isIdentifier()); | 2496 assert(identifier.isIdentifier()); |
2495 Token afterId = identifier.next; | 2497 Token afterId = identifier.next; |
2496 int afterIdKind = afterId.kind; | 2498 int afterIdKind = afterId.kind; |
2497 if (identical(afterIdKind, EQ_TOKEN) || | 2499 if (identical(afterIdKind, EQ_TOKEN) || |
2498 identical(afterIdKind, SEMICOLON_TOKEN) || | 2500 identical(afterIdKind, SEMICOLON_TOKEN) || |
2499 identical(afterIdKind, COMMA_TOKEN)) { | 2501 identical(afterIdKind, COMMA_TOKEN)) { |
2500 // We are looking at "const type identifier" followed by '=', ';', or | 2502 // We are looking at "const type identifier" followed by '=', ';', or |
2501 // ','. | 2503 // ','. |
(...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2790 token = parseArguments(token); | 2792 token = parseArguments(token); |
2791 listener.endSend(beginToken, token); | 2793 listener.endSend(beginToken, token); |
2792 } else { | 2794 } else { |
2793 break; | 2795 break; |
2794 } | 2796 } |
2795 } | 2797 } |
2796 return token; | 2798 return token; |
2797 } | 2799 } |
2798 | 2800 |
2799 Token parsePrimary(Token token, IdentifierContext context) { | 2801 Token parsePrimary(Token token, IdentifierContext context) { |
2800 token = _injectGenericCommentTypeList(token); | 2802 token = listener.injectGenericCommentTypeList(token); |
2801 final kind = token.kind; | 2803 final kind = token.kind; |
2802 if (kind == IDENTIFIER_TOKEN) { | 2804 if (kind == IDENTIFIER_TOKEN) { |
2803 return parseSendOrFunctionLiteral(token, context); | 2805 return parseSendOrFunctionLiteral(token, context); |
2804 } else if (kind == INT_TOKEN || kind == HEXADECIMAL_TOKEN) { | 2806 } else if (kind == INT_TOKEN || kind == HEXADECIMAL_TOKEN) { |
2805 return parseLiteralInt(token); | 2807 return parseLiteralInt(token); |
2806 } else if (kind == DOUBLE_TOKEN) { | 2808 } else if (kind == DOUBLE_TOKEN) { |
2807 return parseLiteralDouble(token); | 2809 return parseLiteralDouble(token); |
2808 } else if (kind == STRING_TOKEN) { | 2810 } else if (kind == STRING_TOKEN) { |
2809 return parseLiteralString(token); | 2811 return parseLiteralString(token); |
2810 } else if (kind == HASH_TOKEN) { | 2812 } else if (kind == HASH_TOKEN) { |
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3083 listener.beginNewExpression(newKeyword); | 3085 listener.beginNewExpression(newKeyword); |
3084 token = parseConstructorReference(token); | 3086 token = parseConstructorReference(token); |
3085 token = parseRequiredArguments(token); | 3087 token = parseRequiredArguments(token); |
3086 listener.endNewExpression(newKeyword); | 3088 listener.endNewExpression(newKeyword); |
3087 return token; | 3089 return token; |
3088 } | 3090 } |
3089 | 3091 |
3090 Token parseConstExpression(Token token) { | 3092 Token parseConstExpression(Token token) { |
3091 Token constKeyword = token; | 3093 Token constKeyword = token; |
3092 token = expect('const', token); | 3094 token = expect('const', token); |
3093 token = _injectGenericCommentTypeList(token); | 3095 token = listener.injectGenericCommentTypeList(token); |
3094 final String value = token.stringValue; | 3096 final String value = token.stringValue; |
3095 if ((identical(value, '[')) || (identical(value, '[]'))) { | 3097 if ((identical(value, '[')) || (identical(value, '[]'))) { |
3096 listener.beginConstLiteral(token); | 3098 listener.beginConstLiteral(token); |
3097 listener.handleNoTypeArguments(token); | 3099 listener.handleNoTypeArguments(token); |
3098 token = parseLiteralListSuffix(token, constKeyword); | 3100 token = parseLiteralListSuffix(token, constKeyword); |
3099 listener.endConstLiteral(token); | 3101 listener.endConstLiteral(token); |
3100 return token; | 3102 return token; |
3101 } | 3103 } |
3102 if (identical(value, '{')) { | 3104 if (identical(value, '{')) { |
3103 listener.beginConstLiteral(token); | 3105 listener.beginConstLiteral(token); |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3206 | 3208 |
3207 Token parseLiteralNull(Token token) { | 3209 Token parseLiteralNull(Token token) { |
3208 listener.handleLiteralNull(token); | 3210 listener.handleLiteralNull(token); |
3209 return token.next; | 3211 return token.next; |
3210 } | 3212 } |
3211 | 3213 |
3212 Token parseSend(Token token, IdentifierContext context) { | 3214 Token parseSend(Token token, IdentifierContext context) { |
3213 Token beginToken = token; | 3215 Token beginToken = token; |
3214 listener.beginSend(token); | 3216 listener.beginSend(token); |
3215 token = parseIdentifier(token, context); | 3217 token = parseIdentifier(token, context); |
3216 token = _injectGenericCommentTypeList(token); | 3218 token = listener.injectGenericCommentTypeList(token); |
3217 if (isValidMethodTypeArguments(token)) { | 3219 if (isValidMethodTypeArguments(token)) { |
3218 token = parseTypeArgumentsOpt(token); | 3220 token = parseTypeArgumentsOpt(token); |
3219 } else { | 3221 } else { |
3220 listener.handleNoTypeArguments(token); | 3222 listener.handleNoTypeArguments(token); |
3221 } | 3223 } |
3222 token = parseArgumentsOpt(token); | 3224 token = parseArgumentsOpt(token); |
3223 listener.endSend(beginToken, token); | 3225 listener.endSend(beginToken, token); |
3224 return token; | 3226 return token; |
3225 } | 3227 } |
3226 | 3228 |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3312 | 3314 |
3313 Token parseVariablesDeclarationNoSemicolon(Token token) { | 3315 Token parseVariablesDeclarationNoSemicolon(Token token) { |
3314 // Only called when parsing a for loop, so this is for parsing locals. | 3316 // Only called when parsing a for loop, so this is for parsing locals. |
3315 return parseVariablesDeclarationMaybeSemicolon(token, false); | 3317 return parseVariablesDeclarationMaybeSemicolon(token, false); |
3316 } | 3318 } |
3317 | 3319 |
3318 Token parseVariablesDeclarationMaybeSemicolon( | 3320 Token parseVariablesDeclarationMaybeSemicolon( |
3319 Token token, bool endWithSemicolon) { | 3321 Token token, bool endWithSemicolon) { |
3320 int count = 1; | 3322 int count = 1; |
3321 token = parseMetadataStar(token); | 3323 token = parseMetadataStar(token); |
| 3324 |
| 3325 // If the next token has a type substitution comment /*=T*/, then |
| 3326 // the current 'var' token should be repealed and replaced. |
| 3327 if (identical('var', token.stringValue)) { |
| 3328 token = |
| 3329 listener.replaceTokenWithGenericCommentTypeAssign(token, token.next); |
| 3330 } |
| 3331 |
3322 token = parseModifiers(token); | 3332 token = parseModifiers(token); |
3323 token = parseTypeOpt(token); | 3333 token = parseTypeOpt(token); |
3324 listener.beginVariablesDeclaration(token); | 3334 listener.beginVariablesDeclaration(token); |
3325 token = parseOptionallyInitializedIdentifier(token); | 3335 token = parseOptionallyInitializedIdentifier(token); |
3326 while (optional(',', token)) { | 3336 while (optional(',', token)) { |
3327 token = parseOptionallyInitializedIdentifier(token.next); | 3337 token = parseOptionallyInitializedIdentifier(token.next); |
3328 ++count; | 3338 ++count; |
3329 } | 3339 } |
3330 if (endWithSemicolon) { | 3340 if (endWithSemicolon) { |
3331 Token semicolon = token; | 3341 Token semicolon = token; |
(...skipping 472 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3804 Token token, FastaCode<TokenArgument> code) { | 3814 Token token, FastaCode<TokenArgument> code) { |
3805 return reportUnrecoverableError( | 3815 return reportUnrecoverableError( |
3806 token, () => code.format(uri, token.charOffset, token)); | 3816 token, () => code.format(uri, token.charOffset, token)); |
3807 } | 3817 } |
3808 | 3818 |
3809 Token reportUnrecoverableErrorCodeWithString( | 3819 Token reportUnrecoverableErrorCodeWithString( |
3810 Token token, FastaCode<StringArgument> code, String string) { | 3820 Token token, FastaCode<StringArgument> code, String string) { |
3811 return reportUnrecoverableError( | 3821 return reportUnrecoverableError( |
3812 token, () => code.format(uri, token.charOffset, string)); | 3822 token, () => code.format(uri, token.charOffset, string)); |
3813 } | 3823 } |
3814 | |
3815 /// Matches a generic comment type parameters or type arguments and injects | |
3816 /// them into the token stream before the given [token]. | |
3817 Token _injectGenericCommentTypeList(Token token) { | |
3818 return _injectGenericComment(token, GENERIC_METHOD_TYPE_LIST, 2); | |
3819 } | |
3820 | |
3821 /// Check if the given [token] has a comment token with the given [info], | |
3822 /// which should be either [GENERIC_METHOD_TYPE_ASSIGN] or | |
3823 /// [GENERIC_METHOD_TYPE_LIST]. If found, parse the comment into tokens and | |
3824 /// inject into the token stream before the [token]. | |
3825 Token _injectGenericComment(Token token, PrecedenceInfo info, int prefixLen) { | |
3826 if (parseGenericMethodComments) { | |
3827 CommentToken t = token.precedingCommentTokens; | |
3828 for (; t != null; t = t.next) { | |
3829 if (t.info == info) { | |
3830 String code = t.lexeme.substring(prefixLen, t.lexeme.length - 2); | |
3831 Token tokens = _scanGenericMethodComment(code, t.offset + prefixLen); | |
3832 if (tokens != null) { | |
3833 // Remove the token from the comment stream. | |
3834 t.remove(); | |
3835 // Insert the tokens into the stream. | |
3836 _injectTokenList(token, tokens); | |
3837 return tokens; | |
3838 } | |
3839 } | |
3840 } | |
3841 } | |
3842 return token; | |
3843 } | |
3844 | |
3845 /// Scans the given [code], and returns the tokens, otherwise returns `null`. | |
3846 Token _scanGenericMethodComment(String code, int offset) { | |
3847 var scanner = new SubStringScanner(offset, code); | |
3848 Token firstToken = scanner.tokenize(); | |
3849 if (scanner.hasErrors) { | |
3850 return null; | |
3851 } | |
3852 return firstToken; | |
3853 } | |
3854 | |
3855 void _injectTokenList(Token beforeToken, Token firstToken) { | |
3856 // Scanner creates a cyclic EOF token. | |
3857 Token lastToken = firstToken; | |
3858 while (lastToken.next.info != EOF_INFO) { | |
3859 lastToken = lastToken.next; | |
3860 } | |
3861 // Inject these new tokens into the stream. | |
3862 Token previous = beforeToken.previous; | |
3863 lastToken.setNext(beforeToken); | |
3864 previous.setNext(firstToken); | |
3865 beforeToken = firstToken; | |
3866 } | |
3867 } | 3824 } |
3868 | 3825 |
3869 typedef FastaMessage NoArgument(Uri uri, int charOffset); | 3826 typedef FastaMessage NoArgument(Uri uri, int charOffset); |
3870 | 3827 |
3871 typedef FastaMessage TokenArgument(Uri uri, int charOffset, Token token); | 3828 typedef FastaMessage TokenArgument(Uri uri, int charOffset, Token token); |
3872 | 3829 |
3873 typedef FastaMessage StringArgument(Uri uri, int charOffset, String string); | 3830 typedef FastaMessage StringArgument(Uri uri, int charOffset, String string); |
OLD | NEW |