pub span: Span
}
+/// Represents lifetimes and type parameters attached to a declaration
+/// of a function, enum, trait, etc.
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
pub struct Generics {
pub lifetimes: Vec<Lifetime>,
PatWild,
PatWildMulti,
// A PatIdent may either be a new bound variable,
- // or a nullary enum (in which case the second field
+ // or a nullary enum (in which case the third field
// is None).
// In the nullary enum case, the parser can't determine
// which it is. The resolver determines this, and
ExprCast(Gc<Expr>, P<Ty>),
ExprIf(Gc<Expr>, P<Block>, Option<Gc<Expr>>),
ExprWhile(Gc<Expr>, P<Block>),
- // FIXME #6993: change to Option<Name>
+ // FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
ExprForLoop(Gc<Pat>, Gc<Expr>, P<Block>, Option<Ident>),
// Conditionless loop (can be exited with break, cont, or ret)
- // FIXME #6993: change to Option<Name>
+ // FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
ExprLoop(P<Block>, Option<Ident>),
ExprMatch(Gc<Expr>, Vec<Arm>),
ExprFnBlock(P<FnDecl>, P<Block>),
ExprField(Gc<Expr>, SpannedIdent, Vec<P<Ty>>),
ExprIndex(Gc<Expr>, Gc<Expr>),
- /// Expression that looks like a "name". For example,
- /// `std::slice::from_elem::<uint>` is an ExprPath that's the "name" part
- /// of a function call.
+ /// Variable reference, possibly containing `::` and/or
+ /// type parameters, e.g. foo::bar::<baz>
ExprPath(Path),
ExprAddrOf(Mutability, Gc<Expr>),
pub span: Span,
}
+/// Represents a required method in a trait declaration,
+/// one without a default implementation
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
pub struct TypeMethod {
pub ident: Ident,
pub vis: Visibility,
}
+/// Represents a method declaration in a trait declaration, possibly
+/// including a default implementation
// A trait method is either required (meaning it doesn't have an
// implementation, just a signature) or provided (meaning it has a default
// implementation).
}
}
+/// Represents the type of a closure
#[deriving(PartialEq, Eq, Encodable, Decodable, Hash)]
pub struct ClosureTy {
pub lifetimes: Vec<Lifetime>,
pub dialect: AsmDialect
}
+/// represents an argument in a function header
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
pub struct Arg {
pub ty: P<Ty>,
}
}
-// represents the header (not the body) of a function declaration
+/// represents the header (not the body) of a function declaration
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
pub struct FnDecl {
pub inputs: Vec<Arg>,
ItemTy(P<Ty>, Generics),
ItemEnum(EnumDef, Generics),
ItemStruct(Gc<StructDef>, Generics),
+ /// Represents a Trait Declaration
ItemTrait(Generics, Sized, Vec<TraitRef> , Vec<TraitMethod> ),
ItemImpl(Generics,
Option<TraitRef>, // (optional) trait this impl implements
use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
use crateid::CrateId;
use ext::base::*;
+use fold;
use fold::*;
use parse;
use parse::token::{fresh_mark, fresh_name, intern};
fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
}
+ ast::ExprFnBlock(fn_decl, block) => {
+ let (rewritten_fn_decl, rewritten_block)
+ = expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
+ let new_node = ast::ExprFnBlock(rewritten_fn_decl, rewritten_block);
+ box(GC) ast::Expr{id:e.id, node: new_node, span: fld.new_span(e.span)}
+ }
+
+ ast::ExprProc(fn_decl, block) => {
+ let (rewritten_fn_decl, rewritten_block)
+ = expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
+ let new_node = ast::ExprProc(rewritten_fn_decl, rewritten_block);
+ box(GC) ast::Expr{id:e.id, node: new_node, span: fld.new_span(e.span)}
+ }
+
_ => noop_fold_expr(e, fld)
}
}
}
}
-// eval $e with a new exts frame:
+// eval $e with a new exts frame.
+// must be a macro so that $e isn't evaluated too early.
macro_rules! with_exts_frame (
($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
({$extsboxexpr.push_frame();
fn expand_item_modifiers(mut it: Gc<ast::Item>, fld: &mut MacroExpander)
-> Gc<ast::Item> {
- let (modifiers, attrs) = it.attrs.partitioned(|attr| {
+ // partition the attributes into ItemModifiers and others
+ let (modifiers, other_attrs) = it.attrs.partitioned(|attr| {
match fld.extsbox.find(&intern(attr.name().get())) {
Some(&ItemModifier(_)) => true,
_ => false
}
});
-
+ // update the attrs, leave everything else alone. Is this mutation really a good idea?
it = box(GC) ast::Item {
- attrs: attrs,
+ attrs: other_attrs,
..(*it).clone()
};
expand_item_modifiers(it, fld)
}
+/// Expand item_underscore
+fn expand_item_underscore(item: &ast::Item_, fld: &mut MacroExpander) -> ast::Item_ {
+ match *item {
+ ast::ItemFn(decl, fn_style, abi, ref generics, body) => {
+ let (rewritten_fn_decl, rewritten_body)
+ = expand_and_rename_fn_decl_and_block(decl,body,fld);
+ let expanded_generics = fold::fold_generics(generics,fld);
+ ast::ItemFn(rewritten_fn_decl, fn_style, abi, expanded_generics, rewritten_body)
+ }
+ _ => noop_fold_item_underscore(&*item, fld)
+ }
+}
+
// does this attribute list contain "macro_escape" ?
fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
attr::contains_name(attrs, "macro_escape")
} = **local;
// expand the pat (it might contain macro uses):
let expanded_pat = fld.fold_pat(pat);
- // find the pat_idents in the pattern:
+ // find the PatIdents in the pattern:
// oh dear heaven... this is going to include the enum
// names, as well... but that should be okay, as long as
// the new names are gensyms for the old ones.
}
}
+// expand the arm of a 'match', renaming for macro hygiene
fn expand_arm(arm: &ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
// expand pats... they might contain macro uses:
let expanded_pats : Vec<Gc<ast::Pat>> = arm.pats.iter().map(|pat| fld.fold_pat(*pat)).collect();
// all of the pats must have the same set of bindings, so use the
// first one to extract them and generate new names:
let first_pat = expanded_pats.get(0);
- // code duplicated from 'let', above. Perhaps this can be lifted
- // into a separate function:
let idents = pattern_bindings(*first_pat);
- let mut new_pending_renames =
+ let new_renames =
idents.iter().map(|id| (*id,fresh_name(id))).collect();
- // rewrite all of the patterns using the new names (the old
- // ones have already been applied). Note that we depend here
- // on the guarantee that after expansion, there can't be any
- // Path expressions (a.k.a. varrefs) left in the pattern. If
- // this were false, we'd need to apply this renaming only to
- // the bindings, and not to the varrefs, using a more targeted
- // fold-er.
- let mut rename_fld = IdentRenamer{renames:&mut new_pending_renames};
+ // apply the renaming, but only to the PatIdents:
+ let mut rename_pats_fld = PatIdentRenamer{renames:&new_renames};
let rewritten_pats =
- expanded_pats.iter().map(|pat| rename_fld.fold_pat(*pat)).collect();
+ expanded_pats.iter().map(|pat| rename_pats_fld.fold_pat(*pat)).collect();
// apply renaming and then expansion to the guard and the body:
+ let mut rename_fld = IdentRenamer{renames:&new_renames};
let rewritten_guard =
arm.guard.map(|g| fld.fold_expr(rename_fld.fold_expr(g)));
let rewritten_body = fld.fold_expr(rename_fld.fold_expr(arm.body));
}
}
-
-
-// a visitor that extracts the pat_ident (binding) paths
-// from a given thingy and puts them in a mutable
-// array
+/// A visitor that extracts the PatIdent (binding) paths
+/// from a given thingy and puts them in a mutable
+/// array
#[deriving(Clone)]
-struct NameFinderContext {
+struct PatIdentFinder {
ident_accumulator: Vec<ast::Ident> ,
}
-impl Visitor<()> for NameFinderContext {
+impl Visitor<()> for PatIdentFinder {
fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
match *pattern {
- // we found a pat_ident!
- ast::Pat {
- id: _,
- node: ast::PatIdent(_, ref path1, ref inner),
- span: _
- } => {
+ ast::Pat { id: _, node: ast::PatIdent(_, ref path1, ref inner), span: _ } => {
self.ident_accumulator.push(path1.node);
- // visit optional subpattern of pat_ident:
+ // visit optional subpattern of PatIdent:
for subpat in inner.iter() {
self.visit_pat(&**subpat, ())
}
}
- // use the default traversal for non-pat_idents
+ // use the default traversal for non-PatIdents
_ => visit::walk_pat(self, pattern, ())
}
}
}
-// find the pat_ident paths in a pattern
+/// find the PatIdent paths in a pattern
fn pattern_bindings(pat : &ast::Pat) -> Vec<ast::Ident> {
- let mut name_finder = NameFinderContext{ident_accumulator:Vec::new()};
+ let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
name_finder.visit_pat(pat,());
name_finder.ident_accumulator
}
+/// find the PatIdent paths in a
+fn fn_decl_arg_bindings(fn_decl: &ast::FnDecl) -> Vec<ast::Ident> {
+ let mut pat_idents = PatIdentFinder{ident_accumulator:Vec::new()};
+ for arg in fn_decl.inputs.iter() {
+ pat_idents.visit_pat(arg.pat,());
+ }
+ pat_idents.ident_accumulator
+}
+
// expand a block. pushes a new exts_frame, then calls expand_block_elts
fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P<Block> {
// see note below about treatment of exts table
}
}
-// a tree-folder that applies every rename in its (mutable) list
-// to every identifier, including both bindings and varrefs
-// (and lots of things that will turn out to be neither)
+/// A tree-folder that applies every rename in its (mutable) list
+/// to every identifier, including both bindings and varrefs
+/// (and lots of things that will turn out to be neither)
pub struct IdentRenamer<'a> {
- renames: &'a mut RenameList,
+ renames: &'a mtwt::RenameList,
}
impl<'a> Folder for IdentRenamer<'a> {
fn fold_ident(&mut self, id: Ident) -> Ident {
- let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
- mtwt::new_rename(from, to, ctxt)
- });
Ident {
name: id.name,
- ctxt: new_ctxt,
+ ctxt: mtwt::apply_renames(self.renames, id.ctxt),
}
}
}
-fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
- /* this discards information in the case of macro-defining macros */
- Span {
- lo: sp.lo,
- hi: sp.hi,
- expn_info: cx.backtrace(),
+/// A tree-folder that applies every rename in its list to
+/// the idents that are in PatIdent patterns. This is more narrowly
+/// focused than IdentRenamer, and is needed for FnDecl,
+/// where we want to rename the args but not the fn name or the generics etc.
+pub struct PatIdentRenamer<'a> {
+ renames: &'a mtwt::RenameList,
+}
+
+impl<'a> Folder for PatIdentRenamer<'a> {
+ fn fold_pat(&mut self, pat: Gc<ast::Pat>) -> Gc<ast::Pat> {
+ match pat.node {
+ ast::PatIdent(binding_mode, Spanned{span: ref sp, node: id}, ref sub) => {
+ let new_ident = Ident{name: id.name,
+ ctxt: mtwt::apply_renames(self.renames, id.ctxt)};
+ let new_node =
+ ast::PatIdent(binding_mode,
+ Spanned{span: self.new_span(*sp), node: new_ident},
+ sub.map(|p| self.fold_pat(p)));
+ box(GC) ast::Pat {
+ id: pat.id,
+ span: self.new_span(pat.span),
+ node: new_node,
+ }
+ },
+ _ => noop_fold_pat(pat, self)
+ }
}
}
+/// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
+/// PatIdents in its arguments to perform renaming in the FnDecl and
+/// the block, returning both the new FnDecl and the new Block.
+fn expand_and_rename_fn_decl_and_block(fn_decl: &ast::FnDecl, block: Gc<ast::Block>,
+ fld: &mut MacroExpander)
+ -> (Gc<ast::FnDecl>, Gc<ast::Block>) {
+ let expanded_decl = fld.fold_fn_decl(fn_decl);
+ let idents = fn_decl_arg_bindings(expanded_decl);
+ let renames =
+ idents.iter().map(|id : &ast::Ident| (*id,fresh_name(id))).collect();
+ // first, a renamer for the PatIdents, for the fn_decl:
+ let mut rename_pat_fld = PatIdentRenamer{renames: &renames};
+ let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(expanded_decl);
+ // now, a renamer for *all* idents, for the body:
+ let mut rename_fld = IdentRenamer{renames: &renames};
+ let rewritten_body = fld.fold_block(rename_fld.fold_block(block));
+ (rewritten_fn_decl,rewritten_body)
+}
+
+/// A tree-folder that performs macro expansion
pub struct MacroExpander<'a, 'b> {
pub extsbox: SyntaxEnv,
pub cx: &'a mut ExtCtxt<'b>,
expand_item(item, self)
}
+ fn fold_item_underscore(&mut self, item: &ast::Item_) -> ast::Item_ {
+ expand_item_underscore(item, self)
+ }
+
fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<Gc<ast::Stmt>> {
expand_stmt(stmt, self)
}
}
}
+fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
+ /* this discards information in the case of macro-defining macros */
+ Span {
+ lo: sp.lo,
+ hi: sp.hi,
+ expn_info: cx.backtrace(),
+ }
+}
+
pub struct ExpansionConfig {
pub deriving_hash_type_parameter: bool,
pub crate_id: CrateId,
fn fold_ident(&mut self, id: Ident) -> Ident {
ast::Ident {
name: id.name,
- ctxt: mtwt::new_mark(self.mark, id.ctxt)
+ ctxt: mtwt::apply_mark(self.mark, id.ctxt)
}
}
fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
MacInvocTT(ref path, ref tts, ctxt) => {
MacInvocTT(self.fold_path(path),
fold_tts(tts.as_slice(), self),
- mtwt::new_mark(self.mark, ctxt))
+ mtwt::apply_mark(self.mark, ctxt))
}
};
Spanned {
#[cfg(test)]
mod test {
use super::{pattern_bindings, expand_crate, contains_macro_escape};
- use super::{NameFinderContext};
+ use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer};
use ast;
use ast::{Attribute_, AttrOuter, MetaWord};
use attr;
use codemap;
use codemap::Spanned;
use ext::mtwt;
+ use fold::Folder;
use parse;
use parse::token;
use util::parser_testing::{string_to_parser};
path_finder.path_accumulator
}
+ /// A Visitor that extracts the identifiers from a thingy.
+ // as a side note, I'm starting to want to abstract over these....
+ struct IdentFinder{
+ ident_accumulator: Vec<ast::Ident>
+ }
+
+ impl Visitor<()> for IdentFinder {
+ fn visit_ident(&mut self, _: codemap::Span, id: ast::Ident, _: ()){
+ self.ident_accumulator.push(id);
+ }
+ }
+ /// Find the idents in a crate
+ fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> {
+ let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()};
+ visit::walk_crate(&mut ident_finder, the_crate, ());
+ ident_finder.ident_accumulator
+ }
// these following tests are quite fragile, in that they don't test what
// *kind* of failure occurs.
// find the pat_ident paths in a crate
fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> {
- let mut name_finder = NameFinderContext{ident_accumulator:Vec::new()};
+ let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
visit::walk_crate(&mut name_finder, the_crate, ());
name_finder.ident_accumulator
}
-
//fn expand_and_resolve(crate_str: @str) -> ast::crate {
//let expanded_ast = expand_crate_str(crate_str);
// println!("expanded: {:?}\n",expanded_ast);
// but *shouldn't* bind because it was inserted by a different macro....
// can't write this test case until we have macro-generating macros.
- // FIXME #9383 : lambda var hygiene
- // interesting... can't even write this test, yet, because the name-finder
- // only finds pattern vars. Time to upgrade test framework.
- /*#[test]
- fn issue_9383(){
+ // item fn hygiene
+ // expands to fn q(x_1:int){fn g(x_2:int){x_2 + x_1};}
+ #[test] fn issue_9383(){
run_renaming_test(
- &("macro_rules! bad_macro (($ex:expr) => ({(|_x| { $ex }) (9) }))
- fn takes_x(_x : int) { assert_eq!(bad_macro!(_x),8); }
- fn main() { takes_x(8); }",
- vec!(vec!()),false),
+ &("macro_rules! bad_macro (($ex:expr) => (fn g(x:int){ x + $ex }))
+ fn q(x:int) { bad_macro!(x); }",
+ vec!(vec!(1),vec!(0)),true),
0)
- }*/
+ }
+
+ // closure arg hygiene (ExprFnBlock)
+ // expands to fn f(){(|x_1 : int| {(x_2 + x_1)})(3);}
+ #[test] fn closure_arg_hygiene(){
+ run_renaming_test(
+ &("macro_rules! inject_x (()=>(x))
+ fn f(){(|x : int| {(inject_x!() + x)})(3);}",
+ vec!(vec!(1)),
+ true),
+ 0)
+ }
+
+ // closure arg hygiene (ExprProc)
+ // expands to fn f(){(proc(x_1 : int) {(x_2 + x_1)})(3);}
+ #[test] fn closure_arg_hygiene_2(){
+ run_renaming_test(
+ &("macro_rules! inject_x (()=>(x))
+ fn f(){ (proc(x : int){(inject_x!() + x)})(3); }",
+ vec!(vec!(1)),
+ true),
+ 0)
+ }
// run one of the renaming tests
fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
assert_eq!(varref_marks,binding_marks.clone());
}
} else {
+ let varref_name = mtwt::resolve(varref.segments.get(0).identifier);
let fail = (varref.segments.len() == 1)
- && (mtwt::resolve(varref.segments.get(0).identifier)
- == binding_name);
+ && (varref_name == binding_name);
// temp debugging:
if fail {
let varref_idents : Vec<ast::Ident>
println!("text of test case: \"{}\"", teststr);
println!("");
println!("uh oh, matches but shouldn't:");
- println!("varref: {}",varref_idents);
+ println!("varref #{}: {}, resolves to {}",idx, varref_idents,
+ varref_name);
// good lord, you can't make a path with 0 segments, can you?
let string = token::get_ident(varref.segments
.get(0)
println!("varref's first segment's uint: {}, and string: \"{}\"",
varref.segments.get(0).identifier.name,
string.get());
- println!("binding: {}", *bindings.get(binding_idx));
+ println!("binding #{}: {}, resolves to {}",
+ binding_idx, *bindings.get(binding_idx),
+ binding_name);
mtwt::with_sctable(|x| mtwt::display_sctable(x));
}
assert!(!fail);
// 'None' is listed as an identifier pattern because we don't yet know that
// it's the name of a 0-ary variant, and that 'i' appears twice in succession.
#[test]
- fn crate_idents(){
+ fn crate_bindings_test(){
let the_crate = string_to_crate("fn main (a : int) -> int {|b| {
match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
let idents = crate_bindings(&the_crate);
assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
}
- //
+ // test the IdentRenamer directly
+ #[test]
+ fn ident_renamer_test () {
+ let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string());
+ let f_ident = token::str_to_ident("f");
+ let x_ident = token::str_to_ident("x");
+ let int_ident = token::str_to_ident("int");
+ let renames = vec!((x_ident,16));
+ let mut renamer = IdentRenamer{renames: &renames};
+ let renamed_crate = renamer.fold_crate(the_crate);
+ let idents = crate_idents(&renamed_crate);
+ let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
+ assert_eq!(resolved,vec!(f_ident.name,16,int_ident.name,16,16,16));
+ }
+
+ // test the PatIdentRenamer; only PatIdents get renamed
+ #[test]
+ fn pat_ident_renamer_test () {
+ let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string());
+ let f_ident = token::str_to_ident("f");
+ let x_ident = token::str_to_ident("x");
+ let int_ident = token::str_to_ident("int");
+ let renames = vec!((x_ident,16));
+ let mut renamer = PatIdentRenamer{renames: &renames};
+ let renamed_crate = renamer.fold_crate(the_crate);
+ let idents = crate_idents(&renamed_crate);
+ let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
+ let x_name = x_ident.name;
+ assert_eq!(resolved,vec!(f_ident.name,16,int_ident.name,16,x_name,x_name));
+ }
+
}
IllegalCtxt
}
+/// A list of ident->name renamings
+pub type RenameList = Vec<(Ident, Name)>;
+
/// Extend a syntax context with a given mark
-pub fn new_mark(m: Mrk, tail: SyntaxContext) -> SyntaxContext {
- with_sctable(|table| new_mark_internal(m, tail, table))
+pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext {
+ with_sctable(|table| apply_mark_internal(m, ctxt, table))
}
-// Extend a syntax context with a given mark and table
-fn new_mark_internal(m: Mrk, tail: SyntaxContext, table: &SCTable) -> SyntaxContext {
- let key = (tail, m);
+// Extend a syntax context with a given mark and sctable (explicit memoization)
+fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext {
+ let key = (ctxt, m);
let new_ctxt = |_: &(SyntaxContext, Mrk)|
- idx_push(&mut *table.table.borrow_mut(), Mark(m, tail));
+ idx_push(&mut *table.table.borrow_mut(), Mark(m, ctxt));
*table.mark_memo.borrow_mut().find_or_insert_with(key, new_ctxt)
}
/// Extend a syntax context with a given rename
-pub fn new_rename(id: Ident, to:Name,
- tail: SyntaxContext) -> SyntaxContext {
- with_sctable(|table| new_rename_internal(id, to, tail, table))
+pub fn apply_rename(id: Ident, to:Name,
+ ctxt: SyntaxContext) -> SyntaxContext {
+ with_sctable(|table| apply_rename_internal(id, to, ctxt, table))
}
-// Extend a syntax context with a given rename and sctable
-fn new_rename_internal(id: Ident,
+// Extend a syntax context with a given rename and sctable (explicit memoization)
+fn apply_rename_internal(id: Ident,
to: Name,
- tail: SyntaxContext,
+ ctxt: SyntaxContext,
table: &SCTable) -> SyntaxContext {
- let key = (tail,id,to);
+ let key = (ctxt,id,to);
let new_ctxt = |_: &(SyntaxContext, Ident, Mrk)|
- idx_push(&mut *table.table.borrow_mut(), Rename(id, to, tail));
+ idx_push(&mut *table.table.borrow_mut(), Rename(id, to, ctxt));
*table.rename_memo.borrow_mut().find_or_insert_with(key, new_ctxt)
}
+/// Apply a list of renamings to a context
+// if these rename lists get long, it would make sense
+// to consider memoizing this fold. This may come up
+// when we add hygiene to item names.
+pub fn apply_renames(renames: &RenameList, ctxt: SyntaxContext) -> SyntaxContext {
+ renames.iter().fold(ctxt, |ctxt, &(from, to)| {
+ apply_rename(from, to, ctxt)
+ })
+}
+
/// Fetch the SCTable from TLS, create one if it doesn't yet exist.
pub fn with_sctable<T>(op: |&SCTable| -> T) -> T {
local_data_key!(sctable_key: Rc<SCTable>)
#[cfg(test)]
mod tests {
- use ast::*;
- use super::{resolve, xor_push, new_mark_internal, new_sctable_internal};
- use super::{new_rename_internal, marksof_internal, resolve_internal};
+ use ast::{EMPTY_CTXT, Ident, Mrk, Name, SyntaxContext};
+ use super::{resolve, xor_push, apply_mark_internal, new_sctable_internal};
+ use super::{apply_rename_internal, apply_renames, marksof_internal, resolve_internal};
use super::{SCTable, EmptyCtxt, Mark, Rename, IllegalCtxt};
use std::collections::HashMap;
-> SyntaxContext {
tscs.iter().rev().fold(tail, |tail : SyntaxContext, tsc : &TestSC|
{match *tsc {
- M(mrk) => new_mark_internal(mrk,tail,table),
- R(ident,name) => new_rename_internal(ident,name,tail,table)}})
+ M(mrk) => apply_mark_internal(mrk,tail,table),
+ R(ident,name) => apply_rename_internal(ident,name,tail,table)}})
}
// gather a SyntaxContext back into a vector of TestSCs
fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
-> SyntaxContext {
mrks.iter().rev().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
- {new_mark_internal(*mrk,tail,table)})
+ {apply_mark_internal(*mrk,tail,table)})
}
#[test] fn unfold_marks_test() {
// rename where stop doesn't match:
{ let chain = vec!(M(9),
R(id(name1,
- new_mark_internal (4, EMPTY_CTXT,&mut t)),
+ apply_mark_internal (4, EMPTY_CTXT,&mut t)),
100101102),
M(14));
let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
assert_eq! (marksof_internal (ans, stopname, &t), vec!(9,14));}
// rename where stop does match
- { let name1sc = new_mark_internal(4, EMPTY_CTXT, &mut t);
+ { let name1sc = apply_mark_internal(4, EMPTY_CTXT, &mut t);
let chain = vec!(M(9),
R(id(name1, name1sc),
stopname),
{ let sc = unfold_test_sc(vec!(R(id(50,EMPTY_CTXT),51),M(12)),EMPTY_CTXT,&mut t);
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);}
// - rename where names do match, but marks don't
- { let sc1 = new_mark_internal(1,EMPTY_CTXT,&mut t);
+ { let sc1 = apply_mark_internal(1,EMPTY_CTXT,&mut t);
let sc = unfold_test_sc(vec!(R(id(a,sc1),50),
M(1),
M(2)),
EMPTY_CTXT,&mut t);
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 51); }
// the simplest double-rename:
- { let a_to_a50 = new_rename_internal(id(a,EMPTY_CTXT),50,EMPTY_CTXT,&mut t);
- let a50_to_a51 = new_rename_internal(id(a,a_to_a50),51,a_to_a50,&mut t);
+ { let a_to_a50 = apply_rename_internal(id(a,EMPTY_CTXT),50,EMPTY_CTXT,&mut t);
+ let a50_to_a51 = apply_rename_internal(id(a,a_to_a50),51,a_to_a50,&mut t);
assert_eq!(resolve_internal(id(a,a50_to_a51),&mut t, &mut rt),51);
// mark on the outside doesn't stop rename:
- let sc = new_mark_internal(9,a50_to_a51,&mut t);
+ let sc = apply_mark_internal(9,a50_to_a51,&mut t);
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),51);
// but mark on the inside does:
let a50_to_a51_b = unfold_test_sc(vec!(R(id(a,a_to_a50),51),
#[test]
fn hashing_tests () {
let mut t = new_sctable_internal();
- assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
- assert_eq!(new_mark_internal(13,EMPTY_CTXT,&mut t),3);
+ assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),2);
+ assert_eq!(apply_mark_internal(13,EMPTY_CTXT,&mut t),3);
// using the same one again should result in the same index:
- assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
+ assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),2);
// I'm assuming that the rename table will behave the same....
}
resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
assert_eq!(rt.len(),2);
}
+
+ #[test]
+ fn new_resolves_test() {
+ let renames = vec!((Ident{name:23,ctxt:EMPTY_CTXT},24),
+ (Ident{name:29,ctxt:EMPTY_CTXT},29));
+ let new_ctxt1 = apply_renames(&renames,EMPTY_CTXT);
+ assert_eq!(resolve(Ident{name:23,ctxt:new_ctxt1}),24);
+ assert_eq!(resolve(Ident{name:29,ctxt:new_ctxt1}),29);
+ }
}