Refactor and fix FIXME's in mtwt hygiene code
- Moves mtwt hygiene code into its own file - Fixes FIXME's which leads to ~2x speed gain in expansion pass - It is now @-free
This commit is contained in:
parent
ea71a08f6b
commit
2302ce903d
8 changed files with 558 additions and 562 deletions
|
@ -19,8 +19,9 @@ use middle::pat_util::pat_bindings;
|
||||||
|
|
||||||
use syntax::ast::*;
|
use syntax::ast::*;
|
||||||
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::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::special_idents;
|
||||||
use syntax::parse::token;
|
use syntax::parse::token;
|
||||||
use syntax::print::pprust::path_to_str;
|
use syntax::print::pprust::path_to_str;
|
||||||
|
@ -4176,7 +4177,7 @@ impl Resolver {
|
||||||
fn binding_mode_map(&mut self, pat: @Pat) -> BindingMap {
|
fn binding_mode_map(&mut self, pat: @Pat) -> BindingMap {
|
||||||
let mut result = HashMap::new();
|
let mut result = HashMap::new();
|
||||||
pat_bindings(self.def_map, pat, |binding_mode, _id, sp, path| {
|
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,
|
result.insert(name,
|
||||||
binding_info {span: sp,
|
binding_info {span: sp,
|
||||||
binding_mode: binding_mode});
|
binding_mode: binding_mode});
|
||||||
|
@ -4411,7 +4412,7 @@ impl Resolver {
|
||||||
// what you want).
|
// what you want).
|
||||||
|
|
||||||
let ident = path.segments.get(0).identifier;
|
let ident = path.segments.get(0).identifier;
|
||||||
let renamed = mtwt_resolve(ident);
|
let renamed = mtwt::resolve(ident);
|
||||||
|
|
||||||
match self.resolve_bare_identifier_pattern(ident) {
|
match self.resolve_bare_identifier_pattern(ident) {
|
||||||
FoundStructOrEnumVariant(def, lp)
|
FoundStructOrEnumVariant(def, lp)
|
||||||
|
@ -4965,7 +4966,7 @@ impl Resolver {
|
||||||
let search_result;
|
let search_result;
|
||||||
match namespace {
|
match namespace {
|
||||||
ValueNS => {
|
ValueNS => {
|
||||||
let renamed = mtwt_resolve(ident);
|
let renamed = mtwt::resolve(ident);
|
||||||
let mut value_ribs = self.value_ribs.borrow_mut();
|
let mut value_ribs = self.value_ribs.borrow_mut();
|
||||||
search_result = self.search_ribs(value_ribs.get(),
|
search_result = self.search_ribs(value_ribs.get(),
|
||||||
renamed,
|
renamed,
|
||||||
|
@ -5213,7 +5214,7 @@ impl Resolver {
|
||||||
let rib = label_ribs.get()[label_ribs.get().len() -
|
let rib = label_ribs.get()[label_ribs.get().len() -
|
||||||
1];
|
1];
|
||||||
let mut bindings = rib.bindings.borrow_mut();
|
let mut bindings = rib.bindings.borrow_mut();
|
||||||
let renamed = mtwt_resolve(label);
|
let renamed = mtwt::resolve(label);
|
||||||
bindings.get().insert(renamed, def_like);
|
bindings.get().insert(renamed, def_like);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5225,7 +5226,7 @@ impl Resolver {
|
||||||
|
|
||||||
ExprBreak(Some(label)) | ExprAgain(Some(label)) => {
|
ExprBreak(Some(label)) | ExprAgain(Some(label)) => {
|
||||||
let mut label_ribs = self.label_ribs.borrow_mut();
|
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) {
|
match self.search_ribs(label_ribs.get(), renamed, expr.span) {
|
||||||
None =>
|
None =>
|
||||||
self.resolve_error(expr.span,
|
self.resolve_error(expr.span,
|
||||||
|
|
|
@ -19,8 +19,6 @@ use parse::token;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Show;
|
use std::fmt::Show;
|
||||||
use std::cell::RefCell;
|
|
||||||
use collections::HashMap;
|
|
||||||
use std::option::Option;
|
use std::option::Option;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::vec_ng::Vec;
|
use std::vec_ng::Vec;
|
||||||
|
@ -42,7 +40,10 @@ pub fn P<T: 'static>(value: T) -> P<T> {
|
||||||
// macro expansion per Flatt et al., "Macros
|
// macro expansion per Flatt et al., "Macros
|
||||||
// That Work Together"
|
// That Work Together"
|
||||||
#[deriving(Clone, Hash, TotalEq, TotalOrd, Show)]
|
#[deriving(Clone, Hash, TotalEq, TotalOrd, Show)]
|
||||||
pub struct Ident { name: Name, ctxt: SyntaxContext }
|
pub struct Ident {
|
||||||
|
name: Name,
|
||||||
|
ctxt: SyntaxContext
|
||||||
|
}
|
||||||
|
|
||||||
impl Ident {
|
impl Ident {
|
||||||
/// Construct an identifier with the given name and an empty context:
|
/// Construct an identifier with the given name and an empty context:
|
||||||
|
@ -88,43 +89,9 @@ impl Eq for Ident {
|
||||||
// this uint is a reference to a table stored in thread-local
|
// this uint is a reference to a table stored in thread-local
|
||||||
// storage.
|
// storage.
|
||||||
pub type SyntaxContext = u32;
|
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 EMPTY_CTXT : SyntaxContext = 0;
|
||||||
pub static ILLEGAL_CTXT : SyntaxContext = 1;
|
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
|
/// A name is a part of an identifier, representing a string or gensym. It's
|
||||||
/// the result of interning.
|
/// the result of interning.
|
||||||
pub type Name = u32;
|
pub type Name = u32;
|
||||||
|
|
|
@ -18,11 +18,9 @@ use print::pprust;
|
||||||
use visit::Visitor;
|
use visit::Visitor;
|
||||||
use visit;
|
use visit;
|
||||||
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::Cell;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use collections::HashMap;
|
|
||||||
use std::u32;
|
use std::u32;
|
||||||
use std::local_data;
|
|
||||||
use std::vec_ng::Vec;
|
use std::vec_ng::Vec;
|
||||||
|
|
||||||
pub fn path_name_i(idents: &[Ident]) -> ~str {
|
pub fn path_name_i(idents: &[Ident]) -> ~str {
|
||||||
|
@ -651,251 +649,6 @@ pub fn pat_is_ident(pat: @ast::Pat) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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?
|
// are two paths equal when compared unhygienically?
|
||||||
// since I'm using this to replace ==, it seems appropriate
|
// since I'm using this to replace ==, it seems appropriate
|
||||||
// to compare the span, global, etc. fields as well.
|
// to compare the span, global, etc. fields as well.
|
||||||
|
@ -937,9 +690,6 @@ mod test {
|
||||||
use ast::*;
|
use ast::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use opt_vec;
|
use opt_vec;
|
||||||
use collections::HashMap;
|
|
||||||
|
|
||||||
use std::vec_ng::Vec;
|
|
||||||
|
|
||||||
fn ident_to_segment(id : &Ident) -> PathSegment {
|
fn ident_to_segment(id : &Ident) -> PathSegment {
|
||||||
PathSegment {identifier:id.clone(),
|
PathSegment {identifier:id.clone(),
|
||||||
|
@ -957,210 +707,4 @@ mod test {
|
||||||
[Ident{name:3,ctxt:104},
|
[Ident{name:3,ctxt:104},
|
||||||
Ident{name:77,ctxt:182}].map(ident_to_segment)));
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,9 +146,9 @@ pub enum SyntaxExtension {
|
||||||
|
|
||||||
pub struct BlockInfo {
|
pub struct BlockInfo {
|
||||||
// should macros escape from this scope?
|
// should macros escape from this scope?
|
||||||
macros_escape : bool,
|
macros_escape: bool,
|
||||||
// what are the pending renames?
|
// what are the pending renames?
|
||||||
pending_renames : RenameList,
|
pending_renames: RenameList,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockInfo {
|
impl BlockInfo {
|
||||||
|
@ -161,7 +161,7 @@ impl BlockInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
// a list of ident->name renamings
|
// 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
|
// The base map of methods for expanding syntax extension
|
||||||
// AST nodes into full ASTs
|
// AST nodes into full ASTs
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
use ast::{P, Block, Crate, DeclLocal, ExprMac};
|
use ast::{P, Block, Crate, DeclLocal, ExprMac};
|
||||||
use ast::{Local, Ident, MacInvocTT};
|
use ast::{Local, Ident, MacInvocTT};
|
||||||
use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
|
use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
|
||||||
use ast::{TokenTree};
|
use ast::TokenTree;
|
||||||
use ast;
|
use ast;
|
||||||
use ast_util::{new_rename, new_mark};
|
use ext::mtwt;
|
||||||
use ext::build::AstBuilder;
|
use ext::build::AstBuilder;
|
||||||
use attr;
|
use attr;
|
||||||
use attr::AttrMetaMethods;
|
use attr::AttrMetaMethods;
|
||||||
|
@ -140,9 +140,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
|
||||||
// Expand any interior macros etc.
|
// Expand any interior macros etc.
|
||||||
// NB: we don't fold pats yet. Curious.
|
// NB: we don't fold pats yet. Curious.
|
||||||
let src_expr = fld.fold_expr(src_expr).clone();
|
let src_expr = fld.fold_expr(src_expr).clone();
|
||||||
// Rename label before expansion.
|
let (src_loop_block, opt_ident) = expand_loop_block(src_loop_block, opt_ident, fld);
|
||||||
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 span = e.span;
|
let span = e.span;
|
||||||
|
|
||||||
|
@ -205,9 +203,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::ExprLoop(loop_block, opt_ident) => {
|
ast::ExprLoop(loop_block, opt_ident) => {
|
||||||
let (opt_ident, loop_block) =
|
let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
|
||||||
rename_loop_label(opt_ident, loop_block, fld);
|
|
||||||
let loop_block = fld.fold_block(loop_block);
|
|
||||||
fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
|
fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,22 +211,38 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rename loop label and its all occurrences inside the loop body
|
// Rename loop label and expand its loop body
|
||||||
fn rename_loop_label(opt_ident: Option<Ident>,
|
//
|
||||||
loop_block: P<Block>,
|
// The renaming procedure for loop is different in the sense that the loop
|
||||||
fld: &mut MacroExpander) -> (Option<Ident>, P<Block>) {
|
// 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 {
|
match opt_ident {
|
||||||
Some(label) => {
|
Some(label) => {
|
||||||
// Generate fresh label and add to the existing pending renames
|
|
||||||
let new_label = fresh_name(&label);
|
let new_label = fresh_name(&label);
|
||||||
let rename = (label, new_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);
|
fld.extsbox.info().pending_renames.push(rename);
|
||||||
let mut pending_renames = vec!(rename);
|
let expanded_block = expand_block_elts(loop_block, fld);
|
||||||
let mut rename_fld = renames_to_fold(&mut pending_renames);
|
fld.extsbox.pop_frame();
|
||||||
(Some(rename_fld.fold_ident(label)),
|
|
||||||
rename_fld.fold_block(loop_block))
|
(expanded_block, Some(renamed_ident))
|
||||||
}
|
}
|
||||||
None => (None, loop_block)
|
None => (fld.fold_block(loop_block), opt_ident)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -628,9 +640,7 @@ fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
|
||||||
rename_fld.fold_pat(expanded_pat)
|
rename_fld.fold_pat(expanded_pat)
|
||||||
};
|
};
|
||||||
// add them to the existing pending renames:
|
// add them to the existing pending renames:
|
||||||
for pr in new_pending_renames.iter() {
|
fld.extsbox.info().pending_renames.push_all_move(new_pending_renames);
|
||||||
fld.extsbox.info().pending_renames.push(*pr)
|
|
||||||
}
|
|
||||||
// also, don't forget to expand the init:
|
// also, don't forget to expand the init:
|
||||||
let new_init_opt = init.map(|e| fld.fold_expr(e));
|
let new_init_opt = init.map(|e| fld.fold_expr(e));
|
||||||
let rewritten_local =
|
let rewritten_local =
|
||||||
|
@ -754,11 +764,11 @@ pub struct IdentRenamer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Folder for IdentRenamer<'a> {
|
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)| {
|
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,
|
name: id.name,
|
||||||
ctxt: new_ctxt,
|
ctxt: new_ctxt,
|
||||||
}
|
}
|
||||||
|
@ -839,10 +849,10 @@ pub fn expand_crate(parse_sess: @parse::ParseSess,
|
||||||
struct Marker { mark: Mrk }
|
struct Marker { mark: Mrk }
|
||||||
|
|
||||||
impl Folder for Marker {
|
impl Folder for Marker {
|
||||||
fn fold_ident(&mut self, id: ast::Ident) -> ast::Ident {
|
fn fold_ident(&mut self, id: Ident) -> Ident {
|
||||||
ast::Ident {
|
ast::Ident {
|
||||||
name: id.name,
|
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 {
|
fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
|
||||||
|
@ -850,7 +860,7 @@ impl Folder for Marker {
|
||||||
MacInvocTT(ref path, ref tts, ctxt) => {
|
MacInvocTT(ref path, ref tts, ctxt) => {
|
||||||
MacInvocTT(self.fold_path(path),
|
MacInvocTT(self.fold_path(path),
|
||||||
fold_tts(tts.as_slice(), self),
|
fold_tts(tts.as_slice(), self),
|
||||||
new_mark(self.mark, ctxt))
|
mtwt::new_mark(self.mark, ctxt))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Spanned {
|
Spanned {
|
||||||
|
@ -906,11 +916,10 @@ mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ast;
|
use ast;
|
||||||
use ast::{Attribute_, AttrOuter, MetaWord};
|
use ast::{Attribute_, AttrOuter, MetaWord};
|
||||||
use ast_util::{get_sctable, mtwt_marksof, mtwt_resolve};
|
|
||||||
use ast_util;
|
|
||||||
use codemap;
|
use codemap;
|
||||||
use codemap::Spanned;
|
use codemap::Spanned;
|
||||||
use ext::base::{CrateLoader, MacroCrate};
|
use ext::base::{CrateLoader, MacroCrate};
|
||||||
|
use ext::mtwt;
|
||||||
use parse;
|
use parse;
|
||||||
use parse::token;
|
use parse::token;
|
||||||
use util::parser_testing::{string_to_crate_and_sess};
|
use util::parser_testing::{string_to_crate_and_sess};
|
||||||
|
@ -1139,8 +1148,8 @@ mod test {
|
||||||
// must be one check clause for each binding:
|
// must be one check clause for each binding:
|
||||||
assert_eq!(bindings.len(),bound_connections.len());
|
assert_eq!(bindings.len(),bound_connections.len());
|
||||||
for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
|
for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
|
||||||
let binding_name = mtwt_resolve(*bindings.get(binding_idx));
|
let binding_name = mtwt::resolve(*bindings.get(binding_idx));
|
||||||
let binding_marks = mtwt_marksof(bindings.get(binding_idx).ctxt,invalid_name);
|
let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
|
||||||
// shouldmatch can't name varrefs that don't exist:
|
// shouldmatch can't name varrefs that don't exist:
|
||||||
assert!((shouldmatch.len() == 0) ||
|
assert!((shouldmatch.len() == 0) ||
|
||||||
(varrefs.len() > *shouldmatch.iter().max().unwrap()));
|
(varrefs.len() > *shouldmatch.iter().max().unwrap()));
|
||||||
|
@ -1149,10 +1158,10 @@ mod test {
|
||||||
// it should be a path of length 1, and it should
|
// it should be a path of length 1, and it should
|
||||||
// be free-identifier=? or bound-identifier=? to the given binding
|
// be free-identifier=? or bound-identifier=? to the given binding
|
||||||
assert_eq!(varref.segments.len(),1);
|
assert_eq!(varref.segments.len(),1);
|
||||||
let varref_name = mtwt_resolve(varref.segments
|
let varref_name = mtwt::resolve(varref.segments
|
||||||
.get(0)
|
.get(0)
|
||||||
.identifier);
|
.identifier);
|
||||||
let varref_marks = mtwt_marksof(varref.segments
|
let varref_marks = mtwt::marksof(varref.segments
|
||||||
.get(0)
|
.get(0)
|
||||||
.identifier
|
.identifier
|
||||||
.ctxt,
|
.ctxt,
|
||||||
|
@ -1161,7 +1170,7 @@ mod test {
|
||||||
println!("uh oh, should match but doesn't:");
|
println!("uh oh, should match but doesn't:");
|
||||||
println!("varref: {:?}",varref);
|
println!("varref: {:?}",varref);
|
||||||
println!("binding: {:?}", *bindings.get(binding_idx));
|
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);
|
assert_eq!(varref_name,binding_name);
|
||||||
if bound_ident_check {
|
if bound_ident_check {
|
||||||
|
@ -1171,8 +1180,8 @@ mod test {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let fail = (varref.segments.len() == 1)
|
let fail = (varref.segments.len() == 1)
|
||||||
&& (mtwt_resolve(varref.segments.get(0).identifier) ==
|
&& (mtwt::resolve(varref.segments.get(0).identifier)
|
||||||
binding_name);
|
== binding_name);
|
||||||
// temp debugging:
|
// temp debugging:
|
||||||
if fail {
|
if fail {
|
||||||
println!("failure on test {}",test_idx);
|
println!("failure on test {}",test_idx);
|
||||||
|
@ -1188,7 +1197,7 @@ mod test {
|
||||||
varref.segments.get(0).identifier.name,
|
varref.segments.get(0).identifier.name,
|
||||||
string.get());
|
string.get());
|
||||||
println!("binding: {:?}", *bindings.get(binding_idx));
|
println!("binding: {:?}", *bindings.get(binding_idx));
|
||||||
ast_util::display_sctable(get_sctable());
|
mtwt::with_sctable(|x| mtwt::display_sctable(x));
|
||||||
}
|
}
|
||||||
assert!(!fail);
|
assert!(!fail);
|
||||||
}
|
}
|
||||||
|
@ -1218,7 +1227,7 @@ foo_module!()
|
||||||
[b] => b,
|
[b] => b,
|
||||||
_ => fail!("expected just one binding for ext_cx")
|
_ => 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:
|
// find all the xx varrefs:
|
||||||
let mut path_finder = new_path_finder(Vec::new());
|
let mut path_finder = new_path_finder(Vec::new());
|
||||||
visit::walk_crate(&mut path_finder, &cr, ());
|
visit::walk_crate(&mut path_finder, &cr, ());
|
||||||
|
@ -1229,26 +1238,17 @@ foo_module!()
|
||||||
p.segments.len() == 1
|
p.segments.len() == 1
|
||||||
&& "xx" == token::get_ident(p.segments.get(0).identifier).get()
|
&& "xx" == token::get_ident(p.segments.get(0).identifier).get()
|
||||||
}).enumerate() {
|
}).enumerate() {
|
||||||
if mtwt_resolve(v.segments.get(0).identifier) !=
|
if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
|
||||||
resolved_binding {
|
|
||||||
println!("uh oh, xx binding didn't match xx varref:");
|
println!("uh oh, xx binding didn't match xx varref:");
|
||||||
println!("this is xx varref \\# {:?}",idx);
|
println!("this is xx varref \\# {:?}",idx);
|
||||||
println!("binding: {:?}",cxbind);
|
println!("binding: {:?}",cxbind);
|
||||||
println!("resolves to: {:?}",resolved_binding);
|
println!("resolves to: {:?}",resolved_binding);
|
||||||
println!("varref: {:?}",v.segments.get(0).identifier);
|
println!("varref: {:?}",v.segments.get(0).identifier);
|
||||||
println!("resolves to: {:?}",
|
println!("resolves to: {:?}",
|
||||||
mtwt_resolve(v.segments.get(0).identifier));
|
mtwt::resolve(v.segments.get(0).identifier));
|
||||||
let table = get_sctable();
|
mtwt::with_sctable(|x| mtwt::display_sctable(x));
|
||||||
println!("SC table:");
|
|
||||||
|
|
||||||
{
|
|
||||||
let table = table.table.borrow();
|
|
||||||
for (idx,val) in table.get().iter().enumerate() {
|
|
||||||
println!("{:4u}: {:?}",idx,val);
|
|
||||||
}
|
}
|
||||||
}
|
assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
|
||||||
}
|
|
||||||
assert_eq!(mtwt_resolve(v.segments.get(0).identifier),
|
|
||||||
resolved_binding);
|
resolved_binding);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
482
src/libsyntax/ext/mtwt.rs
Normal file
482
src/libsyntax/ext/mtwt.rs
Normal file
|
@ -0,0 +1,482 @@
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -87,6 +87,7 @@ pub mod ext {
|
||||||
pub mod macro_rules;
|
pub mod macro_rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod mtwt;
|
||||||
|
|
||||||
pub mod cfg;
|
pub mod cfg;
|
||||||
pub mod fmt;
|
pub mod fmt;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
use ast;
|
use ast;
|
||||||
use ast::{P, Ident, Name, Mrk};
|
use ast::{P, Ident, Name, Mrk};
|
||||||
use ast_util;
|
use ast_util;
|
||||||
|
use ext::mtwt;
|
||||||
use parse::token;
|
use parse::token;
|
||||||
use util::interner::{RcStr, StrInterner};
|
use util::interner::{RcStr, StrInterner};
|
||||||
use util::interner;
|
use util::interner;
|
||||||
|
@ -722,7 +723,7 @@ pub fn is_reserved_keyword(tok: &Token) -> bool {
|
||||||
pub fn mtwt_token_eq(t1 : &Token, t2 : &Token) -> bool {
|
pub fn mtwt_token_eq(t1 : &Token, t2 : &Token) -> bool {
|
||||||
match (t1,t2) {
|
match (t1,t2) {
|
||||||
(&IDENT(id1,_),&IDENT(id2,_)) | (&LIFETIME(id1),&LIFETIME(id2)) =>
|
(&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
|
_ => *t1 == *t2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -732,10 +733,10 @@ pub fn mtwt_token_eq(t1 : &Token, t2 : &Token) -> bool {
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ast;
|
use ast;
|
||||||
use ast_util;
|
use ext::mtwt;
|
||||||
|
|
||||||
fn mark_ident(id : ast::Ident, m : ast::Mrk) -> ast::Ident {
|
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() {
|
#[test] fn mtwt_token_eq_test() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue