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_>>,
marks: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
renames: RefCell<HashMap<Name,SyntaxContext>>,
}
#[derive(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)]
pub enum SyntaxContext_ {
EmptyCtxt,
Mark (Mrk,SyntaxContext),
Rename (Name),
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 ctxts = &mut *table.table.borrow_mut();
match ctxts[ctxt.0 as usize] {
Mark(outer_mark, prev_ctxt) if outer_mark == m => return prev_ctxt,
_ => *table.marks.borrow_mut().entry((ctxt, m)).or_insert_with(|| {
SyntaxContext(idx_push(ctxts, Mark(m, ctxt)))
}),
}
}
pub fn apply_rename(from: Ident, to: Name, ident: Ident) -> Ident {
with_sctable(|table| apply_rename_internal(from, to, ident, table))
}
fn apply_rename_internal(from: Ident, to: Name, ident: Ident, table: &SCTable) -> Ident {
if (ident.name, ident.ctxt) != (from.name, from.ctxt) {
return ident;
}
let ctxt = *table.renames.borrow_mut().entry(to).or_insert_with(|| {
SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Rename(to)))
});
Ident { ctxt: ctxt, ..ident }
}
pub fn apply_renames(renames: &RenameList, ident: Ident) -> Ident {
renames.iter().fold(ident, |ident, &(from, to)| {
apply_rename(from, to, ident)
})
}
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)),
marks: RefCell::new(HashMap::new()),
renames: 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.marks.borrow_mut() = HashMap::new();
*table.renames.borrow_mut() = HashMap::new();
});
}
pub fn reset_tables() {
with_sctable(|table| {
*table.table.borrow_mut() = vec!(EmptyCtxt, IllegalCtxt);
*table.marks.borrow_mut() = HashMap::new();
*table.renames.borrow_mut() = 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| {
resolve_internal(id, sctable)
})
}
fn resolve_internal(id: Ident, table: &SCTable) -> Name {
match table.table.borrow()[id.ctxt.0 as usize] {
EmptyCtxt => id.name,
Mark(_, subctxt) => resolve_internal(Ident::new(id.name, subctxt), table),
Rename(name) => name,
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")
}
})
}
#[cfg(test)]
mod tests {
use ast::{EMPTY_CTXT, Ident, Mrk, Name, SyntaxContext};
use super::{resolve, apply_mark_internal, new_sctable_internal};
use super::{SCTable, Mark};
fn id(n: u32, s: SyntaxContext) -> Ident {
Ident::new(Name(n), s)
}
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 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));
}
}