1
Fork 0

Teach libsyntax about if let

This commit is contained in:
Kevin Ballard 2014-08-24 18:04:29 -07:00 committed by Jakub Wieczorek
parent 38015eeb70
commit 5d8cfd53b5
6 changed files with 71 additions and 13 deletions

View file

@ -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.

View file

@ -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),

View file

@ -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(..)

View file

@ -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 => {
if self.is_keyword(kw) {
self.bump();
true
}
_ => false
}
} 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<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 +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<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> {

View file

@ -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() {

View file

@ -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)