Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(515)

Side by Side Diff: pkg/analyzer/lib/src/generated/scanner.dart

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

Powered by Google App Engine
This is Rietveld 408576698