serde-jsonのjson!マクロを読む

# serde-jsonのjson!マクロ?
Rustでシリアライズ・デシリアライズをするためのライブラリ「serde」の子ライブラリで、JSONのシリアライズ・デシリアライズをする「serde-json」には、JSONを解釈して`serde_json::Value`型にするためのマクロがついています。

```rs
use serde_json::json;

fn main() {
     let my_json = json!{
        {"status": 200,
         "foo": {
             "aaa": 123,
             "bar": [1, 3, "str", ["a", "b", 2]]
         },
         "result": null,
         "bool": [true, false],
         "float": 1.23
        }
     };
    println!("{}", my_json);
    /* 結果
{"bool":[true,false],"float":1.23,"foo":{"aaa":123,"bar":[1,3,"str",["a","b",2]]},"result":null,"status":200}
    */

    // デバッグ表示
    println!("{:?}", my_json);
    /* 結果
Object({"bool": Array([Bool(true), Bool(false)]), "float": Number(1.23), "foo": Object({"aaa": Number(123), "bar": Array([Number(1), Number(3), String("str"), Array([String("a"), String("b"), Number(2)])])}), "result": Null, "status": Number(200)})
    */
}
```

「構文解析とかがんばってるんだろうなー」と思いきや、案外、力技でなんとかしていたので、今回はそれを解説していきます。

Rustの基本的な文法や、Rustでの`macro_rules!`を使ったマクロ定義については、ある程度は理解している読者を想定しています。
マクロについて詳しくない方は、[こちら](https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/macros.html)などが参考になります。

# `serde_json::Value`型
JavaScriptをはじめとする動的型付け言語では、普通にあることなのですが、同じ配列や連想配列に、整数や文字列など様々な型のものが混ざっているのは、Rustのような静的型付け言語では基本的にはご法度です。
けれど、実際そういうデータ形式になっているので、`enum`型でなんとかしています。

```rs
#[derive(Clone, PartialEq)]
pub enum Value {
    Null,
    Bool(bool),
    Number(Number),
    String(String),
    Array(Vec<Value>),
    Object(Map<String, Value>),
}
```

