Chromium Code Reviews| Index: pkg/front_end/lib/src/fasta/parser/parser.dart |
| diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart |
| index e6dba6a39dc1d8cd5fc6c8321fba028110f6bda5..ecf6fc15339d4f334ec5a4e9a6b0bb794b508bf3 100644 |
| --- a/pkg/front_end/lib/src/fasta/parser/parser.dart |
| +++ b/pkg/front_end/lib/src/fasta/parser/parser.dart |
| @@ -65,7 +65,10 @@ import '../scanner/precedence.dart' |
| ASSIGNMENT_PRECEDENCE, |
| AS_INFO, |
| CASCADE_PRECEDENCE, |
| + EOF_INFO, |
| EQUALITY_PRECEDENCE, |
| + GENERIC_METHOD_TYPE_ASSIGN, |
| + GENERIC_METHOD_TYPE_LIST, |
| GT_INFO, |
| IS_INFO, |
| MINUS_MINUS_INFO, |
| @@ -83,6 +86,7 @@ import '../scanner/precedence.dart' |
| import '../scanner/token.dart' |
| show |
| BeginGroupToken, |
| + CommentToken, |
| KeywordToken, |
| SymbolToken, |
| Token, |
| @@ -113,6 +117,7 @@ import '../scanner/token_constants.dart' |
| STRING_TOKEN; |
| import '../scanner/characters.dart' show $CLOSE_CURLY_BRACKET; |
| +import '../scanner/string_scanner.dart'; |
|
ahe
2017/04/04 08:10:45
The parser shouldn't be importing as specific scan
scheglov
2017/04/04 15:33:47
Why?
The parser knows what it wants to parse and h
|
| import '../util/link.dart' show Link; |
| @@ -184,6 +189,8 @@ class Parser { |
| bool mayParseFunctionExpressions = true; |
| + bool parseGenericMethodComments = false; |
| + |
| /// Represents parser state: what asynchronous syntax is allowed in the |
| /// function being currently parsed. In rare situations, this can be set by |
| /// external clients, for example, to parse an expression outside a function. |
| @@ -633,6 +640,7 @@ class Parser { |
| } |
| } |
| + token = _injectGenericCommentTypeList(token); |
|
ahe
2017/04/04 08:10:45
This method should be public and overridden in a s
scheglov
2017/04/04 15:33:47
In which subclass?
|
| if (optional('(', token)) { |
| Token inlineFunctionTypeStart = token; |
| listener.beginFunctionTypedFormalParameter(token); |
| @@ -1107,9 +1115,10 @@ class Parser { |
| (t) => listener.handleNoTypeVariables(t)); |
| } |
| - // TODO(ahe): Clean this up. |
| + /// TODO(ahe): Clean this up. |
|
ahe
2017/04/04 08:10:45
This isn't a documentation comment.
scheglov
2017/04/04 15:33:47
Well, I think any comment before a method, and whi
|
| Token parseStuff(Token token, Function beginStuff, Function stuffParser, |
| Function endStuff, Function handleNoStuff) { |
| + token = _injectGenericCommentTypeList(token); |
| if (optional('<', token)) { |
| Token begin = token; |
| beginStuff(begin); |
| @@ -2788,6 +2797,7 @@ class Parser { |
| } |
| Token parsePrimary(Token token, IdentifierContext context) { |
| + token = _injectGenericCommentTypeList(token); |
| final kind = token.kind; |
| if (kind == IDENTIFIER_TOKEN) { |
| return parseSendOrFunctionLiteral(token, context); |
| @@ -3080,6 +3090,7 @@ class Parser { |
| Token parseConstExpression(Token token) { |
| Token constKeyword = token; |
| token = expect('const', token); |
| + token = _injectGenericCommentTypeList(token); |
| final String value = token.stringValue; |
| if ((identical(value, '[')) || (identical(value, '[]'))) { |
| listener.handleNoTypeArguments(token); |
| @@ -3193,6 +3204,7 @@ class Parser { |
| Token beginToken = token; |
| listener.beginSend(token); |
| token = parseIdentifier(token, context); |
| + token = _injectGenericCommentTypeList(token); |
| if (isValidMethodTypeArguments(token)) { |
| token = parseTypeArgumentsOpt(token); |
| } else { |
| @@ -3789,6 +3801,60 @@ class Parser { |
| return reportUnrecoverableError( |
| token, () => code.format(uri, token.charOffset, string)); |
| } |
| + |
| + /// Matches a generic comment type parameters or type arguments and injects |
| + /// them into the token stream before the given [token]. |
| + Token _injectGenericCommentTypeList(Token token) { |
| + return _injectGenericComment(token, GENERIC_METHOD_TYPE_LIST, 2); |
| + } |
| + |
| + /// Check if the given [token] has a comment token with the given [info], |
| + /// which should be either [GENERIC_METHOD_TYPE_ASSIGN] or |
| + /// [GENERIC_METHOD_TYPE_LIST]. If found, parse the comment into tokens and |
| + /// inject into the token stream before the [token]. |
| + Token _injectGenericComment(Token token, PrecedenceInfo info, int prefixLen) { |
| + if (parseGenericMethodComments) { |
| + CommentToken t = token.precedingCommentTokens; |
| + for (; t != null; t = t.next) { |
| + if (t.info == info) { |
| + String code = t.lexeme.substring(prefixLen, t.lexeme.length - 2); |
| + Token tokens = _scanGenericMethodComment(code, t.offset + prefixLen); |
| + if (tokens != null) { |
| + // Remove the token from the comment stream. |
| + t.remove(); |
| + // Insert the tokens into the stream. |
| + _injectTokenList(token, tokens); |
| + return tokens; |
| + } |
| + } |
| + } |
| + } |
| + return token; |
| + } |
| + |
| + /// Scans the given [code], and returns the tokens, otherwise returns `null`. |
| + Token _scanGenericMethodComment(String code, int offset) { |
| + // TODO(scheglov) Let StringScanner to specify the string offset. |
| + var scanner = new StringScanner(' ' * offset + code); |
|
ahe
2017/04/04 08:10:45
The listener should be asked how to create a scann
scheglov
2017/04/04 15:33:47
What do we want to extend?
These comments are part
|
| + Token firstToken = scanner.tokenize(); |
| + if (scanner.hasErrors) { |
| + return null; |
| + } |
| + return firstToken; |
| + } |
| + |
| + void _injectTokenList(Token beforeToken, Token firstToken) { |
| + // Scanner creates a cyclic EOF token. |
| + Token lastToken = firstToken; |
| + while (lastToken.next.info != EOF_INFO) { |
| + lastToken = lastToken.next; |
| + } |
| + // Inject these new tokens into the stream. |
| + Token previous = beforeToken.previous; |
| + lastToken.setNext(beforeToken); |
| + previous.setNext(firstToken); |
| + beforeToken = firstToken; |
| + } |
| } |
| typedef FastaMessage NoArgument(Uri uri, int charOffset); |