diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 274bb2e39e019..675249b259148 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -524,6 +524,8 @@ pub enum Expr_ { // FIXME #6993: change to Option ... or not, if these are hygienic. ExprWhile(P, P, Option), // FIXME #6993: change to Option ... or not, if these are hygienic. + ExprWhileLet(P, P, P, Option), + // FIXME #6993: change to Option ... or not, if these are hygienic. ExprForLoop(P, P, P, Option), // Conditionless loop (can be exited with break, cont, or ret) // FIXME #6993: change to Option ... or not, if these are hygienic. diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index efe4b76354f56..575dcf32dd6cb 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -67,6 +67,42 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident)) } + // Desugar ExprWhileLet + // From: `[opt_ident]: while let = ` + ast::ExprWhileLet(pat, expr, body, opt_ident) => { + // to: + // + // [opt_ident]: loop { + // match { + // => , + // _ => break + // } + // } + + // ` => ` + let pat_arm = { + let body_expr = fld.cx.expr_block(body); + fld.cx.arm(pat.span, vec![pat], body_expr) + }; + + // `_ => break` + let break_arm = { + let pat_under = fld.cx.pat_wild(span); + let break_expr = fld.cx.expr_break(span); + fld.cx.arm(span, vec![pat_under], break_expr) + }; + + // `match { ... }` + let arms = vec![pat_arm, break_arm]; + let match_expr = fld.cx.expr(span, + ast::ExprMatch(expr, arms, ast::MatchWhileLetDesugar)); + + // `[opt_ident]: loop { ... }` + let loop_block = fld.cx.block_expr(match_expr); + let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld); + fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident)) + } + // Desugar ExprIfLet // From: `if let = []` ast::ExprIfLet(pat, expr, body, mut elseopt) => { diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 32e226361e9d4..6ecb4d3208d14 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1218,6 +1218,12 @@ pub fn noop_fold_expr(Expr {id, node, span}: Expr, folder: &mut T) -> folder.fold_block(body), opt_ident.map(|i| folder.fold_ident(i))) } + ExprWhileLet(pat, expr, body, opt_ident) => { + ExprWhileLet(folder.fold_pat(pat), + folder.fold_expr(expr), + folder.fold_block(body), + opt_ident.map(|i| folder.fold_ident(i))) + } ExprForLoop(pat, iter, body, opt_ident) => { ExprForLoop(folder.fold_pat(pat), folder.fold_expr(iter), diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs index cb57318445e41..d46d078c776be 100644 --- a/src/libsyntax/parse/classify.rs +++ b/src/libsyntax/parse/classify.rs @@ -28,6 +28,7 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { | ast::ExprMatch(..) | ast::ExprBlock(_) | ast::ExprWhile(..) + | ast::ExprWhileLet(..) | ast::ExprLoop(..) | ast::ExprForLoop(..) => false, _ => true diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index e7f40cf072296..5f304fb0aeb82 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -26,7 +26,7 @@ use ast::{ExprField, ExprTupField, ExprFnBlock, ExprIf, ExprIfLet, ExprIndex, Ex use ast::{ExprLit, ExprLoop, ExprMac}; use ast::{ExprMethodCall, ExprParen, ExprPath, ExprProc}; use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn}; -use ast::{ExprVec, ExprWhile, ExprForLoop, Field, FnDecl}; +use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl}; use ast::{Once, Many}; use ast::{FnUnboxedClosureKind, FnMutUnboxedClosureKind}; use ast::{FnOnceUnboxedClosureKind}; @@ -2935,7 +2935,11 @@ impl<'a> Parser<'a> { self.mk_expr(lo, hi, ExprForLoop(pat, expr, loop_block, opt_ident)) } + /// Parse a 'while' or 'while let' expression ('while' token already eaten) pub fn parse_while_expr(&mut self, opt_ident: Option) -> P { + if self.is_keyword(keywords::Let) { + return self.parse_while_let_expr(opt_ident); + } let lo = self.last_span.lo; let cond = self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL); let body = self.parse_block(); @@ -2943,6 +2947,18 @@ impl<'a> Parser<'a> { return self.mk_expr(lo, hi, ExprWhile(cond, body, opt_ident)); } + /// Parse a 'while let' expression ('while' token already eaten) + pub fn parse_while_let_expr(&mut self, opt_ident: Option) -> P { + 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(RESTRICTION_NO_STRUCT_LITERAL); + let body = self.parse_block(); + let hi = body.span.hi; + return self.mk_expr(lo, hi, ExprWhileLet(pat, expr, body, opt_ident)); + } + pub fn parse_loop_expr(&mut self, opt_ident: Option) -> P { let lo = self.last_span.lo; let body = self.parse_block(); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index e1a2b2aeefeea..fd3dd1c5a2cee 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1525,6 +1525,19 @@ impl<'a> State<'a> { try!(space(&mut self.s)); try!(self.print_block(&**blk)); } + ast::ExprWhileLet(ref pat, ref expr, ref blk, opt_ident) => { + for ident in opt_ident.iter() { + try!(self.print_ident(*ident)); + try!(self.word_space(":")); + } + try!(self.head("while 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)); + } ast::ExprForLoop(ref pat, ref iter, ref blk, opt_ident) => { for ident in opt_ident.iter() { try!(self.print_ident(*ident)); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 5c7b144f4ab6e..ce316ef57668a 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -737,6 +737,11 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_block(&**if_block); walk_expr_opt(visitor, optional_else); } + ExprWhileLet(ref pattern, ref subexpression, ref block, _) => { + visitor.visit_pat(&**pattern); + visitor.visit_expr(&**subexpression); + visitor.visit_block(&**block); + } ExprForLoop(ref pattern, ref subexpression, ref block, _) => { visitor.visit_pat(&**pattern); visitor.visit_expr(&**subexpression);