(引用元: [src/value/mod.rs](https://github.com/serde-rs/json/blob/master/src/value/mod.rs)より、docstringを削除)

`Map`とは? `Number`とは? とか思うかもしれませんが、あまり深く気にせずいきましょう。(featureによって切り替わりますが、デフォルトでは、`Map`は`BTreeMap`を使っていて、`Number`はさらなる`enum`型になっています)

`json!`マクロは、`Value`型を作るためのマクロです。

# `json_internal!`マクロ

`json!`マクロ自体は実質なにもしておらず、`json_internal!`マクロががんばっています。

```rs
macro_rules! json {
    // Hide distracting implementation details from the generated rustdoc.
    ($($json:tt)+) => {
        json_internal!($($json)+)
    };
}
```

(引用元: [src/macros.rs](https://github.com/serde-rs/json/blob/master/src/macros.rs) 以下も同じ)

なので、`json_internal!`マクロを読みます。

長いですが、一旦全部引用します。

```rs
macro_rules! json_internal {
    //////////////////////////////////////////////////////////////////////////
    // TT muncher for parsing the inside of an array [...]. Produces a vec![...]
    // of the elements.
    //
    // Must be invoked as: json_internal!(@array [] $($tt)*)
    //////////////////////////////////////////////////////////////////////////

    // Done with trailing comma.
    (@array [$($elems:expr,)*]) => {
        json_internal_vec![$($elems,)*]
    };

    // Done without trailing comma.
    (@array [$($elems:expr),*]) => {
        json_internal_vec![$($elems),*]
    };

    // Next element is `null`.
    (@array [$($elems:expr,)*] null $($rest:tt)*) => {
        json_internal!(@array [$($elems,)* json_internal!(null)] $($rest)*)
    };

    // Next element is `true`.
    (@array [$($elems:expr,)*] true $($rest:tt)*) => {
        json_internal!(@array [$($elems,)* json_internal!(true)] $($rest)*)
    };

    // Next element is `false`.
    (@array [$($elems:expr,)*] false $($rest:tt)*) => {
        json_internal!(@array [$($elems,)* json_internal!(false)] $($rest)*)
    };

    // Next element is an array.
    (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
        json_internal!(@array [$($elems,)* json_internal!([$($array)*])] $($rest)*)
    };

    // Next element is a map.
    (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
        json_internal!(@array [$($elems,)* json_internal!({$($map)*})] $($rest)*)
    };

    // Next element is an expression followed by comma.
    (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
        json_internal!(@array [$($elems,)* json_internal!($next),] $($rest)*)
    };

    // Last element is an expression with no trailing comma.
    (@array [$($elems:expr,)*] $last:expr) => {
        json_internal!(@array [$($elems,)* json_internal!($last)])
    };

    // Comma after the most recent element.
    (@array [$($elems:expr),*] , $($rest:tt)*) => {
        json_internal!(@array [$($elems,)*] $($rest)*)
    };

    // Unexpected token after most recent element.
    (@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
        json_unexpected!($unexpected)
    };

    //////////////////////////////////////////////////////////////////////////
    // TT muncher for parsing the inside of an object {...}. Each entry is
    // inserted into the given map variable.
    //
    // Must be invoked as: json_internal!(@object $map () ($($tt)*) ($($tt)*))
    //
    // We require two copies of the input tokens so that we can match on one
    // copy and trigger errors on the other copy.
    //////////////////////////////////////////////////////////////////////////

    // Done.
    (@object $object:ident () () ()) => {};

    // Insert the current entry followed by trailing comma.
    (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
        let _ = $object.insert(($($key)+).into(), $value);
        json_internal!(@object $object () ($($rest)*) ($($rest)*));
    };

    // Current entry followed by unexpected token.
    (@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
        json_unexpected!($unexpected);
    };

    // Insert the last entry without trailing comma.
    (@object $object:ident [$($key:tt)+] ($value:expr)) => {
        let _ = $object.insert(($($key)+).into(), $value);
    };

    // Next value is `null`.
    (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!(null)) $($rest)*);
    };

    // Next value is `true`.
    (@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!(true)) $($rest)*);
    };

    // Next value is `false`.
    (@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!(false)) $($rest)*);
    };

    // Next value is an array.
    (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!([$($array)*])) $($rest)*);
    };

    // Next value is a map.
    (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!({$($map)*})) $($rest)*);
    };

    // Next value is an expression followed by comma.
    (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!($value)) , $($rest)*);
    };

    // Last value is an expression with no trailing comma.
    (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!($value)));
    };

    // Missing value for last entry. Trigger a reasonable error message.
    (@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
        // "unexpected end of macro invocation"
        json_internal!();
    };

    // Missing colon and value for last entry. Trigger a reasonable error
    // message.
    (@object $object:ident ($($key:tt)+) () $copy:tt) => {
        // "unexpected end of macro invocation"
        json_internal!();
    };

    // Misplaced colon. Trigger a reasonable error message.
    (@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
        // Takes no arguments so "no rules expected the token `:`".
        json_unexpected!($colon);
    };

    // Found a comma inside a key. Trigger a reasonable error message.
    (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
        // Takes no arguments so "no rules expected the token `,`".
        json_unexpected!($comma);
    };

    // Key is fully parenthesized. This avoids clippy double_parens false
    // positives because the parenthesization may be necessary here.
    (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*));
    };

    // Munch a token into the current key.
    (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*));
    };

    //////////////////////////////////////////////////////////////////////////
    // The main implementation.
    //
    // Must be invoked as: json_internal!($($json)+)
    //////////////////////////////////////////////////////////////////////////

    (null) => {
        $crate::Value::Null
    };

    (true) => {
        $crate::Value::Bool(true)
    };

    (false) => {
        $crate::Value::Bool(false)
    };

    ([]) => {
        $crate::Value::Array(json_internal_vec![])
    };

    ([ $($tt:tt)+ ]) => {
        $crate::Value::Array(json_internal!(@array [] $($tt)+))
    };

    ({}) => {
        $crate::Value::Object($crate::Map::new())
    };

    ({ $($tt:tt)+ }) => {
        $crate::Value::Object({
            let mut object = $crate::Map::new();
            json_internal!(@object object () ($($tt)+) ($($tt)+));
            object
        })
    };

    // Any Serialize type: numbers, strings, struct literals, variables etc.
    // Must be below every other rule.
    ($other:expr) => {
        $crate::to_value(&$other).unwrap()
    };
}
```

## The main implementation を読む
恐らく、ここから読むのが一番簡単なので、読んでいきます。


```rs
//////////////////////////////////////////////////////////////////////////
    // The main implementation.
    //
    // Must be invoked as: json_internal!($($json)+)
    //////////////////////////////////////////////////////////////////////////

    (null) => {
        $crate::Value::Null
    };

    (true) => {
        $crate::Value::Bool(true)
    };

    (false) => {
        $crate::Value::Bool(false)
    };

    ([]) => {
        $crate::Value::Array(json_internal_vec![])
    };

    ([ $($tt:tt)+ ]) => {
        $crate::Value::Array(json_internal!(@array [] $($tt)+))
    };

    ({}) => {
        $crate::Value::Object($crate::Map::new())
    };

    ({ $($tt:tt)+ }) => {
        $crate::Value::Object({
            let mut object = $crate::Map::new();
            json_internal!(@object object () ($($tt)+) ($($tt)+));
            object
        })
    };

    // Any Serialize type: numbers, strings, struct literals, variables etc.
    // Must be below every other rule.
    ($other:expr) => {
        $crate::to_value(&$other).unwrap()
    };
```


`null`, `true`, `false`, `[]`, `{}`は直接値を作っています。また、その他の式(数字、文字列も含む)は`Value`型に変換を試みて`unwrap`しています。

蛇足ですが、`to_value`は

```rs
pub fn to_value<T>(value: T) -> Result<Value, Error>
where
    T: Serialize,
{
    value.serialize(Serializer)
}
```

(引用元: [src/value/mod.rs](https://github.com/serde-rs/json/blob/master/src/value/mod.rs))
となっていますので、型自体が不適切な場合はコンパイル時にチェックできますが、`serialize`が実装されているけれど実行時に変換にミスった場合は`panic`します。

本題に戻ります。
やや不穏なのは

```rs
    ([ $($tt:tt)+ ]) => {
        $crate::Value::Array(json_internal!(@array [] $($tt)+))
    };
    // 略
    ({ $($tt:tt)+ }) => {
        $crate::Value::Object({
            let mut object = $crate::Map::new();
            json_internal!(@object object () ($($tt)+) ($($tt)+));
            object
        })
    };
```

の実装です。

つまり、
`json_internal!( [1, 2, 3] )`は
`Value::Array(json_internal!(@array [] 1, 2, 3))`
として、
`json_internal!( {"a": 1, "b": 2} )`は、
`Value::Object({ let mut object = Map::new(); json_internal!(@object object () ("a": 1, "b": 2) ("a": 1, "b": 2)); object })`
として再帰的に呼び出されます。

`@array`や`@object`は、Rustの文法的には特に意味がなく、単にマクロのパターンマッチのためのマーカーとして使われます。

## `@array`たちを読む

適宜、順番を入れ替えながら説明します。
`Value::Array(json_internal!(@array [] 1 , 2 , 3))`の形に変換されるのでした。これがどう変換されていくのでしょうか。

```rs
    // Next element is `null`.
    (@array [$($elems:expr,)*] null $($rest:tt)*) => {
        json_internal!(@array [$($elems,)* json_internal!(null)] $($rest)*)
    };

    // Next element is `true`.
    (@array [$($elems:expr,)*] true $($rest:tt)*) => {
        json_internal!(@array [$($elems,)* json_internal!(true)] $($rest)*)
    };

    // Next element is `false`.
    (@array [$($elems:expr,)*] false $($rest:tt)*) => {
        json_internal!(@array [$($elems,)* json_internal!(false)] $($rest)*)
    };

    // Next element is an array.
    (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
        json_internal!(@array [$($elems,)* json_internal!([$($array)*])] $($rest)*)
    };

    // Next element is a map.
    (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
        json_internal!(@array [$($elems,)* json_internal!({$($map)*})] $($rest)*)
    };

    // Next element is an expression followed by comma.
    (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
        json_internal!(@array [$($elems,)* json_internal!($next),] $($rest)*)
    };

```

ここで挙げたものはすべて
`(@array [$($elems)*] $next $($rest)*`または`(@array [$($elems)*] $next, $($rest)*`
の`$next`をValue化しながら、再帰的に`[$($elems)*]`の中に付け足していく働きをしています。

また、先頭カンマは、次のように取り除きます。

```rs
    // Comma after the most recent element.
    (@array [$($elems:expr),*] , $($rest:tt)*) => {
        json_internal!(@array [$($elems,)*] $($rest)*)
    };
```

このようにすると、`[]`に入っていない要素がどんどん減っていき、最後の要素は

```rs

    // Last element is an expression with no trailing comma.
    (@array [$($elems:expr,)*] $last:expr) => {
        json_internal!(@array [$($elems,)* json_internal!($last)])
    };
```

のように処理されます。そして、

```rs
    // Done with trailing comma.
    (@array [$($elems:expr,)*]) => {
        json_internal_vec![$($elems,)*]
    };

    // Done without trailing comma.
    (@array [$($elems:expr),*]) => {
        json_internal_vec![$($elems),*]
    };
```

によって、`Value::Array(Vec<Value>)`になります。
なお、この過程で、どれにもパターンマッチしない例が出たときは、ここに落ちるようです。(プログラマが直接`@array`を書いたとき以外、こうなることはあるのか、よく分かりませんでした)

```rs
    // Unexpected token after most recent element.
    (@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
        json_unexpected!($unexpected)
    };
```

## `@object`たちを読む

`json_internal!( {"a": 1, "b": 2} )`は、
`Value::Object({ let mut object = Map::new(); json_internal!(@object object () ("a": 1, "b": 2) ("a": 1, "b": 2)); object })`
となるのでした。
マクロで`object`に値を挿入していくんだろうな、と予想ができます。

なお、中味がなぜか2つある件については、エラー処理に使う、とのコメントが書いてあります。

`:`の直前まで、`@object object () ...`の、`()`の中に入れていきます。

```rs
    // Key is fully parenthesized. This avoids clippy double_parens false
    // positives because the parenthesization may be necessary here.
    (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*));
    };

    // Munch a token into the current key.
    (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*));
    };
```

通常は、上のパターンマッチが使われます。
下のパターンマッチのように、式ではないが連想配列のキーになるものが何なのか、私には分かりませんでした。

続いて、コロンが来て、値が来るはずです。

```rs
    // Next value is `null`.
    (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!(null)) $($rest)*);
    };

    // Next value is `true`.
    (@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!(true)) $($rest)*);
    };

    // Next value is `false`.
    (@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!(false)) $($rest)*);
    };

    // Next value is an array.
    (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!([$($array)*])) $($rest)*);
    };

    // Next value is a map.
    (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!({$($map)*})) $($rest)*);
    };

    // Next value is an expression followed by comma.
    (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!($value)) , $($rest)*);
    };

    // Last value is an expression with no trailing comma.
    (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
        json_internal!(@object $object [$($key)+] (json_internal!($value)));
    };
```

面白いのは、`(@object $object ($key) ...)`だったのが`(@object $object [$key] ...)`と、角かっこに変わっています。
角かっこのパターンマッチを見ていきましょう。

```rs
    // Insert the current entry followed by trailing comma.
    (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
        let _ = $object.insert(($($key)+).into(), $value);
        json_internal!(@object $object () ($($rest)*) ($($rest)*));
    };

    // Current entry followed by unexpected token.
    (@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
        json_unexpected!($unexpected);
    };

    // Insert the last entry without trailing comma.
    (@object $object:ident [$($key:tt)+] ($value:expr)) => {
        let _ = $object.insert(($($key)+).into(), $value);
    };
```

値を読んだら、次はカンマか何もないか、どちらかになっているはずです。
それを確認したら、`$object`にキーと値の組をinsertし、
続きがある場合は`$object`の後のキーを`()`に戻して再び`json_internal!`します。

そしてこれは恐らく、最終要素の末尾にカンマがついていた場合に通る処理です。

```rs
    // Done.
    (@object $object:ident () () ()) => {};
```

あと、以下がエラー処理用です。

```rs
    // Missing value for last entry. Trigger a reasonable error message.
    (@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
        // "unexpected end of macro invocation"
        json_internal!();
    };

    // Missing colon and value for last entry. Trigger a reasonable error
    // message.
    (@object $object:ident ($($key:tt)+) () $copy:tt) => {
        // "unexpected end of macro invocation"
        json_internal!();
    };

    // Misplaced colon. Trigger a reasonable error message.
    (@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
        // Takes no arguments so "no rules expected the token `:`".
        json_unexpected!($colon);
    };

    // Found a comma inside a key. Trigger a reasonable error message.
    (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
        // Takes no arguments so "no rules expected the token `,`".
        json_unexpected!($comma);
    };
```

マクロの中でエラーが起こると分かりにくいんですが、マクロのマッチングミスで出るエラーメッセージを利用して、マシなエラーになるように心がけているっぽいです。

これでめでたく、`json_internal!`を読むことができました。

# まとめ
今回、serde-jsonの`json!`マクロが何をやっているのか読んでみました。

「うまいことやってるなぁ」という気持ちと、「黒魔術では？」という気持ちでいっぱいになりますが、`json!`マクロ自体は非常に便利です。また、ここまでではなくても、何かマクロでDSLを書きたい場合に「JSONができたんだから、これも頑張ればできるはず」と思えるのは嬉しいことです。

みなさんも、Rustマクロ黒魔術にぜひとも挑戦してみてください。