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

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

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

Powered by Google App Engine
This is Rietveld 408576698