from_pest/
lib.rs

1//! The [`FromPest`] conversion framework to convert from pest trees into typed structure.
2
3#[doc(hidden)]
4pub extern crate log;
5#[doc(hidden)]
6pub extern crate pest;
7extern crate void;
8
9#[doc(inline)]
10pub use void::Void;
11
12use {
13    pest::{
14        iterators::{Pair, Pairs},
15        RuleType,
16    },
17    std::marker::PhantomData,
18};
19
20/// An error that occurs during conversion.
21#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
22pub enum ConversionError<FatalError> {
23    /// No match occurred: this node is not present here
24    NoMatch,
25    /// Fatal error: this node is present but malformed
26    Malformed(FatalError),
27    /// Found unexpected tokens at the end
28    Extraneous { current_node: &'static str },
29}
30
31use std::fmt;
32
33impl<FatalError> fmt::Display for ConversionError<FatalError>
34where
35    FatalError: fmt::Display,
36{
37    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38        match self {
39            ConversionError::NoMatch => write!(f, "Rule did not match, failed to convert node"),
40            ConversionError::Malformed(fatalerror) => write!(f, "Malformed node: {fatalerror}"),
41            ConversionError::Extraneous { current_node, .. } => {
42                write!(f, "when converting {current_node}, found extraneous tokens")
43            }
44        }
45    }
46}
47
48use std::error;
49
50impl<FatalError> error::Error for ConversionError<FatalError>
51where
52    FatalError: error::Error + 'static,
53{
54    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
55        match self {
56            ConversionError::NoMatch => None,
57            ConversionError::Extraneous { .. } => None,
58            ConversionError::Malformed(ref fatalerror) => Some(fatalerror),
59        }
60    }
61}
62
63/// Potentially borrowing conversion from a pest parse tree.
64pub trait FromPest<'pest>: Sized {
65    /// The rule type for the parse tree this type corresponds to.
66    type Rule: RuleType;
67    /// A fatal error during conversion.
68    type FatalError;
69    /// Convert from a Pest parse tree.
70    ///
71    /// # Return type semantics
72    ///
73    /// - `Err(ConversionError::NoMatch)` => node not at head of the cursor, cursor unchanged
74    /// - `Err(ConversionError::Malformed)` => fatal error; node at head of the cursor but malformed
75    /// - `Ok` => success; the cursor has been updated past this node
76    fn from_pest(
77        pest: &mut Pairs<'pest, Self::Rule>,
78    ) -> Result<Self, ConversionError<Self::FatalError>>;
79}
80
81/// Convert a production without storing it.
82impl<'pest, Rule: RuleType, T: FromPest<'pest, Rule = Rule>> FromPest<'pest> for PhantomData<T> {
83    type Rule = Rule;
84    type FatalError = T::FatalError;
85    fn from_pest(pest: &mut Pairs<'pest, Rule>) -> Result<Self, ConversionError<T::FatalError>> {
86        T::from_pest(pest).map(|_| PhantomData)
87    }
88}
89
90/// For recursive grammars.
91impl<'pest, Rule: RuleType, T: FromPest<'pest, Rule = Rule>> FromPest<'pest> for Box<T> {
92    type Rule = Rule;
93    type FatalError = T::FatalError;
94    fn from_pest(pest: &mut Pairs<'pest, Rule>) -> Result<Self, ConversionError<T::FatalError>> {
95        T::from_pest(pest).map(Box::new)
96    }
97}
98
99/// Convert an optional production.
100impl<'pest, Rule: RuleType, T: FromPest<'pest, Rule = Rule>> FromPest<'pest> for Option<T> {
101    type Rule = Rule;
102    type FatalError = T::FatalError;
103    fn from_pest(pest: &mut Pairs<'pest, Rule>) -> Result<Self, ConversionError<T::FatalError>> {
104        match T::from_pest(pest) {
105            Err(ConversionError::NoMatch) => Ok(None),
106            result => result.map(Some),
107        }
108    }
109}
110
111/// Convert many productions. (If `<T as FromPest>` is non-advancing, this will be non-terminating.)
112impl<'pest, Rule: RuleType, T: FromPest<'pest, Rule = Rule>> FromPest<'pest> for Vec<T> {
113    type Rule = Rule;
114    type FatalError = T::FatalError;
115    fn from_pest(pest: &mut Pairs<'pest, Rule>) -> Result<Self, ConversionError<T::FatalError>> {
116        let mut acc = vec![];
117        loop {
118            match T::from_pest(pest) {
119                Ok(t) => acc.push(t),
120                Err(ConversionError::NoMatch) => break,
121                Err(error) => return Err(error),
122            }
123        }
124        Ok(acc)
125    }
126}
127
128/// Consume a production without doing any processing.
129impl<'pest, Rule: RuleType> FromPest<'pest> for Pair<'pest, Rule> {
130    type Rule = Rule;
131    type FatalError = Void;
132    fn from_pest(pest: &mut Pairs<'pest, Rule>) -> Result<Self, ConversionError<Void>> {
133        pest.next().ok_or(ConversionError::NoMatch)
134    }
135}
136
137macro_rules! impl_for_tuple {
138    () => {};
139    ($ty1:ident $($ty:ident)*) => {
140        impl<'pest, $ty1, $($ty,)* Rule: RuleType, FatalError> FromPest<'pest> for ($ty1, $($ty),*)
141        where
142            $ty1: FromPest<'pest, Rule=Rule, FatalError=FatalError>,
143            $($ty: FromPest<'pest, Rule=Rule, FatalError=FatalError>,)*
144        {
145            type Rule = Rule;
146            type FatalError = FatalError;
147            fn from_pest(pest: &mut Pairs<'pest, Rule>)
148                -> Result<Self, ConversionError<FatalError>>
149            {
150                let mut clone = pest.clone();
151                let this = (
152                    $ty1::from_pest(&mut clone)?,
153                    $($ty::from_pest(&mut clone)?),*
154                );
155                *pest = clone;
156                Ok(this)
157            }
158        }
159        impl_for_tuple!($($ty)*);
160    };
161}
162
163impl_for_tuple!(A B C D);