unsynn/
combinator.rs

1//! A unique feature of unsynn is that one can define a parser as a composition of other
2//! parsers on the fly without the need to define custom structures. This is done by using the
3//! [`Cons`] and [`Either`] types. The [`Cons`] type is used to define a parser that is a
4//! conjunction of two to four other parsers, while the [`Either`] type is used to define a
5//! parser that is a disjunction of two to four other parsers.
6
7use crate::{Error, Invalid, Nothing, Parse, Parser, Result, ToTokens, TokenIter, TokenStream};
8
9/// Conjunctive `A` followed by `B` and optional `C` and `D`
10/// When `C` and `D` are not used, they are set to [`Nothing`].
11#[derive(Clone)]
12pub struct Cons<A, B, C = Nothing, D = Nothing> {
13    /// The first value
14    pub first: A,
15    /// The second value
16    pub second: B,
17    /// The third value
18    pub third: C,
19    /// The fourth value
20    pub fourth: D,
21}
22
23impl<A, B, C: 'static, D: 'static> Cons<A, B, C, D> {
24    /// Return the number of consecutive used items (not `Nothing`) in a `Cons`
25    ///
26    /// # Panics
27    ///
28    /// Asserts that the `Cons` is not sparse, if `C` is not `Nothing` then `D` must not be
29    /// `Nothing` either.
30    fn used_conjunctions() -> usize {
31        let mut len = 2;
32        // PLANNED: static NOTHING: TypeId once stable
33        let nothing = std::any::TypeId::of::<Nothing>();
34        if std::any::TypeId::of::<C>() != nothing {
35            len += 1;
36        }
37        if std::any::TypeId::of::<D>() != nothing {
38            assert_ne!(
39                std::any::TypeId::of::<C>(),
40                nothing,
41                "If C is not Nothing then D must be Nothing"
42            );
43            len += 1;
44        }
45        len
46    }
47}
48
49impl<A: Parse, B: Parse, C: Parse, D: Parse> Parser for Cons<A, B, C, D> {
50    fn parser(tokens: &mut TokenIter) -> Result<Self> {
51        Ok(Self {
52            first: A::parser(tokens)?,
53            second: B::parser(tokens)?,
54            third: C::parser(tokens)?,
55            fourth: D::parser(tokens)?,
56        })
57    }
58}
59
60impl<A: ToTokens, B: ToTokens, C: ToTokens, D: ToTokens> ToTokens for Cons<A, B, C, D> {
61    fn to_tokens(&self, tokens: &mut TokenStream) {
62        self.first.to_tokens(tokens);
63        self.second.to_tokens(tokens);
64        self.third.to_tokens(tokens);
65        self.fourth.to_tokens(tokens);
66    }
67}
68
69impl<A, B> From<Cons<A, B>> for (A, B) {
70    fn from(cons: Cons<A, B>) -> Self {
71        (cons.first, cons.second)
72    }
73}
74
75impl<A, B, C> From<Cons<A, B, C>> for (A, B, C) {
76    fn from(cons: Cons<A, B, C>) -> Self {
77        (cons.first, cons.second, cons.third)
78    }
79}
80
81impl<A, B, C, D> From<Cons<A, B, C, D>> for (A, B, C, D) {
82    fn from(cons: Cons<A, B, C, D>) -> Self {
83        (cons.first, cons.second, cons.third, cons.fourth)
84    }
85}
86
87#[mutants::skip]
88impl<A, B, C, D> std::fmt::Debug for Cons<A, B, C, D>
89where
90    A: std::fmt::Debug,
91    B: std::fmt::Debug,
92    C: std::fmt::Debug + 'static,
93    D: std::fmt::Debug + 'static,
94{
95    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
96        match Self::used_conjunctions() {
97            1 | 2 => f
98                .debug_struct(&format!(
99                    "Cons<{}, {}>",
100                    std::any::type_name::<A>(),
101                    std::any::type_name::<B>(),
102                ))
103                .field("first", &self.first)
104                .field("second", &self.second)
105                .finish(),
106            3 => f
107                .debug_struct(&format!(
108                    "Cons<{}, {}, {}>",
109                    std::any::type_name::<A>(),
110                    std::any::type_name::<B>(),
111                    std::any::type_name::<C>(),
112                ))
113                .field("first", &self.first)
114                .field("second", &self.second)
115                .field("third", &self.third)
116                .finish(),
117            _ => f
118                .debug_struct(&format!(
119                    "Cons<{}, {}, {}, {}>",
120                    std::any::type_name::<A>(),
121                    std::any::type_name::<B>(),
122                    std::any::type_name::<C>(),
123                    std::any::type_name::<D>(),
124                ))
125                .field("first", &self.first)
126                .field("second", &self.second)
127                .field("third", &self.third)
128                .field("fourth", &self.fourth)
129                .finish(),
130        }
131    }
132}
133
134/// Disjunctive `A` or `B` or optional `C` or `D`  tried in that order.
135/// When `C` and `D` are not used, they are set to [`Invalid`].
136#[derive(Clone)]
137pub enum Either<A, B, C = Invalid, D = Invalid> {
138    /// The first alternative
139    First(A),
140    /// The second alternative
141    Second(B),
142    /// The third alternative
143    Third(C),
144    /// The fourth alternative
145    Fourth(D),
146}
147
148impl<A, B, C: 'static, D: 'static> Either<A, B, C, D> {
149    /// Return the number of consecutive used items (not `Invalid`) in a `Either`
150    ///
151    /// # Panics
152    ///
153    /// Asserts that the `Either` is not sparse, if `C` is not `Invalid` then `D` must not be
154    /// `Invalid` either.
155    fn used_disjunctions() -> usize {
156        let mut len = 2;
157        // PLANNED: static NOTHING: TypeId once stable
158        let invalid = std::any::TypeId::of::<Invalid>();
159        if std::any::TypeId::of::<C>() != invalid {
160            len += 1;
161        }
162        if std::any::TypeId::of::<D>() != invalid {
163            assert_ne!(
164                std::any::TypeId::of::<C>(),
165                invalid,
166                "If C is not Invalid then D must be Invalid"
167            );
168            len += 1;
169        }
170        len
171    }
172
173    /// Deconstructs an `Either` with 2 alternatives and produces a common result type, by
174    /// applying one of the two functions depending on the alternative.
175    ///
176    /// # Panics
177    ///
178    /// When the variant is `Either::Third` or `Either::Fourth`
179    ///
180    /// # Example
181    ///
182    /// ```
183    /// # use unsynn::*;
184    /// let either = Either::<LiteralInteger, Ident>::First(LiteralInteger::new(42));
185    /// let result: String = either.fold2(
186    ///     |a| a.tokens_to_string(),
187    ///     |b| b.tokens_to_string(),
188    /// );
189    /// assert_eq!(result, "42");
190    /// ```
191    pub fn fold2<R, FA, FB>(self, first_fn: FA, second_fn: FB) -> R
192    where
193        FA: FnOnce(A) -> R,
194        FB: FnOnce(B) -> R,
195    {
196        debug_assert_eq!(Self::used_disjunctions(), 2);
197        match self {
198            Either::First(a) => first_fn(a),
199            Either::Second(b) => second_fn(b),
200            _ => unimplemented!(),
201        }
202    }
203
204    /// Deconstructs an `Either` with 3 alternatives and produces a common result type, by
205    /// applying one of the three functions depending on the alternative.
206    ///
207    /// # Panics
208    ///
209    /// When the variant is `Either::Fourth`
210    pub fn fold3<R, FA, FB, FC>(self, first_fn: FA, second_fn: FB, third_fn: FC) -> R
211    where
212        FA: FnOnce(A) -> R,
213        FB: FnOnce(B) -> R,
214        FC: FnOnce(C) -> R,
215    {
216        debug_assert_eq!(Self::used_disjunctions(), 3);
217        match self {
218            Either::First(a) => first_fn(a),
219            Either::Second(b) => second_fn(b),
220            Either::Third(c) => third_fn(c),
221            Either::Fourth(_) => unimplemented!(),
222        }
223    }
224
225    /// Deconstructs an `Either` with 4 alternatives and produces a common result type, by
226    /// applying one of the provided functions.
227    pub fn fold4<R, FA, FB, FC, FD>(
228        self,
229        first_fn: FA,
230        second_fn: FB,
231        third_fn: FC,
232        fourth_fn: FD,
233    ) -> R
234    where
235        FA: FnOnce(A) -> R,
236        FB: FnOnce(B) -> R,
237        FC: FnOnce(C) -> R,
238        FD: FnOnce(D) -> R,
239    {
240        debug_assert_eq!(Self::used_disjunctions(), 4);
241        match self {
242            Either::First(a) => first_fn(a),
243            Either::Second(b) => second_fn(b),
244            Either::Third(c) => third_fn(c),
245            Either::Fourth(d) => fourth_fn(d),
246        }
247    }
248
249    /// Deconstructs an `Either` with 2 alternatives and produces a common result type for
250    /// types that implement `Into<T>`.
251    ///
252    /// # Panics
253    ///
254    /// When more then two alternatives are used.
255    ///
256    /// # Example
257    ///
258    /// ```
259    /// # use unsynn::*;
260    /// let either = Either::<LiteralInteger, Ident>::First(LiteralInteger::new(42));
261    /// let tt: TokenTree = either.into2();
262    /// assert_eq!(tt.tokens_to_string(), "42");
263    /// ```
264    pub fn into2<T>(self) -> T
265    where
266        A: Into<T>,
267        B: Into<T>,
268    {
269        debug_assert_eq!(Self::used_disjunctions(), 2);
270        match self {
271            Either::First(a) => a.into(),
272            Either::Second(b) => b.into(),
273            _ => unimplemented!(),
274        }
275    }
276
277    /// Deconstructs an `Either` with 3 alternatives and produces a common result type for types
278    /// that implement `Into<T>`.
279    ///
280    /// # Panics
281    ///
282    /// When more then three alternatives are used.
283    pub fn into3<T>(self) -> T
284    where
285        A: Into<T>,
286        B: Into<T>,
287        C: Into<T>,
288    {
289        debug_assert_eq!(Self::used_disjunctions(), 3);
290        match self {
291            Either::First(a) => a.into(),
292            Either::Second(b) => b.into(),
293            Either::Third(c) => c.into(),
294            Either::Fourth(_) => unimplemented!(),
295        }
296    }
297
298    /// Deconstructs an `Either` with 4 alternatives and produces a common result type for types
299    /// that implement `Into<T>`.
300    pub fn into4<T>(self) -> T
301    where
302        A: Into<T>,
303        B: Into<T>,
304        C: Into<T>,
305        D: Into<T>,
306    {
307        debug_assert_eq!(Self::used_disjunctions(), 4);
308        match self {
309            Either::First(a) => a.into(),
310            Either::Second(b) => b.into(),
311            Either::Third(c) => c.into(),
312            Either::Fourth(d) => d.into(),
313        }
314    }
315}
316
317impl<A, B, C, D> Parser for Either<A, B, C, D>
318where
319    A: Parse,
320    B: Parse,
321    C: Parse,
322    D: Parse,
323{
324    fn parser(tokens: &mut TokenIter) -> Result<Self> {
325        let mut err = Error::no_error();
326
327        if let Ok(first) = err.upgrade(A::parse(tokens)) {
328            Ok(Either::First(first))
329        } else if let Ok(second) = err.upgrade(B::parse(tokens)) {
330            Ok(Either::Second(second))
331        } else if let Ok(third) = err.upgrade(C::parse(tokens)) {
332            Ok(Either::Third(third))
333        } else if let Ok(fourth) = err.upgrade(D::parse(tokens)) {
334            Ok(Either::Fourth(fourth))
335        } else {
336            Err(err)
337        }
338    }
339}
340
341impl<A, B, C, D> ToTokens for Either<A, B, C, D>
342where
343    A: ToTokens,
344    B: ToTokens,
345    C: ToTokens,
346    D: ToTokens,
347{
348    fn to_tokens(&self, tokens: &mut TokenStream) {
349        match self {
350            Either::First(a) => a.to_tokens(tokens),
351            Either::Second(b) => b.to_tokens(tokens),
352            Either::Third(c) => c.to_tokens(tokens),
353            Either::Fourth(d) => d.to_tokens(tokens),
354        }
355    }
356}
357
358#[mutants::skip]
359impl<A, B, C, D> std::fmt::Debug for Either<A, B, C, D>
360where
361    A: std::fmt::Debug,
362    B: std::fmt::Debug,
363    C: std::fmt::Debug + 'static,
364    D: std::fmt::Debug + 'static,
365{
366    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
367        let typename = match Self::used_disjunctions() {
368            1 | 2 => format!(
369                "Either<{}, {}>",
370                std::any::type_name::<A>(),
371                std::any::type_name::<B>()
372            ),
373            3 => format!(
374                "Either<{}, {}, {}>",
375                std::any::type_name::<A>(),
376                std::any::type_name::<B>(),
377                std::any::type_name::<C>()
378            ),
379            _ => format!(
380                "Either<{}, {}, {}, {}>",
381                std::any::type_name::<A>(),
382                std::any::type_name::<B>(),
383                std::any::type_name::<C>(),
384                std::any::type_name::<D>()
385            ),
386        };
387
388        match self {
389            Either::First(a) => f.debug_tuple(&typename).field(a).finish(),
390            Either::Second(b) => f.debug_tuple(&typename).field(b).finish(),
391            Either::Third(c) => f.debug_tuple(&typename).field(c).finish(),
392            Either::Fourth(d) => f.debug_tuple(&typename).field(d).finish(),
393        }
394    }
395}
396
397#[test]
398fn test_either_into_tt() {
399    use crate::{LiteralInteger, TokenTree};
400    let either = Either::<LiteralInteger, TokenTree>::First(LiteralInteger::new(42));
401    let _tt: TokenTree = either.into2();
402
403    let either = Either::<TokenTree, LiteralInteger>::Second(LiteralInteger::new(43));
404    let _tt: TokenTree = either.into2();
405}
406
407#[cfg(test)]
408mod tests {
409    use crate::*;
410    #[test]
411    fn test_cons_used_conjunctions() {
412        assert_eq!(Cons::<Punct, Ident>::used_conjunctions(), 2);
413        assert_eq!(Cons::<Punct, Ident, LiteralInteger>::used_conjunctions(), 3);
414        assert_eq!(
415            Cons::<Punct, Ident, LiteralInteger, LiteralCharacter>::used_conjunctions(),
416            4
417        );
418    }
419
420    #[test]
421    #[should_panic(expected = "If C is not Nothing then D must be Nothing")]
422    fn test_cons_invalid_nothing() {
423        Cons::<Punct, Ident, Nothing, LiteralInteger>::used_conjunctions();
424    }
425
426    #[test]
427    fn test_either_used_disjunctions() {
428        assert_eq!(Either::<Punct, Ident>::used_disjunctions(), 2);
429        assert_eq!(
430            Either::<Punct, Ident, LiteralInteger>::used_disjunctions(),
431            3
432        );
433        assert_eq!(
434            Either::<Punct, Ident, LiteralInteger, LiteralCharacter>::used_disjunctions(),
435            4
436        );
437    }
438
439    #[test]
440    #[should_panic(expected = "If C is not Invalid then D must be Invalid")]
441    fn test_either_invalid_invalid() {
442        Either::<Punct, Ident, Invalid, LiteralInteger>::used_disjunctions();
443    }
444}