From 5d8cfd53b513d999ffff22f17e3066a30a8ed949 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sun, 24 Aug 2014 18:04:29 -0700 Subject: [PATCH] Teach libsyntax about `if let` --- src/libsyntax/ast.rs | 1 + src/libsyntax/fold.rs | 6 ++++++ src/libsyntax/parse/classify.rs | 1 + src/libsyntax/parse/parser.rs | 35 ++++++++++++++++++++++++--------- src/libsyntax/print/pprust.rs | 33 ++++++++++++++++++++++++++++--- src/libsyntax/visit.rs | 8 +++++++- 6 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 0fee3ff3218..02922fe3655 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -521,6 +521,7 @@ pub enum Expr_ { ExprLit(P), ExprCast(P, P), ExprIf(P, P, Option>), + ExprIfLet(P, P, P, Option>), // 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. diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 53be7f2c20c..8bdc64a926d 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1205,6 +1205,12 @@ pub fn noop_fold_expr(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), 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..d4ba81c737b 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}; @@ -576,13 +576,10 @@ 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 } } /// If the given word is not a keyword, signal an error. @@ -2860,8 +2857,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 { + 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 +2875,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 { + 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 { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ae4ba611bab..1e5810fb311 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() { 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)