rustc: Add ternary operator. Closes #565
The implementation is so simple it might be considered cheating: at almost every step the expr_ternary is just converted to expr_if.
This commit is contained in:
parent
5495ad17d1
commit
05c0216654
@ -241,6 +241,7 @@ tag expr_ {
|
||||
expr_lit(@lit);
|
||||
expr_cast(@expr, @ty);
|
||||
expr_if(@expr, block, option::t[@expr]);
|
||||
expr_ternary(@expr, @expr, @expr);
|
||||
expr_while(@expr, block);
|
||||
expr_for(@local, @expr, block);
|
||||
expr_for_each(@local, @expr, block);
|
||||
@ -550,6 +551,32 @@ fn is_constraint_arg(@expr e) -> bool {
|
||||
fn eq_ty(&@ty a, &@ty b) -> bool { ret std::box::ptr_eq(a, b); }
|
||||
|
||||
fn hash_ty(&@ty t) -> uint { ret t.span.lo << 16u + t.span.hi; }
|
||||
|
||||
fn block_from_expr(@expr e) -> block {
|
||||
let block_ blk_ =
|
||||
rec(stmts=[],
|
||||
expr=option::some[@expr](e),
|
||||
id=e.id);
|
||||
ret rec(node=blk_, span=e.span);
|
||||
}
|
||||
|
||||
// This is a convenience function to transfor ternary expressions to if
|
||||
// expressions so that they can be treated the same
|
||||
fn ternary_to_if(&@expr e) -> @ast::expr {
|
||||
alt (e.node) {
|
||||
case (expr_ternary(?cond, ?then, ?els)) {
|
||||
auto then_blk = block_from_expr(then);
|
||||
auto els_blk = block_from_expr(els);
|
||||
auto els_expr = @rec(id=els.id, node=expr_block(els_blk),
|
||||
span=els.span);
|
||||
ret @rec(id=e.id,
|
||||
node=expr_if(cond, then_blk, option::some(els_expr)),
|
||||
span=e.span);
|
||||
}
|
||||
case (_) { fail; }
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
|
@ -1071,6 +1071,20 @@ fn parse_prefix_expr(&parser p) -> @ast::expr {
|
||||
ret mk_expr(p, lo, hi, ex);
|
||||
}
|
||||
|
||||
fn parse_ternary(&parser p) -> @ast::expr {
|
||||
auto cond_expr = parse_binops(p);
|
||||
if (p.peek() == token::QUES) {
|
||||
p.bump();
|
||||
auto then_expr = parse_expr(p);
|
||||
expect(p, token::COLON);
|
||||
auto else_expr = parse_expr(p);
|
||||
ret mk_expr(p, cond_expr.span.lo, else_expr.span.hi,
|
||||
ast::expr_ternary(cond_expr, then_expr, else_expr));
|
||||
} else {
|
||||
ret cond_expr;
|
||||
}
|
||||
}
|
||||
|
||||
type op_spec = rec(token::token tok, ast::binop op, int prec);
|
||||
|
||||
|
||||
@ -1128,7 +1142,7 @@ fn parse_more_binops(&parser p, @ast::expr lhs, int min_prec) -> @ast::expr {
|
||||
|
||||
fn parse_assign_expr(&parser p) -> @ast::expr {
|
||||
auto lo = p.get_lo_pos();
|
||||
auto lhs = parse_binops(p);
|
||||
auto lhs = parse_ternary(p);
|
||||
alt (p.peek()) {
|
||||
case (token::EQ) {
|
||||
p.bump();
|
||||
|
@ -5746,6 +5746,9 @@ fn trans_expr_out(&@block_ctxt cx, &@ast::expr e, out_method output) ->
|
||||
ret with_out_method(bind trans_if(cx, cond, thn, els, e.id, _),
|
||||
cx, e.id, output);
|
||||
}
|
||||
case (ast::expr_ternary(_, _, _)) {
|
||||
ret trans_expr_out(cx, ast::ternary_to_if(e), output);
|
||||
}
|
||||
case (ast::expr_for(?decl, ?seq, ?body)) {
|
||||
ret trans_for(cx, decl, seq, body);
|
||||
}
|
||||
|
@ -422,6 +422,9 @@ fn find_pre_post_expr(&fn_ctxt fcx, @expr e) {
|
||||
case (expr_if(?antec, ?conseq, ?maybe_alt)) {
|
||||
join_then_else(fcx, antec, conseq, maybe_alt, e.id, plain_if);
|
||||
}
|
||||
case (expr_ternary(_, _, _)) {
|
||||
find_pre_post_expr(fcx, ternary_to_if(e));
|
||||
}
|
||||
case (expr_binary(?bop, ?l, ?r)) {
|
||||
/* *unless* bop is lazy (e.g. and, or)?
|
||||
FIXME */
|
||||
|
@ -489,6 +489,9 @@ fn find_pre_post_state_expr(&fn_ctxt fcx, &prestate pres, @expr e) -> bool {
|
||||
|| changed;
|
||||
ret changed;
|
||||
}
|
||||
case (expr_ternary(_, _, _)) {
|
||||
ret find_pre_post_state_expr(fcx, pres, ternary_to_if(e));
|
||||
}
|
||||
case (expr_binary(?bop, ?l, ?r)) {
|
||||
/* FIXME: what if bop is lazy? */
|
||||
|
||||
|
@ -1609,6 +1609,9 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
|
||||
check_pred_expr(fcx, cond);
|
||||
check_then_else(fcx, thn, elsopt, id, expr.span);
|
||||
}
|
||||
case (ast::expr_ternary(_, _, _)) {
|
||||
check_expr(fcx, ast::ternary_to_if(expr));
|
||||
}
|
||||
case (ast::expr_assert(?e)) {
|
||||
check_expr(fcx, e);
|
||||
auto ety = expr_ty(fcx.ccx.tcx, e);
|
||||
|
@ -295,6 +295,11 @@ fn visit_expr[E](&@expr ex, &E e, &vt[E] v) {
|
||||
vt(v).visit_block(b, e, v);
|
||||
visit_expr_opt(eo, e, v);
|
||||
}
|
||||
case (expr_ternary(?c, ?t, ?el)) {
|
||||
vt(v).visit_expr(c, e, v);
|
||||
vt(v).visit_expr(t, e, v);
|
||||
vt(v).visit_expr(el, e, v);
|
||||
}
|
||||
case (expr_while(?x, ?b)) {
|
||||
vt(v).visit_expr(x, e, v);
|
||||
vt(v).visit_block(b, e, v);
|
||||
|
@ -307,7 +307,11 @@ fn walk_expr(&ast_visitor v, @ast::expr e) {
|
||||
walk_block(v, b);
|
||||
walk_expr_opt(v, eo);
|
||||
}
|
||||
|
||||
case (ast::expr_ternary(?c, ?t, ?e)) {
|
||||
walk_expr(v, c);
|
||||
walk_expr(v, t);
|
||||
walk_expr(v, e);
|
||||
}
|
||||
case (ast::expr_while(?x, ?b)) {
|
||||
walk_expr(v, x);
|
||||
walk_block(v, b);
|
||||
|
46
src/test/run-pass/ternary.rs
Normal file
46
src/test/run-pass/ternary.rs
Normal file
@ -0,0 +1,46 @@
|
||||
// xfail-stage0
|
||||
|
||||
fn test_simple() {
|
||||
auto x = true ? 10 : 11;
|
||||
assert (x == 10);
|
||||
}
|
||||
|
||||
fn test_precedence() {
|
||||
auto x;
|
||||
|
||||
x = true == false ? 10 : 11;
|
||||
assert (x == 11);
|
||||
|
||||
x = true ? false ? 10 : 11 : 12;
|
||||
assert (x == 11);
|
||||
|
||||
auto y = false ? 10 : 0xF0 | 0x0F;
|
||||
assert (y == 0xFF);
|
||||
}
|
||||
|
||||
fn test_associativity() {
|
||||
// Ternary is right-associative
|
||||
auto x = false ? 10 : false ? 11 : 12;
|
||||
assert (x == 12);
|
||||
}
|
||||
|
||||
fn test_lval() {
|
||||
let @mutable int box1 = @mutable 10;
|
||||
let @mutable int box2 = @mutable 10;
|
||||
*(true ? box1 : box2) = 100;
|
||||
assert (*box1 == 100);
|
||||
}
|
||||
|
||||
fn test_as_stmt() {
|
||||
auto s;
|
||||
true ? s = 10 : s = 12;
|
||||
assert (s == 10);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_simple();
|
||||
test_precedence();
|
||||
test_associativity();
|
||||
test_lval();
|
||||
test_as_stmt();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user