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

Side by Side Diff: parser.dart

Issue 8400017: Peek past balanced parens to see if they are an expression or lambda. (Closed) Base URL: https://dart.googlecode.com/svn/experimental/frog
Patch Set: expand comments Created 9 years, 1 month 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 | « frogsh ('k') | tests/frog/frog.status » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 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 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 // TODO(jimhug): Error recovery needs major work! 5 // TODO(jimhug): Error recovery needs major work!
6 /** 6 /**
7 * A simple recursive descent parser for the dart language. 7 * A simple recursive descent parser for the dart language.
8 * 8 *
9 * This parser is designed to be more permissive than the official 9 * This parser is designed to be more permissive than the official
10 * Dart grammar. It is expected that many grammar errors would be 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 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 12 * to extend an arbitrary number of base classes - this can be
13 * very clearly detected and is reported in a later compiler phase. 13 * very clearly detected and is reported in a later compiler phase.
14 */ 14 */
15 class Parser { 15 class Parser {
16 Tokenizer tokenizer; 16 TokenSource tokenizer;
jimhug 2011/10/31 14:16:46 Note: This change seems good independent of the re
17 17
18 final SourceFile source; 18 final SourceFile source;
19 /** Enables diet parse, which skips function bodies. */ 19 /** Enables diet parse, which skips function bodies. */
20 final bool diet; 20 final bool diet;
21 21
22 // TODO(jimhug): Is it possible to handle initializers cleanly? 22 // TODO(jimhug): Is it possible to handle initializers cleanly?
23 bool _inInitializers; 23 bool _inInitializers;
24 24
25 Token _previousToken; 25 Token _previousToken;
26 Token _peekToken; 26 Token _peekToken;
27 27
28 // Map from start position of a '(' to token following the matching ')'. Used
29 // to distinguish closure formal parameter lists from parenthesised
30 // expressions and argument lists. Closure formals are followed by '=>' or
31 // '{'.
32 Map<int, Token> _afterCloseParenCache;
33 int _highestCachePosition = -1;
34
28 Parser(this.source, [this.diet = false, int startOffset = 0]) { 35 Parser(this.source, [this.diet = false, int startOffset = 0]) {
29 tokenizer = new Tokenizer(source, true, startOffset); 36 tokenizer = new Tokenizer(source, true, startOffset);
30 _peekToken = tokenizer.next(); 37 _peekToken = tokenizer.next();
31 _previousToken = null; 38 _previousToken = null;
32 _inInitializers = false; 39 _inInitializers = false;
40
41 _afterCloseParenCache = new Map<int, Token>();
33 } 42 }
34 43
35 /** Generate an error if [source] has not been completely consumed. */ 44 /** Generate an error if [source] has not been completely consumed. */
36 void checkEndOfFile() { 45 void checkEndOfFile() {
37 _eat(TokenKind.END_OF_FILE); 46 _eat(TokenKind.END_OF_FILE);
38 } 47 }
39 48
40 /** Guard to break out of parser when an unexpected end of file is found. */ 49 /** Guard to break out of parser when an unexpected end of file is found. */
41 // TODO(jimhug): Failure to call this method can lead to inifinite parser 50 // TODO(jimhug): Failure to call this method can lead to inifinite parser
42 // loops. Consider embracing exceptions for more errors to reduce 51 // loops. Consider embracing exceptions for more errors to reduce
(...skipping 820 matching lines...) Expand 10 before | Expand all | Expand 10 after
863 expr = expression(); 872 expr = expression();
864 if (label === null && _maybeEat(TokenKind.COLON)) { 873 if (label === null && _maybeEat(TokenKind.COLON)) {
865 label = _makeLabel(expr); 874 label = _makeLabel(expr);
866 expr = expression(); 875 expr = expression();
867 } 876 }
868 return new ArgumentNode(label, expr, _makeSpan(start)); 877 return new ArgumentNode(label, expr, _makeSpan(start));
869 } 878 }
870 879
871 arguments() { 880 arguments() {
872 var args = []; 881 var args = [];
873 // TODO(jimhug): switch to forced formals when get a DeclaredId
874 _eat(TokenKind.LPAREN); 882 _eat(TokenKind.LPAREN);
875 if (!_maybeEat(TokenKind.RPAREN)) { 883 if (!_maybeEat(TokenKind.RPAREN)) {
876 do { 884 do {
877 args.add(argument()); 885 args.add(argument());
878 } while (_maybeEat(TokenKind.COMMA)); 886 } while (_maybeEat(TokenKind.COMMA));
879 _eat(TokenKind.RPAREN); 887 _eat(TokenKind.RPAREN);
880 } 888 }
881 return args; 889 return args;
882 } 890 }
883 891
884 finishPostfixExpression(expr) { 892 finishPostfixExpression(expr) {
885 switch(_peek()) { 893 switch(_peek()) {
886 case TokenKind.LPAREN: 894 case TokenKind.LPAREN:
887 return finishPostfixExpression(new CallExpression(expr, arguments(), 895 return finishCallOrLambdaExpression(expr);
888 _makeSpan(expr.span.start)));
889 case TokenKind.LBRACK: 896 case TokenKind.LBRACK:
890 _eat(TokenKind.LBRACK); 897 _eat(TokenKind.LBRACK);
891 var index = expression(); 898 var index = expression();
892 _eat(TokenKind.RBRACK); 899 _eat(TokenKind.RBRACK);
893 return finishPostfixExpression(new IndexExpression(expr, index, 900 return finishPostfixExpression(new IndexExpression(expr, index,
894 _makeSpan(expr.span.start))); 901 _makeSpan(expr.span.start)));
895 case TokenKind.DOT: 902 case TokenKind.DOT:
896 _eat(TokenKind.DOT); 903 _eat(TokenKind.DOT);
897 var name = identifier(); 904 var name = identifier();
898 var ret = new DotExpression(expr, name, _makeSpan(expr.span.start)); 905 var ret = new DotExpression(expr, name, _makeSpan(expr.span.start));
899 return finishPostfixExpression(ret); 906 return finishPostfixExpression(ret);
900 907
901 case TokenKind.INCR: 908 case TokenKind.INCR:
902 case TokenKind.DECR: 909 case TokenKind.DECR:
903 var tok = _next(); 910 var tok = _next();
904 return new PostfixExpression(expr, tok, _makeSpan(expr.span.start)); 911 return new PostfixExpression(expr, tok, _makeSpan(expr.span.start));
905 912
906 // These are pseudo-expressions supported for cover grammar 913 // These are pseudo-expressions supported for cover grammar
907 // must be forbidden when parsing initializers. 914 // must be forbidden when parsing initializers.
915
908 case TokenKind.ARROW: 916 case TokenKind.ARROW:
909 case TokenKind.LBRACE: 917 case TokenKind.LBRACE:
910 if (_inInitializers) return expr; 918 return expr;
911 var body = functionBody(true);
912 return _makeFunction(expr, body);
913 919
914 default: 920 default:
915 if (_peekIdentifier()) { 921 if (_peekIdentifier()) {
916 return finishPostfixExpression( 922 return finishPostfixExpression(
917 new DeclaredIdentifier(_makeType(expr), identifier(), 923 new DeclaredIdentifier(_makeType(expr), identifier(),
918 _makeSpan(expr.span.start))); 924 _makeSpan(expr.span.start)));
919 } else { 925 } else {
920 return expr; 926 return expr;
921 } 927 }
922 } 928 }
923 } 929 }
924 930
931 finishCallOrLambdaExpression(expr) {
932 if (!_inInitializers && _atClosureParameters()) {
933 var formals = formalParameterList();
934 var body = functionBody(true);
935 return _makeFunction(expr, formals, body);
936 } else {
937 var args = arguments();
938 return finishPostfixExpression(
939 new CallExpression(expr, args, _makeSpan(expr.span.start)));
940 }
941 }
942
925 /** Checks if the given expression is a binary op of the given kind. */ 943 /** Checks if the given expression is a binary op of the given kind. */
926 _isBin(expr, kind) { 944 _isBin(expr, kind) {
927 return expr is BinaryExpression && expr.op.kind == kind; 945 return expr is BinaryExpression && expr.op.kind == kind;
928 } 946 }
929 947
930 _boolTypeRef(SourceSpan span) { 948 _boolTypeRef(SourceSpan span) {
931 return new TypeReference(span, world.boolType); 949 return new TypeReference(span, world.boolType);
932 } 950 }
933 951
934 _numTypeRef(SourceSpan span) { 952 _numTypeRef(SourceSpan span) {
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
1083 _errorExpected('string literal, but found interpolated string start'); 1101 _errorExpected('string literal, but found interpolated string start');
1084 } else if (kind == TokenKind.INCOMPLETE_STRING) { 1102 } else if (kind == TokenKind.INCOMPLETE_STRING) {
1085 _next(); 1103 _next();
1086 _errorExpected('string literal, but found incomplete string'); 1104 _errorExpected('string literal, but found incomplete string');
1087 } 1105 }
1088 return null; 1106 return null;
1089 } 1107 }
1090 1108
1091 _parenOrLambda() { 1109 _parenOrLambda() {
1092 int start = _peekToken.start; 1110 int start = _peekToken.start;
1093 var args = arguments(); 1111 if (!_inInitializers && _atClosureParameters()) {
1094 if (!_inInitializers && 1112 var formals = formalParameterList();
1095 (_peekKind(TokenKind.ARROW) || _peekKind(TokenKind.LBRACE))) {
1096 var body = functionBody(true); 1113 var body = functionBody(true);
1097 var formals = _makeFormals(args);
1098 var func = new FunctionDefinition(null, null, null, formals, null, 1114 var func = new FunctionDefinition(null, null, null, formals, null,
1099 body, _makeSpan(start)); 1115 body, _makeSpan(start));
1100 return new LambdaExpression(func, func.span); 1116 return new LambdaExpression(func, func.span);
1101 } else { 1117 } else {
1118 var args = arguments();
1102 if (args.length == 1) { 1119 if (args.length == 1) {
1103 return new ParenExpression(args[0].value, _makeSpan(start)); 1120 return new ParenExpression(args[0].value, _makeSpan(start));
1104 } else { 1121 } else {
1105 _error('unexpected comma expression'); 1122 _error('unexpected comma expression');
1106 return args[0].value; 1123 return args[0].value;
1107 } 1124 }
1108 } 1125 }
1109 } 1126 }
1110 1127
1128 bool _atClosureParameters() {
1129 Token afterCloseParen = _peekPastCloseParen();
1130 return afterCloseParen.kind == TokenKind.ARROW
1131 || afterCloseParen.kind == TokenKind.LBRACE;
1132 }
1133
1134 Token _peekPastCloseParen() {
1135 int pos = _peekToken.start;
1136 if (pos > _highestCachePosition)
jimhug 2011/10/31 14:16:46 Style: The return either needs to be on the same l
1137 return _fillAfterCloseParenCache();
1138 return _afterCloseParenCache[pos];
1139 }
1140
1141 _fillAfterCloseParenCache() {
1142 // Scan for the matching RPAREN to the current LPAREN and return the
1143 // following token. Add intermediate values to cache to prevent this
1144 // look-ahead scan from being called again for nested parentheses. Note
1145 // that the outermost parens are not added to the cache as the following
1146 // token is directly available; not touching the cache for non-nested parens
1147 // has a small performance benefit.
1148 List tokens = [];
1149 List positions = [];
1150 Token firstOpenParen = _peekToken;
1151 while (true) {
1152 Token token = _next();
1153 tokens.add(token);
1154 int kind = token.kind;
1155 if (kind == TokenKind.LPAREN) {
1156 positions.add(token.start);
1157 } else if (kind == TokenKind.RPAREN) {
1158 int openPos = positions.removeLast();
1159 if (positions.length == 0)
1160 break;
1161 _afterCloseParenCache[openPos] = _peekToken;
1162 if (openPos > _highestCachePosition)
1163 _highestCachePosition = openPos;
1164 } else if (kind == TokenKind.END_OF_FILE) {
1165 _error('parenthesis never closed', firstOpenParen.span);
1166 // The invariant that all positions less than _highestCachePosition are
1167 // in the cache is violated if we bail out here due to the error. We
1168 // could add the pending elements of the positions list, but instead we
1169 // clear the cache. Clearing the cache also causes all the unmatched
1170 // parens to be enumerated, which might be a useful diagnostic behavior.
1171 _afterCloseParenCache = new Map<int,Token>();
1172 _highestCachePosition = -1;
1173 break;
1174 }
1175 }
1176
1177 var after = _peekToken;
1178 // Put all the lookahead tokens back into the parser's token stream.
1179 tokens.add(_peekToken);
1180 tokenizer = new DivertedTokenSource(tokens, this, tokenizer);
1181 _next(); // Re-synchronize parser lookahead state.
1182 return after;
1183 }
1184
1111 1185
1112 _typeAsIdentifier(type) { 1186 _typeAsIdentifier(type) {
1113 // TODO(jimhug): lots of errors to check for 1187 // TODO(jimhug): lots of errors to check for
1114 return type.name; 1188 return type.name;
1115 } 1189 }
1116 1190
1117 _specialIdentifier(bool includeOperators) { 1191 _specialIdentifier(bool includeOperators) {
1118 int start = _peekToken.start; 1192 int start = _peekToken.start;
1119 String name; 1193 String name;
1120 1194
(...skipping 360 matching lines...) Expand 10 before | Expand all | Expand 10 after
1481 1555
1482 return new Identifier(tok.text, _makeSpan(tok.start)); 1556 return new Identifier(tok.text, _makeSpan(tok.start));
1483 } 1557 }
1484 1558
1485 /////////////////////////////////////////////////////////////////// 1559 ///////////////////////////////////////////////////////////////////
1486 // These last productions handle most ambiguities in grammar 1560 // These last productions handle most ambiguities in grammar
1487 // They will convert expressions into other types. 1561 // They will convert expressions into other types.
1488 /////////////////////////////////////////////////////////////////// 1562 ///////////////////////////////////////////////////////////////////
1489 1563
1490 /** 1564 /**
1491 * Converts an [Expression] and a [Statment] body into a 1565 * Converts an [Expression], [Formals] and a [Statment] body into a
1492 * [FunctionDefinition]. 1566 * [FunctionDefinition].
1493 */ 1567 */
1494 _makeFunction(expr, body) { 1568 _makeFunction(expr, formals, body) {
1495 var name, type; 1569 var name, type;
1496 if (expr is CallExpression) { 1570 if (expr is VarExpression) {
1497 if (expr.target is VarExpression) { 1571 name = expr.name;
1498 name = expr.target.name; 1572 type = null;
1499 type = null; 1573 } else if (expr is DeclaredIdentifier) {
1500 } else if (expr.target is DeclaredIdentifier) { 1574 name = expr.name;
1501 name = expr.target.name; 1575 type = expr.type;
1502 type = expr.target.type; 1576 } else {
1503 } else { 1577 _error('bad function');
1504 _error('bad function'); 1578 }
1505 } 1579 var span = new SourceSpan(expr.span.file, expr.span.start, body.span.end);
1506 var formals = _makeFormals(expr.arguments); 1580 var func =
1507 var span =
1508 new SourceSpan(expr.span.file, expr.span.start, body.span.end);
1509 var func =
1510 new FunctionDefinition(null, type, name, formals, null, body, span); 1581 new FunctionDefinition(null, type, name, formals, null, body, span);
1511 return new LambdaExpression(func, func.span); 1582 return new LambdaExpression(func, func.span);
1512 } else {
1513 _error('expected function');
1514 }
1515 }
1516
1517 /** Converts a single expression into a formal or list of formals. */
1518 _makeFormal(expr) {
1519 if (expr is VarExpression) {
1520 return new FormalNode(false, false, null, expr.name, null, expr.span);
1521 } else if (expr is DeclaredIdentifier) {
1522 return new FormalNode(false, false, expr.type, expr.name, null,
1523 expr.span);
1524 } else if (_isBin(expr, TokenKind.ASSIGN) &&
1525 (expr.x is DeclaredIdentifier)) {
1526 DeclaredIdentifier di = expr.x; // TODO(jimhug): inference should handle!
1527 return new FormalNode(false, false, di.type, di.name, expr.y,
1528 expr.span);
1529 } else if (_isBin(expr, TokenKind.LT)) {
1530 // special signaling value to merge with next arg.
1531 return null;
1532 } else if (expr is ListExpression) {
1533 return _makeFormalsFromList(expr);
1534 } else {
1535 _error('expected formal', expr.span);
1536 }
1537 }
1538
1539 _makeFormalsFromList(expr) {
1540 if (expr.isConst) {
1541 _error('expected formal, but found "const"', expr.span);
1542 } else if (expr.type != null) {
1543 _error('expected formal, but found generic type arguments',
1544 expr.type.span);
1545 }
1546
1547 return _makeFormalsFromExpressions(expr.values, allowOptional:false);
1548 }
1549
1550 /** Converts a list of arguments into a list of formals. */
1551 _makeFormals(arguments) {
1552 var expressions = [];
1553 for (int i = 0; i < arguments.length; i++) {
1554 final arg = arguments[i];
1555 if (arg.label != null) {
1556 _error('expected formal, but found ":"');
1557 }
1558 expressions.add(arg.value);
1559 }
1560 return _makeFormalsFromExpressions(expressions, allowOptional:true);
1561 }
1562
1563 /** Converts a list of expressions into a list of formals. */
1564 _makeFormalsFromExpressions(expressions, [bool allowOptional]) {
1565 var formals = [];
1566 for (int i = 0; i < expressions.length; i++) {
1567 var formal = _makeFormal(expressions[i]);
1568 if (formal == null) {
1569 // special signal that we have the A<C case
1570 var baseType = _makeType(expressions[i].x);
1571 var typeParams = [_makeType(expressions[i].y)];
1572 i++;
1573 while (i < expressions.length) {
1574 var expr = expressions[i++];
1575 // Looking for D > m closer
1576 if (_isBin(expr, TokenKind.GT)) {
1577 typeParams.add(_makeType(expr.x));
1578 var type = new GenericTypeReference(baseType, typeParams, 0,
1579 _makeSpan(baseType.span.start));
1580 var name = null;
1581 if (expr.y is VarExpression) {
1582 // TODO(jimhug): Should be handled by inference!
1583 VarExpression ve = expr.y;
1584 name = ve.name;
1585 } else {
1586 _error('expected formal', expr.span);
1587 }
1588 formal = new FormalNode(false, false, type, name, null,
1589 _makeSpan(expressions[0].span.start));
1590 break;
1591 } else {
1592 typeParams.add(_makeType(expr));
1593 }
1594 }
1595 formals.add(formal);
1596
1597 } else if (formal is List) {
1598 formals.addAll(formal);
1599 if (!allowOptional) {
1600 _error('unexpected nested optional formal', expressions[i].span);
1601 }
1602
1603 } else {
1604 formals.add(formal);
1605 }
1606 }
1607 return formals;
1608 } 1583 }
1609 1584
1610 /** Converts an expression to a [DeclaredIdentifier]. */ 1585 /** Converts an expression to a [DeclaredIdentifier]. */
1611 _makeDeclaredIdentifier(e) { 1586 _makeDeclaredIdentifier(e) {
1612 if (e is VarExpression) { 1587 if (e is VarExpression) {
1613 return new DeclaredIdentifier(null, e.name, e.span); 1588 return new DeclaredIdentifier(null, e.name, e.span);
1614 } else if (e is DeclaredIdentifier) { 1589 } else if (e is DeclaredIdentifier) {
1615 return e; 1590 return e;
1616 } else { 1591 } else {
1617 _error('expected declared identifier'); 1592 _error('expected declared identifier');
1618 return new DeclaredIdentifier(null, null, e.span); 1593 return new DeclaredIdentifier(null, null, e.span);
1619 } 1594 }
1620 } 1595 }
1621 1596
1622 /** Converts an expression into a label. */ 1597 /** Converts an expression into a label. */
1623 _makeLabel(expr) { 1598 _makeLabel(expr) {
1624 if (expr is VarExpression) { 1599 if (expr is VarExpression) {
1625 return expr.name; 1600 return expr.name;
1626 } else { 1601 } else {
1627 _errorExpected('label'); 1602 _errorExpected('label');
1628 return null; 1603 return null;
1629 } 1604 }
1630 } 1605 }
1631 } 1606 }
1607
1608
1609 class DivertedTokenSource implements TokenSource {
jimhug 2011/10/31 14:16:46 Now that you've provided this, I'd love to do an e
1610 final List tokens;
1611 final Parser parser;
1612 final TokenSource previousTokenizer;
1613 DivertedTokenSource(this.tokens, this.parser, this.previousTokenizer);
1614
1615 int _pos = 0;
1616 next() {
1617 var token = tokens[_pos];
1618 ++_pos;
jimhug 2011/10/31 14:16:46 Style: prefer _pos++ for this bare increment.
1619 if (_pos == tokens.length) {
1620 parser.tokenizer = previousTokenizer;
1621 }
1622 return token;
1623 }
1624 }
OLDNEW
« no previous file with comments | « frogsh ('k') | tests/frog/frog.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698