1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#![deny(missing_docs)]

/*!
Some utilities for parsing some format based on nested lists into arbitrary data structures.
It's also meant to be used as a backend for parsers.
**/

/// A trait required for all contexts being used for token parsing.
/// By default, only the empty tuple implements it.
/// 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.
pub trait Context {
    #[cfg(feature = "radix-parsing")]
    #[inline]
    /// Specifies the radix if the feature radix parsing is enabled.
    fn radix(&self) -> u32 {
        10
    }
}

impl Context for () {}

#[derive(Debug)]
/// The error type for token parsing.
pub enum Error {
    /// The sublist contains less elements than expected by a specified amount.
    NotEnoughElements(usize),
    /// The sublist contains more elements than expected by a specified amount.
    TooManyElements(usize),
    /// No list is allowed in this context.
    ListNotAllowed,
    /// No symbol is allowed in this context.
    SymbolNotAllowed,
    /// Error with string parsing.
    StringParsing,
    /// Some specific element is invalid.
    InvalidElement,
}

/// The result type for token parsing.
pub type Result<T> = std::result::Result<T, Error>;

/// Some unit, which represents an intermediate state.
pub enum Unit {
    /// The current unit is a single symbol.
    Symbol(String),
    /// The current unit is a parser, which can yield multiple units.
    Parser(Parser),
}

impl Unit {
    /// Returns the symbol, if applicable, as a result type.
    pub fn symbol(self) -> Result<String> {
        use Unit::*;
        match self {
            Symbol(name) => Ok(name),
            Parser(_) => Err(Error::ListNotAllowed),
        }
    }

    /// Returns the parser, if applicable, as a result type.
    pub fn parser(self) -> Result<Parser> {
        use Unit::*;
        match self {
            Symbol(_) => Err(Error::SymbolNotAllowed),
            Parser(parser) => Ok(parser),
        }
    }
}

/// This trait needs to be implemented for every struct which can be parsed using the token parser.
pub trait Parsable<C: Context>: Sized {
    /// When a symbol is found by the parser, this will be called.
    fn parse_symbol(_name: String, _context: &C) -> Result<Self> {
        Err(Error::SymbolNotAllowed)
    }

    /// When a subparser is found by the parser, this will be called.
    fn parse_list(_parser: &mut Parser, _context: &C) -> Result<Self> {
        Err(Error::ListNotAllowed)
    }
}

fn parse<C: Context, P: Parsable<C>>(unit: Unit, context: &C) -> Result<P> {
    use Unit::*;
    match unit {
        Symbol(name) => Parsable::parse_symbol(name, context),
        Parser(mut parser) => parser.parse_rest(context),
    }
}

impl<C: Context, T: Parsable<C>> Parsable<C> for Box<T> {
    fn parse_symbol(name: String, context: &C) -> Result<Self> {
        Ok(Box::new(Parsable::parse_symbol(name, context)?))
    }

    fn parse_list(parser: &mut Parser, context: &C) -> Result<Self> {
        Ok(Box::new(parser.parse_list(context)?))
    }
}

impl<C: Context, T: Parsable<C>> Parsable<C> for Vec<T> {
    fn parse_list(parser: &mut Parser, context: &C) -> Result<Self> {
        let Parser { form, count } = parser;
        let result = form
            .drain(..)
            .rev()
            .map(|unit| {
                *count += 1;
                parse(unit, context)
            })
            .collect();
        result
    }
}

impl<C: Context> Parsable<C> for String {
    fn parse_symbol(name: String, _context: &C) -> Result<Self> {
        Ok(name)
    }
}

#[macro_export]
/// Derives `Parsable` from symbol for types which implement `FromStr`.
macro_rules! derive_symbol_parsable {
    ($t:ty) => {
        impl<C: $crate::Context> $crate::Parsable<C> for $t {
            fn parse_symbol(name: String, _context: &C) -> $crate::Result<Self> {
                if let Some(value) = name.parse().ok() {
                    Ok(value)
                } else {
                    Err($crate::Error::StringParsing)
                }
            }
        }
    };
    ($t:ty, $($rest:ty),+) => {
        derive_symbol_parsable!($t);
        derive_symbol_parsable!($($rest),+);
    };
}

#[cfg(not(feature = "radix-parsing"))]
mod numbers;
derive_symbol_parsable!(bool);

/// The token parser to parse the units into wanted types.
pub struct Parser {
    form: Vec<Unit>,
    count: usize,
}

impl Parser {
    /// Creates a new parser from a list of objects.
    pub fn new<I: IntoIterator>(form: I) -> Self
    where
        I::Item: Into<Unit>,
    {
        let mut form: Vec<_> = form.into_iter().map(|unit| unit.into()).collect();
        form.reverse();
        Self { form, count: 0 }
    }

    /// Tries to parse the next unit as the required type.
    pub fn parse_next<C: Context, T: Parsable<C>>(&mut self, context: &C) -> Result<T> {
        self.count += 1;
        if let Some(token) = self.form.pop() {
            parse(token.into(), context)
        } else {
            Result::Err(Error::NotEnoughElements(self.count))
        }
    }

    /// Tries to parse the rest of the current list into the required type.
    /// If not every available token is used, this will be an error.
    pub fn parse_rest<C: Context, T: Parsable<C>>(&mut self, context: &C) -> Result<T> {
        let result = self.parse_list(context);
        if let Some(_) = self.form.pop() {
            let mut count = 1;
            while let Some(_) = self.form.pop() {
                count += 1;
            }
            Err(Error::TooManyElements(count))
        } else {
            result
        }
    }

    /// Tries to parse as many tokens of the current list as needed into the required type.
    pub fn parse_list<C: Context, T: Parsable<C>>(&mut self, context: &C) -> Result<T> {
        Parsable::parse_list(self, context)
    }
}

impl Iterator for Parser {
    type Item = Result<Parser>;

    fn next(&mut self) -> Option<Result<Parser>> {
        self.count += 1;
        Some(self.form.pop()?.parser())
    }
}

#[cfg(feature = "radix-parsing")]
/// Contains utilities for radix parsing.
pub mod radix;