Chromium Code Reviews| 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, | |
| 68 EQUALITY_PRECEDENCE, | 69 EQUALITY_PRECEDENCE, |
| 70 GENERIC_METHOD_TYPE_ASSIGN, | |
| 71 GENERIC_METHOD_TYPE_LIST, | |
| 69 GT_INFO, | 72 GT_INFO, |
| 70 IS_INFO, | 73 IS_INFO, |
| 71 MINUS_MINUS_INFO, | 74 MINUS_MINUS_INFO, |
| 72 OPEN_PAREN_INFO, | 75 OPEN_PAREN_INFO, |
| 73 OPEN_SQUARE_BRACKET_INFO, | 76 OPEN_SQUARE_BRACKET_INFO, |
| 74 PERIOD_INFO, | 77 PERIOD_INFO, |
| 75 PLUS_PLUS_INFO, | 78 PLUS_PLUS_INFO, |
| 76 POSTFIX_PRECEDENCE, | 79 POSTFIX_PRECEDENCE, |
| 77 PrecedenceInfo, | 80 PrecedenceInfo, |
| 78 QUESTION_INFO, | 81 QUESTION_INFO, |
| 79 QUESTION_PERIOD_INFO, | 82 QUESTION_PERIOD_INFO, |
| 80 RELATIONAL_PRECEDENCE, | 83 RELATIONAL_PRECEDENCE, |
| 81 SCRIPT_INFO; | 84 SCRIPT_INFO; |
| 82 | 85 |
| 83 import '../scanner/token.dart' | 86 import '../scanner/token.dart' |
| 84 show | 87 show |
| 85 BeginGroupToken, | 88 BeginGroupToken, |
| 89 CommentToken, | |
| 86 KeywordToken, | 90 KeywordToken, |
| 87 SymbolToken, | 91 SymbolToken, |
| 88 Token, | 92 Token, |
| 89 isUserDefinableOperator; | 93 isUserDefinableOperator; |
| 90 | 94 |
| 91 import '../scanner/token_constants.dart' | 95 import '../scanner/token_constants.dart' |
| 92 show | 96 show |
| 93 COMMA_TOKEN, | 97 COMMA_TOKEN, |
| 94 DOUBLE_TOKEN, | 98 DOUBLE_TOKEN, |
| 95 EOF_TOKEN, | 99 EOF_TOKEN, |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 106 OPEN_CURLY_BRACKET_TOKEN, | 110 OPEN_CURLY_BRACKET_TOKEN, |
| 107 OPEN_PAREN_TOKEN, | 111 OPEN_PAREN_TOKEN, |
| 108 OPEN_SQUARE_BRACKET_TOKEN, | 112 OPEN_SQUARE_BRACKET_TOKEN, |
| 109 PERIOD_TOKEN, | 113 PERIOD_TOKEN, |
| 110 SEMICOLON_TOKEN, | 114 SEMICOLON_TOKEN, |
| 111 STRING_INTERPOLATION_IDENTIFIER_TOKEN, | 115 STRING_INTERPOLATION_IDENTIFIER_TOKEN, |
| 112 STRING_INTERPOLATION_TOKEN, | 116 STRING_INTERPOLATION_TOKEN, |
| 113 STRING_TOKEN; | 117 STRING_TOKEN; |
| 114 | 118 |
| 115 import '../scanner/characters.dart' show $CLOSE_CURLY_BRACKET; | 119 import '../scanner/characters.dart' show $CLOSE_CURLY_BRACKET; |
| 120 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
| |
| 116 | 121 |
| 117 import '../util/link.dart' show Link; | 122 import '../util/link.dart' show Link; |
| 118 | 123 |
| 119 import 'async_modifier.dart' show AsyncModifier; | 124 import 'async_modifier.dart' show AsyncModifier; |
| 120 | 125 |
| 121 import 'listener.dart' show Listener; | 126 import 'listener.dart' show Listener; |
| 122 | 127 |
| 123 import 'identifier_context.dart' show IdentifierContext; | 128 import 'identifier_context.dart' show IdentifierContext; |
| 124 | 129 |
| 125 /// Returns true if [token] is the symbol or keyword [value]. | 130 /// Returns true if [token] is the symbol or keyword [value]. |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 177 /// | 182 /// |
| 178 /// Historically, we over-used identical, and when identical is used on other | 183 /// Historically, we over-used identical, and when identical is used on other |
| 179 /// objects than strings, it can often be replaced by `==`. | 184 /// objects than strings, it can often be replaced by `==`. |
| 180 class Parser { | 185 class Parser { |
| 181 final Listener listener; | 186 final Listener listener; |
| 182 | 187 |
| 183 Uri get uri => listener.uri; | 188 Uri get uri => listener.uri; |
| 184 | 189 |
| 185 bool mayParseFunctionExpressions = true; | 190 bool mayParseFunctionExpressions = true; |
| 186 | 191 |
| 192 bool parseGenericMethodComments = false; | |
| 193 | |
| 187 /// Represents parser state: what asynchronous syntax is allowed in the | 194 /// Represents parser state: what asynchronous syntax is allowed in the |
| 188 /// function being currently parsed. In rare situations, this can be set by | 195 /// function being currently parsed. In rare situations, this can be set by |
| 189 /// external clients, for example, to parse an expression outside a function. | 196 /// external clients, for example, to parse an expression outside a function. |
| 190 AsyncModifier asyncState = AsyncModifier.Sync; | 197 AsyncModifier asyncState = AsyncModifier.Sync; |
| 191 | 198 |
| 192 Parser(this.listener); | 199 Parser(this.listener); |
| 193 | 200 |
| 194 bool get inGenerator { | 201 bool get inGenerator { |
| 195 return asyncState == AsyncModifier.AsyncStar || | 202 return asyncState == AsyncModifier.AsyncStar || |
| 196 asyncState == AsyncModifier.SyncStar; | 203 asyncState == AsyncModifier.SyncStar; |
| (...skipping 429 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 626 token = expect('.', token.next); | 633 token = expect('.', token.next); |
| 627 nameToken = token; | 634 nameToken = token; |
| 628 token = parseIdentifier(token, IdentifierContext.fieldInitializer); | 635 token = parseIdentifier(token, IdentifierContext.fieldInitializer); |
| 629 } else { | 636 } else { |
| 630 nameToken = token; | 637 nameToken = token; |
| 631 token = parseIdentifier( | 638 token = parseIdentifier( |
| 632 token, IdentifierContext.formalParameterDeclaration); | 639 token, IdentifierContext.formalParameterDeclaration); |
| 633 } | 640 } |
| 634 } | 641 } |
| 635 | 642 |
| 643 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?
| |
| 636 if (optional('(', token)) { | 644 if (optional('(', token)) { |
| 637 Token inlineFunctionTypeStart = token; | 645 Token inlineFunctionTypeStart = token; |
| 638 listener.beginFunctionTypedFormalParameter(token); | 646 listener.beginFunctionTypedFormalParameter(token); |
| 639 listener.handleNoTypeVariables(token); | 647 listener.handleNoTypeVariables(token); |
| 640 token = parseFormalParameters(token); | 648 token = parseFormalParameters(token); |
| 641 listener.endFunctionTypedFormalParameter( | 649 listener.endFunctionTypedFormalParameter( |
| 642 covariantKeyword, thisKeyword, kind); | 650 covariantKeyword, thisKeyword, kind); |
| 643 // Generalized function types don't allow inline function types. | 651 // Generalized function types don't allow inline function types. |
| 644 // The following isn't allowed: | 652 // The following isn't allowed: |
| 645 // int Function(int bar(String x)). | 653 // int Function(int bar(String x)). |
| (...skipping 454 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1100 | 1108 |
| 1101 Token parseTypeVariablesOpt(Token token) { | 1109 Token parseTypeVariablesOpt(Token token) { |
| 1102 return parseStuff( | 1110 return parseStuff( |
| 1103 token, | 1111 token, |
| 1104 (t) => listener.beginTypeVariables(t), | 1112 (t) => listener.beginTypeVariables(t), |
| 1105 (t) => parseTypeVariable(t), | 1113 (t) => parseTypeVariable(t), |
| 1106 (c, bt, et) => listener.endTypeVariables(c, bt, et), | 1114 (c, bt, et) => listener.endTypeVariables(c, bt, et), |
| 1107 (t) => listener.handleNoTypeVariables(t)); | 1115 (t) => listener.handleNoTypeVariables(t)); |
| 1108 } | 1116 } |
| 1109 | 1117 |
| 1110 // TODO(ahe): Clean this up. | 1118 /// 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
| |
| 1111 Token parseStuff(Token token, Function beginStuff, Function stuffParser, | 1119 Token parseStuff(Token token, Function beginStuff, Function stuffParser, |
| 1112 Function endStuff, Function handleNoStuff) { | 1120 Function endStuff, Function handleNoStuff) { |
| 1121 token = _injectGenericCommentTypeList(token); | |
| 1113 if (optional('<', token)) { | 1122 if (optional('<', token)) { |
| 1114 Token begin = token; | 1123 Token begin = token; |
| 1115 beginStuff(begin); | 1124 beginStuff(begin); |
| 1116 int count = 0; | 1125 int count = 0; |
| 1117 do { | 1126 do { |
| 1118 token = stuffParser(token.next); | 1127 token = stuffParser(token.next); |
| 1119 ++count; | 1128 ++count; |
| 1120 } while (optional(',', token)); | 1129 } while (optional(',', token)); |
| 1121 Token next = token.next; | 1130 Token next = token.next; |
| 1122 if (identical(token.stringValue, '>>')) { | 1131 if (identical(token.stringValue, '>>')) { |
| (...skipping 1658 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2781 token = parseArguments(token); | 2790 token = parseArguments(token); |
| 2782 listener.endSend(beginToken, token); | 2791 listener.endSend(beginToken, token); |
| 2783 } else { | 2792 } else { |
| 2784 break; | 2793 break; |
| 2785 } | 2794 } |
| 2786 } | 2795 } |
| 2787 return token; | 2796 return token; |
| 2788 } | 2797 } |
| 2789 | 2798 |
| 2790 Token parsePrimary(Token token, IdentifierContext context) { | 2799 Token parsePrimary(Token token, IdentifierContext context) { |
| 2800 token = _injectGenericCommentTypeList(token); | |
| 2791 final kind = token.kind; | 2801 final kind = token.kind; |
| 2792 if (kind == IDENTIFIER_TOKEN) { | 2802 if (kind == IDENTIFIER_TOKEN) { |
| 2793 return parseSendOrFunctionLiteral(token, context); | 2803 return parseSendOrFunctionLiteral(token, context); |
| 2794 } else if (kind == INT_TOKEN || kind == HEXADECIMAL_TOKEN) { | 2804 } else if (kind == INT_TOKEN || kind == HEXADECIMAL_TOKEN) { |
| 2795 return parseLiteralInt(token); | 2805 return parseLiteralInt(token); |
| 2796 } else if (kind == DOUBLE_TOKEN) { | 2806 } else if (kind == DOUBLE_TOKEN) { |
| 2797 return parseLiteralDouble(token); | 2807 return parseLiteralDouble(token); |
| 2798 } else if (kind == STRING_TOKEN) { | 2808 } else if (kind == STRING_TOKEN) { |
| 2799 return parseLiteralString(token); | 2809 return parseLiteralString(token); |
| 2800 } else if (kind == HASH_TOKEN) { | 2810 } else if (kind == HASH_TOKEN) { |
| (...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3073 listener.beginNewExpression(newKeyword); | 3083 listener.beginNewExpression(newKeyword); |
| 3074 token = parseConstructorReference(token); | 3084 token = parseConstructorReference(token); |
| 3075 token = parseRequiredArguments(token); | 3085 token = parseRequiredArguments(token); |
| 3076 listener.endNewExpression(newKeyword); | 3086 listener.endNewExpression(newKeyword); |
| 3077 return token; | 3087 return token; |
| 3078 } | 3088 } |
| 3079 | 3089 |
| 3080 Token parseConstExpression(Token token) { | 3090 Token parseConstExpression(Token token) { |
| 3081 Token constKeyword = token; | 3091 Token constKeyword = token; |
| 3082 token = expect('const', token); | 3092 token = expect('const', token); |
| 3093 token = _injectGenericCommentTypeList(token); | |
| 3083 final String value = token.stringValue; | 3094 final String value = token.stringValue; |
| 3084 if ((identical(value, '[')) || (identical(value, '[]'))) { | 3095 if ((identical(value, '[')) || (identical(value, '[]'))) { |
| 3085 listener.handleNoTypeArguments(token); | 3096 listener.handleNoTypeArguments(token); |
| 3086 return parseLiteralListSuffix(token, constKeyword); | 3097 return parseLiteralListSuffix(token, constKeyword); |
| 3087 } | 3098 } |
| 3088 if (identical(value, '{')) { | 3099 if (identical(value, '{')) { |
| 3089 listener.handleNoTypeArguments(token); | 3100 listener.handleNoTypeArguments(token); |
| 3090 return parseLiteralMapSuffix(token, constKeyword); | 3101 return parseLiteralMapSuffix(token, constKeyword); |
| 3091 } | 3102 } |
| 3092 if (identical(value, '<')) { | 3103 if (identical(value, '<')) { |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3186 | 3197 |
| 3187 Token parseLiteralNull(Token token) { | 3198 Token parseLiteralNull(Token token) { |
| 3188 listener.handleLiteralNull(token); | 3199 listener.handleLiteralNull(token); |
| 3189 return token.next; | 3200 return token.next; |
| 3190 } | 3201 } |
| 3191 | 3202 |
| 3192 Token parseSend(Token token, IdentifierContext context) { | 3203 Token parseSend(Token token, IdentifierContext context) { |
| 3193 Token beginToken = token; | 3204 Token beginToken = token; |
| 3194 listener.beginSend(token); | 3205 listener.beginSend(token); |
| 3195 token = parseIdentifier(token, context); | 3206 token = parseIdentifier(token, context); |
| 3207 token = _injectGenericCommentTypeList(token); | |
| 3196 if (isValidMethodTypeArguments(token)) { | 3208 if (isValidMethodTypeArguments(token)) { |
| 3197 token = parseTypeArgumentsOpt(token); | 3209 token = parseTypeArgumentsOpt(token); |
| 3198 } else { | 3210 } else { |
| 3199 listener.handleNoTypeArguments(token); | 3211 listener.handleNoTypeArguments(token); |
| 3200 } | 3212 } |
| 3201 token = parseArgumentsOpt(token); | 3213 token = parseArgumentsOpt(token); |
| 3202 listener.endSend(beginToken, token); | 3214 listener.endSend(beginToken, token); |
| 3203 return token; | 3215 return token; |
| 3204 } | 3216 } |
| 3205 | 3217 |
| (...skipping 576 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3782 Token token, FastaCode<TokenArgument> code) { | 3794 Token token, FastaCode<TokenArgument> code) { |
| 3783 return reportUnrecoverableError( | 3795 return reportUnrecoverableError( |
| 3784 token, () => code.format(uri, token.charOffset, token)); | 3796 token, () => code.format(uri, token.charOffset, token)); |
| 3785 } | 3797 } |
| 3786 | 3798 |
| 3787 Token reportUnrecoverableErrorCodeWithString( | 3799 Token reportUnrecoverableErrorCodeWithString( |
| 3788 Token token, FastaCode<StringArgument> code, String string) { | 3800 Token token, FastaCode<StringArgument> code, String string) { |
| 3789 return reportUnrecoverableError( | 3801 return reportUnrecoverableError( |
| 3790 token, () => code.format(uri, token.charOffset, string)); | 3802 token, () => code.format(uri, token.charOffset, string)); |
| 3791 } | 3803 } |
| 3804 | |
| 3805 /// Matches a generic comment type parameters or type arguments and injects | |
| 3806 /// them into the token stream before the given [token]. | |
| 3807 Token _injectGenericCommentTypeList(Token token) { | |
| 3808 return _injectGenericComment(token, GENERIC_METHOD_TYPE_LIST, 2); | |
| 3809 } | |
| 3810 | |
| 3811 /// Check if the given [token] has a comment token with the given [info], | |
| 3812 /// which should be either [GENERIC_METHOD_TYPE_ASSIGN] or | |
| 3813 /// [GENERIC_METHOD_TYPE_LIST]. If found, parse the comment into tokens and | |
| 3814 /// inject into the token stream before the [token]. | |
| 3815 Token _injectGenericComment(Token token, PrecedenceInfo info, int prefixLen) { | |
| 3816 if (parseGenericMethodComments) { | |
| 3817 CommentToken t = token.precedingCommentTokens; | |
| 3818 for (; t != null; t = t.next) { | |
| 3819 if (t.info == info) { | |
| 3820 String code = t.lexeme.substring(prefixLen, t.lexeme.length - 2); | |
| 3821 Token tokens = _scanGenericMethodComment(code, t.offset + prefixLen); | |
| 3822 if (tokens != null) { | |
| 3823 // Remove the token from the comment stream. | |
| 3824 t.remove(); | |
| 3825 // Insert the tokens into the stream. | |
| 3826 _injectTokenList(token, tokens); | |
| 3827 return tokens; | |
| 3828 } | |
| 3829 } | |
| 3830 } | |
| 3831 } | |
| 3832 return token; | |
| 3833 } | |
| 3834 | |
| 3835 /// Scans the given [code], and returns the tokens, otherwise returns `null`. | |
| 3836 Token _scanGenericMethodComment(String code, int offset) { | |
| 3837 // TODO(scheglov) Let StringScanner to specify the string offset. | |
| 3838 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
| |
| 3839 Token firstToken = scanner.tokenize(); | |
| 3840 if (scanner.hasErrors) { | |
| 3841 return null; | |
| 3842 } | |
| 3843 return firstToken; | |
| 3844 } | |
| 3845 | |
| 3846 void _injectTokenList(Token beforeToken, Token firstToken) { | |
| 3847 // Scanner creates a cyclic EOF token. | |
| 3848 Token lastToken = firstToken; | |
| 3849 while (lastToken.next.info != EOF_INFO) { | |
| 3850 lastToken = lastToken.next; | |
| 3851 } | |
| 3852 // Inject these new tokens into the stream. | |
| 3853 Token previous = beforeToken.previous; | |
| 3854 lastToken.setNext(beforeToken); | |
| 3855 previous.setNext(firstToken); | |
| 3856 beforeToken = firstToken; | |
| 3857 } | |
| 3792 } | 3858 } |
| 3793 | 3859 |
| 3794 typedef FastaMessage NoArgument(Uri uri, int charOffset); | 3860 typedef FastaMessage NoArgument(Uri uri, int charOffset); |
| 3795 | 3861 |
| 3796 typedef FastaMessage TokenArgument(Uri uri, int charOffset, Token token); | 3862 typedef FastaMessage TokenArgument(Uri uri, int charOffset, Token token); |
| 3797 | 3863 |
| 3798 typedef FastaMessage StringArgument(Uri uri, int charOffset, String string); | 3864 typedef FastaMessage StringArgument(Uri uri, int charOffset, String string); |
| OLD | NEW |