pub use self::SyntaxContext_::*;
use ast::{Ident, Mrk, Name, SyntaxContext};
use std::cell::RefCell;
use std::collections::HashMap;
pub struct SCTable {
table: RefCell<Vec<SyntaxContext_>>,
mark_memo: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
rename_memo: RefCell<HashMap<(SyntaxContext,(Name,SyntaxContext),Name),SyntaxContext>>,
}
#[derive(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)]
pub enum SyntaxContext_ {
EmptyCtxt,
Mark (Mrk,SyntaxContext),
Rename (Ident,Name,SyntaxContext),
IllegalCtxt
}
pub type RenameList = Vec<(Ident, Name)>;
pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext {
with_sctable(|table| apply_mark_internal(m, ctxt, table))
}
fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext {
let key = (ctxt, m);
*table.mark_memo.borrow_mut().entry(key).or_insert_with(|| {
SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Mark(m, ctxt)))
})
}
pub fn apply_rename(id: Ident, to:Name,
ctxt: SyntaxContext) -> SyntaxContext {
with_sctable(|table| apply_rename_internal(id, to, ctxt, table))
}
fn apply_rename_internal(id: Ident,
to: Name,
ctxt: SyntaxContext,
table: &SCTable) -> SyntaxContext {
let key = (ctxt, (id.name, id.ctxt), to);
*table.rename_memo.borrow_mut().entry(key).or_insert_with(|| {
SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Rename(id, to, ctxt)))
})
}
pub fn apply_renames(renames: &RenameList, ctxt: SyntaxContext) -> SyntaxContext {
renames.iter().fold(ctxt, |ctxt, &(from, to)| {
apply_rename(from, to, ctxt)
})
}
pub fn with_sctable<T, F>(op: F) -> T where
F: FnOnce(&SCTable) -> T,
{
thread_local!(static SCTABLE_KEY: SCTable = new_sctable_internal());
SCTABLE_KEY.with(move |slot| op(slot))
}
fn new_sctable_internal() -> SCTable {
SCTable {
table: RefCell::new(vec!(EmptyCtxt, IllegalCtxt)),
mark_memo: RefCell::new(HashMap::new()),
rename_memo: RefCell::new(HashMap::new()),
}
}
pub fn display_sctable(table: &SCTable) {
error!("SC table:");
for (idx,val) in table.table.borrow().iter().enumerate() {
error!("{:4} : {:?}",idx,val);
}
}
pub fn clear_tables() {
with_sctable(|table| {
*table.table.borrow_mut() = Vec::new();
*table.mark_memo.borrow_mut() = HashMap::new();
*table.rename_memo.borrow_mut() = HashMap::new();
});
with_resolve_table_mut(|table| *table = HashMap::new());
}
pub fn reset_tables() {
with_sctable(|table| {
*table.table.borrow_mut() = vec!(EmptyCtxt, IllegalCtxt);
*table.mark_memo.borrow_mut() = HashMap::new();
*table.rename_memo.borrow_mut() = HashMap::new();
});
with_resolve_table_mut(|table| *table = HashMap::new());
}
fn idx_push<T>(vec: &mut Vec<T>, val: T) -> u32 {
vec.push(val);
(vec.len() - 1) as u32
}
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>;
fn with_resolve_table_mut<T, F>(op: F) -> T where
F: FnOnce(&mut ResolveTable) -> T,
{
thread_local!(static RESOLVE_TABLE_KEY: RefCell<ResolveTable> = {
RefCell::new(HashMap::new())
});
RESOLVE_TABLE_KEY.with(move |slot| op(&mut *slot.borrow_mut()))
}
fn resolve_internal(id: Ident,
table: &SCTable,
resolve_table: &mut ResolveTable) -> Name {
let key = (id.name, id.ctxt);
match resolve_table.get(&key) {
Some(&name) => return name,
None => {}
}
let resolved = {
let result = (*table.table.borrow())[id.ctxt.0 as usize];
match result {
EmptyCtxt => id.name,
Mark(_,subctxt) =>
resolve_internal(Ident::new(id.name, subctxt),
table, resolve_table),
Rename(Ident{name, ctxt}, toname, subctxt) => {
let resolvedfrom =
resolve_internal(Ident::new(name, ctxt),
table, resolve_table);
let resolvedthis =
resolve_internal(Ident::new(id.name, subctxt),
table, resolve_table);
if (resolvedthis == resolvedfrom)
&& (marksof_internal(ctxt, resolvedthis, table)
== marksof_internal(subctxt, resolvedthis, table)) {
toname
} else {
resolvedthis
}
}
IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
}
};
resolve_table.insert(key, resolved);
resolved
}
pub fn marksof(ctxt: SyntaxContext, stopname: Name) -> Vec<Mrk> {
with_sctable(|table| marksof_internal(ctxt, stopname, table))
}
fn marksof_internal(ctxt: SyntaxContext,
stopname: Name,
table: &SCTable) -> Vec<Mrk> {
let mut result = Vec::new();
let mut loopvar = ctxt;
loop {
let table_entry = (*table.table.borrow())[loopvar.0 as usize];
match table_entry {
EmptyCtxt => {
return result;
},
Mark(mark, tl) => {
xor_push(&mut result, mark);
loopvar = tl;
},
Rename(_,name,tl) => {
if name == stopname {
return result;
} else {
loopvar = tl;
}
}
IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
}
}
}
pub fn outer_mark(ctxt: SyntaxContext) -> Mrk {
with_sctable(|sctable| {
match (*sctable.table.borrow())[ctxt.0 as usize] {
Mark(mrk, _) => mrk,
_ => panic!("can't retrieve outer mark when outside is not a mark")
}
})
}
fn xor_push(marks: &mut Vec<Mrk>, mark: Mrk) {
if (!marks.is_empty()) && (*marks.last().unwrap() == mark) {
marks.pop().unwrap();
} else {
marks.push(mark);
}
}
#[cfg(test)]
mod tests {
use self::TestSC::*;
use ast::{EMPTY_CTXT, Ident, Mrk, Name, SyntaxContext};
use super::{resolve, xor_push, apply_mark_internal, new_sctable_internal};
use super::{apply_rename_internal, apply_renames, marksof_internal, resolve_internal};
use super::{SCTable, EmptyCtxt, Mark, Rename, IllegalCtxt};
use std::collections::HashMap;
#[test]
fn xorpush_test () {
let mut s = Vec::new();
xor_push(&mut s, 14);
assert_eq!(s.clone(), [14]);
xor_push(&mut s, 14);
assert_eq!(s.clone(), []);
xor_push(&mut s, 14);
assert_eq!(s.clone(), [14]);
xor_push(&mut s, 15);
assert_eq!(s.clone(), [14, 15]);
xor_push(&mut s, 16);
assert_eq!(s.clone(), [14, 15, 16]);
xor_push(&mut s, 16);
assert_eq!(s.clone(), [14, 15]);
xor_push(&mut s, 15);
assert_eq!(s.clone(), [14]);
}
fn id(n: u32, s: SyntaxContext) -> Ident {
Ident::new(Name(n), s)
}
#[derive(Clone, PartialEq, Debug)]
enum TestSC {
M(Mrk),
R(Ident,Name)
}
fn unfold_test_sc(tscs : Vec<TestSC> , tail: SyntaxContext, table: &SCTable)
-> SyntaxContext {
tscs.iter().rev().fold(tail, |tail : SyntaxContext, tsc : &TestSC|
{match *tsc {
M(mrk) => apply_mark_internal(mrk,tail,table),
R(ident,name) => apply_rename_internal(ident,name,tail,table)}})
}
fn refold_test_sc(mut sc: SyntaxContext, table : &SCTable) -> Vec<TestSC> {
let mut result = Vec::new();
loop {
let table = table.table.borrow();
match (*table)[sc.0 as usize] {
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 => panic!("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,EMPTY_CTXT),Name(14)),M(9));
assert_eq!(unfold_test_sc(test_sc.clone(),EMPTY_CTXT,&mut t),SyntaxContext(4));
{
let table = t.table.borrow();
assert!((*table)[2] == Mark(9,EMPTY_CTXT));
assert!((*table)[3] == Rename(id(101,EMPTY_CTXT),Name(14),SyntaxContext(2)));
assert!((*table)[4] == Mark(3,SyntaxContext(3)));
}
assert_eq!(refold_test_sc(SyntaxContext(4),&t),test_sc);
}
fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
-> SyntaxContext {
mrks.iter().rev().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
{apply_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),SyntaxContext(3));
{
let table = t.table.borrow();
assert!((*table)[2] == Mark(7,EMPTY_CTXT));
assert!((*table)[3] == Mark(3,SyntaxContext(2)));
}
}
#[test]
fn test_marksof () {
let stopname = Name(242);
let name1 = Name(243);
let mut t = new_sctable_internal();
assert_eq!(marksof_internal (EMPTY_CTXT,stopname,&t),Vec::new());
{ let ans = unfold_marks(vec!(4,98),EMPTY_CTXT,&mut t);
assert_eq! (marksof_internal (ans,stopname,&t), [4, 98]);}
{ let ans = unfold_marks(vec!(5,5,16),EMPTY_CTXT,&mut t);
assert_eq! (marksof_internal (ans,stopname,&t), [16]);}
{ let ans = unfold_marks(vec!(5,10,10,5,16),EMPTY_CTXT,&mut t);
assert_eq! (marksof_internal (ans, stopname,&t), [16]);}
{ let chain = vec!(M(9),
R(id(name1.0,
apply_mark_internal (4, EMPTY_CTXT,&mut t)),
Name(100101102)),
M(14));
let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
assert_eq! (marksof_internal (ans, stopname, &t), [9, 14]);}
{ let name1sc = apply_mark_internal(4, EMPTY_CTXT, &mut t);
let chain = vec!(M(9),
R(id(name1.0, name1sc),
stopname),
M(14));
let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
assert_eq! (marksof_internal (ans, stopname, &t), [9]); }
}
#[test]
fn resolve_tests () {
let a = 40;
let mut t = new_sctable_internal();
let mut rt = HashMap::new();
assert_eq!(resolve_internal(id(a,EMPTY_CTXT),&mut t, &mut rt),Name(a));
{ let sc = unfold_marks(vec!(1,2,3),EMPTY_CTXT,&mut t);
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),Name(a));}
{ let sc = unfold_test_sc(vec!(R(id(50,EMPTY_CTXT),Name(51)),M(12)),EMPTY_CTXT,&mut t);
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),Name(a));}
{ let sc1 = apply_mark_internal(1,EMPTY_CTXT,&mut t);
let sc = unfold_test_sc(vec!(R(id(a,sc1),Name(50)),
M(1),
M(2)),
EMPTY_CTXT,&mut t);
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(a));}
{ let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
let sc = unfold_test_sc(vec!(R(id(a,sc1),Name(50)),M(1),M(2)),EMPTY_CTXT,&mut t);
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(50)); }
{ let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
let sc = unfold_test_sc(vec!(R(id(a,sc1),Name(50))),sc1,&mut t);
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(50)); }
println!("about to run bad test");
{ let sc = unfold_test_sc(vec!(R(id(a,EMPTY_CTXT),Name(50)),
R(id(a,EMPTY_CTXT),Name(51))),
EMPTY_CTXT,&mut t);
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(51)); }
{ let a_to_a50 = apply_rename_internal(id(a,EMPTY_CTXT),Name(50),EMPTY_CTXT,&mut t);
let a50_to_a51 = apply_rename_internal(id(a,a_to_a50),Name(51),a_to_a50,&mut t);
assert_eq!(resolve_internal(id(a,a50_to_a51),&mut t, &mut rt),Name(51));
let sc = apply_mark_internal(9,a50_to_a51,&mut t);
assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),Name(51));
let a50_to_a51_b = unfold_test_sc(vec!(R(id(a,a_to_a50),Name(51)),
M(9)),
a_to_a50,
&mut t);
assert_eq!(resolve_internal(id(a,a50_to_a51_b),&mut t, &mut rt),Name(50));}
}
#[test]
fn mtwt_resolve_test(){
let a = 40;
assert_eq!(resolve(id(a,EMPTY_CTXT)),Name(a));
}
#[test]
fn hashing_tests () {
let mut t = new_sctable_internal();
assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(2));
assert_eq!(apply_mark_internal(13,EMPTY_CTXT,&mut t),SyntaxContext(3));
assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(2));
}
#[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);
}
#[test]
fn new_resolves_test() {
let renames = vec!((Ident::with_empty_ctxt(Name(23)),Name(24)),
(Ident::with_empty_ctxt(Name(29)),Name(29)));
let new_ctxt1 = apply_renames(&renames,EMPTY_CTXT);
assert_eq!(resolve(Ident::new(Name(23),new_ctxt1)),Name(24));
assert_eq!(resolve(Ident::new(Name(29),new_ctxt1)),Name(29));
}
}