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

Unified 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, 2 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « frogsh ('k') | tests/frog/frog.status » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: parser.dart
diff --git a/parser.dart b/parser.dart
index 1fe93cb0fa817ae5b66c8bfd58d36624d164a84d..62777145c9194271791acca757a70c910e0f0b8e 100644
--- a/parser.dart
+++ b/parser.dart
@@ -13,7 +13,7 @@
* very clearly detected and is reported in a later compiler phase.
*/
class Parser {
- Tokenizer tokenizer;
+ TokenSource tokenizer;
jimhug 2011/10/31 14:16:46 Note: This change seems good independent of the re
final SourceFile source;
/** Enables diet parse, which skips function bodies. */
@@ -25,11 +25,20 @@ class Parser {
Token _previousToken;
Token _peekToken;
+ // Map from start position of a '(' to token following the matching ')'. Used
+ // to distinguish closure formal parameter lists from parenthesised
+ // expressions and argument lists. Closure formals are followed by '=>' or
+ // '{'.
+ Map<int, Token> _afterCloseParenCache;
+ int _highestCachePosition = -1;
+
Parser(this.source, [this.diet = false, int startOffset = 0]) {
tokenizer = new Tokenizer(source, true, startOffset);
_peekToken = tokenizer.next();
_previousToken = null;
_inInitializers = false;
+
+ _afterCloseParenCache = new Map<int, Token>();
}
/** Generate an error if [source] has not been completely consumed. */
@@ -870,7 +879,6 @@ class Parser {
arguments() {
var args = [];
- // TODO(jimhug): switch to forced formals when get a DeclaredId
_eat(TokenKind.LPAREN);
if (!_maybeEat(TokenKind.RPAREN)) {
do {
@@ -884,8 +892,7 @@ class Parser {
finishPostfixExpression(expr) {
switch(_peek()) {
case TokenKind.LPAREN:
- return finishPostfixExpression(new CallExpression(expr, arguments(),
- _makeSpan(expr.span.start)));
+ return finishCallOrLambdaExpression(expr);
case TokenKind.LBRACK:
_eat(TokenKind.LBRACK);
var index = expression();
@@ -905,11 +912,10 @@ class Parser {
// These are pseudo-expressions supported for cover grammar
// must be forbidden when parsing initializers.
+
case TokenKind.ARROW:
case TokenKind.LBRACE:
- if (_inInitializers) return expr;
- var body = functionBody(true);
- return _makeFunction(expr, body);
+ return expr;
default:
if (_peekIdentifier()) {
@@ -922,6 +928,18 @@ class Parser {
}
}
+ finishCallOrLambdaExpression(expr) {
+ if (!_inInitializers && _atClosureParameters()) {
+ var formals = formalParameterList();
+ var body = functionBody(true);
+ return _makeFunction(expr, formals, body);
+ } else {
+ var args = arguments();
+ return finishPostfixExpression(
+ new CallExpression(expr, args, _makeSpan(expr.span.start)));
+ }
+ }
+
/** Checks if the given expression is a binary op of the given kind. */
_isBin(expr, kind) {
return expr is BinaryExpression && expr.op.kind == kind;
@@ -1090,15 +1108,14 @@ class Parser {
_parenOrLambda() {
int start = _peekToken.start;
- var args = arguments();
- if (!_inInitializers &&
- (_peekKind(TokenKind.ARROW) || _peekKind(TokenKind.LBRACE))) {
+ if (!_inInitializers && _atClosureParameters()) {
+ var formals = formalParameterList();
var body = functionBody(true);
- var formals = _makeFormals(args);
var func = new FunctionDefinition(null, null, null, formals, null,
body, _makeSpan(start));
return new LambdaExpression(func, func.span);
} else {
+ var args = arguments();
if (args.length == 1) {
return new ParenExpression(args[0].value, _makeSpan(start));
} else {
@@ -1108,6 +1125,63 @@ class Parser {
}
}
+ bool _atClosureParameters() {
+ Token afterCloseParen = _peekPastCloseParen();
+ return afterCloseParen.kind == TokenKind.ARROW
+ || afterCloseParen.kind == TokenKind.LBRACE;
+ }
+
+ Token _peekPastCloseParen() {
+ int pos = _peekToken.start;
+ if (pos > _highestCachePosition)
jimhug 2011/10/31 14:16:46 Style: The return either needs to be on the same l
+ return _fillAfterCloseParenCache();
+ return _afterCloseParenCache[pos];
+ }
+
+ _fillAfterCloseParenCache() {
+ // Scan for the matching RPAREN to the current LPAREN and return the
+ // following token. Add intermediate values to cache to prevent this
+ // look-ahead scan from being called again for nested parentheses. Note
+ // that the outermost parens are not added to the cache as the following
+ // token is directly available; not touching the cache for non-nested parens
+ // has a small performance benefit.
+ List tokens = [];
+ List positions = [];
+ Token firstOpenParen = _peekToken;
+ while (true) {
+ Token token = _next();
+ tokens.add(token);
+ int kind = token.kind;
+ if (kind == TokenKind.LPAREN) {
+ positions.add(token.start);
+ } else if (kind == TokenKind.RPAREN) {
+ int openPos = positions.removeLast();
+ if (positions.length == 0)
+ break;
+ _afterCloseParenCache[openPos] = _peekToken;
+ if (openPos > _highestCachePosition)
+ _highestCachePosition = openPos;
+ } else if (kind == TokenKind.END_OF_FILE) {
+ _error('parenthesis never closed', firstOpenParen.span);
+ // The invariant that all positions less than _highestCachePosition are
+ // in the cache is violated if we bail out here due to the error. We
+ // could add the pending elements of the positions list, but instead we
+ // clear the cache. Clearing the cache also causes all the unmatched
+ // parens to be enumerated, which might be a useful diagnostic behavior.
+ _afterCloseParenCache = new Map<int,Token>();
+ _highestCachePosition = -1;
+ break;
+ }
+ }
+
+ var after = _peekToken;
+ // Put all the lookahead tokens back into the parser's token stream.
+ tokens.add(_peekToken);
+ tokenizer = new DivertedTokenSource(tokens, this, tokenizer);
+ _next(); // Re-synchronize parser lookahead state.
+ return after;
+ }
+
_typeAsIdentifier(type) {
// TODO(jimhug): lots of errors to check for
@@ -1488,123 +1562,24 @@ class Parser {
///////////////////////////////////////////////////////////////////
/**
- * Converts an [Expression] and a [Statment] body into a
+ * Converts an [Expression], [Formals] and a [Statment] body into a
* [FunctionDefinition].
*/
- _makeFunction(expr, body) {
+ _makeFunction(expr, formals, body) {
var name, type;
- if (expr is CallExpression) {
- if (expr.target is VarExpression) {
- name = expr.target.name;
- type = null;
- } else if (expr.target is DeclaredIdentifier) {
- name = expr.target.name;
- type = expr.target.type;
- } else {
- _error('bad function');
- }
- var formals = _makeFormals(expr.arguments);
- var span =
- new SourceSpan(expr.span.file, expr.span.start, body.span.end);
- var func =
- new FunctionDefinition(null, type, name, formals, null, body, span);
- return new LambdaExpression(func, func.span);
- } else {
- _error('expected function');
- }
- }
-
- /** Converts a single expression into a formal or list of formals. */
- _makeFormal(expr) {
if (expr is VarExpression) {
- return new FormalNode(false, false, null, expr.name, null, expr.span);
+ name = expr.name;
+ type = null;
} else if (expr is DeclaredIdentifier) {
- return new FormalNode(false, false, expr.type, expr.name, null,
- expr.span);
- } else if (_isBin(expr, TokenKind.ASSIGN) &&
- (expr.x is DeclaredIdentifier)) {
- DeclaredIdentifier di = expr.x; // TODO(jimhug): inference should handle!
- return new FormalNode(false, false, di.type, di.name, expr.y,
- expr.span);
- } else if (_isBin(expr, TokenKind.LT)) {
- // special signaling value to merge with next arg.
- return null;
- } else if (expr is ListExpression) {
- return _makeFormalsFromList(expr);
+ name = expr.name;
+ type = expr.type;
} else {
- _error('expected formal', expr.span);
- }
- }
-
- _makeFormalsFromList(expr) {
- if (expr.isConst) {
- _error('expected formal, but found "const"', expr.span);
- } else if (expr.type != null) {
- _error('expected formal, but found generic type arguments',
- expr.type.span);
- }
-
- return _makeFormalsFromExpressions(expr.values, allowOptional:false);
- }
-
- /** Converts a list of arguments into a list of formals. */
- _makeFormals(arguments) {
- var expressions = [];
- for (int i = 0; i < arguments.length; i++) {
- final arg = arguments[i];
- if (arg.label != null) {
- _error('expected formal, but found ":"');
- }
- expressions.add(arg.value);
- }
- return _makeFormalsFromExpressions(expressions, allowOptional:true);
- }
-
- /** Converts a list of expressions into a list of formals. */
- _makeFormalsFromExpressions(expressions, [bool allowOptional]) {
- var formals = [];
- for (int i = 0; i < expressions.length; i++) {
- var formal = _makeFormal(expressions[i]);
- if (formal == null) {
- // special signal that we have the A<C case
- var baseType = _makeType(expressions[i].x);
- var typeParams = [_makeType(expressions[i].y)];
- i++;
- while (i < expressions.length) {
- var expr = expressions[i++];
- // Looking for D > m closer
- if (_isBin(expr, TokenKind.GT)) {
- typeParams.add(_makeType(expr.x));
- var type = new GenericTypeReference(baseType, typeParams, 0,
- _makeSpan(baseType.span.start));
- var name = null;
- if (expr.y is VarExpression) {
- // TODO(jimhug): Should be handled by inference!
- VarExpression ve = expr.y;
- name = ve.name;
- } else {
- _error('expected formal', expr.span);
- }
- formal = new FormalNode(false, false, type, name, null,
- _makeSpan(expressions[0].span.start));
- break;
- } else {
- typeParams.add(_makeType(expr));
- }
- }
- formals.add(formal);
-
- } else if (formal is List) {
- formals.addAll(formal);
- if (!allowOptional) {
- _error('unexpected nested optional formal', expressions[i].span);
- }
-
- } else {
- formals.add(formal);
- }
+ _error('bad function');
}
- return formals;
+ var span = new SourceSpan(expr.span.file, expr.span.start, body.span.end);
+ var func =
+ new FunctionDefinition(null, type, name, formals, null, body, span);
+ return new LambdaExpression(func, func.span);
}
/** Converts an expression to a [DeclaredIdentifier]. */
@@ -1629,3 +1604,21 @@ class Parser {
}
}
}
+
+
+class DivertedTokenSource implements TokenSource {
jimhug 2011/10/31 14:16:46 Now that you've provided this, I'd love to do an e
+ final List tokens;
+ final Parser parser;
+ final TokenSource previousTokenizer;
+ DivertedTokenSource(this.tokens, this.parser, this.previousTokenizer);
+
+ int _pos = 0;
+ next() {
+ var token = tokens[_pos];
+ ++_pos;
jimhug 2011/10/31 14:16:46 Style: prefer _pos++ for this bare increment.
+ if (_pos == tokens.length) {
+ parser.tokenizer = previousTokenizer;
+ }
+ return token;
+ }
+}
« 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