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

Side by Side Diff: frog/parser.dart

Issue 10548047: Remove frog from the repository. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Move test and update apidoc.gyp. Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « frog/minfrogc.dart ('k') | frog/presubmit.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011, 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 // TODO(jimhug): Error recovery needs major work!
6 /**
7 * A simple recursive descent parser for the dart language.
8 *
9 * This parser is designed to be more permissive than the official
10 * Dart grammar. It is expected that many grammar errors would be
11 * reported by a later compiler phase. For example, a class is allowed
12 * to extend an arbitrary number of base classes - this can be
13 * very clearly detected and is reported in a later compiler phase.
14 */
15 class Parser {
16 TokenSource tokenizer;
17
18 final SourceFile source;
19 /** Enables diet parse, which skips function bodies. */
20 final bool diet;
21 /**
22 * Throw an IncompleteSourceException if the parser encounters a premature end
23 * of file or an incomplete multiline string.
24 */
25 final bool throwOnIncomplete;
26
27 /** Allow semicolons to be omitted at the end of lines. */
28 // TODO(nweiz): make this work for more than just end-of-file
29 final bool optionalSemicolons;
30
31 /**
32 * Allow the await keyword, when the await transformation is available (see
33 * await/awaitc.dart).
34 */
35 bool get enableAwait() => experimentalAwaitPhase != null;
36
37 /**
38 * To resolve ambiguity in initializers between constructor body and lambda
39 * expression.
40 */
41 bool _inhibitLambda = false;
42
43 Token _previousToken;
44 Token _peekToken;
45
46 // When we encounter '(' in a method body we need to find the ')' to know it
47 // we're parsing a lambda, paren-expr, or argument list. Closure formals are
48 // followed by '=>' or '{'. This list is used to cache the tokens after any
49 // nested parenthesis we find while peeking.
50 // TODO(jmesserly): it's simpler and faster to cache this on the Token itself,
51 // but that might add too much complexity for tools that need to invalidate.
52 List<Token> _afterParens;
53 int _afterParensIndex = 0;
54
55 bool _recover = false;
56
57 Parser(this.source, [this.diet = false, this.throwOnIncomplete = false,
58 this.optionalSemicolons = false, int startOffset = 0]) {
59 tokenizer = new Tokenizer(source, true, startOffset);
60 _peekToken = tokenizer.next();
61 _afterParens = <Token>[];
62 }
63
64 /** Generate an error if [source] has not been completely consumed. */
65 void checkEndOfFile() {
66 _eat(TokenKind.END_OF_FILE);
67 }
68
69 /** Guard to break out of parser when an unexpected end of file is found. */
70 bool isPrematureEndOfFile() {
71 if (throwOnIncomplete && _maybeEat(TokenKind.END_OF_FILE)) {
72 throw new IncompleteSourceException(_previousToken);
73 } else if (_maybeEat(TokenKind.END_OF_FILE)) {
74 _error('unexpected end of file', _peekToken.span);
75 return true;
76 } else {
77 return false;
78 }
79 }
80
81 /**
82 * Recovers the parser after an error, by iterating until it finds one of
83 * the provide [TokenKind] values.
84 */
85 bool _recoverTo(int kind1, [int kind2, int kind3]) {
86 assert(_recover);
87 while (!isPrematureEndOfFile()) {
88 int kind = _peek();
89 if (kind == kind1 || kind == kind2 || kind == kind3) {
90 _recover = false; // Done recovering. Issue errors normally.
91 return true;
92 }
93 _next();
94 }
95 // End of file without finding a match
96 return false;
97 }
98
99 ///////////////////////////////////////////////////////////////////
100 // Basic support methods
101 ///////////////////////////////////////////////////////////////////
102 int _peek() => _peekToken.kind;
103
104 Token _next() {
105 _previousToken = _peekToken;
106 _peekToken = tokenizer.next();
107 return _previousToken;
108 }
109
110 bool _peekKind(int kind) => _peekToken.kind == kind;
111
112 /* Is the next token a legal identifier? This includes pseudo-keywords. */
113 bool _peekIdentifier() => _isIdentifier(_peekToken.kind);
114
115 bool _isIdentifier(kind) {
116 return TokenKind.isIdentifier(kind)
117 // Note: 'await' is not a pseudo-keyword. When [enableAwait] is true, it
118 // is illegal to consider 'await' an identifier.
119 || (!enableAwait && kind == TokenKind.AWAIT);
120 }
121
122 bool _maybeEat(int kind) {
123 if (_peekToken.kind == kind) {
124 _previousToken = _peekToken;
125 _peekToken = tokenizer.next();
126 return true;
127 } else {
128 return false;
129 }
130 }
131
132 void _eat(int kind) {
133 if (!_maybeEat(kind)) {
134 _errorExpected(TokenKind.kindToString(kind));
135 }
136 }
137
138 void _eatSemicolon() {
139 if (optionalSemicolons && _peekKind(TokenKind.END_OF_FILE)) return;
140 _eat(TokenKind.SEMICOLON);
141 }
142
143 void _errorExpected(String expected) {
144 // Throw an IncompleteSourceException if that's the problem and
145 // throwOnIncomplete is true
146 if (throwOnIncomplete) isPrematureEndOfFile();
147 var tok = _next();
148 if (tok is ErrorToken && tok.message != null) {
149 // give priority to tokenizer errors
150 _error(tok.message, tok.span);
151 } else {
152 _error('expected $expected, but found $tok', tok.span);
153 }
154 }
155
156 void _error(String message, [SourceSpan location=null]) {
157 // Suppress error messages while we're trying to recover.
158 if (_recover) return;
159
160 if (location == null) {
161 location = _peekToken.span;
162 }
163 world.fatal(message, location); // syntax errors are fatal for now
164 _recover = true; // start error recovery
165 }
166
167 /** Skips from an opening '{' to the syntactically matching '}'. */
168 void _skipBlock() {
169 int depth = 1;
170 _eat(TokenKind.LBRACE);
171 while (true) {
172 var tok = _next();
173 if (tok.kind == TokenKind.LBRACE) {
174 depth += 1;
175 } else if (tok.kind == TokenKind.RBRACE) {
176 depth -= 1;
177 if (depth == 0) return;
178 } else if (tok.kind == TokenKind.END_OF_FILE) {
179 _error('unexpected end of file during diet parse', tok.span);
180 return;
181 }
182 }
183 }
184
185 SourceSpan _makeSpan(int start) {
186 return new SourceSpan(source, start, _previousToken.end);
187 }
188
189 ///////////////////////////////////////////////////////////////////
190 // Top level productions
191 ///////////////////////////////////////////////////////////////////
192
193 /** Entry point to the parser for parsing a compilation unit (i.e. a file). */
194 List<Definition> compilationUnit() {
195 var ret = [];
196 _maybeEat(TokenKind.HASHBANG);
197
198 while (_peekKind(TokenKind.HASH)) {
199 ret.add(directive());
200 }
201 _recover = false;
202 while (!_maybeEat(TokenKind.END_OF_FILE)) {
203 ret.add(topLevelDefinition());
204 }
205 _recover = false;
206 return ret;
207 }
208
209 directive() {
210 int start = _peekToken.start;
211 _eat(TokenKind.HASH);
212 var name = identifier();
213 var args = arguments();
214 _eatSemicolon();
215 return new DirectiveDefinition(name, args, _makeSpan(start));
216 }
217
218 topLevelDefinition() {
219 switch (_peek()) {
220 case TokenKind.CLASS:
221 return classDefinition(TokenKind.CLASS);
222 case TokenKind.INTERFACE:
223 return classDefinition(TokenKind.INTERFACE);
224 case TokenKind.TYPEDEF:
225 return functionTypeAlias();
226 default:
227 return declaration();
228 }
229 }
230
231 /** Entry point to the parser for an eval unit (i.e. a repl command). */
232 evalUnit() {
233 switch (_peek()) {
234 case TokenKind.CLASS:
235 return classDefinition(TokenKind.CLASS);
236 case TokenKind.INTERFACE:
237 return classDefinition(TokenKind.INTERFACE);
238 case TokenKind.TYPEDEF:
239 return functionTypeAlias();
240 default:
241 return statement();
242 }
243 _recover = false;
244 }
245
246 ///////////////////////////////////////////////////////////////////
247 // Definition productions
248 ///////////////////////////////////////////////////////////////////
249
250 classDefinition(int kind) {
251 int start = _peekToken.start;
252 _eat(kind);
253 var name = identifierForType();
254
255 var typeParams = null;
256 if (_peekKind(TokenKind.LT)) {
257 typeParams = typeParameters();
258 }
259
260 var _extends = null;
261 if (_maybeEat(TokenKind.EXTENDS)) {
262 _extends = typeList();
263 }
264
265 var _implements = null;
266 if (_maybeEat(TokenKind.IMPLEMENTS)) {
267 _implements = typeList();
268 }
269
270 var _native = null;
271 if (_maybeEat(TokenKind.NATIVE)) {
272 _native = maybeStringLiteral();
273 if (_native != null) _native = new NativeType(_native);
274 }
275
276 bool oldFactory = _maybeEat(TokenKind.FACTORY);
277 var defaultType = null;
278 if (oldFactory || _maybeEat(TokenKind.DEFAULT)) {
279 // TODO(jmesserly): keep old factory support for now. Remove soon.
280 if (oldFactory) {
281 world.warning('factory no longer supported, use "default" instead',
282 _previousToken.span);
283 }
284
285 // Note: this can't be type() because it has type parameters not type
286 // arguments.
287 var baseType = nameTypeReference();
288 var factTypeParams = null;
289 if (_peekKind(TokenKind.LT)) {
290 factTypeParams = typeParameters();
291 }
292 defaultType = new DefaultTypeReference(oldFactory,
293 baseType, factTypeParams, _makeSpan(baseType.span.start));
294 }
295
296 var body = [];
297 if (_maybeEat(TokenKind.LBRACE)) {
298 while (!_maybeEat(TokenKind.RBRACE)) {
299 body.add(declaration());
300 if (_recover) {
301 if (!_recoverTo(TokenKind.RBRACE, TokenKind.SEMICOLON)) break;
302 _maybeEat(TokenKind.SEMICOLON);
303 }
304 }
305 } else {
306 _errorExpected('block starting with "{" or ";"');
307 }
308 return new TypeDefinition(kind == TokenKind.CLASS, name, typeParams,
309 _extends, _implements, _native, defaultType, body, _makeSpan(start));
310 }
311
312 functionTypeAlias() {
313 int start = _peekToken.start;
314 _eat(TokenKind.TYPEDEF);
315
316 var di = declaredIdentifier(false);
317 var typeParams = null;
318 if (_peekKind(TokenKind.LT)) {
319 typeParams = typeParameters();
320 }
321 var formals = formalParameterList();
322 _eatSemicolon();
323
324 // TODO(jimhug): Validate that di.name is not a pseudo-keyword
325 var func = new FunctionDefinition(null, di.type, di.name, formals,
326 null, null, null, _makeSpan(start));
327
328 return new FunctionTypeDefinition(func, typeParams, _makeSpan(start));
329 }
330
331 initializers() {
332 _inhibitLambda = true;
333 var ret = [];
334 do {
335 ret.add(expression());
336 } while (_maybeEat(TokenKind.COMMA));
337 _inhibitLambda = false;
338 return ret;
339 }
340
341 functionBody(bool inExpression) {
342 int start = _peekToken.start;
343 if (_maybeEat(TokenKind.ARROW)) {
344 var expr = expression();
345 if (!inExpression) {
346 _eatSemicolon();
347 }
348 return new ReturnStatement(expr, _makeSpan(start));
349 } else if (_peekKind(TokenKind.LBRACE)) {
350 if (diet) {
351 _skipBlock();
352 return new DietStatement(_makeSpan(start));
353 } else {
354 return block();
355 }
356 } else if (!inExpression) {
357 if (_maybeEat(TokenKind.SEMICOLON)) {
358 return null;
359 }
360 }
361
362 _error('Expected function body (neither { nor => found)');
363 }
364
365 finishField(start, modifiers, type, name, value) {
366 var names = [name];
367 var values = [value];
368
369 while (_maybeEat(TokenKind.COMMA)) {
370 names.add(identifier());
371 if (_maybeEat(TokenKind.ASSIGN)) {
372 values.add(expression());
373 } else {
374 values.add(null);
375 }
376 }
377
378 _eatSemicolon();
379 return new VariableDefinition(modifiers, type, names, values,
380 _makeSpan(start));
381 }
382
383 finishDefinition(int start, List<Token> modifiers, di) {
384 switch(_peek()) {
385 case TokenKind.LPAREN:
386 var formals = formalParameterList();
387 var inits = null, native = null;
388 if (_maybeEat(TokenKind.COLON)) {
389 inits = initializers();
390 }
391 if (_maybeEat(TokenKind.NATIVE)) {
392 native = maybeStringLiteral();
393 if (native == null) native = '';
394 }
395 var body = functionBody(/*inExpression:*/false);
396 if (di.name == null) {
397 // TODO(jimhug): Must be named constructor - verify how?
398 di.name = di.type.name;
399 }
400 return new FunctionDefinition(modifiers, di.type, di.name, formals,
401 inits, native, body, _makeSpan(start));
402
403 case TokenKind.ASSIGN:
404 _eat(TokenKind.ASSIGN);
405 var value = expression();
406 return finishField(start, modifiers, di.type, di.name, value);
407
408 case TokenKind.COMMA:
409 case TokenKind.SEMICOLON:
410 return finishField(start, modifiers, di.type, di.name, null);
411
412 default:
413 // TODO(jimhug): This error message sucks.
414 _errorExpected('declaration');
415
416 return null;
417 }
418 }
419
420 declaration([bool includeOperators=true]) {
421 int start = _peekToken.start;
422 if (_peekKind(TokenKind.FACTORY)) {
423 return factoryConstructorDeclaration();
424 }
425
426 var modifiers = _readModifiers();
427 return finishDefinition(start, modifiers,
428 declaredIdentifier(includeOperators));
429 }
430
431 // TODO(jmesserly): do we still need this method?
432 // I left it here for now to support old-style factories
433 factoryConstructorDeclaration() {
434 int start = _peekToken.start;
435 var factoryToken = _next();
436
437 var names = [identifier()];
438 while (_maybeEat(TokenKind.DOT)) {
439 names.add(identifier());
440 }
441 if (_peekKind(TokenKind.LT)) {
442 var tp = typeParameters();
443 world.warning('type parameters on factories are no longer supported, '
444 + 'place them on the class instead', _makeSpan(tp[0].span.start));
445 }
446
447 var name = null;
448 var type = null;
449 if (_maybeEat(TokenKind.DOT)) {
450 name = identifier();
451 } else {
452 if (names.length > 1) {
453 name = names.removeLast();
454 } else {
455 name = new Identifier('', names[0].span);
456 }
457 }
458
459 if (names.length > 1) {
460 // TODO(jimhug): This is nasty to support and currently unused.
461 _error('unsupported qualified name for factory', names[0].span);
462 }
463 type = new NameTypeReference(false, names[0], null, names[0].span);
464 var di = new DeclaredIdentifier(type, name, false, _makeSpan(start));
465 return finishDefinition(start, [factoryToken], di);
466 }
467
468 ///////////////////////////////////////////////////////////////////
469 // Statement productions
470 ///////////////////////////////////////////////////////////////////
471 Statement statement() {
472 switch (_peek()) {
473 case TokenKind.BREAK:
474 return breakStatement();
475 case TokenKind.CONTINUE:
476 return continueStatement();
477 case TokenKind.RETURN:
478 return returnStatement();
479 case TokenKind.THROW:
480 return throwStatement();
481 case TokenKind.ASSERT:
482 return assertStatement();
483
484 case TokenKind.WHILE:
485 return whileStatement();
486 case TokenKind.DO:
487 return doStatement();
488 case TokenKind.FOR:
489 return forStatement();
490
491 case TokenKind.IF:
492 return ifStatement();
493 case TokenKind.SWITCH:
494 return switchStatement();
495
496 case TokenKind.TRY:
497 return tryStatement();
498
499 case TokenKind.LBRACE:
500 return block();
501 case TokenKind.SEMICOLON:
502 return emptyStatement();
503
504 case TokenKind.FINAL:
505 return declaration(false);
506 case TokenKind.VAR:
507 return declaration(false);
508
509 default:
510 // Covers var decl, func decl, labeled stmt and real expressions.
511 return finishExpressionAsStatement(expression());
512 }
513 }
514
515 finishExpressionAsStatement(expr) {
516 // TODO(jimhug): This method looks very inefficient - bundle tests.
517 int start = expr.span.start;
518
519 if (_maybeEat(TokenKind.COLON)) {
520 var label = _makeLabel(expr);
521 return new LabeledStatement(label, statement(), _makeSpan(start));
522 }
523
524 if (expr is LambdaExpression) {
525 if (expr.func.body is! BlockStatement) {
526 _eatSemicolon();
527 expr.func.span = _makeSpan(start);
528 }
529 return expr.func;
530 } else if (expr is DeclaredIdentifier) {
531 var value = null;
532 if (_maybeEat(TokenKind.ASSIGN)) {
533 value = expression();
534 }
535 return finishField(start, null, expr.type, expr.name, value);
536 } else if (_isBin(expr, TokenKind.ASSIGN) &&
537 (expr.x is DeclaredIdentifier)) {
538 DeclaredIdentifier di = expr.x; // TODO(jimhug): inference should handle!
539 return finishField(start, null, di.type, di.name, expr.y);
540 } else if (_isBin(expr, TokenKind.LT) && _maybeEat(TokenKind.COMMA)) {
541 var baseType = _makeType(expr.x);
542 var typeArgs = [_makeType(expr.y)];
543 var gt = _finishTypeArguments(baseType, 0, typeArgs);
544 var name = identifier();
545 var value = null;
546 if (_maybeEat(TokenKind.ASSIGN)) {
547 value = expression();
548 }
549 return finishField(expr.span.start, null, gt, name, value);
550 } else {
551 _eatSemicolon();
552 return new ExpressionStatement(expr, _makeSpan(expr.span.start));
553 }
554 }
555
556 Expression testCondition() {
557 _eatLeftParen();
558 var ret = expression();
559 _eat(TokenKind.RPAREN);
560 return ret;
561 }
562
563 /** Parses a block. Also is an entry point when parsing [DietStatement]. */
564 BlockStatement block() {
565 int start = _peekToken.start;
566 _eat(TokenKind.LBRACE);
567 var stmts = [];
568 while (!_maybeEat(TokenKind.RBRACE)) {
569 stmts.add(statement());
570 if (_recover && !_recoverTo(TokenKind.RBRACE, TokenKind.SEMICOLON)) break;
571 }
572 _recover = false;
573 return new BlockStatement(stmts, _makeSpan(start));
574 }
575
576 EmptyStatement emptyStatement() {
577 int start = _peekToken.start;
578 _eat(TokenKind.SEMICOLON);
579 return new EmptyStatement(_makeSpan(start));
580 }
581
582
583 IfStatement ifStatement() {
584 int start = _peekToken.start;
585 _eat(TokenKind.IF);
586 var test = testCondition();
587 var trueBranch = statement();
588 var falseBranch = null;
589 if (_maybeEat(TokenKind.ELSE)) {
590 falseBranch = statement();
591 }
592 return new IfStatement(test, trueBranch, falseBranch, _makeSpan(start));
593 }
594
595 WhileStatement whileStatement() {
596 int start = _peekToken.start;
597 _eat(TokenKind.WHILE);
598 var test = testCondition();
599 var body = statement();
600 return new WhileStatement(test, body, _makeSpan(start));
601 }
602
603 DoStatement doStatement() {
604 int start = _peekToken.start;
605 _eat(TokenKind.DO);
606 var body = statement();
607 _eat(TokenKind.WHILE);
608 var test = testCondition();
609 _eatSemicolon();
610 return new DoStatement(body, test, _makeSpan(start));
611 }
612
613 forStatement() {
614 int start = _peekToken.start;
615 _eat(TokenKind.FOR);
616 _eatLeftParen();
617
618 var init = forInitializerStatement(start);
619 if (init is ForInStatement) {
620 return init;
621 }
622 var test = null;
623 if (!_maybeEat(TokenKind.SEMICOLON)) {
624 test = expression();
625 _eatSemicolon();
626 }
627 var step = [];
628 if (!_maybeEat(TokenKind.RPAREN)) {
629 step.add(expression());
630 while (_maybeEat(TokenKind.COMMA)) {
631 step.add(expression());
632 }
633 _eat(TokenKind.RPAREN);
634 }
635
636 var body = statement();
637
638 return new ForStatement(init, test, step, body, _makeSpan(start));
639 }
640
641 forInitializerStatement(int start) {
642 if (_maybeEat(TokenKind.SEMICOLON)) {
643 return null;
644 } else {
645 var init = expression();
646 // Weird code here is needed to handle generic type and for in
647 // TODO(jmesserly): unify with block in finishExpressionAsStatement
648 if (_peekKind(TokenKind.COMMA) && _isBin(init, TokenKind.LT)) {
649 _eat(TokenKind.COMMA);
650 var baseType = _makeType(init.x);
651 var typeArgs = [_makeType(init.y)];
652 var gt = _finishTypeArguments(baseType, 0, typeArgs);
653 var name = identifier();
654 init = new DeclaredIdentifier(gt, name, false, _makeSpan(init.span.start ));
655 }
656
657 if (_maybeEat(TokenKind.IN)) {
658 return _finishForIn(start, _makeDeclaredIdentifier(init));
659 } else {
660 return finishExpressionAsStatement(init);
661 }
662 }
663 }
664
665 _finishForIn(int start, DeclaredIdentifier di) {
666 var expr = expression();
667 _eat(TokenKind.RPAREN);
668 var body = statement();
669 return new ForInStatement(di, expr, body,
670 _makeSpan(start));
671 }
672
673 tryStatement() {
674 int start = _peekToken.start;
675 _eat(TokenKind.TRY);
676 var body = block();
677 var catches = [];
678
679 while (_peekKind(TokenKind.CATCH)) {
680 catches.add(catchNode());
681 }
682
683 var finallyBlock = null;
684 if (_maybeEat(TokenKind.FINALLY)) {
685 finallyBlock = block();
686 }
687 return new TryStatement(body, catches, finallyBlock, _makeSpan(start));
688 }
689
690 catchNode() {
691 int start = _peekToken.start;
692 _eat(TokenKind.CATCH);
693 _eatLeftParen();
694 var exc = declaredIdentifier();
695 var trace = null;
696 if (_maybeEat(TokenKind.COMMA)) {
697 trace = declaredIdentifier();
698 }
699 _eat(TokenKind.RPAREN);
700 var body = block();
701 return new CatchNode(exc, trace, body, _makeSpan(start));
702 }
703
704 switchStatement() {
705 int start = _peekToken.start;
706 _eat(TokenKind.SWITCH);
707 var test = testCondition();
708 var cases = [];
709 _eat(TokenKind.LBRACE);
710 while (!_maybeEat(TokenKind.RBRACE)) {
711 cases.add(caseNode());
712 }
713 return new SwitchStatement(test, cases, _makeSpan(start));
714 }
715
716 _peekCaseEnd() {
717 var kind = _peek();
718 //TODO(efortuna): also if the first is an identifier followed by a colon, we
719 //have a label for the case statement.
720 return kind == TokenKind.RBRACE || kind == TokenKind.CASE ||
721 kind == TokenKind.DEFAULT;
722 }
723
724 caseNode() {
725 int start = _peekToken.start;
726 var label = null;
727 if (_peekIdentifier()) {
728 label = identifier();
729 _eat(TokenKind.COLON);
730 }
731 var cases = [];
732 while (true) {
733 if (_maybeEat(TokenKind.CASE)) {
734 cases.add(expression());
735 _eat(TokenKind.COLON);
736 } else if (_maybeEat(TokenKind.DEFAULT)) {
737 cases.add(null);
738 _eat(TokenKind.COLON);
739 } else {
740 break;
741 }
742 }
743 if (cases.length == 0) {
744 _error('case or default');
745 }
746 var stmts = [];
747 while (!_peekCaseEnd()) {
748 stmts.add(statement());
749 if (_recover && !_recoverTo(
750 TokenKind.RBRACE, TokenKind.CASE, TokenKind.DEFAULT)) {
751 break;
752 }
753 }
754 return new CaseNode(label, cases, stmts, _makeSpan(start));
755 }
756
757 returnStatement() {
758 int start = _peekToken.start;
759 _eat(TokenKind.RETURN);
760 var expr;
761 if (_maybeEat(TokenKind.SEMICOLON)) {
762 expr = null;
763 } else {
764 expr = expression();
765 _eatSemicolon();
766 }
767 return new ReturnStatement(expr, _makeSpan(start));
768 }
769
770 throwStatement() {
771 int start = _peekToken.start;
772 _eat(TokenKind.THROW);
773 var expr;
774 if (_maybeEat(TokenKind.SEMICOLON)) {
775 expr = null;
776 } else {
777 expr = expression();
778 _eatSemicolon();
779 }
780 return new ThrowStatement(expr, _makeSpan(start));
781 }
782
783 assertStatement() {
784 int start = _peekToken.start;
785 _eat(TokenKind.ASSERT);
786 _eatLeftParen();
787 var expr = expression();
788 _eat(TokenKind.RPAREN);
789 _eatSemicolon();
790 return new AssertStatement(expr, _makeSpan(start));
791 }
792
793 breakStatement() {
794 int start = _peekToken.start;
795 _eat(TokenKind.BREAK);
796 var name = null;
797 if (_peekIdentifier()) {
798 name = identifier();
799 }
800 _eatSemicolon();
801 return new BreakStatement(name, _makeSpan(start));
802 }
803
804 continueStatement() {
805 int start = _peekToken.start;
806 _eat(TokenKind.CONTINUE);
807 var name = null;
808 if (_peekIdentifier()) {
809 name = identifier();
810 }
811 _eatSemicolon();
812 return new ContinueStatement(name, _makeSpan(start));
813 }
814
815
816 ///////////////////////////////////////////////////////////////////
817 // Expression productions
818 ///////////////////////////////////////////////////////////////////
819 expression() {
820 return infixExpression(0);
821 }
822
823 _makeType(expr) {
824 if (expr is VarExpression) {
825 return new NameTypeReference(false, expr.name, null, expr.span);
826 } else if (expr is DotExpression) {
827 var type = _makeType(expr.self);
828 if (type.names == null) {
829 type.names = [expr.name];
830 } else {
831 type.names.add(expr.name);
832 }
833 type.span = expr.span;
834 return type;
835 } else {
836 _error('expected type reference');
837 return null;
838 }
839 }
840
841 infixExpression(int precedence) {
842 return finishInfixExpression(unaryExpression(), precedence);
843 }
844
845 _finishDeclaredId(type) {
846 var name = identifier();
847 return finishPostfixExpression(
848 new DeclaredIdentifier(type, name, false, _makeSpan(type.span.start)));
849 }
850
851 /**
852 * Takes an initial binary expression of A < B and turns it into a
853 * declared identifier included the A < B piece in the type.
854 */
855 _fixAsType(BinaryExpression x) {
856 assert(_isBin(x, TokenKind.LT));
857 // TODO(jimhug): good errors when expectations are violated
858 if (_maybeEat(TokenKind.GT)) {
859 // The simple case of A < B > just becomes a generic type
860 var base = _makeType(x.x);
861 var typeParam = _makeType(x.y);
862 var type = new GenericTypeReference(base, [typeParam], 0,
863 _makeSpan(x.span.start));
864 return _finishDeclaredId(type);
865 } else {
866 // The case of A < B < kicks off a lot more parsing.
867 assert(_peekKind(TokenKind.LT));
868
869 var base = _makeType(x.x);
870 var paramBase = _makeType(x.y);
871 var firstParam = addTypeArguments(paramBase, 1);
872
873 var type;
874 if (firstParam.depth <= 0) {
875 type = new GenericTypeReference(base, [firstParam], 0,
876 _makeSpan(x.span.start));
877 } else if (_maybeEat(TokenKind.COMMA)) {
878 type = _finishTypeArguments(base, 0, [firstParam]);
879 } else {
880 _eat(TokenKind.GT);
881 type = new GenericTypeReference(base, [firstParam], 0,
882 _makeSpan(x.span.start));
883 }
884 return _finishDeclaredId(type);
885 }
886 }
887
888 finishInfixExpression(Expression x, int precedence) {
889 while (true) {
890 int kind = _peek();
891 var prec = TokenKind.infixPrecedence(_peek());
892 if (prec >= precedence) {
893 if (kind == TokenKind.LT || kind == TokenKind.GT) {
894 if (_isBin(x, TokenKind.LT)) {
895 // This must be a generic type according the the Dart grammar.
896 // This rule is in the grammar to forbid A < B < C and
897 // A < B > C as expressions both because they don't make sense
898 // and to make it easier to disambiguate the generic types.
899 // There are a number of other comparison operators that are
900 // also unallowed to nest in this way, but in the spirit of this
901 // "friendly" parser, those will be allowed until a later phase.
902 return _fixAsType(x);
903 }
904 }
905 var op = _next();
906 if (op.kind == TokenKind.IS) {
907 var isTrue = !_maybeEat(TokenKind.NOT);
908 var typeRef = type();
909 x = new IsExpression(isTrue, x, typeRef, _makeSpan(x.span.start));
910 continue;
911 }
912 // Using prec + 1 ensures that a - b - c will group correctly.
913 // Using prec for ASSIGN ops ensures that a = b = c groups correctly.
914 var y = infixExpression(prec == 2 ? prec: prec+1);
915 if (op.kind == TokenKind.CONDITIONAL) {
916 _eat(TokenKind.COLON);
917 // Using prec for so "a ? b : c ? d : e" groups correctly as
918 // "a ? b : (c ? d : e)"
919 var z = infixExpression(prec);
920 x = new ConditionalExpression(x, y, z, _makeSpan(x.span.start));
921 } else {
922 x = new BinaryExpression(op, x, y, _makeSpan(x.span.start));
923 }
924 } else {
925 break;
926 }
927 }
928 return x;
929 }
930
931 _isPrefixUnaryOperator(int kind) {
932 switch(kind) {
933 case TokenKind.ADD:
934 case TokenKind.SUB:
935 case TokenKind.NOT:
936 case TokenKind.BIT_NOT:
937 case TokenKind.INCR:
938 case TokenKind.DECR:
939 return true;
940 default:
941 return false;
942 }
943 }
944
945 unaryExpression() {
946 int start = _peekToken.start;
947 // peek for prefixOperators and incrementOperators
948 if (_isPrefixUnaryOperator(_peek())) {
949 var tok = _next();
950 var expr = unaryExpression();
951 return new UnaryExpression(tok, expr, _makeSpan(start));
952 } else if (enableAwait && _maybeEat(TokenKind.AWAIT)) {
953 var expr = unaryExpression();
954 return new AwaitExpression(expr, _makeSpan(start));
955 }
956
957 return finishPostfixExpression(primary());
958 }
959
960 argument() {
961 int start = _peekToken.start;
962 var expr;
963 var label = null;
964 if (_maybeEat(TokenKind.ELLIPSIS)) {
965 label = new Identifier('...', _makeSpan(start));
966 }
967 expr = expression();
968 if (label == null && _maybeEat(TokenKind.COLON)) {
969 label = _makeLabel(expr);
970 expr = expression();
971 }
972 return new ArgumentNode(label, expr, _makeSpan(start));
973 }
974
975 arguments() {
976 var args = [];
977 _eatLeftParen();
978 var saved = _inhibitLambda;
979 _inhibitLambda = false;
980 if (!_maybeEat(TokenKind.RPAREN)) {
981 do {
982 args.add(argument());
983 } while (_maybeEat(TokenKind.COMMA));
984 _eat(TokenKind.RPAREN);
985 }
986 _inhibitLambda = saved;
987 return args;
988 }
989
990 finishPostfixExpression(expr) {
991 switch(_peek()) {
992 case TokenKind.LPAREN:
993 return finishCallOrLambdaExpression(expr);
994 case TokenKind.LBRACK:
995 _eat(TokenKind.LBRACK);
996 var index = expression();
997 _eat(TokenKind.RBRACK);
998 return finishPostfixExpression(new IndexExpression(expr, index,
999 _makeSpan(expr.span.start)));
1000 case TokenKind.DOT:
1001 _eat(TokenKind.DOT);
1002 var name = identifier();
1003 var ret = new DotExpression(expr, name, _makeSpan(expr.span.start));
1004 return finishPostfixExpression(ret);
1005
1006 case TokenKind.INCR:
1007 case TokenKind.DECR:
1008 var tok = _next();
1009 return new PostfixExpression(expr, tok, _makeSpan(expr.span.start));
1010
1011 // These are pseudo-expressions supported for cover grammar
1012 // must be forbidden when parsing initializers.
1013 // TODO(jmesserly): is this still needed?
1014 case TokenKind.ARROW:
1015 case TokenKind.LBRACE:
1016 return expr;
1017
1018 default:
1019 if (_peekIdentifier()) {
1020 return finishPostfixExpression(
1021 new DeclaredIdentifier(_makeType(expr), identifier(),
1022 false, _makeSpan(expr.span.start)));
1023 } else {
1024 return expr;
1025 }
1026 }
1027 }
1028
1029 finishCallOrLambdaExpression(expr) {
1030 if (_atClosureParameters()) {
1031 var formals = formalParameterList();
1032 var body = functionBody(true);
1033 return _makeFunction(expr, formals, body);
1034 } else {
1035 if (expr is DeclaredIdentifier) {
1036 _error('illegal target for call, did you mean to declare a function?',
1037 expr.span);
1038 }
1039 var args = arguments();
1040 return finishPostfixExpression(
1041 new CallExpression(expr, args, _makeSpan(expr.span.start)));
1042 }
1043 }
1044
1045 /** Checks if the given expression is a binary op of the given kind. */
1046 _isBin(expr, kind) {
1047 return expr is BinaryExpression && expr.op.kind == kind;
1048 }
1049
1050 _makeLiteral(Value value) {
1051 return new LiteralExpression(value, value.span);
1052 }
1053
1054 primary() {
1055 int start = _peekToken.start;
1056 switch (_peek()) {
1057 case TokenKind.THIS:
1058 _eat(TokenKind.THIS);
1059 return new ThisExpression(_makeSpan(start));
1060
1061 case TokenKind.SUPER:
1062 _eat(TokenKind.SUPER);
1063 return new SuperExpression(_makeSpan(start));
1064
1065 case TokenKind.CONST:
1066 _eat(TokenKind.CONST);
1067 if (_peekKind(TokenKind.LBRACK) || _peekKind(TokenKind.INDEX)) {
1068 return finishListLiteral(start, true, null);
1069 } else if (_peekKind(TokenKind.LBRACE)) {
1070 return finishMapLiteral(start, true, null, null);
1071 } else if (_peekKind(TokenKind.LT)) {
1072 return finishTypedLiteral(start, true);
1073 } else {
1074 return finishNewExpression(start, true);
1075 }
1076
1077 case TokenKind.NEW:
1078 _eat(TokenKind.NEW);
1079 return finishNewExpression(start, false);
1080
1081 case TokenKind.LPAREN:
1082 return _parenOrLambda();
1083
1084 case TokenKind.LBRACK:
1085 case TokenKind.INDEX:
1086 return finishListLiteral(start, false, null);
1087 case TokenKind.LBRACE:
1088 return finishMapLiteral(start, false, null, null);
1089
1090 // Literals
1091 case TokenKind.NULL:
1092 _eat(TokenKind.NULL);
1093 return _makeLiteral(Value.fromNull(_makeSpan(start)));
1094
1095 // TODO(jimhug): Make Literal creation less wasteful - no dup span/text.
1096 case TokenKind.TRUE:
1097 _eat(TokenKind.TRUE);
1098 return _makeLiteral(Value.fromBool(true, _makeSpan(start)));
1099
1100 case TokenKind.FALSE:
1101 _eat(TokenKind.FALSE);
1102 return _makeLiteral(Value.fromBool(false, _makeSpan(start)));
1103
1104 case TokenKind.HEX_INTEGER:
1105 var t = _next();
1106 return _makeLiteral(Value.fromInt(t.value, t.span));
1107
1108 case TokenKind.INTEGER:
1109 var t = _next();
1110 return _makeLiteral(Value.fromInt(Math.parseInt(t.text), t.span));
1111
1112 case TokenKind.DOUBLE:
1113 var t = _next();
1114 return _makeLiteral(
1115 Value.fromDouble(Math.parseDouble(t.text), t.span));
1116
1117 case TokenKind.STRING:
1118 case TokenKind.STRING_PART:
1119 return adjacentStrings();
1120
1121 case TokenKind.LT:
1122 return finishTypedLiteral(start, false);
1123
1124 case TokenKind.VOID:
1125 case TokenKind.VAR:
1126 case TokenKind.FINAL:
1127 return declaredIdentifier(false);
1128
1129 default:
1130 if (!_peekIdentifier()) {
1131 // TODO(jimhug): Better error message.
1132 _errorExpected('expression');
1133 }
1134 return new VarExpression(identifier(), _makeSpan(start));
1135 }
1136 }
1137
1138 adjacentStrings() {
1139 int start = _peekToken.start;
1140 List<Expression> strings = [];
1141 while (_peek() == TokenKind.STRING || _peek() == TokenKind.STRING_PART) {
1142 Expression part = null;
1143 if (_peek() == TokenKind.STRING) {
1144 var t = _next();
1145 part = _makeLiteral(Value.fromString(t.value, t.span));
1146 } else {
1147 part = stringInterpolation();
1148 }
1149 strings.add(part);
1150 }
1151 if (strings.length == 1) {
1152 return strings[0];
1153 } else {
1154 assert(!strings.isEmpty());
1155 return new StringConcatExpression(strings, _makeSpan(start));
1156 }
1157 }
1158
1159 stringInterpolation() {
1160 int start = _peekToken.start;
1161 var pieces = new List<Expression>();
1162 var startQuote = null, endQuote = null;
1163 while(_peekKind(TokenKind.STRING_PART)) {
1164 var token = _next();
1165 pieces.add(_makeLiteral(Value.fromString(token.value, token.span)));
1166 if (_maybeEat(TokenKind.LBRACE)) {
1167 pieces.add(expression());
1168 _eat(TokenKind.RBRACE);
1169 } else if (_maybeEat(TokenKind.THIS)) {
1170 pieces.add(new ThisExpression(_previousToken.span));
1171 } else {
1172 var id = identifier();
1173 pieces.add(new VarExpression(id, id.span));
1174 }
1175 }
1176 var tok = _next();
1177 if (tok.kind != TokenKind.STRING) {
1178 _errorExpected('interpolated string');
1179 }
1180 pieces.add(_makeLiteral(Value.fromString(tok.value, tok.span)));
1181 var span = _makeSpan(start);
1182 return new StringInterpExpression(pieces, span);
1183 }
1184
1185 String maybeStringLiteral() {
1186 var kind = _peek();
1187 if (kind == TokenKind.STRING) {
1188 var t = _next();
1189 return t.value;
1190 } else if (kind == TokenKind.STRING_PART) {
1191 _next();
1192 _errorExpected('string literal, but found interpolated string start');
1193 }
1194 return null;
1195 }
1196
1197 _parenOrLambda() {
1198 int start = _peekToken.start;
1199 if (_atClosureParameters()) {
1200 var formals = formalParameterList();
1201 var body = functionBody(true);
1202 var func = new FunctionDefinition(null, null, null, formals, null, null,
1203 body, _makeSpan(start));
1204 return new LambdaExpression(func, func.span);
1205 } else {
1206 _eatLeftParen();
1207 var saved = _inhibitLambda;
1208 _inhibitLambda = false;
1209 var expr = expression();
1210 _eat(TokenKind.RPAREN);
1211 _inhibitLambda = saved;
1212 return new ParenExpression(expr, _makeSpan(start));
1213 }
1214 }
1215
1216 bool _atClosureParameters() {
1217 if (_inhibitLambda) return false;
1218 Token after = _peekAfterCloseParen();
1219 return after.kind == TokenKind.ARROW || after.kind == TokenKind.LBRACE;
1220 }
1221
1222 /** Eats an LPAREN, and advances our after-RPAREN lookahead. */
1223 _eatLeftParen() {
1224 _eat(TokenKind.LPAREN);
1225 _afterParensIndex++;
1226 }
1227
1228 Token _peekAfterCloseParen() {
1229 if (_afterParensIndex < _afterParens.length) {
1230 return _afterParens[_afterParensIndex];
1231 }
1232
1233 // Reset the queue
1234 _afterParensIndex = 0;
1235 _afterParens.clear();
1236
1237 // Start copying tokens as we lookahead
1238 var tokens = <Token>[_next()]; // LPAREN
1239 _lookaheadAfterParens(tokens);
1240
1241 // Put all the lookahead tokens back into the parser's token stream.
1242 var after = _peekToken;
1243 tokens.add(after);
1244 tokenizer = new DivertedTokenSource(tokens, this, tokenizer);
1245 _next(); // Re-synchronize parser lookahead state.
1246 return after;
1247 }
1248
1249 /**
1250 * This scan for the matching RPAREN to the current LPAREN and saves this
1251 * result for all nested parentheses so we don't need to look-head again.
1252 */
1253 _lookaheadAfterParens(List<Token> tokens) {
1254 // Save a slot in the array. This will hold the token after the parens.
1255 int saved = _afterParens.length;
1256 _afterParens.add(null); // save a slot
1257 while (true) {
1258 Token token = _next();
1259 tokens.add(token);
1260 int kind = token.kind;
1261 if (kind == TokenKind.RPAREN || kind == TokenKind.END_OF_FILE) {
1262 _afterParens[saved] = _peekToken;
1263 return;
1264 } else if (kind == TokenKind.LPAREN) {
1265 // Scan anything inside these nested parenthesis
1266 _lookaheadAfterParens(tokens);
1267 }
1268 }
1269 }
1270
1271 _typeAsIdentifier(type) {
1272 if (type.name.name == 'void') {
1273 _errorExpected('identifer, but found "${type.name.name}"');
1274 }
1275
1276 // TODO(jimhug): lots of errors to check for
1277 return type.name;
1278 }
1279
1280 _specialIdentifier(bool includeOperators) {
1281 int start = _peekToken.start;
1282 String name;
1283
1284 switch (_peek()) {
1285 case TokenKind.ELLIPSIS:
1286 _eat(TokenKind.ELLIPSIS);
1287 _error('rest no longer supported', _previousToken.span);
1288 name = identifier().name;
1289 break;
1290 case TokenKind.THIS:
1291 _eat(TokenKind.THIS);
1292 _eat(TokenKind.DOT);
1293 name = 'this.${identifier().name}';
1294 break;
1295 case TokenKind.GET:
1296 if (!includeOperators) return null;
1297 _eat(TokenKind.GET);
1298 if (_peekIdentifier()) {
1299 name = 'get:${identifier().name}';
1300 } else {
1301 name = 'get';
1302 }
1303 break;
1304 case TokenKind.SET:
1305 if (!includeOperators) return null;
1306 _eat(TokenKind.SET);
1307 if (_peekIdentifier()) {
1308 name = 'set:${identifier().name}';
1309 } else {
1310 name = 'set';
1311 }
1312 break;
1313 case TokenKind.OPERATOR:
1314 if (!includeOperators) return null;
1315 _eat(TokenKind.OPERATOR);
1316 var kind = _peek();
1317 if (kind == TokenKind.NEGATE) {
1318 name = ':negate';
1319 _next();
1320 } else {
1321 name = TokenKind.binaryMethodName(kind);
1322 if (name == null) {
1323 // TODO(jimhug): This is a very useful error, but we have to
1324 // lose it because operator is a pseudo-keyword...
1325 //_errorExpected('legal operator name, but found: ${tok}');
1326 name = 'operator';
1327 } else {
1328 _next();
1329 }
1330 }
1331 break;
1332 default:
1333 return null;
1334 }
1335 return new Identifier(name, _makeSpan(start));
1336 }
1337
1338 // always includes this and ... as legal names to simplify other code.
1339 declaredIdentifier([bool includeOperators=false]) {
1340 int start = _peekToken.start;
1341 var myType = null;
1342 var name = _specialIdentifier(includeOperators);
1343 bool isFinal = false;
1344 if (name == null) {
1345 myType = type();
1346 name = _specialIdentifier(includeOperators);
1347 if (name == null) {
1348 if (_peekIdentifier()) {
1349 name = identifier();
1350 } else if (myType is NameTypeReference && myType.names == null) {
1351 name = _typeAsIdentifier(myType);
1352 isFinal = myType.isFinal;
1353 myType = null;
1354 } else {
1355 // TODO(jimhug): Where do these errors get handled?
1356 }
1357 }
1358 }
1359 return new DeclaredIdentifier(myType, name, isFinal, _makeSpan(start));
1360 }
1361
1362 finishNewExpression(int start, bool isConst) {
1363 var type = type();
1364 var name = null;
1365 if (_maybeEat(TokenKind.DOT)) {
1366 name = identifier();
1367 }
1368 var args = arguments();
1369 return new NewExpression(isConst, type, name, args, _makeSpan(start));
1370 }
1371
1372 finishListLiteral(int start, bool isConst, TypeReference itemType) {
1373 if (_maybeEat(TokenKind.INDEX)) {
1374 // This is an empty array.
1375 return new ListExpression(isConst, itemType, [], _makeSpan(start));
1376 }
1377
1378 var values = [];
1379 _eat(TokenKind.LBRACK);
1380 while (!_maybeEat(TokenKind.RBRACK)) {
1381 values.add(expression());
1382 if (_recover && !_recoverTo(TokenKind.RBRACK, TokenKind.COMMA)) break;
1383 if (!_maybeEat(TokenKind.COMMA)) {
1384 _eat(TokenKind.RBRACK);
1385 break;
1386 }
1387 }
1388 return new ListExpression(isConst, itemType, values, _makeSpan(start));
1389 }
1390
1391 finishMapLiteral(int start, bool isConst,
1392 TypeReference keyType, TypeReference valueType) {
1393 var items = [];
1394 _eat(TokenKind.LBRACE);
1395 while (!_maybeEat(TokenKind.RBRACE)) {
1396 // This is deliberately overly permissive - checked in later pass.
1397 items.add(expression());
1398 _eat(TokenKind.COLON);
1399 items.add(expression());
1400 if (_recover && !_recoverTo(TokenKind.RBRACE, TokenKind.COMMA)) break;
1401 if (!_maybeEat(TokenKind.COMMA)) {
1402 _eat(TokenKind.RBRACE);
1403 break;
1404 }
1405 }
1406 return new MapExpression(isConst, keyType, valueType, items,
1407 _makeSpan(start));
1408 }
1409
1410 finishTypedLiteral(int start, bool isConst) {
1411 var span = _makeSpan(start);
1412
1413 final typeToBeNamedLater = new NameTypeReference(false, null, null, span);
1414 final genericType = addTypeArguments(typeToBeNamedLater, 0);
1415 final typeArgs = genericType.typeArguments;
1416
1417 if (_peekKind(TokenKind.LBRACK) || _peekKind(TokenKind.INDEX)) {
1418 if (typeArgs.length != 1) {
1419 world.error('exactly one type argument expected for list',
1420 genericType.span);
1421 }
1422 return finishListLiteral(start, isConst, typeArgs[0]);
1423 } else if (_peekKind(TokenKind.LBRACE)) {
1424 var keyType, valueType;
1425 if (typeArgs.length == 1) {
1426 keyType = null;
1427 valueType = typeArgs[0];
1428 } else if (typeArgs.length == 2) {
1429 keyType = typeArgs[0];
1430 // making key explicit is just a warning.
1431 world.warning(
1432 'a map literal takes one type argument specifying the value type',
1433 keyType.span);
1434 valueType = typeArgs[1];
1435 } // o.w. the type system will detect the mismatch in type arguments.
1436 return finishMapLiteral(start, isConst, keyType, valueType);
1437 } else {
1438 _errorExpected('array or map literal');
1439 }
1440 }
1441
1442 ///////////////////////////////////////////////////////////////////
1443 // Some auxilary productions.
1444 ///////////////////////////////////////////////////////////////////
1445 _readModifiers() {
1446 var modifiers = null;
1447 while (true) {
1448 switch(_peek()) {
1449 case TokenKind.STATIC:
1450 case TokenKind.FINAL:
1451 case TokenKind.CONST:
1452 case TokenKind.ABSTRACT:
1453 case TokenKind.FACTORY:
1454 if (modifiers == null) modifiers = [];
1455 modifiers.add(_next());
1456 break;
1457 default:
1458 return modifiers;
1459 }
1460 }
1461
1462 return null;
1463 }
1464
1465 ParameterType typeParameter() {
1466 // non-recursive - so always starts from zero depth
1467 int start = _peekToken.start;
1468 var name = identifier();
1469 var myType = null;
1470 if (_maybeEat(TokenKind.EXTENDS)) {
1471 myType = type(1);
1472 }
1473
1474 var tp = new TypeParameter(name, myType, _makeSpan(start));
1475 return new ParameterType(name.name, tp);
1476 }
1477
1478 List<ParameterType> typeParameters() {
1479 // always starts from zero depth
1480 _eat(TokenKind.LT);
1481
1482 bool closed = false;
1483 var ret = [];
1484 do {
1485 var tp = typeParameter();
1486 ret.add(tp);
1487 if (tp.typeParameter.extendsType is GenericTypeReference &&
1488 tp.typeParameter.extendsType.dynamic.depth == 0) {
1489 closed = true;
1490 break;
1491 }
1492 } while (_maybeEat(TokenKind.COMMA));
1493 if (!closed) {
1494 _eat(TokenKind.GT);
1495 }
1496 return ret;
1497 }
1498
1499 int _eatClosingAngle(int depth) {
1500 if (_maybeEat(TokenKind.GT)) {
1501 return depth;
1502 } else if (depth > 0 && _maybeEat(TokenKind.SAR)) {
1503 return depth-1;
1504 } else if (depth > 1 && _maybeEat(TokenKind.SHR)) {
1505 return depth-2;
1506 } else {
1507 _errorExpected('>');
1508 return depth;
1509 }
1510 }
1511
1512 addTypeArguments(TypeReference baseType, int depth) {
1513 _eat(TokenKind.LT);
1514 return _finishTypeArguments(baseType, depth, []);
1515 }
1516
1517 _finishTypeArguments(TypeReference baseType, int depth, types) {
1518 var delta = -1;
1519 do {
1520 var myType = type(depth+1);
1521 types.add(myType);
1522 if (myType is GenericTypeReference && myType.depth <= depth) {
1523 // TODO(jimhug): Friendly error if peek(COMMA).
1524 delta = depth - myType.depth;
1525 break;
1526 }
1527 } while (_maybeEat(TokenKind.COMMA));
1528 if (delta >= 0) {
1529 depth -= delta;
1530 } else {
1531 depth = _eatClosingAngle(depth);
1532 }
1533
1534 var span = _makeSpan(baseType.span.start);
1535 return new GenericTypeReference(baseType, types, depth, span);
1536 }
1537
1538 typeList() {
1539 var types = [];
1540 do {
1541 types.add(type());
1542 } while (_maybeEat(TokenKind.COMMA));
1543
1544 return types;
1545 }
1546
1547 nameTypeReference() {
1548 int start = _peekToken.start;
1549 var name;
1550 var names = null;
1551 var typeArgs = null;
1552 var isFinal = false;
1553
1554 switch (_peek()) {
1555 case TokenKind.VOID:
1556 return new SimpleTypeReference(world.voidType, _next().span);
1557 case TokenKind.VAR:
1558 return new SimpleTypeReference(world.varType, _next().span);
1559 case TokenKind.FINAL:
1560 _eat(TokenKind.FINAL);
1561 isFinal = true;
1562 name = identifier();
1563 break;
1564 default:
1565 name = identifier();
1566 break;
1567 }
1568
1569 while (_maybeEat(TokenKind.DOT)) {
1570 if (names == null) names = [];
1571 names.add(identifier());
1572 }
1573
1574 return new NameTypeReference(isFinal, name, names, _makeSpan(start));
1575 }
1576
1577 type([int depth = 0]) {
1578 var typeRef = nameTypeReference();
1579
1580 if (_peekKind(TokenKind.LT)) {
1581 return addTypeArguments(typeRef, depth);
1582 } else {
1583 return typeRef;
1584 }
1585 }
1586
1587 formalParameter(bool inOptionalBlock) {
1588 int start = _peekToken.start;
1589 var isThis = false;
1590 var isRest = false;
1591 var di = declaredIdentifier(false);
1592 var type = di.type;
1593 var name = di.name;
1594
1595 if (name == null) {
1596 _error('Formal parameter invalid', _makeSpan(start));
1597 }
1598
1599 var value = null;
1600 if (_maybeEat(TokenKind.ASSIGN)) {
1601 if (!inOptionalBlock) {
1602 _error('default values only allowed inside [optional] section');
1603 }
1604 value = expression();
1605 } else if (_peekKind(TokenKind.LPAREN)) {
1606 var formals = formalParameterList();
1607 var func = new FunctionDefinition(null, type, name, formals,
1608 null, null, null, _makeSpan(start));
1609 type = new FunctionTypeReference(false, func, func.span);
1610 }
1611 if (inOptionalBlock && value == null) {
1612 value = _makeLiteral(Value.fromNull(_makeSpan(start)));
1613 }
1614
1615 return new FormalNode(isThis, isRest, type, name, value, _makeSpan(start));
1616 }
1617
1618 formalParameterList() {
1619 _eatLeftParen();
1620 var formals = [];
1621 var inOptionalBlock = false;
1622 if (!_maybeEat(TokenKind.RPAREN)) {
1623 if (_maybeEat(TokenKind.LBRACK)) {
1624 inOptionalBlock = true;
1625 }
1626 formals.add(formalParameter(inOptionalBlock));
1627 while (_maybeEat(TokenKind.COMMA)) {
1628 if (_maybeEat(TokenKind.LBRACK)) {
1629 if (inOptionalBlock) {
1630 _error('already inside an optional block', _previousToken.span);
1631 }
1632 inOptionalBlock = true;
1633 }
1634 formals.add(formalParameter(inOptionalBlock));
1635 }
1636 if (inOptionalBlock) {
1637 _eat(TokenKind.RBRACK);
1638 }
1639 _eat(TokenKind.RPAREN);
1640 }
1641 return formals;
1642 }
1643
1644 // Type names are not allowed to use pseudo keywords
1645 identifierForType() {
1646 var tok = _next();
1647 if (!_isIdentifier(tok.kind)) {
1648 _error('expected identifier, but found $tok', tok.span);
1649 }
1650 if (tok.kind !== TokenKind.IDENTIFIER && tok.kind != TokenKind.NATIVE) {
1651 _error('$tok may not be used as a type name', tok.span);
1652 }
1653 return new Identifier(tok.text, _makeSpan(tok.start));
1654 }
1655
1656 identifier() {
1657 var tok = _next();
1658 if (!_isIdentifier(tok.kind)) {
1659 _error('expected identifier, but found $tok', tok.span);
1660 }
1661
1662 return new Identifier(tok.text, _makeSpan(tok.start));
1663 }
1664
1665 ///////////////////////////////////////////////////////////////////
1666 // These last productions handle most ambiguities in grammar
1667 // They will convert expressions into other types.
1668 ///////////////////////////////////////////////////////////////////
1669
1670 /**
1671 * Converts an [Expression], [Formals] and a [Statment] body into a
1672 * [FunctionDefinition].
1673 */
1674 _makeFunction(expr, formals, body) {
1675 var name, type;
1676 if (expr is VarExpression) {
1677 name = expr.name;
1678 type = null;
1679 } else if (expr is DeclaredIdentifier) {
1680 name = expr.name;
1681 type = expr.type;
1682 if (name == null) {
1683 _error('expected name and type', expr.span);
1684 }
1685 } else {
1686 _error('bad function body', expr.span);
1687 }
1688 var span = new SourceSpan(expr.span.file, expr.span.start, body.span.end);
1689 var func = new FunctionDefinition(null, type, name, formals, null, null,
1690 body, span);
1691 return new LambdaExpression(func, func.span);
1692 }
1693
1694 /** Converts an expression to a [DeclaredIdentifier]. */
1695 _makeDeclaredIdentifier(e) {
1696 if (e is VarExpression) {
1697 return new DeclaredIdentifier(null, e.name, false, e.span);
1698 } else if (e is DeclaredIdentifier) {
1699 return e;
1700 } else {
1701 _error('expected declared identifier');
1702 return new DeclaredIdentifier(null, null, false, e.span);
1703 }
1704 }
1705
1706 /** Converts an expression into a label. */
1707 _makeLabel(expr) {
1708 if (expr is VarExpression) {
1709 return expr.name;
1710 } else {
1711 _errorExpected('label');
1712 return null;
1713 }
1714 }
1715 }
1716
1717 class IncompleteSourceException implements Exception {
1718 final Token token;
1719
1720 IncompleteSourceException(this.token);
1721
1722 String toString() {
1723 if (token.span == null) return 'Unexpected $token';
1724 return token.span.toMessageString('Unexpected $token');
1725 }
1726 }
1727
1728 /**
1729 * Stores a token stream that will be used by the parser. Once the parser has
1730 * reached the end of this [TokenSource], it switches back to the
1731 * [previousTokenizer]
1732 */
1733 class DivertedTokenSource implements TokenSource {
1734 final List<Token> tokens;
1735 final Parser parser;
1736 final TokenSource previousTokenizer;
1737 DivertedTokenSource(this.tokens, this.parser, this.previousTokenizer);
1738
1739 int _pos = 0;
1740 next() {
1741 var token = tokens[_pos];
1742 ++_pos;
1743 if (_pos == tokens.length) {
1744 parser.tokenizer = previousTokenizer;
1745 }
1746 return token;
1747 }
1748 }
OLDNEW
« no previous file with comments | « frog/minfrogc.dart ('k') | frog/presubmit.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698