OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library fasta.analyzer.token_utils; | 5 library fasta.analyzer.token_utils; |
6 | 6 |
7 import 'package:front_end/src/fasta/scanner/error_token.dart' show ErrorToken; | 7 import 'package:front_end/src/scanner/token.dart' show Token; |
8 | 8 |
9 import 'package:front_end/src/scanner/token.dart' show Keyword, Token; | 9 import 'package:front_end/src/fasta/scanner/token.dart' show SymbolToken; |
10 | |
11 import 'package:front_end/src/fasta/scanner/token.dart' | |
12 show BeginGroupToken, CommentToken, DartDocToken, StringToken, SymbolToken; | |
13 | 10 |
14 import 'package:front_end/src/fasta/scanner/token_constants.dart'; | 11 import 'package:front_end/src/fasta/scanner/token_constants.dart'; |
15 | 12 |
16 import 'package:front_end/src/scanner/errors.dart' show translateErrorToken; | 13 import 'package:front_end/src/scanner/errors.dart' show translateErrorToken; |
17 | 14 |
18 import 'package:front_end/src/scanner/token.dart' as analyzer | |
19 show | |
20 BeginToken, | |
21 BeginTokenWithComment, | |
22 CommentToken, | |
23 Keyword, | |
24 KeywordToken, | |
25 KeywordTokenWithComment, | |
26 StringToken, | |
27 StringTokenWithComment, | |
28 Token, | |
29 TokenWithComment; | |
30 | |
31 import 'package:front_end/src/scanner/errors.dart' as analyzer | 15 import 'package:front_end/src/scanner/errors.dart' as analyzer |
32 show ScannerErrorCode; | 16 show ScannerErrorCode; |
33 | 17 |
34 import 'package:analyzer/dart/ast/token.dart' show TokenType; | |
35 | |
36 import 'package:front_end/src/fasta/errors.dart' show internalError; | |
37 | |
38 /// Class capable of converting a stream of Fasta tokens to a stream of analyzer | 18 /// Class capable of converting a stream of Fasta tokens to a stream of analyzer |
39 /// tokens. | 19 /// tokens. |
40 /// | 20 /// |
41 /// This is a class rather than an ordinary method so that it can be subclassed | 21 /// This is a class rather than an ordinary method so that it can be subclassed |
42 /// in tests. | 22 /// in tests. |
43 /// | |
44 /// TODO(paulberry,ahe): Fasta includes comments directly in the token | |
45 /// stream, rather than pointing to them via a "precedingComment" pointer, as | |
46 /// analyzer does. This seems like it will complicate parsing and other | |
47 /// operations. | |
48 class ToAnalyzerTokenStreamConverter { | 23 class ToAnalyzerTokenStreamConverter { |
49 /// Synthetic token pointing to the first token in the analyzer token stream. | |
50 analyzer.Token _analyzerTokenHead; | |
51 | |
52 /// The most recently generated analyzer token, or [_analyzerTokenHead] if no | |
53 /// tokens have been generated yet. | |
54 analyzer.Token _analyzerTokenTail; | |
55 | |
56 /// Stack of analyzer "begin" tokens which need to be linked up to | |
57 /// corresponding "end" tokens once those tokens are translated. | |
58 /// | |
59 /// The first element of this list is always a sentinel `null` value so that | |
60 /// we don't have to check if it is empty. | |
61 /// | |
62 /// See additional documentation in [_matchGroups]. | |
63 List<analyzer.BeginToken> _beginTokenStack; | |
64 | |
65 /// Stack of fasta "end" tokens corresponding to the tokens in | |
66 /// [_endTokenStack]. | |
67 /// | |
68 /// The first element of this list is always a sentinel `null` value so that | |
69 /// we don't have to check if it is empty. | |
70 /// | |
71 /// See additional documentation in [_matchGroups]. | |
72 List<Token> _endTokenStack; | |
73 | |
74 /// Converts a stream of Fasta tokens (starting with [token] and continuing to | 24 /// Converts a stream of Fasta tokens (starting with [token] and continuing to |
75 /// EOF) to a stream of analyzer tokens. | 25 /// EOF) to a stream of analyzer tokens. This modifies the fasta token stream |
76 analyzer.Token convertTokens(Token token) { | 26 /// to be an analyzer token stream by removing error tokens and reporting |
77 _analyzerTokenHead = new analyzer.Token(TokenType.EOF, -1); | 27 /// those errors to the associated error listener. |
78 _analyzerTokenHead.previous = _analyzerTokenHead; | 28 Token convertTokens(Token firstToken) { |
79 _analyzerTokenTail = _analyzerTokenHead; | 29 Token previous = new SymbolToken.eof(-1); |
80 _beginTokenStack = [null]; | 30 Token token = firstToken; |
81 _endTokenStack = <Token>[null]; | 31 token.previous = previous; |
82 | 32 previous.next = token; |
83 while (true) { | 33 while (!token.isEof) { |
84 if (token.type.kind == BAD_INPUT_TOKEN) { | 34 if (token.type.kind == BAD_INPUT_TOKEN) { |
85 ErrorToken errorToken = token; | 35 translateErrorToken(token, reportError); |
86 translateErrorToken(errorToken, reportError); | 36 previous.next = token.next; |
| 37 token.next.previous = previous; |
87 } else { | 38 } else { |
88 var translatedToken = translateToken( | 39 previous = token; |
89 token, translateCommentTokens(token.precedingComments)); | |
90 _matchGroups(token, translatedToken); | |
91 translatedToken.setNext(translatedToken); | |
92 _analyzerTokenTail.setNext(translatedToken); | |
93 translatedToken.previous = _analyzerTokenTail; | |
94 _analyzerTokenTail = translatedToken; | |
95 } | |
96 if (token.isEof) { | |
97 return _analyzerTokenHead.next; | |
98 } | 40 } |
99 token = token.next; | 41 token = token.next; |
100 } | 42 } |
| 43 return firstToken; |
101 } | 44 } |
102 | 45 |
103 /// Handles an error found during [convertTokens]. | 46 /// Handles an error found during [convertTokens]. |
104 /// | 47 /// |
105 /// Intended to be overridden by derived classes; by default, does nothing. | 48 /// Intended to be overridden by derived classes; by default, does nothing. |
106 void reportError(analyzer.ScannerErrorCode errorCode, int offset, | 49 void reportError(analyzer.ScannerErrorCode errorCode, int offset, |
107 List<Object> arguments) {} | 50 List<Object> arguments) {} |
108 | |
109 /// Translates a sequence of fasta comment tokens to the corresponding | |
110 /// analyzer tokens. | |
111 analyzer.CommentToken translateCommentTokens(analyzer.Token token) { | |
112 analyzer.CommentToken head; | |
113 if (token != null) { | |
114 head = toAnalyzerCommentToken(token); | |
115 analyzer.CommentToken tail = head; | |
116 token = token.next; | |
117 while (token != null) { | |
118 tail = tail.setNext(toAnalyzerCommentToken(token)); | |
119 token = token.next; | |
120 } | |
121 } | |
122 return head; | |
123 } | |
124 | |
125 /// Translates a single fasta non-comment token to the corresponding analyzer | |
126 /// token. | |
127 /// | |
128 /// [precedingComments] is not `null`, the translated token is pointed to it. | |
129 analyzer.Token translateToken( | |
130 Token token, analyzer.CommentToken precedingComments) => | |
131 toAnalyzerToken(token, precedingComments); | |
132 | |
133 /// Creates appropriate begin/end token links based on the fact that [token] | |
134 /// was translated to [translatedToken]. | |
135 /// | |
136 /// Background: both fasta and analyzer have links from a "BeginToken" to its | |
137 /// matching "EndToken" in a group (like parentheses and braces). However, | |
138 /// fasta may contain synthetic tokens from error recovery that are not mapped | |
139 /// to the analyzer token stream. We use [_beginTokenStack] and | |
140 /// [_endTokenStack] to create the appropriate links for non-synthetic tokens | |
141 /// in the way analyzer expects. | |
142 void _matchGroups(Token token, analyzer.Token translatedToken) { | |
143 if (identical(_endTokenStack.last, token)) { | |
144 _beginTokenStack.last.endToken = translatedToken; | |
145 _beginTokenStack.removeLast(); | |
146 _endTokenStack.removeLast(); | |
147 } | |
148 // Synthetic end tokens have a length of zero. | |
149 if (translatedToken is analyzer.BeginToken && | |
150 token is BeginGroupToken && | |
151 token.endGroup != null && | |
152 token.endGroup.charOffset != token.charOffset) { | |
153 _beginTokenStack.add(translatedToken); | |
154 _endTokenStack.add(token.endGroup); | |
155 } | |
156 } | |
157 } | 51 } |
158 | |
159 /// Converts a single Fasta comment token to an analyzer comment token. | |
160 analyzer.CommentToken toAnalyzerCommentToken(Token token) { | |
161 TokenType type; | |
162 if (token.type == TokenType.GENERIC_METHOD_TYPE_ASSIGN) { | |
163 type = TokenType.GENERIC_METHOD_TYPE_ASSIGN; | |
164 } else if (token.type == TokenType.GENERIC_METHOD_TYPE_LIST) { | |
165 type = TokenType.GENERIC_METHOD_TYPE_LIST; | |
166 } else { | |
167 // TODO(paulberry,ahe): It would be nice if the scanner gave us an | |
168 // easier way to distinguish between the two types of comment. | |
169 type = token.lexeme.startsWith('/*') | |
170 ? TokenType.MULTI_LINE_COMMENT | |
171 : TokenType.SINGLE_LINE_COMMENT; | |
172 } | |
173 return new analyzer.CommentToken(type, token.lexeme, token.charOffset); | |
174 } | |
175 | |
176 /// Converts a stream of Analyzer tokens (starting with [token] and continuing | |
177 /// to EOF) to a stream of Fasta tokens. | |
178 /// | |
179 /// TODO(paulberry): Analyzer tokens do not record error conditions, so a round | |
180 /// trip through this function and [toAnalyzerTokenStream] will lose error | |
181 /// information. | |
182 Token fromAnalyzerTokenStream(analyzer.Token analyzerToken) { | |
183 Token tokenHead = new SymbolToken.eof(-1); | |
184 Token tokenTail = tokenHead; | |
185 | |
186 // Both fasta and analyzer have links from a "BeginToken" to its matching | |
187 // "EndToken" in a group (like parentheses and braces). However, only fasta | |
188 // makes these links for angle brackets. We use these stacks to map the | |
189 // links from the analyzer token stream into equivalent links in the fasta | |
190 // token stream, and to create the links that fasta expects for angle | |
191 // brackets. | |
192 | |
193 // Note: beginTokenStack and endTokenStack are seeded with a sentinel value | |
194 // so that we don't have to check if they're empty. | |
195 var beginTokenStack = <BeginGroupToken>[null]; | |
196 var endTokenStack = <analyzer.Token>[null]; | |
197 var angleBracketStack = <BeginGroupToken>[]; | |
198 void matchGroups(analyzer.Token analyzerToken, Token translatedToken) { | |
199 if (identical(endTokenStack.last, analyzerToken)) { | |
200 angleBracketStack.clear(); | |
201 beginTokenStack.last.endGroup = translatedToken; | |
202 beginTokenStack.removeLast(); | |
203 endTokenStack.removeLast(); | |
204 } else if (translatedToken.type.kind == LT_TOKEN) { | |
205 BeginGroupToken beginGroupToken = translatedToken; | |
206 angleBracketStack.add(beginGroupToken); | |
207 } else if (translatedToken.type.kind == GT_TOKEN && | |
208 angleBracketStack.isNotEmpty) { | |
209 angleBracketStack.removeLast().endGroup = translatedToken; | |
210 } else if (translatedToken.type.kind == GT_GT_TOKEN && | |
211 angleBracketStack.isNotEmpty) { | |
212 angleBracketStack.removeLast(); | |
213 if (angleBracketStack.isNotEmpty) { | |
214 angleBracketStack.removeLast().endGroup = translatedToken; | |
215 } | |
216 } | |
217 // TODO(paulberry): generate synthetic closer tokens and "UnmatchedToken" | |
218 // tokens as appropriate. | |
219 if (translatedToken is BeginGroupToken && | |
220 analyzerToken is analyzer.BeginToken && | |
221 analyzerToken.endToken != null) { | |
222 angleBracketStack.clear(); | |
223 beginTokenStack.add(translatedToken); | |
224 endTokenStack.add(analyzerToken.endToken); | |
225 } | |
226 } | |
227 | |
228 analyzer.Token translateAndAppend(analyzer.Token analyzerToken) { | |
229 var token = fromAnalyzerToken(analyzerToken); | |
230 // Sanity check | |
231 if (analyzerToken.precedingComments != null) { | |
232 if (token.precedingComments == null) { | |
233 return internalError( | |
234 'expected translated token $token to have preceedingComments'); | |
235 } | |
236 } else { | |
237 if (token.precedingComments != null) { | |
238 return internalError('token $token has unexpected preceedingComments'); | |
239 } | |
240 } | |
241 tokenTail.next = token; | |
242 tokenTail.next.previous = tokenTail; // ignore: deprecated_member_use | |
243 tokenTail = token; | |
244 matchGroups(analyzerToken, token); | |
245 return analyzerToken.next; | |
246 } | |
247 | |
248 while (true) { | |
249 // TODO(paulberry): join up begingroup/endgroup. | |
250 if (analyzerToken.type == TokenType.EOF) { | |
251 SymbolToken eof = new SymbolToken.eof(analyzerToken.offset); | |
252 tokenTail.next = eof; | |
253 eof.previous = tokenTail; // ignore: deprecated_member_use | |
254 eof.precedingComments = | |
255 _translateComments(analyzerToken.precedingComments); | |
256 eof.next = eof; | |
257 return tokenHead.next; | |
258 } | |
259 analyzerToken = translateAndAppend(analyzerToken); | |
260 } | |
261 } | |
262 | |
263 /// Converts a single analyzer token into a Fasta token. | |
264 Token fromAnalyzerToken(analyzer.Token token) { | |
265 Token comments = _translateComments(token.precedingComments); | |
266 Token beginGroup(TokenType type) => | |
267 new BeginGroupToken(type, token.offset, comments); | |
268 Token string(TokenType type) => | |
269 new StringToken.fromString(type, token.lexeme, token.offset, | |
270 precedingComments: comments); | |
271 Token symbol(TokenType type) => new SymbolToken(type, token.offset, comments); | |
272 if (token.type.isKeyword) { | |
273 var keyword = Keyword.keywords[token.lexeme]; | |
274 if (keyword != null) { | |
275 return new analyzer.KeywordTokenWithComment( | |
276 keyword, token.offset, comments); | |
277 } else { | |
278 return internalError("Unrecognized keyword: '${token.lexeme}'."); | |
279 } | |
280 } | |
281 switch (token.type) { | |
282 case TokenType.DOUBLE: | |
283 return string(TokenType.DOUBLE); | |
284 case TokenType.HEXADECIMAL: | |
285 return string(TokenType.HEXADECIMAL); | |
286 case TokenType.IDENTIFIER: | |
287 // Certain identifiers have special grammatical meanings even though they | |
288 // are neither keywords nor built-in identifiers (e.g. "async"). Analyzer | |
289 // represents these as identifiers. Fasta represents them as keywords | |
290 // with the "isPseudo" property. | |
291 var keyword = Keyword.keywords[token.lexeme]; | |
292 if (keyword != null) { | |
293 assert(keyword.isPseudo); | |
294 return new analyzer.KeywordTokenWithComment( | |
295 keyword, token.offset, comments); | |
296 } else { | |
297 return string(TokenType.IDENTIFIER); | |
298 } | |
299 break; | |
300 case TokenType.INT: | |
301 return string(TokenType.INT); | |
302 case TokenType.MULTI_LINE_COMMENT: | |
303 if (token.lexeme.startsWith('/**')) { | |
304 return new DartDocToken.fromSubstring(TokenType.MULTI_LINE_COMMENT, | |
305 token.lexeme, 0, token.lexeme.length, 0); | |
306 } | |
307 return new CommentToken.fromSubstring(TokenType.MULTI_LINE_COMMENT, | |
308 token.lexeme, 0, token.lexeme.length, 0); | |
309 case TokenType.SCRIPT_TAG: | |
310 return string(TokenType.SCRIPT_TAG); | |
311 case TokenType.SINGLE_LINE_COMMENT: | |
312 if (token.lexeme.startsWith('///')) { | |
313 return new DartDocToken.fromSubstring(TokenType.SINGLE_LINE_COMMENT, | |
314 token.lexeme, 0, token.lexeme.length, 0); | |
315 } | |
316 return new CommentToken.fromSubstring(TokenType.SINGLE_LINE_COMMENT, | |
317 token.lexeme, 0, token.lexeme.length, 0); | |
318 case TokenType.STRING: | |
319 return string(TokenType.STRING); | |
320 case TokenType.AMPERSAND: | |
321 return symbol(TokenType.AMPERSAND); | |
322 case TokenType.AMPERSAND_AMPERSAND: | |
323 return symbol(TokenType.AMPERSAND_AMPERSAND); | |
324 case TokenType.AMPERSAND_AMPERSAND_EQ: | |
325 return symbol(TokenType.AMPERSAND_AMPERSAND_EQ); | |
326 case TokenType.AMPERSAND_EQ: | |
327 return symbol(TokenType.AMPERSAND_EQ); | |
328 case TokenType.AT: | |
329 return symbol(TokenType.AT); | |
330 case TokenType.BANG: | |
331 return symbol(TokenType.BANG); | |
332 case TokenType.BANG_EQ: | |
333 return symbol(TokenType.BANG_EQ); | |
334 case TokenType.BAR: | |
335 return symbol(TokenType.BAR); | |
336 case TokenType.BAR_BAR: | |
337 return symbol(TokenType.BAR_BAR); | |
338 case TokenType.BAR_BAR_EQ: | |
339 return symbol(TokenType.BAR_BAR_EQ); | |
340 case TokenType.BAR_EQ: | |
341 return symbol(TokenType.BAR_EQ); | |
342 case TokenType.COLON: | |
343 return symbol(TokenType.COLON); | |
344 case TokenType.COMMA: | |
345 return symbol(TokenType.COMMA); | |
346 case TokenType.CARET: | |
347 return symbol(TokenType.CARET); | |
348 case TokenType.CARET_EQ: | |
349 return symbol(TokenType.CARET_EQ); | |
350 case TokenType.CLOSE_CURLY_BRACKET: | |
351 return symbol(TokenType.CLOSE_CURLY_BRACKET); | |
352 case TokenType.CLOSE_PAREN: | |
353 return symbol(TokenType.CLOSE_PAREN); | |
354 case TokenType.CLOSE_SQUARE_BRACKET: | |
355 return symbol(TokenType.CLOSE_SQUARE_BRACKET); | |
356 case TokenType.EQ: | |
357 return symbol(TokenType.EQ); | |
358 case TokenType.EQ_EQ: | |
359 return symbol(TokenType.EQ_EQ); | |
360 case TokenType.FUNCTION: | |
361 return symbol(TokenType.FUNCTION); | |
362 case TokenType.GT: | |
363 return symbol(TokenType.GT); | |
364 case TokenType.GT_EQ: | |
365 return symbol(TokenType.GT_EQ); | |
366 case TokenType.GT_GT: | |
367 return symbol(TokenType.GT_GT); | |
368 case TokenType.GT_GT_EQ: | |
369 return symbol(TokenType.GT_GT_EQ); | |
370 case TokenType.HASH: | |
371 return symbol(TokenType.HASH); | |
372 case TokenType.INDEX: | |
373 return symbol(TokenType.INDEX); | |
374 case TokenType.INDEX_EQ: | |
375 return symbol(TokenType.INDEX_EQ); | |
376 case TokenType.LT: | |
377 return beginGroup(TokenType.LT); | |
378 case TokenType.LT_EQ: | |
379 return symbol(TokenType.LT_EQ); | |
380 case TokenType.LT_LT: | |
381 return symbol(TokenType.LT_LT); | |
382 case TokenType.LT_LT_EQ: | |
383 return symbol(TokenType.LT_LT_EQ); | |
384 case TokenType.MINUS: | |
385 return symbol(TokenType.MINUS); | |
386 case TokenType.MINUS_EQ: | |
387 return symbol(TokenType.MINUS_EQ); | |
388 case TokenType.MINUS_MINUS: | |
389 return symbol(TokenType.MINUS_MINUS); | |
390 case TokenType.OPEN_CURLY_BRACKET: | |
391 return beginGroup(TokenType.OPEN_CURLY_BRACKET); | |
392 case TokenType.OPEN_PAREN: | |
393 return beginGroup(TokenType.OPEN_PAREN); | |
394 case TokenType.OPEN_SQUARE_BRACKET: | |
395 return beginGroup(TokenType.OPEN_SQUARE_BRACKET); | |
396 case TokenType.PERCENT: | |
397 return symbol(TokenType.PERCENT); | |
398 case TokenType.PERCENT_EQ: | |
399 return symbol(TokenType.PERCENT_EQ); | |
400 case TokenType.PERIOD: | |
401 return symbol(TokenType.PERIOD); | |
402 case TokenType.PERIOD_PERIOD: | |
403 return symbol(TokenType.PERIOD_PERIOD); | |
404 case TokenType.PLUS: | |
405 return symbol(TokenType.PLUS); | |
406 case TokenType.PLUS_EQ: | |
407 return symbol(TokenType.PLUS_EQ); | |
408 case TokenType.PLUS_PLUS: | |
409 return symbol(TokenType.PLUS_PLUS); | |
410 case TokenType.QUESTION: | |
411 return symbol(TokenType.QUESTION); | |
412 case TokenType.QUESTION_PERIOD: | |
413 return symbol(TokenType.QUESTION_PERIOD); | |
414 case TokenType.QUESTION_QUESTION: | |
415 return symbol(TokenType.QUESTION_QUESTION); | |
416 case TokenType.QUESTION_QUESTION_EQ: | |
417 return symbol(TokenType.QUESTION_QUESTION_EQ); | |
418 case TokenType.SEMICOLON: | |
419 return symbol(TokenType.SEMICOLON); | |
420 case TokenType.SLASH: | |
421 return symbol(TokenType.SLASH); | |
422 case TokenType.SLASH_EQ: | |
423 return symbol(TokenType.SLASH_EQ); | |
424 case TokenType.STAR: | |
425 return symbol(TokenType.STAR); | |
426 case TokenType.STAR_EQ: | |
427 return symbol(TokenType.STAR_EQ); | |
428 case TokenType.STRING_INTERPOLATION_EXPRESSION: | |
429 return beginGroup(TokenType.STRING_INTERPOLATION_EXPRESSION); | |
430 case TokenType.STRING_INTERPOLATION_IDENTIFIER: | |
431 return symbol(TokenType.STRING_INTERPOLATION_IDENTIFIER); | |
432 case TokenType.TILDE: | |
433 return symbol(TokenType.TILDE); | |
434 case TokenType.TILDE_SLASH: | |
435 return symbol(TokenType.TILDE_SLASH); | |
436 case TokenType.TILDE_SLASH_EQ: | |
437 return symbol(TokenType.TILDE_SLASH_EQ); | |
438 case TokenType.BACKPING: | |
439 return symbol(TokenType.BACKPING); | |
440 case TokenType.BACKSLASH: | |
441 return symbol(TokenType.BACKSLASH); | |
442 case TokenType.PERIOD_PERIOD_PERIOD: | |
443 return symbol(TokenType.PERIOD_PERIOD_PERIOD); | |
444 case TokenType.GENERIC_METHOD_TYPE_ASSIGN: | |
445 return new CommentToken.fromSubstring( | |
446 TokenType.GENERIC_METHOD_TYPE_ASSIGN, | |
447 token.lexeme, | |
448 0, | |
449 token.lexeme.length, | |
450 0); | |
451 case TokenType.GENERIC_METHOD_TYPE_LIST: | |
452 return new CommentToken.fromSubstring(TokenType.GENERIC_METHOD_TYPE_LIST, | |
453 token.lexeme, 0, token.lexeme.length, 0); | |
454 default: | |
455 return internalError('Unhandled token type ${token.type}'); | |
456 } | |
457 } | |
458 | |
459 analyzer.Token toAnalyzerToken(Token token, | |
460 [analyzer.CommentToken commentToken]) { | |
461 if (token == null) return null; | |
462 analyzer.Token makeStringToken(TokenType tokenType) { | |
463 if (commentToken == null) { | |
464 return new analyzer.StringToken( | |
465 tokenType, token.lexeme, token.charOffset); | |
466 } else { | |
467 return new analyzer.StringTokenWithComment( | |
468 tokenType, token.lexeme, token.charOffset, commentToken); | |
469 } | |
470 } | |
471 | |
472 analyzer.Token makeBeginToken(TokenType tokenType) { | |
473 if (commentToken == null) { | |
474 return new analyzer.BeginToken(tokenType, token.charOffset); | |
475 } else { | |
476 return new analyzer.BeginTokenWithComment( | |
477 tokenType, token.charOffset, commentToken); | |
478 } | |
479 } | |
480 | |
481 switch (token.kind) { | |
482 case DOUBLE_TOKEN: | |
483 return makeStringToken(TokenType.DOUBLE); | |
484 | |
485 case HEXADECIMAL_TOKEN: | |
486 return makeStringToken(TokenType.HEXADECIMAL); | |
487 | |
488 case IDENTIFIER_TOKEN: | |
489 return makeStringToken(TokenType.IDENTIFIER); | |
490 | |
491 case INT_TOKEN: | |
492 return makeStringToken(TokenType.INT); | |
493 | |
494 case KEYWORD_TOKEN: | |
495 var syntax = token.type.lexeme; | |
496 // TODO(paulberry): if the map lookup proves to be too slow, consider | |
497 // using a switch statement, or perhaps a string of | |
498 // "if (identical(syntax, "foo"))" checks. (Note that identical checks | |
499 // should be safe because the Fasta scanner uses string literals for | |
500 // the values of keyword.syntax.) | |
501 var keyword = | |
502 _keywordMap[syntax] ?? internalError('Unknown keyword: $syntax'); | |
503 if (commentToken == null) { | |
504 return new analyzer.KeywordToken(keyword, token.charOffset); | |
505 } else { | |
506 return new analyzer.KeywordTokenWithComment( | |
507 keyword, token.charOffset, commentToken); | |
508 } | |
509 break; | |
510 | |
511 case SCRIPT_TOKEN: | |
512 return makeStringToken(TokenType.SCRIPT_TAG); | |
513 | |
514 case STRING_TOKEN: | |
515 return makeStringToken(TokenType.STRING); | |
516 | |
517 case OPEN_CURLY_BRACKET_TOKEN: | |
518 case OPEN_SQUARE_BRACKET_TOKEN: | |
519 case OPEN_PAREN_TOKEN: | |
520 case STRING_INTERPOLATION_TOKEN: | |
521 return makeBeginToken(token.type); | |
522 | |
523 default: | |
524 if (commentToken == null) { | |
525 return new analyzer.Token(token.type, token.charOffset); | |
526 } else { | |
527 return new analyzer.TokenWithComment( | |
528 token.type, token.charOffset, commentToken); | |
529 } | |
530 break; | |
531 } | |
532 } | |
533 | |
534 analyzer.Token _translateComments(analyzer.Token token) { | |
535 if (token == null) { | |
536 return null; | |
537 } | |
538 Token head = fromAnalyzerToken(token); | |
539 Token tail = head; | |
540 token = token.next; | |
541 while (token != null) { | |
542 tail.next = fromAnalyzerToken(token); | |
543 tail.next.previous = tail; // ignore: deprecated_member_use | |
544 tail = tail.next; | |
545 token = token.next; | |
546 } | |
547 return head; | |
548 } | |
549 | |
550 final _keywordMap = { | |
551 "assert": analyzer.Keyword.ASSERT, | |
552 "break": analyzer.Keyword.BREAK, | |
553 "case": analyzer.Keyword.CASE, | |
554 "catch": analyzer.Keyword.CATCH, | |
555 "class": analyzer.Keyword.CLASS, | |
556 "const": analyzer.Keyword.CONST, | |
557 "continue": analyzer.Keyword.CONTINUE, | |
558 "default": analyzer.Keyword.DEFAULT, | |
559 "do": analyzer.Keyword.DO, | |
560 "else": analyzer.Keyword.ELSE, | |
561 "enum": analyzer.Keyword.ENUM, | |
562 "extends": analyzer.Keyword.EXTENDS, | |
563 "false": analyzer.Keyword.FALSE, | |
564 "final": analyzer.Keyword.FINAL, | |
565 "finally": analyzer.Keyword.FINALLY, | |
566 "for": analyzer.Keyword.FOR, | |
567 "if": analyzer.Keyword.IF, | |
568 "in": analyzer.Keyword.IN, | |
569 "new": analyzer.Keyword.NEW, | |
570 "null": analyzer.Keyword.NULL, | |
571 "rethrow": analyzer.Keyword.RETHROW, | |
572 "return": analyzer.Keyword.RETURN, | |
573 "super": analyzer.Keyword.SUPER, | |
574 "switch": analyzer.Keyword.SWITCH, | |
575 "this": analyzer.Keyword.THIS, | |
576 "throw": analyzer.Keyword.THROW, | |
577 "true": analyzer.Keyword.TRUE, | |
578 "try": analyzer.Keyword.TRY, | |
579 "var": analyzer.Keyword.VAR, | |
580 "void": analyzer.Keyword.VOID, | |
581 "while": analyzer.Keyword.WHILE, | |
582 "with": analyzer.Keyword.WITH, | |
583 // | |
584 "is": analyzer.Keyword.IS, | |
585 // | |
586 "abstract": analyzer.Keyword.ABSTRACT, | |
587 "as": analyzer.Keyword.AS, | |
588 "covariant": analyzer.Keyword.COVARIANT, | |
589 "deferred": analyzer.Keyword.DEFERRED, | |
590 "dynamic": analyzer.Keyword.DYNAMIC, | |
591 "export": analyzer.Keyword.EXPORT, | |
592 "external": analyzer.Keyword.EXTERNAL, | |
593 "factory": analyzer.Keyword.FACTORY, | |
594 "get": analyzer.Keyword.GET, | |
595 "implements": analyzer.Keyword.IMPLEMENTS, | |
596 "import": analyzer.Keyword.IMPORT, | |
597 "library": analyzer.Keyword.LIBRARY, | |
598 "operator": analyzer.Keyword.OPERATOR, | |
599 "part": analyzer.Keyword.PART, | |
600 "set": analyzer.Keyword.SET, | |
601 "static": analyzer.Keyword.STATIC, | |
602 "typedef": analyzer.Keyword.TYPEDEF, | |
603 // | |
604 "async": analyzer.Keyword.ASYNC, | |
605 "await": analyzer.Keyword.AWAIT, | |
606 "Function": analyzer.Keyword.FUNCTION, | |
607 "hide": analyzer.Keyword.HIDE, | |
608 "native": analyzer.Keyword.NATIVE, | |
609 "of": analyzer.Keyword.OF, | |
610 "on": analyzer.Keyword.ON, | |
611 "patch": analyzer.Keyword.PATCH, | |
612 "show": analyzer.Keyword.SHOW, | |
613 "source": analyzer.Keyword.SOURCE, | |
614 "sync": analyzer.Keyword.SYNC, | |
615 "yield": analyzer.Keyword.YIELD, | |
616 }; | |
OLD | NEW |