use syntax::ast::*;
use syntax::ast;
-use syntax::ast_util::{def_id_of_def, local_def, mtwt_resolve};
+use syntax::ast_util::{def_id_of_def, local_def};
use syntax::ast_util::{path_to_ident, walk_pat, trait_method_to_ty_method};
+use syntax::ext::mtwt;
use syntax::parse::token::special_idents;
use syntax::parse::token;
use syntax::print::pprust::path_to_str;
fn binding_mode_map(&mut self, pat: @Pat) -> BindingMap {
let mut result = HashMap::new();
pat_bindings(self.def_map, pat, |binding_mode, _id, sp, path| {
- let name = mtwt_resolve(path_to_ident(path));
+ let name = mtwt::resolve(path_to_ident(path));
result.insert(name,
binding_info {span: sp,
binding_mode: binding_mode});
// what you want).
let ident = path.segments.get(0).identifier;
- let renamed = mtwt_resolve(ident);
+ let renamed = mtwt::resolve(ident);
match self.resolve_bare_identifier_pattern(ident) {
FoundStructOrEnumVariant(def, lp)
let search_result;
match namespace {
ValueNS => {
- let renamed = mtwt_resolve(ident);
+ let renamed = mtwt::resolve(ident);
let mut value_ribs = self.value_ribs.borrow_mut();
search_result = self.search_ribs(value_ribs.get(),
renamed,
let rib = label_ribs.get()[label_ribs.get().len() -
1];
let mut bindings = rib.bindings.borrow_mut();
- let renamed = mtwt_resolve(label);
+ let renamed = mtwt::resolve(label);
bindings.get().insert(renamed, def_like);
}
ExprBreak(Some(label)) | ExprAgain(Some(label)) => {
let mut label_ribs = self.label_ribs.borrow_mut();
- let renamed = mtwt_resolve(label);
+ let renamed = mtwt::resolve(label);
match self.search_ribs(label_ribs.get(), renamed, expr.span) {
None =>
self.resolve_error(expr.span,
use std::fmt;
use std::fmt::Show;
-use std::cell::RefCell;
-use collections::HashMap;
use std::option::Option;
use std::rc::Rc;
use std::vec_ng::Vec;
// macro expansion per Flatt et al., "Macros
// That Work Together"
#[deriving(Clone, Hash, TotalEq, TotalOrd, Show)]
-pub struct Ident { name: Name, ctxt: SyntaxContext }
+pub struct Ident {
+ name: Name,
+ ctxt: SyntaxContext
+}
impl Ident {
/// Construct an identifier with the given name and an empty context:
// this uint is a reference to a table stored in thread-local
// storage.
pub type SyntaxContext = u32;
-
-// the SCTable contains a table of SyntaxContext_'s. It
-// represents a flattened tree structure, to avoid having
-// managed pointers everywhere (that caused an ICE).
-// the mark_memo and rename_memo fields are side-tables
-// that ensure that adding the same mark to the same context
-// gives you back the same context as before. This shouldn't
-// change the semantics--everything here is immutable--but
-// it should cut down on memory use *a lot*; applying a mark
-// to a tree containing 50 identifiers would otherwise generate
-pub struct SCTable {
- table: RefCell<Vec<SyntaxContext_> >,
- mark_memo: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
- rename_memo: RefCell<HashMap<(SyntaxContext,Ident,Name),SyntaxContext>>,
-}
-
-// NB: these must be placed in any SCTable...
pub static EMPTY_CTXT : SyntaxContext = 0;
pub static ILLEGAL_CTXT : SyntaxContext = 1;
-#[deriving(Eq, Encodable, Decodable, Hash)]
-pub enum SyntaxContext_ {
- EmptyCtxt,
- Mark (Mrk,SyntaxContext),
- // flattening the name and syntaxcontext into the rename...
- // HIDDEN INVARIANTS:
- // 1) the first name in a Rename node
- // can only be a programmer-supplied name.
- // 2) Every Rename node with a given Name in the
- // "to" slot must have the same name and context
- // in the "from" slot. In essence, they're all
- // pointers to a single "rename" event node.
- Rename (Ident,Name,SyntaxContext),
- // actually, IllegalCtxt may not be necessary.
- IllegalCtxt
-}
-
/// A name is a part of an identifier, representing a string or gensym. It's
/// the result of interning.
pub type Name = u32;
use visit::Visitor;
use visit;
-use std::cell::{Cell, RefCell};
+use std::cell::Cell;
use std::cmp;
-use collections::HashMap;
use std::u32;
-use std::local_data;
use std::vec_ng::Vec;
pub fn path_name_i(idents: &[Ident]) -> ~str {
}
}
-// HYGIENE FUNCTIONS
-
-/// Extend a syntax context with a given mark
-pub fn new_mark(m:Mrk, tail:SyntaxContext) -> SyntaxContext {
- new_mark_internal(m,tail,get_sctable())
-}
-
-// Extend a syntax context with a given mark and table
-// FIXME #8215 : currently pub to allow testing
-pub fn new_mark_internal(m: Mrk, tail: SyntaxContext, table: &SCTable)
- -> SyntaxContext {
- let key = (tail,m);
- // FIXME #5074 : can't use more natural style because we're missing
- // flow-sensitivity. Results in two lookups on a hash table hit.
- // also applies to new_rename_internal, below.
- // let try_lookup = table.mark_memo.find(&key);
- let mut mark_memo = table.mark_memo.borrow_mut();
- match mark_memo.get().contains_key(&key) {
- false => {
- let new_idx = {
- let mut table = table.table.borrow_mut();
- idx_push(table.get(), Mark(m,tail))
- };
- mark_memo.get().insert(key,new_idx);
- new_idx
- }
- true => {
- match mark_memo.get().find(&key) {
- None => fail!("internal error: key disappeared 2013042901"),
- Some(idxptr) => {*idxptr}
- }
- }
- }
-}
-
-/// Extend a syntax context with a given rename
-pub fn new_rename(id:Ident, to:Name, tail:SyntaxContext) -> SyntaxContext {
- new_rename_internal(id, to, tail, get_sctable())
-}
-
-// Extend a syntax context with a given rename and sctable
-// FIXME #8215 : currently pub to allow testing
-pub fn new_rename_internal(id: Ident,
- to: Name,
- tail: SyntaxContext,
- table: &SCTable)
- -> SyntaxContext {
- let key = (tail,id,to);
- // FIXME #5074
- //let try_lookup = table.rename_memo.find(&key);
- let mut rename_memo = table.rename_memo.borrow_mut();
- match rename_memo.get().contains_key(&key) {
- false => {
- let new_idx = {
- let mut table = table.table.borrow_mut();
- idx_push(table.get(), Rename(id,to,tail))
- };
- rename_memo.get().insert(key,new_idx);
- new_idx
- }
- true => {
- match rename_memo.get().find(&key) {
- None => fail!("internal error: key disappeared 2013042902"),
- Some(idxptr) => {*idxptr}
- }
- }
- }
-}
-
-/// Make a fresh syntax context table with EmptyCtxt in slot zero
-/// and IllegalCtxt in slot one.
-// FIXME #8215 : currently pub to allow testing
-pub fn new_sctable_internal() -> SCTable {
- SCTable {
- table: RefCell::new(vec!(EmptyCtxt,IllegalCtxt)),
- mark_memo: RefCell::new(HashMap::new()),
- rename_memo: RefCell::new(HashMap::new()),
- }
-}
-
-// fetch the SCTable from TLS, create one if it doesn't yet exist.
-pub fn get_sctable() -> @SCTable {
- local_data_key!(sctable_key: @@SCTable)
- match local_data::get(sctable_key, |k| k.map(|k| *k)) {
- None => {
- let new_table = @@new_sctable_internal();
- local_data::set(sctable_key,new_table);
- *new_table
- },
- Some(intr) => *intr
- }
-}
-
-/// print out an SCTable for debugging
-pub fn display_sctable(table : &SCTable) {
- error!("SC table:");
- let table = table.table.borrow();
- for (idx,val) in table.get().iter().enumerate() {
- error!("{:4u} : {:?}",idx,val);
- }
-}
-
-
-/// Add a value to the end of a vec, return its index
-fn idx_push<T>(vec: &mut Vec<T> , val: T) -> u32 {
- vec.push(val);
- (vec.len() - 1) as u32
-}
-
-/// Resolve a syntax object to a name, per MTWT.
-pub fn mtwt_resolve(id : Ident) -> Name {
- let resolve_table = get_resolve_table();
- let mut resolve_table = resolve_table.borrow_mut();
- resolve_internal(id, get_sctable(), resolve_table.get())
-}
-
-// FIXME #8215: must be pub for testing
-pub type ResolveTable = HashMap<(Name,SyntaxContext),Name>;
-
-// okay, I admit, putting this in TLS is not so nice:
-// fetch the SCTable from TLS, create one if it doesn't yet exist.
-pub fn get_resolve_table() -> @RefCell<ResolveTable> {
- local_data_key!(resolve_table_key: @@RefCell<ResolveTable>)
- match local_data::get(resolve_table_key, |k| k.map(|k| *k)) {
- None => {
- let new_table = @@RefCell::new(HashMap::new());
- local_data::set(resolve_table_key, new_table);
- *new_table
- },
- Some(intr) => *intr
- }
-}
-
-// Resolve a syntax object to a name, per MTWT.
-// adding memoization to possibly resolve 500+ seconds in resolve for librustc (!)
-// FIXME #8215 : currently pub to allow testing
-pub fn resolve_internal(id : Ident,
- table : &SCTable,
- resolve_table : &mut ResolveTable) -> Name {
- let key = (id.name,id.ctxt);
- match resolve_table.contains_key(&key) {
- false => {
- let resolved = {
- let result = {
- let table = table.table.borrow();
- *table.get().get(id.ctxt as uint)
- };
- match result {
- EmptyCtxt => id.name,
- // ignore marks here:
- Mark(_,subctxt) =>
- resolve_internal(Ident{name:id.name, ctxt: subctxt},table,resolve_table),
- // do the rename if necessary:
- Rename(Ident{name,ctxt},toname,subctxt) => {
- let resolvedfrom =
- resolve_internal(Ident{name:name,ctxt:ctxt},table,resolve_table);
- let resolvedthis =
- resolve_internal(Ident{name:id.name,ctxt:subctxt},table,resolve_table);
- if (resolvedthis == resolvedfrom)
- && (marksof(ctxt,resolvedthis,table)
- == marksof(subctxt,resolvedthis,table)) {
- toname
- } else {
- resolvedthis
- }
- }
- IllegalCtxt() => fail!("expected resolvable context, got IllegalCtxt")
- }
- };
- resolve_table.insert(key,resolved);
- resolved
- }
- true => {
- // it's guaranteed to be there, because we just checked that it was
- // there and we never remove anything from the table:
- *(resolve_table.find(&key).unwrap())
- }
- }
-}
-
-/// Compute the marks associated with a syntax context.
-pub fn mtwt_marksof(ctxt: SyntaxContext, stopname: Name) -> Vec<Mrk> {
- marksof(ctxt, stopname, get_sctable())
-}
-
-// the internal function for computing marks
-// it's not clear to me whether it's better to use a [] mutable
-// vector or a cons-list for this.
-pub fn marksof(ctxt: SyntaxContext, stopname: Name, table: &SCTable) -> Vec<Mrk> {
- let mut result = Vec::new();
- let mut loopvar = ctxt;
- loop {
- let table_entry = {
- let table = table.table.borrow();
- *table.get().get(loopvar as uint)
- };
- match table_entry {
- EmptyCtxt => {
- return result;
- },
- Mark(mark, tl) => {
- xorPush(&mut result, mark);
- loopvar = tl;
- },
- Rename(_,name,tl) => {
- // see MTWT for details on the purpose of the stopname.
- // short version: it prevents duplication of effort.
- if name == stopname {
- return result;
- } else {
- loopvar = tl;
- }
- }
- IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
- }
- }
-}
-
-/// Return the outer mark for a context with a mark at the outside.
-/// FAILS when outside is not a mark.
-pub fn mtwt_outer_mark(ctxt: SyntaxContext) -> Mrk {
- let sctable = get_sctable();
- let table = sctable.table.borrow();
- match *table.get().get(ctxt as uint) {
- ast::Mark(mrk,_) => mrk,
- _ => fail!("can't retrieve outer mark when outside is not a mark")
- }
-}
-
-/// Push a name... unless it matches the one on top, in which
-/// case pop and discard (so two of the same marks cancel)
-pub fn xorPush(marks: &mut Vec<Mrk> , mark: Mrk) {
- if (marks.len() > 0) && (getLast(marks) == mark) {
- marks.pop().unwrap();
- } else {
- marks.push(mark);
- }
-}
-
-// get the last element of a mutable array.
-// FIXME #4903: , must be a separate procedure for now.
-pub fn getLast(arr: &Vec<Mrk> ) -> Mrk {
- *arr.last().unwrap()
-}
-
// are two paths equal when compared unhygienically?
// since I'm using this to replace ==, it seems appropriate
// to compare the span, global, etc. fields as well.
use ast::*;
use super::*;
use opt_vec;
- use collections::HashMap;
-
- use std::vec_ng::Vec;
fn ident_to_segment(id : &Ident) -> PathSegment {
PathSegment {identifier:id.clone(),
[Ident{name:3,ctxt:104},
Ident{name:77,ctxt:182}].map(ident_to_segment)));
}
-
- #[test] fn xorpush_test () {
- let mut s = Vec::new();
- xorPush(&mut s, 14);
- assert_eq!(s.clone(), vec!(14));
- xorPush(&mut s, 14);
- assert_eq!(s.clone(), Vec::new());
- xorPush(&mut s, 14);
- assert_eq!(s.clone(), vec!(14));
- xorPush(&mut s, 15);
- assert_eq!(s.clone(), vec!(14, 15));
- xorPush(&mut s, 16);
- assert_eq!(s.clone(), vec!(14, 15, 16));
- xorPush(&mut s, 16);
- assert_eq!(s.clone(), vec!(14, 15));
- xorPush(&mut s, 15);
- assert_eq!(s.clone(), vec!(14));
- }
-
- fn id(n: Name, s: SyntaxContext) -> Ident {
- Ident {name: n, ctxt: s}
- }
-
- // because of the SCTable, I now need a tidy way of
- // creating syntax objects. Sigh.
- #[deriving(Clone, Eq, Show)]
- enum TestSC {
- M(Mrk),
- R(Ident,Name)
- }
-
- // unfold a vector of TestSC values into a SCTable,
- // returning the resulting index
- fn unfold_test_sc(tscs : Vec<TestSC> , tail: SyntaxContext, table: &SCTable)
- -> SyntaxContext {
- tscs.rev_iter().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)}})
- }
-
- // gather a SyntaxContext back into a vector of TestSCs
- fn refold_test_sc(mut sc: SyntaxContext, table : &SCTable) -> Vec<TestSC> {
- let mut result = Vec::new();
- loop {
- let table = table.table.borrow();
- match *table.get().get(sc as uint) {
- EmptyCtxt => {return result;},
- Mark(mrk,tail) => {
- result.push(M(mrk));
- sc = tail;
- continue;
- },
- Rename(id,name,tail) => {
- result.push(R(id,name));
- sc = tail;
- continue;
- }
- IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
- }
- }
- }
-
- #[test] fn test_unfold_refold(){
- let mut t = new_sctable_internal();
-
- let test_sc = vec!(M(3),R(id(101,0),14),M(9));
- assert_eq!(unfold_test_sc(test_sc.clone(),EMPTY_CTXT,&mut t),4);
- {
- let table = t.table.borrow();
- assert!(*table.get().get(2) == Mark(9,0));
- assert!(*table.get().get(3) == Rename(id(101,0),14,2));
- assert!(*table.get().get(4) == Mark(3,3));
- }
- assert_eq!(refold_test_sc(4,&t),test_sc);
- }
-
- // extend a syntax context with a sequence of marks given
- // in a vector. v[0] will be the outermost mark.
- fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
- -> SyntaxContext {
- mrks.rev_iter().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
- {new_mark_internal(*mrk,tail,table)})
- }
-
- #[test] fn unfold_marks_test() {
- let mut t = new_sctable_internal();
-
- assert_eq!(unfold_marks(vec!(3,7),EMPTY_CTXT,&mut t),3);
- {
- let table = t.table.borrow();
- assert!(*table.get().get(2) == Mark(7,0));
- assert!(*table.get().get(3) == Mark(3,2));
- }
- }
-
- #[test] fn test_marksof () {
- let stopname = 242;
- let name1 = 243;
- let mut t = new_sctable_internal();
- assert_eq!(marksof (EMPTY_CTXT,stopname,&t),Vec::new());
- // FIXME #5074: ANF'd to dodge nested calls
- { let ans = unfold_marks(vec!(4,98),EMPTY_CTXT,&mut t);
- assert_eq! (marksof (ans,stopname,&t),vec!(4,98));}
- // does xoring work?
- { let ans = unfold_marks(vec!(5,5,16),EMPTY_CTXT,&mut t);
- assert_eq! (marksof (ans,stopname,&t), vec!(16));}
- // does nested xoring work?
- { let ans = unfold_marks(vec!(5,10,10,5,16),EMPTY_CTXT,&mut t);
- assert_eq! (marksof (ans, stopname,&t), vec!(16));}
- // rename where stop doesn't match:
- { let chain = vec!(M(9),
- R(id(name1,
- new_mark_internal (4, EMPTY_CTXT,&mut t)),
- 100101102),
- M(14));
- let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
- assert_eq! (marksof (ans, stopname, &t), vec!(9,14));}
- // rename where stop does match
- { let name1sc = new_mark_internal(4, EMPTY_CTXT, &mut t);
- let chain = vec!(M(9),
- R(id(name1, name1sc),
- stopname),
- M(14));
- let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
- assert_eq! (marksof (ans, stopname, &t), vec!(9)); }
- }
-
-
- #[test] fn resolve_tests () {
- let a = 40;
- let mut t = new_sctable_internal();
- let mut rt = HashMap::new();
- // - ctxt is MT
- assert_eq!(resolve_internal(id(a,EMPTY_CTXT),&mut t, &mut rt),a);
- // - simple ignored marks
- { let sc = unfold_marks(vec!(1,2,3),EMPTY_CTXT,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);}
- // - orthogonal rename where names don't match
- { 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 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), a);}
- // - rename where names and marks match
- { let sc1 = unfold_test_sc(vec!(M(1),M(2)),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), 50); }
- // - rename where names and marks match by literal sharing
- { let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
- let sc = unfold_test_sc(vec!(R(id(a,sc1),50)),sc1,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 50); }
- // - two renames of the same var.. can only happen if you use
- // local-expand to prevent the inner binding from being renamed
- // during the rename-pass caused by the first:
- println!("about to run bad test");
- { let sc = unfold_test_sc(vec!(R(id(a,EMPTY_CTXT),50),
- R(id(a,EMPTY_CTXT),51)),
- 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);
- 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);
- 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),
- M(9)),
- a_to_a50,
- &mut t);
- assert_eq!(resolve_internal(id(a,a50_to_a51_b),&mut t, &mut rt),50);}
- }
-
- #[test] fn mtwt_resolve_test(){
- let a = 40;
- assert_eq!(mtwt_resolve(id(a,EMPTY_CTXT)),a);
- }
-
-
- #[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);
- // using the same one again should result in the same index:
- assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
- // I'm assuming that the rename table will behave the same....
- }
-
- #[test] fn resolve_table_hashing_tests() {
- let mut t = new_sctable_internal();
- let mut rt = HashMap::new();
- assert_eq!(rt.len(),0);
- resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
- assert_eq!(rt.len(),1);
- resolve_internal(id(39,EMPTY_CTXT),&mut t, &mut rt);
- assert_eq!(rt.len(),2);
- resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
- assert_eq!(rt.len(),2);
- }
-
}
pub struct BlockInfo {
// should macros escape from this scope?
- macros_escape : bool,
+ macros_escape: bool,
// what are the pending renames?
- pending_renames : RenameList,
+ pending_renames: RenameList,
}
impl BlockInfo {
}
// a list of ident->name renamings
-pub type RenameList = Vec<(ast::Ident,Name)> ;
+pub type RenameList = Vec<(ast::Ident, Name)>;
// The base map of methods for expanding syntax extension
// AST nodes into full ASTs
use ast::{P, Block, Crate, DeclLocal, ExprMac};
use ast::{Local, Ident, MacInvocTT};
use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
-use ast::{TokenTree};
+use ast::TokenTree;
use ast;
-use ast_util::{new_rename, new_mark};
+use ext::mtwt;
use ext::build::AstBuilder;
use attr;
use attr::AttrMetaMethods;
// Expand any interior macros etc.
// NB: we don't fold pats yet. Curious.
let src_expr = fld.fold_expr(src_expr).clone();
- // Rename label before expansion.
- let (opt_ident, src_loop_block) = rename_loop_label(opt_ident, src_loop_block, fld);
- let src_loop_block = fld.fold_block(src_loop_block);
+ let (src_loop_block, opt_ident) = expand_loop_block(src_loop_block, opt_ident, fld);
let span = e.span;
}
ast::ExprLoop(loop_block, opt_ident) => {
- let (opt_ident, loop_block) =
- rename_loop_label(opt_ident, loop_block, fld);
- let loop_block = fld.fold_block(loop_block);
+ let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
}
}
}
-// Rename loop label and its all occurrences inside the loop body
-fn rename_loop_label(opt_ident: Option<Ident>,
- loop_block: P<Block>,
- fld: &mut MacroExpander) -> (Option<Ident>, P<Block>) {
+// Rename loop label and expand its loop body
+//
+// The renaming procedure for loop is different in the sense that the loop
+// body is in a block enclosed by loop head so the renaming of loop label
+// must be propagated to the enclosed context.
+fn expand_loop_block(loop_block: P<Block>,
+ opt_ident: Option<Ident>,
+ fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
match opt_ident {
Some(label) => {
- // Generate fresh label and add to the existing pending renames
let new_label = fresh_name(&label);
let rename = (label, new_label);
+
+ // The rename *must not* be added to the pending list of current
+ // syntax context otherwise an unrelated `break` or `continue` in
+ // the same context will pick that up in the deferred renaming pass
+ // and be renamed incorrectly.
+ let mut rename_list = vec!(rename);
+ let mut rename_fld = renames_to_fold(&mut rename_list);
+ let renamed_ident = rename_fld.fold_ident(label);
+
+ // The rename *must* be added to the enclosed syntax context for
+ // `break` or `continue` to pick up because by definition they are
+ // in a block enclosed by loop head.
+ fld.extsbox.push_frame();
fld.extsbox.info().pending_renames.push(rename);
- let mut pending_renames = vec!(rename);
- let mut rename_fld = renames_to_fold(&mut pending_renames);
- (Some(rename_fld.fold_ident(label)),
- rename_fld.fold_block(loop_block))
+ let expanded_block = expand_block_elts(loop_block, fld);
+ fld.extsbox.pop_frame();
+
+ (expanded_block, Some(renamed_ident))
}
- None => (None, loop_block)
+ None => (fld.fold_block(loop_block), opt_ident)
}
}
rename_fld.fold_pat(expanded_pat)
};
// add them to the existing pending renames:
- for pr in new_pending_renames.iter() {
- fld.extsbox.info().pending_renames.push(*pr)
- }
+ fld.extsbox.info().pending_renames.push_all_move(new_pending_renames);
// also, don't forget to expand the init:
let new_init_opt = init.map(|e| fld.fold_expr(e));
let rewritten_local =
}
impl<'a> Folder for IdentRenamer<'a> {
- fn fold_ident(&mut self, id: ast::Ident) -> ast::Ident {
+ fn fold_ident(&mut self, id: Ident) -> Ident {
let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
- new_rename(from, to, ctxt)
+ mtwt::new_rename(from, to, ctxt)
});
- ast::Ident {
+ Ident {
name: id.name,
ctxt: new_ctxt,
}
struct Marker { mark: Mrk }
impl Folder for Marker {
- fn fold_ident(&mut self, id: ast::Ident) -> ast::Ident {
+ fn fold_ident(&mut self, id: Ident) -> Ident {
ast::Ident {
name: id.name,
- ctxt: new_mark(self.mark, id.ctxt)
+ ctxt: mtwt::new_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),
- new_mark(self.mark, ctxt))
+ mtwt::new_mark(self.mark, ctxt))
}
};
Spanned {
use super::*;
use ast;
use ast::{Attribute_, AttrOuter, MetaWord};
- use ast_util::{get_sctable, mtwt_marksof, mtwt_resolve};
- use ast_util;
use codemap;
use codemap::Spanned;
use ext::base::{CrateLoader, MacroCrate};
+ use ext::mtwt;
use parse;
use parse::token;
use util::parser_testing::{string_to_crate_and_sess};
// must be one check clause for each binding:
assert_eq!(bindings.len(),bound_connections.len());
for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
- let binding_name = mtwt_resolve(*bindings.get(binding_idx));
- let binding_marks = mtwt_marksof(bindings.get(binding_idx).ctxt,invalid_name);
+ let binding_name = mtwt::resolve(*bindings.get(binding_idx));
+ let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
// shouldmatch can't name varrefs that don't exist:
assert!((shouldmatch.len() == 0) ||
(varrefs.len() > *shouldmatch.iter().max().unwrap()));
// it should be a path of length 1, and it should
// be free-identifier=? or bound-identifier=? to the given binding
assert_eq!(varref.segments.len(),1);
- let varref_name = mtwt_resolve(varref.segments
- .get(0)
- .identifier);
- let varref_marks = mtwt_marksof(varref.segments
+ let varref_name = mtwt::resolve(varref.segments
.get(0)
- .identifier
- .ctxt,
- invalid_name);
+ .identifier);
+ let varref_marks = mtwt::marksof(varref.segments
+ .get(0)
+ .identifier
+ .ctxt,
+ invalid_name);
if !(varref_name==binding_name) {
println!("uh oh, should match but doesn't:");
println!("varref: {:?}",varref);
println!("binding: {:?}", *bindings.get(binding_idx));
- ast_util::display_sctable(get_sctable());
+ mtwt::with_sctable(|x| mtwt::display_sctable(x));
}
assert_eq!(varref_name,binding_name);
if bound_ident_check {
}
} else {
let fail = (varref.segments.len() == 1)
- && (mtwt_resolve(varref.segments.get(0).identifier) ==
- binding_name);
+ && (mtwt::resolve(varref.segments.get(0).identifier)
+ == binding_name);
// temp debugging:
if fail {
println!("failure on test {}",test_idx);
varref.segments.get(0).identifier.name,
string.get());
println!("binding: {:?}", *bindings.get(binding_idx));
- ast_util::display_sctable(get_sctable());
+ mtwt::with_sctable(|x| mtwt::display_sctable(x));
}
assert!(!fail);
}
[b] => b,
_ => fail!("expected just one binding for ext_cx")
};
- let resolved_binding = mtwt_resolve(*cxbind);
+ let resolved_binding = mtwt::resolve(*cxbind);
// find all the xx varrefs:
let mut path_finder = new_path_finder(Vec::new());
visit::walk_crate(&mut path_finder, &cr, ());
p.segments.len() == 1
&& "xx" == token::get_ident(p.segments.get(0).identifier).get()
}).enumerate() {
- if mtwt_resolve(v.segments.get(0).identifier) !=
- resolved_binding {
+ if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
println!("uh oh, xx binding didn't match xx varref:");
println!("this is xx varref \\# {:?}",idx);
println!("binding: {:?}",cxbind);
println!("resolves to: {:?}",resolved_binding);
println!("varref: {:?}",v.segments.get(0).identifier);
println!("resolves to: {:?}",
- mtwt_resolve(v.segments.get(0).identifier));
- let table = get_sctable();
- println!("SC table:");
-
- {
- let table = table.table.borrow();
- for (idx,val) in table.get().iter().enumerate() {
- println!("{:4u}: {:?}",idx,val);
- }
- }
+ mtwt::resolve(v.segments.get(0).identifier));
+ mtwt::with_sctable(|x| mtwt::display_sctable(x));
}
- assert_eq!(mtwt_resolve(v.segments.get(0).identifier),
+ assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
resolved_binding);
};
}
--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Machinery for hygienic macros, as described in the MTWT[1] paper.
+//!
+//! [1] Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler.
+//! 2012. *Macros that work together: Compile-time bindings, partial expansion,
+//! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216.
+//! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093
+
+use ast::{Ident, Mrk, Name, SyntaxContext};
+
+use std::cell::RefCell;
+use std::local_data;
+use std::rc::Rc;
+use std::vec_ng::Vec;
+
+use collections::HashMap;
+
+// the SCTable contains a table of SyntaxContext_'s. It
+// represents a flattened tree structure, to avoid having
+// managed pointers everywhere (that caused an ICE).
+// the mark_memo and rename_memo fields are side-tables
+// that ensure that adding the same mark to the same context
+// gives you back the same context as before. This shouldn't
+// change the semantics--everything here is immutable--but
+// it should cut down on memory use *a lot*; applying a mark
+// to a tree containing 50 identifiers would otherwise generate
+pub struct SCTable {
+ table: RefCell<Vec<SyntaxContext_>>,
+ mark_memo: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
+ rename_memo: RefCell<HashMap<(SyntaxContext,Ident,Name),SyntaxContext>>,
+}
+
+#[deriving(Eq, Encodable, Decodable, Hash)]
+pub enum SyntaxContext_ {
+ EmptyCtxt,
+ Mark (Mrk,SyntaxContext),
+ // flattening the name and syntaxcontext into the rename...
+ // HIDDEN INVARIANTS:
+ // 1) the first name in a Rename node
+ // can only be a programmer-supplied name.
+ // 2) Every Rename node with a given Name in the
+ // "to" slot must have the same name and context
+ // in the "from" slot. In essence, they're all
+ // pointers to a single "rename" event node.
+ Rename (Ident,Name,SyntaxContext),
+ // actually, IllegalCtxt may not be necessary.
+ IllegalCtxt
+}
+
+/// 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))
+}
+
+// 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);
+ let mut mark_memo = table.mark_memo.borrow_mut();
+ let new_ctxt = |_: &(SyntaxContext, Mrk)|
+ idx_push(table.table.borrow_mut().get(), Mark(m, tail));
+
+ *mark_memo.get().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))
+}
+
+// Extend a syntax context with a given rename and sctable
+fn new_rename_internal(id: Ident,
+ to: Name,
+ tail: SyntaxContext,
+ table: &SCTable) -> SyntaxContext {
+ let key = (tail,id,to);
+ let mut rename_memo = table.rename_memo.borrow_mut();
+ let new_ctxt = |_: &(SyntaxContext, Ident, Mrk)|
+ idx_push(table.table.borrow_mut().get(), Rename(id, to, tail));
+
+ *rename_memo.get().find_or_insert_with(key, new_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>)
+
+ local_data::get(sctable_key, |opt_ts| {
+ let table = match opt_ts {
+ None => {
+ let ts = Rc::new(new_sctable_internal());
+ local_data::set(sctable_key, ts.clone());
+ ts
+ }
+ Some(ts) => ts.clone()
+ };
+ op(table.borrow())
+ })
+}
+
+// Make a fresh syntax context table with EmptyCtxt in slot zero
+// and IllegalCtxt in slot one.
+fn new_sctable_internal() -> SCTable {
+ SCTable {
+ table: RefCell::new(vec!(EmptyCtxt, IllegalCtxt)),
+ mark_memo: RefCell::new(HashMap::new()),
+ rename_memo: RefCell::new(HashMap::new()),
+ }
+}
+
+/// Print out an SCTable for debugging
+pub fn display_sctable(table: &SCTable) {
+ error!("SC table:");
+ let table = table.table.borrow();
+ for (idx,val) in table.get().iter().enumerate() {
+ error!("{:4u} : {:?}",idx,val);
+ }
+}
+
+
+// Add a value to the end of a vec, return its index
+fn idx_push<T>(vec: &mut Vec<T> , val: T) -> u32 {
+ vec.push(val);
+ (vec.len() - 1) as u32
+}
+
+/// Resolve a syntax object to a name, per MTWT.
+pub fn resolve(id: Ident) -> Name {
+ with_sctable(|sctable| {
+ with_resolve_table_mut(|resolve_table| {
+ resolve_internal(id, sctable, resolve_table)
+ })
+ })
+}
+
+type ResolveTable = HashMap<(Name,SyntaxContext),Name>;
+
+// okay, I admit, putting this in TLS is not so nice:
+// fetch the SCTable from TLS, create one if it doesn't yet exist.
+fn with_resolve_table_mut<T>(op: |&mut ResolveTable| -> T) -> T {
+ local_data_key!(resolve_table_key: Rc<RefCell<ResolveTable>>)
+
+ local_data::get(resolve_table_key, |opt_ts| {
+ let table = match opt_ts {
+ None => {
+ let ts = Rc::new(RefCell::new(HashMap::new()));
+ local_data::set(resolve_table_key, ts.clone());
+ ts
+ }
+ Some(ts) => ts.clone()
+ };
+ op(table.borrow().borrow_mut().get())
+ })
+}
+
+// Resolve a syntax object to a name, per MTWT.
+// adding memorization to possibly resolve 500+ seconds in resolve for librustc (!)
+fn resolve_internal(id: Ident,
+ table: &SCTable,
+ resolve_table: &mut ResolveTable) -> Name {
+ let key = (id.name, id.ctxt);
+
+ match resolve_table.find(&key) {
+ Some(&name) => return name,
+ None => {}
+ }
+
+ let resolved = {
+ let result = *table.table.borrow().get().get(id.ctxt as uint);
+ match result {
+ EmptyCtxt => id.name,
+ // ignore marks here:
+ Mark(_,subctxt) =>
+ resolve_internal(Ident{name:id.name, ctxt: subctxt},
+ table, resolve_table),
+ // do the rename if necessary:
+ Rename(Ident{name, ctxt}, toname, subctxt) => {
+ let resolvedfrom =
+ resolve_internal(Ident{name:name, ctxt:ctxt},
+ table, resolve_table);
+ let resolvedthis =
+ resolve_internal(Ident{name:id.name, ctxt:subctxt},
+ table, resolve_table);
+ if (resolvedthis == resolvedfrom)
+ && (marksof_internal(ctxt, resolvedthis, table)
+ == marksof_internal(subctxt, resolvedthis, table)) {
+ toname
+ } else {
+ resolvedthis
+ }
+ }
+ IllegalCtxt() => fail!("expected resolvable context, got IllegalCtxt")
+ }
+ };
+ resolve_table.insert(key, resolved);
+ resolved
+}
+
+/// Compute the marks associated with a syntax context.
+pub fn marksof(ctxt: SyntaxContext, stopname: Name) -> Vec<Mrk> {
+ with_sctable(|table| marksof_internal(ctxt, stopname, table))
+}
+
+// the internal function for computing marks
+// it's not clear to me whether it's better to use a [] mutable
+// vector or a cons-list for this.
+fn marksof_internal(ctxt: SyntaxContext,
+ stopname: Name,
+ table: &SCTable) -> Vec<Mrk> {
+ let mut result = Vec::new();
+ let mut loopvar = ctxt;
+ loop {
+ let table_entry = {
+ let table = table.table.borrow();
+ *table.get().get(loopvar as uint)
+ };
+ match table_entry {
+ EmptyCtxt => {
+ return result;
+ },
+ Mark(mark, tl) => {
+ xorPush(&mut result, mark);
+ loopvar = tl;
+ },
+ Rename(_,name,tl) => {
+ // see MTWT for details on the purpose of the stopname.
+ // short version: it prevents duplication of effort.
+ if name == stopname {
+ return result;
+ } else {
+ loopvar = tl;
+ }
+ }
+ IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
+ }
+ }
+}
+
+/// Return the outer mark for a context with a mark at the outside.
+/// FAILS when outside is not a mark.
+pub fn outer_mark(ctxt: SyntaxContext) -> Mrk {
+ with_sctable(|sctable| {
+ match *sctable.table.borrow().get().get(ctxt as uint) {
+ Mark(mrk, _) => mrk,
+ _ => fail!("can't retrieve outer mark when outside is not a mark")
+ }
+ })
+}
+
+// Push a name... unless it matches the one on top, in which
+// case pop and discard (so two of the same marks cancel)
+fn xorPush(marks: &mut Vec<Mrk>, mark: Mrk) {
+ if (marks.len() > 0) && (*marks.last().unwrap() == mark) {
+ marks.pop().unwrap();
+ } else {
+ marks.push(mark);
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use ast::*;
+ use super::{resolve, xorPush, new_mark_internal, new_sctable_internal};
+ use super::{new_rename_internal, marksof_internal, resolve_internal};
+ use super::{SCTable, EmptyCtxt, Mark, Rename, IllegalCtxt};
+ use std::vec_ng::Vec;
+ use collections::HashMap;
+
+ #[test] fn xorpush_test () {
+ let mut s = Vec::new();
+ xorPush(&mut s, 14);
+ assert_eq!(s.clone(), vec!(14));
+ xorPush(&mut s, 14);
+ assert_eq!(s.clone(), Vec::new());
+ xorPush(&mut s, 14);
+ assert_eq!(s.clone(), vec!(14));
+ xorPush(&mut s, 15);
+ assert_eq!(s.clone(), vec!(14, 15));
+ xorPush(&mut s, 16);
+ assert_eq!(s.clone(), vec!(14, 15, 16));
+ xorPush(&mut s, 16);
+ assert_eq!(s.clone(), vec!(14, 15));
+ xorPush(&mut s, 15);
+ assert_eq!(s.clone(), vec!(14));
+ }
+
+ fn id(n: Name, s: SyntaxContext) -> Ident {
+ Ident {name: n, ctxt: s}
+ }
+
+ // because of the SCTable, I now need a tidy way of
+ // creating syntax objects. Sigh.
+ #[deriving(Clone, Eq, Show)]
+ enum TestSC {
+ M(Mrk),
+ R(Ident,Name)
+ }
+
+ // unfold a vector of TestSC values into a SCTable,
+ // returning the resulting index
+ fn unfold_test_sc(tscs : Vec<TestSC> , tail: SyntaxContext, table: &SCTable)
+ -> SyntaxContext {
+ tscs.rev_iter().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)}})
+ }
+
+ // gather a SyntaxContext back into a vector of TestSCs
+ fn refold_test_sc(mut sc: SyntaxContext, table : &SCTable) -> Vec<TestSC> {
+ let mut result = Vec::new();
+ loop {
+ let table = table.table.borrow();
+ match *table.get().get(sc as uint) {
+ EmptyCtxt => {return result;},
+ Mark(mrk,tail) => {
+ result.push(M(mrk));
+ sc = tail;
+ continue;
+ },
+ Rename(id,name,tail) => {
+ result.push(R(id,name));
+ sc = tail;
+ continue;
+ }
+ IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
+ }
+ }
+ }
+
+ #[test] fn test_unfold_refold(){
+ let mut t = new_sctable_internal();
+
+ let test_sc = vec!(M(3),R(id(101,0),14),M(9));
+ assert_eq!(unfold_test_sc(test_sc.clone(),EMPTY_CTXT,&mut t),4);
+ {
+ let table = t.table.borrow();
+ assert!(*table.get().get(2) == Mark(9,0));
+ assert!(*table.get().get(3) == Rename(id(101,0),14,2));
+ assert!(*table.get().get(4) == Mark(3,3));
+ }
+ assert_eq!(refold_test_sc(4,&t),test_sc);
+ }
+
+ // extend a syntax context with a sequence of marks given
+ // in a vector. v[0] will be the outermost mark.
+ fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
+ -> SyntaxContext {
+ mrks.rev_iter().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
+ {new_mark_internal(*mrk,tail,table)})
+ }
+
+ #[test] fn unfold_marks_test() {
+ let mut t = new_sctable_internal();
+
+ assert_eq!(unfold_marks(vec!(3,7),EMPTY_CTXT,&mut t),3);
+ {
+ let table = t.table.borrow();
+ assert!(*table.get().get(2) == Mark(7,0));
+ assert!(*table.get().get(3) == Mark(3,2));
+ }
+ }
+
+ #[test] fn test_marksof () {
+ let stopname = 242;
+ let name1 = 243;
+ let mut t = new_sctable_internal();
+ assert_eq!(marksof_internal (EMPTY_CTXT,stopname,&t),Vec::new());
+ // FIXME #5074: ANF'd to dodge nested calls
+ { let ans = unfold_marks(vec!(4,98),EMPTY_CTXT,&mut t);
+ assert_eq! (marksof_internal (ans,stopname,&t),vec!(4,98));}
+ // does xoring work?
+ { let ans = unfold_marks(vec!(5,5,16),EMPTY_CTXT,&mut t);
+ assert_eq! (marksof_internal (ans,stopname,&t), vec!(16));}
+ // does nested xoring work?
+ { let ans = unfold_marks(vec!(5,10,10,5,16),EMPTY_CTXT,&mut t);
+ assert_eq! (marksof_internal (ans, stopname,&t), vec!(16));}
+ // rename where stop doesn't match:
+ { let chain = vec!(M(9),
+ R(id(name1,
+ new_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 chain = vec!(M(9),
+ R(id(name1, name1sc),
+ stopname),
+ M(14));
+ let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
+ assert_eq! (marksof_internal (ans, stopname, &t), vec!(9)); }
+ }
+
+
+ #[test] fn resolve_tests () {
+ let a = 40;
+ let mut t = new_sctable_internal();
+ let mut rt = HashMap::new();
+ // - ctxt is MT
+ assert_eq!(resolve_internal(id(a,EMPTY_CTXT),&mut t, &mut rt),a);
+ // - simple ignored marks
+ { let sc = unfold_marks(vec!(1,2,3),EMPTY_CTXT,&mut t);
+ assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);}
+ // - orthogonal rename where names don't match
+ { 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 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), a);}
+ // - rename where names and marks match
+ { let sc1 = unfold_test_sc(vec!(M(1),M(2)),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), 50); }
+ // - rename where names and marks match by literal sharing
+ { let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
+ let sc = unfold_test_sc(vec!(R(id(a,sc1),50)),sc1,&mut t);
+ assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 50); }
+ // - two renames of the same var.. can only happen if you use
+ // local-expand to prevent the inner binding from being renamed
+ // during the rename-pass caused by the first:
+ println!("about to run bad test");
+ { let sc = unfold_test_sc(vec!(R(id(a,EMPTY_CTXT),50),
+ R(id(a,EMPTY_CTXT),51)),
+ 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);
+ 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);
+ 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),
+ M(9)),
+ a_to_a50,
+ &mut t);
+ assert_eq!(resolve_internal(id(a,a50_to_a51_b),&mut t, &mut rt),50);}
+ }
+
+ #[test] fn mtwt_resolve_test(){
+ let a = 40;
+ assert_eq!(resolve(id(a,EMPTY_CTXT)),a);
+ }
+
+
+ #[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);
+ // using the same one again should result in the same index:
+ assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
+ // I'm assuming that the rename table will behave the same....
+ }
+
+ #[test] fn resolve_table_hashing_tests() {
+ let mut t = new_sctable_internal();
+ let mut rt = HashMap::new();
+ assert_eq!(rt.len(),0);
+ resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
+ assert_eq!(rt.len(),1);
+ resolve_internal(id(39,EMPTY_CTXT),&mut t, &mut rt);
+ assert_eq!(rt.len(),2);
+ resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
+ assert_eq!(rt.len(),2);
+ }
+}
pub mod macro_rules;
}
+ pub mod mtwt;
pub mod cfg;
pub mod fmt;
use ast;
use ast::{P, Ident, Name, Mrk};
use ast_util;
+use ext::mtwt;
use parse::token;
use util::interner::{RcStr, StrInterner};
use util::interner;
pub fn mtwt_token_eq(t1 : &Token, t2 : &Token) -> bool {
match (t1,t2) {
(&IDENT(id1,_),&IDENT(id2,_)) | (&LIFETIME(id1),&LIFETIME(id2)) =>
- ast_util::mtwt_resolve(id1) == ast_util::mtwt_resolve(id2),
+ mtwt::resolve(id1) == mtwt::resolve(id2),
_ => *t1 == *t2
}
}
mod test {
use super::*;
use ast;
- use ast_util;
+ use ext::mtwt;
fn mark_ident(id : ast::Ident, m : ast::Mrk) -> ast::Ident {
- ast::Ident{name:id.name,ctxt:ast_util::new_mark(m,id.ctxt)}
+ ast::Ident{name:id.name,ctxt:mtwt::new_mark(m,id.ctxt)}
}
#[test] fn mtwt_token_eq_test() {