Skip to main content

syntree/
lib.rs

1//! [<img alt="github" src="https://img.shields.io/badge/github-udoprog/syntree-8da0cb?style=for-the-badge&logo=github" height="20">](https://github.com/udoprog/syntree)
2//! [<img alt="crates.io" src="https://img.shields.io/crates/v/syntree.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/syntree)
3//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-syntree-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/syntree)
4//!
5//! A memory efficient syntax tree.
6//!
7//! This crate provides a tree structure which always is contiguously stored and
8//! manipulated in memory. It provides similar APIs as [`rowan`] and is intended
9//! to be an efficient replacement for it (read more below).
10//!
11//! Anything can be stored in the tree as long as it implements `Copy`.
12//!
13//! <br>
14//!
15//! ## Usage
16//!
17//! Add `syntree` to your crate:
18//!
19//! ```toml
20//! syntree = "0.19.0"
21//! ```
22//!
23//! If you want a complete sample for how `syntree` can be used for parsing, see
24//! the [calculator example][calculator].
25//!
26//! <br>
27//!
28//! ## Syntax trees
29//!
30//! This crate provides a way to efficiently model [abstract syntax trees]. The
31//! nodes of the tree are typically represented by variants in an enum, but
32//! [could be whatever you want][any-syntax].
33//!
34//! Each tree consists of *nodes* and *tokens*. Nodes are intermediary elements
35//! in the tree which encapsulate zero or more other nodes or tokens, while
36//! tokens are leaf elements representing exact source locations.
37//!
38//! An example tree for the simple expression `256 / 2 + 64 * 2` could be
39//! represented like this:
40//!
41//! ```text
42//! Operation@0..16
43//!   Number@0..3
44//!     Number@0..3 "256"
45//!   Whitespace@3..4 " "
46//!   Operator@4..5
47//!     Div@4..5 "/"
48//!   Whitespace@5..6 " "
49//!   Number@6..7
50//!     Number@6..7 "2"
51//!   Whitespace@7..8 " "
52//!   Operator@8..9
53//!     Plus@8..9 "+"
54//!   Whitespace@9..10 " "
55//!   Operation@10..16
56//!     Number@10..12
57//!       Number@10..12 "64"
58//!     Whitespace@12..13 " "
59//!     Operator@13..14
60//!       Mul@13..14 "*"
61//!     Whitespace@14..15 " "
62//!     Number@15..16
63//!       Number@15..16 "2"
64//! ```
65//!
66//! > Try it for yourself with:
67//! >
68//! > ```sh
69//! > cargo run --example calculator -- "256 / 2 + 64 * 2"
70//! > ```
71//!
72//! The primary difference between `syntree` and [`rowan`] is that *we don't
73//! store the original source* in the syntax tree. Instead, the user of the
74//! library is responsible for providing it as necessary. Like when calling
75//! [`print_with_source`].
76//!
77//! The API for constructing a syntax tree is provided through [`Builder`] which
78//! implements streaming builder methods. Internally the builder is represented
79//! as a contiguous slab of memory. Once a tree is built the structure of the
80//! tree can be queried through the [`Tree`] type.
81//!
82//! Note that [`syntree::tree!`] is only a helper which simplifies building
83//! trees for examples. It corresponds exactly to performing [`open`],
84//! [`close`], and [`token`] calls on [`Builder`] as specified.
85//!
86//! ```
87//! use syntree::{Builder, Span};
88//!
89//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
90//! enum Syntax {
91//!     Number,
92//!     Lit,
93//!     Nested,
94//! }
95//!
96//! use Syntax::*;
97//!
98//! let mut tree = Builder::new();
99//!
100//! tree.open(Number)?;
101//! tree.token(Lit, 1)?;
102//! tree.token(Lit, 3)?;
103//!
104//! tree.open(Nested)?;
105//! tree.token(Lit, 1)?;
106//! tree.close()?;
107//!
108//! tree.close()?;
109//!
110//! let tree = tree.build()?;
111//!
112//! let expected = syntree::tree! {
113//!     Number => {
114//!         (Lit, 1),
115//!         (Lit, 3),
116//!         Nested => {
117//!             (Lit, 1)
118//!         }
119//!     }
120//! };
121//!
122//! assert_eq!(tree, expected);
123//!
124//! let number = tree.first().ok_or("missing number")?;
125//! assert_eq!(number.span(), Span::new(0, 5));
126//! # Ok::<_, Box<dyn core::error::Error>>(())
127//! ```
128//!
129//! Note how the resulting [`Span`] for `Number` corresponds to the full span of
130//! its `Lit` children. Including the ones within `Nested`.
131//!
132//! Trees are usually constructed by parsing an input. This library encourages
133//! the use of a [handwritten pratt parser][pratt]. See the [calculator
134//! example][calculator] for a complete use case.
135//!
136//! <br>
137//!
138//! ## Compact or empty spans
139//!
140//! The [`Flavor`] of a tree decides the types used for its spans and pointers.
141//! The default flavor, [`FlavorDefault`], uses `u32` indexes for spans and
142//! `usize`-based pointers to address nodes. A custom flavor declared with
143//! [`flavor!`] can change both, and is used through constructors such as
144//! [`Builder::new_with`]:
145//!
146//! * `type Index` is the type of span positions: `u32`, `usize`, or [`Empty`]
147//!   for no spans at all (see below).
148//! * `type Width` is the width of node pointers, such as `u8`, `u16`, `u32`
149//!   or `usize`. It bounds how many elements a tree can hold, adding more
150//!   fails with [`Error::Overflow`].
151//!
152//! ```
153//! use syntree::{Builder, Span};
154//!
155//! syntree::flavor! {
156//!     struct FlavorCompact {
157//!         type Index = u32;
158//!         type Width = u16;
159//!     }
160//! };
161//!
162//! let mut tree = Builder::<_, FlavorCompact>::new_with();
163//!
164//! tree.open("root")?;
165//! tree.open("child")?;
166//! tree.token("token", 100)?;
167//! tree.close()?;
168//! tree.open("child2")?;
169//! tree.close()?;
170//! tree.close()?;
171//!
172//! let tree = tree.build()?;
173//!
174//! // Trees of different flavors compare equal if their structure, values and
175//! // spans do.
176//! let expected = syntree::tree! {
177//!     "root" => {
178//!         "child" => { ("token", 100) },
179//!         "child2" => {}
180//!     }
181//! };
182//!
183//! assert_eq!(tree, expected);
184//! assert_eq!(tree.span(), Span::new(0, 100));
185//! # Ok::<_, Box<dyn core::error::Error>>(())
186//! ```
187//!
188//! Using [`Empty`] as the index allows for building trees without spans, if
189//! that is something you want to do. A tree also keeps the indexes of its
190//! tokens in `type Indexes` for lookups such as [`Tree::node_with_span`].
191//! Without spans there is nothing to look up, so they can be discarded by
192//! storing them in [`EmptyVec`]:
193//!
194//! ```
195//! use syntree::{Builder, Empty, EmptyVec, TreeIndex, Tree};
196//!
197//! syntree::flavor! {
198//!     struct FlavorEmpty {
199//!         type Index = Empty;
200//!         type Indexes = EmptyVec<TreeIndex<Self>>;
201//!     }
202//! };
203//!
204//! let mut tree = Builder::<_, FlavorEmpty>::new_with();
205//!
206//! tree.open("root")?;
207//! tree.open("child")?;
208//! tree.token("token", Empty)?;
209//! tree.close()?;
210//! tree.open("child2")?;
211//! tree.close()?;
212//! tree.close()?;
213//!
214//! let tree = tree.build()?;
215//!
216//! let expected: Tree<_, FlavorEmpty> = syntree::tree_with! {
217//!     "root" => {
218//!         "child" => { "token" },
219//!         "child2" => {}
220//!     }
221//! };
222//!
223//! assert_eq!(tree, expected);
224//! assert!(tree.span().is_empty());
225//! # Ok::<_, Box<dyn core::error::Error>>(())
226//! ```
227//!
228//! <br>
229//!
230//! ## Why not `rowan`?
231//!
232//! I love [`rowan`]. It's the reason why I started this project. But this crate
233//! still exists for a few philosophical differences that would be hard to
234//! reconcile directly in `rowan`.
235//!
236//! `rowan` only supports adding types which in some way can be coerced into an
237//! `repr(u16)` as part of the syntax tree. I think this decision is reasonable,
238//! but it precludes you from designing trees which contain anything else other
239//! than source references without having to perform some form of indirect
240//! lookup. This is something needed in order to move [Rune] to lossless syntax
241//! trees (see [the representation of `Kind::Str` variant][kind-str]).
242//!
243//! To exemplify this scenario consider the following syntax:
244//!
245//! ```
246//! #[derive(Debug, Clone, Copy)]
247//! enum Syntax {
248//!     /// A string referenced somewhere else using the provided ID.
249//!     Synthetic(usize),
250//!     /// A literal string from the source.
251//!     Literal,
252//!     /// Whitespace.
253//!     Whitespace,
254//!     /// A lexer error.
255//!     Error,
256//! }
257//! ```
258//!
259//! You can see the [full `synthetic_strings` example][synthetic_strings] for
260//! how this might be used. But not only can the `Synthetic` token correspond to
261//! some source location (as it should because it was expanded from one!). It
262//! also directly represents that it's *not* a literal string referencing a
263//! source location.
264//!
265//! In [Rune] this became needed once we started [expanding
266//! macros][rune-macros]. Because macros expand to things which do not reference
267//! source locations so we need some other mechanism to include what the tokens
268//! represent in the syntax trees.
269//!
270//! You can try a *very* simple lex-time variable expander in the
271//! [`synthetic_strings` example][synthetic_strings]:
272//!
273//! ```sh
274//! cargo run --example synthetic_strings -- "Hello $world"
275//! ```
276//!
277//! Which would output:
278//!
279//! ```text
280//! Tree:
281//! Literal@0..5 "Hello"
282//! Whitespace@5..6 " "
283//! Synthetic(0)@6..12 "$world"
284//! Eval:
285//! 0 = "Hello"
286//! 1 = "Earth"
287//! ```
288//!
289//! So in essence `syntree` doesn't believe you need to store strings in the
290//! tree itself. Even if you want to deduplicate string storage. All of that can
291//! be done on the side and encoded into the syntax tree as you wish.
292//!
293//! <br>
294//!
295//! ### Errors instead of panics
296//!
297//! Another point where this crate differs is that we rely on propagating a
298//! [`Error`] during tree construction if the API is used incorrectly
299//! *instead* of panicking.
300//!
301//! While on the surface this might seem like a minor difference in opinion on
302//! whether programming mistakes should be errors or not. In my experience
303//! parsers tend to be part of a crate in a larger project. And errors triggered
304//! by edge cases in user-provided input that once encountered can usually be
305//! avoided.
306//!
307//! So let's say [Rune] is embedded in [OxidizeBot] and there's a piece of code
308//! in a user-provided script which triggers a bug in the rune compiler. Which
309//! in turn causes an illegal tree to be constructed. If tree construction
310//! simply panics, the whole bot will go down. But if we instead propagate an
311//! error this would have to be handled in [OxidizeBot] which could panic if it
312//! wanted to. In this instance it's simply more appropriate to log the error
313//! and unload the script (and hopefully receive a bug report!) than to crash
314//! the bot.
315//!
316//! Rust has great diagnostics for indicating that results should be handled,
317//! and while it is [more awkward to deal with results][syntree-math] than [to
318//! simply panic][rowan-math] I believe that the end result is more robust
319//! software.
320//!
321//! <br>
322//!
323//! ## Performance and memory use
324//!
325//! The only goal in terms of performance is to be as performant as `rowan`. And
326//! cursory testing shows `syntree` to be a bit faster on synthetic workloads
327//! which can probably be solely attributed to storing and manipulating the
328//! entire tree in a single contiguous memory region. This might or might not
329//! change in the future, depending on if the needs for `syntree` changes. While
330//! performance is important, it *is not* a primary focus.
331//!
332//! I also expect (but haven't verified) that `syntree` could end up having a
333//! similarly low memory profile as `rowan` which performs node deduplication.
334//! The one caveat is that it depends on how the original source is stored and
335//! queried. Something which `rowan` solves for you, but `syntree` leaves as an
336//! exercise to the reader.
337//!
338//! <br>
339//!
340//! ## Features
341//!
342//! * `alloc` (default) stores trees in `Vec`s.
343//! * `std` (default) enables [`edit`] and [`print`], and implies `alloc`.
344//!
345//! Without `alloc` the default storage is [`EmptyVec`], which discards
346//! everything pushed into it. A tree built with [`Builder::new`] or any flavor
347//! using default storage then holds nothing, and building it fails with an
348//! error such as [`Error::MissingNode`]. To build trees without `alloc`,
349//! declare a flavor through [`flavor!`] with its own `type Storage<T>` and
350//! `type Indexes`, such as a fixed-capacity array implementing [`Storage`].
351//!
352//! The crate always needs an allocator, since checkpoints are reference
353//! counted.
354//!
355//! [`Builder::new_with`]: https://docs.rs/syntree/latest/syntree/struct.Builder.html#method.new_with
356//! [`Builder::new`]: https://docs.rs/syntree/latest/syntree/struct.Builder.html#method.new
357//! [`Builder`]: https://docs.rs/syntree/latest/syntree/struct.Builder.html
358//! [`close`]: https://docs.rs/syntree/latest/syntree/struct.Builder.html#method.close
359//! [`edit`]: https://docs.rs/syntree/latest/syntree/edit/index.html
360//! [`Empty`]: https://docs.rs/syntree/latest/syntree/struct.Empty.html
361//! [`EmptyVec`]: https://docs.rs/syntree/latest/syntree/struct.EmptyVec.html
362//! [`Error::Overflow`]: https://docs.rs/syntree/latest/syntree/enum.Error.html#variant.Overflow
363//! [`Error::MissingNode`]: https://docs.rs/syntree/latest/syntree/enum.Error.html#variant.MissingNode
364//! [`Error`]: https://docs.rs/syntree/latest/syntree/enum.Error.html
365//! [`Flavor`]: https://docs.rs/syntree/latest/syntree/trait.Flavor.html
366//! [`FlavorDefault`]: https://docs.rs/syntree/latest/syntree/struct.FlavorDefault.html
367//! [`flavor!`]: https://docs.rs/syntree/latest/syntree/macro.flavor.html
368//! [`open`]: https://docs.rs/syntree/latest/syntree/struct.Builder.html#method.open
369//! [`print_with_source`]: https://docs.rs/syntree/latest/syntree/print/fn.print_with_source.html
370//! [`print`]: https://docs.rs/syntree/latest/syntree/print/index.html
371//! [`rowan`]: https://docs.rs/rowan/latest/rowan/
372//! [`Span`]: https://docs.rs/syntree/latest/syntree/struct.Span.html
373//! [`Storage`]: https://docs.rs/syntree/latest/syntree/trait.Storage.html
374//! [`syntree::tree!`]: https://docs.rs/syntree/latest/syntree/macro.tree.html
375//! [`token`]: https://docs.rs/syntree/latest/syntree/struct.Builder.html#method.token
376//! [`Tree::node_with_span`]: https://docs.rs/syntree/latest/syntree/struct.Tree.html#method.node_with_span
377//! [`Tree`]: https://docs.rs/syntree/latest/syntree/struct.Tree.html
378//! [abstract syntax trees]: https://en.wikipedia.org/wiki/Abstract_syntax_tree
379//! [any-syntax]: https://github.com/udoprog/syntree/blob/main/examples/iterator.rs
380//! [calculator]: https://github.com/udoprog/syntree/blob/main/examples/calculator
381//! [kind-str]: https://github.com/rune-rs/rune/blob/e97a32e/crates/rune/src/ast/generated.rs#L4359
382//! [OxidizeBot]: https://github.com/udoprog/OxidizeBot
383//! [pratt]: https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
384//! [rowan-math]: https://github.com/rust-analyzer/rowan/blob/master/examples/math.rs
385//! [rune-macros]: https://github.com/rune-rs/rune/blob/main/crates/rune-modules/src/core.rs#L36
386//! [Rune]: https://github.com/rune-rs/rune
387//! [synthetic_strings]: https://github.com/udoprog/syntree/blob/main/examples/synthetic_strings.rs
388//! [syntree-math]: https://github.com/udoprog/syntree/blob/main/examples/math.rs
389
390#![deny(missing_docs)]
391#![cfg_attr(docsrs, feature(doc_cfg))]
392#![no_std]
393
394extern crate alloc;
395
396#[cfg(feature = "std")]
397extern crate std;
398
399#[macro_use]
400mod macros;
401mod builder;
402
403#[cfg(feature = "std")]
404#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
405pub mod edit;
406
407mod empty;
408mod error;
409#[macro_use]
410mod flavor;
411mod index;
412mod links;
413pub mod node;
414pub mod pointer;
415pub mod print;
416mod span;
417mod tree;
418
419#[doc(inline)]
420pub use self::builder::{Builder, Checkpoint};
421#[doc(inline)]
422pub use self::empty::{Empty, EmptyVec};
423#[doc(inline)]
424pub use self::error::Error;
425#[doc(inline)]
426pub use self::flavor::{Flavor, FlavorDefault, Storage};
427#[doc(inline)]
428pub use self::index::{Index, Length, TreeIndex};
429#[doc(inline)]
430pub use self::node::node_impl::Node;
431#[doc(inline)]
432pub use self::pointer::{Pointer, Width};
433#[doc(inline)]
434pub use self::span::Span;
435#[doc(inline)]
436pub use self::tree::Tree;
437
438#[doc(hidden)]
439pub mod macro_support {
440    use crate::index::TreeIndex;
441
442    #[cfg(feature = "alloc")]
443    pub type Vec<T> = alloc::vec::Vec<T>;
444
445    #[cfg(not(feature = "alloc"))]
446    pub type Vec<T> = crate::empty::EmptyVec<T>;
447
448    pub type DefaultIndexes<F> = crate::macro_support::Vec<TreeIndex<F>>;
449}