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}