syntax: implement foreach .. in .. { .. } via desugaring.
This commit is contained in:
parent
9a2d183d6a
commit
c29e9fb60b
17 changed files with 215 additions and 3 deletions
|
@ -239,6 +239,8 @@ impl CFGBuilder {
|
|||
expr_exit
|
||||
}
|
||||
|
||||
ast::expr_for_loop(*) => fail!("non-desugared expr_for_loop"),
|
||||
|
||||
ast::expr_loop(ref body, _) => {
|
||||
//
|
||||
// [pred]
|
||||
|
|
|
@ -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) <--+
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(*) |
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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, "'");
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue