syntax: implement foreach .. in .. { .. } via desugaring.

This commit is contained in:
Graydon Hoare 2013-07-29 17:25:00 -07:00
parent 9a2d183d6a
commit c29e9fb60b
17 changed files with 215 additions and 3 deletions

View file

@ -239,6 +239,8 @@ impl CFGBuilder {
expr_exit
}
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
ast::expr_loop(ref body, _) => {
//
// [pred]

View file

@ -583,6 +583,8 @@ impl<'self, O:DataFlowOperator> PropagationContext<'self, O> {
copy_bits(new_loop_scope.break_bits, in_out);
}
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
ast::expr_loop(ref blk, _) => {
//
// (expr) <--+

View file

@ -503,6 +503,7 @@ fn visit_expr(expr: @expr, (this, vt): (@mut IrMaps, vt<@mut IrMaps>)) {
this.add_live_node_for_node(expr.id, ExprNode(expr.span));
visit::visit_expr(expr, (this, vt));
}
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
expr_binary(_, op, _, _) if ast_util::lazy_binop(op) => {
this.add_live_node_for_node(expr.id, ExprNode(expr.span));
visit::visit_expr(expr, (this, vt));
@ -1057,6 +1058,8 @@ impl Liveness {
self.propagate_through_loop(expr, Some(cond), blk, succ)
}
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
// Note that labels have been resolved, so we don't need to look
// at the label ident
expr_loop(ref blk, _) => {
@ -1487,6 +1490,7 @@ fn check_expr(expr: @expr, (this, vt): (@Liveness, vt<@Liveness>)) {
expr_paren(*) | expr_fn_block(*) | expr_path(*) | expr_self(*) => {
visit::visit_expr(expr, (this, vt));
}
expr_for_loop(*) => fail!("non-desugared expr_for_loop")
}
}

View file

@ -435,6 +435,8 @@ impl mem_categorization_ctxt {
ast::expr_inline_asm(*) => {
return self.cat_rvalue_node(expr, expr_ty);
}
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop")
}
}

View file

@ -487,6 +487,8 @@ impl VisitContext {
self.consume_block(blk, visitor);
}
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
expr_unary(_, _, lhs) => {
if !self.use_overloaded_operator(
expr, lhs, [], visitor)

View file

@ -5016,6 +5016,8 @@ impl Resolver {
}
}
expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
expr_break(Some(label)) | expr_again(Some(label)) => {
match self.search_ribs(self.label_ribs, label, expr.span,
DontAllowCapturingSelf) {

View file

@ -401,7 +401,9 @@ pub fn mark_for_expr(cx: &Context, e: &expr) {
expr_match(*) | expr_block(_) | expr_if(*) | expr_while(*) |
expr_break(_) | expr_again(_) | expr_unary(*) | expr_lit(_) |
expr_mac(_) | expr_addr_of(*) | expr_ret(_) | expr_loop(*) |
expr_loop_body(_) | expr_do_body(_) => ()
expr_loop_body(_) | expr_do_body(_) => (),
expr_for_loop(*) => fail!("non-desugared expr_for_loop")
}
}

View file

@ -3240,6 +3240,8 @@ pub fn expr_kind(tcx: ctxt,
RvalueStmtExpr
}
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
ast::expr_lit(_) | // Note: lit_str is carved out above
ast::expr_unary(*) |
ast::expr_addr_of(*) |

View file

@ -2559,6 +2559,8 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
fcx.write_nil(id);
}
}
ast::expr_for_loop(*) =>
fail!("non-desugared expr_for_loop"),
ast::expr_loop(ref body, _) => {
check_block_no_value(fcx, (body));
if !may_break(tcx, expr.id, body) {

View file

@ -1041,6 +1041,7 @@ pub mod guarantor {
rcx.fcx.tcx(), rcx.fcx.inh.method_map, expr));
None
}
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
}
}

View file

