Skip to content

Commit

Permalink
Teach libsyntax about while let
Browse files Browse the repository at this point in the history
  • Loading branch information
hydhknn committed Oct 11, 2014
1 parent 78a7676 commit 0c2c811
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/libsyntax/ast.rs
Expand Up @@ -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.
Expand Down
36 changes: 36 additions & 0 deletions src/libsyntax/ext/expand.rs
Expand Up @@ -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) => {
Expand Down
6 changes: 6 additions & 0 deletions src/libsyntax/fold.rs
Expand Up @@ -1218,6 +1218,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),
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/parse/classify.rs
Expand Up @@ -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
Expand Down
18 changes: 17 additions & 1 deletion src/libsyntax/parse/parser.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -2935,14 +2935,30 @@ 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();
let hi = body.span.hi;
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();
Expand Down
13 changes: 13 additions & 0 deletions src/libsyntax/print/pprust.rs
Expand Up @@ -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));
Expand Down
5 changes: 5 additions & 0 deletions src/libsyntax/visit.rs
Expand Up @@ -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);
Expand Down

0 comments on commit 0c2c811

Please sign in to comment.