syntree/macros.rs
1/// Helper macro for building a tree in place.
2///
3/// The tree is built with [`Builder::new`], so it uses the default flavor. Use
4/// [`tree_with!`] to build a tree of another flavor.
5///
6/// Each element is one of:
7/// * `value` a token with an empty span, see [`Builder::token_empty`].
8/// * `(value, len)` a token of length `len`, see [`Builder::token`].
9/// * `(value, (start, end))` a token with an explicit span, see
10/// [`Builder::token_with`].
11/// * `value => { .. }` a node whose span covers its children, see
12/// [`Builder::open`].
13/// * `(value, (start, end)) => { .. }` a node with an explicit span, see
14/// [`Builder::open_with`].
15///
16/// Elements are separated by commas. The macro expands to builder calls
17/// followed by `?`, so it can only be used in a function (or doctest) returning
18/// a `Result` whose error type implements `From<Error<F::Error>>`, where `F` is
19/// the flavor of the tree.
20///
21/// [`Builder::new`]: crate::Builder::new
22/// [`Builder::token_empty`]: crate::Builder::token_empty
23/// [`Builder::token`]: crate::Builder::token
24/// [`Builder::token_with`]: crate::Builder::token_with
25/// [`Builder::open`]: crate::Builder::open
26/// [`Builder::open_with`]: crate::Builder::open_with
27/// [`tree_with!`]: crate::tree_with
28///
29/// # Examples
30///
31/// ```
32/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
33/// enum Syntax {
34/// Root,
35/// Number,
36/// Lit,
37/// Whitespace,
38/// }
39///
40/// let mut tree = syntree::Builder::new();
41///
42/// tree.open(Syntax::Root)?;
43///
44/// tree.open(Syntax::Number)?;
45/// tree.token(Syntax::Lit, 1)?;
46/// tree.close()?;
47///
48/// tree.token(Syntax::Whitespace, 3)?;
49///
50/// tree.open(Syntax::Number)?;
51/// tree.token(Syntax::Lit, 2)?;
52/// tree.token_empty(Syntax::Lit)?;
53/// tree.close()?;
54///
55/// tree.close()?;
56///
57/// let tree = tree.build()?;
58///
59/// let expected = syntree::tree! {
60/// Syntax::Root => {
61/// Syntax::Number => {
62/// (Syntax::Lit, 1)
63/// },
64/// (Syntax::Whitespace, 3),
65/// Syntax::Number => {
66/// (Syntax::Lit, 2),
67/// Syntax::Lit
68/// }
69/// }
70/// };
71///
72/// assert_eq!(expected, tree);
73/// # Ok::<_, Box<dyn core::error::Error>>(())
74/// ```
75///
76/// Explicit spans:
77///
78/// ```
79/// use syntree::Span;
80///
81/// let tree = syntree::tree! {
82/// ("root", (0, 10)) => {
83/// ("token", (2, 5)),
84/// }
85/// };
86///
87/// let root = tree.first().ok_or("missing root")?;
88/// assert_eq!(root.span(), Span::new(0, 10));
89///
90/// let token = root.first().ok_or("missing token")?;
91/// assert_eq!(token.span(), Span::new(2, 5));
92/// # Ok::<_, Box<dyn core::error::Error>>(())
93/// ```
94#[macro_export]
95macro_rules! tree {
96 (@o $b:ident,) => {};
97
98 (@o $b:ident, ($expr:expr, ($start:expr, $end:expr)) $(,)?) => {{
99 $b.token_with($expr, $crate::Span::new($start, $end))?;
100 }};
101
102 (@o $b:ident, ($expr:expr, $len:expr) $(,)?) => {{
103 $b.token($expr, $len)?;
104 }};
105
106 (@o $b:ident, ($expr:expr, ($start:expr, $end:expr)), $($rest:tt)*) => {{
107 $b.token_with($expr, $crate::Span::new($start, $end))?;
108 $crate::tree!(@o $b, $($rest)*);
109 }};
110
111 (@o $b:ident, ($expr:expr, $len:expr), $($rest:tt)*) => {{
112 $b.token($expr, $len)?;
113 $crate::tree!(@o $b, $($rest)*);
114 }};
115
116 (@o $b:ident, $expr:expr $(,)?) => {{
117 $b.token_empty($expr)?;
118 }};
119
120 (@o $b:ident, $expr:expr, $($rest:tt)*) => {{
121 $b.token_empty($expr)?;
122 $crate::tree!(@o $b, $($rest)*);
123 }};
124
125 (@o $b:ident, ($expr:expr, ($start:expr, $end:expr)) => { $($tt:tt)* } $(,)?) => {{
126 $b.open_with($expr, $crate::Span::new($start, $end))?;
127 $crate::tree!(@o $b, $($tt)*);
128 $b.close()?;
129 }};
130
131 (@o $b:ident, $expr:expr => { $($tt:tt)* } $(,)?) => {{
132 $b.open($expr)?;
133 $crate::tree!(@o $b, $($tt)*);
134 $b.close()?;
135 }};
136
137 (@o $b:ident, ($expr:expr, ($start:expr, $end:expr)) => { $($tt:tt)* }, $($rest:tt)*) => {{
138 $b.open_with($expr, $crate::Span::new($start, $end))?;
139 $crate::tree!(@o $b, $($tt)*);
140 $b.close()?;
141 $crate::tree!(@o $b, $($rest)*);
142 }};
143
144 (@o $b:ident, $expr:expr => { $($tt:tt)* }, $($rest:tt)*) => {{
145 $b.open($expr)?;
146 $crate::tree!(@o $b, $($tt)*);
147 $b.close()?;
148 $crate::tree!(@o $b, $($rest)*);
149 }};
150
151 ($($tt:tt)*) => {{
152 let mut b = $crate::Builder::new();
153 $crate::tree!(@o b, $($tt)*);
154 b.build()?
155 }};
156}
157
158/// Helper macro for building a tree in place with a custom flavor.
159///
160/// This takes the same input as [`tree!`], but builds the tree with
161/// [`Builder::new_with`]. The flavor is inferred from the type the result is
162/// used as, so it usually needs a type annotation such as
163/// `let tree: Tree<_, MyFlavor> = ..`.
164///
165/// Like [`tree!`], it expands to builder calls followed by `?`, so it can only
166/// be used in a function returning a `Result` whose error type implements
167/// `From<Error<F::Error>>`, where `F` is the flavor of the tree.
168///
169/// [`tree!`]: crate::tree
170/// [`Builder::new_with`]: crate::Builder::new_with
171///
172/// # Examples
173///
174/// ```
175/// use syntree::{Empty, EmptyVec, Tree, TreeIndex};
176///
177/// syntree::flavor! {
178/// struct FlavorEmpty {
179/// type Index = Empty;
180/// type Indexes = EmptyVec<TreeIndex<Self>>;
181/// }
182/// };
183///
184/// let tree: Tree<_, FlavorEmpty> = syntree::tree_with! {
185/// "root" => {
186/// "child" => {
187/// "token"
188/// },
189/// "child2"
190/// }
191/// };
192///
193/// let expected: Tree<_, FlavorEmpty> = syntree::tree_with! {
194/// "root" => {
195/// "child" => {
196/// ("token", Empty)
197/// },
198/// "child2"
199/// }
200/// };
201///
202/// assert_eq!(tree, expected);
203/// # Ok::<_, Box<dyn core::error::Error>>(())
204/// ```
205#[macro_export]
206macro_rules! tree_with {
207 ($($tt:tt)*) => {{
208 let mut b = $crate::Builder::new_with();
209 $crate::tree!(@o b, $($tt)*);
210 b.build()?
211 }};
212}