use parse::token;
use opt_vec::OptVec;
+// this file defines an ast_fold trait for objects that can perform
+// a "fold" on Rust ASTs. It also contains a structure that implements
+// that trait, and a "default_fold" whose fields contain closures
+// that perform "default traversals", visiting all of the sub-elements
+// and re-assembling the result. The "fun_to_ident_folder" in the
+// test module provides a simple example of creating a very simple
+// fold that only looks at identifiers.
+
pub trait ast_fold {
fn fold_crate(@self, &Crate) -> Crate;
fn fold_view_item(@self, &view_item) -> view_item;
fn fold_ident(@self, Ident) -> Ident;
fn fold_path(@self, &Path) -> Path;
fn fold_local(@self, @Local) -> @Local;
+ fn fold_mac(@self, &mac) -> mac;
fn map_exprs(@self, @fn(@Expr) -> @Expr, &[@Expr]) -> ~[@Expr];
fn new_id(@self, NodeId) -> NodeId;
fn new_span(@self, Span) -> Span;
fold_ident: @fn(Ident, @ast_fold) -> Ident,
fold_path: @fn(&Path, @ast_fold) -> Path,
fold_local: @fn(@Local, @ast_fold) -> @Local,
+ fold_mac: @fn(&mac_, Span, @ast_fold) -> (mac_, Span),
map_exprs: @fn(@fn(@Expr) -> @Expr, &[@Expr]) -> ~[@Expr],
new_id: @fn(NodeId) -> NodeId,
new_span: @fn(Span) -> Span
}
}
-//used in noop_fold_expr, and possibly elsewhere in the future
-fn fold_mac_(m: &mac, fld: @ast_fold) -> mac {
- Spanned {
- node: match m.node {
- mac_invoc_tt(ref p,ref tts) =>
- mac_invoc_tt(fld.fold_path(p),
- fold_tts(*tts,fld))
- },
- span: fld.new_span(m.span)
- }
-}
-
-fn fold_tts(tts : &[token_tree], fld: @ast_fold) -> ~[token_tree] {
+// build a new vector of tts by appling the ast_fold's fold_ident to
+// all of the identifiers in the token trees.
+pub fn fold_tts(tts : &[token_tree], f : @ast_fold) -> ~[token_tree] {
do tts.map |tt| {
match *tt {
tt_tok(span, ref tok) =>
- tt_tok(span,maybe_fold_ident(tok,fld)),
+ tt_tok(span,maybe_fold_ident(tok,f)),
tt_delim(ref tts) =>
- tt_delim(@mut fold_tts(**tts, fld)),
+ tt_delim(@mut fold_tts(**tts, f)),
tt_seq(span, ref pattern, ref sep, is_optional) =>
tt_seq(span,
- @mut fold_tts(**pattern, fld),
- sep.map(|tok|maybe_fold_ident(tok,fld)),
+ @mut fold_tts(**pattern, f),
+ sep.map(|tok|maybe_fold_ident(tok,f)),
is_optional),
tt_nonterminal(sp,ref ident) =>
- tt_nonterminal(sp,fld.fold_ident(*ident))
+ tt_nonterminal(sp,f.fold_ident(*ident))
}
}
}
// apply ident folder if it's an ident, otherwise leave it alone
-fn maybe_fold_ident(t: &token::Token, fld: @ast_fold) -> token::Token {
+fn maybe_fold_ident(t : &token::Token, f: @ast_fold) -> token::Token {
match *t {
token::IDENT(id,followed_by_colons) =>
- token::IDENT(fld.fold_ident(id),followed_by_colons),
+ token::IDENT(f.fold_ident(id),followed_by_colons),
_ => (*t).clone()
}
}
}
fn noop_fold_view_item(vi: &view_item_, _fld: @ast_fold) -> view_item_ {
+ // FIXME #7654: doesn't iterate over idents in a view_item_use
return /* FIXME (#2543) */ (*vi).clone();
}
)
}
item_mac(ref m) => {
- // FIXME #2888: we might actually want to do something here.
- // ... okay, we're doing something. It would probably be nicer
- // to add something to the ast_fold trait, but I'll defer
- // that work.
- item_mac(fold_mac_(m,fld))
+ item_mac(fld.fold_mac(m))
}
}
}
}
fn noop_fold_stmt(s: &Stmt_, fld: @ast_fold) -> Option<Stmt_> {
- let fold_mac = |x| fold_mac_(x, fld);
match *s {
StmtDecl(d, nid) => {
match fld.fold_decl(d) {
StmtSemi(e, nid) => {
Some(StmtSemi(fld.fold_expr(e), fld.new_id(nid)))
}
- StmtMac(ref mac, semi) => Some(StmtMac(fold_mac(mac), semi))
+ StmtMac(ref mac, semi) => Some(StmtMac(fld.fold_mac(mac), semi))
}
}
}
}
+// lift a function in ast-thingy X fold -> ast-thingy to a function
+// in (ast-thingy X span X fold) -> (ast-thingy X span). Basically,
+// carries the span around.
+// It seems strange to me that the call to new_fold doesn't happen
+// here but instead in the impl down below.... probably just an
+// accident?
pub fn wrap<T>(f: @fn(&T, @ast_fold) -> T)
-> @fn(&T, Span, @ast_fold) -> (T, Span) {
let result: @fn(&T, Span, @ast_fold) -> (T, Span) = |x, s, fld| {
}
let fold_field = |x| fold_field_(x, fld);
- let fold_mac = |x| fold_mac_(x, fld);
-
match *e {
ExprVstore(e, v) => {
ExprVstore(fld.fold_expr(e), v)
ExprDoBody(f) => ExprDoBody(fld.fold_expr(f)),
ExprLit(_) => (*e).clone(),
ExprCast(expr, ref ty) => {
- ExprCast(fld.fold_expr(expr), (*ty).clone())
+ ExprCast(fld.fold_expr(expr), fld.fold_ty(ty))
}
ExprAddrOf(m, ohs) => ExprAddrOf(m, fld.fold_expr(ohs)),
ExprIf(cond, ref tr, fl) => {
ExprWhile(cond, ref body) => {
ExprWhile(fld.fold_expr(cond), fld.fold_block(body))
}
- ExprForLoop(pat, iter, ref body) => {
+ ExprForLoop(pat, iter, ref body, opt_ident) => {
ExprForLoop(fld.fold_pat(pat),
- fld.fold_expr(iter),
- fld.fold_block(body))
+ fld.fold_expr(iter),
+ fld.fold_block(body),
+ opt_ident.map_move(|x| fld.fold_ident(x)))
}
ExprLoop(ref body, opt_ident) => {
ExprLoop(
.. (*a).clone()
})
}
- ExprMac(ref mac) => ExprMac(fold_mac(mac)),
+ ExprMac(ref mac) => ExprMac(fld.fold_mac(mac)),
ExprStruct(ref path, ref fields, maybe_expr) => {
ExprStruct(
fld.fold_path(path),
}
pub fn noop_fold_ty(t: &ty_, fld: @ast_fold) -> ty_ {
- let fold_mac = |x| fold_mac_(x, fld);
fn fold_mt(mt: &mt, fld: @ast_fold) -> mt {
mt {
ty: ~fld.fold_ty(mt.ty),
)
}
ty_typeof(e) => ty_typeof(fld.fold_expr(e)),
- ty_mac(ref mac) => ty_mac(fold_mac(mac))
+ ty_mac(ref mac) => ty_mac(fld.fold_mac(mac))
}
}
}
}
+// the default macro traversal. visit the path
+// using fold_path, and the tts using fold_tts,
+// and the span using new_span
+fn noop_fold_mac(m: &mac_, fld: @ast_fold) -> mac_ {
+ match *m {
+ mac_invoc_tt(ref p,ref tts,ctxt) =>
+ mac_invoc_tt(fld.fold_path(p),
+ fold_tts(*tts,fld),
+ ctxt)
+ }
+}
+
+
/* temporarily eta-expand because of a compiler bug with using `fn<T>` as a
value */
fn noop_map_exprs(f: @fn(@Expr) -> @Expr, es: &[@Expr]) -> ~[@Expr] {
fold_ident: noop_fold_ident,
fold_path: noop_fold_path,
fold_local: noop_fold_local,
+ fold_mac: wrap(noop_fold_mac),
map_exprs: noop_map_exprs,
new_id: noop_id,
new_span: noop_span,
fn fold_local(@self, x: @Local) -> @Local {
(self.fold_local)(x, self as @ast_fold)
}
+ fn fold_mac(@self, x: &mac) -> mac {
+ let (n, s) = (self.fold_mac)(&x.node, x.span, self as @ast_fold);
+ Spanned { node: n, span: (self.new_span)(s) }
+ }
fn map_exprs(@self,
f: @fn(@Expr) -> @Expr,
e: &[@Expr])
}
}
+// brson agrees with me that this function's existence is probably
+// not a good or useful thing.
pub fn make_fold(afp: ast_fold_fns) -> @ast_fold {
afp as @ast_fold
}
token::get_ident_interner()),
~"zz!zz((zz$zz:zz$(zz $zz:zz)zz+=>(zz$(zz$zz$zz)+)))");
}
+
+ // and in cast expressions... this appears to be an existing bug.
+ #[test] fn ident_transformation_in_types () {
+ let zz_fold = fun_to_ident_folder(to_zz());
+ let ast = string_to_crate(@"fn a() {let z = 13 as int;}");
+ assert_pred!(matches_codepattern,
+ "matches_codepattern",
+ pprust::to_str(&zz_fold.fold_crate(ast),fake_print_crate,
+ token::get_ident_interner()),
+ ~"fn zz(){let zz=13 as zz;}");
+ }
}