Skip to main content

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