tinyparse/
lib.rs

1pub mod error;
2pub mod grammar;
3pub mod common;
4
5use error::{ParseError, ParseResult, ParseErrorKind};
6use std::fmt::Display;
7
8/// Performs a conversion for any type that can be converted into a [Parser].
9pub trait IntoParser<'a, R> {
10    fn into_parser(self) -> Parser<'a, R>;
11}
12
13impl<'a, R, P: Parse<'a, R> + 'a> IntoParser<'a, R> for P {
14    fn into_parser(self) -> Parser<'a, R> {
15        Parser { parser: Box::new(self) }
16    }
17}
18
19// TODO: Each Parser should come with a grammar so that we can create parsers from just Grammar.
20// TODO: Implement optimizer that will change .or.or.or.or to just be one_of function. Same for and.
21/// Anything that is capable of Parsing from a [Span].
22pub trait Parse<'a, R> {
23    fn parse(&self, span: Span<'a>) -> ParseResult<'a, R>;
24
25    /// Maps the result of this parser to another value.
26    fn map<F, M>(self, map_fn: F) -> Parser<'a, M>
27    where
28        F: Fn(R) -> M + 'a,
29        Self: Sized + 'a,
30    {
31        (move |span| {
32            self.parse(span).map(|(span, res)| (span, map_fn(res)))
33        }).into_parser()
34    }
35
36    /// Runs the next parser but returns the result of this one. Essentially skipping the result of the `other_parser`.
37    fn skip<P, R2>(self, other_parser: P) -> Parser<'a, R>
38    where
39        P: Parse<'a, R2> + 'a,
40        Self: Sized + 'a,
41    {
42        (move |span| {
43            let (span, result) = self.parse(span)?;
44            let (span, _) = other_parser.parse(span)?;
45            Ok((span, result))
46        }).into_parser()
47    }
48
49    /// Maps the error of this parser to another error. (Only if there was one)
50    fn map_error<F>(self, map_fn: F) -> Parser<'a, R>
51    where
52        F: Fn(ParseError) -> ParseError + 'a,
53        Self: Sized + 'a,
54    {
55        (move |span| {
56            #[allow(clippy::redundant_closure)] // The closure consumes self if we just replace |err| map_fn(err) with the shorthand.
57            self.parse(span).map_err(|err| map_fn(err))
58        }).into_parser()
59    }
60
61    /// Verifies the result of this parser. If the predicate failed; a [ParseErrorKind::ConditionFailed] error is returned.
62    fn only_if<F>(self, pred: F) -> Parser<'a, R>
63    where
64        F: Fn(&R) -> bool + 'a,
65        Self: Sized + 'a,
66    {
67        (move |old_span| {
68            let (span, parsed) = self.parse(old_span)?;
69            if pred(&parsed) {
70                Ok((span, parsed))
71            } else {
72                Err(ParseError::new(old_span, ParseErrorKind::ConditionFailed))
73            }
74        }).into_parser()
75    }
76
77    /// If this parser fails, use the `val` as a result instead.
78    fn or_value(self, val: R) -> Parser<'a, R>
79    where
80        Self: Sized + 'a,
81        R: Clone + 'a,
82    {
83        (move |span| {
84            Ok(self.parse(span).unwrap_or((span, val.clone())))
85        }).into_parser()
86    }
87
88    /// Same as [Parse::or_value] but it computes the value from a Fn.
89    fn or_else_value<F>(self, compute: F) -> Parser<'a, R>
90    where
91        Self: Sized + 'a,
92        F: Fn() -> R + 'a,
93    {
94        (move |span| {
95            Ok(self.parse(span).unwrap_or_else(|_| (span, compute())))
96        }).into_parser()
97    }
98
99    /// Returns the result of this parser if it succeeded, otherwise it returns the result of `other_parser`.
100    /// 
101    /// # Errors
102    /// [ParseErrorKind::Neither] is returned when neither of the parsers successfully ran.
103    fn or<P: Parse<'a, R> + 'a>(self, other_parser: P) -> Parser<'a, R>
104    where
105        Self: Sized + 'a,
106    {
107        (move |span| {
108            match self.parse(span) {
109                ok @ Ok(_) => ok,
110                Err(error) => match other_parser.parse(span) {
111                    ok @ Ok(_) => ok,
112                    Err(second_error) => Err(ParseError::new(span, ParseErrorKind::Neither(vec![error, second_error])))
113                }
114            }
115        }).into_parser()
116    }
117
118    /// Combines this parser with another and returns the results in a tuple.
119    /// 
120    /// # Errors
121    /// No new errors are thrown by this function, only by the combining ones.
122    fn and<R2, P: Parse<'a, R2> + 'a>(self, other_parser: P) -> Parser<'a, (R, R2)>
123    where
124        Self: Sized + 'a,
125    {
126        (move |span| {
127            let (span, res0) = self.parse(span)?;
128            let (span, res1) = other_parser.parse(span)?;
129            Ok((span, (res0, res1)))
130        }).into_parser()
131    }
132
133    /// Runs this parser n or more times and returns the results in a [Vec].
134    /// 
135    /// # Errors
136    /// A [ParseErrorKind::Starving] error is returned when the parser could not match `n` times.
137    fn n_or_more(self, n: usize) -> Parser<'a, Vec<R>>
138    where
139        Self: Sized + 'a,
140    {
141        (move |mut span| {
142            let initial_span = span;
143            let mut results = Vec::with_capacity(n);
144            while let Ok((new_span, res)) = self.parse(span) {
145                results.push(res);
146                span = new_span;
147            }
148
149            if results.len() < n {
150                Err(ParseError::new(initial_span, ParseErrorKind::Starving { found: results.len(), required: n }))
151            } else {
152                Ok((span, results))
153            }
154        }).into_parser()
155    }
156}
157
158impl<'a, R, F> Parse<'a, R> for F
159where 
160    F: Fn(Span<'a>) -> ParseResult<'a, R>,
161{
162    fn parse(&self, span: Span<'a>) -> ParseResult<'a, R> {
163        self(span)
164    }
165}
166
167impl<'a, R> Parse<'a, R> for Parser<'a, R> {
168    fn parse(&self, span: Span<'a>) -> ParseResult<'a, R> {
169        self.parser.parse(span)
170    }
171}
172
173/// A container for the actual data we are parsing.
174/// 
175/// 
176/// # Usage
177/// 
178/// ```
179/// use tinyparse::{Parser, Span, ParseError, ParseErrorKind, IntoParser};
180/// 
181/// // You can use impl Parse<'a, &'a str> as the return type here if you don't want to do .into_parser().
182/// fn my_special_parser<'a>() -> Parser<'a, &'a str> {
183///     (move |span: Span<'a>| {
184///         let hello = "hello";
185///         if span.left.len() < hello.len() {
186///             Err(ParseError::new(span, ParseErrorKind::Starving { found: span.left.len(), required: hello.len() }))
187///         } else if &span.left[..hello.len()] == hello {
188///             Ok((span.incremented(hello.len()), "They really said hello!"))
189///         } else {
190///             Err(ParseError::new(span.until(hello.len()), ParseErrorKind::Unexpected { found: String::from(&span.left[..hello.len()]), expected: String::from(hello) }))
191///         }
192///     }).into_parser()
193/// }
194/// ```
195/// ## Note: This struct is only commonly used for calling the [Parse::parse] function. The usage above can be recreated with the provided parsers already.
196#[derive(Debug, Clone, Copy, PartialEq, Eq)]
197pub struct Span<'a> {
198    src_idx: usize,
199    src: &'a str,
200    pub left: &'a str,
201}
202
203impl<'a> Span<'a> {
204    pub fn new(left: &'a str) -> Span<'a> {
205        Span { left, src: left, src_idx: 0 }
206    }
207
208    pub fn incremented(&self, n: usize) -> Span<'a> {
209        Span {
210            left: &self.left[n..],
211            src: self.src,
212            src_idx: self.src_idx + n,
213        }
214    }
215
216    pub fn until(&self, n: usize) -> Span<'a> {
217        Span { left: &self.left[..n], src: self.src, src_idx: self.src_idx }
218    }
219
220    pub fn until_span(&self, other: Span<'a>) -> Span<'a> {
221        Span { src_idx: self.src_idx, src: self.src, left: &self.src[self.src_idx..other.src_idx] }
222    }
223
224    pub fn empty() -> Span<'a> {
225        Span { left: "", src: "", src_idx: 0 }
226    }
227
228    pub fn frozen(&self) -> FrozenSpan {
229        let src = String::from(self.src);
230        let left = String::from(self.left);
231        FrozenSpan { src_idx: self.src_idx, src, left }
232    } 
233} 
234
235impl<'a> Default for Span<'a> {
236    fn default() -> Self {
237        Self::empty()
238    }
239}
240
241impl<'a> Display for Span<'a> {
242    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243        write!(f, "{}", self.left)
244    }
245}
246
247// TODO: Make Span a trait that can be implemented instead of this?
248/// A snapshot of a Span which was returned in an error.
249#[derive(Debug, Clone, PartialEq, Eq)]
250pub struct FrozenSpan {
251    src: String,
252    left: String,
253    src_idx: usize,
254}
255
256/// A boxed version of the [Parse] trait.
257pub struct Parser<'a, R> {
258    parser: Box<dyn Parse<'a, R> + 'a>,
259}
260
261impl<'a, R> Parser<'a, R> {
262    pub fn new(parser: Box<dyn Parse<'a, R>>) -> Self {
263        Parser { parser }
264    }
265}
266
267#[cfg(test)]
268mod tests {
269    use std::fmt::Debug;
270    use crate::common::*;
271    use super::*;
272
273    fn assert_parses<'a, R: Debug + PartialEq, P: Parse<'a, R>>(parser: P, test: &'static str, expected: R) {
274        let parse_result = parser.parse(Span::new(test));
275        assert!(parse_result.is_ok(), "Failed to parse valid string.");
276        let success_result = parse_result.unwrap();
277        assert!(success_result.0.left == "", "Didn't parse the whole string.");
278        assert!(success_result.1 == expected, "Parsed the string but the result is wrong.");
279    }
280
281    fn assert_not_parses<'a, P: Parse<'a, R>, R>(parser: P, test: &'static str) {
282        assert!(parser.parse(Span::new(test)).is_err(), "Parses invalid string.")
283    }
284
285    #[test]
286    fn it_parses_numbers() {
287        assert_parses(uint(), "200", 200);
288        assert_not_parses(uint(), "not a number");
289        assert_parses(int(), "-5", -5);
290        assert_parses(int(), "5", 5);
291        assert_parses(int(), "+5", 5);
292        assert_not_parses(int(), "not a number");
293        assert_not_parses(int(), "+not a number");
294        assert_not_parses(int(), "-not a number");
295    }
296
297    #[test]
298    fn it_parses_literals() {
299        assert_parses(literal("hey"), "hey", "hey");
300        assert_not_parses(literal("hey"), "not hey");
301    }
302}