@ -465,6 +465,7 @@ pub enum expr_ {
expr_cast(@expr, Ty),
expr_if(@expr, Block, Option<@expr>),
expr_while(@expr, Block),
expr_for_loop(@pat, @expr, Block),
/* Conditionless loop (can be exited with break, cont, or ret)
Same semantics as while(true) { body }, but typestate knows that the
(implicit) condition is always true. */

View file

@ -16,11 +16,12 @@ use ast_util::{new_rename, new_mark, resolve};
use attr;
use attr::AttrMetaMethods;
use codemap;
use codemap::{span, ExpnInfo, NameAndSpan};
use codemap::{span, spanned, ExpnInfo, NameAndSpan};
use ext::base::*;
use fold::*;
use parse;
use parse::{parse_item_from_source_str};
use parse::token;
use parse::token::{ident_to_str, intern};
use visit;
use visit::Visitor;
@ -99,6 +100,159 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
}
}
}
// Desugar expr_for_loop
// From: `foreach <src_pat> in <src_expr> <src_loop_block>`
ast::expr_for_loop(src_pat, src_expr, ref src_loop_block) => {
let src_pat = src_pat.clone();
let src_expr = src_expr.clone();
// Expand any interior macros etc.
// NB: we don't fold pats yet. Curious.
let src_expr = fld.fold_expr(src_expr).clone();
let src_loop_block = fld.fold_block(src_loop_block).clone();
let span = s;
let lo = s.lo;
let hi = s.hi;
pub fn mk_expr(cx: @ExtCtxt, span: span,
node: expr_) -> @ast::expr {
@ast::expr {
id: cx.next_id(),
node: node,
span: span,
}
}
fn mk_block(cx: @ExtCtxt,
stmts: &[@ast::stmt],
expr: Option<@ast::expr>,
span: span) -> ast::Block {
ast::Block {
view_items: ~[],
stmts: stmts.to_owned(),
expr: expr,
id: cx.next_id(),
rules: ast::DefaultBlock,
span: span,
}
}
fn mk_simple_path(ident: ast::ident, span: span) -> ast::Path {
ast::Path {
span: span,
global: false,
idents: ~[ident],
rp: None,
types: ~[]
}
}
// to:
//
// {
// let _i = &mut <src_expr>;
// loop {
// match i.next() {
// None => break,
// Some(<src_pat>) => <src_loop_block>
// }
// }
// }
let local_ident = token::gensym_ident("i");
let some_ident = token::str_to_ident("Some");
let none_ident = token::str_to_ident("None");
let next_ident = token::str_to_ident("next");
let local_path_1 = mk_simple_path(local_ident, span);
let local_path_2 = mk_simple_path(local_ident, span);
let some_path = mk_simple_path(some_ident, span);
let none_path = mk_simple_path(none_ident, span);
// `let i = &mut <src_expr>`
let iter_decl_stmt = {
let ty = ast::Ty {
id: cx.next_id(),
node: ast::ty_infer,
span: span
};
let local = @ast::Local {
is_mutbl: false,
ty: ty,
pat: @ast::pat {
id: cx.next_id(),
node: ast::pat_ident(ast::bind_infer, local_path_1, None),
span: src_expr.span
},
init: Some(mk_expr(cx, src_expr.span,
ast::expr_addr_of(ast::m_mutbl, src_expr))),
id: cx.next_id(),
span: src_expr.span,
};
let e = @spanned(src_expr.span.lo,
src_expr.span.hi,
ast::decl_local(local));
@spanned(lo, hi, ast::stmt_decl(e, cx.next_id()))
};
// `None => break;`
let none_arm = {
let break_expr = mk_expr(cx, span, ast::expr_break(None));
let break_stmt = @spanned(lo, hi, ast::stmt_expr(break_expr, cx.next_id()));
let none_block = mk_block(cx, [break_stmt], None, span);
let none_pat = @ast::pat {
id: cx.next_id(),
node: ast::pat_ident(ast::bind_infer, none_path, None),
span: span
};
ast::arm {
pats: ~[none_pat],
guard: None,
body: none_block
}
};
// `Some(<src_pat>) => <src_loop_block>`
let some_arm = {
let pat = @ast::pat {
id: cx.next_id(),
node: ast::pat_enum(some_path, Some(~[src_pat])),
span: src_pat.span
};
ast::arm {
pats: ~[pat],
guard: None,
body: src_loop_block
}
};
// `match i.next() { ... }`
let match_stmt = {
let local_expr = mk_expr(cx, span, ast::expr_path(local_path_2));
let next_call_expr = mk_expr(cx, span,
ast::expr_method_call(cx.next_id(),
local_expr, next_ident,
~[], ~[], ast::NoSugar));
let match_expr = mk_expr(cx, span, ast::expr_match(next_call_expr,
~[none_arm, some_arm]));
@spanned(lo, hi, ast::stmt_expr(match_expr, cx.next_id()))
};
// `loop { ... }`
let loop_block = {
let loop_body_block = mk_block(cx, [match_stmt], None, span);
let loop_body_expr = mk_expr(cx, span, ast::expr_loop(loop_body_block, None));
let loop_body_stmt = @spanned(lo, hi, ast::stmt_expr(loop_body_expr, cx.next_id()));
mk_block(cx, [iter_decl_stmt,
loop_body_stmt],
None, span)
};
(ast::expr_block(loop_block), span)
}
_ => orig(e, s, fld)
}
}

