tt_call/
lib.rs

1//! [![github]](https://github.com/dtolnay/tt-call) [![crates-io]](https://crates.io/crates/tt-call) [![docs-rs]](https://docs.rs/tt-call)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! <br>
8//!
9//! **This library is an attempt at seeding an ecosystem of high-quality modular
10//! interoperable tt-muncher building blocks.**
11//!
12//! Tt-munching is a powerful technique for parsing macro\_rules input grammars
13//! of significant complexity. In building more and more sophisticated
14//! tt-muncher macros it becomes valuable to share code for dealing with certain
15//! common input patterns, rather than reimplementing support for those patterns
16//! in a low quality and poorly tested way each time.
17//!
18//! The core macros provided by this library are **[`tt_call!`]** and
19//! **[`tt_return!`]**. Together these provide a flexible way to propagate input
20//! and output tokens along a recursive descent call hierarchy. One may also
21//! view them as a flexible library-only stable implementation of eager
22//! expansion for macro\_rules.
23//!
24//! [`tt_call!`]: macro.tt_call.html
25//! [`tt_return!`]: macro.tt_return.html
26//!
27//! ```toml
28//! [dependencies]
29//! tt-call = "1.0"
30//! ```
31//!
32//! *Version requirement: tt-call requires a Rust compiler version 1.31 or
33//! newer.*
34//!
35//!
36//! ## Calling convention rules
37//!
38//! - **Macros that conform to tt-call must be invoked with curly braces.**
39//!
40//!     ```
41//!     # macro_rules! some_macro {
42//!     #     () => {};
43//!     # }
44//!     #
45//!     some_macro! {
46//!         /* ... */
47//!     }
48//!     ```
49//!
50//!     The Rust grammar is very particular about punctuation after
51//!     parenthesized and square bracketed macro invocations. In expression or
52//!     type position they must not be followed by a semicolon. In item or
53//!     statement position they are required to be followed by a semicolon. The
54//!     inconsistency is applied transitively to any helper macros they forward
55//!     to, and means that parenthesized and square bracketed macro invocations
56//!     must decide whether to support expression and type position only or item
57//!     and statement position only. They cannot support both, which is a
58//!     problem for broadly applicable macro building blocks.
59//!
60//!     There is no such punctuation requirement after curly brace invocations.
61//!     Consistently using curly braces makes the same macro building blocks
62//!     usable in any syntactic position.
63//!
64//! - **Input and output values must be passed in the following key-value
65//!   form.**
66//!
67//!     ```
68//!     # macro_rules! some_macro {
69//!     #     {
70//!     $key:ident = [{ $($value:tt)* }]
71//!     #     } => {};
72//!     # }
73//!     ```
74//!
75//!     This is enforced by the `tt_call!` and `tt_return!` macros. The
76//!     consistency is important for composability and makes it possible to
77//!     write higher-order macros that operate on the input or output of an
78//!     arbitrary tt-call macro.
79//!
80//!     Except in libraries intended specifically as tt-call building blocks,
81//!     generally tt-call macros will be private `#[doc(hidden)]` helpers with a
82//!     user-facing non-tt-call entry point. Thus the rigid key-value syntax
83//!     need not be exposed to users of the public macro.
84//!
85//! - **Before its key-value inputs, every rule must accept a `$caller:tt`.**
86//!
87//!     This is an opaque tt bundle used by `tt_call!` and `tt_return!` to
88//!     record the call hierarchy. A `tt_return!` accepts a `$caller` to return
89//!     back to.
90//!
91//! - **Every rule must expand to exactly one macro invocation and nothing
92//!   else.**
93//!
94//!     Output tokens are expected to be returned through `tt_return!`.
95//!     Expanding to nothing, expanding to more than one macro invocation, or
96//!     expanding to anything other than a macro invocation are not permitted.
97//!
98//!
99//! ## Examples
100//!
101//! Just as a flavor of the syntax, one of the rules from the implementation of
102//! the built-in [`tt_replace!`] macro is written as follows. The macro takes in
103//! a token stream and for each token that matches a given predicate it replaces
104//! that token with a given replacement sequence of tokens. For example the
105//! caller may want to replace the token `self` with the single token `__value`.
106//!
107//! The rule shown here is responsible for performing one step of the
108//! replacement. It matches one token of input in `$first:tt`, uses [`tt_if!`]
109//! to invoke the predicate with `$first` as input, recurses with an accumulated
110//! copy of the replacement tokens if the predicate returns true, and recurses
111//! on the remaining tokens with `$first` preserved unchanged if the predicate
112//! returns false.
113//!
114//! [`tt_replace!`]: macro.tt_replace.html
115//! [`tt_if!`]: macro.tt_if.html
116//!
117//! ```
118//! # macro_rules! ignore {
119//! {
120//!     $caller:tt
121//!     condition = [{ $condition:ident }]
122//!     replace_with = [{ $($with:tt)* }]
123//!     tokens = [{ $($tokens:tt)* }]
124//!     rest = [{ $first:tt $($rest:tt)* }]
125//! } => {
126//!     tt_if! {
127//!         condition = [{ $condition }]
128//!         input = [{ $first }]
129//!         true = [{
130//!             private_replace! {
131//!                 $caller
132//!                 condition = [{ $condition }]
133//!                 replace_with = [{ $($with)* }]
134//!                 tokens = [{ $($tokens)* $($with)* }]
135//!                 rest = [{ $($rest)* }]
136//!             }
137//!         }]
138//!         false = [{
139//!             private_replace! {
140//!                 $caller
141//!                 condition = [{ $condition }]
142//!                 replace_with = [{ $($with)* }]
143//!                 tokens = [{ $($tokens)* $first }]
144//!                 rest = [{ $($rest)* }]
145//!             }
146//!         }]
147//!     }
148//! };
149//! # }
150//! ```
151//!
152//! Here is another macro rule selected from `tt_replace!`. This one matches if
153//! the tt-muncher has reached the end of its input. It returns the finished
154//! tokens back to the caller using `tt_return!`.
155//!
156//! ```
157//! # macro_rules! ignore {
158//! {
159//!     $caller:tt
160//!     condition = [{ $condition:ident }]
161//!     replace_with = [{ $($with:tt)* }]
162//!     tokens = [{ $($tokens:tt)* }]
163//!     rest = [{ }]
164//! } => {
165//!     tt_return! {
166//!         $caller
167//!         tokens = [{ $($tokens)* }]
168//!     }
169//! };
170//! # }
171//! ```
172//!
173//! One example of a caller-provided predicate for `tt_replace!` could be
174//! written as follows. This predicate determines whether the input token is
175//! lowercase `self`.
176//!
177//! ```
178//! macro_rules! is_lowercase_self {
179//!     // Input token is `self`.
180//!     {
181//!         $caller:tt
182//!         input = [{ self }]
183//!     } => {
184//!         tt_return! {
185//!             $caller
186//!             is = [{ true }]
187//!         }
188//!     };
189//!
190//!     // Input token is anything other than `self`.
191//!     {
192//!         $caller:tt
193//!         input = [{ $other:tt }]
194//!     } => {
195//!         tt_return! {
196//!             $caller
197//!             is = [{ false }]
198//!         }
199//!     };
200//! }
201//! ```
202//!
203//! From here, calling `tt_replace!` with our `is_lowercase_self!` as the
204//! condition predicate can be used to implement a fanciful syntax for unary
205//! closures: `closure!(self + 1)` should expand to `|__value| __value + 1`.
206//!
207//! Notice that this user-facing `closure!` macro does not follow the tt-call
208//! calling convention. Internally though it uses several tt-call helpers as
209//! building blocks.
210//!
211//! ```
212//! # macro_rules! tt_call {
213//! #     ($($ignore:tt)*) => {
214//! #         2
215//! #     };
216//! # }
217//! #
218//! macro_rules! closure {
219//!     ($($expr:tt)+) => {
220//!         |__value| tt_call! {
221//!             macro = [{ tt_replace }]
222//!             condition = [{ is_lowercase_self }]
223//!             replace_with = [{ __value }]
224//!             input = [{ $($expr)+ }]
225//!         }
226//!     };
227//! }
228//!
229//! fn main() {
230//!     let add_one = closure!(self + 1);
231//!     println!("{}", add_one(1));
232//! }
233//! ```
234//!
235//!
236//! ## Motivation
237//!
238//! This may seem like a lot of ceremony around what should be very simple macro
239//! calls. After all, couldn't we write `is_lowercase_self` in a much more
240//! straightforward way as follows?
241//!
242//! ```
243//! macro_rules! is_lowercase_self {
244//!     (self) => { true };
245//!     ($other:tt) => { false };
246//! }
247//!
248//! fn main() {
249//!     println!("{}", is_lowercase_self!(self)); // true
250//!     println!("{}", is_lowercase_self!(not_self)); // false
251//! }
252//! ```
253//!
254//! Qualified yes. As written, the simpler `is_lowercase_self!` behaves as it
255//! looks like it should.
256//!
257//! But suppose we want to build `tt_replace!` or similar macro that needs to
258//! invoke `is_lowercase_self!` as a helper. There is no way to do it with this
259//! simpler one. No matter what our macro does, there is no way for it to expand
260//! `is_lowercase_self!` before expanding itself. If it expands itself first,
261//! there is no way for it to use the expansion of `is_lowercase_self!` to
262//! decide whether the current token is supposed to be replaced.
263//!
264//! The `tt_call!` and `tt_return!` abstraction along with `$caller:tt` tracking
265//! of the call hierarchy are critical to building composable macros that freely
266//! pass around arbitrary tokens and return in a way that can inform expansion
267//! of their caller.
268//!
269//! A future eager expansion feature for declarative macros may render the
270//! tt-call approach unnecessary. Eager expansion is listed as an unresolved
271//! question in the [tracking issue for declarative macros 2.0][tracking] but is
272//! believed to be quite a ways out, if it ever happens. And even then it is not
273//! clear whether it is desirable to allow macros expanding to arbitrary tokens.
274//! Today macros always expand to an expression, item, statement, type, or
275//! pattern. Eager expansion does not automatically mean that the restriction
276//! would be lifted to allow a macro that expands to arbitrary tokens such as `!
277//! @ #`. The token tree calling convention provides working eager expansion
278//! today with support for passing and returning arbitrary token streams.
279//!
280//! [tracking]: https://github.com/rust-lang/rust/issues/39412
281//!
282//! And function-like procedural macros once those are stable? It is going to
283//! depend on your choice of syntax for the macro input whether a procedural
284//! macro is a better choice, but note that they present their own DIY parsing
285//! adventures and can be even nastier than tt-call once you get the hang of
286//! both. In addition, procedural macros must be defined in a separate crate
287//! from the rest of your library so they are not well suited for quick one-off
288//! helper macros.
289//!
290//!
291//! ## Design philosphy
292//!
293//! As may be no surprise by this point, the calling convention design
294//! prioritizes scalability and composability over conciseness. A reader
295//! familiar with the calling convention (maybe you, six months after writing
296//! the macro) should be able to look at any individual tt-call rule by itself
297//! and comfortably read off what it does top to bottom and identify its
298//! purpose.
299//!
300//!
301//! ## Links
302//!
303//! - The code that implements `closure!(self + 1)`, all of which is shown
304//!   above, can be found all together in [`examples/replace.rs`].
305//!
306//! - As a more elaborate example of a tt-call macro,
307//!   [`examples/comma_separated.rs`] demonstrates a macro that does primitive
308//!   name mangling of Rust types. It uses [`parse_type!`] which is a tt-call
309//!   version of `$:ty`.
310//!
311//!     ```
312//!     # macro_rules! mangle_type_names {
313//!     #     ($($ignore:tt)*) => {
314//!     #         &[
315//!     #             "_std_fs_File",
316//!     #             "_ref_mut_str",
317//!     #             "_impl_Display",
318//!     #             "_fn_s_ref_str_to_String",
319//!     #         ]
320//!     #     };
321//!     # }
322//!     #
323//!     static MANGLED: &[&str] = mangle_type_names! {
324//!         std::fs::File,
325//!         &'a mut str,
326//!         impl Display,
327//!         fn(s: &str) -> String,
328//!     };
329//!
330//!     fn main() {
331//!         assert_eq!(MANGLED, [
332//!             "_std_fs_File",
333//!             "_ref_mut_str",
334//!             "_impl_Display",
335//!             "_fn_s_ref_str_to_String",
336//!         ]);
337//!     }
338//!     ```
339//!
340//! [`examples/replace.rs`]: https://github.com/dtolnay/tt-call/blob/master/examples/replace.rs
341//! [`examples/comma_separated.rs`]: https://github.com/dtolnay/tt-call/blob/master/examples/comma_separated.rs
342//! [`parse_type!`]: macro.parse_type.html
343
344#![no_std]
345#![doc(html_root_url = "https://docs.rs/tt-call/1.0.9")]
346#![allow(clippy::module_name_repetitions, clippy::needless_doctest_main)]
347
348mod predicate;
349mod replace;
350mod rust;
351mod unexpected;
352
353// In general it is not possible today in Rust to produce good error messages
354// and good error spans at the same time. See:
355//
356//     https://github.com/rust-lang/rust/issues/44535
357//
358// Within this crate we prefer to produce errors with the right span, even if
359// the message is not good. This scales much better to large input token
360// streams.
361
362/// Evaluate a tt-call macro and return its output to a given return
363/// destination.
364///
365/// # Input
366///
367/// The input must start with an argument called `macro` which provides the name
368/// of the macro for `tt_call!` to invoke.
369///
370///   - `macro = [{` name of macro to call `}]`
371///
372/// After that there may be any number of key-value pairs to be passed as
373/// arguments to the macro being called.
374///
375///   - **`$(`**<br>
376///     &emsp;&emsp;arbitrary key `= [{` arbitrary tokens `}]`<br>
377///     **`)*`**
378///
379/// Finally a specification of the macro invocation to which this call should
380/// return its output.
381///
382///   - `~~>` name of return destination macro `! {`<br>
383///     &emsp;&emsp;arbitrary tokens<br>
384///     `}`
385///
386/// # Examples
387///
388/// ```
389/// use tt_call::{tt_call, tt_is_ident};
390///
391/// macro_rules! print_is_ident {
392///     {
393///         token = [{ $token:tt }]
394///         is_ident = [{ true }]
395///     } => {
396///         println!("turns out `{}` is an ident", stringify!($token));
397///     };
398///
399///     {
400///         token = [{ $token:tt }]
401///         is_ident = [{ false }]
402///     } => {
403///         println!("nope, `{}` is not an ident", stringify!($token));
404///     };
405/// }
406///
407/// fn main() {
408///     tt_call! {
409///         macro = [{ tt_is_ident }]
410///         input = [{ foo }]
411///         ~~> print_is_ident! {
412///             token = [{ foo }]
413///         }
414///     }
415/// }
416/// ```
417///
418/// If the invoked macro provides the entirety of the input to the return
419/// destination macro, then the `!` and argument list may be omitted.
420///
421/// ```
422/// use tt_call::{tt_call, tt_is_ident};
423///
424/// macro_rules! print_is_ident {
425///     {
426///         is_ident = [{ true }]
427///     } => {
428///         println!("that token is an ident");
429///     };
430///
431///     {
432///         is_ident = [{ false }]
433///     } => {
434///         println!("nope, not an ident");
435///     };
436/// }
437///
438/// fn main() {
439///     tt_call! {
440///         macro = [{ tt_is_ident }]
441///         input = [{ foo }]
442///         ~~> print_is_ident
443///     }
444/// }
445/// ```
446///
447/// And if the invoked macro produces exactly one output value and we just want
448/// to expand to that output value, the destination macro may be omitted
449/// entirely.
450///
451/// ```
452/// use tt_call::{tt_call, tt_is_ident};
453///
454/// fn main() {
455///     let is_ident = tt_call! {
456///         macro = [{ tt_is_ident }]
457///         input = [{ foo }]
458///     };
459///     println!("{}", is_ident); // prints true or false
460/// }
461/// ```
462#[macro_export]
463macro_rules! tt_call {
464    // Call macro and expand into the tokens of its one return value.
465    {
466        macro = [{ $($m:ident)::* }]
467        $(
468            $input:ident = [{ $($tokens:tt)* }]
469        )*
470    } => {
471        $($m)::* ! {
472            (__tt_call_private $crate::tt_identity_return! {})
473            $(
474                $input = [{ $($tokens)* }]
475            )*
476        }
477    };
478
479    // Call macro and pass its return values to the given return destination.
480    {
481        macro = [{ $($m:ident)::* }]
482        $(
483            $input:ident = [{ $($tokens:tt)* }]
484        )*
485        ~~> $($return:ident)::*
486    } => {
487        $($m)::* ! {
488            (__tt_call_private $($return)::* ! {})
489            $(
490                $input = [{ $($tokens)* }]
491            )*
492        }
493    };
494
495    // Call macro and append its return values onto the invocation of the given
496    // return destination without caller.
497    {
498        macro = [{ $($m:ident)::* }]
499        $(
500            $input:ident = [{ $($tokens:tt)* }]
501        )*
502        ~~> $($return:ident)::* ! {
503            $(
504                $name:ident = [{ $($state:tt)* }]
505            )*
506        }
507    } => {
508        $($m)::* ! {
509            (__tt_call_private $($return)::* ! {
510                $(
511                    $name = [{ $($state)* }]
512                )*
513            })
514            $(
515                $input = [{ $($tokens)* }]
516            )*
517        }
518    };
519
520    // Call macro and append its return values onto the invocation of the given
521    // return destination with caller.
522    {
523        macro = [{ $($m:ident)::* }]
524        $(
525            $input:ident = [{ $($tokens:tt)* }]
526        )*
527        ~~> $($return:ident)::* ! {
528            $caller:tt
529            $(
530                $name:ident = [{ $($state:tt)* }]
531            )*
532        }
533    } => {
534        $($m)::* ! {
535            (__tt_call_private $($return)::* ! {
536                $caller
537                $(
538                    $name = [{ $($state)* }]
539                )*
540            })
541            $(
542                $input = [{ $($tokens)* }]
543            )*
544        }
545    };
546}
547
548#[doc(hidden)]
549#[macro_export]
550macro_rules! tt_identity_return {
551    // Macro returned one value.
552    {
553        $name:ident = [{ $($output:tt)* }]
554    } => {
555        $($output)*
556    };
557
558    // Macro parsed the entire input and returned one value.
559    {
560        $name:ident = [{ $($output:tt)* }]
561        rest = [{ }]
562    } => {
563        $($output)*
564    };
565
566    // Unexpected: macro failed to parse the entire input.
567    {
568        $name:ident = [{ $($output:tt)* }]
569        rest = [{ $($unexpected:tt)* }]
570    } => {
571        $crate::error_unexpected! {
572            $($unexpected)*
573        }
574    };
575}
576
577/// Return zero or more output values to the caller macro.
578///
579/// # Input
580///
581/// The `tt_return!` invocation should be given a `$caller` to return to and a
582/// sequence of zero or more named return values.
583///
584///   - **`$(`**<br>
585///     &emsp;&emsp;arbitrary key `= [{` arbitrary tokens `}]`<br>
586///     **`)*`**
587///
588/// # Example
589///
590/// ```
591/// use tt_call::{tt_call, tt_return};
592///
593/// macro_rules! is_lowercase_self {
594///     // Input token is `self`.
595///     {
596///         $caller:tt
597///         input = [{ self }]
598///     } => {
599///         tt_return! {
600///             $caller
601///             is = [{ true }]
602///         }
603///     };
604///
605///     // Input token is anything other than `self`.
606///     {
607///         $caller:tt
608///         input = [{ $other:tt }]
609///     } => {
610///         tt_return! {
611///             $caller
612///             is = [{ false }]
613///         }
614///     };
615/// }
616///
617/// fn main() {
618///     let is = tt_call! {
619///         macro = [{ is_lowercase_self }]
620///         input = [{ self }]
621///     };
622///     println!("{}", is);
623/// }
624/// ```
625#[macro_export]
626macro_rules! tt_return {
627    {
628        $caller:tt
629        $(
630            $output:ident = [{ $($tokens:tt)* }]
631        )*
632    } => {
633        $crate::private_return! {
634            $caller
635            $(
636                $output = [{ $($tokens)* }]
637            )*
638        }
639    };
640}
641
642#[doc(hidden)]
643#[macro_export]
644macro_rules! private_return {
645    {
646        (__tt_call_private $($caller:ident)::* ! { $($state:tt)* })
647        $($append:tt)*
648    } => {
649        $($caller)::* ! {
650            $($state)*
651            $($append)*
652        }
653    };
654}
655
656/// Evaluate a condition and expand to one or the other of two branches.
657///
658/// # Input
659///
660///   - `condition = [{` name of predicate macro to invoke `}]`
661///   - `input = [{` arbitrary tokens to pass as input to the predicate `}]`
662///   - `true = [{` tokens to expand to if the predicate returns true `}]`
663///   - `false = [{` and if the predicate returns false `}]`
664///
665/// The predicate macro must accept a single input value named `input`. It is
666/// expected to return a single output value which may have any name but must
667/// hold the tokens `true` or `false`. For example the built-in `tt_is_comma!`
668/// predicate expands to `is_comma = [{ true }]` or `is_comma = [{ false }]`.
669///
670/// # Example
671///
672/// ```
673/// use tt_call::{tt_call, tt_if, tt_is_comma, tt_return};
674///
675/// macro_rules! parse_until_comma {
676///     ($($input:tt)*) => {
677///         tt_call! {
678///             macro = [{ parse_until_comma_helper }]
679///             before_comma = [{ }]
680///             tokens = [{ $($input)* }]
681///         }
682///     };
683/// }
684///
685/// macro_rules! parse_until_comma_helper {
686///     {
687///         $caller:tt
688///         before_comma = [{ $($before:tt)* }]
689///         tokens = [{ $first:tt $($rest:tt)* }]
690///     } => {
691///         tt_if! {
692///             condition = [{ tt_is_comma }]
693///             input = [{ $first }]
694///             true = [{
695///                 tt_return! {
696///                     $caller
697///                     before_comma = [{ $($before)* }]
698///                 }
699///             }]
700///             false = [{
701///                 parse_until_comma_helper! {
702///                     $caller
703///                     before_comma = [{ $($before)* $first }]
704///                     tokens = [{ $($rest)* }]
705///                 }
706///             }]
707///         }
708///     };
709/// }
710///
711/// fn main() {
712///     assert_eq!(3, parse_until_comma!(1 + 2, three, four));
713/// }
714/// ```
715#[macro_export]
716macro_rules! tt_if {
717    {
718        condition = [{ $($condition:ident)::* }]
719        input = [{ $($input:tt)* }]
720        true = [{ $($then:tt)* }]
721        false = [{ $($else:tt)* }]
722    } => {
723        $crate::tt_call! {
724            macro = [{ $($condition)::* }]
725            input = [{ $($input)* }]
726            ~~> $crate::private_if_branch! {
727                true = [{ $($then)* }]
728                false = [{ $($else)* }]
729            }
730        }
731    };
732}
733
734#[doc(hidden)]
735#[macro_export]
736macro_rules! private_if_branch {
737    // Branch condition returned true.
738    {
739        true = [{ $($then:tt)* }]
740        false = [{ $($else:tt)* }]
741        $condition:ident = [{ true }]
742    } => {
743        $($then)*
744    };
745
746    // Branch condition returned false.
747    {
748        true = [{ $($then:tt)* }]
749        false = [{ $($else:tt)* }]
750        $condition:ident = [{ false }]
751    } => {
752        $($else)*
753    };
754}
755
756/// Print arbitrary output values returned by a tt-call macro. This is valuable
757/// for debugging.
758/// <sup>**[tt-call]**</sup>
759///
760/// # Example
761///
762/// ```
763/// use tt_call::{parse_type, tt_call, tt_debug};
764///
765/// fn main() {
766///     tt_call! {
767///         macro = [{ parse_type }]
768///         input = [{ Vec<u8>, compressed=false }]
769///         ~~> tt_debug
770///     }
771/// }
772/// ```
773///
774/// The output is:
775///
776/// ```text
777/// type = [{ Vec < u8 > }]
778/// rest = [{ , compressed = false }]
779/// ```
780#[macro_export]
781macro_rules! tt_debug {
782    {
783        $(
784            $output:ident = [{ $($tokens:tt)* }]
785        )*
786    } => {
787        $(
788            println!(
789                "{}",
790                concat!(
791                    stringify!($output),
792                    " = [{ ",
793                    stringify!($($tokens)*),
794                    " }]",
795                )
796            );
797        )*
798    }
799}