Rustの宣言的マクロ内でマクロを定義する

# 概要

Rustの宣言的マクロ（`macro_rules!`で定義するマクロ）内でマクロを定義するとき、繰り返しパターンをそのまま書くことができない。
一般的には手続き的マクロを使用すれば可能であるが、宣言的マクロのままでも`$`をエスケープすれば書くことができる。

すべてのコードは、Rust 1.52.1で動作を確認している。

# マクロ内マクロ定義

次のように、繰り返しパターンの無いマクロであれば通常通りマクロ内に定義できる。

```rust
macro_rules! outer {
    ($out:expr) => {
        macro_rules! empty { () => { $out } }
        macro_rules! one { ($e:expr) => { $out + $e } }
    }
}

outer!(3);
// 展開後
/* macro_rules! empty { () => { 3 } }
   macro_rules! one { ($ e : expr) => { 3 + $ e } } */

fn main() {
    assert_eq!(empty!(), 3);
    assert_eq!(one!(2), 5); // 3 + 2 == 5
}
```

# ダメなパターン

一方、繰り返しパターンのあるマクロをマクロ内に定義しようとすると、コンパイルエラーになってしまう。

```rust
macro_rules! outer {
    ($out:expr) => {
        macro_rules! multiple { ($($e:expr),*) => { $out $(+ $e)* } }
    }
}

outer!(3);
// 次のように展開されてほしい
/* macro_rules! multiple { ($($e:expr),*) => { 3 $(+ $e)* } } */
```

```log
error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
 --> src/lib.rs:3:35
  |
3 |         macro_rules! multiple { ($($e:expr),*) => { $out $(+ $e)* } }
  |                                   ^^^^^^^^^
```

# 回避策

`$`を token tree として受け取り（`$dol:tt`）、1段階展開後に残したい内部マクロの`$`をエスケープする（`$dol`にする）。

```rust
macro_rules! outer {
    // escape `$` by `$dol`
    (@inner ($dol:tt) $out:expr) => {
        /* previous:
        macro_rules! multiple { ($   ($    e:expr),*) => { $out $   (+ $    e)* } } */
        macro_rules! multiple { ($dol($dol e:expr),*) => { $out $dol(+ $dol e)* } }
    };

    // entry point
    ($out:expr) => {
        outer!(@inner ($) $out);
    }
}

outer!(3);
// 展開後
/* macro_rules! multiple { ($($e:expr),*) => { 3 $(+ $e)* } } */

fn main() {
    assert_eq!(multiple!(), 3);
    assert_eq!(multiple!(2), 5); // 3 + 2 == 5
    assert_eq!(multiple!(2, 4), 9); // 3 + 2 + 4 == 9
}
```

# 使用例
## 標準出力をバッファリングするマクロを用意するマクロ

main関数の先頭に`prepare!();`と書くだけで`print(ln)!`のバッファリング版マクロ`bprint(ln)!`を使用できるようにする。

```rust
#[macro_export]
macro_rules! prepare {
    (@inner $dol:tt) => {
        #[allow(unused_imports)]
        use ::std::io::Write as _;
        let stdout = ::std::io::stdout();
        let mut out = ::std::io::BufWriter::new(stdout.lock());
        #[allow(unused_macros)]
        macro_rules! bprint {
            ($dol($dol arg:tt)*) => {
                ::std::write!(out, $dol($dol arg)*).expect("failed printing to buffered stdout")
            }
        }
        #[allow(unused_macros)]
        macro_rules! bprintln {
            ($dol($dol arg:tt)*) => {
                ::std::writeln!(out, $dol($dol arg)*).expect("failed printing to buffered stdout")
            }
        }
        #[allow(unused_macros)]
        macro_rules! bflush {
            () => { out.flush() }
        }
    };
    () => {
        $crate::prepare!(@inner $);
    }
}

fn main() {
    prepare!();
    bprintln!("{}", 100);
    bflush!().ok();
}
```

## 再帰関数を自動でメモ化するマクロ
再帰部で`func(map, arg...)` と呼ぶところをマクロで第一引数を省略する。
メモの変数名を持たなくてよいので呼び出し側がすっきり書ける。

```rust
#[macro_export]
macro_rules! memorize {
    // escape `$` by `$dol`
    (@macro_def ($dol:tt) $func:ident $map:ident) => {
        #[allow(unused_macros)]
        macro_rules! $func {
            ($dol ($dol args:expr),*) => {
                $func($map, $dol ($dol args,)* )
            }
        }
    };

    (@inner [$map:ident, $Map:ty] fn $func:ident ($($args:ident: $argsty:ty),*) -> $ret:ty $body:block) => {
        fn $func(map: &mut $Map, $($args: $argsty),*) -> $ret {
            if let Some(value) = map.get(&($($args,)*)).cloned() {
                value
            } else {
                let value = {
                    $crate::memorize!(@macro_def ($) $func map);
                    $body
                };
                map.insert(($($args,)*), value.clone());
                value
            }
        }
        let mut $map: $Map = Default::default();
        #[allow(unused_mut)]
        let mut $func = |$($args: $argsty,)*| -> $ret { $func(&mut $map, $($args),*) };
    };

    // entry point
    (fn $func:ident ($($args:ident: $argsty:ty),* $(,)?) -> $ret:ty $body:block) => {
        $crate::memorize!(
            @inner [__memorize_map, ::std::collections::HashMap<($($argsty,)*), $ret>]
            fn $func ($($args: $argsty),*) -> $ret $body
        )
    };
}

fn main() {
    memorize!(
        fn fib(n: usize) -> usize {
            if n < 2 {
                n
            } else {
                fib!(n - 1) + fib!(n - 2)
            }
        }
    );
    assert_eq!(fib(40), 102334155);

    memorize!(
        fn comb(n: usize, r: usize) -> usize {
            if r > n {
                0
            } else if r == 0 || r == n {
                1
            } else {
                comb!(n - 1, r) + comb!(n - 1, r - 1)
            }
        }
    );
    assert_eq!(comb(30, 12), 86493225);
}
```

勿論、手続き的マクロを使用する方が便利なものが書ける。

# 参考
エスケープするアイディアは[defmac](https://docs.rs/defmac/0.2.1/defmac/)から得た。