token_parser/
lib.rs

1#![deny(missing_docs)]
2
3/*!
4Some utilities for parsing some format based on nested lists into arbitrary data structures.
5It's also meant to be used as a backend for parsers.
6**/
7
8use thiserror::Error;
9
10/// A trait required for all contexts being used for token parsing.
11///
12/// By default, only the empty tuple implements it.
13/// It currently does not contain anything by default. It's just there to achieve compatibility with features and to allow more changes without breaking anything.
14pub trait Context {
15    #[cfg(feature = "radix-parsing")]
16    #[inline]
17    /// Specifies the radix if the feature radix parsing is enabled.
18    fn radix(&self) -> u32 {
19        10
20    }
21}
22
23impl Context for () {}
24
25/// The error type for token parsing.
26#[derive(Debug, Error)]
27pub enum Error {
28    /// The sublist contains less elements than expected by a specified amount.
29    #[error("Not enough elements: {0} more expected")]
30    NotEnoughElements(usize),
31
32    /// The sublist contains more elements than expected by a specified amount.
33    #[error("Too many elements: {0} less expected")]
34    TooManyElements(usize),
35
36    /// No list is allowed in this context.
37    #[error("List not allowed")]
38    ListNotAllowed,
39
40    /// No symbol is allowed in this context.
41    #[error("Symbol not allowed")]
42    SymbolNotAllowed,
43
44    /// Error with string parsing.
45    #[error("String parsing error")]
46    StringParsing,
47
48    /// Some specific element is invalid.
49    #[error("Invalid element")]
50    InvalidElement,
51}
52
53/// The result type for token parsing.
54pub type Result<T> = std::result::Result<T, Error>;
55
56/// Some unit, which represents an intermediate state.
57pub enum Unit {
58    /// The current unit is a single symbol.
59    Symbol(Box<str>),
60    /// The current unit is a parser, which can yield multiple units.
61    Parser(Parser),
62}
63
64impl Unit {
65    /// Returns the symbol, if applicable, as a result type.
66    pub fn symbol(self) -> Result<Box<str>> {
67        use Unit::*;
68        match self {
69            Symbol(name) => Ok(name),
70            Parser(_) => Err(Error::ListNotAllowed),
71        }
72    }
73
74    /// Returns the parser, if applicable, as a result type.
75    pub fn parser(self) -> Result<Parser> {
76        use Unit::*;
77        match self {
78            Symbol(_) => Err(Error::SymbolNotAllowed),
79            Parser(parser) => Ok(parser),
80        }
81    }
82}
83
84/// This trait needs to be implemented for every struct which can be parsed using the token parser.
85pub trait Parsable<C: Context>: Sized {
86    /// When a symbol is found by the parser, this will be called.
87    fn parse_symbol(_name: Box<str>, _context: &C) -> Result<Self> {
88        Err(Error::SymbolNotAllowed)
89    }
90
91    /// When a subparser is found by the parser, this will be called.
92    fn parse_list(_parser: &mut Parser, _context: &C) -> Result<Self> {
93        Err(Error::ListNotAllowed)
94    }
95}
96
97fn parse<C: Context, P: Parsable<C>>(unit: Unit, context: &C) -> Result<P> {
98    use Unit::*;
99    match unit {
100        Symbol(name) => Parsable::parse_symbol(name, context),
101        Parser(mut parser) => parser.parse_rest(context),
102    }
103}
104
105impl<C: Context, T: Parsable<C>> Parsable<C> for Box<T> {
106    fn parse_symbol(name: Box<str>, context: &C) -> Result<Self> {
107        Ok(Self::new(Parsable::parse_symbol(name, context)?))
108    }
109
110    fn parse_list(parser: &mut Parser, context: &C) -> Result<Self> {
111        Ok(Self::new(parser.parse_list(context)?))
112    }
113}
114
115impl<C: Context, T: Parsable<C>> Parsable<C> for Vec<T> {
116    fn parse_list(parser: &mut Parser, context: &C) -> Result<Self> {
117        let Parser { form, count } = parser;
118        let result = form
119            .drain(..)
120            .rev()
121            .map(|unit| {
122                *count += 1;
123                parse(unit, context)
124            })
125            .collect();
126        result
127    }
128}
129
130impl<C: Context> Parsable<C> for String {
131    fn parse_symbol(name: Box<str>, _context: &C) -> Result<Self> {
132        Ok(name.into())
133    }
134}
135
136impl<C: Context> Parsable<C> for Box<str> {
137    fn parse_symbol(name: Box<str>, _context: &C) -> Result<Self> {
138        Ok(name)
139    }
140}
141
142#[macro_export]
143/// Derives `Parsable` from symbol for types which implement `FromStr`.
144macro_rules! derive_symbol_parsable {
145    ($t:ty) => {
146        impl<C: $crate::Context> $crate::Parsable<C> for $t {
147            fn parse_symbol(name: Box<str>, _context: &C) -> $crate::Result<Self> {
148                if let Ok(value) = name.parse() {
149                    Ok(value)
150                } else {
151                    Err($crate::Error::StringParsing)
152                }
153            }
154        }
155    };
156    ($t:ty, $($rest:ty),+) => {
157        derive_symbol_parsable!($t);
158        derive_symbol_parsable!($($rest),+);
159    };
160}
161
162#[cfg(not(feature = "radix-parsing"))]
163mod numbers;
164derive_symbol_parsable!(bool);
165
166/// The token parser to parse the units into wanted types.
167pub struct Parser {
168    form: Vec<Unit>,
169    count: usize,
170}
171
172impl Parser {
173    /// Creates a new parser from a list of objects.
174    pub fn new<I: IntoIterator>(form: I) -> Self
175    where
176        I::Item: Into<Unit>,
177    {
178        let mut form: Vec<_> = form.into_iter().map(I::Item::into).collect();
179        form.reverse();
180        Self { form, count: 0 }
181    }
182
183    /// Tries to parse the next unit as the required type.
184    pub fn parse_next<C: Context, T: Parsable<C>>(&mut self, context: &C) -> Result<T> {
185        self.count += 1;
186        if let Some(token) = self.form.pop() {
187            parse(token, context)
188        } else {
189            Result::Err(Error::NotEnoughElements(self.count))
190        }
191    }
192
193    /// Tries to parse the rest of the current list into the required type.
194    /// If not every available token is used, this will be an error.
195    pub fn parse_rest<C: Context, T: Parsable<C>>(&mut self, context: &C) -> Result<T> {
196        let result = self.parse_list(context);
197        let count = self.form.len();
198        if count > 0 {
199            self.form.clear();
200            Err(Error::TooManyElements(count))
201        } else {
202            result
203        }
204    }
205
206    /// Tries to parse as many tokens of the current list as needed into the required type.
207    pub fn parse_list<C: Context, T: Parsable<C>>(&mut self, context: &C) -> Result<T> {
208        Parsable::parse_list(self, context)
209    }
210}
211
212impl Iterator for Parser {
213    type Item = Result<Self>;
214
215    fn next(&mut self) -> Option<Result<Self>> {
216        self.count += 1;
217        Some(self.form.pop()?.parser())
218    }
219}
220
221#[cfg(feature = "radix-parsing")]
222/// Contains utilities for radix parsing.
223pub mod radix;