about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-10-13 19:37:40 +0000
committerbors <bors@rust-lang.org>2014-10-13 19:37:40 +0000
commitc7e072427469ef50880a722b7b6cd00b052939f5 (patch)
treec87cb2f922c34bf1b6d9b6152ae466a31b817eff /src/libsyntax
parentdaa71e40a98f32084539e7cfe893a3e94f83fe69 (diff)
parent16ccdba8512fc7ce397095cf5d62858ef2234c47 (diff)
downloadrust-c7e072427469ef50880a722b7b6cd00b052939f5.tar.gz
rust-c7e072427469ef50880a722b7b6cd00b052939f5.zip
auto merge of #17733 : jgallagher/rust/while-let, r=alexcrichton
This is *heavily* based on `if let` (#17634) by @jakub- and @kballard

This should close #17687
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs5
-rw-r--r--src/libsyntax/ext/build.rs8
-rw-r--r--src/libsyntax/ext/expand.rs36
-rw-r--r--src/libsyntax/feature_gate.rs5
-rw-r--r--src/libsyntax/fold.rs6
-rw-r--r--src/libsyntax/parse/classify.rs1
-rw-r--r--src/libsyntax/parse/parser.rs18
-rw-r--r--src/libsyntax/print/pprust.rs13
-rw-r--r--src/libsyntax/visit.rs5
9 files changed, 95 insertions, 2 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 44b327745ed..bf4734529a2 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -524,6 +524,8 @@ pub enum 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.
+    ExprWhileLet(P<Pat>, P<Expr>, P<Block>, Option<Ident>),
+    // FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
     ExprForLoop(P<Pat>, P<Expr>, P<Block>, Option<Ident>),
     // Conditionless loop (can be exited with break, cont, or ret)
     // FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
@@ -579,7 +581,8 @@ pub struct QPath {
 #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
 pub enum MatchSource {
     MatchNormal,
-    MatchIfLetDesugar
+    MatchIfLetDesugar,
+    MatchWhileLetDesugar,
 }
 
 #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 87983e1aea3..98ac6fe6a6c 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -147,6 +147,8 @@ pub trait AstBuilder {
     fn expr_some(&self, sp: Span, expr: P<ast::Expr>) -> P<ast::Expr>;
     fn expr_none(&self, sp: Span) -> P<ast::Expr>;
 
+    fn expr_break(&self, sp: Span) -> P<ast::Expr>;
+
     fn expr_tuple(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr>;
 
     fn expr_fail(&self, span: Span, msg: InternedString) -> P<ast::Expr>;
@@ -688,6 +690,12 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         self.expr_path(none)
     }
 
+
+    fn expr_break(&self, sp: Span) -> P<ast::Expr> {
+        self.expr(sp, ast::ExprBreak(None))
+    }
+
+
     fn expr_tuple(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
         self.expr(sp, ast::ExprTup(exprs))
     }
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index efe4b76354f..575dcf32dd6 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -67,6 +67,42 @@ 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 ExprWhileLet
+        // From: `[opt_ident]: while let <pat> = <expr> <body>`
+        ast::ExprWhileLet(pat, expr, body, opt_ident) => {
+            // to:
+            //
+            //   [opt_ident]: loop {
+            //     match <expr> {
+            //       <pat> => <body>,
+            //       _ => break
+            //     }
+            //   }
+
+            // `<pat> => <body>`
+            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 <expr> { ... }`
+            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 <pat> = <expr> <body> [<elseopt>]`
         ast::ExprIfLet(pat, expr, body, mut elseopt) => {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 590157d677d..c1877c827a4 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -73,6 +73,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
     ("slicing_syntax", Active),
 
     ("if_let", Active),
+    ("while_let", Active),
 
     // if you change this list without updating src/doc/reference.md, cmr will be sad
 
@@ -345,6 +346,10 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
                                   e.span,
                                   "slicing syntax is experimental");
             }
+            ast::ExprWhileLet(..) => {
+                self.gate_feature("while_let", e.span,
+                                  "`while let` syntax is experimental");
+            }
             _ => {}
         }
         visit::walk_expr(self, e);
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 3e16bca72ef..8faecd407a8 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -1216,6 +1216,12 @@ pub fn noop_fold_expr<T: Folder>(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 cb57318445e..d46d078c776 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 460e96a6ad8..d5253cff6cb 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<ast::Ident>) -> P<Expr> {
+        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<ast::Ident>) -> 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(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<ast::Ident>) -> P<Expr> {
         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 c9a6354bf97..d32828192e9 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1515,6 +1515,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 c98c3bffcb6..00377a324a7 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -733,6 +733,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);