unsynn/
lib.rs

1#![doc = include_str!("../README.md")]
2//!
3//! # Detailed Introduction / Cookbook
4//!
5//! For a more detailed introduction about how to use unsynn see the
6//! [Cookbook](Parse#cookbook) section in the `Parse` trait.
7//!
8//! # Roadmap
9//!
10#![doc = include_str!("../ROADMAP.md")]
11
12use shadow_counted::ShadowCountedIter;
13
14/// Type alias for the iterator type we use for parsing. This Iterator is Clone and produces
15/// `&TokenTree`. The shadow counter counts tokens in the background to track progress which
16/// is used to keep the error that made the most progress in disjunctive parsers.
17pub type TokenIter<'a> = ShadowCountedIter<'a, <TokenStream as IntoIterator>::IntoIter>;
18
19/// The `Parser` trait that must be implemented by anything we want to parse. We are parsing
20/// over a [`TokenIter`] ([`proc_macro2::TokenStream`] iterator).
21pub trait Parser
22where
23    Self: Sized,
24{
25    /// The actual parsing function that must be implemented. This mutates the `tokens`
26    /// iterator directly. It should not be called from user code except for implementing
27    /// parsers itself and then only when the rules below are followed.
28    ///
29    /// # Implementing Parsers
30    ///
31    /// The parsers for [`TokenStream`], [`TokenTree`], [`Group`], [`Ident`], [`Punct`],
32    /// [`Literal`], [`Except`] and [`Nothing`] (and few more) are the fundamental parsers.
33    /// Any other parser is composed from those.
34    ///
35    /// Calling another `T::parser()` implementation is only valid when this is a conjunctive
36    /// operation and a failure is returned immediately by the `?` operator. This can be used
37    /// as performance optimization. Any other call to a parser must be done within a transaction.
38    /// Otherwise the iterator will be left in a consumed state which breaks further parsing.
39    ///
40    /// Transactions can be done by calling [`Parse::parse()`] or with the
41    /// [`Transaction::transaction()`] method on the iterator.
42    ///
43    /// # Errors
44    ///
45    /// The `parser()` implementation must return an error when it cannot parse the
46    /// input. This error must be a [`Error`]. User code will parse a grammar by calling
47    /// [`Parse::parse_all()`], [`Parse::parse()`] or [`Parse::parse_with()`] which will call
48    /// this method within a transaction and roll back on error.
49    fn parser(tokens: &mut TokenIter) -> Result<Self>;
50}
51
52/// This trait provides the user facing API to parse grammatical entities. It is implemented
53/// for anything that implements the [`Parser`] trait. The methods here encapsulating the
54/// iterator that is used for parsing into a transaction. This iterator is always
55/// `Copy`. Instead using a peekable iterator or implementing deeper peeking, parse clones
56/// this iterator to make access transactional, when parsing succeeds then the transaction
57/// becomes committed, otherwise it is rolled back.
58///
59/// This trait cannot be implemented by user code.
60#[doc = include_str!("../COOKBOOK.md")]
61pub trait Parse
62where
63    Self: Parser,
64{
65    /// This is the user facing API to parse grammatical entities. Calls a `parser()` within a
66    /// transaction. Commits changes on success and returns the parsed value.
67    ///
68    /// # Errors
69    ///
70    /// When the parser returns an error the transaction is rolled back and the error is
71    /// returned.
72    #[inline]
73    fn parse(tokens: &mut TokenIter) -> Result<Self> {
74        tokens.transaction(Self::parser)
75    }
76
77    /// Exhaustive parsing within a transaction. This is a convenience method that implies a
78    /// `EndOfStream` at the end. Thus it will error if parsing is not exhaustive.
79    ///
80    /// # Errors
81    ///
82    /// When the parser returns an error or there are tokens left in the stream the
83    /// transaction is rolled back and a error is returned.
84    #[inline]
85    fn parse_all(tokens: &mut TokenIter) -> Result<Self> {
86        tokens
87            .transaction(Cons::<Self, EndOfStream>::parser)
88            .map(|result| result.first)
89    }
90
91    /// Parse a value in a transaction, pass it to a
92    /// `FnOnce(Self, &mut TokenIter) -> Result<T>` closure which
93    /// creates a new result or returns an Error.
94    ///
95    /// This method is a very powerful tool as it allows anything from simple validations to
96    /// complete transformations into a new type. You may find this useful to implement
97    /// parsers for complex types that need some runtime logic.
98    ///
99    /// The closures first argument is the parsed value and the second argument is the
100    /// transactional iterator pointing after parsing `Self`. This can be used to create
101    /// errors or parse further. In many cases it can be ignored with `_`.
102    ///
103    /// # Example
104    ///
105    /// ```rust
106    /// # use unsynn::*;
107    /// # use std::collections::BTreeSet;
108    /// // A parser that parses a comma delimited list of anything but commas
109    /// // and stores these lexical sorted.
110    /// struct OrderedStrings {
111    ///     strings: Vec<String>
112    /// }
113    ///
114    /// impl Parser for OrderedStrings {
115    ///     fn parser(tokens: &mut TokenIter) -> Result<Self> {
116    ///         // Our input is CommaDelimitedVec<String>, we'll transform that into
117    ///         // OrderedStrings.
118    ///         Parse::parse_with(tokens, |this : CommaDelimitedVec<String>, _| {
119    ///             let mut strings: Vec<String> = this.into_iter()
120    ///                 .map(|s| s.value)
121    ///                 .collect();
122    ///             strings.sort();
123    ///             Ok(OrderedStrings { strings })
124    ///         })
125    ///     }
126    /// }
127    /// let mut input = "a, d, b, e, c,".to_token_iter();
128    /// let ordered_strings: OrderedStrings = input.parse().unwrap();
129    /// assert_eq!(ordered_strings.strings, vec!["a", "b", "c", "d", "e"]);
130    /// ```
131    ///
132    /// # Errors
133    ///
134    /// When the parser or the closure returns an error, the transaction is rolled back and
135    /// the error is returned.
136    fn parse_with<T>(
137        tokens: &mut TokenIter,
138        f: impl FnOnce(Self, &mut TokenIter) -> Result<T>,
139    ) -> Result<T> {
140        tokens.transaction(|tokens| {
141            let result = Self::parser(tokens)?;
142            f(result, tokens)
143        })
144    }
145}
146
147/// Parse is implemented for anything that implements [`Parser`].
148impl<T: Parser> Parse for T {}
149
150/// unsynn defines its own [`ToTokens`] trait to be able to implement it for std container types.
151/// This is similar to the `ToTokens` from the quote crate but adds some extra methods and is
152/// implemented for more types. Moreover the `to_token_iter()` method is the main entry point
153/// for crating an iterator that can be used for parsing.
154pub trait ToTokens {
155    /// Write `&self` to the given [`TokenStream`].
156    fn to_tokens(&self, tokens: &mut TokenStream);
157
158    /// Convert `&self` into a [`TokenIter`] object.
159    fn to_token_iter(&self) -> TokenIter {
160        self.to_token_stream().into_iter().into()
161    }
162
163    /// Convert `self` into a [`TokenIter`] object.
164    fn into_token_iter<'a>(self) -> TokenIter<'a>
165    where
166        Self: Sized,
167    {
168        self.into_token_stream().into_iter().into()
169    }
170
171    /// Convert `&self` into a [`TokenStream`] object.
172    fn to_token_stream(&self) -> TokenStream {
173        let mut tokens = TokenStream::new();
174        self.to_tokens(&mut tokens);
175        tokens
176    }
177
178    /// Convert `self` into a [`TokenStream`] object.
179    #[inline]
180    #[mutants::skip]
181    fn into_token_stream(self) -> TokenStream
182    where
183        Self: Sized,
184    {
185        self.to_token_stream()
186    }
187
188    /// Convert `&self` into a [`String`] object.  This is mostly used in the test suite to
189    /// compare the outputs.  When the input is a `&str` then this parses it and returns a
190    /// normalized [`String`].
191    fn tokens_to_string(&self) -> String {
192        self.to_token_stream().to_string()
193    }
194}
195
196// Full circle
197impl ToTokens for TokenIter<'_> {
198    fn to_tokens(&self, tokens: &mut TokenStream) {
199        tokens.extend(self.clone());
200    }
201}
202
203/// implement `Display` using `ToTokens::tokens_to_string()` for all types that implement `ToTokens`
204impl std::fmt::Display for dyn ToTokens {
205    #[mutants::skip]
206    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207        write!(f, "{}", self.tokens_to_string())
208    }
209}
210
211/// Extension trait for [`TokenIter`] that calls [`Parse::parse()`].
212#[allow(clippy::missing_errors_doc)]
213pub trait IParse: private::Sealed {
214    /// Parse a value from the iterator. This is a convenience method that calls
215    /// [`Parse::parse()`].
216    fn parse<T: Parse>(self) -> Result<T>;
217
218    /// Parse a value from the iterator. This is a convenience method that calls
219    /// [`Parse::parse_all()`].
220    fn parse_all<T: Parse>(self) -> Result<T>;
221}
222
223impl private::Sealed for &mut TokenIter<'_> {}
224
225/// Implements [`IParse`] for [`&mut TokenIter`]. This API is more convenient in cases where the
226/// compiler can infer types because no turbofish notations are required.
227///
228/// # Example
229///
230/// ```rust
231/// use unsynn::*;
232///
233/// struct MyStruct {
234///     number: LiteralInteger,
235///     name:   Ident,
236/// }
237///
238/// fn example() -> Result<MyStruct> {
239///     let mut input = " 1234 name ".to_token_iter();
240///     Ok(
241///         MyStruct {
242///             // types are inferred here
243///             number: input.parse()?,
244///             name: input.parse()?
245///         }
246///     )
247/// }
248/// ```
249impl IParse for &mut TokenIter<'_> {
250    #[inline]
251    fn parse<T: Parse>(self) -> Result<T> {
252        T::parse(self)
253    }
254
255    #[inline]
256    fn parse_all<T: Parse>(self) -> Result<T> {
257        T::parse_all(self)
258    }
259}
260
261/// Helper trait to make [`TokenIter`] transactional
262pub trait Transaction: Clone {
263    /// Transaction on a [`TokenIter`], calls a `FnOnce(&mut TokenIter) -> Result<T>` within a
264    /// transaction. When the closure succeeds, then the transaction is committed and its result
265    /// is returned.
266    ///
267    /// # Errors
268    ///
269    /// When the closure returns an error, the transaction is rolled back and the error
270    /// is returned.
271    fn transaction<R>(&mut self, f: impl FnOnce(&mut Self) -> Result<R>) -> Result<R> {
272        let mut ttokens = self.clone();
273        #[allow(clippy::manual_inspect)] // not pre 1.81
274        f(&mut ttokens).map(|result| {
275            *self = ttokens;
276            result
277        })
278    }
279}
280
281impl Transaction for TokenIter<'_> {}
282
283// Result and error type
284mod error;
285pub use error::*;
286
287// various declarative macros
288mod macros;
289
290// Parsers for the `proc_macro2` entities and other fundamental types
291pub mod fundamental;
292#[doc(inline)]
293pub use fundamental::*;
294
295// Groups by explicit bracket types
296pub mod group;
297#[doc(inline)]
298pub use group::*;
299
300// Punctuation, delimiters
301pub mod punct;
302#[doc(inline)]
303pub use punct::*;
304
305// operators
306pub mod operator;
307#[doc(inline)]
308pub use operator::{names::*, *};
309
310// Literals
311pub mod literal;
312#[doc(inline)]
313pub use literal::*;
314
315// Parse into certain rust types
316pub mod rust_types;
317#[doc(inline)]
318/* is this a bug in the linter when the module only implements traits? */
319//#[expect(unused_imports)] // don't want to bump msrv to 1.81 just for this
320#[allow(unused_imports)]
321pub use rust_types::*;
322
323// Delimited sequences
324pub mod delimited;
325#[doc(inline)]
326pub use delimited::*;
327
328// containers and smart pointers
329pub mod container;
330#[doc(inline)]
331pub use container::*;
332
333// combinators
334pub mod combinator;
335#[doc(inline)]
336pub use combinator::*;
337
338// helpers for the keyword macro
339#[doc(hidden)]
340pub mod keyword_group;
341pub use keyword_group::*;
342
343pub use proc_macro2::{
344    Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree,
345};
346
347mod private {
348    pub trait Sealed {}
349}