about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorKeegan McAllister <kmcallister@mozilla.com>2014-05-19 13:59:35 -0700
committerKeegan McAllister <kmcallister@mozilla.com>2014-05-28 12:42:21 -0700
commitb2f6dd53c95c866e206798976cfcc2f9a9b2fe2a (patch)
tree18bdae434d9f36b5c05db3858caa31f48f9fc900 /src/libsyntax
parent00704ea33b98dc880d9ac1e4ae7f61b45e8e4330 (diff)
downloadrust-b2f6dd53c95c866e206798976cfcc2f9a9b2fe2a.tar.gz
rust-b2f6dd53c95c866e206798976cfcc2f9a9b2fe2a.zip
Expand macros in patterns
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ext/expand.rs95
1 files changed, 90 insertions, 5 deletions
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 0c885c32b76..03001acc5d0 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use ast::{P, Block, Crate, DeclLocal, ExprMac};
+use ast::{P, Block, Crate, DeclLocal, ExprMac, PatMac};
 use ast::{Local, Ident, MacInvocTT};
 use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
 use ast::TokenTree;
@@ -92,8 +92,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
                                 None => {
                                     fld.cx.span_err(
                                         pth.span,
-                                        format!("non-expr macro in expr pos: \
-                                                 {}",
+                                        format!("non-expression macro in expression position: {}",
                                                 extnamestr.get().as_slice()
                                         ).as_slice());
                                     return DummyResult::raw_expr(e.span);
@@ -487,7 +486,7 @@ pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander)
                 }
                 None => {
                     fld.cx.span_err(pth.span,
-                                    format!("expr macro in item position: {}",
+                                    format!("non-item macro in item position: {}",
                                             extnamestr.get()).as_slice());
                     return SmallVector::zero();
                 }
@@ -639,7 +638,7 @@ pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> {
                 Some(stmt) => stmt,
                 None => {
                     fld.cx.span_err(pth.span,
-                                    format!("non-stmt macro in stmt pos: {}",
+                                    format!("non-statement macro in statement position: {}",
                                             extnamestr).as_slice());
                     return SmallVector::zero();
                 }
@@ -842,6 +841,83 @@ pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
     })
 }
 
+pub fn expand_pat(p: @ast::Pat, fld: &mut MacroExpander) -> @ast::Pat {
+    let (pth, tts) = match p.node {
+        PatMac(ref mac) => {
+            match mac.node {
+                MacInvocTT(ref pth, ref tts, _) => {
+                    (pth, (*tts).clone())
+                }
+            }
+        }
+        _ => return noop_fold_pat(p, fld),
+    };
+    if pth.segments.len() > 1u {
+        fld.cx.span_err(pth.span, "expected macro name without module separators");
+        return DummyResult::raw_pat(p.span);
+    }
+    let extname = pth.segments.get(0).identifier;
+    let extnamestr = token::get_ident(extname);
+    let marked_after = match fld.extsbox.find(&extname.name) {
+        None => {
+            fld.cx.span_err(pth.span,
+                            format!("macro undefined: '{}!'",
+                                    extnamestr).as_slice());
+            // let compilation continue
+            return DummyResult::raw_pat(p.span);
+        }
+
+        Some(&NormalTT(ref expander, span)) => {
+            fld.cx.bt_push(ExpnInfo {
+                call_site: p.span,
+                callee: NameAndSpan {
+                    name: extnamestr.get().to_string(),
+                    format: MacroBang,
+                    span: span
+                }
+            });
+
+            let fm = fresh_mark();
+            let marked_before = mark_tts(tts.as_slice(), fm);
+            let mac_span = original_span(fld.cx);
+            let expanded = match expander.expand(fld.cx,
+                                   mac_span.call_site,
+                                   marked_before.as_slice()).make_pat() {
+                Some(e) => e,
+                None => {
+                    fld.cx.span_err(
+                        pth.span,
+                        format!(
+                            "non-pattern macro in pattern position: {}",
+                            extnamestr.get()
+                        ).as_slice()
+                    );
+                    return DummyResult::raw_pat(p.span);
+                }
+            };
+
+            // mark after:
+            mark_pat(expanded,fm)
+        }
+        _ => {
+            fld.cx.span_err(p.span,
+                            format!("{}! is not legal in pattern position",
+                                    extnamestr.get()).as_slice());
+            return DummyResult::raw_pat(p.span);
+        }
+    };
+
+    let fully_expanded =
+        fld.fold_pat(marked_after).node.clone();
+    fld.cx.bt_pop();
+
+    @ast::Pat {
+        id: ast::DUMMY_NODE_ID,
+        node: fully_expanded,
+        span: p.span,
+    }
+}
+
 pub struct IdentRenamer<'a> {
     renames: &'a mut RenameList,
 }
@@ -885,6 +961,10 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
         expand_expr(expr, self)
     }
 
+    fn fold_pat(&mut self, pat: @ast::Pat) -> @ast::Pat {
+        expand_pat(pat, self)
+    }
+
     fn fold_item(&mut self, item: @ast::Item) -> SmallVector<@ast::Item> {
         expand_item(item, self)
     }
@@ -974,6 +1054,11 @@ fn mark_expr(expr: @ast::Expr, m: Mrk) -> @ast::Expr {
     new_mark_folder(m).fold_expr(expr)
 }
 
+// apply a given mark to the given pattern. Used following the expansion of a macro.
+fn mark_pat(pat: @ast::Pat, m: Mrk) -> @ast::Pat {
+    new_mark_folder(m).fold_pat(pat)
+}
+
 // apply a given mark to the given stmt. Used following the expansion of a macro.
 fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> @ast::Stmt {
     new_mark_folder(m).fold_stmt(expr)