imbl_value/
macros.rs

1#[macro_export(local_inner_macros)]
2macro_rules! json {
3    // Hide distracting implementation details from the generated rustdoc.
4    ($($json:tt)+) => {
5        json_internal!($($json)+)
6    };
7}
8
9#[macro_export(local_inner_macros)]
10#[doc(hidden)]
11macro_rules! json_internal {
12//////////////////////////////////////////////////////////////////////////
13    // TT muncher for parsing the inside of an array [...]. Produces a vec![...]
14    // of the elements.
15    //
16    // Must be invoked as: json!(@array [] $($tt)*)
17    //////////////////////////////////////////////////////////////////////////
18
19    // Done with trailing comma.
20    (@array [$($elems:expr,)*]) => {
21        json_internal_vec![$($elems,)*]
22    };
23
24    // Done without trailing comma.
25    (@array [$($elems:expr),*]) => {
26        json_internal_vec![$($elems),*]
27    };
28
29    // Next element is `null`.
30    (@array [$($elems:expr,)*] null $($rest:tt)*) => {
31        json!(@array [$($elems,)* json!(null)] $($rest)*)
32    };
33
34    // Next element is `true`.
35    (@array [$($elems:expr,)*] true $($rest:tt)*) => {
36        json!(@array [$($elems,)* json!(true)] $($rest)*)
37    };
38
39    // Next element is `false`.
40    (@array [$($elems:expr,)*] false $($rest:tt)*) => {
41        json!(@array [$($elems,)* json!(false)] $($rest)*)
42    };
43
44    // Next element is an array.
45    (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
46        json!(@array [$($elems,)* json!([$($array)*])] $($rest)*)
47    };
48
49    // Next element is a map.
50    (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
51        json!(@array [$($elems,)* json!({$($map)*})] $($rest)*)
52    };
53
54    // Next element is an expression followed by comma.
55    (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
56        json!(@array [$($elems,)* json!($next),] $($rest)*)
57    };
58
59    // Last element is an expression with no trailing comma.
60    (@array [$($elems:expr,)*] $last:expr) => {
61        json!(@array [$($elems,)* json!($last)])
62    };
63
64    // Comma after the most recent element.
65    (@array [$($elems:expr),*] , $($rest:tt)*) => {
66        json!(@array [$($elems,)*] $($rest)*)
67    };
68
69    // Unexpected token after most recent element.
70    (@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
71        json_unexpected!($unexpected)
72    };
73
74    //////////////////////////////////////////////////////////////////////////
75    // TT muncher for parsing the inside of an object {...}. Each entry is
76    // inserted into the given map variable.
77    //
78    // Must be invoked as: json!(@object $map () ($($tt)*) ($($tt)*))
79    //
80    // We require two copies of the input tokens so that we can match on one
81    // copy and trigger errors on the other copy.
82    //////////////////////////////////////////////////////////////////////////
83
84    // Done.
85    (@object $object:ident () () ()) => {};
86
87    // Insert the current entry followed by trailing comma.
88    (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
89        let _ = $object.insert($crate::InternedString::intern(($($key)+)), $value);
90        json!(@object $object () ($($rest)*) ($($rest)*));
91    };
92
93    // Current entry followed by unexpected token.
94    (@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
95        json_unexpected!($unexpected);
96    };
97
98    // Insert the last entry without trailing comma.
99    (@object $object:ident [$($key:tt)+] ($value:expr)) => {
100        let _ = $object.insert($crate::InternedString::intern(($($key)+)), $value);
101    };
102
103    // Next value is `null`.
104    (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
105        json!(@object $object [$($key)+] (json!(null)) $($rest)*);
106    };
107
108    // Next value is `true`.
109    (@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => {
110        json!(@object $object [$($key)+] (json!(true)) $($rest)*);
111    };
112
113    // Next value is `false`.
114    (@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => {
115        json!(@object $object [$($key)+] (json!(false)) $($rest)*);
116    };
117
118    // Next value is an array.
119    (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
120        json!(@object $object [$($key)+] (json!([$($array)*])) $($rest)*);
121    };
122
123    // Next value is a map.
124    (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
125        json!(@object $object [$($key)+] (json!({$($map)*})) $($rest)*);
126    };
127
128    // Next value is an expression followed by comma.
129    (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
130        json!(@object $object [$($key)+] (json!($value)) , $($rest)*);
131    };
132
133    // Last value is an expression with no trailing comma.
134    (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
135        json!(@object $object [$($key)+] (json!($value)));
136    };
137
138    // Missing value for last entry. Trigger a reasonable error message.
139    (@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
140        // "unexpected end of macro invocation"
141        json!();
142    };
143
144    // Missing colon and value for last entry. Trigger a reasonable error
145    // message.
146    (@object $object:ident ($($key:tt)+) () $copy:tt) => {
147        // "unexpected end of macro invocation"
148        json!();
149    };
150
151    // Misplaced colon. Trigger a reasonable error message.
152    (@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
153        // Takes no arguments so "no rules expected the token `:`".
154        json_unexpected!($colon);
155    };
156
157    // Found a comma inside a key. Trigger a reasonable error message.
158    (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
159        // Takes no arguments so "no rules expected the token `,`".
160        json_unexpected!($comma);
161    };
162
163    // Key is fully parenthesized. This avoids clippy double_parens false
164    // positives because the parenthesization may be necessary here.
165    (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
166        json!(@object $object ($key) (: $($rest)*) (: $($rest)*));
167    };
168
169    // Refuse to absorb colon token into key expression.
170    (@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => {
171        json_expect_expr_comma!($($unexpected)+);
172    };
173
174    // Munch a token into the current key.
175    (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
176        json!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*));
177    };
178
179    //////////////////////////////////////////////////////////////////////////
180    // The main implementation.
181    //
182    // Must be invoked as: json!($($json)+)
183    //////////////////////////////////////////////////////////////////////////
184
185    (null) => {
186        $crate::Value::Null
187    };
188
189    (true) => {
190        $crate::Value::Bool(true)
191    };
192
193    (false) => {
194        $crate::Value::Bool(false)
195    };
196
197    ([]) => {
198        $crate::Value::Array($crate::imbl::Vector::new())
199    };
200
201    ([ $($tt:tt)+ ]) => {
202        $crate::Value::Array(json!(@array [] $($tt)+))
203    };
204
205    ({}) => {
206        $crate::Value::Object($crate::InOMap::new())
207    };
208
209    ({ $($tt:tt)+ }) => {
210        $crate::Value::Object({
211            let mut object = $crate::InOMap::new();
212            json!(@object object () ($($tt)+) ($($tt)+));
213            object
214        })
215    };
216
217    // Any Serialize type: numbers, strings, struct literals, variables etc.
218    // Must be below every other rule.
219    ($other:expr) => {
220        $crate::to_value(&$other).unwrap()
221    };
222}
223
224// The json_internal macro above cannot invoke vec directly because it uses
225// local_inner_macros. A vec invocation there would resolve to $crate::vec.
226// Instead invoke vec here outside of local_inner_macros.
227#[macro_export]
228#[doc(hidden)]
229macro_rules! json_internal_vec {
230    ($($content:tt)*) => {
231        $crate::imbl::vector![$($content)*]
232    };
233}
234
235#[macro_export]
236#[doc(hidden)]
237macro_rules! json_unexpected {
238    () => {};
239}
240
241#[macro_export]
242#[doc(hidden)]
243macro_rules! json_expect_expr_comma {
244    ($e:expr , $($tt:tt)*) => {};
245}