| 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/scanner/error_token.dart' show ErrorToken; | 7 import 'package:front_end/src/scanner/token.dart' show Token; |
| 8 | 8 |
| 9 import 'package:front_end/src/scanner/token.dart' show Keyword, Token; | 9 import 'package:front_end/src/fasta/scanner/token.dart' show SymbolToken; |
| 10 | |
| 11 import 'package:front_end/src/fasta/scanner/token.dart' | |
| 12 show BeginGroupToken, CommentToken, DartDocToken, StringToken, SymbolToken; | |
| 13 | 10 |
| 14 import 'package:front_end/src/fasta/scanner/token_constants.dart'; | 11 import 'package:front_end/src/fasta/scanner/token_constants.dart'; |
| 15 | 12 |
| 16 import 'package:front_end/src/scanner/errors.dart' show translateErrorToken; | 13 import 'package:front_end/src/scanner/errors.dart' show translateErrorToken; |
| 17 | 14 |
| 18 import 'package:front_end/src/scanner/token.dart' as analyzer | |
| 19 show | |
| 20 BeginToken, | |
| 21 BeginTokenWithComment, | |
| 22 CommentToken, | |
| 23 Keyword, | |
| 24 KeywordToken, | |
| 25 KeywordTokenWithComment, | |
| 26 StringToken, | |
| 27 StringTokenWithComment, | |
| 28 Token, | |
| 29 TokenWithComment; | |
| 30 | |
| 31 import 'package:front_end/src/scanner/errors.dart' as analyzer | 15 import 'package:front_end/src/scanner/errors.dart' as analyzer |
| 32 show ScannerErrorCode; | 16 show ScannerErrorCode; |
| 33 | 17 |
| 34 import 'package:analyzer/dart/ast/token.dart' show TokenType; | |
| 35 | |
| 36 import 'package:front_end/src/fasta/errors.dart' show internalError; | |
| 37 | |
| 38 /// Class capable of converting a stream of Fasta tokens to a stream of analyzer | 18 /// Class capable of converting a stream of Fasta tokens to a stream of analyzer |
| 39 /// tokens. | 19 /// tokens. |
| 40 /// | 20 /// |
| 41 /// This is a class rather than an ordinary method so that it can be subclassed | 21 /// This is a class rather than an ordinary method so that it can be subclassed |
| 42 /// in tests. | 22 /// in tests. |
| 43 /// | |
| 44 /// TODO(paulberry,ahe): Fasta includes comments directly in the token | |
| 45 /// stream, rather than pointing to them via a "precedingComment" pointer, as | |
| 46 /// analyzer does. This seems like it will complicate parsing and other | |
| 47 /// operations. | |
| 48 class ToAnalyzerTokenStreamConverter { | 23 class ToAnalyzerTokenStreamConverter { |
| 49 /// Synthetic token pointing to the first token in the analyzer token stream. | |
| 50 analyzer.Token _analyzerTokenHead; | |
| 51 | |
| 52 /// The most recently generated analyzer token, or [_analyzerTokenHead] if no | |
| 53 /// tokens have been generated yet. | |
| 54 analyzer.Token _analyzerTokenTail; | |
| 55 | |
| 56 /// Stack of analyzer "begin" tokens which need to be linked up to | |
| 57 /// corresponding "end" tokens once those tokens are translated. | |
| 58 /// | |
| 59 /// The first element of this list is always a sentinel `null` value so that | |
| 60 /// we don't have to check if it is empty. | |
| 61 /// | |
| 62 /// See additional documentation in [_matchGroups]. | |
| 63 List<analyzer.BeginToken> _beginTokenStack; | |
| 64 | |
| 65 /// Stack of fasta "end" tokens corresponding to the tokens in | |
| 66 /// [_endTokenStack]. | |
| 67 /// | |
| 68 /// The first element of this list is always a sentinel `null` value so that | |
| 69 /// we don't have to check if it is empty. | |
| 70 /// | |
| 71 /// See additional documentation in [_matchGroups]. | |
| 72 List<Token> _endTokenStack; | |
| 73 | |
| 74 /// Converts a stream of Fasta tokens (starting with [token] and continuing to | 24 /// Converts a stream of Fasta tokens (starting with [token] and continuing to |
| 75 /// EOF) to a stream of analyzer tokens. | 25 /// EOF) to a stream of analyzer tokens. This modifies the fasta token stream |
| 76 analyzer.Token convertTokens(Token token) { | 26 /// to be an analyzer token stream by removing error tokens and reporting |
| 77 _analyzerTokenHead = new analyzer.Token(TokenType.EOF, -1); | 27 /// those errors to the associated error listener. |
| 78 _analyzerTokenHead.previous = _analyzerTokenHead; | 28 Token convertTokens(Token firstToken) { |
| 79 _analyzerTokenTail = _analyzerTokenHead; | 29 Token previous = new SymbolToken.eof(-1); |
| 80 _beginTokenStack = [null]; | 30 Token token = firstToken; |
| 81 _endTokenStack = <Token>[null]; | 31 token.previous = previous; |
| 82 | 32 previous.next = token; |
| 83 while (true) { | 33 while (!token.isEof) { |
| 84 if (token.type.kind == BAD_INPUT_TOKEN) { | 34 if (token.type.kind == BAD_INPUT_TOKEN) { |
| 85 ErrorToken errorToken = token; | 35 translateErrorToken(token, reportError); |
| 86 translateErrorToken(errorToken, reportError); | 36 previous.next = token.next; |
| 37 token.next.previous = previous; |
| 87 } else { | 38 } else { |
| 88 var translatedToken = translateToken( | 39 previous = token; |
| 89 token, translateCommentTokens(token.precedingComments)); | |
| 90 _matchGroups(token, translatedToken); | |
| 91 translatedToken.setNext(translatedToken); | |
| 92 _analyzerTokenTail.setNext(translatedToken); | |
| 93 translatedToken.previous = _analyzerTokenTail; | |
| 94 _analyzerTokenTail = translatedToken; | |
| 95 } | |
| 96 if (token.isEof) { | |
| 97 return _analyzerTokenHead.next; | |
| 98 } | 40 } |
| 99 token = token.next; | 41 token = token.next; |
| 100 } | 42 } |
| 43 return firstToken; |
| 101 } | 44 } |
| 102 | 45 |
| 103 /// Handles an error found during [convertTokens]. | 46 /// Handles an error found during [convertTokens]. |
| 104 /// | 47 /// |
| 105 /// Intended to be overridden by derived classes; by default, does nothing. | 48 /// Intended to be overridden by derived classes; by default, does nothing. |
| 106 void reportError(analyzer.ScannerErrorCode errorCode, int offset, | 49 void reportError(analyzer.ScannerErrorCode errorCode, int offset, |
| 107 List<Object> arguments) {} | 50 List<Object> arguments) {} |
| 108 | |
| 109 /// Translates a sequence of fasta comment tokens to the corresponding | |
| 110 /// analyzer tokens. | |
| 111 analyzer.CommentToken translateCommentTokens(analyzer.Token token) { | |
| 112 analyzer.CommentToken head; | |
| 113 if (token != null) { | |
| 114 head = toAnalyzerCommentToken(token); | |
| 115 analyzer.CommentToken tail = head; | |
| 116 token = token.next; | |
| 117 while (token != null) { | |
| 118 tail = tail.setNext(toAnalyzerCommentToken(token)); | |
| 119 token = token.next; | |
| 120 } | |
| 121 } | |
| 122 return head; | |
| 123 } | |
| 124 | |
| 125 /// Translates a single fasta non-comment token to the corresponding analyzer | |
| 126 /// token. | |
| 127 /// | |
| 128 /// [precedingComments] is not `null`, the translated token is pointed to it. | |
| 129 analyzer.Token translateToken( | |
| 130 Token token, analyzer.CommentToken precedingComments) => | |
| 131 toAnalyzerToken(token, precedingComments); | |
| 132 | |
| 133 /// Creates appropriate begin/end token links based on the fact that [token] | |
| 134 /// was translated to [translatedToken]. | |
| 135 /// | |
| 136 /// Background: both fasta and analyzer have links from a "BeginToken" to its | |
| 137 /// matching "EndToken" in a group (like parentheses and braces). However, | |
| 138 /// fasta may contain synthetic tokens from error recovery that are not mapped | |
| 139 /// to the analyzer token stream. We use [_beginTokenStack] and | |
| 140 /// [_endTokenStack] to create the appropriate links for non-synthetic tokens | |
| 141 /// in the way analyzer expects. | |
| 142 void _matchGroups(Token token, analyzer.Token translatedToken) { | |
| 143 if (identical(_endTokenStack.last, token)) { | |
| 144 _beginTokenStack.last.endToken = translatedToken; | |
| 145 _beginTokenStack.removeLast(); | |
| 146 _endTokenStack.removeLast(); | |
| 147 } | |
| 148 // Synthetic end tokens have a length of zero. | |
| 149 if (translatedToken is analyzer.BeginToken && | |
| 150 token is BeginGroupToken && | |
| 151 token.endGroup != null && | |
| 152 token.endGroup.charOffset != token.charOffset) { | |
| 153 _beginTokenStack.add(translatedToken); | |
| 154 _endTokenStack.add(token.endGroup); | |
| 155 } | |
| 156 } | |
| 157 } | 51 } |
| 158 | |
| 159 /// Converts a single Fasta comment token to an analyzer comment token. | |
| 160 analyzer.CommentToken toAnalyzerCommentToken(Token token) { | |
| 161 TokenType type; | |
| 162 if (token.type == TokenType.GENERIC_METHOD_TYPE_ASSIGN) { | |
| 163 type = TokenType.GENERIC_METHOD_TYPE_ASSIGN; | |
| 164 } else if (token.type == TokenType.GENERIC_METHOD_TYPE_LIST) { | |
| 165 type = TokenType.GENERIC_METHOD_TYPE_LIST; | |
| 166 } else { | |
| 167 // TODO(paulberry,ahe): It would be nice if the scanner gave us an | |
| 168 // easier way to distinguish between the two types of comment. | |
| 169 type = token.lexeme.startsWith('/*') | |
| 170 ? TokenType.MULTI_LINE_COMMENT | |
| 171 : TokenType.SINGLE_LINE_COMMENT; | |
| 172 } | |
| 173 return new analyzer.CommentToken(type, token.lexeme, token.charOffset); | |
| 174 } | |
| 175 | |
| 176 /// Converts a stream of Analyzer tokens (starting with [token] and continuing | |
| 177 /// to EOF) to a stream of Fasta tokens. | |
| 178 /// | |
| 179 /// TODO(paulberry): Analyzer tokens do not record error conditions, so a round | |
| 180 /// trip through this function and [toAnalyzerTokenStream] will lose error | |
| 181 /// information. | |
| 182 Token fromAnalyzerTokenStream(analyzer.Token analyzerToken) { | |
| 183 Token tokenHead = new SymbolToken.eof(-1); | |
| 184 Token tokenTail = tokenHead; | |
| 185 | |
| 186 // Both fasta and analyzer have links from a "BeginToken" to its matching | |
| 187 // "EndToken" in a group (like parentheses and braces). However, only fasta | |
| 188 // makes these links for angle brackets. We use these stacks to map the | |
| 189 // links from the analyzer token stream into equivalent links in the fasta | |
| 190 // token stream, and to create the links that fasta expects for angle | |
| 191 // brackets. | |
| 192 | |
| 193 // Note: beginTokenStack and endTokenStack are seeded with a sentinel value | |
| 194 // so that we don't have to check if they're empty. | |
| 195 var beginTokenStack = <BeginGroupToken>[null]; | |
| 196 var endTokenStack = <analyzer.Token>[null]; | |
| 197 var angleBracketStack = <BeginGroupToken>[]; | |
| 198 void matchGroups(analyzer.Token analyzerToken, Token translatedToken) { | |
| 199 if (identical(endTokenStack.last, analyzerToken)) { | |
| 200 angleBracketStack.clear(); | |
| 201 beginTokenStack.last.endGroup = translatedToken; | |
| 202 beginTokenStack.removeLast(); | |
| 203 endTokenStack.removeLast(); | |
| 204 } else if (translatedToken.type.kind == LT_TOKEN) { | |
| 205 BeginGroupToken beginGroupToken = translatedToken; | |
| 206 angleBracketStack.add(beginGroupToken); | |
| 207 } else if (translatedToken.type.kind == GT_TOKEN && | |
| 208 angleBracketStack.isNotEmpty) { | |
| 209 angleBracketStack.removeLast().endGroup = translatedToken; | |
| 210 } else if (translatedToken.type.kind == GT_GT_TOKEN && | |
| 211 angleBracketStack.isNotEmpty) { | |
| 212 angleBracketStack.removeLast(); | |
| 213 if (angleBracketStack.isNotEmpty) { | |
| 214 angleBracketStack.removeLast().endGroup = translatedToken; | |
| 215 } | |
| 216 } | |
| 217 // TODO(paulberry): generate synthetic closer tokens and "UnmatchedToken" | |
| 218 // tokens as appropriate. | |
| 219 if (translatedToken is BeginGroupToken && | |
| 220 analyzerToken is analyzer.BeginToken && | |
| 221 analyzerToken.endToken != null) { | |
| 222 angleBracketStack.clear(); | |
| 223 beginTokenStack.add(translatedToken); | |
| 224 endTokenStack.add(analyzerToken.endToken); | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 analyzer.Token translateAndAppend(analyzer.Token analyzerToken) { | |
| 229 var token = fromAnalyzerToken(analyzerToken); | |
| 230 // Sanity check | |
| 231 if (analyzerToken.precedingComments != null) { | |
| 232 if (token.precedingComments == null) { | |
| 233 return internalError( | |
| 234 'expected translated token $token to have preceedingComments'); | |
| 235 } | |
| 236 } else { | |
| 237 if (token.precedingComments != null) { | |
| 238 return internalError('token $token has unexpected preceedingComments'); | |
| 239 } | |
| 240 } | |
| 241 tokenTail.next = token; | |
| 242 tokenTail.next.previous = tokenTail; // ignore: deprecated_member_use | |
| 243 tokenTail = token; | |
| 244 matchGroups(analyzerToken, token); | |
| 245 return analyzerToken.next; | |
| 246 } | |
| 247 | |
| 248 while (true) { | |
| 249 // TODO(paulberry): join up begingroup/endgroup. | |
| 250 if (analyzerToken.type == TokenType.EOF) { | |
| 251 SymbolToken eof = new SymbolToken.eof(analyzerToken.offset); | |
| 252 tokenTail.next = eof; | |
| 253 eof.previous = tokenTail; // ignore: deprecated_member_use | |
| 254 eof.precedingComments = | |
| 255 _translateComments(analyzerToken.precedingComments); | |
| 256 eof.next = eof; | |
| 257 return tokenHead.next; | |
| 258 } | |
| 259 analyzerToken = translateAndAppend(analyzerToken); | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 /// Converts a single analyzer token into a Fasta token. | |
| 264 Token fromAnalyzerToken(analyzer.Token token) { | |
| 265 Token comments = _translateComments(token.precedingComments); | |
| 266 Token beginGroup(TokenType type) => | |
| 267 new BeginGroupToken(type, token.offset, comments); | |
| 268 Token string(TokenType type) => | |
| 269 new StringToken.fromString(type, token.lexeme, token.offset, | |
| 270 precedingComments: comments); | |
| 271 Token symbol(TokenType type) => new SymbolToken(type, token.offset, comments); | |
| 272 if (token.type.isKeyword) { | |
| 273 var keyword = Keyword.keywords[token.lexeme]; | |
| 274 if (keyword != null) { | |
| 275 return new analyzer.KeywordTokenWithComment( | |
| 276 keyword, token.offset, comments); | |
| 277 } else { | |
| 278 return internalError("Unrecognized keyword: '${token.lexeme}'."); | |
| 279 } | |
| 280 } | |
| 281 switch (token.type) { | |
| 282 case TokenType.DOUBLE: | |
| 283 return string(TokenType.DOUBLE); | |
| 284 case TokenType.HEXADECIMAL: | |
| 285 return string(TokenType.HEXADECIMAL); | |
| 286 case TokenType.IDENTIFIER: | |
| 287 // Certain identifiers have special grammatical meanings even though they | |
| 288 // are neither keywords nor built-in identifiers (e.g. "async"). Analyzer | |
| 289 // represents these as identifiers. Fasta represents them as keywords | |
| 290 // with the "isPseudo" property. | |
| 291 var keyword = Keyword.keywords[token.lexeme]; | |
| 292 if (keyword != null) { | |
| 293 assert(keyword.isPseudo); | |
| 294 return new analyzer.KeywordTokenWithComment( | |
| 295 keyword, token.offset, comments); | |
| 296 } else { | |
| 297 return string(TokenType.IDENTIFIER); | |
| 298 } | |
| 299 break; | |
| 300 case TokenType.INT: | |
| 301 return string(TokenType.INT); | |
| 302 case TokenType.MULTI_LINE_COMMENT: | |
| 303 if (token.lexeme.startsWith('/**')) { | |
| 304 return new DartDocToken.fromSubstring(TokenType.MULTI_LINE_COMMENT, | |
| 305 token.lexeme, 0, token.lexeme.length, 0); | |
| 306 } | |
| 307 return new CommentToken.fromSubstring(TokenType.MULTI_LINE_COMMENT, | |
| 308 token.lexeme, 0, token.lexeme.length, 0); | |
| 309 case TokenType.SCRIPT_TAG: | |
| 310 return string(TokenType.SCRIPT_TAG); | |
| 311 case TokenType.SINGLE_LINE_COMMENT: | |
| 312 if (token.lexeme.startsWith('///')) { | |
| 313 return new DartDocToken.fromSubstring(TokenType.SINGLE_LINE_COMMENT, | |
| 314 token.lexeme, 0, token.lexeme.length, 0); | |
| 315 } | |
| 316 return new CommentToken.fromSubstring(TokenType.SINGLE_LINE_COMMENT, | |
| 317 token.lexeme, 0, token.lexeme.length, 0); | |
| 318 case TokenType.STRING: | |
| 319 return string(TokenType.STRING); | |
| 320 case TokenType.AMPERSAND: | |
| 321 return symbol(TokenType.AMPERSAND); | |
| 322 case TokenType.AMPERSAND_AMPERSAND: | |
| 323 return symbol(TokenType.AMPERSAND_AMPERSAND); | |
| 324 case TokenType.AMPERSAND_AMPERSAND_EQ: | |
| 325 return symbol(TokenType.AMPERSAND_AMPERSAND_EQ); | |
| 326 case TokenType.AMPERSAND_EQ: | |
| 327 return symbol(TokenType.AMPERSAND_EQ); | |
| 328 case TokenType.AT: | |
| 329 return symbol(TokenType.AT); | |
| 330 case TokenType.BANG: | |
| 331 return symbol(TokenType.BANG); | |
| 332 case TokenType.BANG_EQ: | |
| 333 return symbol(TokenType.BANG_EQ); | |
| 334 case TokenType.BAR: | |
| 335 return symbol(TokenType.BAR); | |
| 336 case TokenType.BAR_BAR: | |
| 337 return symbol(TokenType.BAR_BAR); | |
| 338 case TokenType.BAR_BAR_EQ: | |
| 339 return symbol(TokenType.BAR_BAR_EQ); | |
| 340 case TokenType.BAR_EQ: | |
| 341 return symbol(TokenType.BAR_EQ); | |
| 342 case TokenType.COLON: | |
| 343 return symbol(TokenType.COLON); | |
| 344 case TokenType.COMMA: | |
| 345 return symbol(TokenType.COMMA); | |
| 346 case TokenType.CARET: | |
| 347 return symbol(TokenType.CARET); | |
| 348 case TokenType.CARET_EQ: | |
| 349 return symbol(TokenType.CARET_EQ); | |
| 350 case TokenType.CLOSE_CURLY_BRACKET: | |
| 351 return symbol(TokenType.CLOSE_CURLY_BRACKET); | |
| 352 case TokenType.CLOSE_PAREN: | |
| 353 return symbol(TokenType.CLOSE_PAREN); | |
| 354 case TokenType.CLOSE_SQUARE_BRACKET: | |
| 355 return symbol(TokenType.CLOSE_SQUARE_BRACKET); | |
| 356 case TokenType.EQ: | |
| 357 return symbol(TokenType.EQ); | |
| 358 case TokenType.EQ_EQ: | |
| 359 return symbol(TokenType.EQ_EQ); | |
| 360 case TokenType.FUNCTION: | |
| 361 return symbol(TokenType.FUNCTION); | |
| 362 case TokenType.GT: | |
| 363 return symbol(TokenType.GT); | |
| 364 case TokenType.GT_EQ: | |
| 365 return symbol(TokenType.GT_EQ); | |
| 366 case TokenType.GT_GT: | |
| 367 return symbol(TokenType.GT_GT); | |
| 368 case TokenType.GT_GT_EQ: | |
| 369 return symbol(TokenType.GT_GT_EQ); | |
| 370 case TokenType.HASH: | |
| 371 return symbol(TokenType.HASH); | |
| 372 case TokenType.INDEX: | |
| 373 return symbol(TokenType.INDEX); | |
| 374 case TokenType.INDEX_EQ: | |
| 375 return symbol(TokenType.INDEX_EQ); | |
| 376 case TokenType.LT: | |
| 377 return beginGroup(TokenType.LT); | |
| 378 case TokenType.LT_EQ: | |
| 379 return symbol(TokenType.LT_EQ); | |
| 380 case TokenType.LT_LT: | |
| 381 return symbol(TokenType.LT_LT); | |
| 382 case TokenType.LT_LT_EQ: | |
| 383 return symbol(TokenType.LT_LT_EQ); | |
| 384 case TokenType.MINUS: | |
| 385 return symbol(TokenType.MINUS); | |
| 386 case TokenType.MINUS_EQ: | |
| 387 return symbol(TokenType.MINUS_EQ); | |
| 388 case TokenType.MINUS_MINUS: | |
| 389 return symbol(TokenType.MINUS_MINUS); | |
| 390 case TokenType.OPEN_CURLY_BRACKET: | |
| 391 return beginGroup(TokenType.OPEN_CURLY_BRACKET); | |
| 392 case TokenType.OPEN_PAREN: | |
| 393 return beginGroup(TokenType.OPEN_PAREN); | |
| 394 case TokenType.OPEN_SQUARE_BRACKET: | |
| 395 return beginGroup(TokenType.OPEN_SQUARE_BRACKET); | |
| 396 case TokenType.PERCENT: | |
| 397 return symbol(TokenType.PERCENT); | |
| 398 case TokenType.PERCENT_EQ: | |
| 399 return symbol(TokenType.PERCENT_EQ); | |
| 400 case TokenType.PERIOD: | |
| 401 return symbol(TokenType.PERIOD); | |
| 402 case TokenType.PERIOD_PERIOD: | |
| 403 return symbol(TokenType.PERIOD_PERIOD); | |
| 404 case TokenType.PLUS: | |
| 405 return symbol(TokenType.PLUS); | |
| 406 case TokenType.PLUS_EQ: | |
| 407 return symbol(TokenType.PLUS_EQ); | |
| 408 case TokenType.PLUS_PLUS: | |
| 409 return symbol(TokenType.PLUS_PLUS); | |
| 410 case TokenType.QUESTION: | |
| 411 return symbol(TokenType.QUESTION); | |
| 412 case TokenType.QUESTION_PERIOD: | |
| 413 return symbol(TokenType.QUESTION_PERIOD); | |
| 414 case TokenType.QUESTION_QUESTION: | |
| 415 return symbol(TokenType.QUESTION_QUESTION); | |
| 416 case TokenType.QUESTION_QUESTION_EQ: | |
| 417 return symbol(TokenType.QUESTION_QUESTION_EQ); | |
| 418 case TokenType.SEMICOLON: | |
| 419 return symbol(TokenType.SEMICOLON); | |
| 420 case TokenType.SLASH: | |
| 421 return symbol(TokenType.SLASH); | |
| 422 case TokenType.SLASH_EQ: | |
| 423 return symbol(TokenType.SLASH_EQ); | |
| 424 case TokenType.STAR: | |
| 425 return symbol(TokenType.STAR); | |
| 426 case TokenType.STAR_EQ: | |
| 427 return symbol(TokenType.STAR_EQ); | |
| 428 case TokenType.STRING_INTERPOLATION_EXPRESSION: | |
| 429 return beginGroup(TokenType.STRING_INTERPOLATION_EXPRESSION); | |
| 430 case TokenType.STRING_INTERPOLATION_IDENTIFIER: | |
| 431 return symbol(TokenType.STRING_INTERPOLATION_IDENTIFIER); | |
| 432 case TokenType.TILDE: | |
| 433 return symbol(TokenType.TILDE); | |
| 434 case TokenType.TILDE_SLASH: | |
| 435 return symbol(TokenType.TILDE_SLASH); | |
| 436 case TokenType.TILDE_SLASH_EQ: | |
| 437 return symbol(TokenType.TILDE_SLASH_EQ); | |
| 438 case TokenType.BACKPING: | |
| 439 return symbol(TokenType.BACKPING); | |
| 440 case TokenType.BACKSLASH: | |
| 441 return symbol(TokenType.BACKSLASH); | |
| 442 case TokenType.PERIOD_PERIOD_PERIOD: | |
| 443 return symbol(TokenType.PERIOD_PERIOD_PERIOD); | |
| 444 case TokenType.GENERIC_METHOD_TYPE_ASSIGN: | |
| 445 return new CommentToken.fromSubstring( | |
| 446 TokenType.GENERIC_METHOD_TYPE_ASSIGN, | |
| 447 token.lexeme, | |
| 448 0, | |
| 449 token.lexeme.length, | |
| 450 0); | |
| 451 case TokenType.GENERIC_METHOD_TYPE_LIST: | |
| 452 return new CommentToken.fromSubstring(TokenType.GENERIC_METHOD_TYPE_LIST, | |
| 453 token.lexeme, 0, token.lexeme.length, 0); | |
| 454 default: | |
| 455 return internalError('Unhandled token type ${token.type}'); | |
| 456 } | |
| 457 } | |
| 458 | |
| 459 analyzer.Token toAnalyzerToken(Token token, | |
| 460 [analyzer.CommentToken commentToken]) { | |
| 461 if (token == null) return null; | |
| 462 analyzer.Token makeStringToken(TokenType tokenType) { | |
| 463 if (commentToken == null) { | |
| 464 return new analyzer.StringToken( | |
| 465 tokenType, token.lexeme, token.charOffset); | |
| 466 } else { | |
| 467 return new analyzer.StringTokenWithComment( | |
| 468 tokenType, token.lexeme, token.charOffset, commentToken); | |
| 469 } | |
| 470 } | |
| 471 | |
| 472 analyzer.Token makeBeginToken(TokenType tokenType) { | |
| 473 if (commentToken == null) { | |
| 474 return new analyzer.BeginToken(tokenType, token.charOffset); | |
| 475 } else { | |
| 476 return new analyzer.BeginTokenWithComment( | |
| 477 tokenType, token.charOffset, commentToken); | |
| 478 } | |
| 479 } | |
| 480 | |
| 481 switch (token.kind) { | |
| 482 case DOUBLE_TOKEN: | |
| 483 return makeStringToken(TokenType.DOUBLE); | |
| 484 | |
| 485 case HEXADECIMAL_TOKEN: | |
| 486 return makeStringToken(TokenType.HEXADECIMAL); | |
| 487 | |
| 488 case IDENTIFIER_TOKEN: | |
| 489 return makeStringToken(TokenType.IDENTIFIER); | |
| 490 | |
| 491 case INT_TOKEN: | |
| 492 return makeStringToken(TokenType.INT); | |
| 493 | |
| 494 case KEYWORD_TOKEN: | |
| 495 var syntax = token.type.lexeme; | |
| 496 // TODO(paulberry): if the map lookup proves to be too slow, consider | |
| 497 // using a switch statement, or perhaps a string of | |
| 498 // "if (identical(syntax, "foo"))" checks. (Note that identical checks | |
| 499 // should be safe because the Fasta scanner uses string literals for | |
| 500 // the values of keyword.syntax.) | |
| 501 var keyword = | |
| 502 _keywordMap[syntax] ?? internalError('Unknown keyword: $syntax'); | |
| 503 if (commentToken == null) { | |
| 504 return new analyzer.KeywordToken(keyword, token.charOffset); | |
| 505 } else { | |
| 506 return new analyzer.KeywordTokenWithComment( | |
| 507 keyword, token.charOffset, commentToken); | |
| 508 } | |
| 509 break; | |
| 510 | |
| 511 case SCRIPT_TOKEN: | |
| 512 return makeStringToken(TokenType.SCRIPT_TAG); | |
| 513 | |
| 514 case STRING_TOKEN: | |
| 515 return makeStringToken(TokenType.STRING); | |
| 516 | |
| 517 case OPEN_CURLY_BRACKET_TOKEN: | |
| 518 case OPEN_SQUARE_BRACKET_TOKEN: | |
| 519 case OPEN_PAREN_TOKEN: | |
| 520 case STRING_INTERPOLATION_TOKEN: | |
| 521 return makeBeginToken(token.type); | |
| 522 | |
| 523 default: | |
| 524 if (commentToken == null) { | |
| 525 return new analyzer.Token(token.type, token.charOffset); | |
| 526 } else { | |
| 527 return new analyzer.TokenWithComment( | |
| 528 token.type, token.charOffset, commentToken); | |
| 529 } | |
| 530 break; | |
| 531 } | |
| 532 } | |
| 533 | |
| 534 analyzer.Token _translateComments(analyzer.Token token) { | |
| 535 if (token == null) { | |
| 536 return null; | |
| 537 } | |
| 538 Token head = fromAnalyzerToken(token); | |
| 539 Token tail = head; | |
| 540 token = token.next; | |
| 541 while (token != null) { | |
| 542 tail.next = fromAnalyzerToken(token); | |
| 543 tail.next.previous = tail; // ignore: deprecated_member_use | |
| 544 tail = tail.next; | |
| 545 token = token.next; | |
| 546 } | |
| 547 return head; | |
| 548 } | |
| 549 | |
| 550 final _keywordMap = { | |
| 551 "assert": analyzer.Keyword.ASSERT, | |
| 552 "break": analyzer.Keyword.BREAK, | |
| 553 "case": analyzer.Keyword.CASE, | |
| 554 "catch": analyzer.Keyword.CATCH, | |
| 555 "class": analyzer.Keyword.CLASS, | |
| 556 "const": analyzer.Keyword.CONST, | |
| 557 "continue": analyzer.Keyword.CONTINUE, | |
| 558 "default": analyzer.Keyword.DEFAULT, | |
| 559 "do": analyzer.Keyword.DO, | |
| 560 "else": analyzer.Keyword.ELSE, | |
| 561 "enum": analyzer.Keyword.ENUM, | |
| 562 "extends": analyzer.Keyword.EXTENDS, | |
| 563 "false": analyzer.Keyword.FALSE, | |
| 564 "final": analyzer.Keyword.FINAL, | |
| 565 "finally": analyzer.Keyword.FINALLY, | |
| 566 "for": analyzer.Keyword.FOR, | |
| 567 "if": analyzer.Keyword.IF, | |
| 568 "in": analyzer.Keyword.IN, | |
| 569 "new": analyzer.Keyword.NEW, | |
| 570 "null": analyzer.Keyword.NULL, | |
| 571 "rethrow": analyzer.Keyword.RETHROW, | |
| 572 "return": analyzer.Keyword.RETURN, | |
| 573 "super": analyzer.Keyword.SUPER, | |
| 574 "switch": analyzer.Keyword.SWITCH, | |
| 575 "this": analyzer.Keyword.THIS, | |
| 576 "throw": analyzer.Keyword.THROW, | |
| 577 "true": analyzer.Keyword.TRUE, | |
| 578 "try": analyzer.Keyword.TRY, | |
| 579 "var": analyzer.Keyword.VAR, | |
| 580 "void": analyzer.Keyword.VOID, | |
| 581 "while": analyzer.Keyword.WHILE, | |
| 582 "with": analyzer.Keyword.WITH, | |
| 583 // | |
| 584 "is": analyzer.Keyword.IS, | |
| 585 // | |
| 586 "abstract": analyzer.Keyword.ABSTRACT, | |
| 587 "as": analyzer.Keyword.AS, | |
| 588 "covariant": analyzer.Keyword.COVARIANT, | |
| 589 "deferred": analyzer.Keyword.DEFERRED, | |
| 590 "dynamic": analyzer.Keyword.DYNAMIC, | |
| 591 "export": analyzer.Keyword.EXPORT, | |
| 592 "external": analyzer.Keyword.EXTERNAL, | |
| 593 "factory": analyzer.Keyword.FACTORY, | |
| 594 "get": analyzer.Keyword.GET, | |
| 595 "implements": analyzer.Keyword.IMPLEMENTS, | |
| 596 "import": analyzer.Keyword.IMPORT, | |
| 597 "library": analyzer.Keyword.LIBRARY, | |
| 598 "operator": analyzer.Keyword.OPERATOR, | |
| 599 "part": analyzer.Keyword.PART, | |
| 600 "set": analyzer.Keyword.SET, | |
| 601 "static": analyzer.Keyword.STATIC, | |
| 602 "typedef": analyzer.Keyword.TYPEDEF, | |
| 603 // | |
| 604 "async": analyzer.Keyword.ASYNC, | |
| 605 "await": analyzer.Keyword.AWAIT, | |
| 606 "Function": analyzer.Keyword.FUNCTION, | |
| 607 "hide": analyzer.Keyword.HIDE, | |
| 608 "native": analyzer.Keyword.NATIVE, | |
| 609 "of": analyzer.Keyword.OF, | |
| 610 "on": analyzer.Keyword.ON, | |
| 611 "patch": analyzer.Keyword.PATCH, | |
| 612 "show": analyzer.Keyword.SHOW, | |
| 613 "source": analyzer.Keyword.SOURCE, | |
| 614 "sync": analyzer.Keyword.SYNC, | |
| 615 "yield": analyzer.Keyword.YIELD, | |
| 616 }; | |
| OLD | NEW |