标记树聚束

  1. macro_rules! call_a_or_b_on_tail {
  2. ((a: $a:expr, b: $b:expr), a $($tail:tt)*) => {
  3. $a(stringify!($($tail)*))
  4. };
  5. ((a: $a:expr, b: $b:expr), b $($tail:tt)*) => {
  6. $b(stringify!($($tail)*))
  7. };
  8. ($ab:tt, $_skip:tt $($tail:tt)*) => {
  9. call_a_or_b_on_tail!($ab, $($tail)*)
  10. };
  11. }
  12. fn compute_len(s: &str) -> Option<usize> {
  13. Some(s.len())
  14. }
  15. fn show_tail(s: &str) -> Option<usize> {
  16. println!("tail: {:?}", s);
  17. None
  18. }
  19. fn main() {
  20. assert_eq!(
  21. call_a_or_b_on_tail!(
  22. (a: compute_len, b: show_tail),
  23. 规则的 递归部分 跳过 所有这些 标记
  24. 它们 并不 关心 我们究竟 b 还是 a
  25. 只有 终结规则 关心
  26. ),
  27. None
  28. );
  29. assert_eq!(
  30. call_a_or_b_on_tail!(
  31. (a: compute_len, b: show_tail),
  32. 而现在 为了 显式 可能的路径 有两条
  33. 我们也 a 一哈: 它的 输入 应该
  34. 自我引用 因此 我们给它 一个 72),
  35. Some(72)
  36. );
  37. }

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

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

上面的例子把表达式$a$b聚束,然后作为一棵tt交由递归规则转发。随后,终结规则将这组标记打开,并访问其中的表达式。