// force-host
// no-prefer-dynamic

#![crate_type = "proc-macro"]

extern crate proc_macro;
use proc_macro::{Ident, Group, TokenStream, TokenTree as Tt};

// This constant has to be above the ALLOCATING_ALGO_THRESHOLD
// constant in inherent_impls_overlap.rs
const REPEAT_COUNT: u32 = 501;

#[proc_macro]
/// Repeats the input many times, while replacing idents
/// named "IDENT" with "id_$v", where v is a counter.
pub fn repeat_with_idents(input: TokenStream) -> TokenStream {
    let mut res = Vec::new();
    fn visit_stream(res: &mut Vec<Tt>, stream :TokenStream, v: u32) {
        let mut stream_iter = stream.into_iter();
        while let Some(tt) = stream_iter.next() {
            match tt {
                Tt::Group(group) => {
                    let tt = Tt::Group(visit_group(group, v));
                    res.push(tt);
                },
                Tt::Ident(id) => {
                    let id = if &id.to_string() == "IDENT" {
                        Ident::new(&format!("id_{}", v), id.span())
                    } else {
                        id
                    };
                    res.push(Tt::Ident(id));
                },
                Tt::Punct(p) => {
                    res.push(Tt::Punct(p));
                },
                Tt::Literal(lit) => {
                    res.push(Tt::Literal(lit));
                },
            }
        }
    }
    fn visit_group(group :Group, v: u32) -> Group {
        let mut res = Vec::new();
        visit_stream(&mut res, group.stream(), v);
        let stream = res.into_iter().collect();
        let delim = group.delimiter();
        Group::new(delim, stream)
    }
    for v in 0 .. REPEAT_COUNT {
        visit_stream(&mut res, input.clone(), v)
    }
    res.into_iter().collect()
}
