| 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 import 'package:front_end/src/base/errors.dart'; | 5 import 'package:front_end/src/base/errors.dart'; |
| 6 import 'package:front_end/src/base/jenkins_smi_hash.dart'; | 6 import 'package:front_end/src/base/jenkins_smi_hash.dart'; |
| 7 import 'package:front_end/src/fasta/scanner/error_token.dart' as fasta; | |
| 8 import 'package:front_end/src/fasta/scanner/keyword.dart' as fasta; | |
| 9 import 'package:front_end/src/fasta/scanner/string_scanner.dart' as fasta; | |
| 10 import 'package:front_end/src/fasta/scanner/token.dart' as fasta; | |
| 11 import 'package:front_end/src/fasta/scanner/token_constants.dart' as fasta; | |
| 12 import 'package:front_end/src/scanner/errors.dart'; | 7 import 'package:front_end/src/scanner/errors.dart'; |
| 13 import 'package:front_end/src/scanner/reader.dart'; | 8 import 'package:front_end/src/scanner/reader.dart'; |
| 14 import 'package:front_end/src/scanner/scanner.dart'; | 9 import 'package:front_end/src/scanner/scanner.dart'; |
| 15 import 'package:front_end/src/scanner/token.dart'; | 10 import 'package:front_end/src/scanner/token.dart'; |
| 16 import 'package:test/test.dart'; | 11 import 'package:test/test.dart'; |
| 17 import 'package:test_reflective_loader/test_reflective_loader.dart'; | 12 import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| 18 | 13 |
| 19 main() { | 14 main() { |
| 20 defineReflectiveSuite(() { | 15 defineReflectiveSuite(() { |
| 21 defineReflectiveTests(CharSequenceReaderTest); | 16 defineReflectiveTests(CharSequenceReaderTest); |
| 22 defineReflectiveTests(KeywordStateTest); | 17 defineReflectiveTests(KeywordStateTest); |
| 23 defineReflectiveTests(ScannerTest); | 18 defineReflectiveTests(ScannerTest); |
| 24 defineReflectiveTests(ScannerTest_Fasta); | |
| 25 defineReflectiveTests(TokenTypeTest); | 19 defineReflectiveTests(TokenTypeTest); |
| 26 }); | 20 }); |
| 27 } | 21 } |
| 28 | 22 |
| 29 @reflectiveTest | 23 @reflectiveTest |
| 30 class CharSequenceReaderTest { | 24 class CharSequenceReaderTest { |
| 31 void test_advance() { | 25 void test_advance() { |
| 32 CharSequenceReader reader = new CharSequenceReader("x"); | 26 CharSequenceReader reader = new CharSequenceReader("x"); |
| 33 expect(reader.advance(), 0x78); | 27 expect(reader.advance(), 0x78); |
| 34 expect(reader.advance(), -1); | 28 expect(reader.advance(), -1); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 67 expect(reader.peek(), -1); | 61 expect(reader.peek(), -1); |
| 68 } | 62 } |
| 69 | 63 |
| 70 void test_setOffset() { | 64 void test_setOffset() { |
| 71 CharSequenceReader reader = new CharSequenceReader("xyz"); | 65 CharSequenceReader reader = new CharSequenceReader("xyz"); |
| 72 reader.offset = 2; | 66 reader.offset = 2; |
| 73 expect(reader.offset, 2); | 67 expect(reader.offset, 2); |
| 74 } | 68 } |
| 75 } | 69 } |
| 76 | 70 |
| 71 class ErrorListener { |
| 72 final errors = <TestError>[]; |
| 73 |
| 74 void assertErrors(List<TestError> expectedErrors) { |
| 75 expect(errors, unorderedEquals(expectedErrors)); |
| 76 } |
| 77 |
| 78 void assertNoErrors() { |
| 79 assertErrors([]); |
| 80 } |
| 81 } |
| 82 |
| 77 @reflectiveTest | 83 @reflectiveTest |
| 78 class KeywordStateTest { | 84 class KeywordStateTest { |
| 79 void test_KeywordState() { | 85 void test_KeywordState() { |
| 80 // | 86 // |
| 81 // Generate the test data to be scanned. | 87 // Generate the test data to be scanned. |
| 82 // | 88 // |
| 83 List<Keyword> keywords = Keyword.values; | 89 List<Keyword> keywords = Keyword.values; |
| 84 int keywordCount = keywords.length; | 90 int keywordCount = keywords.length; |
| 85 List<String> textToTest = new List<String>(keywordCount * 3); | 91 List<String> textToTest = new List<String>(keywordCount * 3); |
| 86 for (int i = 0; i < keywordCount; i++) { | 92 for (int i = 0; i < keywordCount; i++) { |
| (...skipping 27 matching lines...) Expand all Loading... |
| 114 // keyword.substring(0, keyword.length() - 1) | 120 // keyword.substring(0, keyword.length() - 1) |
| 115 expect(state, isNotNull); | 121 expect(state, isNotNull); |
| 116 } | 122 } |
| 117 } | 123 } |
| 118 } | 124 } |
| 119 } | 125 } |
| 120 | 126 |
| 121 @reflectiveTest | 127 @reflectiveTest |
| 122 class ScannerTest extends ScannerTestBase { | 128 class ScannerTest extends ScannerTestBase { |
| 123 @override | 129 @override |
| 124 @failingTest | 130 Token scanWithListener(String source, ErrorListener listener, |
| 125 void test_incomplete_string_interpolation() { | |
| 126 super.test_incomplete_string_interpolation(); | |
| 127 } | |
| 128 | |
| 129 @override | |
| 130 Token _scanWithListener(String source, _ErrorListener listener, | |
| 131 {bool genericMethodComments: false, | 131 {bool genericMethodComments: false, |
| 132 bool lazyAssignmentOperators: false}) { | 132 bool lazyAssignmentOperators: false}) { |
| 133 Scanner scanner = | 133 Scanner scanner = |
| 134 new _TestScanner(new CharSequenceReader(source), listener); | 134 new _TestScanner(new CharSequenceReader(source), listener); |
| 135 scanner.scanGenericMethodComments = genericMethodComments; | 135 scanner.scanGenericMethodComments = genericMethodComments; |
| 136 scanner.scanLazyAssignmentOperators = lazyAssignmentOperators; | 136 scanner.scanLazyAssignmentOperators = lazyAssignmentOperators; |
| 137 return scanner.tokenize(); | 137 return scanner.tokenize(); |
| 138 } | 138 } |
| 139 } | |
| 140 | |
| 141 @reflectiveTest | |
| 142 class ScannerTest_Fasta extends ScannerTestBase { | |
| 143 final _keywordMap = { | |
| 144 "assert": Keyword.ASSERT, | |
| 145 "break": Keyword.BREAK, | |
| 146 "case": Keyword.CASE, | |
| 147 "catch": Keyword.CATCH, | |
| 148 "class": Keyword.CLASS, | |
| 149 "const": Keyword.CONST, | |
| 150 "continue": Keyword.CONTINUE, | |
| 151 "default": Keyword.DEFAULT, | |
| 152 "do": Keyword.DO, | |
| 153 "else": Keyword.ELSE, | |
| 154 "enum": Keyword.ENUM, | |
| 155 "extends": Keyword.EXTENDS, | |
| 156 "false": Keyword.FALSE, | |
| 157 "final": Keyword.FINAL, | |
| 158 "finally": Keyword.FINALLY, | |
| 159 "for": Keyword.FOR, | |
| 160 "if": Keyword.IF, | |
| 161 "in": Keyword.IN, | |
| 162 "new": Keyword.NEW, | |
| 163 "null": Keyword.NULL, | |
| 164 "rethrow": Keyword.RETHROW, | |
| 165 "return": Keyword.RETURN, | |
| 166 "super": Keyword.SUPER, | |
| 167 "switch": Keyword.SWITCH, | |
| 168 "this": Keyword.THIS, | |
| 169 "throw": Keyword.THROW, | |
| 170 "true": Keyword.TRUE, | |
| 171 "try": Keyword.TRY, | |
| 172 "var": Keyword.VAR, | |
| 173 "void": Keyword.VOID, | |
| 174 "while": Keyword.WHILE, | |
| 175 "with": Keyword.WITH, | |
| 176 "is": Keyword.IS, | |
| 177 "abstract": Keyword.ABSTRACT, | |
| 178 "as": Keyword.AS, | |
| 179 "covariant": Keyword.COVARIANT, | |
| 180 "dynamic": Keyword.DYNAMIC, | |
| 181 "export": Keyword.EXPORT, | |
| 182 "external": Keyword.EXTERNAL, | |
| 183 "factory": Keyword.FACTORY, | |
| 184 "get": Keyword.GET, | |
| 185 "implements": Keyword.IMPLEMENTS, | |
| 186 "import": Keyword.IMPORT, | |
| 187 "library": Keyword.LIBRARY, | |
| 188 "operator": Keyword.OPERATOR, | |
| 189 "part": Keyword.PART, | |
| 190 "set": Keyword.SET, | |
| 191 "static": Keyword.STATIC, | |
| 192 "typedef": Keyword.TYPEDEF, | |
| 193 "deferred": Keyword.DEFERRED, | |
| 194 }; | |
| 195 | |
| 196 @override | |
| 197 @failingTest | |
| 198 void test_ampersand_ampersand_eq() { | |
| 199 // TODO(paulberry,ahe): Fasta doesn't support `&&=` yet | |
| 200 super.test_ampersand_ampersand_eq(); | |
| 201 } | |
| 202 | 139 |
| 203 @override | 140 @override |
| 204 @failingTest | 141 @failingTest |
| 205 void test_bar_bar_eq() { | 142 void test_incomplete_string_interpolation() { |
| 206 // TODO(paulberry,ahe): Fasta doesn't support `||=` yet | 143 super.test_incomplete_string_interpolation(); |
| 207 super.test_bar_bar_eq(); | |
| 208 } | |
| 209 | |
| 210 @override | |
| 211 @failingTest | |
| 212 void test_comment_generic_method_type_assign() { | |
| 213 // TODO(paulberry,ahe): Fasta doesn't support generic method comment syntax. | |
| 214 super.test_comment_generic_method_type_assign(); | |
| 215 } | |
| 216 | |
| 217 @override | |
| 218 @failingTest | |
| 219 void test_comment_generic_method_type_list() { | |
| 220 // TODO(paulberry,ahe): Fasta doesn't support generic method comment syntax. | |
| 221 super.test_comment_generic_method_type_list(); | |
| 222 } | |
| 223 | |
| 224 @override | |
| 225 @failingTest | |
| 226 void test_index() { | |
| 227 // TODO(paulberry,ahe): "[]" should be parsed as a single token. | |
| 228 super.test_index(); | |
| 229 } | |
| 230 | |
| 231 @override | |
| 232 @failingTest | |
| 233 void test_index_eq() { | |
| 234 // TODO(paulberry,ahe): "[]=" should be parsed as a single token. | |
| 235 super.test_index_eq(); | |
| 236 } | |
| 237 | |
| 238 @override | |
| 239 @failingTest | |
| 240 void test_scriptTag_withArgs() { | |
| 241 // TODO(paulberry,ahe): script tags are needed by analyzer. | |
| 242 super.test_scriptTag_withArgs(); | |
| 243 } | |
| 244 | |
| 245 @override | |
| 246 @failingTest | |
| 247 void test_scriptTag_withoutSpace() { | |
| 248 // TODO(paulberry,ahe): script tags are needed by analyzer. | |
| 249 super.test_scriptTag_withoutSpace(); | |
| 250 } | |
| 251 | |
| 252 @override | |
| 253 @failingTest | |
| 254 void test_scriptTag_withSpace() { | |
| 255 // TODO(paulberry,ahe): script tags are needed by analyzer. | |
| 256 super.test_scriptTag_withSpace(); | |
| 257 } | |
| 258 | |
| 259 @override | |
| 260 @failingTest | |
| 261 void test_string_multi_unterminated() { | |
| 262 // TODO(paulberry,ahe): bad error recovery. | |
| 263 super.test_string_multi_unterminated(); | |
| 264 } | |
| 265 | |
| 266 @override | |
| 267 @failingTest | |
| 268 void test_string_multi_unterminated_interpolation_block() { | |
| 269 // TODO(paulberry,ahe): bad error recovery. | |
| 270 super.test_string_multi_unterminated_interpolation_block(); | |
| 271 } | |
| 272 | |
| 273 @override | |
| 274 @failingTest | |
| 275 void test_string_multi_unterminated_interpolation_identifier() { | |
| 276 // TODO(paulberry,ahe): bad error recovery. | |
| 277 super.test_string_multi_unterminated_interpolation_identifier(); | |
| 278 } | |
| 279 | |
| 280 @override | |
| 281 @failingTest | |
| 282 void test_string_raw_multi_unterminated() { | |
| 283 // TODO(paulberry,ahe): bad error recovery. | |
| 284 super.test_string_raw_multi_unterminated(); | |
| 285 } | |
| 286 | |
| 287 @override | |
| 288 @failingTest | |
| 289 void test_string_raw_simple_unterminated_eof() { | |
| 290 // TODO(paulberry,ahe): bad error recovery. | |
| 291 super.test_string_raw_simple_unterminated_eof(); | |
| 292 } | |
| 293 | |
| 294 @override | |
| 295 @failingTest | |
| 296 void test_string_raw_simple_unterminated_eol() { | |
| 297 // TODO(paulberry,ahe): bad error recovery. | |
| 298 super.test_string_raw_simple_unterminated_eol(); | |
| 299 } | |
| 300 | |
| 301 @override | |
| 302 @failingTest | |
| 303 void test_string_simple_unterminated_eof() { | |
| 304 // TODO(paulberry,ahe): bad error recovery. | |
| 305 super.test_string_simple_unterminated_eof(); | |
| 306 } | |
| 307 | |
| 308 @override | |
| 309 @failingTest | |
| 310 void test_string_simple_unterminated_eol() { | |
| 311 // TODO(paulberry,ahe): bad error recovery. | |
| 312 super.test_string_simple_unterminated_eol(); | |
| 313 } | |
| 314 | |
| 315 @override | |
| 316 @failingTest | |
| 317 void test_string_simple_unterminated_interpolation_block() { | |
| 318 // TODO(paulberry,ahe): bad error recovery. | |
| 319 super.test_string_simple_unterminated_interpolation_block(); | |
| 320 } | |
| 321 | |
| 322 @override | |
| 323 @failingTest | |
| 324 void test_string_simple_unterminated_interpolation_identifier() { | |
| 325 // TODO(paulberry,ahe): bad error recovery. | |
| 326 super.test_string_simple_unterminated_interpolation_identifier(); | |
| 327 } | |
| 328 | |
| 329 @override | |
| 330 Token _scanWithListener(String source, _ErrorListener listener, | |
| 331 {bool genericMethodComments: false, | |
| 332 bool lazyAssignmentOperators: false}) { | |
| 333 if (genericMethodComments) { | |
| 334 // Fasta doesn't support generic method comments. | |
| 335 // TODO(paulberry): once the analyzer toolchain no longer needs generic | |
| 336 // method comments, remove tests that exercise them. | |
| 337 fail('No generic method comment support in Fasta'); | |
| 338 } | |
| 339 // Note: Fasta always supports lazy assignment operators (`&&=` and `||=`), | |
| 340 // so we can ignore the `lazyAssignmentOperators` flag. | |
| 341 // TODO(paulberry): once lazyAssignmentOperators are fully supported by | |
| 342 // Dart, remove this flag. | |
| 343 var scanner = new fasta.StringScanner(source, includeComments: true); | |
| 344 var token = scanner.tokenize(); | |
| 345 var analyzerTokenHead = new Token(null, 0); | |
| 346 analyzerTokenHead.previous = analyzerTokenHead; | |
| 347 var analyzerTokenTail = analyzerTokenHead; | |
| 348 // TODO(paulberry,ahe): Fasta includes comments directly in the token | |
| 349 // stream, rather than pointing to them via a "precedingComment" pointer, as | |
| 350 // analyzer does. This seems like it will complicate parsing and other | |
| 351 // operations. | |
| 352 CommentToken currentCommentHead; | |
| 353 CommentToken currentCommentTail; | |
| 354 while (true) { | |
| 355 if (scanner.hasErrors && token is fasta.ErrorToken) { | |
| 356 var error = _translateErrorToken(token, source.length); | |
| 357 if (error != null) { | |
| 358 listener.errors.add(error); | |
| 359 } | |
| 360 } else if (token is fasta.StringToken && | |
| 361 token.info.kind == fasta.COMMENT_TOKEN) { | |
| 362 // TODO(paulberry,ahe): It would be nice if the scanner gave us an | |
| 363 // easier way to distinguish between the two types of comment. | |
| 364 var type = token.value.startsWith('/*') | |
| 365 ? TokenType.MULTI_LINE_COMMENT | |
| 366 : TokenType.SINGLE_LINE_COMMENT; | |
| 367 var translatedToken = | |
| 368 new CommentToken(type, token.value, token.charOffset); | |
| 369 if (currentCommentHead == null) { | |
| 370 currentCommentHead = currentCommentTail = translatedToken; | |
| 371 } else { | |
| 372 currentCommentTail.setNext(translatedToken); | |
| 373 currentCommentTail = translatedToken; | |
| 374 } | |
| 375 } else { | |
| 376 var translatedToken = _translateToken(token, currentCommentHead); | |
| 377 translatedToken.setNext(translatedToken); | |
| 378 currentCommentHead = currentCommentTail = null; | |
| 379 analyzerTokenTail.setNext(translatedToken); | |
| 380 translatedToken.previous = analyzerTokenTail; | |
| 381 analyzerTokenTail = translatedToken; | |
| 382 } | |
| 383 if (token.isEof) { | |
| 384 return analyzerTokenHead.next; | |
| 385 } | |
| 386 token = token.next; | |
| 387 } | |
| 388 } | |
| 389 | |
| 390 _TestError _translateErrorToken(fasta.ErrorToken token, int inputLength) { | |
| 391 int charOffset = token.charOffset; | |
| 392 // TODO(paulberry,ahe): why is endOffset sometimes null? | |
| 393 int endOffset = token.endOffset ?? charOffset; | |
| 394 _TestError _makeError(ScannerErrorCode errorCode, List<Object> arguments) { | |
| 395 int errorLength = endOffset - charOffset; | |
| 396 if (charOffset == inputLength) { | |
| 397 // Analyzer never generates an error message past the end of the input, | |
| 398 // since such an error would not be visible in an editor. | |
| 399 // TODO(paulberry,ahe): would it make sense to replicate this behavior | |
| 400 // in fasta, or move it elsewhere in analyzer? | |
| 401 charOffset--; | |
| 402 } | |
| 403 if (errorLength == 0) { | |
| 404 // Analyzer never generates an error message of length zero, | |
| 405 // since such an error would not be visible in an editor. | |
| 406 // TODO(paulberry,ahe): would it make sense to replicate this behavior | |
| 407 // in fasta, or move it elsewhere in analyzer? | |
| 408 errorLength = 1; | |
| 409 } | |
| 410 return new _TestError(charOffset, errorLength, errorCode, arguments); | |
| 411 } | |
| 412 | |
| 413 var errorCode = token.errorCode; | |
| 414 switch (errorCode) { | |
| 415 case fasta.ErrorKind.UnterminatedString: | |
| 416 // TODO(paulberry,ahe): Fasta reports the error location as the entire | |
| 417 // string; analyzer expects the end of the string. | |
| 418 charOffset = endOffset; | |
| 419 return _makeError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL, null); | |
| 420 case fasta.ErrorKind.UnmatchedToken: | |
| 421 return null; | |
| 422 case fasta.ErrorKind.UnterminatedComment: | |
| 423 // TODO(paulberry,ahe): Fasta reports the error location as the entire | |
| 424 // comment; analyzer expects the end of the comment. | |
| 425 charOffset = endOffset; | |
| 426 return _makeError( | |
| 427 ScannerErrorCode.UNTERMINATED_MULTI_LINE_COMMENT, null); | |
| 428 case fasta.ErrorKind.MissingExponent: | |
| 429 // TODO(paulberry,ahe): Fasta reports the error location as the entire | |
| 430 // number; analyzer expects the end of the number. | |
| 431 charOffset = endOffset; | |
| 432 return _makeError(ScannerErrorCode.MISSING_DIGIT, null); | |
| 433 case fasta.ErrorKind.ExpectedHexDigit: | |
| 434 // TODO(paulberry,ahe): Fasta reports the error location as the entire | |
| 435 // number; analyzer expects the end of the number. | |
| 436 charOffset = endOffset; | |
| 437 return _makeError(ScannerErrorCode.MISSING_HEX_DIGIT, null); | |
| 438 case fasta.ErrorKind.NonAsciiIdentifier: | |
| 439 case fasta.ErrorKind.NonAsciiWhitespace: | |
| 440 return _makeError( | |
| 441 ScannerErrorCode.ILLEGAL_CHARACTER, [token.character]); | |
| 442 case fasta.ErrorKind.UnexpectedDollarInString: | |
| 443 return null; | |
| 444 default: | |
| 445 throw new UnimplementedError('$errorCode'); | |
| 446 } | |
| 447 } | |
| 448 | |
| 449 Keyword _translateKeyword(String syntax) => | |
| 450 _keywordMap[syntax] ?? (throw new UnimplementedError('$syntax')); | |
| 451 | |
| 452 Token _translateToken(fasta.Token token, CommentToken comment) { | |
| 453 var type = _translateTokenInfoKind(token.info.kind); | |
| 454 int offset = token.charOffset; | |
| 455 Token makeStringToken(String value) { | |
| 456 if (comment == null) { | |
| 457 return new StringToken(type, value, offset); | |
| 458 } else { | |
| 459 return new StringTokenWithComment(type, value, offset, comment); | |
| 460 } | |
| 461 } | |
| 462 | |
| 463 Token makeKeywordToken(Keyword keyword) { | |
| 464 if (comment == null) { | |
| 465 return new KeywordToken(keyword, offset); | |
| 466 } else { | |
| 467 return new KeywordTokenWithComment(keyword, offset, comment); | |
| 468 } | |
| 469 } | |
| 470 | |
| 471 Token makeBeginToken() { | |
| 472 if (comment == null) { | |
| 473 return new BeginToken(type, offset); | |
| 474 } else { | |
| 475 return new BeginTokenWithComment(type, offset, comment); | |
| 476 } | |
| 477 } | |
| 478 | |
| 479 if (token is fasta.StringToken) { | |
| 480 return makeStringToken(token.value); | |
| 481 } else if (token is fasta.KeywordToken) { | |
| 482 return makeKeywordToken(_translateKeyword(token.keyword.syntax)); | |
| 483 } else if (token is fasta.SymbolToken) { | |
| 484 if (token is fasta.BeginGroupToken) { | |
| 485 if (type == TokenType.LT) { | |
| 486 return makeStringToken(token.value); | |
| 487 } else { | |
| 488 return makeBeginToken(); | |
| 489 } | |
| 490 } else { | |
| 491 return makeStringToken(token.value); | |
| 492 } | |
| 493 } | |
| 494 throw new UnimplementedError('${token.runtimeType}'); | |
| 495 } | |
| 496 | |
| 497 TokenType _translateTokenInfoKind(int kind) { | |
| 498 switch (kind) { | |
| 499 case fasta.EOF_TOKEN: | |
| 500 return TokenType.EOF; | |
| 501 case fasta.KEYWORD_TOKEN: | |
| 502 return TokenType.KEYWORD; | |
| 503 case fasta.IDENTIFIER_TOKEN: | |
| 504 return TokenType.IDENTIFIER; | |
| 505 case fasta.BAD_INPUT_TOKEN: | |
| 506 return TokenType.STRING; | |
| 507 case fasta.DOUBLE_TOKEN: | |
| 508 return TokenType.DOUBLE; | |
| 509 case fasta.INT_TOKEN: | |
| 510 return TokenType.INT; | |
| 511 case fasta.HEXADECIMAL_TOKEN: | |
| 512 return TokenType.HEXADECIMAL; | |
| 513 case fasta.STRING_TOKEN: | |
| 514 return TokenType.STRING; | |
| 515 case fasta.AMPERSAND_TOKEN: | |
| 516 return TokenType.AMPERSAND; | |
| 517 case fasta.BACKPING_TOKEN: | |
| 518 return TokenType.BACKPING; | |
| 519 case fasta.BACKSLASH_TOKEN: | |
| 520 return TokenType.BACKSLASH; | |
| 521 case fasta.BANG_TOKEN: | |
| 522 return TokenType.BANG; | |
| 523 case fasta.BAR_TOKEN: | |
| 524 return TokenType.BAR; | |
| 525 case fasta.COLON_TOKEN: | |
| 526 return TokenType.COLON; | |
| 527 case fasta.COMMA_TOKEN: | |
| 528 return TokenType.COMMA; | |
| 529 case fasta.EQ_TOKEN: | |
| 530 return TokenType.EQ; | |
| 531 case fasta.GT_TOKEN: | |
| 532 return TokenType.GT; | |
| 533 case fasta.HASH_TOKEN: | |
| 534 return TokenType.HASH; | |
| 535 case fasta.OPEN_CURLY_BRACKET_TOKEN: | |
| 536 return TokenType.OPEN_CURLY_BRACKET; | |
| 537 case fasta.OPEN_SQUARE_BRACKET_TOKEN: | |
| 538 return TokenType.OPEN_SQUARE_BRACKET; | |
| 539 case fasta.OPEN_PAREN_TOKEN: | |
| 540 return TokenType.OPEN_PAREN; | |
| 541 case fasta.LT_TOKEN: | |
| 542 return TokenType.LT; | |
| 543 case fasta.MINUS_TOKEN: | |
| 544 return TokenType.MINUS; | |
| 545 case fasta.PERIOD_TOKEN: | |
| 546 return TokenType.PERIOD; | |
| 547 case fasta.PLUS_TOKEN: | |
| 548 return TokenType.PLUS; | |
| 549 case fasta.QUESTION_TOKEN: | |
| 550 return TokenType.QUESTION; | |
| 551 case fasta.AT_TOKEN: | |
| 552 return TokenType.AT; | |
| 553 case fasta.CLOSE_CURLY_BRACKET_TOKEN: | |
| 554 return TokenType.CLOSE_CURLY_BRACKET; | |
| 555 case fasta.CLOSE_SQUARE_BRACKET_TOKEN: | |
| 556 return TokenType.CLOSE_SQUARE_BRACKET; | |
| 557 case fasta.CLOSE_PAREN_TOKEN: | |
| 558 return TokenType.CLOSE_PAREN; | |
| 559 case fasta.SEMICOLON_TOKEN: | |
| 560 return TokenType.SEMICOLON; | |
| 561 case fasta.SLASH_TOKEN: | |
| 562 return TokenType.SLASH; | |
| 563 case fasta.TILDE_TOKEN: | |
| 564 return TokenType.TILDE; | |
| 565 case fasta.STAR_TOKEN: | |
| 566 return TokenType.STAR; | |
| 567 case fasta.PERCENT_TOKEN: | |
| 568 return TokenType.PERCENT; | |
| 569 case fasta.CARET_TOKEN: | |
| 570 return TokenType.CARET; | |
| 571 case fasta.STRING_INTERPOLATION_TOKEN: | |
| 572 return TokenType.STRING_INTERPOLATION_EXPRESSION; | |
| 573 case fasta.LT_EQ_TOKEN: | |
| 574 return TokenType.LT_EQ; | |
| 575 case fasta.FUNCTION_TOKEN: | |
| 576 return TokenType.FUNCTION; | |
| 577 case fasta.SLASH_EQ_TOKEN: | |
| 578 return TokenType.SLASH_EQ; | |
| 579 case fasta.PERIOD_PERIOD_PERIOD_TOKEN: | |
| 580 return TokenType.PERIOD_PERIOD_PERIOD; | |
| 581 case fasta.PERIOD_PERIOD_TOKEN: | |
| 582 return TokenType.PERIOD_PERIOD; | |
| 583 case fasta.EQ_EQ_EQ_TOKEN: | |
| 584 // TODO(paulberry,ahe): what is this? | |
| 585 throw new UnimplementedError(); | |
| 586 case fasta.EQ_EQ_TOKEN: | |
| 587 return TokenType.EQ_EQ; | |
| 588 case fasta.LT_LT_EQ_TOKEN: | |
| 589 return TokenType.LT_LT_EQ; | |
| 590 case fasta.LT_LT_TOKEN: | |
| 591 return TokenType.LT_LT; | |
| 592 case fasta.GT_EQ_TOKEN: | |
| 593 return TokenType.GT_EQ; | |
| 594 case fasta.GT_GT_EQ_TOKEN: | |
| 595 return TokenType.GT_GT_EQ; | |
| 596 case fasta.INDEX_EQ_TOKEN: | |
| 597 return TokenType.INDEX_EQ; | |
| 598 case fasta.INDEX_TOKEN: | |
| 599 return TokenType.INDEX; | |
| 600 case fasta.BANG_EQ_EQ_TOKEN: | |
| 601 // TODO(paulberry,ahe): what is this? | |
| 602 throw new UnimplementedError(); | |
| 603 case fasta.BANG_EQ_TOKEN: | |
| 604 return TokenType.BANG_EQ; | |
| 605 case fasta.AMPERSAND_AMPERSAND_TOKEN: | |
| 606 return TokenType.AMPERSAND_AMPERSAND; | |
| 607 case fasta.AMPERSAND_EQ_TOKEN: | |
| 608 return TokenType.AMPERSAND_EQ; | |
| 609 case fasta.BAR_BAR_TOKEN: | |
| 610 return TokenType.BAR_BAR; | |
| 611 case fasta.BAR_EQ_TOKEN: | |
| 612 return TokenType.BAR_EQ; | |
| 613 case fasta.STAR_EQ_TOKEN: | |
| 614 return TokenType.STAR_EQ; | |
| 615 case fasta.PLUS_PLUS_TOKEN: | |
| 616 return TokenType.PLUS_PLUS; | |
| 617 case fasta.PLUS_EQ_TOKEN: | |
| 618 return TokenType.PLUS_EQ; | |
| 619 case fasta.MINUS_MINUS_TOKEN: | |
| 620 return TokenType.MINUS_MINUS; | |
| 621 case fasta.MINUS_EQ_TOKEN: | |
| 622 return TokenType.MINUS_EQ; | |
| 623 case fasta.TILDE_SLASH_EQ_TOKEN: | |
| 624 return TokenType.TILDE_SLASH_EQ; | |
| 625 case fasta.TILDE_SLASH_TOKEN: | |
| 626 return TokenType.TILDE_SLASH; | |
| 627 case fasta.PERCENT_EQ_TOKEN: | |
| 628 return TokenType.PERCENT_EQ; | |
| 629 case fasta.GT_GT_TOKEN: | |
| 630 return TokenType.GT_GT; | |
| 631 case fasta.CARET_EQ_TOKEN: | |
| 632 return TokenType.CARET_EQ; | |
| 633 case fasta.STRING_INTERPOLATION_IDENTIFIER_TOKEN: | |
| 634 return TokenType.STRING_INTERPOLATION_IDENTIFIER; | |
| 635 case fasta.QUESTION_PERIOD_TOKEN: | |
| 636 return TokenType.QUESTION_PERIOD; | |
| 637 case fasta.QUESTION_QUESTION_TOKEN: | |
| 638 return TokenType.QUESTION_QUESTION; | |
| 639 case fasta.QUESTION_QUESTION_EQ_TOKEN: | |
| 640 return TokenType.QUESTION_QUESTION_EQ; | |
| 641 default: | |
| 642 throw new UnimplementedError('$kind'); | |
| 643 } | |
| 644 } | 144 } |
| 645 } | 145 } |
| 646 | 146 |
| 647 abstract class ScannerTestBase { | 147 abstract class ScannerTestBase { |
| 148 Token scanWithListener(String source, ErrorListener listener, |
| 149 {bool genericMethodComments: false, bool lazyAssignmentOperators: false}); |
| 150 |
| 648 void test_ampersand() { | 151 void test_ampersand() { |
| 649 _assertToken(TokenType.AMPERSAND, "&"); | 152 _assertToken(TokenType.AMPERSAND, "&"); |
| 650 } | 153 } |
| 651 | 154 |
| 652 void test_ampersand_ampersand() { | 155 void test_ampersand_ampersand() { |
| 653 _assertToken(TokenType.AMPERSAND_AMPERSAND, "&&"); | 156 _assertToken(TokenType.AMPERSAND_AMPERSAND, "&&"); |
| 654 } | 157 } |
| 655 | 158 |
| 656 void test_ampersand_ampersand_eq() { | 159 void test_ampersand_ampersand_eq() { |
| 657 _assertToken(TokenType.AMPERSAND_AMPERSAND_EQ, "&&=", | 160 _assertToken(TokenType.AMPERSAND_AMPERSAND_EQ, "&&=", |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 781 expect(token.precedingComments.next.next.next, isNull); | 284 expect(token.precedingComments.next.next.next, isNull); |
| 782 } | 285 } |
| 783 | 286 |
| 784 void test_comment_multi_lineEnds() { | 287 void test_comment_multi_lineEnds() { |
| 785 String code = r''' | 288 String code = r''' |
| 786 /** | 289 /** |
| 787 * aa | 290 * aa |
| 788 * bbb | 291 * bbb |
| 789 * c | 292 * c |
| 790 */'''; | 293 */'''; |
| 791 _ErrorListener listener = new _ErrorListener(); | 294 ErrorListener listener = new ErrorListener(); |
| 792 Scanner scanner = new _TestScanner(new CharSequenceReader(code), listener); | 295 Scanner scanner = new _TestScanner(new CharSequenceReader(code), listener); |
| 793 scanner.tokenize(); | 296 scanner.tokenize(); |
| 794 expect( | 297 expect( |
| 795 scanner.lineStarts, | 298 scanner.lineStarts, |
| 796 equals(<int>[ | 299 equals(<int>[ |
| 797 code.indexOf('/**'), | 300 code.indexOf('/**'), |
| 798 code.indexOf(' * aa'), | 301 code.indexOf(' * aa'), |
| 799 code.indexOf(' * bbb'), | 302 code.indexOf(' * bbb'), |
| 800 code.indexOf(' * c'), | 303 code.indexOf(' * c'), |
| 801 code.indexOf(' */') | 304 code.indexOf(' */') |
| (...skipping 443 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1245 void test_scriptTag_withSpace() { | 748 void test_scriptTag_withSpace() { |
| 1246 _assertToken(TokenType.SCRIPT_TAG, "#! /bin/dart"); | 749 _assertToken(TokenType.SCRIPT_TAG, "#! /bin/dart"); |
| 1247 } | 750 } |
| 1248 | 751 |
| 1249 void test_semicolon() { | 752 void test_semicolon() { |
| 1250 _assertToken(TokenType.SEMICOLON, ";"); | 753 _assertToken(TokenType.SEMICOLON, ";"); |
| 1251 } | 754 } |
| 1252 | 755 |
| 1253 void test_setSourceStart() { | 756 void test_setSourceStart() { |
| 1254 int offsetDelta = 42; | 757 int offsetDelta = 42; |
| 1255 _ErrorListener listener = new _ErrorListener(); | 758 ErrorListener listener = new ErrorListener(); |
| 1256 Scanner scanner = | 759 Scanner scanner = |
| 1257 new _TestScanner(new SubSequenceReader("a", offsetDelta), listener); | 760 new _TestScanner(new SubSequenceReader("a", offsetDelta), listener); |
| 1258 scanner.setSourceStart(3, 9); | 761 scanner.setSourceStart(3, 9); |
| 1259 scanner.tokenize(); | 762 scanner.tokenize(); |
| 1260 List<int> lineStarts = scanner.lineStarts; | 763 List<int> lineStarts = scanner.lineStarts; |
| 1261 expect(lineStarts, isNotNull); | 764 expect(lineStarts, isNotNull); |
| 1262 expect(lineStarts.length, 3); | 765 expect(lineStarts.length, 3); |
| 1263 expect(lineStarts[2], 33); | 766 expect(lineStarts[2], 33); |
| 1264 } | 767 } |
| 1265 | 768 |
| (...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1516 | 1019 |
| 1517 void test_tilde_slash() { | 1020 void test_tilde_slash() { |
| 1518 _assertToken(TokenType.TILDE_SLASH, "~/"); | 1021 _assertToken(TokenType.TILDE_SLASH, "~/"); |
| 1519 } | 1022 } |
| 1520 | 1023 |
| 1521 void test_tilde_slash_eq() { | 1024 void test_tilde_slash_eq() { |
| 1522 _assertToken(TokenType.TILDE_SLASH_EQ, "~/="); | 1025 _assertToken(TokenType.TILDE_SLASH_EQ, "~/="); |
| 1523 } | 1026 } |
| 1524 | 1027 |
| 1525 void test_unclosedPairInInterpolation() { | 1028 void test_unclosedPairInInterpolation() { |
| 1526 _ErrorListener listener = new _ErrorListener(); | 1029 ErrorListener listener = new ErrorListener(); |
| 1527 _scanWithListener("'\${(}'", listener); | 1030 scanWithListener("'\${(}'", listener); |
| 1528 } | 1031 } |
| 1529 | 1032 |
| 1530 void _assertComment(TokenType commentType, String source, | 1033 void _assertComment(TokenType commentType, String source, |
| 1531 {bool genericMethodComments: false}) { | 1034 {bool genericMethodComments: false}) { |
| 1532 // | 1035 // |
| 1533 // Test without a trailing end-of-line marker | 1036 // Test without a trailing end-of-line marker |
| 1534 // | 1037 // |
| 1535 Token token = _scan(source, genericMethodComments: genericMethodComments); | 1038 Token token = _scan(source, genericMethodComments: genericMethodComments); |
| 1536 expect(token, isNotNull); | 1039 expect(token, isNotNull); |
| 1537 expect(token.type, TokenType.EOF); | 1040 expect(token.type, TokenType.EOF); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1559 * Assert that scanning the given [source] produces an error with the given | 1062 * Assert that scanning the given [source] produces an error with the given |
| 1560 * code. | 1063 * code. |
| 1561 * | 1064 * |
| 1562 * [expectedError] the error that should be produced | 1065 * [expectedError] the error that should be produced |
| 1563 * [expectedOffset] the string offset that should be associated with the error | 1066 * [expectedOffset] the string offset that should be associated with the error |
| 1564 * [source] the source to be scanned to produce the error | 1067 * [source] the source to be scanned to produce the error |
| 1565 */ | 1068 */ |
| 1566 void _assertError( | 1069 void _assertError( |
| 1567 ScannerErrorCode expectedError, int expectedOffset, String source, | 1070 ScannerErrorCode expectedError, int expectedOffset, String source, |
| 1568 [List<Object> arguments]) { | 1071 [List<Object> arguments]) { |
| 1569 _ErrorListener listener = new _ErrorListener(); | 1072 ErrorListener listener = new ErrorListener(); |
| 1570 _scanWithListener(source, listener); | 1073 scanWithListener(source, listener); |
| 1571 listener.assertErrors( | 1074 listener.assertErrors( |
| 1572 [new _TestError(expectedOffset, 1, expectedError, arguments)]); | 1075 [new TestError(expectedOffset, 1, expectedError, arguments)]); |
| 1573 } | 1076 } |
| 1574 | 1077 |
| 1575 /** | 1078 /** |
| 1576 * Assert that scanning the given [source] produces an error with the given | 1079 * Assert that scanning the given [source] produces an error with the given |
| 1577 * code, and also produces the given tokens. | 1080 * code, and also produces the given tokens. |
| 1578 * | 1081 * |
| 1579 * [expectedError] the error that should be produced | 1082 * [expectedError] the error that should be produced |
| 1580 * [expectedOffset] the string offset that should be associated with the error | 1083 * [expectedOffset] the string offset that should be associated with the error |
| 1581 * [source] the source to be scanned to produce the error | 1084 * [source] the source to be scanned to produce the error |
| 1582 * [expectedTokens] the tokens that are expected to be in the source | 1085 * [expectedTokens] the tokens that are expected to be in the source |
| 1583 */ | 1086 */ |
| 1584 void _assertErrorAndTokens(ScannerErrorCode expectedError, int expectedOffset, | 1087 void _assertErrorAndTokens(ScannerErrorCode expectedError, int expectedOffset, |
| 1585 String source, List<Token> expectedTokens) { | 1088 String source, List<Token> expectedTokens) { |
| 1586 _ErrorListener listener = new _ErrorListener(); | 1089 ErrorListener listener = new ErrorListener(); |
| 1587 Token token = _scanWithListener(source, listener); | 1090 Token token = scanWithListener(source, listener); |
| 1588 listener | 1091 listener |
| 1589 .assertErrors([new _TestError(expectedOffset, 1, expectedError, null)]); | 1092 .assertErrors([new TestError(expectedOffset, 1, expectedError, null)]); |
| 1590 _checkTokens(token, expectedTokens); | 1093 _checkTokens(token, expectedTokens); |
| 1591 } | 1094 } |
| 1592 | 1095 |
| 1593 /** | 1096 /** |
| 1594 * Assert that when scanned the given [source] contains a single keyword token | 1097 * Assert that when scanned the given [source] contains a single keyword token |
| 1595 * with the same lexeme as the original source. | 1098 * with the same lexeme as the original source. |
| 1596 */ | 1099 */ |
| 1597 void _assertKeywordToken(String source) { | 1100 void _assertKeywordToken(String source) { |
| 1598 Token token = _scan(source); | 1101 Token token = _scan(source); |
| 1599 expect(token, isNotNull); | 1102 expect(token, isNotNull); |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1694 reason: "Wrong lexeme for token $i"); | 1197 reason: "Wrong lexeme for token $i"); |
| 1695 token = token.next; | 1198 token = token.next; |
| 1696 expect(token, isNotNull); | 1199 expect(token, isNotNull); |
| 1697 } | 1200 } |
| 1698 expect(token.type, TokenType.EOF); | 1201 expect(token.type, TokenType.EOF); |
| 1699 } | 1202 } |
| 1700 | 1203 |
| 1701 Token _scan(String source, | 1204 Token _scan(String source, |
| 1702 {bool genericMethodComments: false, | 1205 {bool genericMethodComments: false, |
| 1703 bool lazyAssignmentOperators: false}) { | 1206 bool lazyAssignmentOperators: false}) { |
| 1704 _ErrorListener listener = new _ErrorListener(); | 1207 ErrorListener listener = new ErrorListener(); |
| 1705 Token token = _scanWithListener(source, listener, | 1208 Token token = scanWithListener(source, listener, |
| 1706 genericMethodComments: genericMethodComments, | 1209 genericMethodComments: genericMethodComments, |
| 1707 lazyAssignmentOperators: lazyAssignmentOperators); | 1210 lazyAssignmentOperators: lazyAssignmentOperators); |
| 1708 listener.assertNoErrors(); | 1211 listener.assertNoErrors(); |
| 1709 return token; | 1212 return token; |
| 1710 } | 1213 } |
| 1214 } |
| 1711 | 1215 |
| 1712 Token _scanWithListener(String source, _ErrorListener listener, | 1216 class TestError { |
| 1713 {bool genericMethodComments: false, bool lazyAssignmentOperators: false}); | 1217 final int offset; |
| 1218 final int length; |
| 1219 final ErrorCode errorCode; |
| 1220 final List<Object> arguments; |
| 1221 |
| 1222 TestError(this.offset, this.length, this.errorCode, this.arguments); |
| 1223 |
| 1224 @override |
| 1225 get hashCode { |
| 1226 var h = new JenkinsSmiHash()..add(offset)..add(length)..add(errorCode); |
| 1227 if (arguments != null) { |
| 1228 for (Object argument in arguments) { |
| 1229 h.add(argument); |
| 1230 } |
| 1231 } |
| 1232 return h.hashCode; |
| 1233 } |
| 1234 |
| 1235 @override |
| 1236 operator ==(Object other) { |
| 1237 if (other is TestError && |
| 1238 offset == other.offset && |
| 1239 length == other.length && |
| 1240 errorCode == other.errorCode) { |
| 1241 if (arguments == null) return other.arguments == null; |
| 1242 if (other.arguments == null) return false; |
| 1243 if (arguments.length != other.arguments.length) return false; |
| 1244 for (int i = 0; i < arguments.length; i++) { |
| 1245 if (arguments[i] != other.arguments[i]) return false; |
| 1246 } |
| 1247 return true; |
| 1248 } |
| 1249 return false; |
| 1250 } |
| 1251 |
| 1252 @override |
| 1253 toString() { |
| 1254 var end = offset + length; |
| 1255 var argString = arguments == null ? '' : '(${arguments.join(', ')})'; |
| 1256 return 'Error($offset..$end, $errorCode$argString)'; |
| 1257 } |
| 1714 } | 1258 } |
| 1715 | 1259 |
| 1716 @reflectiveTest | 1260 @reflectiveTest |
| 1717 class TokenTypeTest { | 1261 class TokenTypeTest { |
| 1718 void test_isOperator() { | 1262 void test_isOperator() { |
| 1719 expect(TokenType.AMPERSAND.isOperator, isTrue); | 1263 expect(TokenType.AMPERSAND.isOperator, isTrue); |
| 1720 expect(TokenType.AMPERSAND_AMPERSAND.isOperator, isTrue); | 1264 expect(TokenType.AMPERSAND_AMPERSAND.isOperator, isTrue); |
| 1721 expect(TokenType.AMPERSAND_EQ.isOperator, isTrue); | 1265 expect(TokenType.AMPERSAND_EQ.isOperator, isTrue); |
| 1722 expect(TokenType.BANG.isOperator, isTrue); | 1266 expect(TokenType.BANG.isOperator, isTrue); |
| 1723 expect(TokenType.BANG_EQ.isOperator, isTrue); | 1267 expect(TokenType.BANG_EQ.isOperator, isTrue); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1774 expect(TokenType.MINUS.isUserDefinableOperator, isTrue); | 1318 expect(TokenType.MINUS.isUserDefinableOperator, isTrue); |
| 1775 expect(TokenType.PERCENT.isUserDefinableOperator, isTrue); | 1319 expect(TokenType.PERCENT.isUserDefinableOperator, isTrue); |
| 1776 expect(TokenType.PLUS.isUserDefinableOperator, isTrue); | 1320 expect(TokenType.PLUS.isUserDefinableOperator, isTrue); |
| 1777 expect(TokenType.SLASH.isUserDefinableOperator, isTrue); | 1321 expect(TokenType.SLASH.isUserDefinableOperator, isTrue); |
| 1778 expect(TokenType.STAR.isUserDefinableOperator, isTrue); | 1322 expect(TokenType.STAR.isUserDefinableOperator, isTrue); |
| 1779 expect(TokenType.TILDE.isUserDefinableOperator, isTrue); | 1323 expect(TokenType.TILDE.isUserDefinableOperator, isTrue); |
| 1780 expect(TokenType.TILDE_SLASH.isUserDefinableOperator, isTrue); | 1324 expect(TokenType.TILDE_SLASH.isUserDefinableOperator, isTrue); |
| 1781 } | 1325 } |
| 1782 } | 1326 } |
| 1783 | 1327 |
| 1784 class _ErrorListener { | |
| 1785 final errors = <_TestError>[]; | |
| 1786 | |
| 1787 void assertErrors(List<_TestError> expectedErrors) { | |
| 1788 expect(errors, unorderedEquals(expectedErrors)); | |
| 1789 } | |
| 1790 | |
| 1791 void assertNoErrors() { | |
| 1792 assertErrors([]); | |
| 1793 } | |
| 1794 } | |
| 1795 | |
| 1796 class _TestError { | |
| 1797 final int offset; | |
| 1798 final int length; | |
| 1799 final ErrorCode errorCode; | |
| 1800 final List<Object> arguments; | |
| 1801 | |
| 1802 _TestError(this.offset, this.length, this.errorCode, this.arguments); | |
| 1803 | |
| 1804 @override | |
| 1805 get hashCode { | |
| 1806 var h = new JenkinsSmiHash()..add(offset)..add(length)..add(errorCode); | |
| 1807 if (arguments != null) { | |
| 1808 for (Object argument in arguments) { | |
| 1809 h.add(argument); | |
| 1810 } | |
| 1811 } | |
| 1812 return h.hashCode; | |
| 1813 } | |
| 1814 | |
| 1815 @override | |
| 1816 operator ==(Object other) { | |
| 1817 if (other is _TestError && | |
| 1818 offset == other.offset && | |
| 1819 length == other.length && | |
| 1820 errorCode == other.errorCode) { | |
| 1821 if (arguments == null) return other.arguments == null; | |
| 1822 if (other.arguments == null) return false; | |
| 1823 if (arguments.length != other.arguments.length) return false; | |
| 1824 for (int i = 0; i < arguments.length; i++) { | |
| 1825 if (arguments[i] != other.arguments[i]) return false; | |
| 1826 } | |
| 1827 return true; | |
| 1828 } | |
| 1829 return false; | |
| 1830 } | |
| 1831 | |
| 1832 @override | |
| 1833 toString() { | |
| 1834 var end = offset + length; | |
| 1835 var argString = arguments == null ? '' : '(${arguments.join(', ')})'; | |
| 1836 return 'Error($offset..$end, $errorCode$argString)'; | |
| 1837 } | |
| 1838 } | |
| 1839 | |
| 1840 class _TestScanner extends Scanner { | 1328 class _TestScanner extends Scanner { |
| 1841 final _ErrorListener listener; | 1329 final ErrorListener listener; |
| 1842 | 1330 |
| 1843 _TestScanner(CharacterReader reader, [this.listener]) : super(reader); | 1331 _TestScanner(CharacterReader reader, [this.listener]) : super(reader); |
| 1844 | 1332 |
| 1845 @override | 1333 @override |
| 1846 void reportError( | 1334 void reportError( |
| 1847 ScannerErrorCode errorCode, int offset, List<Object> arguments) { | 1335 ScannerErrorCode errorCode, int offset, List<Object> arguments) { |
| 1848 if (listener != null) { | 1336 if (listener != null) { |
| 1849 listener.errors.add(new _TestError(offset, 1, errorCode, arguments)); | 1337 listener.errors.add(new TestError(offset, 1, errorCode, arguments)); |
| 1850 } | 1338 } |
| 1851 } | 1339 } |
| 1852 } | 1340 } |
| OLD | NEW |