about summary refs log tree commit diff
path: root/src/libsyntax/ext
diff options
context:
space:
mode:
authorJorge Aparicio <japaricious@gmail.com>2015-01-08 18:04:26 -0500
committerJorge Aparicio <japaricious@gmail.com>2015-01-30 10:36:31 -0500
commit9fdc0effd292b097ae487ec4d927ca15102c5791 (patch)
treec6f0cc475b0fe7cee697fdda83714965e33bed01 /src/libsyntax/ext
parenta65d3f5b98cc94f0a759fbf1a08be9aee0f97883 (diff)
downloadrust-9fdc0effd292b097ae487ec4d927ca15102c5791.tar.gz
rust-9fdc0effd292b097ae487ec4d927ca15102c5791.zip
implement for loop desugaring
Diffstat (limited to 'src/libsyntax/ext')
-rw-r--r--src/libsyntax/ext/expand.rs96
1 files changed, 93 insertions, 3 deletions
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index acf0fe7f6cd..357bd2c17ab 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -225,11 +225,101 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
             fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident))
         }
 
+        // Desugar ExprForLoop
+        // From: `[opt_ident]: for <pat> in <head> <body>`
         ast::ExprForLoop(pat, head, body, opt_ident) => {
-            let pat = fld.fold_pat(pat);
+            // to:
+            //
+            //   match ::std::iter::IntoIterator::into_iter(<head>) {
+            //     mut iter => {
+            //       [opt_ident]: loop {
+            //         match ::std::iter::Iterator::next(&mut iter) {
+            //           ::std::option::Option::Some(<pat>) => <body>,
+            //           ::std::option::Option::None => break
+            //         }
+            //       }
+            //     }
+            //   }
+
+            // expand <head>
             let head = fld.fold_expr(head);
-            let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
-            fld.cx.expr(span, ast::ExprForLoop(pat, head, body, opt_ident))
+
+            // create an hygienic ident
+            let iter = {
+                let ident = fld.cx.ident_of("iter");
+                let new_ident = fresh_name(&ident);
+                let rename = (ident, new_ident);
+                let mut rename_list = vec![rename];
+                let mut rename_fld = IdentRenamer{ renames: &mut rename_list };
+
+                rename_fld.fold_ident(ident)
+            };
+
+            let pat_span = pat.span;
+            // `:;std::option::Option::Some(<pat>) => <body>`
+            let pat_arm = {
+                let body_expr = fld.cx.expr_block(body);
+                let some_pat = fld.cx.pat_some(pat_span, pat);
+
+                fld.cx.arm(pat_span, vec![some_pat], body_expr)
+            };
+
+            // `::std::option::Option::None => break`
+            let break_arm = {
+                let break_expr = fld.cx.expr_break(span);
+
+                fld.cx.arm(span, vec![fld.cx.pat_none(span)], break_expr)
+            };
+
+            // `match ::std::iter::Iterator::next(&mut iter) { ... }`
+            let match_expr = {
+                let next_path = {
+                    let strs = vec![
+                        fld.cx.ident_of("std"),
+                        fld.cx.ident_of("iter"),
+                        fld.cx.ident_of("Iterator"),
+                        fld.cx.ident_of("next"),
+                    ];
+
+                    fld.cx.path_global(span, strs)
+                };
+                let ref_mut_iter = fld.cx.expr_mut_addr_of(span, fld.cx.expr_ident(span, iter));
+                let next_expr =
+                    fld.cx.expr_call(span, fld.cx.expr_path(next_path), vec![ref_mut_iter]);
+                let arms = vec![pat_arm, break_arm];
+
+                // FIXME(japaric) This should use `ForLoopDesugar` as MatchSource
+                fld.cx.expr_match(pat_span, next_expr, arms)
+            };
+
+            // `[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);
+            let loop_expr = fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident));
+
+            // `mut iter => { ... }`
+            let iter_arm = {
+                let iter_pat =
+                    fld.cx.pat_ident_binding_mode(span, iter, ast::BindByValue(ast::MutMutable));
+                fld.cx.arm(span, vec![iter_pat], loop_expr)
+            };
+
+            // `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
+            let into_iter_expr = {
+                let into_iter_path = {
+                    let strs = vec![
+                        fld.cx.ident_of("std"),
+                        fld.cx.ident_of("iter"),
+                        fld.cx.ident_of("IntoIterator"),
+                        fld.cx.ident_of("into_iter"),
+                    ];
+
+                    fld.cx.path_global(span, strs)
+                };
+
+                fld.cx.expr_call(span, fld.cx.expr_path(into_iter_path), vec![head])
+            };
+            fld.cx.expr_match(span, into_iter_expr, vec![iter_arm])
         }
 
         ast::ExprClosure(capture_clause, opt_kind, fn_decl, block) => {