OLD | NEW |
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'dart:convert' show UTF8; | 5 import 'dart:convert' show UTF8; |
6 | 6 |
7 import 'package:front_end/src/fasta/scanner/precedence.dart' | 7 import 'package:front_end/src/fasta/scanner/precedence.dart' |
8 show BAD_INPUT_INFO, EOF_INFO; | 8 show BAD_INPUT_INFO, EOF_INFO; |
9 import 'package:front_end/src/fasta/scanner/recover.dart' | 9 import 'package:front_end/src/fasta/scanner/recover.dart' |
10 show defaultRecoveryStrategy; | 10 show defaultRecoveryStrategy; |
11 import 'package:front_end/src/fasta/scanner.dart' as fasta; | 11 import 'package:front_end/src/fasta/scanner.dart' as fasta; |
| 12 import 'package:front_end/src/fasta/scanner/token.dart' as fasta; |
| 13 import 'package:front_end/src/fasta/scanner/error_token.dart' as fasta; |
12 import 'package:front_end/src/scanner/token.dart' as analyzer; | 14 import 'package:front_end/src/scanner/token.dart' as analyzer; |
13 import 'package:front_end/src/scanner/errors.dart' | 15 import 'package:front_end/src/scanner/errors.dart' |
14 show ScannerErrorCode, translateErrorToken; | 16 show ScannerErrorCode, translateErrorToken; |
15 import 'package:test/test.dart'; | 17 import 'package:test/test.dart'; |
16 import 'package:test_reflective_loader/test_reflective_loader.dart'; | 18 import 'package:test_reflective_loader/test_reflective_loader.dart'; |
17 | 19 |
18 import 'scanner_test.dart'; | 20 import 'scanner_test.dart'; |
19 | 21 |
20 main() { | 22 main() { |
21 defineReflectiveSuite(() { | 23 defineReflectiveSuite(() { |
(...skipping 25 matching lines...) Expand all Loading... |
47 // Dart, remove this flag. | 49 // Dart, remove this flag. |
48 fasta.ScannerResult result = fasta.scanString(source, | 50 fasta.ScannerResult result = fasta.scanString(source, |
49 includeComments: true, | 51 includeComments: true, |
50 recover: ((List<int> bytes, fasta.Token tokens, List<int> lineStarts) { | 52 recover: ((List<int> bytes, fasta.Token tokens, List<int> lineStarts) { |
51 // perform recovery as a separate step | 53 // perform recovery as a separate step |
52 // so that the token stream can be validated before and after recovery | 54 // so that the token stream can be validated before and after recovery |
53 return tokens; | 55 return tokens; |
54 })); | 56 })); |
55 fasta.Token tokens = result.tokens; | 57 fasta.Token tokens = result.tokens; |
56 assertValidTokenStream(tokens); | 58 assertValidTokenStream(tokens); |
| 59 assertValidBeginTokens(tokens); |
57 if (result.hasErrors) { | 60 if (result.hasErrors) { |
58 List<int> bytes = UTF8.encode(source); | 61 List<int> bytes = UTF8.encode(source); |
59 tokens = defaultRecoveryStrategy(bytes, tokens, result.lineStarts); | 62 tokens = defaultRecoveryStrategy(bytes, tokens, result.lineStarts); |
60 assertValidTokenStream(tokens); | 63 assertValidTokenStream(tokens, errorsFirst: true); |
61 } | 64 } |
62 return extractErrors(tokens, listener); | 65 return extractErrors(tokens, listener); |
63 } | 66 } |
64 | 67 |
65 @override | 68 @override |
66 @failingTest | 69 @failingTest |
67 void test_ampersand_ampersand_eq() { | 70 void test_ampersand_ampersand_eq() { |
68 // TODO(paulberry,ahe): Fasta scanner doesn't support lazy assignment | 71 // TODO(paulberry,ahe): Fasta scanner doesn't support lazy assignment |
69 // operators. | 72 // operators. |
70 super.test_ampersand_ampersand_eq(); | 73 super.test_ampersand_ampersand_eq(); |
(...skipping 16 matching lines...) Expand all Loading... |
87 } | 90 } |
88 | 91 |
89 @override | 92 @override |
90 @failingTest | 93 @failingTest |
91 void test_comment_generic_method_type_list() { | 94 void test_comment_generic_method_type_list() { |
92 // TODO(paulberry,ahe): Fasta scanner doesn't support generic comment | 95 // TODO(paulberry,ahe): Fasta scanner doesn't support generic comment |
93 // syntax. | 96 // syntax. |
94 super.test_comment_generic_method_type_list(); | 97 super.test_comment_generic_method_type_list(); |
95 } | 98 } |
96 | 99 |
97 @override | 100 void _assertOpenClosePair(String source) { |
98 @failingTest | 101 fasta.BeginGroupToken open = _scan(source); |
99 void test_mismatched_closer() { | 102 fasta.Token close = open.next; |
100 // TODO(danrubel): investigate and fix | 103 expect(close.next.isEof, isTrue); |
101 super.test_mismatched_closer(); | 104 expect(open.endGroup, close); |
| 105 expect(open.isSynthetic, isFalse); |
| 106 expect(close.isSynthetic, isFalse); |
| 107 } |
| 108 |
| 109 void _assertOpenOnly(String source) { |
| 110 fasta.BeginGroupToken open = _scan(source); |
| 111 fasta.Token close = open.next; |
| 112 expect(close.next.isEof, isTrue); |
| 113 expect(open.endGroup, close); |
| 114 expect(open.isSynthetic, isFalse); |
| 115 expect(close.isSynthetic, isTrue); |
| 116 } |
| 117 |
| 118 void test_lt() { |
| 119 // fasta does not automatically insert a closer for '<' |
| 120 // because it could be part of an expression rather than an opener |
| 121 fasta.BeginGroupToken lt = _scan('<'); |
| 122 expect(lt.next.isEof, isTrue); |
| 123 expect(lt.isSynthetic, isFalse); |
| 124 } |
| 125 |
| 126 void test_lt_gt() { |
| 127 _assertOpenClosePair('< >'); |
102 } | 128 } |
103 | 129 |
104 @override | 130 @override |
105 @failingTest | 131 void test_open_curly_bracket() { |
106 void test_mismatched_opener() { | 132 _assertOpenOnly('{'); |
107 // TODO(danrubel): investigate and fix | 133 } |
108 super.test_mismatched_opener(); | 134 |
| 135 void test_open_curly_bracket_with_close() { |
| 136 _assertOpenClosePair('{ }'); |
| 137 } |
| 138 |
| 139 void test_open_paren() { |
| 140 _assertOpenOnly('('); |
| 141 } |
| 142 |
| 143 void test_open_paren_with_close() { |
| 144 _assertOpenClosePair('( )'); |
| 145 } |
| 146 |
| 147 void test_open_square_bracket() { |
| 148 _assertOpenOnly('['); |
| 149 } |
| 150 |
| 151 void test_open_square_bracket_with_close() { |
| 152 _assertOpenClosePair('[ ]'); |
109 } | 153 } |
110 | 154 |
111 @override | 155 @override |
112 @failingTest | 156 void test_mismatched_closer() { |
113 void test_mismatched_opener_in_interpolation() { | 157 // When openers and closers are mismatched, |
114 // TODO(danrubel): investigate and fix | 158 // fasta favors considering the opener to be mismatched, |
115 super.test_mismatched_opener_in_interpolation(); | 159 // and inserts synthetic closers as needed. |
| 160 // `(])` is parsed as `()])` where the first `)` is synthetic |
| 161 // and the trailing `])` are unmatched. |
| 162 fasta.BeginGroupToken openParen = _scan('(])'); |
| 163 fasta.Token closeParen = openParen.next; |
| 164 fasta.Token closeBracket = closeParen.next; |
| 165 fasta.Token closeParen2 = closeBracket.next; |
| 166 fasta.Token eof = closeParen2.next; |
| 167 |
| 168 expect(openParen.endToken, same(closeParen)); |
| 169 expect(closeParen.isSynthetic, isTrue); |
| 170 expect(eof.isEof, isTrue); |
116 } | 171 } |
117 | 172 |
118 @override | 173 @override |
| 174 void test_mismatched_opener() { |
| 175 // When openers and closers are mismatched, |
| 176 // fasta favors considering the opener to be mismatched |
| 177 // and inserts synthetic closers as needed. |
| 178 // `([)` is parsed as `([])` where `]` is synthetic. |
| 179 fasta.BeginGroupToken openParen = _scan('([)'); |
| 180 fasta.BeginGroupToken openBracket = openParen.next; |
| 181 fasta.Token closeBracket = openBracket.next; // <-- synthetic |
| 182 fasta.Token closeParen = closeBracket.next; |
| 183 fasta.Token eof = closeParen.next; |
| 184 |
| 185 expect(openParen.endToken, same(closeParen)); |
| 186 expect(closeParen.isSynthetic, isFalse); |
| 187 expect(openBracket.endToken, same(closeBracket)); |
| 188 expect(closeBracket.isSynthetic, isTrue); |
| 189 expect(eof.isEof, isTrue); |
| 190 } |
| 191 |
| 192 @override |
| 193 void test_mismatched_opener_in_interpolation() { |
| 194 // When openers and closers are mismatched, |
| 195 // fasta favors considering the opener to be mismatched |
| 196 // and inserts synthetic closers as needed. |
| 197 // r'"${({(}}"' is parsed as r'"${({()})}"' |
| 198 // where both ')' are synthetic |
| 199 var stringStart = _scan(r'"${({(}}"'); |
| 200 var interpolationStart = stringStart.next as fasta.BeginGroupToken; |
| 201 var openParen1 = interpolationStart.next as fasta.BeginGroupToken; |
| 202 var openBrace = openParen1.next as fasta.BeginGroupToken; |
| 203 var openParen2 = openBrace.next as fasta.BeginGroupToken; |
| 204 var closeParen2 = openParen2.next; |
| 205 var closeBrace = closeParen2.next; |
| 206 var closeParen1 = closeBrace.next; |
| 207 var interpolationEnd = closeParen1.next; |
| 208 var stringEnd = interpolationEnd.next; |
| 209 var eof = stringEnd.next; |
| 210 |
| 211 expect(interpolationStart.endToken, same(interpolationEnd)); |
| 212 expect(interpolationEnd.isSynthetic, isFalse); |
| 213 expect(openParen1.endToken, same(closeParen1)); |
| 214 expect(closeParen1.isSynthetic, isTrue); |
| 215 expect(openBrace.endToken, same(closeBrace)); |
| 216 expect(closeBrace.isSynthetic, isFalse); |
| 217 expect(openParen2.endToken, same(closeParen2)); |
| 218 expect(closeParen2.isSynthetic, isTrue); |
| 219 expect(eof.isEof, isTrue); |
| 220 } |
| 221 |
| 222 @override |
119 @failingTest | 223 @failingTest |
120 void test_string_multi_unterminated() { | 224 void test_string_multi_unterminated() { |
121 // See defaultRecoveryStrategy recoverString | 225 // See defaultRecoveryStrategy recoverString |
122 super.test_string_multi_unterminated(); | 226 super.test_string_multi_unterminated(); |
123 } | 227 } |
124 | 228 |
125 @override | 229 @override |
126 @failingTest | 230 @failingTest |
127 void test_string_multi_unterminated_interpolation_block() { | 231 void test_string_multi_unterminated_interpolation_block() { |
128 // See defaultRecoveryStrategy recoverString | 232 // See defaultRecoveryStrategy recoverString |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
192 super.test_string_simple_unterminated_interpolation_block(); | 296 super.test_string_simple_unterminated_interpolation_block(); |
193 } | 297 } |
194 | 298 |
195 @override | 299 @override |
196 @failingTest | 300 @failingTest |
197 void test_string_simple_unterminated_interpolation_identifier() { | 301 void test_string_simple_unterminated_interpolation_identifier() { |
198 // See defaultRecoveryStrategy recoverString | 302 // See defaultRecoveryStrategy recoverString |
199 super.test_string_simple_unterminated_interpolation_identifier(); | 303 super.test_string_simple_unterminated_interpolation_identifier(); |
200 } | 304 } |
201 | 305 |
202 @failingTest | |
203 @override | 306 @override |
204 void test_unmatched_openers() { | 307 void test_unmatched_openers() { |
205 // fasta recovery inserts closers | 308 // fasta inserts missing closers except for '<' |
206 var openBrace = _scan('{[(<') as analyzer.BeginToken; | 309 var openBrace = _scan('{[(<') as fasta.BeginGroupToken; |
207 var openBracket = openBrace.next as analyzer.BeginToken; | 310 var openBracket = openBrace.next as fasta.BeginGroupToken; |
208 var openParen = openBracket.next as analyzer.BeginToken; | 311 var openParen = openBracket.next as fasta.BeginGroupToken; |
209 var openLT = openParen.next as analyzer.BeginToken; | 312 var openLT = openParen.next as fasta.BeginGroupToken; |
210 var closeGT = openLT.next; | 313 var closeParen = openLT.next; |
211 var closeParen = closeGT.next; | |
212 var closeBracket = closeParen.next; | 314 var closeBracket = closeParen.next; |
213 var closeBrace = closeBracket.next; | 315 var closeBrace = closeBracket.next; |
214 expect(closeBrace.next.type, analyzer.TokenType.EOF); | 316 var eof = closeBrace.next; |
215 | 317 |
216 expect(openBrace.endToken, closeBrace); | 318 expect(openBrace.endGroup, same(closeBrace)); |
217 expect(openBracket.endToken, closeBracket); | 319 expect(openBracket.endGroup, same(closeBracket)); |
218 expect(openParen.endToken, closeParen); | 320 expect(openParen.endGroup, same(closeParen)); |
219 expect(openLT.endToken, closeGT); | 321 expect(eof.isEof, true); |
220 } | 322 } |
221 | 323 |
222 analyzer.Token _scan(String source, | 324 analyzer.Token _scan(String source, |
223 {bool genericMethodComments: false, | 325 {bool genericMethodComments: false, |
224 bool lazyAssignmentOperators: false}) { | 326 bool lazyAssignmentOperators: false}) { |
225 ErrorListener listener = new ErrorListener(); | 327 ErrorListener listener = new ErrorListener(); |
226 analyzer.Token token = scanWithListener(source, listener, | 328 analyzer.Token token = scanWithListener(source, listener, |
227 genericMethodComments: genericMethodComments, | 329 genericMethodComments: genericMethodComments, |
228 lazyAssignmentOperators: lazyAssignmentOperators); | 330 lazyAssignmentOperators: lazyAssignmentOperators); |
229 listener.assertNoErrors(); | 331 listener.assertNoErrors(); |
(...skipping 13 matching lines...) Expand all Loading... |
243 } | 345 } |
244 if (!token.previousToken.isEof) { | 346 if (!token.previousToken.isEof) { |
245 var head = new fasta.SymbolToken(EOF_INFO, -1); | 347 var head = new fasta.SymbolToken(EOF_INFO, -1); |
246 token.previous = head; | 348 token.previous = head; |
247 head.next = token; | 349 head.next = token; |
248 } | 350 } |
249 return token; | 351 return token; |
250 } | 352 } |
251 | 353 |
252 /// Assert that the tokens in the stream are correctly connected prev/next. | 354 /// Assert that the tokens in the stream are correctly connected prev/next. |
253 void assertValidTokenStream(fasta.Token firstToken) { | 355 void assertValidTokenStream(fasta.Token firstToken, |
| 356 {bool errorsFirst: false}) { |
254 fasta.Token token = firstToken; | 357 fasta.Token token = firstToken; |
255 fasta.Token previous = token.previousToken; | 358 fasta.Token previous = token.previousToken; |
256 expect(previous.isEof, isTrue, reason: 'Missing leading EOF'); | 359 expect(previous.isEof, isTrue, reason: 'Missing leading EOF'); |
257 expect(previous.next, token, reason: 'Invalid leading EOF'); | 360 expect(previous.next, token, reason: 'Invalid leading EOF'); |
258 expect(previous.previous, previous, reason: 'Invalid leading EOF'); | 361 expect(previous.previous, previous, reason: 'Invalid leading EOF'); |
| 362 if (errorsFirst) { |
| 363 while (!token.isEof && token is fasta.ErrorToken) { |
| 364 token = token.next; |
| 365 } |
| 366 } |
| 367 var isNotErrorToken = isNot(new isInstanceOf<fasta.ErrorToken>()); |
259 while (!token.isEof) { | 368 while (!token.isEof) { |
| 369 if (errorsFirst) expect(token, isNotErrorToken); |
260 previous = token; | 370 previous = token; |
261 token = token.next; | 371 token = token.next; |
262 expect(token, isNotNull, reason: previous.toString()); | 372 expect(token, isNotNull, reason: previous.toString()); |
263 expect(token.previous, previous, reason: token.toString()); | 373 expect(token.previous, previous, reason: token.toString()); |
264 } | 374 } |
265 expect(token.next, token, reason: 'Invalid trailing EOF'); | 375 expect(token.next, token, reason: 'Invalid trailing EOF'); |
266 } | 376 } |
| 377 |
| 378 /// Assert that all [fasta.BeginGroupToken] has a valid `endGroup` |
| 379 /// that is in the stream. |
| 380 void assertValidBeginTokens(fasta.Token firstToken) { |
| 381 var openerStack = <fasta.BeginGroupToken>[]; |
| 382 fasta.BeginGroupToken lastClosedGroup; |
| 383 fasta.Token token = firstToken; |
| 384 while (!token.isEof) { |
| 385 if (token is fasta.BeginGroupToken) { |
| 386 if (token.lexeme != '<') |
| 387 expect(token.endGroup, isNotNull, reason: token.lexeme); |
| 388 if (token.endGroup != null) openerStack.add(token); |
| 389 } else if (openerStack.isNotEmpty && openerStack.last.endGroup == token) { |
| 390 lastClosedGroup = openerStack.removeLast(); |
| 391 expect(token.isSynthetic, token.next is fasta.UnmatchedToken, |
| 392 reason: 'Expect synthetic closer then error token'); |
| 393 } else if (token is fasta.UnmatchedToken) { |
| 394 expect(lastClosedGroup?.endGroup?.next, same(token), |
| 395 reason: 'Unexpected error token for group: $lastClosedGroup'); |
| 396 expect(token.begin, lastClosedGroup); |
| 397 } |
| 398 token = token.next; |
| 399 } |
| 400 expect(openerStack, isEmpty, reason: 'Missing closers'); |
| 401 } |
267 } | 402 } |
OLD | NEW |