add hygiene support fns, move them around.
also adds test cases
This commit is contained in:
parent
7fd5bdcb9a
commit
fa6c981606
5 changed files with 176 additions and 61 deletions
|
@ -8,9 +8,9 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast::{Block, Crate, NodeId, Expr_, ExprMac, Ident, mac_invoc_tt};
|
||||
use ast::{item_mac, Stmt_, StmtMac, StmtExpr, StmtSemi};
|
||||
use ast::{ILLEGAL_CTXT};
|
||||
use ast::{Block, Crate, NodeId, DeclLocal, Expr_, ExprMac, Local, Ident, mac_invoc_tt};
|
||||
use ast::{item_mac, Mrk, Stmt_, StmtDecl, StmtMac, StmtExpr, StmtSemi};
|
||||
use ast::{ILLEGAL_CTXT, SCTable, token_tree};
|
||||
use ast;
|
||||
use ast_util::{new_rename, new_mark, mtwt_resolve};
|
||||
use attr;
|
||||
|
@ -23,7 +23,7 @@ use opt_vec;
|
|||
use parse;
|
||||
use parse::{parse_item_from_source_str};
|
||||
use parse::token;
|
||||
use parse::token::{ident_to_str, intern};
|
||||
use parse::token::{fresh_name, ident_to_str, intern};
|
||||
use visit;
|
||||
use visit::Visitor;
|
||||
|
||||
|
@ -521,6 +521,71 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
|
|||
|
||||
}
|
||||
|
||||
// expand a non-macro stmt. this is essentially the fallthrough for
|
||||
// expand_stmt, above.
|
||||
fn expand_non_macro_stmt (exts: SyntaxEnv,
|
||||
s: &Stmt_,
|
||||
sp: Span,
|
||||
fld: @ast_fold,
|
||||
orig: @fn(&Stmt_, Span, @ast_fold) -> (Option<Stmt_>, Span))
|
||||
-> (Option<Stmt_>,Span) {
|
||||
// is it a let?
|
||||
match *s {
|
||||
StmtDecl(@Spanned{node: DeclLocal(ref local), span: stmt_span}, node_id) => {
|
||||
let block_info = get_block_info(exts);
|
||||
let pending_renames = block_info.pending_renames;
|
||||
|
||||
// take it apart:
|
||||
let @Local{is_mutbl:is_mutbl,
|
||||
ty:_,
|
||||
pat:pat,
|
||||
init:init,
|
||||
id:id,
|
||||
span:span
|
||||
} = *local;
|
||||
// types can't be copied automatically because of the owned ptr in ty_tup...
|
||||
let ty = local.ty.clone();
|
||||
// expand the pat (it might contain exprs... #:(o)>
|
||||
let expanded_pat = fld.fold_pat(pat);
|
||||
// find the pat_idents in the pattern:
|
||||
// oh dear heaven... this is going to include the enum names, as well....
|
||||
let idents = @mut ~[];
|
||||
let name_finder = new_name_finder(idents);
|
||||
name_finder.visit_pat(expanded_pat,());
|
||||
// generate fresh names, push them to a new pending list
|
||||
let new_pending_renames = @mut ~[];
|
||||
for ident in idents.iter() {
|
||||
let new_name = fresh_name(ident);
|
||||
new_pending_renames.push((*ident,new_name));
|
||||
}
|
||||
let mut rename_fld = renames_to_fold(new_pending_renames);
|
||||
// rewrite the pattern using the new names (the old ones
|
||||
// have already been applied):
|
||||
let rewritten_pat = rename_fld.fold_pat(expanded_pat);
|
||||
// add them to the existing pending renames:
|
||||
for pr in new_pending_renames.iter() {pending_renames.push(*pr)}
|
||||
// also, don't forget to expand the init:
|
||||
let new_init_opt = init.map(|e| fld.fold_expr(*e));
|
||||
let rewritten_local =
|
||||
@Local{is_mutbl:is_mutbl,
|
||||
ty:ty,
|
||||
pat:rewritten_pat,
|
||||
init:new_init_opt,
|
||||
id:id,
|
||||
span:span};
|
||||
(Some(StmtDecl(@Spanned{node:DeclLocal(rewritten_local),
|
||||
span: stmt_span},node_id)),
|
||||
sp)
|
||||
},
|
||||
_ => {
|
||||
orig(s, sp, fld)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a visitor that extracts the pat_ident paths
|
||||
// from a given pattern and puts them in a mutable
|
||||
// array (passed in to the traversal)
|
||||
#[deriving(Clone)]
|
||||
struct NewNameFinderContext {
|
||||
ident_accumulator: @mut ~[ast::Ident],
|
||||
|
@ -674,30 +739,10 @@ pub fn new_name_finder(idents: @mut ~[ast::Ident]) -> @mut Visitor<()> {
|
|||
context as @mut Visitor<()>
|
||||
}
|
||||
|
||||
pub fn expand_block(extsbox: @mut SyntaxEnv,
|
||||
_cx: @ExtCtxt,
|
||||
blk: &Block,
|
||||
fld: @ast_fold,
|
||||
orig: @fn(&Block, @ast_fold) -> Block)
|
||||
-> Block {
|
||||
// see note below about treatment of exts table
|
||||
with_exts_frame!(extsbox,false,orig(blk,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 {
|
||||
// FIXME #4536: currently pub to allow testing
|
||||
pub fn renames_to_fold(renames : @mut ~[(ast::Ident,ast::Name)]) -> @ast_fold {
|
||||
let afp = default_ast_fold();
|
||||
let f_pre = @AstFoldFns {
|
||||
fold_ident: |id,_| {
|
||||
|
@ -713,15 +758,56 @@ fn renames_to_fold(renames : @mut ~[(ast::Ident,ast::Name)]) -> @ast_fold {
|
|||
make_fold(f_pre)
|
||||
}
|
||||
|
||||
// perform a bunch of renames
|
||||
fn apply_pending_renames(folder : @ast_fold, stmt : ast::Stmt) -> @ast::Stmt {
|
||||
match folder.fold_stmt(&stmt) {
|
||||
Some(s) => s,
|
||||
None => fail!(fmt!("renaming of stmt produced None"))
|
||||
}
|
||||
pub fn expand_block(extsbox: @mut SyntaxEnv,
|
||||
_cx: @ExtCtxt,
|
||||
blk: &Block,
|
||||
fld: @ast_fold,
|
||||
orig: @fn(&Block, @ast_fold) -> Block)
|
||||
-> Block {
|
||||
// see note below about treatment of exts table
|
||||
with_exts_frame!(extsbox,false,orig(blk,fld))
|
||||
}
|
||||
|
||||
|
||||
pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: @ast_fold) -> Block {
|
||||
let block_info = get_block_info(exts);
|
||||
let pending_renames = block_info.pending_renames;
|
||||
let mut rename_fld = renames_to_fold(pending_renames);
|
||||
let new_view_items = b.view_items.map(|x| fld.fold_view_item(x));
|
||||
let mut new_stmts = ~[];
|
||||
for x in b.stmts.iter() {
|
||||
match fld.fold_stmt(mustbesome(rename_fld.fold_stmt(*x))) {
|
||||
Some(s) => new_stmts.push(s),
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
let new_expr = b.expr.map(|x| fld.fold_expr(rename_fld.fold_expr(*x)));
|
||||
Block{
|
||||
view_items: new_view_items,
|
||||
stmts: new_stmts,
|
||||
expr: new_expr,
|
||||
id: fld.new_id(b.id),
|
||||
rules: b.rules,
|
||||
span: b.span,
|
||||
}
|
||||
}
|
||||
|
||||
// rename_fold should never return "None".
|
||||
fn mustbesome<T>(val : Option<T>) -> T {
|
||||
match val {
|
||||
Some(v) => v,
|
||||
None => fail!("rename_fold returned None")
|
||||
}
|
||||
}
|
||||
|
||||
// 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"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_span(cx: @ExtCtxt, sp: Span) -> Span {
|
||||
/* this discards information in the case of macro-defining macros */
|
||||
|
@ -1228,12 +1314,15 @@ mod test {
|
|||
use super::*;
|
||||
use ast;
|
||||
use ast::{Attribute_, AttrOuter, MetaWord, EMPTY_CTXT};
|
||||
use ast_util::{get_sctable, new_rename};
|
||||
use codemap;
|
||||
use codemap::Spanned;
|
||||
use parse;
|
||||
use parse::token::{intern, get_ident_interner};
|
||||
use parse::token::{gensym, intern, get_ident_interner};
|
||||
use print::pprust;
|
||||
use util::parser_testing::{string_to_item, string_to_pat, strs_to_idents};
|
||||
use std;
|
||||
use util::parser_testing::{string_to_crate_and_sess, string_to_item, string_to_pat};
|
||||
use util::parser_testing::{strs_to_idents};
|
||||
|
||||
// make sure that fail! is present
|
||||
#[test] fn fail_exists_test () {
|
||||
|
@ -1333,26 +1422,60 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn renaming () {
|
||||
let maybe_item_ast = string_to_item(@"fn a() -> int { let b = 13; b }");
|
||||
let item_ast = match maybe_item_ast {
|
||||
Some(x) => x,
|
||||
None => fail!("test case fail")
|
||||
};
|
||||
let item_ast = string_to_item(@"fn a() -> int { let b = 13; b }").unwrap();
|
||||
let a_name = intern("a");
|
||||
let a2_name = intern("a2");
|
||||
let a2_name = gensym("a2");
|
||||
let renamer = new_ident_renamer(ast::Ident{name:a_name,ctxt:EMPTY_CTXT},
|
||||
a2_name);
|
||||
let renamed_ast = fun_to_ident_folder(renamer).fold_item(item_ast).unwrap();
|
||||
let resolver = new_ident_resolver();
|
||||
let resolved_ast = fun_to_ident_folder(resolver).fold_item(renamed_ast).unwrap();
|
||||
let resolver_fold = fun_to_ident_folder(resolver);
|
||||
let resolved_ast = resolver_fold.fold_item(renamed_ast).unwrap();
|
||||
let resolved_as_str = pprust::item_to_str(resolved_ast,
|
||||
get_ident_interner());
|
||||
assert_eq!(resolved_as_str,~"fn a2() -> int { let b = 13; b }");
|
||||
|
||||
// try a double-rename, with pending_renames.
|
||||
let a3_name = gensym("a3");
|
||||
let ctxt2 = new_rename(ast::Ident::new(a_name),a2_name,EMPTY_CTXT);
|
||||
let pending_renames = @mut ~[(ast::Ident::new(a_name),a2_name),
|
||||
(ast::Ident{name:a_name,ctxt:ctxt2},a3_name)];
|
||||
let double_renamed = renames_to_fold(pending_renames).fold_item(item_ast).unwrap();
|
||||
let resolved_again = resolver_fold.fold_item(double_renamed).unwrap();
|
||||
let double_renamed_as_str = pprust::item_to_str(resolved_again,
|
||||
get_ident_interner());
|
||||
assert_eq!(double_renamed_as_str,~"fn a3() -> int { let b = 13; b }");
|
||||
|
||||
}
|
||||
|
||||
// sigh... it looks like I have two different renaming mechanisms, now...
|
||||
fn fake_print_crate(s: @pprust::ps, crate: &ast::Crate) {
|
||||
pprust::print_mod(s, &crate.module, crate.attrs);
|
||||
}
|
||||
|
||||
// "fn a() -> int { let b = 13; let c = b; b+c }" --> b & c should get new names, in the expr too.
|
||||
// "macro_rules! f (($x:ident) => ($x + b)) fn a() -> int { let b = 13; f!(b)}" --> one should
|
||||
// be renamed, one should not.
|
||||
|
||||
fn expand_and_resolve_and_pretty_print (crate_str : @str) -> ~str {
|
||||
let resolver = new_ident_resolver();
|
||||
let resolver_fold = fun_to_ident_folder(resolver);
|
||||
let (crate_ast,ps) = string_to_crate_and_sess(crate_str);
|
||||
// the cfg argument actually does matter, here...
|
||||
let expanded_ast = expand_crate(ps,~[],crate_ast);
|
||||
// std::io::println(fmt!("expanded: %?\n",expanded_ast));
|
||||
let resolved_ast = resolver_fold.fold_crate(expanded_ast);
|
||||
pprust::to_str(&resolved_ast,fake_print_crate,get_ident_interner())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn automatic_renaming () {
|
||||
let teststrs =
|
||||
~[@"fn a() -> int { let b = 13; let c = b; b+c }",
|
||||
@"macro_rules! f (($x:ident) => ($x + b)) fn a() -> int { let b = 13; f!(b)}"];
|
||||
for s in teststrs.iter() {
|
||||
std::io::println(expand_and_resolve_and_pretty_print(*s));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pat_idents(){
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue