tinyparse/
common.rs

1use crate::{Parse, Span, Parser};
2use crate::error::{ParseErrorKind, ParseError};
3
4// TODO: Remove impl Parse and replace it with Parser
5
6// Note: This is impl Parse because the type can be inferred at compile time. If the type is unknown; you should use the Parser struct. Not Box<dyn Parser>
7/// Parses any UTF-8 character.
8/// 
9/// # Errors
10/// This returns a [ParseErrorKind::Starving] if the required amount of characters (1) was not fulfilled.
11pub fn any_char<'a>() -> impl Parse<'a, char> {
12    move |span: Span<'a>| {
13        let c = span.left
14            .chars()
15            .next()
16            .ok_or_else(|| ParseError::new(span, ParseErrorKind::Starving { found: 0, required: 1 }))?;
17        Ok((span.incremented(1), c))
18    }
19}
20
21// TODO: Add literal_when() function that takes a predicate for chars. Maybe make it also take a predicate for what's been parsed already, so a &str?
22/// Parses a literal equal to the `expected` parameter.
23/// 
24/// # Errors
25/// A [ParseErrorKind::Starving] is returned if there are not enough characters and a [ParseErrorKind::Unexpected] is returned when the literal did not match.
26pub fn literal<'a>(expected: &'static str) -> impl Parse<'a, &'a str> {
27    move |span: Span<'a>| {
28        if span.left.len() < expected.len() {
29            Err(ParseError::new(span, ParseErrorKind::Starving { found: span.left.len(), required: expected.len() }))
30        } else {
31            let sub = &span.left[..expected.len()];
32            if sub == expected {
33                Ok((span.incremented(expected.len()), sub))
34            } else {
35                Err(ParseError::new(span.until(expected.len()), ParseErrorKind::Unexpected { found: String::from(sub), expected: String::from(expected) }))
36            }
37        }
38    }
39}
40
41/// An alterative to chaining multiple [Parse::or] calls. One benefit is that all the errors will be returned in one [Vec], instead of each returning two errors.
42/// The intention may also be more clear.
43/// 
44/// # Errors
45/// Same as [Parse::or]
46pub fn one_of<'a, R: 'a, const N: usize>(parsers: [Parser<'a, R>; N]) -> impl Parse<'a, R> {
47    move |span: Span<'a>| {
48        let mut errors = Vec::with_capacity(N / 2);
49        for parser in parsers.iter() {
50            match parser.parse(span) {
51                ok @ Ok(_) => return ok,
52                Err(error) => errors.push(error),
53            }
54        }
55
56        Err(ParseError::new(span, ParseErrorKind::Neither(errors)))
57    }
58}
59
60/// An alternative to chaining multiple [Parse::and] calls. One benefit is that all the results will be returned in one [Vec], instead of each returning two results.
61/// The intention may also be more  clear.
62/// 
63/// # Errors
64/// Any of errors from the parameter `parsers`.
65pub fn seq<'a, R: 'a, const N: usize>(parsers: [Parser<'a, R>; N]) -> impl Parse<'a, Vec<R>> {
66    move |mut span: Span<'a>| {
67        let mut results = Vec::with_capacity(N);
68        for parser in parsers.iter() {
69            let (new_span, res) = parser.parse(span)?;
70            results.push(res);
71            span = new_span;
72        }
73        Ok((span, results))
74    }
75}
76
77/// Parses an unsigned integer.
78pub fn uint<'a>() -> impl Parse<'a, u32> {
79    any_char().only_if(|c| c.is_digit(10)).n_or_more(1).map(|digits| {
80        let digits_as_string: String = digits.into_iter().collect();
81        digits_as_string.parse().unwrap()
82    })
83}
84
85/// Parses an integer.
86pub fn int<'a>() -> impl Parse<'a, i32> {
87    let sign = literal("+").or(literal("-")).or_value("+");
88    sign.and(uint()).map(|(sign, uint)| {
89        match sign {
90            "+" => uint as i32,
91            "-" => -(uint as i32),
92            _ => unreachable!(), 
93        }
94    })
95}