| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 engine.scanner; | 5 @deprecated |
| 6 library analyzer.src.generated.scanner; |
| 6 | 7 |
| 7 import 'dart:collection'; | 8 export 'package:analyzer/dart/ast/token.dart'; |
| 8 | 9 export 'package:analyzer/src/dart/ast/token.dart' hide SimpleToken; |
| 9 import 'error.dart'; | 10 export 'package:analyzer/src/dart/scanner/reader.dart'; |
| 10 import 'java_engine.dart'; | 11 export 'package:analyzer/src/dart/scanner/scanner.dart'; |
| 11 import 'source.dart'; | |
| 12 | |
| 13 /** | |
| 14 * The opening half of a grouping pair of tokens. This is used for curly | |
| 15 * brackets ('{'), parentheses ('('), and square brackets ('['). | |
| 16 */ | |
| 17 class BeginToken extends Token { | |
| 18 /** | |
| 19 * The token that corresponds to this token. | |
| 20 */ | |
| 21 Token endToken; | |
| 22 | |
| 23 /** | |
| 24 * Initialize a newly created token to have the given [type] at the given | |
| 25 * [offset]. | |
| 26 */ | |
| 27 BeginToken(TokenType type, int offset) : super(type, offset) { | |
| 28 assert(type == TokenType.OPEN_CURLY_BRACKET || | |
| 29 type == TokenType.OPEN_PAREN || | |
| 30 type == TokenType.OPEN_SQUARE_BRACKET || | |
| 31 type == TokenType.STRING_INTERPOLATION_EXPRESSION); | |
| 32 } | |
| 33 | |
| 34 @override | |
| 35 Token copy() => new BeginToken(type, offset); | |
| 36 } | |
| 37 | |
| 38 /** | |
| 39 * A begin token that is preceded by comments. | |
| 40 */ | |
| 41 class BeginTokenWithComment extends BeginToken { | |
| 42 /** | |
| 43 * The first comment in the list of comments that precede this token. | |
| 44 */ | |
| 45 CommentToken _precedingComment; | |
| 46 | |
| 47 /** | |
| 48 * Initialize a newly created token to have the given [type] at the given | |
| 49 * [offset] and to be preceded by the comments reachable from the given | |
| 50 * [comment]. | |
| 51 */ | |
| 52 BeginTokenWithComment(TokenType type, int offset, this._precedingComment) | |
| 53 : super(type, offset) { | |
| 54 _setCommentParent(_precedingComment); | |
| 55 } | |
| 56 | |
| 57 CommentToken get precedingComments => _precedingComment; | |
| 58 | |
| 59 void set precedingComments(CommentToken comment) { | |
| 60 _precedingComment = comment; | |
| 61 _setCommentParent(_precedingComment); | |
| 62 } | |
| 63 | |
| 64 @override | |
| 65 void applyDelta(int delta) { | |
| 66 super.applyDelta(delta); | |
| 67 Token token = precedingComments; | |
| 68 while (token != null) { | |
| 69 token.applyDelta(delta); | |
| 70 token = token.next; | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 @override | |
| 75 Token copy() => | |
| 76 new BeginTokenWithComment(type, offset, copyComments(precedingComments)); | |
| 77 } | |
| 78 | |
| 79 /** | |
| 80 * A [CharacterReader] that reads a range of characters from another character | |
| 81 * reader. | |
| 82 */ | |
| 83 class CharacterRangeReader extends CharacterReader { | |
| 84 /** | |
| 85 * The reader from which the characters are actually being read. | |
| 86 */ | |
| 87 final CharacterReader baseReader; | |
| 88 | |
| 89 /** | |
| 90 * The last character to be read. | |
| 91 */ | |
| 92 final int endIndex; | |
| 93 | |
| 94 /** | |
| 95 * Initialize a newly created reader to read the characters from the given | |
| 96 * [baseReader] between the [startIndex] inclusive to [endIndex] exclusive. | |
| 97 */ | |
| 98 CharacterRangeReader(this.baseReader, int startIndex, this.endIndex) { | |
| 99 baseReader.offset = startIndex - 1; | |
| 100 } | |
| 101 | |
| 102 @override | |
| 103 int get offset => baseReader.offset; | |
| 104 | |
| 105 @override | |
| 106 void set offset(int offset) { | |
| 107 baseReader.offset = offset; | |
| 108 } | |
| 109 | |
| 110 @override | |
| 111 int advance() { | |
| 112 if (baseReader.offset + 1 >= endIndex) { | |
| 113 return -1; | |
| 114 } | |
| 115 return baseReader.advance(); | |
| 116 } | |
| 117 | |
| 118 @override | |
| 119 String getString(int start, int endDelta) => | |
| 120 baseReader.getString(start, endDelta); | |
| 121 | |
| 122 @override | |
| 123 int peek() { | |
| 124 if (baseReader.offset + 1 >= endIndex) { | |
| 125 return -1; | |
| 126 } | |
| 127 return baseReader.peek(); | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 /** | |
| 132 * An object used by the scanner to read the characters to be scanned. | |
| 133 */ | |
| 134 abstract class CharacterReader { | |
| 135 /** | |
| 136 * The current offset relative to the beginning of the source. Return the | |
| 137 * initial offset if the scanner has not yet scanned the source code, and one | |
| 138 * (1) past the end of the source code if the entire source code has been | |
| 139 * scanned. | |
| 140 */ | |
| 141 int get offset; | |
| 142 | |
| 143 /** | |
| 144 * Set the current offset relative to the beginning of the source to the given | |
| 145 * [offset]. The new offset must be between the initial offset and one (1) | |
| 146 * past the end of the source code. | |
| 147 */ | |
| 148 void set offset(int offset); | |
| 149 | |
| 150 /** | |
| 151 * Advance the current position and return the character at the new current | |
| 152 * position. | |
| 153 */ | |
| 154 int advance(); | |
| 155 | |
| 156 /** | |
| 157 * Return the substring of the source code between the [start] offset and the | |
| 158 * modified current position. The current position is modified by adding the | |
| 159 * [endDelta], which is the number of characters after the current location to | |
| 160 * be included in the string, or the number of characters before the current | |
| 161 * location to be excluded if the offset is negative. | |
| 162 */ | |
| 163 String getString(int start, int endDelta); | |
| 164 | |
| 165 /** | |
| 166 * Return the character at the current position without changing the current | |
| 167 * position. | |
| 168 */ | |
| 169 int peek(); | |
| 170 } | |
| 171 | |
| 172 /** | |
| 173 * A [CharacterReader] that reads characters from a character sequence. | |
| 174 */ | |
| 175 class CharSequenceReader implements CharacterReader { | |
| 176 /** | |
| 177 * The sequence from which characters will be read. | |
| 178 */ | |
| 179 final String _sequence; | |
| 180 | |
| 181 /** | |
| 182 * The number of characters in the string. | |
| 183 */ | |
| 184 int _stringLength = 0; | |
| 185 | |
| 186 /** | |
| 187 * The index, relative to the string, of the last character that was read. | |
| 188 */ | |
| 189 int _charOffset = 0; | |
| 190 | |
| 191 /** | |
| 192 * Initialize a newly created reader to read the characters in the given | |
| 193 * [_sequence]. | |
| 194 */ | |
| 195 CharSequenceReader(this._sequence) { | |
| 196 this._stringLength = _sequence.length; | |
| 197 this._charOffset = -1; | |
| 198 } | |
| 199 | |
| 200 @override | |
| 201 int get offset => _charOffset; | |
| 202 | |
| 203 @override | |
| 204 void set offset(int offset) { | |
| 205 _charOffset = offset; | |
| 206 } | |
| 207 | |
| 208 @override | |
| 209 int advance() { | |
| 210 if (_charOffset + 1 >= _stringLength) { | |
| 211 return -1; | |
| 212 } | |
| 213 return _sequence.codeUnitAt(++_charOffset); | |
| 214 } | |
| 215 | |
| 216 @override | |
| 217 String getString(int start, int endDelta) => | |
| 218 _sequence.substring(start, _charOffset + 1 + endDelta).toString(); | |
| 219 | |
| 220 @override | |
| 221 int peek() { | |
| 222 if (_charOffset + 1 >= _stringLength) { | |
| 223 return -1; | |
| 224 } | |
| 225 return _sequence.codeUnitAt(_charOffset + 1); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 /** | |
| 230 * A token representing a comment. | |
| 231 */ | |
| 232 class CommentToken extends StringToken { | |
| 233 /** | |
| 234 * The [Token] that contains this comment. | |
| 235 */ | |
| 236 Token parent; | |
| 237 | |
| 238 /** | |
| 239 * Initialize a newly created token to represent a token of the given [type] | |
| 240 * with the given [value] at the given [offset]. | |
| 241 */ | |
| 242 CommentToken(TokenType type, String value, int offset) | |
| 243 : super(type, value, offset); | |
| 244 | |
| 245 @override | |
| 246 CommentToken copy() => new CommentToken(type, _value, offset); | |
| 247 } | |
| 248 | |
| 249 /** | |
| 250 * A documentation comment token. | |
| 251 */ | |
| 252 class DocumentationCommentToken extends CommentToken { | |
| 253 /** | |
| 254 * The references embedded within the documentation comment. | |
| 255 * This list will be empty unless this is a documentation comment that has | |
| 256 * references embedded within it. | |
| 257 */ | |
| 258 final List<Token> references = <Token>[]; | |
| 259 | |
| 260 /** | |
| 261 * Initialize a newly created token to represent a token of the given [type] | |
| 262 * with the given [value] at the given [offset]. | |
| 263 */ | |
| 264 DocumentationCommentToken(TokenType type, String value, int offset) | |
| 265 : super(type, value, offset); | |
| 266 | |
| 267 @override | |
| 268 CommentToken copy() => new DocumentationCommentToken(type, _value, offset); | |
| 269 } | |
| 270 | |
| 271 /** | |
| 272 * The keywords in the Dart programming language. | |
| 273 */ | |
| 274 class Keyword { | |
| 275 static const Keyword ASSERT = const Keyword('ASSERT', "assert"); | |
| 276 | |
| 277 static const Keyword BREAK = const Keyword('BREAK', "break"); | |
| 278 | |
| 279 static const Keyword CASE = const Keyword('CASE', "case"); | |
| 280 | |
| 281 static const Keyword CATCH = const Keyword('CATCH', "catch"); | |
| 282 | |
| 283 static const Keyword CLASS = const Keyword('CLASS', "class"); | |
| 284 | |
| 285 static const Keyword CONST = const Keyword('CONST', "const"); | |
| 286 | |
| 287 static const Keyword CONTINUE = const Keyword('CONTINUE', "continue"); | |
| 288 | |
| 289 static const Keyword DEFAULT = const Keyword('DEFAULT', "default"); | |
| 290 | |
| 291 static const Keyword DO = const Keyword('DO', "do"); | |
| 292 | |
| 293 static const Keyword ELSE = const Keyword('ELSE', "else"); | |
| 294 | |
| 295 static const Keyword ENUM = const Keyword('ENUM', "enum"); | |
| 296 | |
| 297 static const Keyword EXTENDS = const Keyword('EXTENDS', "extends"); | |
| 298 | |
| 299 static const Keyword FALSE = const Keyword('FALSE', "false"); | |
| 300 | |
| 301 static const Keyword FINAL = const Keyword('FINAL', "final"); | |
| 302 | |
| 303 static const Keyword FINALLY = const Keyword('FINALLY', "finally"); | |
| 304 | |
| 305 static const Keyword FOR = const Keyword('FOR', "for"); | |
| 306 | |
| 307 static const Keyword IF = const Keyword('IF', "if"); | |
| 308 | |
| 309 static const Keyword IN = const Keyword('IN', "in"); | |
| 310 | |
| 311 static const Keyword IS = const Keyword('IS', "is"); | |
| 312 | |
| 313 static const Keyword NEW = const Keyword('NEW', "new"); | |
| 314 | |
| 315 static const Keyword NULL = const Keyword('NULL', "null"); | |
| 316 | |
| 317 static const Keyword RETHROW = const Keyword('RETHROW', "rethrow"); | |
| 318 | |
| 319 static const Keyword RETURN = const Keyword('RETURN', "return"); | |
| 320 | |
| 321 static const Keyword SUPER = const Keyword('SUPER', "super"); | |
| 322 | |
| 323 static const Keyword SWITCH = const Keyword('SWITCH', "switch"); | |
| 324 | |
| 325 static const Keyword THIS = const Keyword('THIS', "this"); | |
| 326 | |
| 327 static const Keyword THROW = const Keyword('THROW', "throw"); | |
| 328 | |
| 329 static const Keyword TRUE = const Keyword('TRUE', "true"); | |
| 330 | |
| 331 static const Keyword TRY = const Keyword('TRY', "try"); | |
| 332 | |
| 333 static const Keyword VAR = const Keyword('VAR', "var"); | |
| 334 | |
| 335 static const Keyword VOID = const Keyword('VOID', "void"); | |
| 336 | |
| 337 static const Keyword WHILE = const Keyword('WHILE', "while"); | |
| 338 | |
| 339 static const Keyword WITH = const Keyword('WITH', "with"); | |
| 340 | |
| 341 static const Keyword ABSTRACT = const Keyword('ABSTRACT', "abstract", true); | |
| 342 | |
| 343 static const Keyword AS = const Keyword('AS', "as", true); | |
| 344 | |
| 345 static const Keyword DEFERRED = const Keyword('DEFERRED', "deferred", true); | |
| 346 | |
| 347 static const Keyword DYNAMIC = const Keyword('DYNAMIC', "dynamic", true); | |
| 348 | |
| 349 static const Keyword EXPORT = const Keyword('EXPORT', "export", true); | |
| 350 | |
| 351 static const Keyword EXTERNAL = const Keyword('EXTERNAL', "external", true); | |
| 352 | |
| 353 static const Keyword FACTORY = const Keyword('FACTORY', "factory", true); | |
| 354 | |
| 355 static const Keyword GET = const Keyword('GET', "get", true); | |
| 356 | |
| 357 static const Keyword IMPLEMENTS = | |
| 358 const Keyword('IMPLEMENTS', "implements", true); | |
| 359 | |
| 360 static const Keyword IMPORT = const Keyword('IMPORT', "import", true); | |
| 361 | |
| 362 static const Keyword LIBRARY = const Keyword('LIBRARY', "library", true); | |
| 363 | |
| 364 static const Keyword OPERATOR = const Keyword('OPERATOR', "operator", true); | |
| 365 | |
| 366 static const Keyword PART = const Keyword('PART', "part", true); | |
| 367 | |
| 368 static const Keyword SET = const Keyword('SET', "set", true); | |
| 369 | |
| 370 static const Keyword STATIC = const Keyword('STATIC', "static", true); | |
| 371 | |
| 372 static const Keyword TYPEDEF = const Keyword('TYPEDEF', "typedef", true); | |
| 373 | |
| 374 static const List<Keyword> values = const [ | |
| 375 ASSERT, | |
| 376 BREAK, | |
| 377 CASE, | |
| 378 CATCH, | |
| 379 CLASS, | |
| 380 CONST, | |
| 381 CONTINUE, | |
| 382 DEFAULT, | |
| 383 DO, | |
| 384 ELSE, | |
| 385 ENUM, | |
| 386 EXTENDS, | |
| 387 FALSE, | |
| 388 FINAL, | |
| 389 FINALLY, | |
| 390 FOR, | |
| 391 IF, | |
| 392 IN, | |
| 393 IS, | |
| 394 NEW, | |
| 395 NULL, | |
| 396 RETHROW, | |
| 397 RETURN, | |
| 398 SUPER, | |
| 399 SWITCH, | |
| 400 THIS, | |
| 401 THROW, | |
| 402 TRUE, | |
| 403 TRY, | |
| 404 VAR, | |
| 405 VOID, | |
| 406 WHILE, | |
| 407 WITH, | |
| 408 ABSTRACT, | |
| 409 AS, | |
| 410 DEFERRED, | |
| 411 DYNAMIC, | |
| 412 EXPORT, | |
| 413 EXTERNAL, | |
| 414 FACTORY, | |
| 415 GET, | |
| 416 IMPLEMENTS, | |
| 417 IMPORT, | |
| 418 LIBRARY, | |
| 419 OPERATOR, | |
| 420 PART, | |
| 421 SET, | |
| 422 STATIC, | |
| 423 TYPEDEF | |
| 424 ]; | |
| 425 | |
| 426 /** | |
| 427 * A table mapping the lexemes of keywords to the corresponding keyword. | |
| 428 */ | |
| 429 static final Map<String, Keyword> keywords = _createKeywordMap(); | |
| 430 | |
| 431 /** | |
| 432 * The name of the keyword type. | |
| 433 */ | |
| 434 final String name; | |
| 435 | |
| 436 /** | |
| 437 * The lexeme for the keyword. | |
| 438 */ | |
| 439 final String syntax; | |
| 440 | |
| 441 /** | |
| 442 * A flag indicating whether the keyword is a pseudo-keyword. Pseudo keywords | |
| 443 * can be used as identifiers. | |
| 444 */ | |
| 445 final bool isPseudoKeyword; | |
| 446 | |
| 447 /** | |
| 448 * Initialize a newly created keyword to have the given [name] and [syntax]. | |
| 449 * The keyword is a pseudo-keyword if the [isPseudoKeyword] flag is `true`. | |
| 450 */ | |
| 451 const Keyword(this.name, this.syntax, [this.isPseudoKeyword = false]); | |
| 452 | |
| 453 @override | |
| 454 String toString() => name; | |
| 455 | |
| 456 /** | |
| 457 * Create a table mapping the lexemes of keywords to the corresponding keyword | |
| 458 * and return the table that was created. | |
| 459 */ | |
| 460 static Map<String, Keyword> _createKeywordMap() { | |
| 461 LinkedHashMap<String, Keyword> result = | |
| 462 new LinkedHashMap<String, Keyword>(); | |
| 463 for (Keyword keyword in values) { | |
| 464 result[keyword.syntax] = keyword; | |
| 465 } | |
| 466 return result; | |
| 467 } | |
| 468 } | |
| 469 | |
| 470 /** | |
| 471 * A state in a state machine used to scan keywords. | |
| 472 */ | |
| 473 class KeywordState { | |
| 474 /** | |
| 475 * An empty transition table used by leaf states. | |
| 476 */ | |
| 477 static List<KeywordState> _EMPTY_TABLE = new List<KeywordState>(26); | |
| 478 | |
| 479 /** | |
| 480 * The initial state in the state machine. | |
| 481 */ | |
| 482 static final KeywordState KEYWORD_STATE = _createKeywordStateTable(); | |
| 483 | |
| 484 /** | |
| 485 * A table mapping characters to the states to which those characters will | |
| 486 * transition. (The index into the array is the offset from the character | |
| 487 * `'a'` to the transitioning character.) | |
| 488 */ | |
| 489 final List<KeywordState> _table; | |
| 490 | |
| 491 /** | |
| 492 * The keyword that is recognized by this state, or `null` if this state is | |
| 493 * not a terminal state. | |
| 494 */ | |
| 495 Keyword _keyword; | |
| 496 | |
| 497 /** | |
| 498 * Initialize a newly created state to have the given transitions and to | |
| 499 * recognize the keyword with the given [syntax]. | |
| 500 */ | |
| 501 KeywordState(this._table, String syntax) { | |
| 502 this._keyword = (syntax == null) ? null : Keyword.keywords[syntax]; | |
| 503 } | |
| 504 | |
| 505 /** | |
| 506 * Return the keyword that was recognized by this state, or `null` if this | |
| 507 * state does not recognized a keyword. | |
| 508 */ | |
| 509 Keyword keyword() => _keyword; | |
| 510 | |
| 511 /** | |
| 512 * Return the state that follows this state on a transition of the given | |
| 513 * [character], or `null` if there is no valid state reachable from this state | |
| 514 * with such a transition. | |
| 515 */ | |
| 516 KeywordState next(int character) => _table[character - 0x61]; | |
| 517 | |
| 518 /** | |
| 519 * Create the next state in the state machine where we have already recognized | |
| 520 * the subset of strings in the given array of [strings] starting at the given | |
| 521 * [offset] and having the given [length]. All of these strings have a common | |
| 522 * prefix and the next character is at the given [start] index. | |
| 523 */ | |
| 524 static KeywordState _computeKeywordStateTable( | |
| 525 int start, List<String> strings, int offset, int length) { | |
| 526 List<KeywordState> result = new List<KeywordState>(26); | |
| 527 assert(length != 0); | |
| 528 int chunk = 0x0; | |
| 529 int chunkStart = -1; | |
| 530 bool isLeaf = false; | |
| 531 for (int i = offset; i < offset + length; i++) { | |
| 532 if (strings[i].length == start) { | |
| 533 isLeaf = true; | |
| 534 } | |
| 535 if (strings[i].length > start) { | |
| 536 int c = strings[i].codeUnitAt(start); | |
| 537 if (chunk != c) { | |
| 538 if (chunkStart != -1) { | |
| 539 result[chunk - 0x61] = _computeKeywordStateTable( | |
| 540 start + 1, strings, chunkStart, i - chunkStart); | |
| 541 } | |
| 542 chunkStart = i; | |
| 543 chunk = c; | |
| 544 } | |
| 545 } | |
| 546 } | |
| 547 if (chunkStart != -1) { | |
| 548 assert(result[chunk - 0x61] == null); | |
| 549 result[chunk - 0x61] = _computeKeywordStateTable( | |
| 550 start + 1, strings, chunkStart, offset + length - chunkStart); | |
| 551 } else { | |
| 552 assert(length == 1); | |
| 553 return new KeywordState(_EMPTY_TABLE, strings[offset]); | |
| 554 } | |
| 555 if (isLeaf) { | |
| 556 return new KeywordState(result, strings[offset]); | |
| 557 } else { | |
| 558 return new KeywordState(result, null); | |
| 559 } | |
| 560 } | |
| 561 | |
| 562 /** | |
| 563 * Create and return the initial state in the state machine. | |
| 564 */ | |
| 565 static KeywordState _createKeywordStateTable() { | |
| 566 List<Keyword> values = Keyword.values; | |
| 567 List<String> strings = new List<String>(values.length); | |
| 568 for (int i = 0; i < values.length; i++) { | |
| 569 strings[i] = values[i].syntax; | |
| 570 } | |
| 571 strings.sort(); | |
| 572 return _computeKeywordStateTable(0, strings, 0, strings.length); | |
| 573 } | |
| 574 } | |
| 575 | |
| 576 /** | |
| 577 * A token representing a keyword in the language. | |
| 578 */ | |
| 579 class KeywordToken extends Token { | |
| 580 /** | |
| 581 * The keyword being represented by this token. | |
| 582 */ | |
| 583 final Keyword keyword; | |
| 584 | |
| 585 /** | |
| 586 * Initialize a newly created token to represent the given [keyword] at the | |
| 587 * given [offset]. | |
| 588 */ | |
| 589 KeywordToken(this.keyword, int offset) : super(TokenType.KEYWORD, offset); | |
| 590 | |
| 591 @override | |
| 592 String get lexeme => keyword.syntax; | |
| 593 | |
| 594 @override | |
| 595 Token copy() => new KeywordToken(keyword, offset); | |
| 596 | |
| 597 @override | |
| 598 Keyword value() => keyword; | |
| 599 } | |
| 600 | |
| 601 /** | |
| 602 * A keyword token that is preceded by comments. | |
| 603 */ | |
| 604 class KeywordTokenWithComment extends KeywordToken { | |
| 605 /** | |
| 606 * The first comment in the list of comments that precede this token. | |
| 607 */ | |
| 608 CommentToken _precedingComment; | |
| 609 | |
| 610 /** | |
| 611 * Initialize a newly created token to to represent the given [keyword] at the | |
| 612 * given [offset] and to be preceded by the comments reachable from the given | |
| 613 * [comment]. | |
| 614 */ | |
| 615 KeywordTokenWithComment(Keyword keyword, int offset, this._precedingComment) | |
| 616 : super(keyword, offset) { | |
| 617 _setCommentParent(_precedingComment); | |
| 618 } | |
| 619 | |
| 620 CommentToken get precedingComments => _precedingComment; | |
| 621 | |
| 622 void set precedingComments(CommentToken comment) { | |
| 623 _precedingComment = comment; | |
| 624 _setCommentParent(_precedingComment); | |
| 625 } | |
| 626 | |
| 627 @override | |
| 628 void applyDelta(int delta) { | |
| 629 super.applyDelta(delta); | |
| 630 Token token = precedingComments; | |
| 631 while (token != null) { | |
| 632 token.applyDelta(delta); | |
| 633 token = token.next; | |
| 634 } | |
| 635 } | |
| 636 | |
| 637 @override | |
| 638 Token copy() => new KeywordTokenWithComment( | |
| 639 keyword, offset, copyComments(precedingComments)); | |
| 640 } | |
| 641 | |
| 642 /** | |
| 643 * The class `Scanner` implements a scanner for Dart code. | |
| 644 * | |
| 645 * The lexical structure of Dart is ambiguous without knowledge of the context | |
| 646 * in which a token is being scanned. For example, without context we cannot | |
| 647 * determine whether source of the form "<<" should be scanned as a single | |
| 648 * left-shift operator or as two left angle brackets. This scanner does not have | |
| 649 * any context, so it always resolves such conflicts by scanning the longest | |
| 650 * possible token. | |
| 651 */ | |
| 652 class Scanner { | |
| 653 /** | |
| 654 * The source being scanned. | |
| 655 */ | |
| 656 final Source source; | |
| 657 | |
| 658 /** | |
| 659 * The reader used to access the characters in the source. | |
| 660 */ | |
| 661 final CharacterReader _reader; | |
| 662 | |
| 663 /** | |
| 664 * The error listener that will be informed of any errors that are found | |
| 665 * during the scan. | |
| 666 */ | |
| 667 final AnalysisErrorListener _errorListener; | |
| 668 | |
| 669 /** | |
| 670 * The flag specifying whether documentation comments should be parsed. | |
| 671 */ | |
| 672 bool _preserveComments = true; | |
| 673 | |
| 674 /** | |
| 675 * The token pointing to the head of the linked list of tokens. | |
| 676 */ | |
| 677 Token _tokens; | |
| 678 | |
| 679 /** | |
| 680 * The last token that was scanned. | |
| 681 */ | |
| 682 Token _tail; | |
| 683 | |
| 684 /** | |
| 685 * The first token in the list of comment tokens found since the last | |
| 686 * non-comment token. | |
| 687 */ | |
| 688 Token _firstComment; | |
| 689 | |
| 690 /** | |
| 691 * The last token in the list of comment tokens found since the last | |
| 692 * non-comment token. | |
| 693 */ | |
| 694 Token _lastComment; | |
| 695 | |
| 696 /** | |
| 697 * The index of the first character of the current token. | |
| 698 */ | |
| 699 int _tokenStart = 0; | |
| 700 | |
| 701 /** | |
| 702 * A list containing the offsets of the first character of each line in the | |
| 703 * source code. | |
| 704 */ | |
| 705 List<int> _lineStarts = new List<int>(); | |
| 706 | |
| 707 /** | |
| 708 * A list, treated something like a stack, of tokens representing the | |
| 709 * beginning of a matched pair. It is used to pair the end tokens with the | |
| 710 * begin tokens. | |
| 711 */ | |
| 712 List<BeginToken> _groupingStack = new List<BeginToken>(); | |
| 713 | |
| 714 /** | |
| 715 * The index of the last item in the [_groupingStack], or `-1` if the stack is | |
| 716 * empty. | |
| 717 */ | |
| 718 int _stackEnd = -1; | |
| 719 | |
| 720 /** | |
| 721 * A flag indicating whether any unmatched groups were found during the parse. | |
| 722 */ | |
| 723 bool _hasUnmatchedGroups = false; | |
| 724 | |
| 725 /** | |
| 726 * Initialize a newly created scanner to scan characters from the given | |
| 727 * [source]. The given character [_reader] will be used to read the characters | |
| 728 * in the source. The given [_errorListener] will be informed of any errors | |
| 729 * that are found. | |
| 730 */ | |
| 731 Scanner(this.source, this._reader, this._errorListener) { | |
| 732 _tokens = new Token(TokenType.EOF, -1); | |
| 733 _tokens.setNext(_tokens); | |
| 734 _tail = _tokens; | |
| 735 _tokenStart = -1; | |
| 736 _lineStarts.add(0); | |
| 737 } | |
| 738 | |
| 739 /** | |
| 740 * Return the first token in the token stream that was scanned. | |
| 741 */ | |
| 742 Token get firstToken => _tokens.next; | |
| 743 | |
| 744 /** | |
| 745 * Return `true` if any unmatched groups were found during the parse. | |
| 746 */ | |
| 747 bool get hasUnmatchedGroups => _hasUnmatchedGroups; | |
| 748 | |
| 749 /** | |
| 750 * Return an array containing the offsets of the first character of each line | |
| 751 * in the source code. | |
| 752 */ | |
| 753 List<int> get lineStarts => _lineStarts; | |
| 754 | |
| 755 /** | |
| 756 * Set whether documentation tokens should be preserved. | |
| 757 */ | |
| 758 void set preserveComments(bool preserveComments) { | |
| 759 this._preserveComments = preserveComments; | |
| 760 } | |
| 761 | |
| 762 /** | |
| 763 * Return the last token that was scanned. | |
| 764 */ | |
| 765 Token get tail => _tail; | |
| 766 | |
| 767 /** | |
| 768 * Append the given [token] to the end of the token stream being scanned. This | |
| 769 * method is intended to be used by subclasses that copy existing tokens and | |
| 770 * should not normally be used because it will fail to correctly associate any | |
| 771 * comments with the token being passed in. | |
| 772 */ | |
| 773 void appendToken(Token token) { | |
| 774 _tail = _tail.setNext(token); | |
| 775 } | |
| 776 | |
| 777 int bigSwitch(int next) { | |
| 778 _beginToken(); | |
| 779 if (next == 0xD) { | |
| 780 // '\r' | |
| 781 next = _reader.advance(); | |
| 782 if (next == 0xA) { | |
| 783 // '\n' | |
| 784 next = _reader.advance(); | |
| 785 } | |
| 786 recordStartOfLine(); | |
| 787 return next; | |
| 788 } else if (next == 0xA) { | |
| 789 // '\n' | |
| 790 next = _reader.advance(); | |
| 791 recordStartOfLine(); | |
| 792 return next; | |
| 793 } else if (next == 0x9 || next == 0x20) { | |
| 794 // '\t' || ' ' | |
| 795 return _reader.advance(); | |
| 796 } | |
| 797 if (next == 0x72) { | |
| 798 // 'r' | |
| 799 int peek = _reader.peek(); | |
| 800 if (peek == 0x22 || peek == 0x27) { | |
| 801 // '"' || "'" | |
| 802 int start = _reader.offset; | |
| 803 return _tokenizeString(_reader.advance(), start, true); | |
| 804 } | |
| 805 } | |
| 806 if (0x61 <= next && next <= 0x7A) { | |
| 807 // 'a'-'z' | |
| 808 return _tokenizeKeywordOrIdentifier(next, true); | |
| 809 } | |
| 810 if ((0x41 <= next && next <= 0x5A) || next == 0x5F || next == 0x24) { | |
| 811 // 'A'-'Z' || '_' || '$' | |
| 812 return _tokenizeIdentifier(next, _reader.offset, true); | |
| 813 } | |
| 814 if (next == 0x3C) { | |
| 815 // '<' | |
| 816 return _tokenizeLessThan(next); | |
| 817 } | |
| 818 if (next == 0x3E) { | |
| 819 // '>' | |
| 820 return _tokenizeGreaterThan(next); | |
| 821 } | |
| 822 if (next == 0x3D) { | |
| 823 // '=' | |
| 824 return _tokenizeEquals(next); | |
| 825 } | |
| 826 if (next == 0x21) { | |
| 827 // '!' | |
| 828 return _tokenizeExclamation(next); | |
| 829 } | |
| 830 if (next == 0x2B) { | |
| 831 // '+' | |
| 832 return _tokenizePlus(next); | |
| 833 } | |
| 834 if (next == 0x2D) { | |
| 835 // '-' | |
| 836 return _tokenizeMinus(next); | |
| 837 } | |
| 838 if (next == 0x2A) { | |
| 839 // '*' | |
| 840 return _tokenizeMultiply(next); | |
| 841 } | |
| 842 if (next == 0x25) { | |
| 843 // '%' | |
| 844 return _tokenizePercent(next); | |
| 845 } | |
| 846 if (next == 0x26) { | |
| 847 // '&' | |
| 848 return _tokenizeAmpersand(next); | |
| 849 } | |
| 850 if (next == 0x7C) { | |
| 851 // '|' | |
| 852 return _tokenizeBar(next); | |
| 853 } | |
| 854 if (next == 0x5E) { | |
| 855 // '^' | |
| 856 return _tokenizeCaret(next); | |
| 857 } | |
| 858 if (next == 0x5B) { | |
| 859 // '[' | |
| 860 return _tokenizeOpenSquareBracket(next); | |
| 861 } | |
| 862 if (next == 0x7E) { | |
| 863 // '~' | |
| 864 return _tokenizeTilde(next); | |
| 865 } | |
| 866 if (next == 0x5C) { | |
| 867 // '\\' | |
| 868 _appendTokenOfType(TokenType.BACKSLASH); | |
| 869 return _reader.advance(); | |
| 870 } | |
| 871 if (next == 0x23) { | |
| 872 // '#' | |
| 873 return _tokenizeTag(next); | |
| 874 } | |
| 875 if (next == 0x28) { | |
| 876 // '(' | |
| 877 _appendBeginToken(TokenType.OPEN_PAREN); | |
| 878 return _reader.advance(); | |
| 879 } | |
| 880 if (next == 0x29) { | |
| 881 // ')' | |
| 882 _appendEndToken(TokenType.CLOSE_PAREN, TokenType.OPEN_PAREN); | |
| 883 return _reader.advance(); | |
| 884 } | |
| 885 if (next == 0x2C) { | |
| 886 // ',' | |
| 887 _appendTokenOfType(TokenType.COMMA); | |
| 888 return _reader.advance(); | |
| 889 } | |
| 890 if (next == 0x3A) { | |
| 891 // ':' | |
| 892 _appendTokenOfType(TokenType.COLON); | |
| 893 return _reader.advance(); | |
| 894 } | |
| 895 if (next == 0x3B) { | |
| 896 // ';' | |
| 897 _appendTokenOfType(TokenType.SEMICOLON); | |
| 898 return _reader.advance(); | |
| 899 } | |
| 900 if (next == 0x3F) { | |
| 901 // '?' | |
| 902 return _tokenizeQuestion(); | |
| 903 } | |
| 904 if (next == 0x5D) { | |
| 905 // ']' | |
| 906 _appendEndToken( | |
| 907 TokenType.CLOSE_SQUARE_BRACKET, TokenType.OPEN_SQUARE_BRACKET); | |
| 908 return _reader.advance(); | |
| 909 } | |
| 910 if (next == 0x60) { | |
| 911 // '`' | |
| 912 _appendTokenOfType(TokenType.BACKPING); | |
| 913 return _reader.advance(); | |
| 914 } | |
| 915 if (next == 0x7B) { | |
| 916 // '{' | |
| 917 _appendBeginToken(TokenType.OPEN_CURLY_BRACKET); | |
| 918 return _reader.advance(); | |
| 919 } | |
| 920 if (next == 0x7D) { | |
| 921 // '}' | |
| 922 _appendEndToken( | |
| 923 TokenType.CLOSE_CURLY_BRACKET, TokenType.OPEN_CURLY_BRACKET); | |
| 924 return _reader.advance(); | |
| 925 } | |
| 926 if (next == 0x2F) { | |
| 927 // '/' | |
| 928 return _tokenizeSlashOrComment(next); | |
| 929 } | |
| 930 if (next == 0x40) { | |
| 931 // '@' | |
| 932 _appendTokenOfType(TokenType.AT); | |
| 933 return _reader.advance(); | |
| 934 } | |
| 935 if (next == 0x22 || next == 0x27) { | |
| 936 // '"' || "'" | |
| 937 return _tokenizeString(next, _reader.offset, false); | |
| 938 } | |
| 939 if (next == 0x2E) { | |
| 940 // '.' | |
| 941 return _tokenizeDotOrNumber(next); | |
| 942 } | |
| 943 if (next == 0x30) { | |
| 944 // '0' | |
| 945 return _tokenizeHexOrNumber(next); | |
| 946 } | |
| 947 if (0x31 <= next && next <= 0x39) { | |
| 948 // '1'-'9' | |
| 949 return _tokenizeNumber(next); | |
| 950 } | |
| 951 if (next == -1) { | |
| 952 // EOF | |
| 953 return -1; | |
| 954 } | |
| 955 _reportError(ScannerErrorCode.ILLEGAL_CHARACTER, [next]); | |
| 956 return _reader.advance(); | |
| 957 } | |
| 958 | |
| 959 /** | |
| 960 * Record the fact that we are at the beginning of a new line in the source. | |
| 961 */ | |
| 962 void recordStartOfLine() { | |
| 963 _lineStarts.add(_reader.offset); | |
| 964 } | |
| 965 | |
| 966 /** | |
| 967 * Record that the source begins on the given [line] and [column] at the | |
| 968 * current offset as given by the reader. Both the line and the column are | |
| 969 * one-based indexes. The line starts for lines before the given line will not | |
| 970 * be correct. | |
| 971 * | |
| 972 * This method must be invoked at most one time and must be invoked before | |
| 973 * scanning begins. The values provided must be sensible. The results are | |
| 974 * undefined if these conditions are violated. | |
| 975 */ | |
| 976 void setSourceStart(int line, int column) { | |
| 977 int offset = _reader.offset; | |
| 978 if (line < 1 || column < 1 || offset < 0 || (line + column - 2) >= offset) { | |
| 979 return; | |
| 980 } | |
| 981 for (int i = 2; i < line; i++) { | |
| 982 _lineStarts.add(1); | |
| 983 } | |
| 984 _lineStarts.add(offset - column + 1); | |
| 985 } | |
| 986 | |
| 987 /** | |
| 988 * Scan the source code to produce a list of tokens representing the source, | |
| 989 * and return the first token in the list of tokens that were produced. | |
| 990 */ | |
| 991 Token tokenize() { | |
| 992 int next = _reader.advance(); | |
| 993 while (next != -1) { | |
| 994 next = bigSwitch(next); | |
| 995 } | |
| 996 _appendEofToken(); | |
| 997 return firstToken; | |
| 998 } | |
| 999 | |
| 1000 void _appendBeginToken(TokenType type) { | |
| 1001 BeginToken token; | |
| 1002 if (_firstComment == null) { | |
| 1003 token = new BeginToken(type, _tokenStart); | |
| 1004 } else { | |
| 1005 token = new BeginTokenWithComment(type, _tokenStart, _firstComment); | |
| 1006 _firstComment = null; | |
| 1007 _lastComment = null; | |
| 1008 } | |
| 1009 _tail = _tail.setNext(token); | |
| 1010 _groupingStack.add(token); | |
| 1011 _stackEnd++; | |
| 1012 } | |
| 1013 | |
| 1014 void _appendCommentToken(TokenType type, String value) { | |
| 1015 // Ignore comment tokens if client specified that it doesn't need them. | |
| 1016 if (!_preserveComments) { | |
| 1017 return; | |
| 1018 } | |
| 1019 // OK, remember comment tokens. | |
| 1020 CommentToken token; | |
| 1021 if (_isDocumentationComment(value)) { | |
| 1022 token = new DocumentationCommentToken(type, value, _tokenStart); | |
| 1023 } else { | |
| 1024 token = new CommentToken(type, value, _tokenStart); | |
| 1025 } | |
| 1026 if (_firstComment == null) { | |
| 1027 _firstComment = token; | |
| 1028 _lastComment = _firstComment; | |
| 1029 } else { | |
| 1030 _lastComment = _lastComment.setNext(token); | |
| 1031 } | |
| 1032 } | |
| 1033 | |
| 1034 void _appendEndToken(TokenType type, TokenType beginType) { | |
| 1035 Token token; | |
| 1036 if (_firstComment == null) { | |
| 1037 token = new Token(type, _tokenStart); | |
| 1038 } else { | |
| 1039 token = new TokenWithComment(type, _tokenStart, _firstComment); | |
| 1040 _firstComment = null; | |
| 1041 _lastComment = null; | |
| 1042 } | |
| 1043 _tail = _tail.setNext(token); | |
| 1044 if (_stackEnd >= 0) { | |
| 1045 BeginToken begin = _groupingStack[_stackEnd]; | |
| 1046 if (begin.type == beginType) { | |
| 1047 begin.endToken = token; | |
| 1048 _groupingStack.removeAt(_stackEnd--); | |
| 1049 } | |
| 1050 } | |
| 1051 } | |
| 1052 | |
| 1053 void _appendEofToken() { | |
| 1054 Token eofToken; | |
| 1055 if (_firstComment == null) { | |
| 1056 eofToken = new Token(TokenType.EOF, _reader.offset + 1); | |
| 1057 } else { | |
| 1058 eofToken = new TokenWithComment( | |
| 1059 TokenType.EOF, _reader.offset + 1, _firstComment); | |
| 1060 _firstComment = null; | |
| 1061 _lastComment = null; | |
| 1062 } | |
| 1063 // The EOF token points to itself so that there is always infinite | |
| 1064 // look-ahead. | |
| 1065 eofToken.setNext(eofToken); | |
| 1066 _tail = _tail.setNext(eofToken); | |
| 1067 if (_stackEnd >= 0) { | |
| 1068 _hasUnmatchedGroups = true; | |
| 1069 // TODO(brianwilkerson) Fix the ungrouped tokens? | |
| 1070 } | |
| 1071 } | |
| 1072 | |
| 1073 void _appendKeywordToken(Keyword keyword) { | |
| 1074 if (_firstComment == null) { | |
| 1075 _tail = _tail.setNext(new KeywordToken(keyword, _tokenStart)); | |
| 1076 } else { | |
| 1077 _tail = _tail.setNext( | |
| 1078 new KeywordTokenWithComment(keyword, _tokenStart, _firstComment)); | |
| 1079 _firstComment = null; | |
| 1080 _lastComment = null; | |
| 1081 } | |
| 1082 } | |
| 1083 | |
| 1084 void _appendStringToken(TokenType type, String value) { | |
| 1085 if (_firstComment == null) { | |
| 1086 _tail = _tail.setNext(new StringToken(type, value, _tokenStart)); | |
| 1087 } else { | |
| 1088 _tail = _tail.setNext( | |
| 1089 new StringTokenWithComment(type, value, _tokenStart, _firstComment)); | |
| 1090 _firstComment = null; | |
| 1091 _lastComment = null; | |
| 1092 } | |
| 1093 } | |
| 1094 | |
| 1095 void _appendStringTokenWithOffset(TokenType type, String value, int offset) { | |
| 1096 if (_firstComment == null) { | |
| 1097 _tail = _tail.setNext(new StringToken(type, value, _tokenStart + offset)); | |
| 1098 } else { | |
| 1099 _tail = _tail.setNext(new StringTokenWithComment( | |
| 1100 type, value, _tokenStart + offset, _firstComment)); | |
| 1101 _firstComment = null; | |
| 1102 _lastComment = null; | |
| 1103 } | |
| 1104 } | |
| 1105 | |
| 1106 void _appendTokenOfType(TokenType type) { | |
| 1107 if (_firstComment == null) { | |
| 1108 _tail = _tail.setNext(new Token(type, _tokenStart)); | |
| 1109 } else { | |
| 1110 _tail = | |
| 1111 _tail.setNext(new TokenWithComment(type, _tokenStart, _firstComment)); | |
| 1112 _firstComment = null; | |
| 1113 _lastComment = null; | |
| 1114 } | |
| 1115 } | |
| 1116 | |
| 1117 void _appendTokenOfTypeWithOffset(TokenType type, int offset) { | |
| 1118 if (_firstComment == null) { | |
| 1119 _tail = _tail.setNext(new Token(type, offset)); | |
| 1120 } else { | |
| 1121 _tail = _tail.setNext(new TokenWithComment(type, offset, _firstComment)); | |
| 1122 _firstComment = null; | |
| 1123 _lastComment = null; | |
| 1124 } | |
| 1125 } | |
| 1126 | |
| 1127 void _beginToken() { | |
| 1128 _tokenStart = _reader.offset; | |
| 1129 } | |
| 1130 | |
| 1131 /** | |
| 1132 * Return the beginning token corresponding to a closing brace that was found | |
| 1133 * while scanning inside a string interpolation expression. Tokens that cannot | |
| 1134 * be matched with the closing brace will be dropped from the stack. | |
| 1135 */ | |
| 1136 BeginToken _findTokenMatchingClosingBraceInInterpolationExpression() { | |
| 1137 while (_stackEnd >= 0) { | |
| 1138 BeginToken begin = _groupingStack[_stackEnd]; | |
| 1139 if (begin.type == TokenType.OPEN_CURLY_BRACKET || | |
| 1140 begin.type == TokenType.STRING_INTERPOLATION_EXPRESSION) { | |
| 1141 return begin; | |
| 1142 } | |
| 1143 _hasUnmatchedGroups = true; | |
| 1144 _groupingStack.removeAt(_stackEnd--); | |
| 1145 } | |
| 1146 // | |
| 1147 // We should never get to this point because we wouldn't be inside a string | |
| 1148 // interpolation expression unless we had previously found the start of the | |
| 1149 // expression. | |
| 1150 // | |
| 1151 return null; | |
| 1152 } | |
| 1153 | |
| 1154 /** | |
| 1155 * Report an error at the current offset. The [errorCode] is the error code | |
| 1156 * indicating the nature of the error. The [arguments] are any arguments | |
| 1157 * needed to complete the error message | |
| 1158 */ | |
| 1159 void _reportError(ScannerErrorCode errorCode, [List<Object> arguments]) { | |
| 1160 _errorListener.onError( | |
| 1161 new AnalysisError(source, _reader.offset, 1, errorCode, arguments)); | |
| 1162 } | |
| 1163 | |
| 1164 int _select(int choice, TokenType yesType, TokenType noType) { | |
| 1165 int next = _reader.advance(); | |
| 1166 if (next == choice) { | |
| 1167 _appendTokenOfType(yesType); | |
| 1168 return _reader.advance(); | |
| 1169 } else { | |
| 1170 _appendTokenOfType(noType); | |
| 1171 return next; | |
| 1172 } | |
| 1173 } | |
| 1174 | |
| 1175 int _selectWithOffset( | |
| 1176 int choice, TokenType yesType, TokenType noType, int offset) { | |
| 1177 int next = _reader.advance(); | |
| 1178 if (next == choice) { | |
| 1179 _appendTokenOfTypeWithOffset(yesType, offset); | |
| 1180 return _reader.advance(); | |
| 1181 } else { | |
| 1182 _appendTokenOfTypeWithOffset(noType, offset); | |
| 1183 return next; | |
| 1184 } | |
| 1185 } | |
| 1186 | |
| 1187 int _tokenizeAmpersand(int next) { | |
| 1188 // && &= & | |
| 1189 next = _reader.advance(); | |
| 1190 if (next == 0x26) { | |
| 1191 _appendTokenOfType(TokenType.AMPERSAND_AMPERSAND); | |
| 1192 return _reader.advance(); | |
| 1193 } else if (next == 0x3D) { | |
| 1194 _appendTokenOfType(TokenType.AMPERSAND_EQ); | |
| 1195 return _reader.advance(); | |
| 1196 } else { | |
| 1197 _appendTokenOfType(TokenType.AMPERSAND); | |
| 1198 return next; | |
| 1199 } | |
| 1200 } | |
| 1201 | |
| 1202 int _tokenizeBar(int next) { | |
| 1203 // | || |= | |
| 1204 next = _reader.advance(); | |
| 1205 if (next == 0x7C) { | |
| 1206 _appendTokenOfType(TokenType.BAR_BAR); | |
| 1207 return _reader.advance(); | |
| 1208 } else if (next == 0x3D) { | |
| 1209 _appendTokenOfType(TokenType.BAR_EQ); | |
| 1210 return _reader.advance(); | |
| 1211 } else { | |
| 1212 _appendTokenOfType(TokenType.BAR); | |
| 1213 return next; | |
| 1214 } | |
| 1215 } | |
| 1216 | |
| 1217 int _tokenizeCaret(int next) => | |
| 1218 _select(0x3D, TokenType.CARET_EQ, TokenType.CARET); | |
| 1219 | |
| 1220 int _tokenizeDotOrNumber(int next) { | |
| 1221 int start = _reader.offset; | |
| 1222 next = _reader.advance(); | |
| 1223 if (0x30 <= next && next <= 0x39) { | |
| 1224 return _tokenizeFractionPart(next, start); | |
| 1225 } else if (0x2E == next) { | |
| 1226 return _select( | |
| 1227 0x2E, TokenType.PERIOD_PERIOD_PERIOD, TokenType.PERIOD_PERIOD); | |
| 1228 } else { | |
| 1229 _appendTokenOfType(TokenType.PERIOD); | |
| 1230 return next; | |
| 1231 } | |
| 1232 } | |
| 1233 | |
| 1234 int _tokenizeEquals(int next) { | |
| 1235 // = == => | |
| 1236 next = _reader.advance(); | |
| 1237 if (next == 0x3D) { | |
| 1238 _appendTokenOfType(TokenType.EQ_EQ); | |
| 1239 return _reader.advance(); | |
| 1240 } else if (next == 0x3E) { | |
| 1241 _appendTokenOfType(TokenType.FUNCTION); | |
| 1242 return _reader.advance(); | |
| 1243 } | |
| 1244 _appendTokenOfType(TokenType.EQ); | |
| 1245 return next; | |
| 1246 } | |
| 1247 | |
| 1248 int _tokenizeExclamation(int next) { | |
| 1249 // ! != | |
| 1250 next = _reader.advance(); | |
| 1251 if (next == 0x3D) { | |
| 1252 _appendTokenOfType(TokenType.BANG_EQ); | |
| 1253 return _reader.advance(); | |
| 1254 } | |
| 1255 _appendTokenOfType(TokenType.BANG); | |
| 1256 return next; | |
| 1257 } | |
| 1258 | |
| 1259 int _tokenizeExponent(int next) { | |
| 1260 if (next == 0x2B || next == 0x2D) { | |
| 1261 next = _reader.advance(); | |
| 1262 } | |
| 1263 bool hasDigits = false; | |
| 1264 while (true) { | |
| 1265 if (0x30 <= next && next <= 0x39) { | |
| 1266 hasDigits = true; | |
| 1267 } else { | |
| 1268 if (!hasDigits) { | |
| 1269 _reportError(ScannerErrorCode.MISSING_DIGIT); | |
| 1270 } | |
| 1271 return next; | |
| 1272 } | |
| 1273 next = _reader.advance(); | |
| 1274 } | |
| 1275 } | |
| 1276 | |
| 1277 int _tokenizeFractionPart(int next, int start) { | |
| 1278 bool done = false; | |
| 1279 bool hasDigit = false; | |
| 1280 LOOP: while (!done) { | |
| 1281 if (0x30 <= next && next <= 0x39) { | |
| 1282 hasDigit = true; | |
| 1283 } else if (0x65 == next || 0x45 == next) { | |
| 1284 hasDigit = true; | |
| 1285 next = _tokenizeExponent(_reader.advance()); | |
| 1286 done = true; | |
| 1287 continue LOOP; | |
| 1288 } else { | |
| 1289 done = true; | |
| 1290 continue LOOP; | |
| 1291 } | |
| 1292 next = _reader.advance(); | |
| 1293 } | |
| 1294 if (!hasDigit) { | |
| 1295 _appendStringToken(TokenType.INT, _reader.getString(start, -2)); | |
| 1296 if (0x2E == next) { | |
| 1297 return _selectWithOffset(0x2E, TokenType.PERIOD_PERIOD_PERIOD, | |
| 1298 TokenType.PERIOD_PERIOD, _reader.offset - 1); | |
| 1299 } | |
| 1300 _appendTokenOfTypeWithOffset(TokenType.PERIOD, _reader.offset - 1); | |
| 1301 return bigSwitch(next); | |
| 1302 } | |
| 1303 _appendStringToken( | |
| 1304 TokenType.DOUBLE, _reader.getString(start, next < 0 ? 0 : -1)); | |
| 1305 return next; | |
| 1306 } | |
| 1307 | |
| 1308 int _tokenizeGreaterThan(int next) { | |
| 1309 // > >= >> >>= | |
| 1310 next = _reader.advance(); | |
| 1311 if (0x3D == next) { | |
| 1312 _appendTokenOfType(TokenType.GT_EQ); | |
| 1313 return _reader.advance(); | |
| 1314 } else if (0x3E == next) { | |
| 1315 next = _reader.advance(); | |
| 1316 if (0x3D == next) { | |
| 1317 _appendTokenOfType(TokenType.GT_GT_EQ); | |
| 1318 return _reader.advance(); | |
| 1319 } else { | |
| 1320 _appendTokenOfType(TokenType.GT_GT); | |
| 1321 return next; | |
| 1322 } | |
| 1323 } else { | |
| 1324 _appendTokenOfType(TokenType.GT); | |
| 1325 return next; | |
| 1326 } | |
| 1327 } | |
| 1328 | |
| 1329 int _tokenizeHex(int next) { | |
| 1330 int start = _reader.offset - 1; | |
| 1331 bool hasDigits = false; | |
| 1332 while (true) { | |
| 1333 next = _reader.advance(); | |
| 1334 if ((0x30 <= next && next <= 0x39) || | |
| 1335 (0x41 <= next && next <= 0x46) || | |
| 1336 (0x61 <= next && next <= 0x66)) { | |
| 1337 hasDigits = true; | |
| 1338 } else { | |
| 1339 if (!hasDigits) { | |
| 1340 _reportError(ScannerErrorCode.MISSING_HEX_DIGIT); | |
| 1341 } | |
| 1342 _appendStringToken( | |
| 1343 TokenType.HEXADECIMAL, _reader.getString(start, next < 0 ? 0 : -1)); | |
| 1344 return next; | |
| 1345 } | |
| 1346 } | |
| 1347 } | |
| 1348 | |
| 1349 int _tokenizeHexOrNumber(int next) { | |
| 1350 int x = _reader.peek(); | |
| 1351 if (x == 0x78 || x == 0x58) { | |
| 1352 _reader.advance(); | |
| 1353 return _tokenizeHex(x); | |
| 1354 } | |
| 1355 return _tokenizeNumber(next); | |
| 1356 } | |
| 1357 | |
| 1358 int _tokenizeIdentifier(int next, int start, bool allowDollar) { | |
| 1359 while ((0x61 <= next && next <= 0x7A) || | |
| 1360 (0x41 <= next && next <= 0x5A) || | |
| 1361 (0x30 <= next && next <= 0x39) || | |
| 1362 next == 0x5F || | |
| 1363 (next == 0x24 && allowDollar)) { | |
| 1364 next = _reader.advance(); | |
| 1365 } | |
| 1366 _appendStringToken( | |
| 1367 TokenType.IDENTIFIER, _reader.getString(start, next < 0 ? 0 : -1)); | |
| 1368 return next; | |
| 1369 } | |
| 1370 | |
| 1371 int _tokenizeInterpolatedExpression(int next, int start) { | |
| 1372 _appendBeginToken(TokenType.STRING_INTERPOLATION_EXPRESSION); | |
| 1373 next = _reader.advance(); | |
| 1374 while (next != -1) { | |
| 1375 if (next == 0x7D) { | |
| 1376 BeginToken begin = | |
| 1377 _findTokenMatchingClosingBraceInInterpolationExpression(); | |
| 1378 if (begin == null) { | |
| 1379 _beginToken(); | |
| 1380 _appendTokenOfType(TokenType.CLOSE_CURLY_BRACKET); | |
| 1381 next = _reader.advance(); | |
| 1382 _beginToken(); | |
| 1383 return next; | |
| 1384 } else if (begin.type == TokenType.OPEN_CURLY_BRACKET) { | |
| 1385 _beginToken(); | |
| 1386 _appendEndToken( | |
| 1387 TokenType.CLOSE_CURLY_BRACKET, TokenType.OPEN_CURLY_BRACKET); | |
| 1388 next = _reader.advance(); | |
| 1389 _beginToken(); | |
| 1390 } else if (begin.type == TokenType.STRING_INTERPOLATION_EXPRESSION) { | |
| 1391 _beginToken(); | |
| 1392 _appendEndToken(TokenType.CLOSE_CURLY_BRACKET, | |
| 1393 TokenType.STRING_INTERPOLATION_EXPRESSION); | |
| 1394 next = _reader.advance(); | |
| 1395 _beginToken(); | |
| 1396 return next; | |
| 1397 } | |
| 1398 } else { | |
| 1399 next = bigSwitch(next); | |
| 1400 } | |
| 1401 } | |
| 1402 return next; | |
| 1403 } | |
| 1404 | |
| 1405 int _tokenizeInterpolatedIdentifier(int next, int start) { | |
| 1406 _appendStringTokenWithOffset( | |
| 1407 TokenType.STRING_INTERPOLATION_IDENTIFIER, "\$", 0); | |
| 1408 if ((0x41 <= next && next <= 0x5A) || | |
| 1409 (0x61 <= next && next <= 0x7A) || | |
| 1410 next == 0x5F) { | |
| 1411 _beginToken(); | |
| 1412 next = _tokenizeKeywordOrIdentifier(next, false); | |
| 1413 } | |
| 1414 _beginToken(); | |
| 1415 return next; | |
| 1416 } | |
| 1417 | |
| 1418 int _tokenizeKeywordOrIdentifier(int next, bool allowDollar) { | |
| 1419 KeywordState state = KeywordState.KEYWORD_STATE; | |
| 1420 int start = _reader.offset; | |
| 1421 while (state != null && 0x61 <= next && next <= 0x7A) { | |
| 1422 state = state.next(next); | |
| 1423 next = _reader.advance(); | |
| 1424 } | |
| 1425 if (state == null || state.keyword() == null) { | |
| 1426 return _tokenizeIdentifier(next, start, allowDollar); | |
| 1427 } | |
| 1428 if ((0x41 <= next && next <= 0x5A) || | |
| 1429 (0x30 <= next && next <= 0x39) || | |
| 1430 next == 0x5F || | |
| 1431 next == 0x24) { | |
| 1432 return _tokenizeIdentifier(next, start, allowDollar); | |
| 1433 } else if (next < 128) { | |
| 1434 _appendKeywordToken(state.keyword()); | |
| 1435 return next; | |
| 1436 } else { | |
| 1437 return _tokenizeIdentifier(next, start, allowDollar); | |
| 1438 } | |
| 1439 } | |
| 1440 | |
| 1441 int _tokenizeLessThan(int next) { | |
| 1442 // < <= << <<= | |
| 1443 next = _reader.advance(); | |
| 1444 if (0x3D == next) { | |
| 1445 _appendTokenOfType(TokenType.LT_EQ); | |
| 1446 return _reader.advance(); | |
| 1447 } else if (0x3C == next) { | |
| 1448 return _select(0x3D, TokenType.LT_LT_EQ, TokenType.LT_LT); | |
| 1449 } else { | |
| 1450 _appendTokenOfType(TokenType.LT); | |
| 1451 return next; | |
| 1452 } | |
| 1453 } | |
| 1454 | |
| 1455 int _tokenizeMinus(int next) { | |
| 1456 // - -- -= | |
| 1457 next = _reader.advance(); | |
| 1458 if (next == 0x2D) { | |
| 1459 _appendTokenOfType(TokenType.MINUS_MINUS); | |
| 1460 return _reader.advance(); | |
| 1461 } else if (next == 0x3D) { | |
| 1462 _appendTokenOfType(TokenType.MINUS_EQ); | |
| 1463 return _reader.advance(); | |
| 1464 } else { | |
| 1465 _appendTokenOfType(TokenType.MINUS); | |
| 1466 return next; | |
| 1467 } | |
| 1468 } | |
| 1469 | |
| 1470 int _tokenizeMultiLineComment(int next) { | |
| 1471 int nesting = 1; | |
| 1472 next = _reader.advance(); | |
| 1473 while (true) { | |
| 1474 if (-1 == next) { | |
| 1475 _reportError(ScannerErrorCode.UNTERMINATED_MULTI_LINE_COMMENT); | |
| 1476 _appendCommentToken( | |
| 1477 TokenType.MULTI_LINE_COMMENT, _reader.getString(_tokenStart, 0)); | |
| 1478 return next; | |
| 1479 } else if (0x2A == next) { | |
| 1480 next = _reader.advance(); | |
| 1481 if (0x2F == next) { | |
| 1482 --nesting; | |
| 1483 if (0 == nesting) { | |
| 1484 _appendCommentToken(TokenType.MULTI_LINE_COMMENT, | |
| 1485 _reader.getString(_tokenStart, 0)); | |
| 1486 return _reader.advance(); | |
| 1487 } else { | |
| 1488 next = _reader.advance(); | |
| 1489 } | |
| 1490 } | |
| 1491 } else if (0x2F == next) { | |
| 1492 next = _reader.advance(); | |
| 1493 if (0x2A == next) { | |
| 1494 next = _reader.advance(); | |
| 1495 ++nesting; | |
| 1496 } | |
| 1497 } else if (next == 0xD) { | |
| 1498 next = _reader.advance(); | |
| 1499 if (next == 0xA) { | |
| 1500 next = _reader.advance(); | |
| 1501 } | |
| 1502 recordStartOfLine(); | |
| 1503 } else if (next == 0xA) { | |
| 1504 next = _reader.advance(); | |
| 1505 recordStartOfLine(); | |
| 1506 } else { | |
| 1507 next = _reader.advance(); | |
| 1508 } | |
| 1509 } | |
| 1510 } | |
| 1511 | |
| 1512 int _tokenizeMultiLineRawString(int quoteChar, int start) { | |
| 1513 int next = _reader.advance(); | |
| 1514 outer: while (next != -1) { | |
| 1515 while (next != quoteChar) { | |
| 1516 if (next == -1) { | |
| 1517 break outer; | |
| 1518 } else if (next == 0xD) { | |
| 1519 next = _reader.advance(); | |
| 1520 if (next == 0xA) { | |
| 1521 next = _reader.advance(); | |
| 1522 } | |
| 1523 recordStartOfLine(); | |
| 1524 } else if (next == 0xA) { | |
| 1525 next = _reader.advance(); | |
| 1526 recordStartOfLine(); | |
| 1527 } else { | |
| 1528 next = _reader.advance(); | |
| 1529 } | |
| 1530 } | |
| 1531 next = _reader.advance(); | |
| 1532 if (next == quoteChar) { | |
| 1533 next = _reader.advance(); | |
| 1534 if (next == quoteChar) { | |
| 1535 _appendStringToken(TokenType.STRING, _reader.getString(start, 0)); | |
| 1536 return _reader.advance(); | |
| 1537 } | |
| 1538 } | |
| 1539 } | |
| 1540 _reportError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL); | |
| 1541 _appendStringToken(TokenType.STRING, _reader.getString(start, 0)); | |
| 1542 return _reader.advance(); | |
| 1543 } | |
| 1544 | |
| 1545 int _tokenizeMultiLineString(int quoteChar, int start, bool raw) { | |
| 1546 if (raw) { | |
| 1547 return _tokenizeMultiLineRawString(quoteChar, start); | |
| 1548 } | |
| 1549 int next = _reader.advance(); | |
| 1550 while (next != -1) { | |
| 1551 if (next == 0x24) { | |
| 1552 _appendStringToken(TokenType.STRING, _reader.getString(start, -1)); | |
| 1553 next = _tokenizeStringInterpolation(start); | |
| 1554 _beginToken(); | |
| 1555 start = _reader.offset; | |
| 1556 continue; | |
| 1557 } | |
| 1558 if (next == quoteChar) { | |
| 1559 next = _reader.advance(); | |
| 1560 if (next == quoteChar) { | |
| 1561 next = _reader.advance(); | |
| 1562 if (next == quoteChar) { | |
| 1563 _appendStringToken(TokenType.STRING, _reader.getString(start, 0)); | |
| 1564 return _reader.advance(); | |
| 1565 } | |
| 1566 } | |
| 1567 continue; | |
| 1568 } | |
| 1569 if (next == 0x5C) { | |
| 1570 next = _reader.advance(); | |
| 1571 if (next == -1) { | |
| 1572 break; | |
| 1573 } | |
| 1574 if (next == 0xD) { | |
| 1575 next = _reader.advance(); | |
| 1576 if (next == 0xA) { | |
| 1577 next = _reader.advance(); | |
| 1578 } | |
| 1579 recordStartOfLine(); | |
| 1580 } else if (next == 0xA) { | |
| 1581 recordStartOfLine(); | |
| 1582 next = _reader.advance(); | |
| 1583 } else { | |
| 1584 next = _reader.advance(); | |
| 1585 } | |
| 1586 } else if (next == 0xD) { | |
| 1587 next = _reader.advance(); | |
| 1588 if (next == 0xA) { | |
| 1589 next = _reader.advance(); | |
| 1590 } | |
| 1591 recordStartOfLine(); | |
| 1592 } else if (next == 0xA) { | |
| 1593 recordStartOfLine(); | |
| 1594 next = _reader.advance(); | |
| 1595 } else { | |
| 1596 next = _reader.advance(); | |
| 1597 } | |
| 1598 } | |
| 1599 _reportError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL); | |
| 1600 if (start == _reader.offset) { | |
| 1601 _appendStringTokenWithOffset(TokenType.STRING, "", 1); | |
| 1602 } else { | |
| 1603 _appendStringToken(TokenType.STRING, _reader.getString(start, 0)); | |
| 1604 } | |
| 1605 return _reader.advance(); | |
| 1606 } | |
| 1607 | |
| 1608 int _tokenizeMultiply(int next) => | |
| 1609 _select(0x3D, TokenType.STAR_EQ, TokenType.STAR); | |
| 1610 | |
| 1611 int _tokenizeNumber(int next) { | |
| 1612 int start = _reader.offset; | |
| 1613 while (true) { | |
| 1614 next = _reader.advance(); | |
| 1615 if (0x30 <= next && next <= 0x39) { | |
| 1616 continue; | |
| 1617 } else if (next == 0x2E) { | |
| 1618 return _tokenizeFractionPart(_reader.advance(), start); | |
| 1619 } else if (next == 0x65 || next == 0x45) { | |
| 1620 return _tokenizeFractionPart(next, start); | |
| 1621 } else { | |
| 1622 _appendStringToken( | |
| 1623 TokenType.INT, _reader.getString(start, next < 0 ? 0 : -1)); | |
| 1624 return next; | |
| 1625 } | |
| 1626 } | |
| 1627 } | |
| 1628 | |
| 1629 int _tokenizeOpenSquareBracket(int next) { | |
| 1630 // [ [] []= | |
| 1631 next = _reader.advance(); | |
| 1632 if (next == 0x5D) { | |
| 1633 return _select(0x3D, TokenType.INDEX_EQ, TokenType.INDEX); | |
| 1634 } else { | |
| 1635 _appendBeginToken(TokenType.OPEN_SQUARE_BRACKET); | |
| 1636 return next; | |
| 1637 } | |
| 1638 } | |
| 1639 | |
| 1640 int _tokenizePercent(int next) => | |
| 1641 _select(0x3D, TokenType.PERCENT_EQ, TokenType.PERCENT); | |
| 1642 | |
| 1643 int _tokenizePlus(int next) { | |
| 1644 // + ++ += | |
| 1645 next = _reader.advance(); | |
| 1646 if (0x2B == next) { | |
| 1647 _appendTokenOfType(TokenType.PLUS_PLUS); | |
| 1648 return _reader.advance(); | |
| 1649 } else if (0x3D == next) { | |
| 1650 _appendTokenOfType(TokenType.PLUS_EQ); | |
| 1651 return _reader.advance(); | |
| 1652 } else { | |
| 1653 _appendTokenOfType(TokenType.PLUS); | |
| 1654 return next; | |
| 1655 } | |
| 1656 } | |
| 1657 | |
| 1658 int _tokenizeQuestion() { | |
| 1659 // ? ?. ?? ??= | |
| 1660 int next = _reader.advance(); | |
| 1661 if (next == 0x2E) { | |
| 1662 // '.' | |
| 1663 _appendTokenOfType(TokenType.QUESTION_PERIOD); | |
| 1664 return _reader.advance(); | |
| 1665 } else if (next == 0x3F) { | |
| 1666 // '?' | |
| 1667 next = _reader.advance(); | |
| 1668 if (next == 0x3D) { | |
| 1669 // '=' | |
| 1670 _appendTokenOfType(TokenType.QUESTION_QUESTION_EQ); | |
| 1671 return _reader.advance(); | |
| 1672 } else { | |
| 1673 _appendTokenOfType(TokenType.QUESTION_QUESTION); | |
| 1674 return next; | |
| 1675 } | |
| 1676 } else { | |
| 1677 _appendTokenOfType(TokenType.QUESTION); | |
| 1678 return next; | |
| 1679 } | |
| 1680 } | |
| 1681 | |
| 1682 int _tokenizeSingleLineComment(int next) { | |
| 1683 while (true) { | |
| 1684 next = _reader.advance(); | |
| 1685 if (-1 == next) { | |
| 1686 _appendCommentToken( | |
| 1687 TokenType.SINGLE_LINE_COMMENT, _reader.getString(_tokenStart, 0)); | |
| 1688 return next; | |
| 1689 } else if (0xA == next || 0xD == next) { | |
| 1690 _appendCommentToken( | |
| 1691 TokenType.SINGLE_LINE_COMMENT, _reader.getString(_tokenStart, -1)); | |
| 1692 return next; | |
| 1693 } | |
| 1694 } | |
| 1695 } | |
| 1696 | |
| 1697 int _tokenizeSingleLineRawString(int next, int quoteChar, int start) { | |
| 1698 next = _reader.advance(); | |
| 1699 while (next != -1) { | |
| 1700 if (next == quoteChar) { | |
| 1701 _appendStringToken(TokenType.STRING, _reader.getString(start, 0)); | |
| 1702 return _reader.advance(); | |
| 1703 } else if (next == 0xD || next == 0xA) { | |
| 1704 _reportError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL); | |
| 1705 _appendStringToken(TokenType.STRING, _reader.getString(start, -1)); | |
| 1706 return _reader.advance(); | |
| 1707 } | |
| 1708 next = _reader.advance(); | |
| 1709 } | |
| 1710 _reportError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL); | |
| 1711 _appendStringToken(TokenType.STRING, _reader.getString(start, 0)); | |
| 1712 return _reader.advance(); | |
| 1713 } | |
| 1714 | |
| 1715 int _tokenizeSingleLineString(int next, int quoteChar, int start) { | |
| 1716 while (next != quoteChar) { | |
| 1717 if (next == 0x5C) { | |
| 1718 next = _reader.advance(); | |
| 1719 } else if (next == 0x24) { | |
| 1720 _appendStringToken(TokenType.STRING, _reader.getString(start, -1)); | |
| 1721 next = _tokenizeStringInterpolation(start); | |
| 1722 _beginToken(); | |
| 1723 start = _reader.offset; | |
| 1724 continue; | |
| 1725 } | |
| 1726 if (next <= 0xD && (next == 0xA || next == 0xD || next == -1)) { | |
| 1727 _reportError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL); | |
| 1728 if (start == _reader.offset) { | |
| 1729 _appendStringTokenWithOffset(TokenType.STRING, "", 1); | |
| 1730 } else if (next == -1) { | |
| 1731 _appendStringToken(TokenType.STRING, _reader.getString(start, 0)); | |
| 1732 } else { | |
| 1733 _appendStringToken(TokenType.STRING, _reader.getString(start, -1)); | |
| 1734 } | |
| 1735 return _reader.advance(); | |
| 1736 } | |
| 1737 next = _reader.advance(); | |
| 1738 } | |
| 1739 _appendStringToken(TokenType.STRING, _reader.getString(start, 0)); | |
| 1740 return _reader.advance(); | |
| 1741 } | |
| 1742 | |
| 1743 int _tokenizeSlashOrComment(int next) { | |
| 1744 next = _reader.advance(); | |
| 1745 if (0x2A == next) { | |
| 1746 return _tokenizeMultiLineComment(next); | |
| 1747 } else if (0x2F == next) { | |
| 1748 return _tokenizeSingleLineComment(next); | |
| 1749 } else if (0x3D == next) { | |
| 1750 _appendTokenOfType(TokenType.SLASH_EQ); | |
| 1751 return _reader.advance(); | |
| 1752 } else { | |
| 1753 _appendTokenOfType(TokenType.SLASH); | |
| 1754 return next; | |
| 1755 } | |
| 1756 } | |
| 1757 | |
| 1758 int _tokenizeString(int next, int start, bool raw) { | |
| 1759 int quoteChar = next; | |
| 1760 next = _reader.advance(); | |
| 1761 if (quoteChar == next) { | |
| 1762 next = _reader.advance(); | |
| 1763 if (quoteChar == next) { | |
| 1764 // Multiline string. | |
| 1765 return _tokenizeMultiLineString(quoteChar, start, raw); | |
| 1766 } else { | |
| 1767 // Empty string. | |
| 1768 _appendStringToken(TokenType.STRING, _reader.getString(start, -1)); | |
| 1769 return next; | |
| 1770 } | |
| 1771 } | |
| 1772 if (raw) { | |
| 1773 return _tokenizeSingleLineRawString(next, quoteChar, start); | |
| 1774 } else { | |
| 1775 return _tokenizeSingleLineString(next, quoteChar, start); | |
| 1776 } | |
| 1777 } | |
| 1778 | |
| 1779 int _tokenizeStringInterpolation(int start) { | |
| 1780 _beginToken(); | |
| 1781 int next = _reader.advance(); | |
| 1782 if (next == 0x7B) { | |
| 1783 return _tokenizeInterpolatedExpression(next, start); | |
| 1784 } else { | |
| 1785 return _tokenizeInterpolatedIdentifier(next, start); | |
| 1786 } | |
| 1787 } | |
| 1788 | |
| 1789 int _tokenizeTag(int next) { | |
| 1790 // # or #!.*[\n\r] | |
| 1791 if (_reader.offset == 0) { | |
| 1792 if (_reader.peek() == 0x21) { | |
| 1793 do { | |
| 1794 next = _reader.advance(); | |
| 1795 } while (next != 0xA && next != 0xD && next > 0); | |
| 1796 _appendStringToken( | |
| 1797 TokenType.SCRIPT_TAG, _reader.getString(_tokenStart, 0)); | |
| 1798 return next; | |
| 1799 } | |
| 1800 } | |
| 1801 _appendTokenOfType(TokenType.HASH); | |
| 1802 return _reader.advance(); | |
| 1803 } | |
| 1804 | |
| 1805 int _tokenizeTilde(int next) { | |
| 1806 // ~ ~/ ~/= | |
| 1807 next = _reader.advance(); | |
| 1808 if (next == 0x2F) { | |
| 1809 return _select(0x3D, TokenType.TILDE_SLASH_EQ, TokenType.TILDE_SLASH); | |
| 1810 } else { | |
| 1811 _appendTokenOfType(TokenType.TILDE); | |
| 1812 return next; | |
| 1813 } | |
| 1814 } | |
| 1815 | |
| 1816 /** | |
| 1817 * Checks if [value] is a single-line or multi-line comment. | |
| 1818 */ | |
| 1819 static bool _isDocumentationComment(String value) { | |
| 1820 return StringUtilities.startsWith3(value, 0, 0x2F, 0x2F, 0x2F) || | |
| 1821 StringUtilities.startsWith3(value, 0, 0x2F, 0x2A, 0x2A); | |
| 1822 } | |
| 1823 } | |
| 1824 | |
| 1825 /** | |
| 1826 * The error codes used for errors detected by the scanner. | |
| 1827 */ | |
| 1828 class ScannerErrorCode extends ErrorCode { | |
| 1829 static const ScannerErrorCode ILLEGAL_CHARACTER = | |
| 1830 const ScannerErrorCode('ILLEGAL_CHARACTER', "Illegal character {0}"); | |
| 1831 | |
| 1832 static const ScannerErrorCode MISSING_DIGIT = | |
| 1833 const ScannerErrorCode('MISSING_DIGIT', "Decimal digit expected"); | |
| 1834 | |
| 1835 static const ScannerErrorCode MISSING_HEX_DIGIT = | |
| 1836 const ScannerErrorCode('MISSING_HEX_DIGIT', "Hexidecimal digit expected"); | |
| 1837 | |
| 1838 static const ScannerErrorCode MISSING_QUOTE = | |
| 1839 const ScannerErrorCode('MISSING_QUOTE', "Expected quote (' or \")"); | |
| 1840 | |
| 1841 static const ScannerErrorCode UNABLE_GET_CONTENT = const ScannerErrorCode( | |
| 1842 'UNABLE_GET_CONTENT', "Unable to get content: {0}"); | |
| 1843 | |
| 1844 static const ScannerErrorCode UNTERMINATED_MULTI_LINE_COMMENT = | |
| 1845 const ScannerErrorCode( | |
| 1846 'UNTERMINATED_MULTI_LINE_COMMENT', "Unterminated multi-line comment"); | |
| 1847 | |
| 1848 static const ScannerErrorCode UNTERMINATED_STRING_LITERAL = | |
| 1849 const ScannerErrorCode( | |
| 1850 'UNTERMINATED_STRING_LITERAL', "Unterminated string literal"); | |
| 1851 | |
| 1852 /** | |
| 1853 * Initialize a newly created error code to have the given [name]. The message | |
| 1854 * associated with the error will be created from the given [message] | |
| 1855 * template. The correction associated with the error will be created from the | |
| 1856 * given [correction] template. | |
| 1857 */ | |
| 1858 const ScannerErrorCode(String name, String message, [String correction]) | |
| 1859 : super(name, message, correction); | |
| 1860 | |
| 1861 @override | |
| 1862 ErrorSeverity get errorSeverity => ErrorSeverity.ERROR; | |
| 1863 | |
| 1864 @override | |
| 1865 ErrorType get type => ErrorType.SYNTACTIC_ERROR; | |
| 1866 } | |
| 1867 | |
| 1868 /** | |
| 1869 * A token whose value is independent of it's type. | |
| 1870 */ | |
| 1871 class StringToken extends Token { | |
| 1872 /** | |
| 1873 * The lexeme represented by this token. | |
| 1874 */ | |
| 1875 String _value; | |
| 1876 | |
| 1877 /** | |
| 1878 * Initialize a newly created token to represent a token of the given [type] | |
| 1879 * with the given [value] at the given [offset]. | |
| 1880 */ | |
| 1881 StringToken(TokenType type, String value, int offset) : super(type, offset) { | |
| 1882 this._value = StringUtilities.intern(value); | |
| 1883 } | |
| 1884 | |
| 1885 @override | |
| 1886 String get lexeme => _value; | |
| 1887 | |
| 1888 @override | |
| 1889 Token copy() => new StringToken(type, _value, offset); | |
| 1890 | |
| 1891 @override | |
| 1892 String value() => _value; | |
| 1893 } | |
| 1894 | |
| 1895 /** | |
| 1896 * A string token that is preceded by comments. | |
| 1897 */ | |
| 1898 class StringTokenWithComment extends StringToken { | |
| 1899 /** | |
| 1900 * The first comment in the list of comments that precede this token. | |
| 1901 */ | |
| 1902 CommentToken _precedingComment; | |
| 1903 | |
| 1904 /** | |
| 1905 * Initialize a newly created token to have the given [type] at the given | |
| 1906 * [offset] and to be preceded by the comments reachable from the given | |
| 1907 * [comment]. | |
| 1908 */ | |
| 1909 StringTokenWithComment( | |
| 1910 TokenType type, String value, int offset, this._precedingComment) | |
| 1911 : super(type, value, offset) { | |
| 1912 _setCommentParent(_precedingComment); | |
| 1913 } | |
| 1914 | |
| 1915 CommentToken get precedingComments => _precedingComment; | |
| 1916 | |
| 1917 void set precedingComments(CommentToken comment) { | |
| 1918 _precedingComment = comment; | |
| 1919 _setCommentParent(_precedingComment); | |
| 1920 } | |
| 1921 | |
| 1922 @override | |
| 1923 void applyDelta(int delta) { | |
| 1924 super.applyDelta(delta); | |
| 1925 Token token = precedingComments; | |
| 1926 while (token != null) { | |
| 1927 token.applyDelta(delta); | |
| 1928 token = token.next; | |
| 1929 } | |
| 1930 } | |
| 1931 | |
| 1932 @override | |
| 1933 Token copy() => new StringTokenWithComment( | |
| 1934 type, lexeme, offset, copyComments(precedingComments)); | |
| 1935 } | |
| 1936 | |
| 1937 /** | |
| 1938 * A [CharacterReader] that reads characters from a character sequence, but adds | |
| 1939 * a delta when reporting the current character offset so that the character | |
| 1940 * sequence can be a subsequence from a larger sequence. | |
| 1941 */ | |
| 1942 class SubSequenceReader extends CharSequenceReader { | |
| 1943 /** | |
| 1944 * The offset from the beginning of the file to the beginning of the source | |
| 1945 * being scanned. | |
| 1946 */ | |
| 1947 final int _offsetDelta; | |
| 1948 | |
| 1949 /** | |
| 1950 * Initialize a newly created reader to read the characters in the given | |
| 1951 * [sequence]. The [_offsetDelta] is the offset from the beginning of the file | |
| 1952 * to the beginning of the source being scanned | |
| 1953 */ | |
| 1954 SubSequenceReader(String sequence, this._offsetDelta) : super(sequence); | |
| 1955 | |
| 1956 @override | |
| 1957 int get offset => _offsetDelta + super.offset; | |
| 1958 | |
| 1959 @override | |
| 1960 void set offset(int offset) { | |
| 1961 super.offset = offset - _offsetDelta; | |
| 1962 } | |
| 1963 | |
| 1964 @override | |
| 1965 String getString(int start, int endDelta) => | |
| 1966 super.getString(start - _offsetDelta, endDelta); | |
| 1967 } | |
| 1968 | |
| 1969 /** | |
| 1970 * A token whose value is independent of it's type. | |
| 1971 */ | |
| 1972 class SyntheticStringToken extends StringToken { | |
| 1973 /** | |
| 1974 * Initialize a newly created token to represent a token of the given [type] | |
| 1975 * with the given [value] at the given [offset]. | |
| 1976 */ | |
| 1977 SyntheticStringToken(TokenType type, String value, int offset) | |
| 1978 : super(type, value, offset); | |
| 1979 | |
| 1980 @override | |
| 1981 bool get isSynthetic => true; | |
| 1982 } | |
| 1983 | |
| 1984 /** | |
| 1985 * A token that was scanned from the input. Each token knows which tokens | |
| 1986 * precede and follow it, acting as a link in a doubly linked list of tokens. | |
| 1987 */ | |
| 1988 class Token { | |
| 1989 /** | |
| 1990 * The type of the token. | |
| 1991 */ | |
| 1992 final TokenType type; | |
| 1993 | |
| 1994 /** | |
| 1995 * The offset from the beginning of the file to the first character in the | |
| 1996 * token. | |
| 1997 */ | |
| 1998 int offset = 0; | |
| 1999 | |
| 2000 /** | |
| 2001 * The previous token in the token stream. | |
| 2002 */ | |
| 2003 Token previous; | |
| 2004 | |
| 2005 /** | |
| 2006 * The next token in the token stream. | |
| 2007 */ | |
| 2008 Token _next; | |
| 2009 | |
| 2010 /** | |
| 2011 * Initialize a newly created token to have the given [type] and [offset]. | |
| 2012 */ | |
| 2013 Token(this.type, this.offset); | |
| 2014 | |
| 2015 /** | |
| 2016 * Return the offset from the beginning of the file to the character after the | |
| 2017 * last character of the token. | |
| 2018 */ | |
| 2019 int get end => offset + length; | |
| 2020 | |
| 2021 /** | |
| 2022 * Return `true` if this token represents an operator. | |
| 2023 */ | |
| 2024 bool get isOperator => type.isOperator; | |
| 2025 | |
| 2026 /** | |
| 2027 * Return `true` if this token is a synthetic token. A synthetic token is a | |
| 2028 * token that was introduced by the parser in order to recover from an error | |
| 2029 * in the code. | |
| 2030 */ | |
| 2031 bool get isSynthetic => length == 0; | |
| 2032 | |
| 2033 /** | |
| 2034 * Return `true` if this token represents an operator that can be defined by | |
| 2035 * users. | |
| 2036 */ | |
| 2037 bool get isUserDefinableOperator => type.isUserDefinableOperator; | |
| 2038 | |
| 2039 /** | |
| 2040 * Return the number of characters in the node's source range. | |
| 2041 */ | |
| 2042 int get length => lexeme.length; | |
| 2043 | |
| 2044 /** | |
| 2045 * Return the lexeme that represents this token. | |
| 2046 */ | |
| 2047 String get lexeme => type.lexeme; | |
| 2048 | |
| 2049 /** | |
| 2050 * Return the next token in the token stream. | |
| 2051 */ | |
| 2052 Token get next => _next; | |
| 2053 | |
| 2054 /** | |
| 2055 * Return the first comment in the list of comments that precede this token, | |
| 2056 * or `null` if there are no comments preceding this token. Additional | |
| 2057 * comments can be reached by following the token stream using [next] until | |
| 2058 * `null` is returned. | |
| 2059 * | |
| 2060 * For example, if the original contents were "/* one */ /* two */ id", then | |
| 2061 * the first preceding comment token will have a lexeme of "/* one */" and | |
| 2062 * the next comment token will have a lexeme of "/* two */". | |
| 2063 */ | |
| 2064 CommentToken get precedingComments => null; | |
| 2065 | |
| 2066 /** | |
| 2067 * Apply (add) the given [delta] to this token's offset. | |
| 2068 */ | |
| 2069 void applyDelta(int delta) { | |
| 2070 offset += delta; | |
| 2071 } | |
| 2072 | |
| 2073 /** | |
| 2074 * Return a newly created token that is a copy of this token but that is not a | |
| 2075 * part of any token stream. | |
| 2076 */ | |
| 2077 Token copy() => new Token(type, offset); | |
| 2078 | |
| 2079 /** | |
| 2080 * Copy a linked list of comment tokens identical to the given comment tokens. | |
| 2081 */ | |
| 2082 Token copyComments(Token token) { | |
| 2083 if (token == null) { | |
| 2084 return null; | |
| 2085 } | |
| 2086 Token head = token.copy(); | |
| 2087 Token tail = head; | |
| 2088 token = token.next; | |
| 2089 while (token != null) { | |
| 2090 tail = tail.setNext(token.copy()); | |
| 2091 token = token.next; | |
| 2092 } | |
| 2093 return head; | |
| 2094 } | |
| 2095 | |
| 2096 /** | |
| 2097 * Return `true` if this token has any one of the given [types]. | |
| 2098 */ | |
| 2099 bool matchesAny(List<TokenType> types) { | |
| 2100 for (TokenType type in types) { | |
| 2101 if (this.type == type) { | |
| 2102 return true; | |
| 2103 } | |
| 2104 } | |
| 2105 return false; | |
| 2106 } | |
| 2107 | |
| 2108 /** | |
| 2109 * Set the next token in the token stream to the given [token]. This has the | |
| 2110 * side-effect of setting this token to be the previous token for the given | |
| 2111 * token. Return the token that was passed in. | |
| 2112 */ | |
| 2113 Token setNext(Token token) { | |
| 2114 _next = token; | |
| 2115 token.previous = this; | |
| 2116 return token; | |
| 2117 } | |
| 2118 | |
| 2119 /** | |
| 2120 * Set the next token in the token stream to the given token without changing | |
| 2121 * which token is the previous token for the given token. Return the token | |
| 2122 * that was passed in. | |
| 2123 */ | |
| 2124 Token setNextWithoutSettingPrevious(Token token) { | |
| 2125 _next = token; | |
| 2126 return token; | |
| 2127 } | |
| 2128 | |
| 2129 @override | |
| 2130 String toString() => lexeme; | |
| 2131 | |
| 2132 /** | |
| 2133 * Return the value of this token. For keyword tokens, this is the keyword | |
| 2134 * associated with the token, for other tokens it is the lexeme associated | |
| 2135 * with the token. | |
| 2136 */ | |
| 2137 Object value() => type.lexeme; | |
| 2138 | |
| 2139 /** | |
| 2140 * Sets the `parent` property to `this` for the given [comment] and all the | |
| 2141 * next tokens. | |
| 2142 */ | |
| 2143 void _setCommentParent(CommentToken comment) { | |
| 2144 while (comment != null) { | |
| 2145 comment.parent = this; | |
| 2146 comment = comment.next; | |
| 2147 } | |
| 2148 } | |
| 2149 | |
| 2150 /** | |
| 2151 * Compare the given [tokens] to find the token that appears first in the | |
| 2152 * source being parsed. That is, return the left-most of all of the tokens. | |
| 2153 * The list must be non-`null`, but the elements of the list are allowed to be | |
| 2154 * `null`. Return the token with the smallest offset, or `null` if the list is | |
| 2155 * empty or if all of the elements of the list are `null`. | |
| 2156 */ | |
| 2157 static Token lexicallyFirst(List<Token> tokens) { | |
| 2158 Token first = null; | |
| 2159 int offset = -1; | |
| 2160 for (Token token in tokens) { | |
| 2161 if (token != null && (offset < 0 || token.offset < offset)) { | |
| 2162 first = token; | |
| 2163 offset = token.offset; | |
| 2164 } | |
| 2165 } | |
| 2166 return first; | |
| 2167 } | |
| 2168 } | |
| 2169 | |
| 2170 /** | |
| 2171 * The classes (or groups) of tokens with a similar use. | |
| 2172 */ | |
| 2173 class TokenClass { | |
| 2174 /** | |
| 2175 * A value used to indicate that the token type is not part of any specific | |
| 2176 * class of token. | |
| 2177 */ | |
| 2178 static const TokenClass NO_CLASS = const TokenClass('NO_CLASS'); | |
| 2179 | |
| 2180 /** | |
| 2181 * A value used to indicate that the token type is an additive operator. | |
| 2182 */ | |
| 2183 static const TokenClass ADDITIVE_OPERATOR = | |
| 2184 const TokenClass('ADDITIVE_OPERATOR', 13); | |
| 2185 | |
| 2186 /** | |
| 2187 * A value used to indicate that the token type is an assignment operator. | |
| 2188 */ | |
| 2189 static const TokenClass ASSIGNMENT_OPERATOR = | |
| 2190 const TokenClass('ASSIGNMENT_OPERATOR', 1); | |
| 2191 | |
| 2192 /** | |
| 2193 * A value used to indicate that the token type is a bitwise-and operator. | |
| 2194 */ | |
| 2195 static const TokenClass BITWISE_AND_OPERATOR = | |
| 2196 const TokenClass('BITWISE_AND_OPERATOR', 11); | |
| 2197 | |
| 2198 /** | |
| 2199 * A value used to indicate that the token type is a bitwise-or operator. | |
| 2200 */ | |
| 2201 static const TokenClass BITWISE_OR_OPERATOR = | |
| 2202 const TokenClass('BITWISE_OR_OPERATOR', 9); | |
| 2203 | |
| 2204 /** | |
| 2205 * A value used to indicate that the token type is a bitwise-xor operator. | |
| 2206 */ | |
| 2207 static const TokenClass BITWISE_XOR_OPERATOR = | |
| 2208 const TokenClass('BITWISE_XOR_OPERATOR', 10); | |
| 2209 | |
| 2210 /** | |
| 2211 * A value used to indicate that the token type is a cascade operator. | |
| 2212 */ | |
| 2213 static const TokenClass CASCADE_OPERATOR = | |
| 2214 const TokenClass('CASCADE_OPERATOR', 2); | |
| 2215 | |
| 2216 /** | |
| 2217 * A value used to indicate that the token type is a conditional operator. | |
| 2218 */ | |
| 2219 static const TokenClass CONDITIONAL_OPERATOR = | |
| 2220 const TokenClass('CONDITIONAL_OPERATOR', 3); | |
| 2221 | |
| 2222 /** | |
| 2223 * A value used to indicate that the token type is an equality operator. | |
| 2224 */ | |
| 2225 static const TokenClass EQUALITY_OPERATOR = | |
| 2226 const TokenClass('EQUALITY_OPERATOR', 7); | |
| 2227 | |
| 2228 /** | |
| 2229 * A value used to indicate that the token type is an if-null operator. | |
| 2230 */ | |
| 2231 static const TokenClass IF_NULL_OPERATOR = | |
| 2232 const TokenClass('IF_NULL_OPERATOR', 4); | |
| 2233 | |
| 2234 /** | |
| 2235 * A value used to indicate that the token type is a logical-and operator. | |
| 2236 */ | |
| 2237 static const TokenClass LOGICAL_AND_OPERATOR = | |
| 2238 const TokenClass('LOGICAL_AND_OPERATOR', 6); | |
| 2239 | |
| 2240 /** | |
| 2241 * A value used to indicate that the token type is a logical-or operator. | |
| 2242 */ | |
| 2243 static const TokenClass LOGICAL_OR_OPERATOR = | |
| 2244 const TokenClass('LOGICAL_OR_OPERATOR', 5); | |
| 2245 | |
| 2246 /** | |
| 2247 * A value used to indicate that the token type is a multiplicative operator. | |
| 2248 */ | |
| 2249 static const TokenClass MULTIPLICATIVE_OPERATOR = | |
| 2250 const TokenClass('MULTIPLICATIVE_OPERATOR', 14); | |
| 2251 | |
| 2252 /** | |
| 2253 * A value used to indicate that the token type is a relational operator. | |
| 2254 */ | |
| 2255 static const TokenClass RELATIONAL_OPERATOR = | |
| 2256 const TokenClass('RELATIONAL_OPERATOR', 8); | |
| 2257 | |
| 2258 /** | |
| 2259 * A value used to indicate that the token type is a shift operator. | |
| 2260 */ | |
| 2261 static const TokenClass SHIFT_OPERATOR = | |
| 2262 const TokenClass('SHIFT_OPERATOR', 12); | |
| 2263 | |
| 2264 /** | |
| 2265 * A value used to indicate that the token type is a unary operator. | |
| 2266 */ | |
| 2267 static const TokenClass UNARY_POSTFIX_OPERATOR = | |
| 2268 const TokenClass('UNARY_POSTFIX_OPERATOR', 16); | |
| 2269 | |
| 2270 /** | |
| 2271 * A value used to indicate that the token type is a unary operator. | |
| 2272 */ | |
| 2273 static const TokenClass UNARY_PREFIX_OPERATOR = | |
| 2274 const TokenClass('UNARY_PREFIX_OPERATOR', 15); | |
| 2275 | |
| 2276 /** | |
| 2277 * The name of the token class. | |
| 2278 */ | |
| 2279 final String name; | |
| 2280 | |
| 2281 /** | |
| 2282 * The precedence of tokens of this class, or `0` if the such tokens do not | |
| 2283 * represent an operator. | |
| 2284 */ | |
| 2285 final int precedence; | |
| 2286 | |
| 2287 const TokenClass(this.name, [this.precedence = 0]); | |
| 2288 | |
| 2289 @override | |
| 2290 String toString() => name; | |
| 2291 } | |
| 2292 | |
| 2293 /** | |
| 2294 * The types of tokens that can be returned by the scanner. | |
| 2295 */ | |
| 2296 class TokenType { | |
| 2297 /** | |
| 2298 * The type of the token that marks the end of the input. | |
| 2299 */ | |
| 2300 static const TokenType EOF = const TokenType_EOF('EOF'); | |
| 2301 | |
| 2302 static const TokenType DOUBLE = const TokenType('DOUBLE'); | |
| 2303 | |
| 2304 static const TokenType HEXADECIMAL = const TokenType('HEXADECIMAL'); | |
| 2305 | |
| 2306 static const TokenType IDENTIFIER = const TokenType('IDENTIFIER'); | |
| 2307 | |
| 2308 static const TokenType INT = const TokenType('INT'); | |
| 2309 | |
| 2310 static const TokenType KEYWORD = const TokenType('KEYWORD'); | |
| 2311 | |
| 2312 static const TokenType MULTI_LINE_COMMENT = | |
| 2313 const TokenType('MULTI_LINE_COMMENT'); | |
| 2314 | |
| 2315 static const TokenType SCRIPT_TAG = const TokenType('SCRIPT_TAG'); | |
| 2316 | |
| 2317 static const TokenType SINGLE_LINE_COMMENT = | |
| 2318 const TokenType('SINGLE_LINE_COMMENT'); | |
| 2319 | |
| 2320 static const TokenType STRING = const TokenType('STRING'); | |
| 2321 | |
| 2322 static const TokenType AMPERSAND = | |
| 2323 const TokenType('AMPERSAND', TokenClass.BITWISE_AND_OPERATOR, "&"); | |
| 2324 | |
| 2325 static const TokenType AMPERSAND_AMPERSAND = const TokenType( | |
| 2326 'AMPERSAND_AMPERSAND', TokenClass.LOGICAL_AND_OPERATOR, "&&"); | |
| 2327 | |
| 2328 static const TokenType AMPERSAND_EQ = | |
| 2329 const TokenType('AMPERSAND_EQ', TokenClass.ASSIGNMENT_OPERATOR, "&="); | |
| 2330 | |
| 2331 static const TokenType AT = const TokenType('AT', TokenClass.NO_CLASS, "@"); | |
| 2332 | |
| 2333 static const TokenType BANG = | |
| 2334 const TokenType('BANG', TokenClass.UNARY_PREFIX_OPERATOR, "!"); | |
| 2335 | |
| 2336 static const TokenType BANG_EQ = | |
| 2337 const TokenType('BANG_EQ', TokenClass.EQUALITY_OPERATOR, "!="); | |
| 2338 | |
| 2339 static const TokenType BAR = | |
| 2340 const TokenType('BAR', TokenClass.BITWISE_OR_OPERATOR, "|"); | |
| 2341 | |
| 2342 static const TokenType BAR_BAR = | |
| 2343 const TokenType('BAR_BAR', TokenClass.LOGICAL_OR_OPERATOR, "||"); | |
| 2344 | |
| 2345 static const TokenType BAR_EQ = | |
| 2346 const TokenType('BAR_EQ', TokenClass.ASSIGNMENT_OPERATOR, "|="); | |
| 2347 | |
| 2348 static const TokenType COLON = | |
| 2349 const TokenType('COLON', TokenClass.NO_CLASS, ":"); | |
| 2350 | |
| 2351 static const TokenType COMMA = | |
| 2352 const TokenType('COMMA', TokenClass.NO_CLASS, ","); | |
| 2353 | |
| 2354 static const TokenType CARET = | |
| 2355 const TokenType('CARET', TokenClass.BITWISE_XOR_OPERATOR, "^"); | |
| 2356 | |
| 2357 static const TokenType CARET_EQ = | |
| 2358 const TokenType('CARET_EQ', TokenClass.ASSIGNMENT_OPERATOR, "^="); | |
| 2359 | |
| 2360 static const TokenType CLOSE_CURLY_BRACKET = | |
| 2361 const TokenType('CLOSE_CURLY_BRACKET', TokenClass.NO_CLASS, "}"); | |
| 2362 | |
| 2363 static const TokenType CLOSE_PAREN = | |
| 2364 const TokenType('CLOSE_PAREN', TokenClass.NO_CLASS, ")"); | |
| 2365 | |
| 2366 static const TokenType CLOSE_SQUARE_BRACKET = | |
| 2367 const TokenType('CLOSE_SQUARE_BRACKET', TokenClass.NO_CLASS, "]"); | |
| 2368 | |
| 2369 static const TokenType EQ = | |
| 2370 const TokenType('EQ', TokenClass.ASSIGNMENT_OPERATOR, "="); | |
| 2371 | |
| 2372 static const TokenType EQ_EQ = | |
| 2373 const TokenType('EQ_EQ', TokenClass.EQUALITY_OPERATOR, "=="); | |
| 2374 | |
| 2375 static const TokenType FUNCTION = | |
| 2376 const TokenType('FUNCTION', TokenClass.NO_CLASS, "=>"); | |
| 2377 | |
| 2378 static const TokenType GT = | |
| 2379 const TokenType('GT', TokenClass.RELATIONAL_OPERATOR, ">"); | |
| 2380 | |
| 2381 static const TokenType GT_EQ = | |
| 2382 const TokenType('GT_EQ', TokenClass.RELATIONAL_OPERATOR, ">="); | |
| 2383 | |
| 2384 static const TokenType GT_GT = | |
| 2385 const TokenType('GT_GT', TokenClass.SHIFT_OPERATOR, ">>"); | |
| 2386 | |
| 2387 static const TokenType GT_GT_EQ = | |
| 2388 const TokenType('GT_GT_EQ', TokenClass.ASSIGNMENT_OPERATOR, ">>="); | |
| 2389 | |
| 2390 static const TokenType HASH = | |
| 2391 const TokenType('HASH', TokenClass.NO_CLASS, "#"); | |
| 2392 | |
| 2393 static const TokenType INDEX = | |
| 2394 const TokenType('INDEX', TokenClass.UNARY_POSTFIX_OPERATOR, "[]"); | |
| 2395 | |
| 2396 static const TokenType INDEX_EQ = | |
| 2397 const TokenType('INDEX_EQ', TokenClass.UNARY_POSTFIX_OPERATOR, "[]="); | |
| 2398 | |
| 2399 static const TokenType IS = | |
| 2400 const TokenType('IS', TokenClass.RELATIONAL_OPERATOR, "is"); | |
| 2401 | |
| 2402 static const TokenType LT = | |
| 2403 const TokenType('LT', TokenClass.RELATIONAL_OPERATOR, "<"); | |
| 2404 | |
| 2405 static const TokenType LT_EQ = | |
| 2406 const TokenType('LT_EQ', TokenClass.RELATIONAL_OPERATOR, "<="); | |
| 2407 | |
| 2408 static const TokenType LT_LT = | |
| 2409 const TokenType('LT_LT', TokenClass.SHIFT_OPERATOR, "<<"); | |
| 2410 | |
| 2411 static const TokenType LT_LT_EQ = | |
| 2412 const TokenType('LT_LT_EQ', TokenClass.ASSIGNMENT_OPERATOR, "<<="); | |
| 2413 | |
| 2414 static const TokenType MINUS = | |
| 2415 const TokenType('MINUS', TokenClass.ADDITIVE_OPERATOR, "-"); | |
| 2416 | |
| 2417 static const TokenType MINUS_EQ = | |
| 2418 const TokenType('MINUS_EQ', TokenClass.ASSIGNMENT_OPERATOR, "-="); | |
| 2419 | |
| 2420 static const TokenType MINUS_MINUS = | |
| 2421 const TokenType('MINUS_MINUS', TokenClass.UNARY_PREFIX_OPERATOR, "--"); | |
| 2422 | |
| 2423 static const TokenType OPEN_CURLY_BRACKET = | |
| 2424 const TokenType('OPEN_CURLY_BRACKET', TokenClass.NO_CLASS, "{"); | |
| 2425 | |
| 2426 static const TokenType OPEN_PAREN = | |
| 2427 const TokenType('OPEN_PAREN', TokenClass.UNARY_POSTFIX_OPERATOR, "("); | |
| 2428 | |
| 2429 static const TokenType OPEN_SQUARE_BRACKET = const TokenType( | |
| 2430 'OPEN_SQUARE_BRACKET', TokenClass.UNARY_POSTFIX_OPERATOR, "["); | |
| 2431 | |
| 2432 static const TokenType PERCENT = | |
| 2433 const TokenType('PERCENT', TokenClass.MULTIPLICATIVE_OPERATOR, "%"); | |
| 2434 | |
| 2435 static const TokenType PERCENT_EQ = | |
| 2436 const TokenType('PERCENT_EQ', TokenClass.ASSIGNMENT_OPERATOR, "%="); | |
| 2437 | |
| 2438 static const TokenType PERIOD = | |
| 2439 const TokenType('PERIOD', TokenClass.UNARY_POSTFIX_OPERATOR, "."); | |
| 2440 | |
| 2441 static const TokenType PERIOD_PERIOD = | |
| 2442 const TokenType('PERIOD_PERIOD', TokenClass.CASCADE_OPERATOR, ".."); | |
| 2443 | |
| 2444 static const TokenType PLUS = | |
| 2445 const TokenType('PLUS', TokenClass.ADDITIVE_OPERATOR, "+"); | |
| 2446 | |
| 2447 static const TokenType PLUS_EQ = | |
| 2448 const TokenType('PLUS_EQ', TokenClass.ASSIGNMENT_OPERATOR, "+="); | |
| 2449 | |
| 2450 static const TokenType PLUS_PLUS = | |
| 2451 const TokenType('PLUS_PLUS', TokenClass.UNARY_PREFIX_OPERATOR, "++"); | |
| 2452 | |
| 2453 static const TokenType QUESTION = | |
| 2454 const TokenType('QUESTION', TokenClass.CONDITIONAL_OPERATOR, "?"); | |
| 2455 | |
| 2456 static const TokenType QUESTION_PERIOD = const TokenType( | |
| 2457 'QUESTION_PERIOD', TokenClass.UNARY_POSTFIX_OPERATOR, '?.'); | |
| 2458 | |
| 2459 static const TokenType QUESTION_QUESTION = | |
| 2460 const TokenType('QUESTION_QUESTION', TokenClass.IF_NULL_OPERATOR, '??'); | |
| 2461 | |
| 2462 static const TokenType QUESTION_QUESTION_EQ = const TokenType( | |
| 2463 'QUESTION_QUESTION_EQ', TokenClass.ASSIGNMENT_OPERATOR, '??='); | |
| 2464 | |
| 2465 static const TokenType SEMICOLON = | |
| 2466 const TokenType('SEMICOLON', TokenClass.NO_CLASS, ";"); | |
| 2467 | |
| 2468 static const TokenType SLASH = | |
| 2469 const TokenType('SLASH', TokenClass.MULTIPLICATIVE_OPERATOR, "/"); | |
| 2470 | |
| 2471 static const TokenType SLASH_EQ = | |
| 2472 const TokenType('SLASH_EQ', TokenClass.ASSIGNMENT_OPERATOR, "/="); | |
| 2473 | |
| 2474 static const TokenType STAR = | |
| 2475 const TokenType('STAR', TokenClass.MULTIPLICATIVE_OPERATOR, "*"); | |
| 2476 | |
| 2477 static const TokenType STAR_EQ = | |
| 2478 const TokenType('STAR_EQ', TokenClass.ASSIGNMENT_OPERATOR, "*="); | |
| 2479 | |
| 2480 static const TokenType STRING_INTERPOLATION_EXPRESSION = const TokenType( | |
| 2481 'STRING_INTERPOLATION_EXPRESSION', TokenClass.NO_CLASS, "\${"); | |
| 2482 | |
| 2483 static const TokenType STRING_INTERPOLATION_IDENTIFIER = const TokenType( | |
| 2484 'STRING_INTERPOLATION_IDENTIFIER', TokenClass.NO_CLASS, "\$"); | |
| 2485 | |
| 2486 static const TokenType TILDE = | |
| 2487 const TokenType('TILDE', TokenClass.UNARY_PREFIX_OPERATOR, "~"); | |
| 2488 | |
| 2489 static const TokenType TILDE_SLASH = | |
| 2490 const TokenType('TILDE_SLASH', TokenClass.MULTIPLICATIVE_OPERATOR, "~/"); | |
| 2491 | |
| 2492 static const TokenType TILDE_SLASH_EQ = | |
| 2493 const TokenType('TILDE_SLASH_EQ', TokenClass.ASSIGNMENT_OPERATOR, "~/="); | |
| 2494 | |
| 2495 static const TokenType BACKPING = | |
| 2496 const TokenType('BACKPING', TokenClass.NO_CLASS, "`"); | |
| 2497 | |
| 2498 static const TokenType BACKSLASH = | |
| 2499 const TokenType('BACKSLASH', TokenClass.NO_CLASS, "\\"); | |
| 2500 | |
| 2501 static const TokenType PERIOD_PERIOD_PERIOD = | |
| 2502 const TokenType('PERIOD_PERIOD_PERIOD', TokenClass.NO_CLASS, "..."); | |
| 2503 | |
| 2504 /** | |
| 2505 * The class of the token. | |
| 2506 */ | |
| 2507 final TokenClass _tokenClass; | |
| 2508 | |
| 2509 /** | |
| 2510 * The name of the token type. | |
| 2511 */ | |
| 2512 final String name; | |
| 2513 | |
| 2514 /** | |
| 2515 * The lexeme that defines this type of token, or `null` if there is more than | |
| 2516 * one possible lexeme for this type of token. | |
| 2517 */ | |
| 2518 final String lexeme; | |
| 2519 | |
| 2520 const TokenType(this.name, | |
| 2521 [this._tokenClass = TokenClass.NO_CLASS, this.lexeme = null]); | |
| 2522 | |
| 2523 /** | |
| 2524 * Return `true` if this type of token represents an additive operator. | |
| 2525 */ | |
| 2526 bool get isAdditiveOperator => _tokenClass == TokenClass.ADDITIVE_OPERATOR; | |
| 2527 | |
| 2528 /** | |
| 2529 * Return `true` if this type of token represents an assignment operator. | |
| 2530 */ | |
| 2531 bool get isAssignmentOperator => | |
| 2532 _tokenClass == TokenClass.ASSIGNMENT_OPERATOR; | |
| 2533 | |
| 2534 /** | |
| 2535 * Return `true` if this type of token represents an associative operator. An | |
| 2536 * associative operator is an operator for which the following equality is | |
| 2537 * true: `(a * b) * c == a * (b * c)`. In other words, if the result of | |
| 2538 * applying the operator to multiple operands does not depend on the order in | |
| 2539 * which those applications occur. | |
| 2540 * | |
| 2541 * Note: This method considers the logical-and and logical-or operators to be | |
| 2542 * associative, even though the order in which the application of those | |
| 2543 * operators can have an effect because evaluation of the right-hand operand | |
| 2544 * is conditional. | |
| 2545 */ | |
| 2546 bool get isAssociativeOperator => this == AMPERSAND || | |
| 2547 this == AMPERSAND_AMPERSAND || | |
| 2548 this == BAR || | |
| 2549 this == BAR_BAR || | |
| 2550 this == CARET || | |
| 2551 this == PLUS || | |
| 2552 this == STAR; | |
| 2553 | |
| 2554 /** | |
| 2555 * Return `true` if this type of token represents an equality operator. | |
| 2556 */ | |
| 2557 bool get isEqualityOperator => _tokenClass == TokenClass.EQUALITY_OPERATOR; | |
| 2558 | |
| 2559 /** | |
| 2560 * Return `true` if this type of token represents an increment operator. | |
| 2561 */ | |
| 2562 bool get isIncrementOperator => | |
| 2563 identical(lexeme, "++") || identical(lexeme, "--"); | |
| 2564 | |
| 2565 /** | |
| 2566 * Return `true` if this type of token represents a multiplicative operator. | |
| 2567 */ | |
| 2568 bool get isMultiplicativeOperator => | |
| 2569 _tokenClass == TokenClass.MULTIPLICATIVE_OPERATOR; | |
| 2570 | |
| 2571 /** | |
| 2572 * Return `true` if this token type represents an operator. | |
| 2573 */ | |
| 2574 bool get isOperator => _tokenClass != TokenClass.NO_CLASS && | |
| 2575 this != OPEN_PAREN && | |
| 2576 this != OPEN_SQUARE_BRACKET && | |
| 2577 this != PERIOD; | |
| 2578 | |
| 2579 /** | |
| 2580 * Return `true` if this type of token represents a relational operator. | |
| 2581 */ | |
| 2582 bool get isRelationalOperator => | |
| 2583 _tokenClass == TokenClass.RELATIONAL_OPERATOR; | |
| 2584 | |
| 2585 /** | |
| 2586 * Return `true` if this type of token represents a shift operator. | |
| 2587 */ | |
| 2588 bool get isShiftOperator => _tokenClass == TokenClass.SHIFT_OPERATOR; | |
| 2589 | |
| 2590 /** | |
| 2591 * Return `true` if this type of token represents a unary postfix operator. | |
| 2592 */ | |
| 2593 bool get isUnaryPostfixOperator => | |
| 2594 _tokenClass == TokenClass.UNARY_POSTFIX_OPERATOR; | |
| 2595 | |
| 2596 /** | |
| 2597 * Return `true` if this type of token represents a unary prefix operator. | |
| 2598 */ | |
| 2599 bool get isUnaryPrefixOperator => | |
| 2600 _tokenClass == TokenClass.UNARY_PREFIX_OPERATOR; | |
| 2601 | |
| 2602 /** | |
| 2603 * Return `true` if this token type represents an operator that can be defined | |
| 2604 * by users. | |
| 2605 */ | |
| 2606 bool get isUserDefinableOperator => identical(lexeme, "==") || | |
| 2607 identical(lexeme, "~") || | |
| 2608 identical(lexeme, "[]") || | |
| 2609 identical(lexeme, "[]=") || | |
| 2610 identical(lexeme, "*") || | |
| 2611 identical(lexeme, "/") || | |
| 2612 identical(lexeme, "%") || | |
| 2613 identical(lexeme, "~/") || | |
| 2614 identical(lexeme, "+") || | |
| 2615 identical(lexeme, "-") || | |
| 2616 identical(lexeme, "<<") || | |
| 2617 identical(lexeme, ">>") || | |
| 2618 identical(lexeme, ">=") || | |
| 2619 identical(lexeme, ">") || | |
| 2620 identical(lexeme, "<=") || | |
| 2621 identical(lexeme, "<") || | |
| 2622 identical(lexeme, "&") || | |
| 2623 identical(lexeme, "^") || | |
| 2624 identical(lexeme, "|"); | |
| 2625 | |
| 2626 /** | |
| 2627 * Return the precedence of the token, or `0` if the token does not represent | |
| 2628 * an operator. | |
| 2629 */ | |
| 2630 int get precedence => _tokenClass.precedence; | |
| 2631 | |
| 2632 @override | |
| 2633 String toString() => name; | |
| 2634 } | |
| 2635 | |
| 2636 class TokenType_EOF extends TokenType { | |
| 2637 const TokenType_EOF(String name) : super(name, TokenClass.NO_CLASS, ""); | |
| 2638 | |
| 2639 @override | |
| 2640 String toString() => "-eof-"; | |
| 2641 } | |
| 2642 | |
| 2643 /** | |
| 2644 * A normal token that is preceded by comments. | |
| 2645 */ | |
| 2646 class TokenWithComment extends Token { | |
| 2647 /** | |
| 2648 * The first comment in the list of comments that precede this token. | |
| 2649 */ | |
| 2650 CommentToken _precedingComment; | |
| 2651 | |
| 2652 /** | |
| 2653 * Initialize a newly created token to have the given [type] at the given | |
| 2654 * [offset] and to be preceded by the comments reachable from the given | |
| 2655 * [comment]. | |
| 2656 */ | |
| 2657 TokenWithComment(TokenType type, int offset, this._precedingComment) | |
| 2658 : super(type, offset) { | |
| 2659 _setCommentParent(_precedingComment); | |
| 2660 } | |
| 2661 | |
| 2662 CommentToken get precedingComments => _precedingComment; | |
| 2663 | |
| 2664 void set precedingComments(CommentToken comment) { | |
| 2665 _precedingComment = comment; | |
| 2666 _setCommentParent(_precedingComment); | |
| 2667 } | |
| 2668 | |
| 2669 @override | |
| 2670 Token copy() => new TokenWithComment(type, offset, precedingComments); | |
| 2671 } | |
| OLD | NEW |