make parser handle parenthesized block-sugar exprs properly

This commit is contained in:
Niko Matsakis 2012-01-03 22:03:07 -08:00
parent e02ab2d65f
commit 37ba5f3b32
3 changed files with 133 additions and 102 deletions

View File

@ -736,7 +736,29 @@ fn mk_lit_u32(p: parser, i: u32) -> @ast::expr {
ret @{id: p.get_id(), node: ast::expr_lit(lv_lit), span: span};
}
fn parse_bottom_expr(p: parser) -> @ast::expr {
// We don't allow single-entry tuples in the true AST; that indicates a
// parenthesized expression. However, we preserve them temporarily while
// parsing because `(while{...})+3` parses differently from `while{...}+3`.
//
// To reflect the fact that the @ast::expr is not a true expr that should be
// part of the AST, we wrap such expressions in the pexpr tag. They
// can then be converted to true expressions by a call to `to_expr()`.
tag pexpr {
pexpr(@ast::expr);
}
fn mk_pexpr(p: parser, lo: uint, hi: uint, node: ast::expr_) -> pexpr {
ret pexpr(mk_expr(p, lo, hi, node));
}
fn to_expr(e: pexpr) -> @ast::expr {
alt e.node {
ast::expr_tup(es) when vec::len(es) == 1u { es[0u] }
_ { *e }
}
}
fn parse_bottom_expr(p: parser) -> pexpr {
let lo = p.get_lo_pos();
let hi = p.get_hi_pos();
@ -747,15 +769,19 @@ fn parse_bottom_expr(p: parser) -> @ast::expr {
hi = p.get_hi_pos();
p.bump();
let lit = @spanned(lo, hi, ast::lit_nil);
ret mk_expr(p, lo, hi, ast::expr_lit(lit));
ret mk_pexpr(p, lo, hi, ast::expr_lit(lit));
}
let es = [parse_expr(p)];
while p.peek() == token::COMMA { p.bump(); es += [parse_expr(p)]; }
hi = p.get_hi_pos();
expect(p, token::RPAREN);
if vec::len(es) == 1u {
ret mk_expr(p, lo, hi, es[0].node);
} else { ret mk_expr(p, lo, hi, ast::expr_tup(es)); }
// Note: we retain the expr_tup() even for simple
// parenthesized expressions, but only for a "little while".
// This is so that wrappers around parse_bottom_expr()
// can tell whether the expression was parenthesized or not,
// which affects expr_is_complete().
ret mk_pexpr(p, lo, hi, ast::expr_tup(es));
} else if p.peek() == token::LBRACE {
p.bump();
if is_word(p, "mutable") ||
@ -775,34 +801,34 @@ fn parse_bottom_expr(p: parser) -> @ast::expr {
expect(p, token::RBRACE);
ex = ast::expr_rec(fields, base);
} else if is_bar(p.peek()) {
ret parse_fn_block_expr(p);
ret pexpr(parse_fn_block_expr(p));
} else {
let blk = parse_block_tail(p, lo, ast::default_blk);
ret mk_expr(p, blk.span.lo, blk.span.hi, ast::expr_block(blk));
ret mk_pexpr(p, blk.span.lo, blk.span.hi, ast::expr_block(blk));
}
} else if eat_word(p, "if") {
ret parse_if_expr(p);
ret pexpr(parse_if_expr(p));
} else if eat_word(p, "for") {
ret parse_for_expr(p);
ret pexpr(parse_for_expr(p));
} else if eat_word(p, "while") {
ret parse_while_expr(p);
ret pexpr(parse_while_expr(p));
} else if eat_word(p, "do") {
ret parse_do_while_expr(p);
ret pexpr(parse_do_while_expr(p));
} else if eat_word(p, "alt") {
ret parse_alt_expr(p);
ret pexpr(parse_alt_expr(p));
} else if eat_word(p, "fn") {
let proto = parse_fn_ty_proto(p);
ret parse_fn_expr(p, proto);
ret pexpr(parse_fn_expr(p, proto));
} else if eat_word(p, "block") {
ret parse_fn_expr(p, ast::proto_block);
ret pexpr(parse_fn_expr(p, ast::proto_block));
} else if eat_word(p, "lambda") {
ret parse_fn_expr(p, ast::proto_shared(ast::sugar_sexy));
ret pexpr(parse_fn_expr(p, ast::proto_shared(ast::sugar_sexy)));
} else if eat_word(p, "sendfn") {
ret parse_fn_expr(p, ast::proto_send);
ret pexpr(parse_fn_expr(p, ast::proto_send));
} else if eat_word(p, "unchecked") {
ret parse_block_expr(p, lo, ast::unchecked_blk);
ret pexpr(parse_block_expr(p, lo, ast::unchecked_blk));
} else if eat_word(p, "unsafe") {
ret parse_block_expr(p, lo, ast::unsafe_blk);
ret pexpr(parse_block_expr(p, lo, ast::unsafe_blk));
} else if p.peek() == token::LBRACKET {
p.bump();
let mut = parse_mutability(p);
@ -816,15 +842,16 @@ fn parse_bottom_expr(p: parser) -> @ast::expr {
expect(p, token::GT);
/* hack: early return to take advantage of specialized function */
ret mk_mac_expr(p, lo, p.get_hi_pos(), ast::mac_embed_type(ty));
ret pexpr(mk_mac_expr(p, lo, p.get_hi_pos(),
ast::mac_embed_type(ty)));
} else if p.peek() == token::POUND_LBRACE {
p.bump();
let blk = ast::mac_embed_block(
parse_block_tail(p, lo, ast::default_blk));
ret mk_mac_expr(p, lo, p.get_hi_pos(), blk);
ret pexpr(mk_mac_expr(p, lo, p.get_hi_pos(), blk));
} else if p.peek() == token::ELLIPSIS {
p.bump();
ret mk_mac_expr(p, lo, p.get_hi_pos(), ast::mac_ellipsis);
ret pexpr(mk_mac_expr(p, lo, p.get_hi_pos(), ast::mac_ellipsis));
} else if eat_word(p, "obj") {
// Anonymous object
@ -941,7 +968,7 @@ fn parse_bottom_expr(p: parser) -> @ast::expr {
hi = lit.span.hi;
ex = ast::expr_lit(@lit);
}
ret mk_expr(p, lo, hi, ex);
ret mk_pexpr(p, lo, hi, ex);
}
fn parse_block_expr(p: parser,
@ -977,7 +1004,7 @@ fn parse_syntax_ext_naked(p: parser, lo: uint) -> @ast::expr {
ret mk_mac_expr(p, lo, hi, ast::mac_invoc(pth, e, none));
}
fn parse_dot_or_call_expr(p: parser) -> @ast::expr {
fn parse_dot_or_call_expr(p: parser) -> pexpr {
let b = parse_bottom_expr(p);
parse_dot_or_call_expr_with(p, b)
}
@ -986,10 +1013,10 @@ fn permits_call(p: parser) -> bool {
ret p.get_restriction() != RESTRICT_NO_CALL_EXPRS;
}
fn parse_dot_or_call_expr_with(p: parser, e: @ast::expr) -> @ast::expr {
fn parse_dot_or_call_expr_with(p: parser, e0: pexpr) -> pexpr {
let e = e0;
let lo = e.span.lo;
let hi = e.span.hi;
let e = e;
while !expr_is_complete(p, e) {
alt p.peek() {
// expr(...)
@ -997,22 +1024,22 @@ fn parse_dot_or_call_expr_with(p: parser, e: @ast::expr) -> @ast::expr {
let es = parse_seq(token::LPAREN, token::RPAREN,
seq_sep(token::COMMA), parse_expr, p);
hi = es.span.hi;
let nd = ast::expr_call(e, es.node, false);
e = mk_expr(p, lo, hi, nd);
let nd = ast::expr_call(to_expr(e), es.node, false);
e = mk_pexpr(p, lo, hi, nd);
}
// expr { || ... }
// expr {|| ... }
token::LBRACE. when is_bar(p.look_ahead(1u)) && permits_call(p) {
p.bump();
let blk = parse_fn_block_expr(p);
alt e.node {
ast::expr_call(f, args, false) {
e = @{node: ast::expr_call(f, args + [blk], true)
with *e};
e = pexpr(@{node: ast::expr_call(f, args + [blk], true)
with *to_expr(e)});
}
_ {
e = mk_expr(p, lo, p.get_last_hi_pos(),
ast::expr_call(e, [blk], true));
e = mk_pexpr(p, lo, p.get_last_hi_pos(),
ast::expr_call(to_expr(e), [blk], true));
}
}
}
@ -1023,7 +1050,7 @@ fn parse_dot_or_call_expr_with(p: parser, e: @ast::expr) -> @ast::expr {
let ix = parse_expr(p);
hi = ix.span.hi;
expect(p, token::RBRACKET);
e = mk_expr(p, lo, hi, ast::expr_index(e, ix));
e = mk_pexpr(p, lo, hi, ast::expr_index(to_expr(e), ix));
}
// expr.f
@ -1038,7 +1065,10 @@ fn parse_dot_or_call_expr_with(p: parser, e: @ast::expr) -> @ast::expr {
parse_seq_to_gt(some(token::COMMA),
{|p| parse_ty(p, false)}, p)
} else { [] };
e = mk_expr(p, lo, hi, ast::expr_field(e, p.get_str(i), tys));
e = mk_pexpr(p, lo, hi,
ast::expr_field(to_expr(e),
p.get_str(i),
tys));
}
t { unexpected(p, t); }
}
@ -1050,7 +1080,7 @@ fn parse_dot_or_call_expr_with(p: parser, e: @ast::expr) -> @ast::expr {
ret e;
}
fn parse_prefix_expr(p: parser) -> @ast::expr {
fn parse_prefix_expr(p: parser) -> pexpr {
let lo = p.get_lo_pos();
let hi = p.get_hi_pos();
@ -1058,7 +1088,7 @@ fn parse_prefix_expr(p: parser) -> @ast::expr {
alt p.peek() {
token::NOT. {
p.bump();
let e = parse_prefix_expr(p);
let e = to_expr(parse_prefix_expr(p));
hi = e.span.hi;
ex = ast::expr_unary(ast::not, e);
}
@ -1066,13 +1096,13 @@ fn parse_prefix_expr(p: parser) -> @ast::expr {
alt b {
token::MINUS. {
p.bump();
let e = parse_prefix_expr(p);
let e = to_expr(parse_prefix_expr(p));
hi = e.span.hi;
ex = ast::expr_unary(ast::neg, e);
}
token::STAR. {
p.bump();
let e = parse_prefix_expr(p);
let e = to_expr(parse_prefix_expr(p));
hi = e.span.hi;
ex = ast::expr_unary(ast::deref, e);
}
@ -1082,20 +1112,20 @@ fn parse_prefix_expr(p: parser) -> @ast::expr {
token::AT. {
p.bump();
let m = parse_mutability(p);
let e = parse_prefix_expr(p);
let e = to_expr(parse_prefix_expr(p));
hi = e.span.hi;
ex = ast::expr_unary(ast::box(m), e);
}
token::TILDE. {
p.bump();
let m = parse_mutability(p);
let e = parse_prefix_expr(p);
let e = to_expr(parse_prefix_expr(p));
hi = e.span.hi;
ex = ast::expr_unary(ast::uniq(m), e);
}
_ { ret parse_dot_or_call_expr(p); }
}
ret mk_expr(p, lo, hi, ex);
ret mk_pexpr(p, lo, hi, ex);
}
fn parse_ternary(p: parser) -> @ast::expr {
@ -1146,9 +1176,10 @@ const unop_prec: int = 100;
const as_prec: int = 5;
const ternary_prec: int = 0;
fn parse_more_binops(p: parser, lhs: @ast::expr, min_prec: int) ->
fn parse_more_binops(p: parser, plhs: pexpr, min_prec: int) ->
@ast::expr {
if expr_is_complete(p, lhs) { ret lhs; }
let lhs = to_expr(plhs);
if expr_is_complete(p, plhs) { ret lhs; }
let peeked = p.peek();
if peeked == token::BINOP(token::OR) &&
p.get_restriction() == RESTRICT_NO_BAR_OP { ret lhs; }
@ -1157,7 +1188,7 @@ fn parse_more_binops(p: parser, lhs: @ast::expr, min_prec: int) ->
p.bump();
let expr = parse_prefix_expr(p);
let rhs = parse_more_binops(p, expr, cur.prec);
let bin = mk_expr(p, lhs.span.lo, rhs.span.hi,
let bin = mk_pexpr(p, lhs.span.lo, rhs.span.hi,
ast::expr_binary(cur.op, lhs, rhs));
ret parse_more_binops(p, bin, min_prec);
}
@ -1165,7 +1196,7 @@ fn parse_more_binops(p: parser, lhs: @ast::expr, min_prec: int) ->
if as_prec > min_prec && eat_word(p, "as") {
let rhs = parse_ty(p, true);
let _as =
mk_expr(p, lhs.span.lo, rhs.span.hi, ast::expr_cast(lhs, rhs));
mk_pexpr(p, lhs.span.lo, rhs.span.hi, ast::expr_cast(lhs, rhs));
ret parse_more_binops(p, _as, min_prec);
}
ret lhs;
@ -1587,12 +1618,12 @@ fn parse_stmt(p: parser) -> @ast::stmt {
}
}
fn expr_is_complete(p: parser, e: @ast::expr) -> bool {
fn expr_is_complete(p: parser, e: pexpr) -> bool {
log(debug, ("expr_is_complete", p.get_restriction(),
print::pprust::expr_to_str(e),
expr_requires_semi_to_be_stmt(e)));
print::pprust::expr_to_str(*e),
expr_requires_semi_to_be_stmt(*e)));
ret p.get_restriction() == RESTRICT_STMT_EXPR &&
!expr_requires_semi_to_be_stmt(e);
!expr_requires_semi_to_be_stmt(*e);
}
fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool {

View File

@ -558,22 +558,6 @@ fn print_attribute(s: ps, attr: ast::attribute) {
word(s.s, "]");
}
// An expression that begins with a dual-form statement/expression like `{
// ... }-10` would be parsed as `{ ... };-10` unless parentheses are used (ie,
// `({...}-10)`). These parentheses are not, however, preserved by the
// parser. This function specifies whether parentheses must be inserted.
fn stmt_expr_requires_parens(ex: @ast::expr) -> bool {
fn helper(ex: @ast::expr, inner: bool) -> bool {
alt ex.node {
ast::expr_call(subex, _, _) | ast::expr_binary(_, subex, _) {
be helper(subex, true);
}
_ when !inner { ret false; }
_ { ret !parse::parser::expr_requires_semi_to_be_stmt(ex); }
}
}
ret helper(ex, false);
}
fn print_stmt(s: ps, st: ast::stmt) {
maybe_print_comment(s, st.span.lo);
@ -583,13 +567,7 @@ fn print_stmt(s: ps, st: ast::stmt) {
}
ast::stmt_expr(expr, _) {
space_if_not_bol(s);
if stmt_expr_requires_parens(expr) {
popen(s);
print_expr(s, expr);
pclose(s);
} else {
print_expr(s, expr);
}
print_tl_expr(s, expr);
}
}
if parse::parser::stmt_ends_with_semi(st) { word(s.s, ";"); }
@ -626,7 +604,7 @@ fn print_possibly_embedded_block(s: ps, blk: ast::blk, embedded: embed_type,
alt blk.node.expr {
some(expr) {
space_if_not_bol(s);
print_expr(s, expr);
print_tl_expr(s, expr);
maybe_print_trailing_comment(s, expr.span, some(blk.span.hi));
}
_ { }
@ -714,6 +692,37 @@ fn print_mac(s: ps, m: ast::mac) {
}
}
// An expression that begins with a dual-form statement/expression like `{
// ... }-10` would be parsed as `{ ... };-10` unless parentheses are used (ie,
// `({...}-10)`). These parentheses are not, however, preserved by the
// parser. This function specifies whether parentheses must be inserted.
fn print_tl_expr(s: ps, &&expr: @ast::expr) {
fn stmt_expr_requires_parens(ex: @ast::expr) -> bool {
fn helper(ex: @ast::expr, inner: bool) -> bool {
log(debug, ("helper", ex, inner));
if inner && !parse::parser::expr_requires_semi_to_be_stmt(ex) {
ret true;
}
alt ex.node {
ast::expr_call(subex, _, _) | ast::expr_binary(_, subex, _) {
be helper(subex, true);
}
_ { ret false; }
}
}
ret helper(ex, false);
}
#debug("print_tl_expr %s", expr_to_str(expr));
if stmt_expr_requires_parens(expr) {
popen(s);
print_expr(s, expr);
pclose(s);
} else {
print_expr(s, expr);
}
}
fn print_expr(s: ps, &&expr: @ast::expr) {
maybe_print_comment(s, expr.span.lo);
ibox(s, indent_unit);
@ -1380,15 +1389,8 @@ fn need_parens(expr: @ast::expr, outer_prec: int) -> bool {
ast::expr_binary(op, _, _) { operator_prec(op) < outer_prec }
ast::expr_cast(_, _) { parse::parser::as_prec < outer_prec }
ast::expr_ternary(_, _, _) { parse::parser::ternary_prec < outer_prec }
// This may be too conservative in some cases
ast::expr_assign(_, _) {
true
}
ast::expr_assign(_, _) { true }
ast::expr_move(_, _) { true }
ast::expr_swap(_, _) { true }
ast::expr_assign_op(_, _, _) { true }

View File

@ -1,23 +1,21 @@
// xfail-test
// FIXME: Parser doesn't distinguish expression in parentheses (as in
// this example) from one that is not! It is somewhat of a pain to
// fix this though there are no theoretical difficulties. We could
// either add paren to the AST (better for pretty-print, I suppose) or
// modify the parser to track whether the expression in question is
// parenthesized. I did the latter once and it was a bit of pain but
// not terribly difficult. We could also the decision as to whether
// something is an "expression with a value" down into the
// parse_expr() codepath, where we *know* if there are parentheses or
// not, but we'd probably have to be a bit more careful then with
// clearing the top-level restrction flag (which we ought to do
// anyhow!)
fn main() {
let v = [1f, 2f, 3f];
let w =
if true { (vec::any(v) { |e| float::nonnegative(e) }) }
else { false };
assert w;
fn w_semi(v: [int]) -> int {
vec::foldl(0, v) {|x,y| x+y};
-10
}
fn wo_paren(v: [int]) -> int {
// Perhaps surprising: this is parsed equivalently to w_semi()
vec::foldl(0, v) {|x,y| x+y} - 10
}
fn w_paren(v: [int]) -> int {
// Here the parentheses force interpretation as an expression:
(vec::foldl(0, v) {|x,y| x+y}) - 10
}
fn main() {
assert wo_paren([0, 1, 2, 3]) == -10;
assert w_semi([0, 1, 2, 3]) == -10;
assert w_paren([0, 1, 2, 3]) == -4;
}