about summary refs log tree commit diff
path: root/src/libsyntax/ext
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-07-31 03:58:21 -0700
committerbors <bors@rust-lang.org>2013-07-31 03:58:21 -0700
commit6296dc0d73527301f18ef55b5f2d07c3241b8a00 (patch)
tree9ca1c483e62e852fb33db8aee766ba4c7e798eb9 /src/libsyntax/ext
parent8b7e241e02bb9f82d7b931033afde477d03ff4f2 (diff)
parenta696f0fecb9d11204f64d310eb66e095f64bd04a (diff)
downloadrust-6296dc0d73527301f18ef55b5f2d07c3241b8a00.tar.gz
rust-6296dc0d73527301f18ef55b5f2d07c3241b8a00.zip
auto merge of #8141 : graydon/rust/foreach-in-sketch, r=brson
This is a preliminary implementation of `for ... in ... { ...}` using a transitionary keyword `foreach`. Codesize seems to be a little bit down (10% or less non-opt) and otherwise it seems quite trivial to rewrite lambda-based loops to use it. Once we've rewritten the codebase away from lambda-based `for` we can retarget that word at the same production, snapshot, rewrite the keywords in one go, and expire `foreach`.

Feedback welcome. It's a desugaring-based approach which is arguably something we should have been doing for other constructs before. I apologize both for the laziness associated with doing it this way and with any sense that I'm bending rules I put in place previously concerning "never doing desugarings". I put the expansion in `expand.rs` and would be amenable to the argument that the code there needs better factoring / more helpers / to move to a submodule or helper function. It does seem to work at this point, though, and I gather we'd like to get the shift done relatively quickly.

Diffstat (limited to 'src/libsyntax/ext')
-rw-r--r--src/libsyntax/ext/expand.rs156
1 files changed, 155 insertions, 1 deletions
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)
     }
 }