added MTWT functions
This commit is contained in:
@@ -24,6 +24,7 @@ use core::str;
|
||||
use core::to_bytes;
|
||||
use core::vec;
|
||||
|
||||
|
||||
pub fn path_name_i(idents: &[ident], intr: @token::ident_interner) -> ~str {
|
||||
// FIXME: Bad copies (#2543 -- same for everything else that says "bad")
|
||||
str::connect(idents.map(|i| copy *intr.get(*i)), ~"::")
|
||||
@@ -587,6 +588,280 @@ pub enum Privacy {
|
||||
Public
|
||||
}
|
||||
|
||||
// HYGIENE FUNCTIONS
|
||||
|
||||
/// Construct an identifier with the given repr and an empty context:
|
||||
pub fn mk_ident(repr: uint) -> ident { ident {repr: repr, ctxt: 0}}
|
||||
|
||||
/// Extend a syntax context with a given mark
|
||||
pub fn mk_mark (m:Mrk,ctxt:SyntaxContext,table:&mut SCTable)
|
||||
-> SyntaxContext {
|
||||
idx_push(table,Mark(m,ctxt))
|
||||
}
|
||||
|
||||
/// Extend a syntax context with a given rename
|
||||
pub fn mk_rename (id:ident, to:Name, tail:SyntaxContext, table: &mut SCTable)
|
||||
-> SyntaxContext {
|
||||
idx_push(table,Rename(id,to,tail))
|
||||
}
|
||||
|
||||
/// Make a fresh syntax context table with EmptyCtxt in slot zero
|
||||
pub fn mk_sctable() -> SCTable { ~[EmptyCtxt] }
|
||||
|
||||
/// Add a value to the end of a vec, return its index
|
||||
fn idx_push<T>(vec: &mut ~[T], +val: T) -> uint {
|
||||
vec.push(val);
|
||||
vec.len() - 1
|
||||
}
|
||||
|
||||
/// Resolve a syntax object to a name, per MTWT.
|
||||
pub fn resolve (id : ident, table : &SCTable) -> Name {
|
||||
match table[id.ctxt] {
|
||||
EmptyCtxt => id.repr,
|
||||
// ignore marks here:
|
||||
Mark(_,subctxt) => resolve (ident{repr:id.repr, ctxt: subctxt},table),
|
||||
// do the rename if necessary:
|
||||
Rename(ident{repr,ctxt},toname,subctxt) => {
|
||||
// this could be cached or computed eagerly:
|
||||
let resolvedfrom = resolve(ident{repr:repr,ctxt:ctxt},table);
|
||||
let resolvedthis = resolve(ident{repr:id.repr,ctxt:subctxt},table);
|
||||
if ((resolvedthis == resolvedfrom)
|
||||
&& (marksof (ctxt,resolvedthis,table)
|
||||
== marksof (subctxt,resolvedthis,table))) {
|
||||
toname
|
||||
} else {
|
||||
resolvedthis
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the marks associated with a syntax context.
|
||||
// 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) -> ~[Mrk] {
|
||||
let mut result = ~[];
|
||||
let mut loopvar = ctxt;
|
||||
loop {
|
||||
match table[loopvar] {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 ~[uint], mark: uint) {
|
||||
if ((marks.len() > 0) && (getLast(marks) == mark)) {
|
||||
marks.pop();
|
||||
} 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: &~[Mrk]) -> uint {
|
||||
*arr.last()
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use ast::*;
|
||||
use super::*;
|
||||
use core::io;
|
||||
|
||||
#[test] fn xorpush_test () {
|
||||
let mut s = ~[];
|
||||
xorPush(&mut s,14);
|
||||
assert_eq!(s,~[14]);
|
||||
xorPush(&mut s,14);
|
||||
assert_eq!(s,~[]);
|
||||
xorPush(&mut s,14);
|
||||
assert_eq!(s,~[14]);
|
||||
xorPush(&mut s,15);
|
||||
assert_eq!(s,~[14,15]);
|
||||
xorPush (&mut s,16);
|
||||
assert_eq! (s,~[14,15,16]);
|
||||
xorPush (&mut s,16);
|
||||
assert_eq! (s,~[14,15]);
|
||||
xorPush (&mut s,15);
|
||||
assert_eq! (s,~[14]);
|
||||
}
|
||||
|
||||
// convert a list of uints to an @~[ident]
|
||||
// (ignores the interner completely)
|
||||
fn uints_to_idents (uints: &~[uint]) -> @~[ident] {
|
||||
@uints.map(|u|{ ident {repr:*u, ctxt: empty_ctxt} })
|
||||
}
|
||||
|
||||
fn id (u : uint, s: SyntaxContext) -> ident {
|
||||
ident{repr:u, ctxt: s}
|
||||
}
|
||||
|
||||
// because of the SCTable, I now need a tidy way of
|
||||
// creating syntax objects. Sigh.
|
||||
#[deriving(Eq)]
|
||||
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 : ~[TestSC], tail: SyntaxContext, table : &mut SCTable)
|
||||
-> SyntaxContext {
|
||||
tscs.foldr(tail, |tsc : &TestSC,tail : SyntaxContext|
|
||||
{match *tsc {
|
||||
M(mrk) => mk_mark(mrk,tail,table),
|
||||
R(ident,name) => mk_rename(ident,name,tail,table)}})
|
||||
}
|
||||
|
||||
// gather a SyntaxContext back into a vector of TestSCs
|
||||
fn refold_test_sc(mut sc: SyntaxContext, table : &SCTable) -> ~[TestSC] {
|
||||
let mut result = ~[];
|
||||
loop {
|
||||
match table[sc] {
|
||||
EmptyCtxt => {return result;},
|
||||
Mark(mrk,tail) => {
|
||||
result.push(M(mrk));
|
||||
sc = tail;
|
||||
loop;
|
||||
},
|
||||
Rename(id,name,tail) => {
|
||||
result.push(R(id,name));
|
||||
sc = tail;
|
||||
loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test] fn test_unfold_refold(){
|
||||
let mut t = mk_sctable();
|
||||
|
||||
let test_sc = ~[M(3),R(id(101,0),14),M(9)];
|
||||
assert_eq!(unfold_test_sc(test_sc,empty_ctxt,&mut t),3);
|
||||
assert_eq!(t[1],Mark(9,0));
|
||||
assert_eq!(t[2],Rename(id(101,0),14,1));
|
||||
assert_eq!(t[3],Mark(3,2));
|
||||
assert_eq!(refold_test_sc(3,&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:~[Mrk],tail:SyntaxContext,table: &mut SCTable) -> SyntaxContext {
|
||||
mrks.foldr(tail, |mrk:&Mrk,tail:SyntaxContext|
|
||||
{mk_mark(*mrk,tail,table)})
|
||||
}
|
||||
|
||||
#[test] fn unfold_marks_test() {
|
||||
let mut t = ~[EmptyCtxt];
|
||||
|
||||
assert_eq!(unfold_marks(~[3,7],empty_ctxt,&mut t),2);
|
||||
assert_eq!(t[1],Mark(7,0));
|
||||
assert_eq!(t[2],Mark(3,1));
|
||||
}
|
||||
|
||||
#[test] fn test_marksof () {
|
||||
let stopname = 242;
|
||||
let name1 = 243;
|
||||
let mut t = mk_sctable();
|
||||
assert_eq!(marksof (empty_ctxt,stopname,&t),~[]);
|
||||
// FIXME #5074: ANF'd to dodge nested calls
|
||||
{ let ans = unfold_marks(~[4,98],empty_ctxt,&mut t);
|
||||
assert_eq! (marksof (ans,stopname,&t),~[4,98]);}
|
||||
// does xoring work?
|
||||
{ let ans = unfold_marks(~[5,5,16],empty_ctxt,&mut t);
|
||||
assert_eq! (marksof (ans,stopname,&t), ~[16]);}
|
||||
// does nested xoring work?
|
||||
{ let ans = unfold_marks(~[5,10,10,5,16],empty_ctxt,&mut t);
|
||||
assert_eq! (marksof (ans, stopname,&t), ~[16]);}
|
||||
// rename where stop doesn't match:
|
||||
{ let chain = ~[M(9),
|
||||
R(id(name1,
|
||||
mk_mark (4, empty_ctxt,&mut t)),
|
||||
100101102),
|
||||
M(14)];
|
||||
let ans = unfold_test_sc(chain,empty_ctxt,&mut t);
|
||||
assert_eq! (marksof (ans, stopname, &t), ~[9,14]);}
|
||||
// rename where stop does match
|
||||
{ let name1sc = mk_mark(4, empty_ctxt, &mut t);
|
||||
let chain = ~[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), ~[9]); }
|
||||
}
|
||||
|
||||
|
||||
#[test] fn resolve_tests () {
|
||||
let a = 40;
|
||||
let mut t = mk_sctable();
|
||||
// - ctxt is MT
|
||||
assert_eq!(resolve(id(a,empty_ctxt),&t),a);
|
||||
// - simple ignored marks
|
||||
{ let sc = unfold_marks(~[1,2,3],empty_ctxt,&mut t);
|
||||
assert_eq!(resolve(id(a,sc),&t),a);}
|
||||
// - orthogonal rename where names don't match
|
||||
{ let sc = unfold_test_sc(~[R(id(50,empty_ctxt),51),M(12)],empty_ctxt,&mut t);
|
||||
assert_eq!(resolve(id(a,sc),&t),a);}
|
||||
// - rename where names do match, but marks don't
|
||||
{ let sc1 = mk_mark(1,empty_ctxt,&mut t);
|
||||
let sc = unfold_test_sc(~[R(id(a,sc1),50),
|
||||
M(1),
|
||||
M(2)],
|
||||
empty_ctxt,&mut t);
|
||||
assert_eq!(resolve(id(a,sc),&t), a);}
|
||||
// - rename where names and marks match
|
||||
{ let sc1 = unfold_test_sc(~[M(1),M(2)],empty_ctxt,&mut t);
|
||||
let sc = unfold_test_sc(~[R(id(a,sc1),50),M(1),M(2)],empty_ctxt,&mut t);
|
||||
assert_eq!(resolve(id(a,sc),&t), 50); }
|
||||
// - rename where names and marks match by literal sharing
|
||||
{ let sc1 = unfold_test_sc(~[M(1),M(2)],empty_ctxt,&mut t);
|
||||
let sc = unfold_test_sc(~[R(id(a,sc1),50)],sc1,&mut t);
|
||||
assert_eq!(resolve(id(a,sc),&t), 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:
|
||||
io::println("about to run bad test");
|
||||
{ let sc = unfold_test_sc(~[R(id(a,empty_ctxt),50),
|
||||
R(id(a,empty_ctxt),51)],
|
||||
empty_ctxt,&mut t);
|
||||
assert_eq!(resolve(id(a,sc),&t), 51); }
|
||||
// the simplest double-rename:
|
||||
{ let a_to_a50 = mk_rename(id(a,empty_ctxt),50,empty_ctxt,&mut t);
|
||||
let a50_to_a51 = mk_rename(id(a,a_to_a50),51,a_to_a50,&mut t);
|
||||
assert_eq!(resolve(id(a,a50_to_a51),&t),51);
|
||||
// mark on the outside doesn't stop rename:
|
||||
let sc = mk_mark(9,a50_to_a51,&mut t);
|
||||
assert_eq!(resolve(id(a,sc),&t),51);
|
||||
// but mark on the inside does:
|
||||
let a50_to_a51_b = unfold_test_sc(~[R(id(a,a_to_a50),51),
|
||||
M(9)],
|
||||
a_to_a50,
|
||||
&mut t);
|
||||
assert_eq!(resolve(id(a,a50_to_a51_b),&t),50);}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
// fill-column: 78;
|
||||
|
||||
Reference in New Issue
Block a user