OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 part of scanner; | |
6 | |
7 class FormalParameterType { | |
8 final String type; | |
9 const FormalParameterType(this.type); | |
10 bool get isRequired => this == REQUIRED; | |
11 bool get isPositional => this == POSITIONAL; | |
12 bool get isNamed => this == NAMED; | |
13 static final REQUIRED = const FormalParameterType('required'); | |
14 static final POSITIONAL = const FormalParameterType('positional'); | |
15 static final NAMED = const FormalParameterType('named'); | |
16 } | |
17 | |
18 /** | |
19 * An event generating parser of Dart programs. This parser expects | |
20 * all tokens in a linked list (aka a token stream). | |
21 * | |
22 * The class [Scanner] is used to generate a token stream. See the | |
23 * file scanner.dart. | |
24 * | |
25 * Subclasses of the class [Listener] are used to listen to events. | |
26 * | |
27 * Most methods of this class belong in one of two major categories: | |
28 * parse metods and peek methods. Parse methods all have the prefix | |
29 * parse, and peek methods all have the prefix peek. | |
30 * | |
31 * Parse methods generate events (by calling methods on [listener]) | |
32 * and return the next token to parse. Peek methods do not generate | |
33 * events (except for errors) and may return null. | |
34 * | |
35 * Parse methods are generally named parseGrammarProductionSuffix. The | |
36 * suffix can be one of "opt", or "star". "opt" means zero or one | |
37 * matches, "star" means zero or more matches. For example, | |
38 * [parseMetadataStar] corresponds to this grammar snippet: [: | |
39 * metadata* :], and [parseTypeOpt] corresponds to: [: type? :]. | |
40 */ | |
41 class Parser { | |
42 final Listener listener; | |
43 bool mayParseFunctionExpressions = true; | |
44 bool yieldIsKeyword = false; | |
45 bool awaitIsKeyword = false; | |
46 | |
47 Parser(this.listener); | |
48 | |
49 Token parseUnit(Token token) { | |
50 listener.beginCompilationUnit(token); | |
51 int count = 0; | |
52 while (!identical(token.kind, EOF_TOKEN)) { | |
53 token = parseTopLevelDeclaration(token); | |
54 listener.endTopLevelDeclaration(token); | |
55 count++; | |
56 } | |
57 listener.endCompilationUnit(count, token); | |
58 return token; | |
59 } | |
60 | |
61 Token parseTopLevelDeclaration(Token token) { | |
62 token = parseMetadataStar(token); | |
63 final String value = token.stringValue; | |
64 if ((identical(value, 'abstract') && optional('class', token.next)) | |
65 || identical(value, 'class')) { | |
66 return parseClassOrNamedMixinApplication(token); | |
67 } else if (identical(value, 'typedef')) { | |
68 return parseTypedef(token); | |
69 } else if (identical(value, 'library')) { | |
70 return parseLibraryName(token); | |
71 } else if (identical(value, 'import')) { | |
72 return parseImport(token); | |
73 } else if (identical(value, 'export')) { | |
74 return parseExport(token); | |
75 } else if (identical(value, 'part')) { | |
76 return parsePartOrPartOf(token); | |
77 } else { | |
78 return parseTopLevelMember(token); | |
79 } | |
80 } | |
81 | |
82 /// library qualified ';' | |
83 Token parseLibraryName(Token token) { | |
84 Token libraryKeyword = token; | |
85 listener.beginLibraryName(libraryKeyword); | |
86 assert(optional('library', token)); | |
87 token = parseQualified(token.next); | |
88 Token semicolon = token; | |
89 token = expect(';', token); | |
90 listener.endLibraryName(libraryKeyword, semicolon); | |
91 return token; | |
92 } | |
93 | |
94 /// import uri (as identifier)? combinator* ';' | |
95 Token parseImport(Token token) { | |
96 Token importKeyword = token; | |
97 listener.beginImport(importKeyword); | |
98 assert(optional('import', token)); | |
99 token = parseLiteralStringOrRecoverExpression(token.next); | |
100 Token deferredKeyword; | |
101 if (optional('deferred', token)) { | |
102 deferredKeyword = token; | |
103 token = token.next; | |
104 } | |
105 Token asKeyword; | |
106 if (optional('as', token)) { | |
107 asKeyword = token; | |
108 token = parseIdentifier(token.next); | |
109 } | |
110 token = parseCombinators(token); | |
111 Token semicolon = token; | |
112 token = expect(';', token); | |
113 listener.endImport(importKeyword, deferredKeyword, asKeyword, semicolon); | |
114 return token; | |
115 } | |
116 | |
117 /// export uri combinator* ';' | |
118 Token parseExport(Token token) { | |
119 Token exportKeyword = token; | |
120 listener.beginExport(exportKeyword); | |
121 assert(optional('export', token)); | |
122 token = parseLiteralStringOrRecoverExpression(token.next); | |
123 token = parseCombinators(token); | |
124 Token semicolon = token; | |
125 token = expect(';', token); | |
126 listener.endExport(exportKeyword, semicolon); | |
127 return token; | |
128 } | |
129 | |
130 Token parseCombinators(Token token) { | |
131 listener.beginCombinators(token); | |
132 int count = 0; | |
133 while (true) { | |
134 String value = token.stringValue; | |
135 if (identical('hide', value)) { | |
136 token = parseHide(token); | |
137 } else if (identical('show', value)) { | |
138 token = parseShow(token); | |
139 } else { | |
140 listener.endCombinators(count); | |
141 break; | |
142 } | |
143 count++; | |
144 } | |
145 return token; | |
146 } | |
147 | |
148 /// hide identifierList | |
149 Token parseHide(Token token) { | |
150 Token hideKeyword = token; | |
151 listener.beginHide(hideKeyword); | |
152 assert(optional('hide', token)); | |
153 token = parseIdentifierList(token.next); | |
154 listener.endHide(hideKeyword); | |
155 return token; | |
156 } | |
157 | |
158 /// show identifierList | |
159 Token parseShow(Token token) { | |
160 Token showKeyword = token; | |
161 listener.beginShow(showKeyword); | |
162 assert(optional('show', token)); | |
163 token = parseIdentifierList(token.next); | |
164 listener.endShow(showKeyword); | |
165 return token; | |
166 } | |
167 | |
168 /// identifier (, identifier)* | |
169 Token parseIdentifierList(Token token) { | |
170 listener.beginIdentifierList(token); | |
171 token = parseIdentifier(token); | |
172 int count = 1; | |
173 while (optional(',', token)) { | |
174 token = parseIdentifier(token.next); | |
175 count++; | |
176 } | |
177 listener.endIdentifierList(count); | |
178 return token; | |
179 } | |
180 | |
181 /// type (, type)* | |
182 Token parseTypeList(Token token) { | |
183 listener.beginTypeList(token); | |
184 token = parseType(token); | |
185 int count = 1; | |
186 while (optional(',', token)) { | |
187 token = parseType(token.next); | |
188 count++; | |
189 } | |
190 listener.endTypeList(count); | |
191 return token; | |
192 } | |
193 | |
194 Token parsePartOrPartOf(Token token) { | |
195 assert(optional('part', token)); | |
196 if (optional('of', token.next)) { | |
197 return parsePartOf(token); | |
198 } else { | |
199 return parsePart(token); | |
200 } | |
201 } | |
202 | |
203 Token parsePart(Token token) { | |
204 Token partKeyword = token; | |
205 listener.beginPart(token); | |
206 assert(optional('part', token)); | |
207 token = parseLiteralStringOrRecoverExpression(token.next); | |
208 Token semicolon = token; | |
209 token = expect(';', token); | |
210 listener.endPart(partKeyword, semicolon); | |
211 return token; | |
212 } | |
213 | |
214 Token parsePartOf(Token token) { | |
215 listener.beginPartOf(token); | |
216 assert(optional('part', token)); | |
217 assert(optional('of', token.next)); | |
218 Token partKeyword = token; | |
219 token = parseQualified(token.next.next); | |
220 Token semicolon = token; | |
221 token = expect(';', token); | |
222 listener.endPartOf(partKeyword, semicolon); | |
223 return token; | |
224 } | |
225 | |
226 Token parseMetadataStar(Token token, {bool forParameter: false}) { | |
227 listener.beginMetadataStar(token); | |
228 int count = 0; | |
229 while (optional('@', token)) { | |
230 token = parseMetadata(token); | |
231 count++; | |
232 } | |
233 listener.endMetadataStar(count, forParameter); | |
234 return token; | |
235 } | |
236 | |
237 /** | |
238 * Parse | |
239 * [: '@' qualified (‘.’ identifier)? (arguments)? :] | |
240 */ | |
241 Token parseMetadata(Token token) { | |
242 listener.beginMetadata(token); | |
243 Token atToken = token; | |
244 assert(optional('@', token)); | |
245 token = parseIdentifier(token.next); | |
246 token = parseQualifiedRestOpt(token); | |
247 token = parseTypeArgumentsOpt(token); | |
248 Token period = null; | |
249 if (optional('.', token)) { | |
250 period = token; | |
251 token = parseIdentifier(token.next); | |
252 } | |
253 token = parseArgumentsOpt(token); | |
254 listener.endMetadata(atToken, period, token); | |
255 return token; | |
256 } | |
257 | |
258 Token parseTypedef(Token token) { | |
259 Token typedefKeyword = token; | |
260 if (optional('=', peekAfterType(token.next))) { | |
261 // TODO(aprelev@gmail.com): Remove deprecated 'typedef' mixin application, | |
262 // remove corresponding diagnostic from members.dart. | |
263 listener.beginNamedMixinApplication(token); | |
264 token = parseIdentifier(token.next); | |
265 token = parseTypeVariablesOpt(token); | |
266 token = expect('=', token); | |
267 token = parseModifiers(token); | |
268 token = parseMixinApplication(token); | |
269 Token implementsKeyword = null; | |
270 if (optional('implements', token)) { | |
271 implementsKeyword = token; | |
272 token = parseTypeList(token.next); | |
273 } | |
274 listener.endNamedMixinApplication( | |
275 typedefKeyword, implementsKeyword, token); | |
276 } else { | |
277 listener.beginFunctionTypeAlias(token); | |
278 token = parseReturnTypeOpt(token.next); | |
279 token = parseIdentifier(token); | |
280 token = parseTypeVariablesOpt(token); | |
281 token = parseFormalParameters(token); | |
282 listener.endFunctionTypeAlias(typedefKeyword, token); | |
283 } | |
284 return expect(';', token); | |
285 } | |
286 | |
287 Token parseMixinApplication(Token token) { | |
288 listener.beginMixinApplication(token); | |
289 token = parseType(token); | |
290 token = expect('with', token); | |
291 token = parseTypeList(token); | |
292 listener.endMixinApplication(); | |
293 return token; | |
294 } | |
295 | |
296 Token parseReturnTypeOpt(Token token) { | |
297 if (identical(token.stringValue, 'void')) { | |
298 listener.handleVoidKeyword(token); | |
299 return token.next; | |
300 } else { | |
301 return parseTypeOpt(token); | |
302 } | |
303 } | |
304 | |
305 Token parseFormalParametersOpt(Token token) { | |
306 if (optional('(', token)) { | |
307 return parseFormalParameters(token); | |
308 } else { | |
309 listener.handleNoFormalParameters(token); | |
310 return token; | |
311 } | |
312 } | |
313 | |
314 Token parseFormalParameters(Token token) { | |
315 Token begin = token; | |
316 listener.beginFormalParameters(begin); | |
317 expect('(', token); | |
318 int parameterCount = 0; | |
319 if (optional(')', token.next)) { | |
320 listener.endFormalParameters(parameterCount, begin, token.next); | |
321 return token.next.next; | |
322 } | |
323 do { | |
324 ++parameterCount; | |
325 token = token.next; | |
326 String value = token.stringValue; | |
327 if (identical(value, '[')) { | |
328 token = parseOptionalFormalParameters(token, false); | |
329 break; | |
330 } else if (identical(value, '{')) { | |
331 token = parseOptionalFormalParameters(token, true); | |
332 break; | |
333 } | |
334 token = parseFormalParameter(token, FormalParameterType.REQUIRED); | |
335 } while (optional(',', token)); | |
336 listener.endFormalParameters(parameterCount, begin, token); | |
337 return expect(')', token); | |
338 } | |
339 | |
340 Token parseFormalParameter(Token token, FormalParameterType type) { | |
341 token = parseMetadataStar(token, forParameter: true); | |
342 listener.beginFormalParameter(token); | |
343 token = parseModifiers(token); | |
344 // TODO(ahe): Validate that there are formal parameters if void. | |
345 token = parseReturnTypeOpt(token); | |
346 Token thisKeyword = null; | |
347 if (optional('this', token)) { | |
348 thisKeyword = token; | |
349 // TODO(ahe): Validate field initializers are only used in | |
350 // constructors, and not for function-typed arguments. | |
351 token = expect('.', token.next); | |
352 } | |
353 token = parseIdentifier(token); | |
354 if (optional('(', token)) { | |
355 token = parseFormalParameters(token); | |
356 listener.handleFunctionTypedFormalParameter(token); | |
357 } | |
358 String value = token.stringValue; | |
359 if ((identical('=', value)) || (identical(':', value))) { | |
360 // TODO(ahe): Validate that these are only used for optional parameters. | |
361 Token equal = token; | |
362 token = parseExpression(token.next); | |
363 listener.handleValuedFormalParameter(equal, token); | |
364 if (type.isRequired) { | |
365 listener.reportError(equal, | |
366 MessageKind.REQUIRED_PARAMETER_WITH_DEFAULT); | |
367 } else if (type.isNamed && identical('=', value)) { | |
368 listener.reportError(equal, MessageKind.NAMED_PARAMETER_WITH_EQUALS); | |
369 } else if (type.isPositional && identical(':', value)) { | |
370 listener.reportError(equal, | |
371 MessageKind.POSITIONAL_PARAMETER_WITH_EQUALS); | |
372 } | |
373 } | |
374 listener.endFormalParameter(thisKeyword); | |
375 return token; | |
376 } | |
377 | |
378 Token parseOptionalFormalParameters(Token token, bool isNamed) { | |
379 Token begin = token; | |
380 listener.beginOptionalFormalParameters(begin); | |
381 assert((isNamed && optional('{', token)) || optional('[', token)); | |
382 int parameterCount = 0; | |
383 do { | |
384 token = token.next; | |
385 var type = isNamed ? FormalParameterType.NAMED | |
386 : FormalParameterType.POSITIONAL; | |
387 token = parseFormalParameter(token, type); | |
388 ++parameterCount; | |
389 } while (optional(',', token)); | |
390 listener.endOptionalFormalParameters(parameterCount, begin, token); | |
391 if (isNamed) { | |
392 return expect('}', token); | |
393 } else { | |
394 return expect(']', token); | |
395 } | |
396 } | |
397 | |
398 Token parseTypeOpt(Token token) { | |
399 String value = token.stringValue; | |
400 Token peek = peekAfterIfType(token); | |
401 if (peek != null && (peek.isIdentifier() || optional('this', peek))) { | |
402 return parseType(token); | |
403 } | |
404 listener.handleNoType(token); | |
405 return token; | |
406 } | |
407 | |
408 bool isValidTypeReference(Token token) { | |
409 final kind = token.kind; | |
410 if (identical(kind, IDENTIFIER_TOKEN)) return true; | |
411 if (identical(kind, KEYWORD_TOKEN)) { | |
412 Keyword keyword = (token as KeywordToken).keyword; | |
413 String value = keyword.syntax; | |
414 return keyword.isPseudo | |
415 || (identical(value, 'dynamic')) | |
416 || (identical(value, 'void')); | |
417 } | |
418 return false; | |
419 } | |
420 | |
421 Token parseQualified(Token token) { | |
422 token = parseIdentifier(token); | |
423 while (optional('.', token)) { | |
424 token = parseQualifiedRest(token); | |
425 } | |
426 return token; | |
427 } | |
428 | |
429 Token parseQualifiedRestOpt(Token token) { | |
430 if (optional('.', token)) { | |
431 return parseQualifiedRest(token); | |
432 } else { | |
433 return token; | |
434 } | |
435 } | |
436 | |
437 Token parseQualifiedRest(Token token) { | |
438 assert(optional('.', token)); | |
439 Token period = token; | |
440 token = parseIdentifier(token.next); | |
441 listener.handleQualified(period); | |
442 return token; | |
443 } | |
444 | |
445 Token skipBlock(Token token) { | |
446 if (!optional('{', token)) { | |
447 return listener.expectedBlockToSkip(token); | |
448 } | |
449 BeginGroupToken beginGroupToken = token; | |
450 Token endGroup = beginGroupToken.endGroup; | |
451 if (endGroup == null) { | |
452 return listener.unmatched(beginGroupToken); | |
453 } else if (!identical(endGroup.kind, $CLOSE_CURLY_BRACKET)) { | |
454 return listener.unmatched(beginGroupToken); | |
455 } | |
456 return beginGroupToken.endGroup; | |
457 } | |
458 | |
459 Token parseClassOrNamedMixinApplication(Token token) { | |
460 Token begin = token; | |
461 Token abstractKeyword; | |
462 if (optional('abstract', token)) { | |
463 abstractKeyword = token; | |
464 token = token.next; | |
465 } | |
466 Token classKeyword = token; | |
467 var isMixinApplication = optional('=', peekAfterType(token.next)); | |
468 if (isMixinApplication) { | |
469 listener.beginNamedMixinApplication(begin); | |
470 token = parseIdentifier(token.next); | |
471 token = parseTypeVariablesOpt(token); | |
472 token = expect('=', token); | |
473 } else { | |
474 listener.beginClassDeclaration(begin); | |
475 } | |
476 | |
477 // TODO(aprelev@gmail.com): Once 'typedef' named mixin application is | |
478 // removed, move modifiers for named mixin application to the bottom of | |
479 // listener stack. This is so stacks for class declaration and named | |
480 // mixin application look similar. | |
481 int modifierCount = 0; | |
482 if (abstractKeyword != null) { | |
483 parseModifier(abstractKeyword); | |
484 modifierCount++; | |
485 } | |
486 listener.handleModifiers(modifierCount); | |
487 | |
488 if (isMixinApplication) { | |
489 return parseNamedMixinApplication(token, classKeyword); | |
490 } else { | |
491 return parseClass(begin, classKeyword); | |
492 } | |
493 } | |
494 | |
495 Token parseNamedMixinApplication(Token token, Token classKeyword) { | |
496 token = parseMixinApplication(token); | |
497 Token implementsKeyword = null; | |
498 if (optional('implements', token)) { | |
499 implementsKeyword = token; | |
500 token = parseTypeList(token.next); | |
501 } | |
502 listener.endNamedMixinApplication( | |
503 classKeyword, implementsKeyword, token); | |
504 return expect(';', token); | |
505 } | |
506 | |
507 Token parseClass(Token begin, Token classKeyword) { | |
508 Token token = parseIdentifier(classKeyword.next); | |
509 token = parseTypeVariablesOpt(token); | |
510 Token extendsKeyword; | |
511 if (optional('extends', token)) { | |
512 extendsKeyword = token; | |
513 if (optional('with', peekAfterType(token.next))) { | |
514 token = parseMixinApplication(token.next); | |
515 } else { | |
516 token = parseType(token.next); | |
517 } | |
518 } else { | |
519 extendsKeyword = null; | |
520 listener.handleNoType(token); | |
521 } | |
522 Token implementsKeyword; | |
523 int interfacesCount = 0; | |
524 if (optional('implements', token)) { | |
525 implementsKeyword = token; | |
526 do { | |
527 token = parseType(token.next); | |
528 ++interfacesCount; | |
529 } while (optional(',', token)); | |
530 } | |
531 token = parseClassBody(token); | |
532 listener.endClassDeclaration(interfacesCount, begin, extendsKeyword, | |
533 implementsKeyword, token); | |
534 return token.next; | |
535 } | |
536 | |
537 Token parseStringPart(Token token) { | |
538 if (identical(token.kind, STRING_TOKEN)) { | |
539 listener.handleStringPart(token); | |
540 return token.next; | |
541 } else { | |
542 return listener.expected('string', token); | |
543 } | |
544 } | |
545 | |
546 Token parseIdentifier(Token token) { | |
547 if (!token.isIdentifier()) { | |
548 token = listener.expectedIdentifier(token); | |
549 } | |
550 listener.handleIdentifier(token); | |
551 return token.next; | |
552 } | |
553 | |
554 Token expect(String string, Token token) { | |
555 if (!identical(string, token.stringValue)) { | |
556 return listener.expected(string, token); | |
557 } | |
558 return token.next; | |
559 } | |
560 | |
561 Token parseTypeVariable(Token token) { | |
562 listener.beginTypeVariable(token); | |
563 token = parseIdentifier(token); | |
564 if (optional('extends', token)) { | |
565 token = parseType(token.next); | |
566 } else { | |
567 listener.handleNoType(token); | |
568 } | |
569 listener.endTypeVariable(token); | |
570 return token; | |
571 } | |
572 | |
573 /** | |
574 * Returns true if the stringValue of the [token] is [value]. | |
575 */ | |
576 bool optional(String value, Token token) { | |
577 return identical(value, token.stringValue); | |
578 } | |
579 | |
580 /** | |
581 * Returns true if the stringValue of the [token] is either [value1], | |
582 * [value2], [value3], or [value4]. | |
583 */ | |
584 bool isOneOf4(Token token, | |
585 String value1, String value2, String value3, String value4) { | |
586 String stringValue = token.stringValue; | |
587 return identical(value1, stringValue) || | |
588 identical(value2, stringValue) || | |
589 identical(value3, stringValue) || | |
590 identical(value4, stringValue); | |
591 } | |
592 | |
593 bool notEofOrValue(String value, Token token) { | |
594 return !identical(token.kind, EOF_TOKEN) && | |
595 !identical(value, token.stringValue); | |
596 } | |
597 | |
598 Token parseType(Token token) { | |
599 Token begin = token; | |
600 if (isValidTypeReference(token)) { | |
601 token = parseIdentifier(token); | |
602 token = parseQualifiedRestOpt(token); | |
603 } else { | |
604 token = listener.expectedType(token); | |
605 } | |
606 token = parseTypeArgumentsOpt(token); | |
607 listener.endType(begin, token); | |
608 return token; | |
609 } | |
610 | |
611 Token parseTypeArgumentsOpt(Token token) { | |
612 return parseStuff(token, | |
613 (t) => listener.beginTypeArguments(t), | |
614 (t) => parseType(t), | |
615 (c, bt, et) => listener.endTypeArguments(c, bt, et), | |
616 (t) => listener.handleNoTypeArguments(t)); | |
617 } | |
618 | |
619 Token parseTypeVariablesOpt(Token token) { | |
620 return parseStuff(token, | |
621 (t) => listener.beginTypeVariables(t), | |
622 (t) => parseTypeVariable(t), | |
623 (c, bt, et) => listener.endTypeVariables(c, bt, et), | |
624 (t) => listener.handleNoTypeVariables(t)); | |
625 } | |
626 | |
627 // TODO(ahe): Clean this up. | |
628 Token parseStuff(Token token, Function beginStuff, Function stuffParser, | |
629 Function endStuff, Function handleNoStuff) { | |
630 if (optional('<', token)) { | |
631 Token begin = token; | |
632 beginStuff(begin); | |
633 int count = 0; | |
634 do { | |
635 token = stuffParser(token.next); | |
636 ++count; | |
637 } while (optional(',', token)); | |
638 Token next = token.next; | |
639 if (identical(token.stringValue, '>>')) { | |
640 token = new SymbolToken(GT_INFO, token.charOffset); | |
641 token.next = new SymbolToken(GT_INFO, token.charOffset + 1); | |
642 token.next.next = next; | |
643 } else if (identical(token.stringValue, '>>>')) { | |
644 token = new SymbolToken(GT_INFO, token.charOffset); | |
645 token.next = new SymbolToken(GT_GT_INFO, token.charOffset + 1); | |
646 token.next.next = next; | |
647 } | |
648 endStuff(count, begin, token); | |
649 return expect('>', token); | |
650 } | |
651 handleNoStuff(token); | |
652 return token; | |
653 } | |
654 | |
655 Token parseTopLevelMember(Token token) { | |
656 Token start = token; | |
657 listener.beginTopLevelMember(token); | |
658 | |
659 Link<Token> identifiers = findMemberName(token); | |
660 if (identifiers.isEmpty) { | |
661 return listener.expectedDeclaration(start); | |
662 } | |
663 Token name = identifiers.head; | |
664 identifiers = identifiers.tail; | |
665 Token getOrSet; | |
666 if (!identifiers.isEmpty) { | |
667 String value = identifiers.head.stringValue; | |
668 if ((identical(value, 'get')) || (identical(value, 'set'))) { | |
669 getOrSet = identifiers.head; | |
670 identifiers = identifiers.tail; | |
671 } | |
672 } | |
673 Token type; | |
674 if (!identifiers.isEmpty) { | |
675 if (isValidTypeReference(identifiers.head)) { | |
676 type = identifiers.head; | |
677 identifiers = identifiers.tail; | |
678 } | |
679 } | |
680 | |
681 token = name.next; | |
682 bool isField; | |
683 while (true) { | |
684 // Loop to allow the listener to rewrite the token stream for | |
685 // error handling. | |
686 final String value = token.stringValue; | |
687 if ((identical(value, '(')) || (identical(value, '{')) | |
688 || (identical(value, '=>'))) { | |
689 isField = false; | |
690 break; | |
691 } else if ((identical(value, '=')) || (identical(value, ','))) { | |
692 isField = true; | |
693 break; | |
694 } else if (identical(value, ';')) { | |
695 if (getOrSet != null) { | |
696 // If we found a "get" keyword, this must be an abstract | |
697 // getter. | |
698 isField = (!identical(getOrSet.stringValue, 'get')); | |
699 // TODO(ahe): This feels like a hack. | |
700 } else { | |
701 isField = true; | |
702 } | |
703 break; | |
704 } else { | |
705 token = listener.unexpected(token); | |
706 if (identical(token.kind, EOF_TOKEN)) return token; | |
707 } | |
708 } | |
709 var modifiers = identifiers.reverse(); | |
710 return isField | |
711 ? parseFields(start, modifiers, type, getOrSet, name, true) | |
712 : parseTopLevelMethod(start, modifiers, type, getOrSet, name); | |
713 } | |
714 | |
715 bool isVarFinalOrConst(Token token) { | |
716 String value = token.stringValue; | |
717 return identical('var', value) | |
718 || identical('final', value) | |
719 || identical('const', value); | |
720 } | |
721 | |
722 Token expectVarFinalOrConst(Link<Token> modifiers, | |
723 bool hasType, | |
724 bool allowStatic) { | |
725 int modifierCount = 0; | |
726 Token staticModifier; | |
727 if (allowStatic && !modifiers.isEmpty | |
728 && optional('static', modifiers.head)) { | |
729 staticModifier = modifiers.head; | |
730 modifierCount++; | |
731 parseModifier(staticModifier); | |
732 modifiers = modifiers.tail; | |
733 } | |
734 if (modifiers.isEmpty) { | |
735 listener.handleModifiers(modifierCount); | |
736 return null; | |
737 } | |
738 if (modifiers.tail.isEmpty) { | |
739 Token modifier = modifiers.head; | |
740 if (isVarFinalOrConst(modifier)) { | |
741 modifierCount++; | |
742 parseModifier(modifier); | |
743 listener.handleModifiers(modifierCount); | |
744 // TODO(ahe): The caller checks for "var Type name", perhaps we should | |
745 // check here instead. | |
746 return modifier; | |
747 } | |
748 } | |
749 | |
750 // Slow case to report errors. | |
751 List<Token> modifierList = modifiers.toList(); | |
752 Token varFinalOrConst = | |
753 modifierList.firstWhere(isVarFinalOrConst, orElse: () => null); | |
754 if (allowStatic && staticModifier == null) { | |
755 staticModifier = | |
756 modifierList.firstWhere( | |
757 (modifier) => optional('static', modifier), orElse: () => null); | |
758 if (staticModifier != null) { | |
759 modifierCount++; | |
760 parseModifier(staticModifier); | |
761 modifierList.remove(staticModifier); | |
762 } | |
763 } | |
764 bool hasTypeOrModifier = hasType; | |
765 if (varFinalOrConst != null) { | |
766 parseModifier(varFinalOrConst); | |
767 modifierCount++; | |
768 hasTypeOrModifier = true; | |
769 modifierList.remove(varFinalOrConst); | |
770 } | |
771 listener.handleModifiers(modifierCount); | |
772 var kind = hasTypeOrModifier | |
773 ? MessageKind.EXTRANEOUS_MODIFIER | |
774 : MessageKind.EXTRANEOUS_MODIFIER_REPLACE; | |
775 for (Token modifier in modifierList) { | |
776 listener.reportError(modifier, kind, {'modifier': modifier}); | |
777 } | |
778 return null; | |
779 } | |
780 | |
781 Token parseFields(Token start, | |
782 Link<Token> modifiers, | |
783 Token type, | |
784 Token getOrSet, | |
785 Token name, | |
786 bool isTopLevel) { | |
787 bool hasType = type != null; | |
788 Token varFinalOrConst = | |
789 expectVarFinalOrConst(modifiers, hasType, !isTopLevel); | |
790 bool isVar = false; | |
791 bool hasModifier = false; | |
792 if (varFinalOrConst != null) { | |
793 hasModifier = true; | |
794 isVar = optional('var', varFinalOrConst); | |
795 } | |
796 | |
797 if (getOrSet != null) { | |
798 var kind = (hasModifier || hasType) | |
799 ? MessageKind.EXTRANEOUS_MODIFIER | |
800 : MessageKind.EXTRANEOUS_MODIFIER_REPLACE; | |
801 listener.reportError(getOrSet, kind, {'modifier': getOrSet}); | |
802 } | |
803 | |
804 if (!hasType) { | |
805 listener.handleNoType(name); | |
806 } else if (optional('void', type)) { | |
807 listener.handleNoType(name); | |
808 // TODO(ahe): This error is reported twice, second time is from | |
809 // [parseVariablesDeclarationMaybeSemicolon] via | |
810 // [PartialFieldListElement.parseNode]. | |
811 listener.reportError(type, MessageKind.VOID_NOT_ALLOWED); | |
812 } else { | |
813 parseType(type); | |
814 if (isVar) { | |
815 listener.reportError( | |
816 modifiers.head, MessageKind.EXTRANEOUS_MODIFIER, | |
817 {'modifier': modifiers.head}); | |
818 } | |
819 } | |
820 | |
821 Token token = parseIdentifier(name); | |
822 | |
823 int fieldCount = 1; | |
824 token = parseVariableInitializerOpt(token); | |
825 while (optional(',', token)) { | |
826 token = parseIdentifier(token.next); | |
827 token = parseVariableInitializerOpt(token); | |
828 ++fieldCount; | |
829 } | |
830 Token semicolon = token; | |
831 token = expectSemicolon(token); | |
832 if (isTopLevel) { | |
833 listener.endTopLevelFields(fieldCount, start, semicolon); | |
834 } else { | |
835 listener.endFields(fieldCount, start, semicolon); | |
836 } | |
837 return token; | |
838 } | |
839 | |
840 Token parseTopLevelMethod(Token start, | |
841 Link<Token> modifiers, | |
842 Token type, | |
843 Token getOrSet, | |
844 Token name) { | |
845 Token externalModifier; | |
846 for (Token modifier in modifiers) { | |
847 if (externalModifier == null && optional('external', modifier)) { | |
848 externalModifier = modifier; | |
849 } else { | |
850 listener.reportError( | |
851 modifier, MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); | |
852 } | |
853 } | |
854 if (externalModifier != null) { | |
855 parseModifier(externalModifier); | |
856 listener.handleModifiers(1); | |
857 } else { | |
858 listener.handleModifiers(0); | |
859 } | |
860 | |
861 if (type == null) { | |
862 listener.handleNoType(name); | |
863 } else { | |
864 parseReturnTypeOpt(type); | |
865 } | |
866 Token token = parseIdentifier(name); | |
867 | |
868 token = parseFormalParametersOpt(token); | |
869 bool previousYieldIsKeyword = yieldIsKeyword; | |
870 bool previousAwaitIsKeyword = awaitIsKeyword; | |
871 token = parseAsyncModifier(token); | |
872 token = parseFunctionBody(token, false, externalModifier != null); | |
873 yieldIsKeyword = previousYieldIsKeyword; | |
874 awaitIsKeyword = previousAwaitIsKeyword; | |
875 listener.endTopLevelMethod(start, getOrSet, token); | |
876 return token.next; | |
877 } | |
878 | |
879 Link<Token> findMemberName(Token token) { | |
880 Token start = token; | |
881 Link<Token> identifiers = const Link<Token>(); | |
882 while (!identical(token.kind, EOF_TOKEN)) { | |
883 String value = token.stringValue; | |
884 if ((identical(value, '(')) || (identical(value, '{')) | |
885 || (identical(value, '=>'))) { | |
886 // A method. | |
887 return identifiers; | |
888 } else if ((identical(value, '=')) || (identical(value, ';')) | |
889 || (identical(value, ','))) { | |
890 // A field or abstract getter. | |
891 return identifiers; | |
892 } | |
893 identifiers = identifiers.prepend(token); | |
894 if (isValidTypeReference(token)) { | |
895 // type ... | |
896 if (optional('.', token.next)) { | |
897 // type '.' ... | |
898 if (token.next.next.isIdentifier()) { | |
899 // type '.' identifier | |
900 token = token.next.next; | |
901 } | |
902 } | |
903 if (optional('<', token.next)) { | |
904 if (token.next is BeginGroupToken) { | |
905 BeginGroupToken beginGroup = token.next; | |
906 if (beginGroup.endGroup == null) { | |
907 listener.unmatched(beginGroup); | |
908 } | |
909 token = beginGroup.endGroup; | |
910 } | |
911 } | |
912 } | |
913 token = token.next; | |
914 } | |
915 return const Link<Token>(); | |
916 } | |
917 | |
918 Token parseVariableInitializerOpt(Token token) { | |
919 if (optional('=', token)) { | |
920 Token assignment = token; | |
921 listener.beginInitializer(token); | |
922 token = parseExpression(token.next); | |
923 listener.endInitializer(assignment); | |
924 } | |
925 return token; | |
926 } | |
927 | |
928 Token parseInitializersOpt(Token token) { | |
929 if (optional(':', token)) { | |
930 return parseInitializers(token); | |
931 } else { | |
932 listener.handleNoInitializers(); | |
933 return token; | |
934 } | |
935 } | |
936 | |
937 Token parseInitializers(Token token) { | |
938 Token begin = token; | |
939 listener.beginInitializers(begin); | |
940 expect(':', token); | |
941 int count = 0; | |
942 bool old = mayParseFunctionExpressions; | |
943 mayParseFunctionExpressions = false; | |
944 do { | |
945 token = parseExpression(token.next); | |
946 ++count; | |
947 } while (optional(',', token)); | |
948 mayParseFunctionExpressions = old; | |
949 listener.endInitializers(count, begin, token); | |
950 return token; | |
951 } | |
952 | |
953 Token parseLiteralStringOrRecoverExpression(Token token) { | |
954 if (identical(token.kind, STRING_TOKEN)) { | |
955 return parseLiteralString(token); | |
956 } else { | |
957 listener.recoverableError(token, "unexpected"); | |
958 return parseExpression(token); | |
959 } | |
960 } | |
961 | |
962 Token expectSemicolon(Token token) { | |
963 return expect(';', token); | |
964 } | |
965 | |
966 bool isModifier(Token token) { | |
967 final String value = token.stringValue; | |
968 return (identical('final', value)) || | |
969 (identical('var', value)) || | |
970 (identical('const', value)) || | |
971 (identical('abstract', value)) || | |
972 (identical('static', value)) || | |
973 (identical('external', value)); | |
974 } | |
975 | |
976 Token parseModifier(Token token) { | |
977 assert(isModifier(token)); | |
978 listener.handleModifier(token); | |
979 return token.next; | |
980 } | |
981 | |
982 void parseModifierList(Link<Token> tokens) { | |
983 int count = 0; | |
984 for (; !tokens.isEmpty; tokens = tokens.tail) { | |
985 Token token = tokens.head; | |
986 if (isModifier(token)) { | |
987 parseModifier(token); | |
988 } else { | |
989 listener.unexpected(token); | |
990 } | |
991 count++; | |
992 } | |
993 listener.handleModifiers(count); | |
994 } | |
995 | |
996 Token parseModifiers(Token token) { | |
997 int count = 0; | |
998 while (identical(token.kind, KEYWORD_TOKEN)) { | |
999 if (!isModifier(token)) | |
1000 break; | |
1001 token = parseModifier(token); | |
1002 count++; | |
1003 } | |
1004 listener.handleModifiers(count); | |
1005 return token; | |
1006 } | |
1007 | |
1008 /** | |
1009 * Returns the first token after the type starting at [token]. | |
1010 * This method assumes that [token] is an identifier (or void). | |
1011 * Use [peekAfterIfType] if [token] isn't known to be an identifier. | |
1012 */ | |
1013 Token peekAfterType(Token token) { | |
1014 // We are looking at "identifier ...". | |
1015 Token peek = token.next; | |
1016 if (identical(peek.kind, PERIOD_TOKEN)) { | |
1017 if (peek.next.isIdentifier()) { | |
1018 // Look past a library prefix. | |
1019 peek = peek.next.next; | |
1020 } | |
1021 } | |
1022 // We are looking at "qualified ...". | |
1023 if (identical(peek.kind, LT_TOKEN)) { | |
1024 // Possibly generic type. | |
1025 // We are looking at "qualified '<'". | |
1026 BeginGroupToken beginGroupToken = peek; | |
1027 Token gtToken = beginGroupToken.endGroup; | |
1028 if (gtToken != null) { | |
1029 // We are looking at "qualified '<' ... '>' ...". | |
1030 return gtToken.next; | |
1031 } | |
1032 } | |
1033 return peek; | |
1034 } | |
1035 | |
1036 /** | |
1037 * If [token] is the start of a type, returns the token after that type. | |
1038 * If [token] is not the start of a type, null is returned. | |
1039 */ | |
1040 Token peekAfterIfType(Token token) { | |
1041 if (!optional('void', token) && !token.isIdentifier()) { | |
1042 return null; | |
1043 } | |
1044 return peekAfterType(token); | |
1045 } | |
1046 | |
1047 Token parseClassBody(Token token) { | |
1048 Token begin = token; | |
1049 listener.beginClassBody(token); | |
1050 if (!optional('{', token)) { | |
1051 token = listener.expectedClassBody(token); | |
1052 } | |
1053 token = token.next; | |
1054 int count = 0; | |
1055 while (notEofOrValue('}', token)) { | |
1056 token = parseMember(token); | |
1057 ++count; | |
1058 } | |
1059 expect('}', token); | |
1060 listener.endClassBody(count, begin, token); | |
1061 return token; | |
1062 } | |
1063 | |
1064 bool isGetOrSet(Token token) { | |
1065 final String value = token.stringValue; | |
1066 return (identical(value, 'get')) || (identical(value, 'set')); | |
1067 } | |
1068 | |
1069 bool isFactoryDeclaration(Token token) { | |
1070 if (optional('external', token)) token = token.next; | |
1071 if (optional('const', token)) token = token.next; | |
1072 return optional('factory', token); | |
1073 } | |
1074 | |
1075 Token parseMember(Token token) { | |
1076 token = parseMetadataStar(token); | |
1077 String value = token.stringValue; | |
1078 if (isFactoryDeclaration(token)) { | |
1079 return parseFactoryMethod(token); | |
1080 } | |
1081 Token start = token; | |
1082 listener.beginMember(token); | |
1083 | |
1084 Link<Token> identifiers = findMemberName(token); | |
1085 if (identifiers.isEmpty) { | |
1086 return listener.expectedDeclaration(start); | |
1087 } | |
1088 Token name = identifiers.head; | |
1089 Token afterName = name.next; | |
1090 identifiers = identifiers.tail; | |
1091 if (!identifiers.isEmpty) { | |
1092 if (optional('operator', identifiers.head)) { | |
1093 name = identifiers.head; | |
1094 identifiers = identifiers.tail; | |
1095 } | |
1096 } | |
1097 Token getOrSet; | |
1098 if (!identifiers.isEmpty) { | |
1099 if (isGetOrSet(identifiers.head)) { | |
1100 getOrSet = identifiers.head; | |
1101 identifiers = identifiers.tail; | |
1102 } | |
1103 } | |
1104 Token type; | |
1105 if (!identifiers.isEmpty) { | |
1106 if (isValidTypeReference(identifiers.head)) { | |
1107 type = identifiers.head; | |
1108 identifiers = identifiers.tail; | |
1109 } | |
1110 } | |
1111 | |
1112 token = afterName; | |
1113 bool isField; | |
1114 while (true) { | |
1115 // Loop to allow the listener to rewrite the token stream for | |
1116 // error handling. | |
1117 final String value = token.stringValue; | |
1118 if ((identical(value, '(')) || (identical(value, '.')) | |
1119 || (identical(value, '{')) || (identical(value, '=>'))) { | |
1120 isField = false; | |
1121 break; | |
1122 } else if (identical(value, ';')) { | |
1123 if (getOrSet != null) { | |
1124 // If we found a "get" keyword, this must be an abstract | |
1125 // getter. | |
1126 isField = (!identical(getOrSet.stringValue, 'get')); | |
1127 // TODO(ahe): This feels like a hack. | |
1128 } else { | |
1129 isField = true; | |
1130 } | |
1131 break; | |
1132 } else if ((identical(value, '=')) || (identical(value, ','))) { | |
1133 isField = true; | |
1134 break; | |
1135 } else { | |
1136 token = listener.unexpected(token); | |
1137 if (identical(token.kind, EOF_TOKEN)) { | |
1138 // TODO(ahe): This is a hack, see parseTopLevelMember. | |
1139 listener.endFields(1, start, token); | |
1140 return token; | |
1141 } | |
1142 } | |
1143 } | |
1144 | |
1145 var modifiers = identifiers.reverse(); | |
1146 return isField | |
1147 ? parseFields(start, modifiers, type, getOrSet, name, false) | |
1148 : parseMethod(start, modifiers, type, getOrSet, name); | |
1149 | |
1150 } | |
1151 | |
1152 Token parseMethod(Token start, | |
1153 Link<Token> modifiers, | |
1154 Token type, | |
1155 Token getOrSet, | |
1156 Token name) { | |
1157 Token externalModifier; | |
1158 Token staticModifier; | |
1159 Token constModifier; | |
1160 int modifierCount = 0; | |
1161 int allowedModifierCount = 1; | |
1162 for (Token modifier in modifiers) { | |
1163 if (externalModifier == null && optional('external', modifier)) { | |
1164 modifierCount++; | |
1165 externalModifier = modifier; | |
1166 if (modifierCount != allowedModifierCount) { | |
1167 listener.reportError( | |
1168 modifier, | |
1169 MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); | |
1170 } | |
1171 allowedModifierCount++; | |
1172 } else if (staticModifier == null && optional('static', modifier)) { | |
1173 modifierCount++; | |
1174 staticModifier = modifier; | |
1175 if (modifierCount != allowedModifierCount) { | |
1176 listener.reportError( | |
1177 modifier, | |
1178 MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); | |
1179 } | |
1180 } else if (constModifier == null && optional('const', modifier)) { | |
1181 modifierCount++; | |
1182 constModifier = modifier; | |
1183 if (modifierCount != allowedModifierCount) { | |
1184 listener.reportError( | |
1185 modifier, | |
1186 MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); | |
1187 } | |
1188 } else { | |
1189 listener.reportError( | |
1190 modifier, MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier}); | |
1191 } | |
1192 } | |
1193 parseModifierList(modifiers); | |
1194 | |
1195 if (type == null) { | |
1196 listener.handleNoType(name); | |
1197 } else { | |
1198 parseReturnTypeOpt(type); | |
1199 } | |
1200 Token token; | |
1201 if (optional('operator', name)) { | |
1202 token = parseOperatorName(name); | |
1203 if (staticModifier != null) { | |
1204 // TODO(ahe): Consider a more specific error message. | |
1205 listener.reportError( | |
1206 staticModifier, MessageKind.EXTRANEOUS_MODIFIER, | |
1207 {'modifier': staticModifier}); | |
1208 } | |
1209 } else { | |
1210 token = parseIdentifier(name); | |
1211 } | |
1212 | |
1213 token = parseQualifiedRestOpt(token); | |
1214 token = parseFormalParametersOpt(token); | |
1215 token = parseInitializersOpt(token); | |
1216 bool previousYieldIsKeyword = yieldIsKeyword; | |
1217 bool previousAwaitIsKeyword = awaitIsKeyword; | |
1218 token = parseAsyncModifier(token); | |
1219 if (optional('=', token)) { | |
1220 token = parseRedirectingFactoryBody(token); | |
1221 } else { | |
1222 token = parseFunctionBody( | |
1223 token, false, staticModifier == null || externalModifier != null); | |
1224 } | |
1225 yieldIsKeyword = previousYieldIsKeyword; | |
1226 awaitIsKeyword = previousAwaitIsKeyword; | |
1227 listener.endMethod(getOrSet, start, token); | |
1228 return token.next; | |
1229 } | |
1230 | |
1231 Token parseFactoryMethod(Token token) { | |
1232 assert(isFactoryDeclaration(token)); | |
1233 Token start = token; | |
1234 Token externalModifier; | |
1235 if (identical(token.stringValue, 'external')) { | |
1236 externalModifier = token; | |
1237 token = token.next; | |
1238 } | |
1239 Token constKeyword = null; | |
1240 if (optional('const', token)) { | |
1241 constKeyword = token; | |
1242 token = token.next; | |
1243 } | |
1244 Token factoryKeyword = token; | |
1245 listener.beginFactoryMethod(factoryKeyword); | |
1246 token = token.next; // Skip 'factory'. | |
1247 token = parseConstructorReference(token); | |
1248 token = parseFormalParameters(token); | |
1249 bool previousYieldIsKeyword = yieldIsKeyword; | |
1250 bool previousAwaitIsKeyword = awaitIsKeyword; | |
1251 token = parseAsyncModifier(token); | |
1252 if (optional('=', token)) { | |
1253 token = parseRedirectingFactoryBody(token); | |
1254 } else { | |
1255 token = parseFunctionBody(token, false, externalModifier != null); | |
1256 } | |
1257 listener.endFactoryMethod(start, token); | |
1258 return token.next; | |
1259 } | |
1260 | |
1261 Token parseOperatorName(Token token) { | |
1262 assert(optional('operator', token)); | |
1263 if (isUserDefinableOperator(token.next.stringValue)) { | |
1264 Token operator = token; | |
1265 token = token.next; | |
1266 listener.handleOperatorName(operator, token); | |
1267 return token.next; | |
1268 } else { | |
1269 return parseIdentifier(token); | |
1270 } | |
1271 } | |
1272 | |
1273 Token parseFunction(Token token, Token getOrSet) { | |
1274 listener.beginFunction(token); | |
1275 token = parseModifiers(token); | |
1276 if (identical(getOrSet, token)) token = token.next; | |
1277 if (optional('operator', token)) { | |
1278 listener.handleNoType(token); | |
1279 listener.beginFunctionName(token); | |
1280 token = parseOperatorName(token); | |
1281 } else { | |
1282 token = parseReturnTypeOpt(token); | |
1283 if (identical(getOrSet, token)) token = token.next; | |
1284 listener.beginFunctionName(token); | |
1285 if (optional('operator', token)) { | |
1286 token = parseOperatorName(token); | |
1287 } else { | |
1288 token = parseIdentifier(token); | |
1289 } | |
1290 } | |
1291 token = parseQualifiedRestOpt(token); | |
1292 listener.endFunctionName(token); | |
1293 token = parseFormalParametersOpt(token); | |
1294 token = parseInitializersOpt(token); | |
1295 bool previousYieldIsKeyword = yieldIsKeyword; | |
1296 bool previousAwaitIsKeyword = awaitIsKeyword; | |
1297 token = parseAsyncModifier(token); | |
1298 if (optional('=', token)) { | |
1299 token = parseRedirectingFactoryBody(token); | |
1300 } else { | |
1301 token = parseFunctionBody(token, false, true); | |
1302 } | |
1303 yieldIsKeyword = previousYieldIsKeyword; | |
1304 awaitIsKeyword = previousAwaitIsKeyword; | |
1305 listener.endFunction(getOrSet, token); | |
1306 return token.next; | |
1307 } | |
1308 | |
1309 Token parseUnnamedFunction(Token token) { | |
1310 listener.beginUnnamedFunction(token); | |
1311 token = parseFormalParameters(token); | |
1312 bool previousYieldIsKeyword = yieldIsKeyword; | |
1313 bool previousAwaitIsKeyword = awaitIsKeyword; | |
1314 token = parseAsyncModifier(token); | |
1315 bool isBlock = optional('{', token); | |
1316 token = parseFunctionBody(token, true, false); | |
1317 yieldIsKeyword = previousYieldIsKeyword; | |
1318 awaitIsKeyword = previousAwaitIsKeyword; | |
1319 listener.endUnnamedFunction(token); | |
1320 return isBlock ? token.next : token; | |
1321 } | |
1322 | |
1323 Token parseFunctionDeclaration(Token token) { | |
1324 listener.beginFunctionDeclaration(token); | |
1325 token = parseFunction(token, null); | |
1326 listener.endFunctionDeclaration(token); | |
1327 return token; | |
1328 } | |
1329 | |
1330 Token parseFunctionExpression(Token token) { | |
1331 listener.beginFunction(token); | |
1332 listener.handleModifiers(0); | |
1333 token = parseReturnTypeOpt(token); | |
1334 listener.beginFunctionName(token); | |
1335 token = parseIdentifier(token); | |
1336 listener.endFunctionName(token); | |
1337 token = parseFormalParameters(token); | |
1338 listener.handleNoInitializers(); | |
1339 bool previousYieldIsKeyword = yieldIsKeyword; | |
1340 bool previousAwaitIsKeyword = awaitIsKeyword; | |
1341 token = parseAsyncModifier(token); | |
1342 bool isBlock = optional('{', token); | |
1343 token = parseFunctionBody(token, true, false); | |
1344 yieldIsKeyword = previousYieldIsKeyword; | |
1345 awaitIsKeyword = previousAwaitIsKeyword; | |
1346 listener.endFunction(null, token); | |
1347 return isBlock ? token.next : token; | |
1348 } | |
1349 | |
1350 Token parseConstructorReference(Token token) { | |
1351 Token start = token; | |
1352 listener.beginConstructorReference(start); | |
1353 token = parseIdentifier(token); | |
1354 token = parseQualifiedRestOpt(token); | |
1355 token = parseTypeArgumentsOpt(token); | |
1356 Token period = null; | |
1357 if (optional('.', token)) { | |
1358 period = token; | |
1359 token = parseIdentifier(token.next); | |
1360 } | |
1361 listener.endConstructorReference(start, period, token); | |
1362 return token; | |
1363 } | |
1364 | |
1365 Token parseRedirectingFactoryBody(Token token) { | |
1366 listener.beginRedirectingFactoryBody(token); | |
1367 assert(optional('=', token)); | |
1368 Token equals = token; | |
1369 token = parseConstructorReference(token.next); | |
1370 Token semicolon = token; | |
1371 expectSemicolon(token); | |
1372 listener.endRedirectingFactoryBody(equals, semicolon); | |
1373 return token; | |
1374 } | |
1375 | |
1376 Token parseFunctionBody(Token token, bool isExpression, bool allowAbstract) { | |
1377 if (optional(';', token)) { | |
1378 if (!allowAbstract) { | |
1379 listener.reportError(token, MessageKind.BODY_EXPECTED); | |
1380 } | |
1381 listener.endFunctionBody(0, null, token); | |
1382 return token; | |
1383 } else if (optional('=>', token)) { | |
1384 Token begin = token; | |
1385 token = parseExpression(token.next); | |
1386 if (!isExpression) { | |
1387 expectSemicolon(token); | |
1388 listener.endReturnStatement(true, begin, token); | |
1389 } else { | |
1390 listener.endReturnStatement(true, begin, null); | |
1391 } | |
1392 return token; | |
1393 } | |
1394 Token begin = token; | |
1395 int statementCount = 0; | |
1396 if (!optional('{', token)) { | |
1397 return listener.expectedFunctionBody(token); | |
1398 } | |
1399 | |
1400 listener.beginFunctionBody(begin); | |
1401 token = token.next; | |
1402 while (notEofOrValue('}', token)) { | |
1403 token = parseStatement(token); | |
1404 ++statementCount; | |
1405 } | |
1406 listener.endFunctionBody(statementCount, begin, token); | |
1407 expect('}', token); | |
1408 return token; | |
1409 } | |
1410 | |
1411 /// `async*` and `sync*` are parsed a two tokens, [token] and [star]. This | |
1412 /// method checks that there is no whitespace between [token] and [star]. | |
1413 void checkStarredModifier(Token token, Token star, String name) { | |
1414 if (star.charOffset > token.charOffset + token.charCount) { | |
1415 listener.reportError(new TokenPair(token, star), | |
1416 MessageKind.INVALID_STARRED_KEYWORD, {'keyword': name}); | |
1417 } | |
1418 } | |
1419 | |
1420 Token parseAsyncModifier(Token token) { | |
1421 Token async; | |
1422 Token star; | |
1423 awaitIsKeyword = false; | |
1424 yieldIsKeyword = false; | |
1425 if (optional('async', token)) { | |
1426 awaitIsKeyword = true; | |
1427 async = token; | |
1428 token = token.next; | |
1429 if (optional('*', token)) { | |
1430 yieldIsKeyword = true; | |
1431 star = token; | |
1432 token = token.next; | |
1433 checkStarredModifier(async, star, 'async*'); | |
1434 } | |
1435 } else if (optional('sync', token)) { | |
1436 async = token; | |
1437 token = token.next; | |
1438 if (optional('*', token)) { | |
1439 yieldIsKeyword = true; | |
1440 star = token; | |
1441 token = token.next; | |
1442 | |
1443 checkStarredModifier(async, star, 'sync*'); | |
1444 } else { | |
1445 listener.reportError(async, | |
1446 MessageKind.INVALID_SYNC_MODIFIER); | |
1447 } | |
1448 } | |
1449 listener.handleAsyncModifier(async, star); | |
1450 return token; | |
1451 } | |
1452 | |
1453 Token parseStatement(Token token) { | |
1454 final value = token.stringValue; | |
1455 if (identical(token.kind, IDENTIFIER_TOKEN)) { | |
1456 return parseExpressionStatementOrDeclaration(token); | |
1457 } else if (identical(value, '{')) { | |
1458 return parseBlock(token); | |
1459 } else if (identical(value, 'return')) { | |
1460 return parseReturnStatement(token); | |
1461 } else if (identical(value, 'var') || identical(value, 'final')) { | |
1462 return parseVariablesDeclaration(token); | |
1463 } else if (identical(value, 'if')) { | |
1464 return parseIfStatement(token); | |
1465 } else if (awaitIsKeyword && identical(value, 'await')) { | |
1466 if (identical(token.next.stringValue, 'for')) { | |
1467 return parseForStatement(token, token.next); | |
1468 } else { | |
1469 return parseExpressionStatement(token); | |
1470 } | |
1471 } else if (identical(value, 'for')) { | |
1472 return parseForStatement(null, token); | |
1473 } else if (identical(value, 'rethrow')) { | |
1474 return parseRethrowStatement(token); | |
1475 } else if (identical(value, 'throw') && optional(';', token.next)) { | |
1476 // TODO(kasperl): Stop dealing with throw here. | |
1477 return parseRethrowStatement(token); | |
1478 } else if (identical(value, 'void')) { | |
1479 return parseExpressionStatementOrDeclaration(token); | |
1480 } else if (identical(value, 'while')) { | |
1481 return parseWhileStatement(token); | |
1482 } else if (identical(value, 'do')) { | |
1483 return parseDoWhileStatement(token); | |
1484 } else if (identical(value, 'try')) { | |
1485 return parseTryStatement(token); | |
1486 } else if (identical(value, 'switch')) { | |
1487 return parseSwitchStatement(token); | |
1488 } else if (identical(value, 'break')) { | |
1489 return parseBreakStatement(token); | |
1490 } else if (identical(value, 'continue')) { | |
1491 return parseContinueStatement(token); | |
1492 } else if (identical(value, 'assert')) { | |
1493 return parseAssertStatement(token); | |
1494 } else if (identical(value, ';')) { | |
1495 return parseEmptyStatement(token); | |
1496 } else if (yieldIsKeyword && identical(value, 'yield')) { | |
1497 return parseYieldStatement(token); | |
1498 } else if (identical(value, 'const')) { | |
1499 return parseExpressionStatementOrConstDeclaration(token); | |
1500 } else if (token.isIdentifier()) { | |
1501 return parseExpressionStatementOrDeclaration(token); | |
1502 } else { | |
1503 return parseExpressionStatement(token); | |
1504 } | |
1505 } | |
1506 | |
1507 Token parseYieldStatement(Token token) { | |
1508 Token begin = token; | |
1509 listener.beginYieldStatement(begin); | |
1510 assert(identical('yield', token.stringValue)); | |
1511 token = token.next; | |
1512 Token starToken; | |
1513 if (optional('*', token)) { | |
1514 starToken = token; | |
1515 token = token.next; | |
1516 checkStarredModifier(begin, starToken, 'yield*'); | |
1517 } | |
1518 token = parseExpression(token); | |
1519 listener.endYieldStatement(begin, starToken, token); | |
1520 return expectSemicolon(token); | |
1521 } | |
1522 | |
1523 | |
1524 Token parseReturnStatement(Token token) { | |
1525 Token begin = token; | |
1526 listener.beginReturnStatement(begin); | |
1527 assert(identical('return', token.stringValue)); | |
1528 token = token.next; | |
1529 if (optional(';', token)) { | |
1530 listener.endReturnStatement(false, begin, token); | |
1531 } else { | |
1532 token = parseExpression(token); | |
1533 listener.endReturnStatement(true, begin, token); | |
1534 } | |
1535 return expectSemicolon(token); | |
1536 } | |
1537 | |
1538 Token peekIdentifierAfterType(Token token) { | |
1539 Token peek = peekAfterType(token); | |
1540 if (peek != null && peek.isIdentifier()) { | |
1541 // We are looking at "type identifier". | |
1542 return peek; | |
1543 } else { | |
1544 return null; | |
1545 } | |
1546 } | |
1547 | |
1548 Token peekIdentifierAfterOptionalType(Token token) { | |
1549 Token peek = peekIdentifierAfterType(token); | |
1550 if (peek != null) { | |
1551 // We are looking at "type identifier". | |
1552 return peek; | |
1553 } else if (token.isIdentifier()) { | |
1554 // We are looking at "identifier". | |
1555 return token; | |
1556 } else { | |
1557 return null; | |
1558 } | |
1559 } | |
1560 | |
1561 Token parseExpressionStatementOrDeclaration(Token token) { | |
1562 assert(token.isIdentifier() || identical(token.stringValue, 'void')); | |
1563 Token identifier = peekIdentifierAfterType(token); | |
1564 if (identifier != null) { | |
1565 assert(identifier.isIdentifier()); | |
1566 Token afterId = identifier.next; | |
1567 int afterIdKind = afterId.kind; | |
1568 if (identical(afterIdKind, EQ_TOKEN) || | |
1569 identical(afterIdKind, SEMICOLON_TOKEN) || | |
1570 identical(afterIdKind, COMMA_TOKEN)) { | |
1571 // We are looking at "type identifier" followed by '=', ';', ','. | |
1572 return parseVariablesDeclaration(token); | |
1573 } else if (identical(afterIdKind, OPEN_PAREN_TOKEN)) { | |
1574 // We are looking at "type identifier '('". | |
1575 BeginGroupToken beginParen = afterId; | |
1576 Token endParen = beginParen.endGroup; | |
1577 Token afterParens = endParen.next; | |
1578 if (optional('{', afterParens) || | |
1579 optional('=>', afterParens) || | |
1580 optional('async', afterParens) || | |
1581 optional('sync', afterParens)) { | |
1582 // We are looking at "type identifier '(' ... ')'" followed | |
1583 // by '=>' or '{'. | |
1584 return parseFunctionDeclaration(token); | |
1585 } | |
1586 } | |
1587 // Fall-through to expression statement. | |
1588 } else { | |
1589 if (optional(':', token.next)) { | |
1590 return parseLabeledStatement(token); | |
1591 } else if (optional('(', token.next)) { | |
1592 BeginGroupToken begin = token.next; | |
1593 String afterParens = begin.endGroup.next.stringValue; | |
1594 if (identical(afterParens, '{') || | |
1595 identical(afterParens, '=>') || | |
1596 identical(afterParens, 'async') || | |
1597 identical(afterParens, 'sync')) { | |
1598 return parseFunctionDeclaration(token); | |
1599 } | |
1600 } | |
1601 } | |
1602 return parseExpressionStatement(token); | |
1603 } | |
1604 | |
1605 Token parseExpressionStatementOrConstDeclaration(Token token) { | |
1606 assert(identical(token.stringValue, 'const')); | |
1607 if (isModifier(token.next)) { | |
1608 return parseVariablesDeclaration(token); | |
1609 } | |
1610 Token identifier = peekIdentifierAfterOptionalType(token.next); | |
1611 if (identifier != null) { | |
1612 assert(identifier.isIdentifier()); | |
1613 Token afterId = identifier.next; | |
1614 int afterIdKind = afterId.kind; | |
1615 if (identical(afterIdKind, EQ_TOKEN) || | |
1616 identical(afterIdKind, SEMICOLON_TOKEN) || | |
1617 identical(afterIdKind, COMMA_TOKEN)) { | |
1618 // We are looking at "const type identifier" followed by '=', ';', or | |
1619 // ','. | |
1620 return parseVariablesDeclaration(token); | |
1621 } | |
1622 // Fall-through to expression statement. | |
1623 } | |
1624 | |
1625 return parseExpressionStatement(token); | |
1626 } | |
1627 | |
1628 Token parseLabel(Token token) { | |
1629 token = parseIdentifier(token); | |
1630 Token colon = token; | |
1631 token = expect(':', token); | |
1632 listener.handleLabel(colon); | |
1633 return token; | |
1634 } | |
1635 | |
1636 Token parseLabeledStatement(Token token) { | |
1637 int labelCount = 0; | |
1638 do { | |
1639 token = parseLabel(token); | |
1640 labelCount++; | |
1641 } while (token.isIdentifier() && optional(':', token.next)); | |
1642 listener.beginLabeledStatement(token, labelCount); | |
1643 token = parseStatement(token); | |
1644 listener.endLabeledStatement(labelCount); | |
1645 return token; | |
1646 } | |
1647 | |
1648 Token parseExpressionStatement(Token token) { | |
1649 listener.beginExpressionStatement(token); | |
1650 token = parseExpression(token); | |
1651 listener.endExpressionStatement(token); | |
1652 return expectSemicolon(token); | |
1653 } | |
1654 | |
1655 Token parseExpression(Token token) { | |
1656 return optional('throw', token) | |
1657 ? parseThrowExpression(token, true) | |
1658 : parsePrecedenceExpression(token, ASSIGNMENT_PRECEDENCE, true); | |
1659 } | |
1660 | |
1661 Token parseExpressionWithoutCascade(Token token) { | |
1662 return optional('throw', token) | |
1663 ? parseThrowExpression(token, false) | |
1664 : parsePrecedenceExpression(token, ASSIGNMENT_PRECEDENCE, false); | |
1665 } | |
1666 | |
1667 Token parseConditionalExpressionRest(Token token) { | |
1668 assert(optional('?', token)); | |
1669 Token question = token; | |
1670 token = parseExpressionWithoutCascade(token.next); | |
1671 Token colon = token; | |
1672 token = expect(':', token); | |
1673 token = parseExpressionWithoutCascade(token); | |
1674 listener.handleConditionalExpression(question, colon); | |
1675 return token; | |
1676 } | |
1677 | |
1678 Token parsePrecedenceExpression(Token token, int precedence, | |
1679 bool allowCascades) { | |
1680 assert(precedence >= 1); | |
1681 assert(precedence <= POSTFIX_PRECEDENCE); | |
1682 token = parseUnaryExpression(token, allowCascades); | |
1683 PrecedenceInfo info = token.info; | |
1684 int tokenLevel = info.precedence; | |
1685 for (int level = tokenLevel; level >= precedence; --level) { | |
1686 while (identical(tokenLevel, level)) { | |
1687 Token operator = token; | |
1688 if (identical(tokenLevel, CASCADE_PRECEDENCE)) { | |
1689 if (!allowCascades) { | |
1690 return token; | |
1691 } | |
1692 token = parseCascadeExpression(token); | |
1693 } else if (identical(tokenLevel, ASSIGNMENT_PRECEDENCE)) { | |
1694 // Right associative, so we recurse at the same precedence | |
1695 // level. | |
1696 token = parsePrecedenceExpression(token.next, level, allowCascades); | |
1697 listener.handleAssignmentExpression(operator); | |
1698 } else if (identical(tokenLevel, POSTFIX_PRECEDENCE)) { | |
1699 if (identical(info, PERIOD_INFO)) { | |
1700 // Left associative, so we recurse at the next higher | |
1701 // precedence level. However, POSTFIX_PRECEDENCE is the | |
1702 // highest level, so we just call parseUnaryExpression | |
1703 // directly. | |
1704 token = parseUnaryExpression(token.next, allowCascades); | |
1705 listener.handleBinaryExpression(operator); | |
1706 } else if ((identical(info, OPEN_PAREN_INFO)) || | |
1707 (identical(info, OPEN_SQUARE_BRACKET_INFO))) { | |
1708 token = parseArgumentOrIndexStar(token); | |
1709 } else if ((identical(info, PLUS_PLUS_INFO)) || | |
1710 (identical(info, MINUS_MINUS_INFO))) { | |
1711 listener.handleUnaryPostfixAssignmentExpression(token); | |
1712 token = token.next; | |
1713 } else { | |
1714 token = listener.unexpected(token); | |
1715 } | |
1716 } else if (identical(info, IS_INFO)) { | |
1717 token = parseIsOperatorRest(token); | |
1718 } else if (identical(info, AS_INFO)) { | |
1719 token = parseAsOperatorRest(token); | |
1720 } else if (identical(info, QUESTION_INFO)) { | |
1721 token = parseConditionalExpressionRest(token); | |
1722 } else { | |
1723 // Left associative, so we recurse at the next higher | |
1724 // precedence level. | |
1725 token = parsePrecedenceExpression(token.next, level + 1, | |
1726 allowCascades); | |
1727 listener.handleBinaryExpression(operator); | |
1728 } | |
1729 info = token.info; | |
1730 tokenLevel = info.precedence; | |
1731 if (level == EQUALITY_PRECEDENCE || level == RELATIONAL_PRECEDENCE) { | |
1732 // We don't allow (a == b == c) or (a < b < c). | |
1733 // Continue the outer loop if we have matched one equality or | |
1734 // relational operator. | |
1735 break; | |
1736 } | |
1737 } | |
1738 } | |
1739 return token; | |
1740 } | |
1741 | |
1742 Token parseCascadeExpression(Token token) { | |
1743 listener.beginCascade(token); | |
1744 assert(optional('..', token)); | |
1745 Token cascadeOperator = token; | |
1746 token = token.next; | |
1747 if (optional('[', token)) { | |
1748 token = parseArgumentOrIndexStar(token); | |
1749 } else if (token.isIdentifier()) { | |
1750 token = parseSend(token); | |
1751 listener.handleBinaryExpression(cascadeOperator); | |
1752 } else { | |
1753 return listener.unexpected(token); | |
1754 } | |
1755 Token mark; | |
1756 do { | |
1757 mark = token; | |
1758 if (optional('.', token)) { | |
1759 Token period = token; | |
1760 token = parseSend(token.next); | |
1761 listener.handleBinaryExpression(period); | |
1762 } | |
1763 token = parseArgumentOrIndexStar(token); | |
1764 } while (!identical(mark, token)); | |
1765 | |
1766 if (identical(token.info.precedence, ASSIGNMENT_PRECEDENCE)) { | |
1767 Token assignment = token; | |
1768 token = parseExpressionWithoutCascade(token.next); | |
1769 listener.handleAssignmentExpression(assignment); | |
1770 } | |
1771 listener.endCascade(); | |
1772 return token; | |
1773 } | |
1774 | |
1775 Token parseUnaryExpression(Token token, bool allowCascades) { | |
1776 String value = token.stringValue; | |
1777 // Prefix: | |
1778 if (awaitIsKeyword && optional('await', token)) { | |
1779 return parseAwaitExpression(token, allowCascades); | |
1780 } else if (identical(value, '+')) { | |
1781 // Dart no longer allows prefix-plus. | |
1782 listener.reportError(token, MessageKind.UNSUPPORTED_PREFIX_PLUS); | |
1783 return parseUnaryExpression(token.next, allowCascades); | |
1784 } else if ((identical(value, '!')) || | |
1785 (identical(value, '-')) || | |
1786 (identical(value, '~'))) { | |
1787 Token operator = token; | |
1788 // Right associative, so we recurse at the same precedence | |
1789 // level. | |
1790 token = parsePrecedenceExpression(token.next, POSTFIX_PRECEDENCE, | |
1791 allowCascades); | |
1792 listener.handleUnaryPrefixExpression(operator); | |
1793 } else if ((identical(value, '++')) || identical(value, '--')) { | |
1794 // TODO(ahe): Validate this is used correctly. | |
1795 Token operator = token; | |
1796 // Right associative, so we recurse at the same precedence | |
1797 // level. | |
1798 token = parsePrecedenceExpression(token.next, POSTFIX_PRECEDENCE, | |
1799 allowCascades); | |
1800 listener.handleUnaryPrefixAssignmentExpression(operator); | |
1801 } else { | |
1802 token = parsePrimary(token); | |
1803 } | |
1804 return token; | |
1805 } | |
1806 | |
1807 Token parseArgumentOrIndexStar(Token token) { | |
1808 while (true) { | |
1809 if (optional('[', token)) { | |
1810 Token openSquareBracket = token; | |
1811 bool old = mayParseFunctionExpressions; | |
1812 mayParseFunctionExpressions = true; | |
1813 token = parseExpression(token.next); | |
1814 mayParseFunctionExpressions = old; | |
1815 listener.handleIndexedExpression(openSquareBracket, token); | |
1816 token = expect(']', token); | |
1817 } else if (optional('(', token)) { | |
1818 token = parseArguments(token); | |
1819 listener.endSend(token); | |
1820 } else { | |
1821 break; | |
1822 } | |
1823 } | |
1824 return token; | |
1825 } | |
1826 | |
1827 Token parsePrimary(Token token) { | |
1828 final kind = token.kind; | |
1829 if (identical(kind, IDENTIFIER_TOKEN)) { | |
1830 return parseSendOrFunctionLiteral(token); | |
1831 } else if (identical(kind, INT_TOKEN) | |
1832 || identical(kind, HEXADECIMAL_TOKEN)) { | |
1833 return parseLiteralInt(token); | |
1834 } else if (identical(kind, DOUBLE_TOKEN)) { | |
1835 return parseLiteralDouble(token); | |
1836 } else if (identical(kind, STRING_TOKEN)) { | |
1837 return parseLiteralString(token); | |
1838 } else if (identical(kind, HASH_TOKEN)) { | |
1839 return parseLiteralSymbol(token); | |
1840 } else if (identical(kind, KEYWORD_TOKEN)) { | |
1841 final value = token.stringValue; | |
1842 if ((identical(value, 'true')) || (identical(value, 'false'))) { | |
1843 return parseLiteralBool(token); | |
1844 } else if (identical(value, 'null')) { | |
1845 return parseLiteralNull(token); | |
1846 } else if (identical(value, 'this')) { | |
1847 return parseThisExpression(token); | |
1848 } else if (identical(value, 'super')) { | |
1849 return parseSuperExpression(token); | |
1850 } else if (identical(value, 'new')) { | |
1851 return parseNewExpression(token); | |
1852 } else if (identical(value, 'const')) { | |
1853 return parseConstExpression(token); | |
1854 } else if (identical(value, 'void')) { | |
1855 return parseFunctionExpression(token); | |
1856 } else if (token.isIdentifier()) { | |
1857 return parseSendOrFunctionLiteral(token); | |
1858 } else { | |
1859 return listener.expectedExpression(token); | |
1860 } | |
1861 } else if (identical(kind, OPEN_PAREN_TOKEN)) { | |
1862 return parseParenthesizedExpressionOrFunctionLiteral(token); | |
1863 } else if ((identical(kind, LT_TOKEN)) || | |
1864 (identical(kind, OPEN_SQUARE_BRACKET_TOKEN)) || | |
1865 (identical(kind, OPEN_CURLY_BRACKET_TOKEN)) || | |
1866 identical(token.stringValue, '[]')) { | |
1867 return parseLiteralListOrMap(token); | |
1868 } else { | |
1869 return listener.expectedExpression(token); | |
1870 } | |
1871 } | |
1872 | |
1873 Token parseParenthesizedExpressionOrFunctionLiteral(Token token) { | |
1874 BeginGroupToken beginGroup = token; | |
1875 Token nextToken = beginGroup.endGroup.next; | |
1876 int kind = nextToken.kind; | |
1877 if (mayParseFunctionExpressions && | |
1878 (identical(kind, FUNCTION_TOKEN) || | |
1879 identical(kind, OPEN_CURLY_BRACKET_TOKEN) || | |
1880 (identical(kind, KEYWORD_TOKEN) && | |
1881 (nextToken.value == 'async' || nextToken.value == 'sync')))) { | |
1882 return parseUnnamedFunction(token); | |
1883 } else { | |
1884 bool old = mayParseFunctionExpressions; | |
1885 mayParseFunctionExpressions = true; | |
1886 token = parseParenthesizedExpression(token); | |
1887 mayParseFunctionExpressions = old; | |
1888 return token; | |
1889 } | |
1890 } | |
1891 | |
1892 Token parseParenthesizedExpression(Token token) { | |
1893 // We expect [begin] to be of type [BeginGroupToken], but we don't know for | |
1894 // sure until after calling expect. | |
1895 var begin = token; | |
1896 token = expect('(', token); | |
1897 // [begin] is now known to have type [BeginGroupToken]. | |
1898 token = parseExpression(token); | |
1899 if (!identical(begin.endGroup, token)) { | |
1900 listener.unexpected(token); | |
1901 token = begin.endGroup; | |
1902 } | |
1903 listener.handleParenthesizedExpression(begin); | |
1904 return expect(')', token); | |
1905 } | |
1906 | |
1907 Token parseThisExpression(Token token) { | |
1908 listener.handleThisExpression(token); | |
1909 token = token.next; | |
1910 if (optional('(', token)) { | |
1911 // Constructor forwarding. | |
1912 token = parseArguments(token); | |
1913 listener.endSend(token); | |
1914 } | |
1915 return token; | |
1916 } | |
1917 | |
1918 Token parseSuperExpression(Token token) { | |
1919 listener.handleSuperExpression(token); | |
1920 token = token.next; | |
1921 if (optional('(', token)) { | |
1922 // Super constructor. | |
1923 token = parseArguments(token); | |
1924 listener.endSend(token); | |
1925 } | |
1926 return token; | |
1927 } | |
1928 | |
1929 Token parseLiteralListOrMap(Token token) { | |
1930 Token constKeyword = null; | |
1931 if (optional('const', token)) { | |
1932 constKeyword = token; | |
1933 token = token.next; | |
1934 } | |
1935 token = parseTypeArgumentsOpt(token); | |
1936 Token beginToken = token; | |
1937 int count = 0; | |
1938 if (optional('{', token)) { | |
1939 bool old = mayParseFunctionExpressions; | |
1940 mayParseFunctionExpressions = true; | |
1941 do { | |
1942 if (optional('}', token.next)) { | |
1943 token = token.next; | |
1944 break; | |
1945 } | |
1946 token = parseMapLiteralEntry(token.next); | |
1947 ++count; | |
1948 } while (optional(',', token)); | |
1949 mayParseFunctionExpressions = old; | |
1950 listener.handleLiteralMap(count, beginToken, constKeyword, token); | |
1951 return expect('}', token); | |
1952 } else if (optional('[', token)) { | |
1953 bool old = mayParseFunctionExpressions; | |
1954 mayParseFunctionExpressions = true; | |
1955 do { | |
1956 if (optional(']', token.next)) { | |
1957 token = token.next; | |
1958 break; | |
1959 } | |
1960 token = parseExpression(token.next); | |
1961 ++count; | |
1962 } while (optional(',', token)); | |
1963 mayParseFunctionExpressions = old; | |
1964 listener.handleLiteralList(count, beginToken, constKeyword, token); | |
1965 return expect(']', token); | |
1966 } else if (optional('[]', token)) { | |
1967 listener.handleLiteralList(0, token, constKeyword, token); | |
1968 return token.next; | |
1969 } else { | |
1970 listener.unexpected(token); | |
1971 return null; | |
1972 } | |
1973 } | |
1974 | |
1975 Token parseMapLiteralEntry(Token token) { | |
1976 listener.beginLiteralMapEntry(token); | |
1977 // Assume the listener rejects non-string keys. | |
1978 token = parseExpression(token); | |
1979 Token colon = token; | |
1980 token = expect(':', token); | |
1981 token = parseExpression(token); | |
1982 listener.endLiteralMapEntry(colon, token); | |
1983 return token; | |
1984 } | |
1985 | |
1986 Token parseSendOrFunctionLiteral(Token token) { | |
1987 if (!mayParseFunctionExpressions) return parseSend(token); | |
1988 Token peek = peekAfterIfType(token); | |
1989 if (peek != null && | |
1990 identical(peek.kind, IDENTIFIER_TOKEN) && | |
1991 isFunctionDeclaration(peek.next)) { | |
1992 return parseFunctionExpression(token); | |
1993 } else if (isFunctionDeclaration(token.next)) { | |
1994 return parseFunctionExpression(token); | |
1995 } else { | |
1996 return parseSend(token); | |
1997 } | |
1998 } | |
1999 | |
2000 bool isFunctionDeclaration(Token token) { | |
2001 if (optional('(', token)) { | |
2002 BeginGroupToken begin = token; | |
2003 String afterParens = begin.endGroup.next.stringValue; | |
2004 if (identical(afterParens, '{') || | |
2005 identical(afterParens, '=>') || | |
2006 identical(afterParens, 'async') || | |
2007 identical(afterParens, 'sync')) { | |
2008 return true; | |
2009 } | |
2010 } | |
2011 return false; | |
2012 } | |
2013 | |
2014 Token parseRequiredArguments(Token token) { | |
2015 if (optional('(', token)) { | |
2016 token = parseArguments(token); | |
2017 } else { | |
2018 listener.handleNoArguments(token); | |
2019 token = listener.unexpected(token); | |
2020 } | |
2021 return token; | |
2022 } | |
2023 | |
2024 Token parseNewExpression(Token token) { | |
2025 Token newKeyword = token; | |
2026 token = expect('new', token); | |
2027 token = parseConstructorReference(token); | |
2028 token = parseRequiredArguments(token); | |
2029 listener.handleNewExpression(newKeyword); | |
2030 return token; | |
2031 } | |
2032 | |
2033 Token parseConstExpression(Token token) { | |
2034 Token constKeyword = token; | |
2035 token = expect('const', token); | |
2036 final String value = token.stringValue; | |
2037 if ((identical(value, '<')) || | |
2038 (identical(value, '[')) || | |
2039 (identical(value, '[]')) || | |
2040 (identical(value, '{'))) { | |
2041 return parseLiteralListOrMap(constKeyword); | |
2042 } | |
2043 token = parseConstructorReference(token); | |
2044 token = parseRequiredArguments(token); | |
2045 listener.handleConstExpression(constKeyword); | |
2046 return token; | |
2047 } | |
2048 | |
2049 Token parseLiteralInt(Token token) { | |
2050 listener.handleLiteralInt(token); | |
2051 return token.next; | |
2052 } | |
2053 | |
2054 Token parseLiteralDouble(Token token) { | |
2055 listener.handleLiteralDouble(token); | |
2056 return token.next; | |
2057 } | |
2058 | |
2059 Token parseLiteralString(Token token) { | |
2060 bool old = mayParseFunctionExpressions; | |
2061 mayParseFunctionExpressions = true; | |
2062 token = parseSingleLiteralString(token); | |
2063 int count = 1; | |
2064 while (identical(token.kind, STRING_TOKEN)) { | |
2065 token = parseSingleLiteralString(token); | |
2066 count++; | |
2067 } | |
2068 if (count > 1) { | |
2069 listener.handleStringJuxtaposition(count); | |
2070 } | |
2071 mayParseFunctionExpressions = old; | |
2072 return token; | |
2073 } | |
2074 | |
2075 Token parseLiteralSymbol(Token token) { | |
2076 Token hashToken = token; | |
2077 listener.beginLiteralSymbol(hashToken); | |
2078 token = token.next; | |
2079 if (isUserDefinableOperator(token.stringValue)) { | |
2080 listener.handleOperator(token); | |
2081 listener.endLiteralSymbol(hashToken, 1); | |
2082 return token.next; | |
2083 } else { | |
2084 int count = 1; | |
2085 token = parseIdentifier(token); | |
2086 while (identical(token.stringValue, '.')) { | |
2087 count++; | |
2088 token = parseIdentifier(token.next); | |
2089 } | |
2090 listener.endLiteralSymbol(hashToken, count); | |
2091 return token; | |
2092 } | |
2093 } | |
2094 | |
2095 /** | |
2096 * Only called when [:token.kind === STRING_TOKEN:]. | |
2097 */ | |
2098 Token parseSingleLiteralString(Token token) { | |
2099 listener.beginLiteralString(token); | |
2100 // Parsing the prefix, for instance 'x of 'x${id}y${id}z' | |
2101 token = token.next; | |
2102 int interpolationCount = 0; | |
2103 var kind = token.kind; | |
2104 while (kind != EOF_TOKEN) { | |
2105 if (identical(kind, STRING_INTERPOLATION_TOKEN)) { | |
2106 // Parsing ${expression}. | |
2107 token = token.next; | |
2108 token = parseExpression(token); | |
2109 token = expect('}', token); | |
2110 } else if (identical(kind, STRING_INTERPOLATION_IDENTIFIER_TOKEN)) { | |
2111 // Parsing $identifier. | |
2112 token = token.next; | |
2113 token = parseExpression(token); | |
2114 } else { | |
2115 break; | |
2116 } | |
2117 ++interpolationCount; | |
2118 // Parsing the infix/suffix, for instance y and z' of 'x${id}y${id}z' | |
2119 token = parseStringPart(token); | |
2120 kind = token.kind; | |
2121 } | |
2122 listener.endLiteralString(interpolationCount); | |
2123 return token; | |
2124 } | |
2125 | |
2126 Token parseLiteralBool(Token token) { | |
2127 listener.handleLiteralBool(token); | |
2128 return token.next; | |
2129 } | |
2130 | |
2131 Token parseLiteralNull(Token token) { | |
2132 listener.handleLiteralNull(token); | |
2133 return token.next; | |
2134 } | |
2135 | |
2136 Token parseSend(Token token) { | |
2137 listener.beginSend(token); | |
2138 token = parseIdentifier(token); | |
2139 token = parseArgumentsOpt(token); | |
2140 listener.endSend(token); | |
2141 return token; | |
2142 } | |
2143 | |
2144 Token parseArgumentsOpt(Token token) { | |
2145 if (!optional('(', token)) { | |
2146 listener.handleNoArguments(token); | |
2147 return token; | |
2148 } else { | |
2149 return parseArguments(token); | |
2150 } | |
2151 } | |
2152 | |
2153 Token parseArguments(Token token) { | |
2154 Token begin = token; | |
2155 listener.beginArguments(begin); | |
2156 assert(identical('(', token.stringValue)); | |
2157 int argumentCount = 0; | |
2158 if (optional(')', token.next)) { | |
2159 listener.endArguments(argumentCount, begin, token.next); | |
2160 return token.next.next; | |
2161 } | |
2162 bool old = mayParseFunctionExpressions; | |
2163 mayParseFunctionExpressions = true; | |
2164 do { | |
2165 Token colon = null; | |
2166 if (optional(':', token.next.next)) { | |
2167 token = parseIdentifier(token.next); | |
2168 colon = token; | |
2169 } | |
2170 token = parseExpression(token.next); | |
2171 if (colon != null) listener.handleNamedArgument(colon); | |
2172 ++argumentCount; | |
2173 } while (optional(',', token)); | |
2174 mayParseFunctionExpressions = old; | |
2175 listener.endArguments(argumentCount, begin, token); | |
2176 return expect(')', token); | |
2177 } | |
2178 | |
2179 Token parseIsOperatorRest(Token token) { | |
2180 assert(optional('is', token)); | |
2181 Token operator = token; | |
2182 Token not = null; | |
2183 if (optional('!', token.next)) { | |
2184 token = token.next; | |
2185 not = token; | |
2186 } | |
2187 token = parseType(token.next); | |
2188 listener.handleIsOperator(operator, not, token); | |
2189 String value = token.stringValue; | |
2190 if (identical(value, 'is') || identical(value, 'as')) { | |
2191 // The is- and as-operators cannot be chained, but they can take part of | |
2192 // expressions like: foo is Foo || foo is Bar. | |
2193 listener.unexpected(token); | |
2194 } | |
2195 return token; | |
2196 } | |
2197 | |
2198 Token parseAsOperatorRest(Token token) { | |
2199 assert(optional('as', token)); | |
2200 Token operator = token; | |
2201 token = parseType(token.next); | |
2202 listener.handleAsOperator(operator, token); | |
2203 String value = token.stringValue; | |
2204 if (identical(value, 'is') || identical(value, 'as')) { | |
2205 // The is- and as-operators cannot be chained. | |
2206 listener.unexpected(token); | |
2207 } | |
2208 return token; | |
2209 } | |
2210 | |
2211 Token parseVariablesDeclaration(Token token) { | |
2212 return parseVariablesDeclarationMaybeSemicolon(token, true); | |
2213 } | |
2214 | |
2215 Token parseVariablesDeclarationNoSemicolon(Token token) { | |
2216 // Only called when parsing a for loop, so this is for parsing locals. | |
2217 return parseVariablesDeclarationMaybeSemicolon(token, false); | |
2218 } | |
2219 | |
2220 Token parseVariablesDeclarationMaybeSemicolon(Token token, | |
2221 bool endWithSemicolon) { | |
2222 int count = 1; | |
2223 listener.beginVariablesDeclaration(token); | |
2224 token = parseModifiers(token); | |
2225 token = parseTypeOpt(token); | |
2226 token = parseOptionallyInitializedIdentifier(token); | |
2227 while (optional(',', token)) { | |
2228 token = parseOptionallyInitializedIdentifier(token.next); | |
2229 ++count; | |
2230 } | |
2231 if (endWithSemicolon) { | |
2232 Token semicolon = token; | |
2233 token = expectSemicolon(semicolon); | |
2234 listener.endVariablesDeclaration(count, semicolon); | |
2235 return token; | |
2236 } else { | |
2237 listener.endVariablesDeclaration(count, null); | |
2238 return token; | |
2239 } | |
2240 } | |
2241 | |
2242 Token parseOptionallyInitializedIdentifier(Token token) { | |
2243 listener.beginInitializedIdentifier(token); | |
2244 token = parseIdentifier(token); | |
2245 token = parseVariableInitializerOpt(token); | |
2246 listener.endInitializedIdentifier(); | |
2247 return token; | |
2248 } | |
2249 | |
2250 Token parseIfStatement(Token token) { | |
2251 Token ifToken = token; | |
2252 listener.beginIfStatement(ifToken); | |
2253 token = expect('if', token); | |
2254 token = parseParenthesizedExpression(token); | |
2255 token = parseStatement(token); | |
2256 Token elseToken = null; | |
2257 if (optional('else', token)) { | |
2258 elseToken = token; | |
2259 token = parseStatement(token.next); | |
2260 } | |
2261 listener.endIfStatement(ifToken, elseToken); | |
2262 return token; | |
2263 } | |
2264 | |
2265 Token parseForStatement(Token awaitToken, Token token) { | |
2266 Token forToken = token; | |
2267 listener.beginForStatement(forToken); | |
2268 token = expect('for', token); | |
2269 token = expect('(', token); | |
2270 token = parseVariablesDeclarationOrExpressionOpt(token); | |
2271 if (optional('in', token)) { | |
2272 return parseForInRest(awaitToken, forToken, token); | |
2273 } else { | |
2274 if (awaitToken != null) { | |
2275 listener.reportError(awaitToken, MessageKind.INVALID_AWAIT_FOR); | |
2276 } | |
2277 return parseForRest(forToken, token); | |
2278 } | |
2279 } | |
2280 | |
2281 Token parseVariablesDeclarationOrExpressionOpt(Token token) { | |
2282 final String value = token.stringValue; | |
2283 if (identical(value, ';')) { | |
2284 listener.handleNoExpression(token); | |
2285 return token; | |
2286 } else if ((identical(value, 'var')) || (identical(value, 'final'))) { | |
2287 return parseVariablesDeclarationNoSemicolon(token); | |
2288 } | |
2289 Token identifier = peekIdentifierAfterType(token); | |
2290 if (identifier != null) { | |
2291 assert(identifier.isIdentifier()); | |
2292 if (isOneOf4(identifier.next, '=', ';', ',', 'in')) { | |
2293 return parseVariablesDeclarationNoSemicolon(token); | |
2294 } | |
2295 } | |
2296 return parseExpression(token); | |
2297 } | |
2298 | |
2299 Token parseForRest(Token forToken, Token token) { | |
2300 token = expectSemicolon(token); | |
2301 if (optional(';', token)) { | |
2302 token = parseEmptyStatement(token); | |
2303 } else { | |
2304 token = parseExpressionStatement(token); | |
2305 } | |
2306 int expressionCount = 0; | |
2307 while (true) { | |
2308 if (optional(')', token)) break; | |
2309 token = parseExpression(token); | |
2310 ++expressionCount; | |
2311 if (optional(',', token)) { | |
2312 token = token.next; | |
2313 } else { | |
2314 break; | |
2315 } | |
2316 } | |
2317 token = expect(')', token); | |
2318 token = parseStatement(token); | |
2319 listener.endForStatement(expressionCount, forToken, token); | |
2320 return token; | |
2321 } | |
2322 | |
2323 Token parseForInRest(Token awaitToken, Token forToken, Token token) { | |
2324 assert(optional('in', token)); | |
2325 Token inKeyword = token; | |
2326 token = parseExpression(token.next); | |
2327 token = expect(')', token); | |
2328 token = parseStatement(token); | |
2329 listener.endForIn(awaitToken, forToken, inKeyword, token); | |
2330 return token; | |
2331 } | |
2332 | |
2333 Token parseWhileStatement(Token token) { | |
2334 Token whileToken = token; | |
2335 listener.beginWhileStatement(whileToken); | |
2336 token = expect('while', token); | |
2337 token = parseParenthesizedExpression(token); | |
2338 token = parseStatement(token); | |
2339 listener.endWhileStatement(whileToken, token); | |
2340 return token; | |
2341 } | |
2342 | |
2343 Token parseDoWhileStatement(Token token) { | |
2344 Token doToken = token; | |
2345 listener.beginDoWhileStatement(doToken); | |
2346 token = expect('do', token); | |
2347 token = parseStatement(token); | |
2348 Token whileToken = token; | |
2349 token = expect('while', token); | |
2350 token = parseParenthesizedExpression(token); | |
2351 listener.endDoWhileStatement(doToken, whileToken, token); | |
2352 return expectSemicolon(token); | |
2353 } | |
2354 | |
2355 Token parseBlock(Token token) { | |
2356 Token begin = token; | |
2357 listener.beginBlock(begin); | |
2358 int statementCount = 0; | |
2359 token = expect('{', token); | |
2360 while (notEofOrValue('}', token)) { | |
2361 token = parseStatement(token); | |
2362 ++statementCount; | |
2363 } | |
2364 listener.endBlock(statementCount, begin, token); | |
2365 return expect('}', token); | |
2366 } | |
2367 | |
2368 Token parseAwaitExpression(Token token, bool allowCascades) { | |
2369 Token awaitToken = token; | |
2370 listener.beginAwaitExpression(awaitToken); | |
2371 token = expect('await', token); | |
2372 token = parseUnaryExpression(token, allowCascades); | |
2373 listener.endAwaitExpression(awaitToken, token); | |
2374 return token; | |
2375 } | |
2376 | |
2377 Token parseThrowExpression(Token token, bool allowCascades) { | |
2378 Token throwToken = token; | |
2379 listener.beginThrowExpression(throwToken); | |
2380 token = expect('throw', token); | |
2381 token = allowCascades | |
2382 ? parseExpression(token) | |
2383 : parseExpressionWithoutCascade(token); | |
2384 listener.endThrowExpression(throwToken, token); | |
2385 return token; | |
2386 } | |
2387 | |
2388 Token parseRethrowStatement(Token token) { | |
2389 Token throwToken = token; | |
2390 listener.beginRethrowStatement(throwToken); | |
2391 // TODO(kasperl): Disallow throw here. | |
2392 if (identical(throwToken.stringValue, 'throw')) { | |
2393 token = expect('throw', token); | |
2394 } else { | |
2395 token = expect('rethrow', token); | |
2396 } | |
2397 listener.endRethrowStatement(throwToken, token); | |
2398 return expectSemicolon(token); | |
2399 } | |
2400 | |
2401 Token parseTryStatement(Token token) { | |
2402 assert(optional('try', token)); | |
2403 Token tryKeyword = token; | |
2404 listener.beginTryStatement(tryKeyword); | |
2405 token = parseBlock(token.next); | |
2406 int catchCount = 0; | |
2407 | |
2408 String value = token.stringValue; | |
2409 while (identical(value, 'catch') || identical(value, 'on')) { | |
2410 var onKeyword = null; | |
2411 if (identical(value, 'on')) { | |
2412 // on qualified catchPart? | |
2413 onKeyword = token; | |
2414 token = parseType(token.next); | |
2415 value = token.stringValue; | |
2416 } | |
2417 Token catchKeyword = null; | |
2418 if (identical(value, 'catch')) { | |
2419 catchKeyword = token; | |
2420 // TODO(ahe): Validate the "parameters". | |
2421 token = parseFormalParameters(token.next); | |
2422 } | |
2423 token = parseBlock(token); | |
2424 ++catchCount; | |
2425 listener.handleCatchBlock(onKeyword, catchKeyword); | |
2426 value = token.stringValue; // while condition | |
2427 } | |
2428 | |
2429 Token finallyKeyword = null; | |
2430 if (optional('finally', token)) { | |
2431 finallyKeyword = token; | |
2432 token = parseBlock(token.next); | |
2433 listener.handleFinallyBlock(finallyKeyword); | |
2434 } | |
2435 listener.endTryStatement(catchCount, tryKeyword, finallyKeyword); | |
2436 return token; | |
2437 } | |
2438 | |
2439 Token parseSwitchStatement(Token token) { | |
2440 assert(optional('switch', token)); | |
2441 Token switchKeyword = token; | |
2442 listener.beginSwitchStatement(switchKeyword); | |
2443 token = parseParenthesizedExpression(token.next); | |
2444 token = parseSwitchBlock(token); | |
2445 listener.endSwitchStatement(switchKeyword, token); | |
2446 return token.next; | |
2447 } | |
2448 | |
2449 Token parseSwitchBlock(Token token) { | |
2450 Token begin = token; | |
2451 listener.beginSwitchBlock(begin); | |
2452 token = expect('{', token); | |
2453 int caseCount = 0; | |
2454 while (!identical(token.kind, EOF_TOKEN)) { | |
2455 if (optional('}', token)) { | |
2456 break; | |
2457 } | |
2458 token = parseSwitchCase(token); | |
2459 ++caseCount; | |
2460 } | |
2461 listener.endSwitchBlock(caseCount, begin, token); | |
2462 expect('}', token); | |
2463 return token; | |
2464 } | |
2465 | |
2466 /** | |
2467 * Peek after the following labels (if any). The following token | |
2468 * is used to determine if the labels belong to a statement or a | |
2469 * switch case. | |
2470 */ | |
2471 Token peekPastLabels(Token token) { | |
2472 while (token.isIdentifier() && optional(':', token.next)) { | |
2473 token = token.next.next; | |
2474 } | |
2475 return token; | |
2476 } | |
2477 | |
2478 /** | |
2479 * Parse a group of labels, cases and possibly a default keyword and | |
2480 * the statements that they select. | |
2481 */ | |
2482 Token parseSwitchCase(Token token) { | |
2483 Token begin = token; | |
2484 Token defaultKeyword = null; | |
2485 int expressionCount = 0; | |
2486 int labelCount = 0; | |
2487 Token peek = peekPastLabels(token); | |
2488 while (true) { | |
2489 // Loop until we find something that can't be part of a switch case. | |
2490 String value = peek.stringValue; | |
2491 if (identical(value, 'default')) { | |
2492 while (!identical(token, peek)) { | |
2493 token = parseLabel(token); | |
2494 labelCount++; | |
2495 } | |
2496 defaultKeyword = token; | |
2497 token = expect(':', token.next); | |
2498 peek = token; | |
2499 break; | |
2500 } else if (identical(value, 'case')) { | |
2501 while (!identical(token, peek)) { | |
2502 token = parseLabel(token); | |
2503 labelCount++; | |
2504 } | |
2505 Token caseKeyword = token; | |
2506 token = parseExpression(token.next); | |
2507 Token colonToken = token; | |
2508 token = expect(':', token); | |
2509 listener.handleCaseMatch(caseKeyword, colonToken); | |
2510 expressionCount++; | |
2511 peek = peekPastLabels(token); | |
2512 } else { | |
2513 if (expressionCount == 0) { | |
2514 listener.expected("case", token); | |
2515 } | |
2516 break; | |
2517 } | |
2518 } | |
2519 // Finally zero or more statements. | |
2520 int statementCount = 0; | |
2521 while (!identical(token.kind, EOF_TOKEN)) { | |
2522 String value = peek.stringValue; | |
2523 if ((identical(value, 'case')) || | |
2524 (identical(value, 'default')) || | |
2525 ((identical(value, '}')) && (identical(token, peek)))) { | |
2526 // A label just before "}" will be handled as a statement error. | |
2527 break; | |
2528 } else { | |
2529 token = parseStatement(token); | |
2530 } | |
2531 statementCount++; | |
2532 peek = peekPastLabels(token); | |
2533 } | |
2534 listener.handleSwitchCase(labelCount, expressionCount, defaultKeyword, | |
2535 statementCount, begin, token); | |
2536 return token; | |
2537 } | |
2538 | |
2539 Token parseBreakStatement(Token token) { | |
2540 assert(optional('break', token)); | |
2541 Token breakKeyword = token; | |
2542 token = token.next; | |
2543 bool hasTarget = false; | |
2544 if (token.isIdentifier()) { | |
2545 token = parseIdentifier(token); | |
2546 hasTarget = true; | |
2547 } | |
2548 listener.handleBreakStatement(hasTarget, breakKeyword, token); | |
2549 return expectSemicolon(token); | |
2550 } | |
2551 | |
2552 Token parseAssertStatement(Token token) { | |
2553 Token assertKeyword = token; | |
2554 token = expect('assert', token); | |
2555 expect('(', token); | |
2556 token = parseArguments(token); | |
2557 listener.handleAssertStatement(assertKeyword, token); | |
2558 return expectSemicolon(token); | |
2559 } | |
2560 | |
2561 Token parseContinueStatement(Token token) { | |
2562 assert(optional('continue', token)); | |
2563 Token continueKeyword = token; | |
2564 token = token.next; | |
2565 bool hasTarget = false; | |
2566 if (token.isIdentifier()) { | |
2567 token = parseIdentifier(token); | |
2568 hasTarget = true; | |
2569 } | |
2570 listener.handleContinueStatement(hasTarget, continueKeyword, token); | |
2571 return expectSemicolon(token); | |
2572 } | |
2573 | |
2574 Token parseEmptyStatement(Token token) { | |
2575 listener.handleEmptyStatement(token); | |
2576 return expectSemicolon(token); | |
2577 } | |
2578 } | |
OLD | NEW |