| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| 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. |
| 4 |
| 5 import 'package:front_end/src/fasta/scanner/error_token.dart' as fasta; |
| 6 import 'package:front_end/src/fasta/scanner/keyword.dart' as fasta; |
| 7 import 'package:front_end/src/fasta/scanner/string_scanner.dart' as fasta; |
| 8 import 'package:front_end/src/fasta/scanner/token.dart' as fasta; |
| 9 import 'package:front_end/src/fasta/scanner/token_constants.dart' as fasta; |
| 10 import 'package:front_end/src/scanner/errors.dart'; |
| 11 import 'package:front_end/src/scanner/token.dart'; |
| 12 import 'package:test/test.dart'; |
| 13 import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| 14 |
| 15 import 'scanner_test.dart'; |
| 16 |
| 17 main() { |
| 18 defineReflectiveSuite(() { |
| 19 defineReflectiveTests(ScannerTest_Fasta); |
| 20 }); |
| 21 } |
| 22 |
| 23 @reflectiveTest |
| 24 class ScannerTest_Fasta extends ScannerTestBase { |
| 25 final _keywordMap = { |
| 26 "assert": Keyword.ASSERT, |
| 27 "break": Keyword.BREAK, |
| 28 "case": Keyword.CASE, |
| 29 "catch": Keyword.CATCH, |
| 30 "class": Keyword.CLASS, |
| 31 "const": Keyword.CONST, |
| 32 "continue": Keyword.CONTINUE, |
| 33 "default": Keyword.DEFAULT, |
| 34 "do": Keyword.DO, |
| 35 "else": Keyword.ELSE, |
| 36 "enum": Keyword.ENUM, |
| 37 "extends": Keyword.EXTENDS, |
| 38 "false": Keyword.FALSE, |
| 39 "final": Keyword.FINAL, |
| 40 "finally": Keyword.FINALLY, |
| 41 "for": Keyword.FOR, |
| 42 "if": Keyword.IF, |
| 43 "in": Keyword.IN, |
| 44 "new": Keyword.NEW, |
| 45 "null": Keyword.NULL, |
| 46 "rethrow": Keyword.RETHROW, |
| 47 "return": Keyword.RETURN, |
| 48 "super": Keyword.SUPER, |
| 49 "switch": Keyword.SWITCH, |
| 50 "this": Keyword.THIS, |
| 51 "throw": Keyword.THROW, |
| 52 "true": Keyword.TRUE, |
| 53 "try": Keyword.TRY, |
| 54 "var": Keyword.VAR, |
| 55 "void": Keyword.VOID, |
| 56 "while": Keyword.WHILE, |
| 57 "with": Keyword.WITH, |
| 58 "is": Keyword.IS, |
| 59 "abstract": Keyword.ABSTRACT, |
| 60 "as": Keyword.AS, |
| 61 "covariant": Keyword.COVARIANT, |
| 62 "dynamic": Keyword.DYNAMIC, |
| 63 "export": Keyword.EXPORT, |
| 64 "external": Keyword.EXTERNAL, |
| 65 "factory": Keyword.FACTORY, |
| 66 "get": Keyword.GET, |
| 67 "implements": Keyword.IMPLEMENTS, |
| 68 "import": Keyword.IMPORT, |
| 69 "library": Keyword.LIBRARY, |
| 70 "operator": Keyword.OPERATOR, |
| 71 "part": Keyword.PART, |
| 72 "set": Keyword.SET, |
| 73 "static": Keyword.STATIC, |
| 74 "typedef": Keyword.TYPEDEF, |
| 75 "deferred": Keyword.DEFERRED, |
| 76 }; |
| 77 |
| 78 @override |
| 79 Token scanWithListener(String source, ErrorListener listener, |
| 80 {bool genericMethodComments: false, |
| 81 bool lazyAssignmentOperators: false}) { |
| 82 if (genericMethodComments) { |
| 83 // Fasta doesn't support generic method comments. |
| 84 // TODO(paulberry): once the analyzer toolchain no longer needs generic |
| 85 // method comments, remove tests that exercise them. |
| 86 fail('No generic method comment support in Fasta'); |
| 87 } |
| 88 // Note: Fasta always supports lazy assignment operators (`&&=` and `||=`), |
| 89 // so we can ignore the `lazyAssignmentOperators` flag. |
| 90 // TODO(paulberry): once lazyAssignmentOperators are fully supported by |
| 91 // Dart, remove this flag. |
| 92 var scanner = new fasta.StringScanner(source, includeComments: true); |
| 93 var token = scanner.tokenize(); |
| 94 var analyzerTokenHead = new Token(null, 0); |
| 95 analyzerTokenHead.previous = analyzerTokenHead; |
| 96 var analyzerTokenTail = analyzerTokenHead; |
| 97 // TODO(paulberry,ahe): Fasta includes comments directly in the token |
| 98 // stream, rather than pointing to them via a "precedingComment" pointer, as |
| 99 // analyzer does. This seems like it will complicate parsing and other |
| 100 // operations. |
| 101 CommentToken currentCommentHead; |
| 102 CommentToken currentCommentTail; |
| 103 while (true) { |
| 104 if (scanner.hasErrors && token is fasta.ErrorToken) { |
| 105 var error = _translateErrorToken(token, source.length); |
| 106 if (error != null) { |
| 107 listener.errors.add(error); |
| 108 } |
| 109 } else if (token is fasta.StringToken && |
| 110 token.info.kind == fasta.COMMENT_TOKEN) { |
| 111 // TODO(paulberry,ahe): It would be nice if the scanner gave us an |
| 112 // easier way to distinguish between the two types of comment. |
| 113 var type = token.value.startsWith('/*') |
| 114 ? TokenType.MULTI_LINE_COMMENT |
| 115 : TokenType.SINGLE_LINE_COMMENT; |
| 116 var translatedToken = |
| 117 new CommentToken(type, token.value, token.charOffset); |
| 118 if (currentCommentHead == null) { |
| 119 currentCommentHead = currentCommentTail = translatedToken; |
| 120 } else { |
| 121 currentCommentTail.setNext(translatedToken); |
| 122 currentCommentTail = translatedToken; |
| 123 } |
| 124 } else { |
| 125 var translatedToken = _translateToken(token, currentCommentHead); |
| 126 translatedToken.setNext(translatedToken); |
| 127 currentCommentHead = currentCommentTail = null; |
| 128 analyzerTokenTail.setNext(translatedToken); |
| 129 translatedToken.previous = analyzerTokenTail; |
| 130 analyzerTokenTail = translatedToken; |
| 131 } |
| 132 if (token.isEof) { |
| 133 return analyzerTokenHead.next; |
| 134 } |
| 135 token = token.next; |
| 136 } |
| 137 } |
| 138 |
| 139 @override |
| 140 @failingTest |
| 141 void test_ampersand_ampersand_eq() { |
| 142 // TODO(paulberry,ahe): Fasta doesn't support `&&=` yet |
| 143 super.test_ampersand_ampersand_eq(); |
| 144 } |
| 145 |
| 146 @override |
| 147 @failingTest |
| 148 void test_bar_bar_eq() { |
| 149 // TODO(paulberry,ahe): Fasta doesn't support `||=` yet |
| 150 super.test_bar_bar_eq(); |
| 151 } |
| 152 |
| 153 @override |
| 154 @failingTest |
| 155 void test_comment_generic_method_type_assign() { |
| 156 // TODO(paulberry,ahe): Fasta doesn't support generic method comment syntax. |
| 157 super.test_comment_generic_method_type_assign(); |
| 158 } |
| 159 |
| 160 @override |
| 161 @failingTest |
| 162 void test_comment_generic_method_type_list() { |
| 163 // TODO(paulberry,ahe): Fasta doesn't support generic method comment syntax. |
| 164 super.test_comment_generic_method_type_list(); |
| 165 } |
| 166 |
| 167 @override |
| 168 @failingTest |
| 169 void test_index() { |
| 170 // TODO(paulberry,ahe): "[]" should be parsed as a single token. |
| 171 super.test_index(); |
| 172 } |
| 173 |
| 174 @override |
| 175 @failingTest |
| 176 void test_index_eq() { |
| 177 // TODO(paulberry,ahe): "[]=" should be parsed as a single token. |
| 178 super.test_index_eq(); |
| 179 } |
| 180 |
| 181 @override |
| 182 @failingTest |
| 183 void test_scriptTag_withArgs() { |
| 184 // TODO(paulberry,ahe): script tags are needed by analyzer. |
| 185 super.test_scriptTag_withArgs(); |
| 186 } |
| 187 |
| 188 @override |
| 189 @failingTest |
| 190 void test_scriptTag_withoutSpace() { |
| 191 // TODO(paulberry,ahe): script tags are needed by analyzer. |
| 192 super.test_scriptTag_withoutSpace(); |
| 193 } |
| 194 |
| 195 @override |
| 196 @failingTest |
| 197 void test_scriptTag_withSpace() { |
| 198 // TODO(paulberry,ahe): script tags are needed by analyzer. |
| 199 super.test_scriptTag_withSpace(); |
| 200 } |
| 201 |
| 202 @override |
| 203 @failingTest |
| 204 void test_string_multi_unterminated() { |
| 205 // TODO(paulberry,ahe): bad error recovery. |
| 206 super.test_string_multi_unterminated(); |
| 207 } |
| 208 |
| 209 @override |
| 210 @failingTest |
| 211 void test_string_multi_unterminated_interpolation_block() { |
| 212 // TODO(paulberry,ahe): bad error recovery. |
| 213 super.test_string_multi_unterminated_interpolation_block(); |
| 214 } |
| 215 |
| 216 @override |
| 217 @failingTest |
| 218 void test_string_multi_unterminated_interpolation_identifier() { |
| 219 // TODO(paulberry,ahe): bad error recovery. |
| 220 super.test_string_multi_unterminated_interpolation_identifier(); |
| 221 } |
| 222 |
| 223 @override |
| 224 @failingTest |
| 225 void test_string_raw_multi_unterminated() { |
| 226 // TODO(paulberry,ahe): bad error recovery. |
| 227 super.test_string_raw_multi_unterminated(); |
| 228 } |
| 229 |
| 230 @override |
| 231 @failingTest |
| 232 void test_string_raw_simple_unterminated_eof() { |
| 233 // TODO(paulberry,ahe): bad error recovery. |
| 234 super.test_string_raw_simple_unterminated_eof(); |
| 235 } |
| 236 |
| 237 @override |
| 238 @failingTest |
| 239 void test_string_raw_simple_unterminated_eol() { |
| 240 // TODO(paulberry,ahe): bad error recovery. |
| 241 super.test_string_raw_simple_unterminated_eol(); |
| 242 } |
| 243 |
| 244 @override |
| 245 @failingTest |
| 246 void test_string_simple_unterminated_eof() { |
| 247 // TODO(paulberry,ahe): bad error recovery. |
| 248 super.test_string_simple_unterminated_eof(); |
| 249 } |
| 250 |
| 251 @override |
| 252 @failingTest |
| 253 void test_string_simple_unterminated_eol() { |
| 254 // TODO(paulberry,ahe): bad error recovery. |
| 255 super.test_string_simple_unterminated_eol(); |
| 256 } |
| 257 |
| 258 @override |
| 259 @failingTest |
| 260 void test_string_simple_unterminated_interpolation_block() { |
| 261 // TODO(paulberry,ahe): bad error recovery. |
| 262 super.test_string_simple_unterminated_interpolation_block(); |
| 263 } |
| 264 |
| 265 @override |
| 266 @failingTest |
| 267 void test_string_simple_unterminated_interpolation_identifier() { |
| 268 // TODO(paulberry,ahe): bad error recovery. |
| 269 super.test_string_simple_unterminated_interpolation_identifier(); |
| 270 } |
| 271 |
| 272 TestError _translateErrorToken(fasta.ErrorToken token, int inputLength) { |
| 273 int charOffset = token.charOffset; |
| 274 // TODO(paulberry,ahe): why is endOffset sometimes null? |
| 275 int endOffset = token.endOffset ?? charOffset; |
| 276 TestError _makeError(ScannerErrorCode errorCode, List<Object> arguments) { |
| 277 int errorLength = endOffset - charOffset; |
| 278 if (charOffset == inputLength) { |
| 279 // Analyzer never generates an error message past the end of the input, |
| 280 // since such an error would not be visible in an editor. |
| 281 // TODO(paulberry,ahe): would it make sense to replicate this behavior |
| 282 // in fasta, or move it elsewhere in analyzer? |
| 283 charOffset--; |
| 284 } |
| 285 if (errorLength == 0) { |
| 286 // Analyzer never generates an error message of length zero, |
| 287 // since such an error would not be visible in an editor. |
| 288 // TODO(paulberry,ahe): would it make sense to replicate this behavior |
| 289 // in fasta, or move it elsewhere in analyzer? |
| 290 errorLength = 1; |
| 291 } |
| 292 return new TestError(charOffset, errorLength, errorCode, arguments); |
| 293 } |
| 294 |
| 295 var errorCode = token.errorCode; |
| 296 switch (errorCode) { |
| 297 case fasta.ErrorKind.UnterminatedString: |
| 298 // TODO(paulberry,ahe): Fasta reports the error location as the entire |
| 299 // string; analyzer expects the end of the string. |
| 300 charOffset = endOffset; |
| 301 return _makeError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL, null); |
| 302 case fasta.ErrorKind.UnmatchedToken: |
| 303 return null; |
| 304 case fasta.ErrorKind.UnterminatedComment: |
| 305 // TODO(paulberry,ahe): Fasta reports the error location as the entire |
| 306 // comment; analyzer expects the end of the comment. |
| 307 charOffset = endOffset; |
| 308 return _makeError( |
| 309 ScannerErrorCode.UNTERMINATED_MULTI_LINE_COMMENT, null); |
| 310 case fasta.ErrorKind.MissingExponent: |
| 311 // TODO(paulberry,ahe): Fasta reports the error location as the entire |
| 312 // number; analyzer expects the end of the number. |
| 313 charOffset = endOffset; |
| 314 return _makeError(ScannerErrorCode.MISSING_DIGIT, null); |
| 315 case fasta.ErrorKind.ExpectedHexDigit: |
| 316 // TODO(paulberry,ahe): Fasta reports the error location as the entire |
| 317 // number; analyzer expects the end of the number. |
| 318 charOffset = endOffset; |
| 319 return _makeError(ScannerErrorCode.MISSING_HEX_DIGIT, null); |
| 320 case fasta.ErrorKind.NonAsciiIdentifier: |
| 321 case fasta.ErrorKind.NonAsciiWhitespace: |
| 322 return _makeError( |
| 323 ScannerErrorCode.ILLEGAL_CHARACTER, [token.character]); |
| 324 case fasta.ErrorKind.UnexpectedDollarInString: |
| 325 return null; |
| 326 default: |
| 327 throw new UnimplementedError('$errorCode'); |
| 328 } |
| 329 } |
| 330 |
| 331 Keyword _translateKeyword(String syntax) => |
| 332 _keywordMap[syntax] ?? (throw new UnimplementedError('$syntax')); |
| 333 |
| 334 Token _translateToken(fasta.Token token, CommentToken comment) { |
| 335 var type = _translateTokenInfoKind(token.info.kind); |
| 336 int offset = token.charOffset; |
| 337 Token makeStringToken(String value) { |
| 338 if (comment == null) { |
| 339 return new StringToken(type, value, offset); |
| 340 } else { |
| 341 return new StringTokenWithComment(type, value, offset, comment); |
| 342 } |
| 343 } |
| 344 |
| 345 Token makeKeywordToken(Keyword keyword) { |
| 346 if (comment == null) { |
| 347 return new KeywordToken(keyword, offset); |
| 348 } else { |
| 349 return new KeywordTokenWithComment(keyword, offset, comment); |
| 350 } |
| 351 } |
| 352 |
| 353 Token makeBeginToken() { |
| 354 if (comment == null) { |
| 355 return new BeginToken(type, offset); |
| 356 } else { |
| 357 return new BeginTokenWithComment(type, offset, comment); |
| 358 } |
| 359 } |
| 360 |
| 361 if (token is fasta.StringToken) { |
| 362 return makeStringToken(token.value); |
| 363 } else if (token is fasta.KeywordToken) { |
| 364 return makeKeywordToken(_translateKeyword(token.keyword.syntax)); |
| 365 } else if (token is fasta.SymbolToken) { |
| 366 if (token is fasta.BeginGroupToken) { |
| 367 if (type == TokenType.LT) { |
| 368 return makeStringToken(token.value); |
| 369 } else { |
| 370 return makeBeginToken(); |
| 371 } |
| 372 } else { |
| 373 return makeStringToken(token.value); |
| 374 } |
| 375 } |
| 376 throw new UnimplementedError('${token.runtimeType}'); |
| 377 } |
| 378 |
| 379 TokenType _translateTokenInfoKind(int kind) { |
| 380 switch (kind) { |
| 381 case fasta.EOF_TOKEN: |
| 382 return TokenType.EOF; |
| 383 case fasta.KEYWORD_TOKEN: |
| 384 return TokenType.KEYWORD; |
| 385 case fasta.IDENTIFIER_TOKEN: |
| 386 return TokenType.IDENTIFIER; |
| 387 case fasta.BAD_INPUT_TOKEN: |
| 388 return TokenType.STRING; |
| 389 case fasta.DOUBLE_TOKEN: |
| 390 return TokenType.DOUBLE; |
| 391 case fasta.INT_TOKEN: |
| 392 return TokenType.INT; |
| 393 case fasta.HEXADECIMAL_TOKEN: |
| 394 return TokenType.HEXADECIMAL; |
| 395 case fasta.STRING_TOKEN: |
| 396 return TokenType.STRING; |
| 397 case fasta.AMPERSAND_TOKEN: |
| 398 return TokenType.AMPERSAND; |
| 399 case fasta.BACKPING_TOKEN: |
| 400 return TokenType.BACKPING; |
| 401 case fasta.BACKSLASH_TOKEN: |
| 402 return TokenType.BACKSLASH; |
| 403 case fasta.BANG_TOKEN: |
| 404 return TokenType.BANG; |
| 405 case fasta.BAR_TOKEN: |
| 406 return TokenType.BAR; |
| 407 case fasta.COLON_TOKEN: |
| 408 return TokenType.COLON; |
| 409 case fasta.COMMA_TOKEN: |
| 410 return TokenType.COMMA; |
| 411 case fasta.EQ_TOKEN: |
| 412 return TokenType.EQ; |
| 413 case fasta.GT_TOKEN: |
| 414 return TokenType.GT; |
| 415 case fasta.HASH_TOKEN: |
| 416 return TokenType.HASH; |
| 417 case fasta.OPEN_CURLY_BRACKET_TOKEN: |
| 418 return TokenType.OPEN_CURLY_BRACKET; |
| 419 case fasta.OPEN_SQUARE_BRACKET_TOKEN: |
| 420 return TokenType.OPEN_SQUARE_BRACKET; |
| 421 case fasta.OPEN_PAREN_TOKEN: |
| 422 return TokenType.OPEN_PAREN; |
| 423 case fasta.LT_TOKEN: |
| 424 return TokenType.LT; |
| 425 case fasta.MINUS_TOKEN: |
| 426 return TokenType.MINUS; |
| 427 case fasta.PERIOD_TOKEN: |
| 428 return TokenType.PERIOD; |
| 429 case fasta.PLUS_TOKEN: |
| 430 return TokenType.PLUS; |
| 431 case fasta.QUESTION_TOKEN: |
| 432 return TokenType.QUESTION; |
| 433 case fasta.AT_TOKEN: |
| 434 return TokenType.AT; |
| 435 case fasta.CLOSE_CURLY_BRACKET_TOKEN: |
| 436 return TokenType.CLOSE_CURLY_BRACKET; |
| 437 case fasta.CLOSE_SQUARE_BRACKET_TOKEN: |
| 438 return TokenType.CLOSE_SQUARE_BRACKET; |
| 439 case fasta.CLOSE_PAREN_TOKEN: |
| 440 return TokenType.CLOSE_PAREN; |
| 441 case fasta.SEMICOLON_TOKEN: |
| 442 return TokenType.SEMICOLON; |
| 443 case fasta.SLASH_TOKEN: |
| 444 return TokenType.SLASH; |
| 445 case fasta.TILDE_TOKEN: |
| 446 return TokenType.TILDE; |
| 447 case fasta.STAR_TOKEN: |
| 448 return TokenType.STAR; |
| 449 case fasta.PERCENT_TOKEN: |
| 450 return TokenType.PERCENT; |
| 451 case fasta.CARET_TOKEN: |
| 452 return TokenType.CARET; |
| 453 case fasta.STRING_INTERPOLATION_TOKEN: |
| 454 return TokenType.STRING_INTERPOLATION_EXPRESSION; |
| 455 case fasta.LT_EQ_TOKEN: |
| 456 return TokenType.LT_EQ; |
| 457 case fasta.FUNCTION_TOKEN: |
| 458 return TokenType.FUNCTION; |
| 459 case fasta.SLASH_EQ_TOKEN: |
| 460 return TokenType.SLASH_EQ; |
| 461 case fasta.PERIOD_PERIOD_PERIOD_TOKEN: |
| 462 return TokenType.PERIOD_PERIOD_PERIOD; |
| 463 case fasta.PERIOD_PERIOD_TOKEN: |
| 464 return TokenType.PERIOD_PERIOD; |
| 465 case fasta.EQ_EQ_EQ_TOKEN: |
| 466 // TODO(paulberry,ahe): what is this? |
| 467 throw new UnimplementedError(); |
| 468 case fasta.EQ_EQ_TOKEN: |
| 469 return TokenType.EQ_EQ; |
| 470 case fasta.LT_LT_EQ_TOKEN: |
| 471 return TokenType.LT_LT_EQ; |
| 472 case fasta.LT_LT_TOKEN: |
| 473 return TokenType.LT_LT; |
| 474 case fasta.GT_EQ_TOKEN: |
| 475 return TokenType.GT_EQ; |
| 476 case fasta.GT_GT_EQ_TOKEN: |
| 477 return TokenType.GT_GT_EQ; |
| 478 case fasta.INDEX_EQ_TOKEN: |
| 479 return TokenType.INDEX_EQ; |
| 480 case fasta.INDEX_TOKEN: |
| 481 return TokenType.INDEX; |
| 482 case fasta.BANG_EQ_EQ_TOKEN: |
| 483 // TODO(paulberry,ahe): what is this? |
| 484 throw new UnimplementedError(); |
| 485 case fasta.BANG_EQ_TOKEN: |
| 486 return TokenType.BANG_EQ; |
| 487 case fasta.AMPERSAND_AMPERSAND_TOKEN: |
| 488 return TokenType.AMPERSAND_AMPERSAND; |
| 489 case fasta.AMPERSAND_EQ_TOKEN: |
| 490 return TokenType.AMPERSAND_EQ; |
| 491 case fasta.BAR_BAR_TOKEN: |
| 492 return TokenType.BAR_BAR; |
| 493 case fasta.BAR_EQ_TOKEN: |
| 494 return TokenType.BAR_EQ; |
| 495 case fasta.STAR_EQ_TOKEN: |
| 496 return TokenType.STAR_EQ; |
| 497 case fasta.PLUS_PLUS_TOKEN: |
| 498 return TokenType.PLUS_PLUS; |
| 499 case fasta.PLUS_EQ_TOKEN: |
| 500 return TokenType.PLUS_EQ; |
| 501 case fasta.MINUS_MINUS_TOKEN: |
| 502 return TokenType.MINUS_MINUS; |
| 503 case fasta.MINUS_EQ_TOKEN: |
| 504 return TokenType.MINUS_EQ; |
| 505 case fasta.TILDE_SLASH_EQ_TOKEN: |
| 506 return TokenType.TILDE_SLASH_EQ; |
| 507 case fasta.TILDE_SLASH_TOKEN: |
| 508 return TokenType.TILDE_SLASH; |
| 509 case fasta.PERCENT_EQ_TOKEN: |
| 510 return TokenType.PERCENT_EQ; |
| 511 case fasta.GT_GT_TOKEN: |
| 512 return TokenType.GT_GT; |
| 513 case fasta.CARET_EQ_TOKEN: |
| 514 return TokenType.CARET_EQ; |
| 515 case fasta.STRING_INTERPOLATION_IDENTIFIER_TOKEN: |
| 516 return TokenType.STRING_INTERPOLATION_IDENTIFIER; |
| 517 case fasta.QUESTION_PERIOD_TOKEN: |
| 518 return TokenType.QUESTION_PERIOD; |
| 519 case fasta.QUESTION_QUESTION_TOKEN: |
| 520 return TokenType.QUESTION_QUESTION; |
| 521 case fasta.QUESTION_QUESTION_EQ_TOKEN: |
| 522 return TokenType.QUESTION_QUESTION_EQ; |
| 523 default: |
| 524 throw new UnimplementedError('$kind'); |
| 525 } |
| 526 } |
| 527 } |
| OLD | NEW |