View file

@ -559,6 +559,11 @@ pub fn noop_fold_expr(e: &expr_, fld: @ast_fold) -> expr_ {
expr_while(cond, ref body) => {
expr_while(fld.fold_expr(cond), fld.fold_block(body))
}
expr_for_loop(pat, iter, ref body) => {
expr_for_loop(fld.fold_pat(pat),
fld.fold_expr(iter),
fld.fold_block(body))
}
expr_loop(ref body, opt_ident) => {
expr_loop(
fld.fold_block(body),

View file

@ -28,6 +28,7 @@ pub fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool {
| ast::expr_block(_)
| ast::expr_while(*)
| ast::expr_loop(*)
| ast::expr_for_loop(*)
| ast::expr_call(_, _, ast::DoSugar)
| ast::expr_call(_, _, ast::ForSugar)
| ast::expr_method_call(_, _, _, _, _, ast::DoSugar)

View file

@ -29,7 +29,7 @@ use ast::{expr_method_call, expr_paren, expr_path, expr_repeat};
use ast::{expr_ret, expr_self, expr_struct, expr_tup, expr_unary};
use ast::{expr_vec, expr_vstore, expr_vstore_mut_box};
use ast::{expr_vstore_slice, expr_vstore_box};
use ast::{expr_vstore_mut_slice, expr_while, extern_fn, Field, fn_decl};
use ast::{expr_vstore_mut_slice, expr_while, expr_for_loop, extern_fn, Field, fn_decl};
use ast::{expr_vstore_uniq, Onceness, Once, Many};
use ast::{foreign_item, foreign_item_static, foreign_item_fn, foreign_mod};
use ast::{ident, impure_fn, inherited, item, item_, item_static};
@ -1622,6 +1622,8 @@ impl Parser {
hi = self.span.hi;
} else if self.eat_keyword(keywords::If) {
return self.parse_if_expr();
} else if self.eat_keyword(keywords::ForEach) {
return self.parse_for_expr();
} else if self.eat_keyword(keywords::For) {
return self.parse_sugary_call_expr(lo, ~"for", ForSugar,
expr_loop_body);
@ -2323,6 +2325,21 @@ impl Parser {
}
}
// parse a 'foreach' .. 'in' expression ('foreach' token already eaten)
pub fn parse_for_expr(&self) -> @expr {
// Parse: `foreach <src_pat> in <src_expr> <src_loop_block>`
let lo = self.last_span.lo;
let pat = self.parse_pat();
self.expect_keyword(keywords::In);
let expr = self.parse_expr();
let loop_block = self.parse_block();
let hi = self.span.hi;
self.mk_expr(lo, hi, expr_for_loop(pat, expr, loop_block))
}
// parse a 'for' or 'do'.
// the 'for' and 'do' expressions parse as calls, but look like
// function calls followed by a closure expression.

View file

@ -1228,6 +1228,14 @@ pub fn print_expr(s: @ps, expr: &ast::expr) {
space(s.s);
print_block(s, blk);
}
ast::expr_for_loop(pat, iter, ref blk) => {
head(s, "foreach");
print_pat(s, pat);
word_space(s, "in");
print_expr(s, iter);
space(s.s);
print_block(s, blk);
}
ast::expr_loop(ref blk, opt_ident) => {
for opt_ident.iter().advance |ident| {
word(s.s, "'");

View file

@ -512,6 +512,11 @@ pub fn visit_expr<E:Clone>(ex: @expr, (e, v): (E, vt<E>)) {
(v.visit_expr)(x, (e.clone(), v));
(v.visit_block)(b, (e.clone(), v));
}
expr_for_loop(pat, iter, ref b) => {
(v.visit_pat)(pat, (e.clone(), v));
(v.visit_expr)(iter, (e.clone(), v));
(v.visit_block)(b, (e.clone(), v));
}
expr_loop(ref b, _) => (v.visit_block)(b, (e.clone(), v)),
expr_match(x, ref arms) => {
(v.visit_expr)(x, (e.clone(), v));