From 05c0216654999e3d33373914b10aebf9dd7d4907 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 23 Jun 2011 15:15:50 -0700 Subject: [PATCH] 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. --- src/comp/front/ast.rs | 27 +++++++++++ src/comp/front/parser.rs | 16 ++++++- src/comp/middle/trans.rs | 3 ++ src/comp/middle/tstate/pre_post_conditions.rs | 3 ++ src/comp/middle/tstate/states.rs | 3 ++ src/comp/middle/typeck.rs | 3 ++ src/comp/middle/visit.rs | 5 ++ src/comp/middle/walk.rs | 6 ++- src/test/run-pass/ternary.rs | 46 +++++++++++++++++++ 9 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 src/test/run-pass/ternary.rs diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs index 31556c66b54..650e4eeddad 100644 --- a/src/comp/front/ast.rs +++ b/src/comp/front/ast.rs @@ -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 diff --git a/src/comp/front/parser.rs b/src/comp/front/parser.rs index 9fc7070e154..80a21639338 100644 --- a/src/comp/front/parser.rs +++ b/src/comp/front/parser.rs @@ -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(); diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index b7152d9e9ba..913c9d968b9 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -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); } diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs index 91d1f82f366..525073451c5 100644 --- a/src/comp/middle/tstate/pre_post_conditions.rs +++ b/src/comp/middle/tstate/pre_post_conditions.rs @@ -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 */ diff --git a/src/comp/middle/tstate/states.rs b/src/comp/middle/tstate/states.rs index 1637924d0a5..4bff6e1d8b4 100644 --- a/src/comp/middle/tstate/states.rs +++ b/src/comp/middle/tstate/states.rs @@ -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? */ diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index b9d4dda6df6..af4ebc62df1 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -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); diff --git a/src/comp/middle/visit.rs b/src/comp/middle/visit.rs index a88a6b4c3d0..48abf3f7a80 100644 --- a/src/comp/middle/visit.rs +++ b/src/comp/middle/visit.rs @@ -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); diff --git a/src/comp/middle/walk.rs b/src/comp/middle/walk.rs index b54a49f95c2..89df2668421 100644 --- a/src/comp/middle/walk.rs +++ b/src/comp/middle/walk.rs @@ -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); diff --git a/src/test/run-pass/ternary.rs b/src/test/run-pass/ternary.rs new file mode 100644 index 00000000000..e2e73d849a7 --- /dev/null +++ b/src/test/run-pass/ternary.rs @@ -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(); +} \ No newline at end of file