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        form.drain(..)
119            .rev()
120            .map(|unit| {
121                *count += 1;
122                parse(unit, context)
123            })
124            .collect()
125    }
126}
127
128impl<C: Context> Parsable<C> for String {
129    fn parse_symbol(name: Box<str>, _context: &C) -> Result<Self> {
130        Ok(name.into())
131    }
132}
133
134impl<C: Context> Parsable<C> for Box<str> {
135    fn parse_symbol(name: Box<str>, _context: &C) -> Result<Self> {
136        Ok(name)
137    }
138}
139
140#[macro_export]
141/// Derives `Parsable` from symbol for types which implement `FromStr`.
142macro_rules! derive_symbol_parsable {
143    ($t:ty) => {
144        impl<C: $crate::Context> $crate::Parsable<C> for $t {
145            fn parse_symbol(name: Box<str>, _context: &C) -> $crate::Result<Self> {
146                if let Ok(value) = name.parse() {
147                    Ok(value)
148                } else {
149                    Err($crate::Error::StringParsing)
150                }
151            }
152        }
153    };
154    ($t:ty, $($rest:ty),+) => {
155        derive_symbol_parsable!($t);
156        derive_symbol_parsable!($($rest),+);
157    };
158}
159
160#[cfg(not(feature = "radix-parsing"))]
161mod numbers;
162derive_symbol_parsable!(bool);
163
164/// The token parser to parse the units into wanted types.
165pub struct Parser {
166    form: Vec<Unit>,
167    count: usize,
168}
169
170impl Parser {
171    /// Creates a new parser from a list of objects.
172    pub fn new<I: IntoIterator>(form: I) -> Self
173    where
174        I::Item: Into<Unit>,
175    {
176        let mut form: Vec<_> = form.into_iter().map(I::Item::into).collect();
177        form.reverse();
178        Self { form, count: 0 }
179    }
180
181    /// Tries to parse the next unit as the required type.
182    pub fn parse_next<C: Context, T: Parsable<C>>(&mut self, context: &C) -> Result<T> {
183        self.count += 1;
184        if let Some(token) = self.form.pop() {
185            parse(token, context)
186        } else {
187            Result::Err(Error::NotEnoughElements(self.count))
188        }
189    }
190
191    /// Tries to parse the rest of the current list into the required type.
192    /// If not every available token is used, this will be an error.
193    pub fn parse_rest<C: Context, T: Parsable<C>>(&mut self, context: &C) -> Result<T> {
194        let result = self.parse_list(context);
195        let count = self.form.len();
196        if count > 0 {
197            self.form.clear();
198            Err(Error::TooManyElements(count))
199        } else {
200            result
201        }
202    }
203
204    /// Tries to parse as many tokens of the current list as needed into the required type.
205    pub fn parse_list<C: Context, T: Parsable<C>>(&mut self, context: &C) -> Result<T> {
206        Parsable::parse_list(self, context)
207    }
208}
209
210impl Iterator for Parser {
211    type Item = Result<Self>;
212
213    fn next(&mut self) -> Option<Result<Self>> {
214        self.count += 1;
215        Some(self.form.pop()?.parser())
216    }
217}
218
219#[cfg(feature = "radix-parsing")]
220/// Contains utilities for radix parsing.
221pub mod radix;