diff options
| author | Graydon Hoare <graydon@mozilla.com> | 2013-07-29 17:25:00 -0700 |
|---|---|---|
| committer | Graydon Hoare <graydon@mozilla.com> | 2013-07-30 18:50:28 -0700 |
| commit | c29e9fb60b46a66bd55643981edbec881c5def82 (patch) | |
| tree | 98eaff9295335b4ca1612f4f28cc7ed23c6fc5b6 /src/libsyntax | |
| parent | 9a2d183d6ae83a0119f5cce1f95e3770ddf5d689 (diff) | |
syntax: implement foreach .. in .. { .. } via desugaring.
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ast.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 156 | ||||
| -rw-r--r-- | src/libsyntax/fold.rs | 5 | ||||
| -rw-r--r-- | src/libsyntax/parse/classify.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 19 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 8 | ||||
| -rw-r--r-- | src/libsyntax/visit.rs | 5 |
7 files changed, 193 insertions, 2 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 97e69cd6918..d6bee4fe19f 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -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. */ diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index af05f726860..72bbc4a96c5 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -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) } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 51475528174..3016ee660dc 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -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), diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs index c7c556c9728..0bf87f10597 100644 --- a/src/libsyntax/parse/classify.rs +++ b/src/libsyntax/parse/classify.rs @@ -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) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 61dc6f47549..6ff4b91ec97 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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. diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 7083d6655f1..a0e5192ab3a 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -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, "'"); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 1d264bcc20e..990aaa99b78 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -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)); |
