summary refs log tree commit diff
path: root/src/libsyntax/ext
diff options
context:
space:
mode:
authorJohn Clements <clements@racket-lang.org>2013-06-04 14:56:33 -0700
committerJohn Clements <clements@racket-lang.org>2013-06-05 12:01:40 -0700
commit5a158f1d19d93af4223fea2da49209e73a3ed002 (patch)
treef79fda064ff81ff3c3f5b7f7d69b9c1cf8fbad5b /src/libsyntax/ext
parentecdb6e472218ea6f542e4b95e1e3ce48a7e78056 (diff)
downloadrust-5a158f1d19d93af4223fea2da49209e73a3ed002.tar.gz
rust-5a158f1d19d93af4223fea2da49209e73a3ed002.zip
add hygiene support functions
Diffstat (limited to 'src/libsyntax/ext')
-rw-r--r--src/libsyntax/ext/base.rs10
-rw-r--r--src/libsyntax/ext/expand.rs58
2 files changed, 64 insertions, 4 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index cc819a00f7d..a3432a00edc 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -479,6 +479,15 @@ impl <K: Eq + Hash + IterBytes ,V: Copy> MapChain<K,V>{
         }
     }
 
+    fn find_in_topmost_frame(&self, key: &K) -> Option<@V> {
+        let map = match *self {
+            BaseMapChain(ref map) => map,
+            ConsMapChain(ref map,_) => map
+        };
+        // strip one layer of indirection off the pointer.
+        map.find(key).map(|r| {**r})
+    }
+
     // insert the binding into the top-level map
     fn insert (&mut self, key: K, ext: @V) -> bool {
         // can't abstract over get_map because of flow sensitivity...
@@ -512,6 +521,7 @@ impl <K: Eq + Hash + IterBytes ,V: Copy> MapChain<K,V>{
     }
 }
 
+// returns true if the binding for 'n' satisfies 'pred' in 'map'
 fn satisfies_pred<K : Eq + Hash + IterBytes,V>(map : &mut HashMap<K,V>,
                                                n: &K,
                                                pred: &fn(&V)->bool)
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 47c22c438a8..f5edc50377e 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -11,8 +11,8 @@
 use core::prelude::*;
 
 use ast::{blk_, attribute_, attr_outer, meta_word};
-use ast::{crate, expr_, expr_mac, mac_invoc_tt};
-use ast::{item_mac, stmt_, stmt_mac, stmt_expr, stmt_semi};
+use ast::{crate, decl_local, expr_, expr_mac, mac_invoc_tt};
+use ast::{item_mac, local_, stmt_, stmt_decl, stmt_mac, stmt_expr, stmt_semi};
 use ast::{SCTable, illegal_ctxt};
 use ast;
 use ast_util::{new_rename, new_mark, resolve, new_sctable};
@@ -26,6 +26,8 @@ use fold::*;
 use parse;
 use parse::{parse_item_from_source_str};
 use parse::token::{ident_to_str, intern};
+use visit;
+use visit::{Visitor,mk_vt};
 
 use core::vec;
 
@@ -276,13 +278,13 @@ pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
 // insert a macro into the innermost frame that doesn't have the
 // macro_escape tag.
 fn insert_macro(exts: SyntaxEnv, name: ast::Name, transformer: @Transformer) {
-    let block_err_msg = "special identifier ' block' was bound to a non-BlockInfo";
     let is_non_escaping_block =
         |t : &@Transformer| -> bool{
         match t {
             &@BlockInfo(BlockInfo {macros_escape:false,_}) => true,
             &@BlockInfo(BlockInfo {_}) => false,
-            _ => fail!(block_err_msg)
+            _ => fail!(fmt!("special identifier %? was bound to a non-BlockInfo",
+                            special_block_name))
         }
     };
     exts.insert_into_frame(name,transformer,intern(special_block_name),
@@ -365,6 +367,34 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
 
 }
 
+// return a visitor that extracts the pat_ident paths
+// from a given pattern and puts them in a mutable
+// array (passed in to the traversal
+pub fn new_name_finder() -> @Visitor<@mut ~[ast::ident]> {
+    let default_visitor = visit::default_visitor();
+    @Visitor{
+        visit_pat : |p:@ast::pat,ident_accum:@mut ~[ast::ident],v:visit::vt<@mut ~[ast::ident]>| {
+            match *p {
+                // we found a pat_ident!
+                ast::pat{id:_, node: ast::pat_ident(_,path,ref inner), span:_} => {
+                    match path {
+                        // a path of length one:
+                        @ast::Path{global: false,idents: [id], span:_,rp:_,types:_} =>
+                        ident_accum.push(id),
+                        // I believe these must be enums...
+                        _ => ()
+                    }
+                    // visit optional subpattern of pat_ident:
+                    for inner.each |subpat: &@ast::pat| { (v.visit_pat)(*subpat, ident_accum, v) }
+                }
+                // use the default traversal for non-pat_idents
+                _ => visit::visit_pat(p,ident_accum,v)
+            }
+        },
+        .. *default_visitor
+    }
+}
+
 
 
 pub fn expand_block(extsbox: @mut SyntaxEnv,
@@ -378,6 +408,17 @@ pub fn expand_block(extsbox: @mut SyntaxEnv,
     with_exts_frame!(extsbox,false,orig(blk,sp,fld))
 }
 
+
+// get the (innermost) BlockInfo from an exts stack
+fn get_block_info(exts : SyntaxEnv) -> BlockInfo {
+    match exts.find_in_topmost_frame(&intern(special_block_name)) {
+        Some(@BlockInfo(bi)) => bi,
+        _ => fail!(fmt!("special identifier %? was bound to a non-BlockInfo",
+                       @~" block"))
+    }
+}
+
+
 // given a mutable list of renames, return a tree-folder that applies those
 // renames.
 fn renames_to_fold(renames : @mut ~[(ast::ident,ast::Name)]) -> @ast_fold {
@@ -738,6 +779,7 @@ mod test {
     use core::io;
     use core::option::{None, Some};
     use util::parser_testing::{string_to_item, string_to_pat, strs_to_idents};
+    use visit::{mk_vt,Visitor};
 
     // make sure that fail! is present
     #[test] fn fail_exists_test () {
@@ -857,4 +899,12 @@ mod test {
         io::print(fmt!("ast: %?\n",resolved_ast))
     }
 
+    #[test]
+    fn pat_idents(){
+        let pat = string_to_pat(@~"(a,Foo{x:c @ (b,9),y:Bar(4,d)})");
+        let pat_idents = new_name_finder();
+        let idents = @mut ~[];
+        ((*pat_idents).visit_pat)(pat,idents, mk_vt(pat_idents));
+        assert_eq!(idents,@mut strs_to_idents(~["a","c","b","d"]));
+    }
 }