diff options
| author | bors <bors@rust-lang.org> | 2014-09-30 23:17:20 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2014-09-30 23:17:20 +0000 |
| commit | 57a05cf49b3149427e3fe190c07f8343a29ad86c (patch) | |
| tree | e936d71b10c6f881337d44a960e1b8901cd50b38 /src/libsyntax | |
| parent | 88d1a22f76a774b2df3c904ceb54c86e58a859c3 (diff) | |
| parent | e723051a2eb840a710f2b79afc1b42b0b707d0e4 (diff) | |
| download | rust-57a05cf49b3149427e3fe190c07f8343a29ad86c.tar.gz rust-57a05cf49b3149427e3fe190c07f8343a29ad86c.zip | |
auto merge of #17634 : jakub-/rust/if_let, r=kballard
Continuation of https://github.com/rust-lang/rust/pull/16741.
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ast.rs | 9 | ||||
| -rw-r--r-- | src/libsyntax/config.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/ext/build.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 93 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 6 | ||||
| -rw-r--r-- | src/libsyntax/fold.rs | 11 | ||||
| -rw-r--r-- | src/libsyntax/parse/classify.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 39 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 35 | ||||
| -rw-r--r-- | src/libsyntax/visit.rs | 8 |
10 files changed, 186 insertions, 22 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 0fee3ff3218..59c824d0eae 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -521,6 +521,7 @@ pub enum Expr_ { ExprLit(P<Lit>), ExprCast(P<Expr>, P<Ty>), ExprIf(P<Expr>, P<Block>, Option<P<Expr>>), + ExprIfLet(P<Pat>, P<Expr>, P<Block>, Option<P<Expr>>), // FIXME #6993: change to Option<Name> ... or not, if these are hygienic. ExprWhile(P<Expr>, P<Block>, Option<Ident>), // FIXME #6993: change to Option<Name> ... or not, if these are hygienic. @@ -528,7 +529,7 @@ pub enum Expr_ { // Conditionless loop (can be exited with break, cont, or ret) // FIXME #6993: change to Option<Name> ... or not, if these are hygienic. ExprLoop(P<Block>, Option<Ident>), - ExprMatch(P<Expr>, Vec<Arm>), + ExprMatch(P<Expr>, Vec<Arm>, MatchSource), ExprFnBlock(CaptureClause, P<FnDecl>, P<Block>), ExprProc(P<FnDecl>, P<Block>), ExprUnboxedFn(CaptureClause, UnboxedClosureKind, P<FnDecl>, P<Block>), @@ -577,6 +578,12 @@ pub struct QPath { } #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] +pub enum MatchSource { + MatchNormal, + MatchIfLetDesugar +} + +#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub enum CaptureClause { CaptureByValue, CaptureByRef, diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 5b17f6f004a..41b7218da0c 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -210,10 +210,10 @@ fn fold_expr(cx: &mut Context, expr: P<ast::Expr>) -> P<ast::Expr> { fold::noop_fold_expr(ast::Expr { id: id, node: match node { - ast::ExprMatch(m, arms) => { + ast::ExprMatch(m, arms, source) => { ast::ExprMatch(m, arms.into_iter() .filter(|a| (cx.in_cfg)(a.attrs.as_slice())) - .collect()) + .collect(), source) } _ => node }, diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 0586868eb45..f2b806f43cc 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -845,7 +845,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { } fn expr_match(&self, span: Span, arg: P<ast::Expr>, arms: Vec<ast::Arm>) -> P<Expr> { - self.expr(span, ast::ExprMatch(arg, arms)) + self.expr(span, ast::ExprMatch(arg, arms, ast::MatchNormal)) } fn expr_if(&self, span: Span, cond: P<ast::Expr>, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 9f3df1a7623..fa3ccc8cf32 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -39,7 +39,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> { e.and_then(|ast::Expr {id, node, span}| match node { // expr_mac should really be expr_ext or something; it's the // entry-point for all syntax extensions. - ExprMac(mac) => { + ast::ExprMac(mac) => { let expanded_expr = match expand_mac_invoc(mac, span, |r| r.make_expr(), mark_expr, fld) { @@ -67,6 +67,97 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> { fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident)) } + // Desugar ExprIfLet + // From: `if let <pat> = <expr> <body> [<elseopt>]` + ast::ExprIfLet(pat, expr, body, mut elseopt) => { + // to: + // + // match <expr> { + // <pat> => <body>, + // [_ if <elseopt_if_cond> => <elseopt_if_body>,] + // _ => [<elseopt> | ()] + // } + + // `<pat> => <body>` + let pat_arm = { + let body_expr = fld.cx.expr_block(body); + fld.cx.arm(pat.span, vec![pat], body_expr) + }; + + // `[_ if <elseopt_if_cond> => <elseopt_if_body>,]` + let else_if_arms = { + let mut arms = vec![]; + loop { + let elseopt_continue = elseopt + .and_then(|els| els.and_then(|els| match els.node { + // else if + ast::ExprIf(cond, then, elseopt) => { + let pat_under = fld.cx.pat_wild(span); + arms.push(ast::Arm { + attrs: vec![], + pats: vec![pat_under], + guard: Some(cond), + body: fld.cx.expr_block(then) + }); + elseopt.map(|elseopt| (elseopt, true)) + } + _ => Some((P(els), false)) + })); + match elseopt_continue { + Some((e, true)) => { + elseopt = Some(e); + } + Some((e, false)) => { + elseopt = Some(e); + break; + } + None => { + elseopt = None; + break; + } + } + } + arms + }; + + // `_ => [<elseopt> | ()]` + let else_arm = { + let pat_under = fld.cx.pat_wild(span); + let else_expr = elseopt.unwrap_or_else(|| fld.cx.expr_lit(span, ast::LitNil)); + fld.cx.arm(span, vec![pat_under], else_expr) + }; + + let mut arms = Vec::with_capacity(else_if_arms.len() + 2); + arms.push(pat_arm); + arms.push_all_move(else_if_arms); + arms.push(else_arm); + + let match_expr = fld.cx.expr(span, ast::ExprMatch(expr, arms, ast::MatchIfLetDesugar)); + fld.fold_expr(match_expr) + } + + // Desugar support for ExprIfLet in the ExprIf else position + ast::ExprIf(cond, blk, elseopt) => { + let elseopt = elseopt.map(|els| els.and_then(|els| match els.node { + ast::ExprIfLet(..) => { + // wrap the if-let expr in a block + let span = els.span; + let blk = P(ast::Block { + view_items: vec![], + stmts: vec![], + expr: Some(P(els)), + id: ast::DUMMY_NODE_ID, + rules: ast::DefaultBlock, + span: span + }); + fld.cx.expr_block(blk) + } + _ => P(els) + })); + let if_expr = fld.cx.expr(span, ast::ExprIf(cond, blk, elseopt)); + if_expr.map(|e| noop_fold_expr(e, fld)) + } + ast::ExprLoop(loop_block, opt_ident) => { let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld); fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident)) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 1c6ee8acc94..ca6d488772c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -71,6 +71,8 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("associated_types", Active), ("visible_private_types", Active), + ("if_let", Active), + // if you change this list without updating src/doc/rust.md, cmr will be sad // A temporary feature gate used to enable parser extensions needed @@ -356,6 +358,10 @@ impl<'a, 'v> Visitor<'v> for Context<'a> { e.span, "tuple indexing is experimental"); } + ast::ExprIfLet(..) => { + self.gate_feature("if_let", e.span, + "`if let` syntax is experimental"); + } _ => {} } visit::walk_expr(self, e); diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 53be7f2c20c..31bec58a4da 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1205,6 +1205,12 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) -> folder.fold_block(tr), fl.map(|x| folder.fold_expr(x))) } + ExprIfLet(pat, expr, tr, fl) => { + ExprIfLet(folder.fold_pat(pat), + folder.fold_expr(expr), + folder.fold_block(tr), + fl.map(|x| folder.fold_expr(x))) + } ExprWhile(cond, body, opt_ident) => { ExprWhile(folder.fold_expr(cond), folder.fold_block(body), @@ -1220,9 +1226,10 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) -> ExprLoop(folder.fold_block(body), opt_ident.map(|i| folder.fold_ident(i))) } - ExprMatch(expr, arms) => { + ExprMatch(expr, arms, source) => { ExprMatch(folder.fold_expr(expr), - arms.move_map(|x| folder.fold_arm(x))) + arms.move_map(|x| folder.fold_arm(x)), + source) } ExprFnBlock(capture_clause, decl, body) => { ExprFnBlock(capture_clause, diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs index cdd221aca7c..cb57318445e 100644 --- a/src/libsyntax/parse/classify.rs +++ b/src/libsyntax/parse/classify.rs @@ -24,6 +24,7 @@ use ast; pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { match e.node { ast::ExprIf(..) + | ast::ExprIfLet(..) | ast::ExprMatch(..) | ast::ExprBlock(_) | ast::ExprWhile(..) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 415ff6a4097..0780e68a062 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -23,7 +23,7 @@ use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, EMPTY_CTXT, EnumDef, Explicit use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain}; use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprBox}; use ast::{ExprBreak, ExprCall, ExprCast}; -use ast::{ExprField, ExprTupField, ExprFnBlock, ExprIf, ExprIndex, ExprSlice}; +use ast::{ExprField, ExprTupField, ExprFnBlock, ExprIf, ExprIfLet, ExprIndex, ExprSlice}; use ast::{ExprLit, ExprLoop, ExprMac}; use ast::{ExprMethodCall, ExprParen, ExprPath, ExprProc}; use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn}; @@ -38,7 +38,7 @@ use ast::{ItemMac, ItemMod, ItemStruct, ItemTrait, ItemTy}; use ast::{LifetimeDef, Lit, Lit_}; use ast::{LitBool, LitChar, LitByte, LitBinary}; use ast::{LitNil, LitStr, LitInt, Local, LocalLet}; -use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, Matcher, MatchNonterminal}; +use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, Matcher, MatchNonterminal, MatchNormal}; use ast::{MatchSeq, MatchTok, Method, MutTy, BiMul, Mutability}; use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, UnNot}; use ast::{Pat, PatEnum, PatIdent, PatLit, PatRange, PatRegion, PatStruct}; @@ -576,12 +576,11 @@ impl<'a> Parser<'a> { /// If the next token is the given keyword, eat it and return /// true. Otherwise, return false. pub fn eat_keyword(&mut self, kw: keywords::Keyword) -> bool { - match self.token { - token::IDENT(sid, false) if kw.to_name() == sid.name => { - self.bump(); - true - } - _ => false + if self.is_keyword(kw) { + self.bump(); + true + } else { + false } } @@ -2860,8 +2859,11 @@ impl<'a> Parser<'a> { } } - /// Parse an 'if' expression ('if' token already eaten) + /// Parse an 'if' or 'if let' expression ('if' token already eaten) pub fn parse_if_expr(&mut self) -> P<Expr> { + if self.is_keyword(keywords::Let) { + return self.parse_if_let_expr(); + } let lo = self.last_span.lo; let cond = self.parse_expr_res(RestrictionNoStructLiteral); let thn = self.parse_block(); @@ -2875,6 +2877,23 @@ impl<'a> Parser<'a> { self.mk_expr(lo, hi, ExprIf(cond, thn, els)) } + /// Parse an 'if let' expression ('if' token already eaten) + pub fn parse_if_let_expr(&mut self) -> P<Expr> { + let lo = self.last_span.lo; + self.expect_keyword(keywords::Let); + let pat = self.parse_pat(); + self.expect(&token::EQ); + let expr = self.parse_expr_res(RestrictionNoStructLiteral); + let thn = self.parse_block(); + let (hi, els) = if self.eat_keyword(keywords::Else) { + let expr = self.parse_else_expr(); + (expr.span.hi, Some(expr)) + } else { + (thn.span.hi, None) + }; + self.mk_expr(lo, hi, ExprIfLet(pat, expr, thn, els)) + } + // `|args| expr` pub fn parse_lambda_expr(&mut self, capture_clause: CaptureClause) -> P<Expr> { @@ -2956,7 +2975,7 @@ impl<'a> Parser<'a> { } let hi = self.span.hi; self.bump(); - return self.mk_expr(lo, hi, ExprMatch(discriminant, arms)); + return self.mk_expr(lo, hi, ExprMatch(discriminant, arms, MatchNormal)); } pub fn parse_arm(&mut self) -> Arm { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ae4ba611bab..a8e99b4e85f 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1307,6 +1307,19 @@ impl<'a> State<'a> { try!(self.print_block(&**then)); self.print_else(e.as_ref().map(|e| &**e)) } + // "another else-if-let" + ast::ExprIfLet(ref pat, ref expr, ref then, ref e) => { + try!(self.cbox(indent_unit - 1u)); + try!(self.ibox(0u)); + try!(word(&mut self.s, " else if let ")); + try!(self.print_pat(&**pat)); + try!(space(&mut self.s)); + try!(self.word_space("=")); + try!(self.print_expr(&**expr)); + try!(space(&mut self.s)); + try!(self.print_block(&**then)); + self.print_else(e.as_ref().map(|e| &**e)) + } // "final else" ast::ExprBlock(ref b) => { try!(self.cbox(indent_unit - 1u)); @@ -1325,15 +1338,26 @@ impl<'a> State<'a> { } pub fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, - elseopt: Option<&ast::Expr>, chk: bool) -> IoResult<()> { + elseopt: Option<&ast::Expr>) -> IoResult<()> { try!(self.head("if")); - if chk { try!(self.word_nbsp("check")); } try!(self.print_expr(test)); try!(space(&mut self.s)); try!(self.print_block(blk)); self.print_else(elseopt) } + pub fn print_if_let(&mut self, pat: &ast::Pat, expr: &ast::Expr, blk: &ast::Block, + elseopt: Option<&ast::Expr>) -> IoResult<()> { + try!(self.head("if let")); + try!(self.print_pat(pat)); + try!(space(&mut self.s)); + try!(self.word_space("=")); + try!(self.print_expr(expr)); + try!(space(&mut self.s)); + try!(self.print_block(blk)); + self.print_else(elseopt) + } + pub fn print_mac(&mut self, m: &ast::Mac) -> IoResult<()> { match m.node { // I think it's reasonable to hide the ctxt here: @@ -1474,7 +1498,10 @@ impl<'a> State<'a> { try!(self.print_type(&**ty)); } ast::ExprIf(ref test, ref blk, ref elseopt) => { - try!(self.print_if(&**test, &**blk, elseopt.as_ref().map(|e| &**e), false)); + try!(self.print_if(&**test, &**blk, elseopt.as_ref().map(|e| &**e))); + } + ast::ExprIfLet(ref pat, ref expr, ref blk, ref elseopt) => { + try!(self.print_if_let(&**pat, &**expr, &** blk, elseopt.as_ref().map(|e| &**e))); } ast::ExprWhile(ref test, ref blk, opt_ident) => { for ident in opt_ident.iter() { @@ -1508,7 +1535,7 @@ impl<'a> State<'a> { try!(space(&mut self.s)); try!(self.print_block(&**blk)); } - ast::ExprMatch(ref expr, ref arms) => { + ast::ExprMatch(ref expr, ref arms, _) => { try!(self.cbox(indent_unit)); try!(self.ibox(4)); try!(self.word_nbsp("match")); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 3b2ed30b76d..6fc79e2c42a 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -730,13 +730,19 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_expr(&**subexpression); visitor.visit_block(&**block) } + ExprIfLet(ref pattern, ref subexpression, ref if_block, ref optional_else) => { + visitor.visit_pat(&**pattern); + visitor.visit_expr(&**subexpression); + visitor.visit_block(&**if_block); + walk_expr_opt(visitor, optional_else); + } ExprForLoop(ref pattern, ref subexpression, ref block, _) => { visitor.visit_pat(&**pattern); visitor.visit_expr(&**subexpression); visitor.visit_block(&**block) } ExprLoop(ref block, _) => visitor.visit_block(&**block), - ExprMatch(ref subexpression, ref arms) => { + ExprMatch(ref subexpression, ref arms, _) => { visitor.visit_expr(&**subexpression); for arm in arms.iter() { visitor.visit_arm(arm) |
