Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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.analyzer.token_utils; | 5 library fasta.analyzer.token_utils; |
| 6 | 6 |
| 7 import 'package:front_end/src/fasta/parser/error_kind.dart' show | |
| 8 ErrorKind; | |
| 9 | |
| 10 import 'package:front_end/src/fasta/scanner/error_token.dart' show | |
| 11 ErrorToken; | |
| 12 | |
| 7 import 'package:front_end/src/fasta/scanner/token.dart' show | 13 import 'package:front_end/src/fasta/scanner/token.dart' show |
| 8 KeywordToken, | 14 KeywordToken, |
| 9 Token; | 15 Token; |
| 10 | 16 |
| 11 import 'package:front_end/src/fasta/scanner/token_constants.dart'; | 17 import 'package:front_end/src/fasta/scanner/token_constants.dart'; |
| 12 | 18 |
| 13 import 'package:front_end/src/scanner/token.dart' as analyzer show | 19 import 'package:front_end/src/scanner/token.dart' as analyzer show |
| 14 CommentToken, | 20 CommentToken, |
| 15 Keyword, | 21 Keyword, |
| 16 KeywordToken, | 22 KeywordToken, |
| 17 KeywordTokenWithComment, | 23 KeywordTokenWithComment, |
| 18 StringToken, | 24 StringToken, |
| 19 StringTokenWithComment, | 25 StringTokenWithComment, |
| 20 Token, | 26 Token, |
| 21 TokenWithComment; | 27 TokenWithComment; |
| 22 | 28 |
| 29 import 'package:front_end/src/scanner/errors.dart' as analyzer show | |
| 30 ScannerErrorCode; | |
| 31 | |
| 23 import 'package:analyzer/dart/ast/token.dart' show | 32 import 'package:analyzer/dart/ast/token.dart' show |
| 24 TokenType; | 33 TokenType; |
| 25 | 34 |
| 26 import '../errors.dart' show | 35 import '../errors.dart' show |
| 27 internalError; | 36 internalError; |
| 28 | 37 |
| 38 /// Converts a stream of Fasta tokens (starting with [token] and continuing to | |
| 39 /// EOF) to a stream of analyzer tokens. | |
| 40 /// | |
| 41 /// If any error tokens are found in the stream, they are reported using the | |
| 42 /// [reportError] callback. | |
| 43 analyzer.Token toAnalyzerTokenStream( | |
| 44 Token token, | |
| 45 void reportError(analyzer.ScannerErrorCode errorCode, int offset, | |
| 46 List<Object> arguments)) { | |
| 47 var analyzerTokenHead = new analyzer.Token(null, 0); | |
| 48 analyzerTokenHead.previous = analyzerTokenHead; | |
| 49 var analyzerTokenTail = analyzerTokenHead; | |
| 50 // TODO(paulberry,ahe): Fasta includes comments directly in the token | |
| 51 // stream, rather than pointing to them via a "precedingComment" pointer, as | |
| 52 // analyzer does. This seems like it will complicate parsing and other | |
| 53 // operations. | |
|
ahe
2017/02/10 21:56:25
Absolutely, we should do something like precedingC
| |
| 54 analyzer.CommentToken currentCommentHead; | |
| 55 analyzer.CommentToken currentCommentTail; | |
| 56 while (true) { | |
| 57 if (token.info.kind == BAD_INPUT_TOKEN) { | |
| 58 ErrorToken errorToken = token; | |
| 59 _translateErrorToken(errorToken, reportError); | |
| 60 } else if (token.info.kind == COMMENT_TOKEN) { | |
| 61 // TODO(paulberry,ahe): It would be nice if the scanner gave us an | |
| 62 // easier way to distinguish between the two types of comment. | |
| 63 var type = token.value.startsWith('/*') | |
| 64 ? TokenType.MULTI_LINE_COMMENT | |
| 65 : TokenType.SINGLE_LINE_COMMENT; | |
| 66 var translatedToken = | |
| 67 new analyzer.CommentToken(type, token.value, token.charOffset); | |
| 68 if (currentCommentHead == null) { | |
| 69 currentCommentHead = currentCommentTail = translatedToken; | |
| 70 } else { | |
| 71 currentCommentTail.setNext(translatedToken); | |
| 72 currentCommentTail = translatedToken; | |
| 73 } | |
| 74 } else { | |
| 75 var translatedToken = toAnalyzerToken(token, currentCommentHead); | |
| 76 translatedToken.setNext(translatedToken); | |
| 77 currentCommentHead = currentCommentTail = null; | |
| 78 analyzerTokenTail.setNext(translatedToken); | |
| 79 translatedToken.previous = analyzerTokenTail; | |
| 80 analyzerTokenTail = translatedToken; | |
| 81 } | |
| 82 if (token.isEof) { | |
| 83 return analyzerTokenHead.next; | |
| 84 } | |
| 85 token = token.next; | |
| 86 } | |
| 87 } | |
| 88 | |
| 89 /// Determines whether the given [charOffset], which came from the non-EOF token | |
| 90 /// [token], represents the end of the input. | |
| 91 bool _isAtEnd(Token token, int charOffset) { | |
| 92 while (true) { | |
| 93 // Skip to the next token. | |
| 94 token = token.next; | |
| 95 // If we've found an EOF token, its charOffset indicates where the end of | |
| 96 // the input is. | |
| 97 if (token.isEof) return token.charOffset == charOffset; | |
| 98 // If we've found a non-error token, then we know there is additional input | |
| 99 // text after [charOffset]. | |
| 100 if (token.info.kind != BAD_INPUT_TOKEN) return false; | |
| 101 // Otherwise keep looking. | |
| 102 } | |
| 103 } | |
| 104 | |
| 105 /// Translates the given error [token] into an analyzer error and reports it | |
| 106 /// using [reportError]. | |
| 107 void _translateErrorToken( | |
| 108 ErrorToken token, | |
| 109 void reportError(analyzer.ScannerErrorCode errorCode, int offset, | |
| 110 List<Object> arguments)) { | |
| 111 int charOffset = token.charOffset; | |
| 112 // TODO(paulberry,ahe): why is endOffset sometimes null? | |
| 113 int endOffset = token.endOffset ?? charOffset; | |
|
ahe
2017/02/10 21:56:25
That sounds bad. Would you mind filing a bug if yo
Paul Berry
2017/02/10 22:55:01
Filed https://github.com/dart-lang/sdk/issues/2874
| |
| 114 void _makeError(analyzer.ScannerErrorCode errorCode, List<Object> arguments) { | |
| 115 if (_isAtEnd(token, charOffset)) { | |
| 116 // Analyzer never generates an error message past the end of the input, | |
| 117 // since such an error would not be visible in an editor. | |
| 118 // TODO(paulberry,ahe): would it make sense to replicate this behavior | |
| 119 // in fasta, or move it elsewhere in analyzer? | |
| 120 charOffset--; | |
| 121 } | |
| 122 reportError(errorCode, charOffset, arguments); | |
| 123 } | |
| 124 | |
| 125 var errorCode = token.errorCode; | |
| 126 switch (errorCode) { | |
| 127 case ErrorKind.UnterminatedString: | |
| 128 // TODO(paulberry,ahe): Fasta reports the error location as the entire | |
| 129 // string; analyzer expects the end of the string. | |
| 130 charOffset = endOffset; | |
| 131 return _makeError( | |
| 132 analyzer.ScannerErrorCode.UNTERMINATED_STRING_LITERAL, null); | |
| 133 case ErrorKind.UnmatchedToken: | |
| 134 return null; | |
| 135 case ErrorKind.UnterminatedComment: | |
| 136 // TODO(paulberry,ahe): Fasta reports the error location as the entire | |
| 137 // comment; analyzer expects the end of the comment. | |
| 138 charOffset = endOffset; | |
| 139 return _makeError( | |
| 140 analyzer.ScannerErrorCode.UNTERMINATED_MULTI_LINE_COMMENT, null); | |
| 141 case ErrorKind.MissingExponent: | |
| 142 // TODO(paulberry,ahe): Fasta reports the error location as the entire | |
| 143 // number; analyzer expects the end of the number. | |
| 144 charOffset = endOffset; | |
| 145 return _makeError(analyzer.ScannerErrorCode.MISSING_DIGIT, null); | |
| 146 case ErrorKind.ExpectedHexDigit: | |
| 147 // TODO(paulberry,ahe): Fasta reports the error location as the entire | |
| 148 // number; analyzer expects the end of the number. | |
| 149 charOffset = endOffset; | |
| 150 return _makeError(analyzer.ScannerErrorCode.MISSING_HEX_DIGIT, null); | |
| 151 case ErrorKind.NonAsciiIdentifier: | |
| 152 case ErrorKind.NonAsciiWhitespace: | |
| 153 return _makeError( | |
| 154 analyzer.ScannerErrorCode.ILLEGAL_CHARACTER, [token.character]); | |
| 155 case ErrorKind.UnexpectedDollarInString: | |
| 156 return null; | |
| 157 default: | |
| 158 throw new UnimplementedError('$errorCode'); | |
| 159 } | |
| 160 } | |
| 161 | |
| 29 analyzer.Token toAnalyzerToken(Token token, | 162 analyzer.Token toAnalyzerToken(Token token, |
| 30 [analyzer.CommentToken commentToken]) { | 163 [analyzer.CommentToken commentToken]) { |
| 31 if (token == null) return null; | 164 if (token == null) return null; |
| 32 analyzer.Token makeStringToken(TokenType tokenType) { | 165 analyzer.Token makeStringToken(TokenType tokenType) { |
| 33 if (commentToken == null) { | 166 if (commentToken == null) { |
| 34 return new analyzer.StringToken(tokenType, token.value, token.charOffset); | 167 return new analyzer.StringToken(tokenType, token.value, token.charOffset); |
| 35 } else { | 168 } else { |
| 36 return new analyzer.StringTokenWithComment( | 169 return new analyzer.StringTokenWithComment( |
| 37 tokenType, token.value, token.charOffset, commentToken); | 170 tokenType, token.value, token.charOffset, commentToken); |
| 38 } | 171 } |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 224 case BACKSLASH_TOKEN: return TokenType.BACKSLASH; | 357 case BACKSLASH_TOKEN: return TokenType.BACKSLASH; |
| 225 case PERIOD_PERIOD_PERIOD_TOKEN: return TokenType.PERIOD_PERIOD_PERIOD; | 358 case PERIOD_PERIOD_PERIOD_TOKEN: return TokenType.PERIOD_PERIOD_PERIOD; |
| 226 // case GENERIC_METHOD_TYPE_LIST_TOKEN: | 359 // case GENERIC_METHOD_TYPE_LIST_TOKEN: |
| 227 // return TokenType.GENERIC_METHOD_TYPE_LIST; | 360 // return TokenType.GENERIC_METHOD_TYPE_LIST; |
| 228 // case GENERIC_METHOD_TYPE_ASSIGN_TOKEN: | 361 // case GENERIC_METHOD_TYPE_ASSIGN_TOKEN: |
| 229 // return TokenType.GENERIC_METHOD_TYPE_ASSIGN; | 362 // return TokenType.GENERIC_METHOD_TYPE_ASSIGN; |
| 230 default: | 363 default: |
| 231 return internalError("Unhandled token ${token.info}"); | 364 return internalError("Unhandled token ${token.info}"); |
| 232 } | 365 } |
| 233 } | 366 } |
| OLD | NEW |