TT 捆绑

macro_rules! call_a_or_b_on_tail {
    ((a: $a:ident, b: $b:ident), call a: $($tail:tt)*) => {
        $a(stringify!($($tail)*))
    };

    ((a: $a:ident, b: $b:ident), call b: $($tail:tt)*) => {
        $b(stringify!($($tail)*))
    };

    ($ab:tt, $_skip:tt $($tail:tt)*) => {
        call_a_or_b_on_tail!($ab, $($tail)*)
    };
}

fn compute_len(s: &str) -> Option<usize> {
    Some(s.len())
}

fn show_tail(s: &str) -> Option<usize> {
    println!("tail: {:?}", s);
    None
}

fn main() {
    assert_eq!(
        call_a_or_b_on_tail!(
            (a: compute_len, b: show_tail),
            the recursive part that skips over all these
            tokens doesn't much care whether we will call a
            or call b: only the terminal rules care.
        ),
        None
    );
    assert_eq!(
        call_a_or_b_on_tail!(
            (a: compute_len, b: show_tail),
            and now, to justify the existence of two paths
            we will also call a: its input should somehow
            be self-referential, so let's make it return
            some eighty-six!
        ),
        Some(92)
    );
}

在十分复杂的递归宏中,可能需要非常多的参数, 才足以在每层调用之间传递必要的标识符与表达式。 然而,根据实现上的差异,可能存在许多这样的中间层, 它们转发了 (forward) 这些参数,但并没有用到。

因此,将所有这些参数捆绑 (bundle) 在一起,通过分组将其放进单独一棵标记树 tt 里, 可以省事许多。这样一来,那些用不到这些参数的递归层可以直接捕获并替换这棵标记树, 而不需要把整组参数完完全全准准确确地捕获替换掉。

上面的例子把表达式 $a$b 捆绑起来, 然后作为一棵 tt 交由递归规则处理。 随后,终结规则 (terminal rules) 将这组标记解构 (destructure) , 并访问其中的表达式。