peggle/
lib.rs

1/// Keeps track of the current parse location of a string input.
2#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
3pub struct Index<'a> {
4    /// The remaining data
5    pub remaining: &'a str,
6    pub lineno: usize,
7    pub colno: usize,
8}
9
10impl<'a> Index<'a> {
11    #[inline]
12    pub fn new(string: &'a str) -> Self {
13        Self {
14            remaining: string,
15            lineno: 0,
16            colno: 0,
17        }
18    }
19
20    #[inline]
21    pub fn peek(&self) -> Option<char> {
22        self.remaining.chars().next()
23    }
24
25    #[inline]
26    pub fn peek_multiple<const N: usize>(&self) -> Option<[char; N]> {
27        let mut peeked = ['\0'; N];
28        // TODO: replace with `try_from_fn` once stable (https://doc.rust-lang.org/std/array/fn.try_from_fn.html)
29
30        let mut chars = self.remaining.chars();
31        for p in peeked.iter_mut() {
32            let Some(c) = chars.next() else {
33                return None
34            };
35            *p = c;
36        }
37
38        Some(peeked)
39    }
40
41    #[inline]
42    pub fn next_multiple<const N: usize>(&mut self) -> Option<[char; N]> {
43        let mut all_next = ['\0'; N];
44        // TODO: replace with `try_from_fn` once stable (https://doc.rust-lang.org/std/array/fn.try_from_fn.html)
45
46        for n in all_next.iter_mut() {
47            let Some(c) = self.next() else {
48                return None
49            };
50            *n = c;
51        }
52        Some(all_next)
53    }
54
55    #[inline]
56    pub fn advance_to_end(&mut self) {
57        for _ in self.by_ref() {}
58    }
59}
60
61impl<'a> Iterator for Index<'a> {
62    type Item = char;
63
64    fn next(&mut self) -> Option<Self::Item> {
65        let c = self.remaining.chars().next()?;
66        let len = c.len_utf8();
67        self.remaining = self.remaining.get(len..)?;
68
69        if c == '\n' {
70            self.lineno += 1;
71            self.colno = 0;
72        } else {
73            self.colno += len;
74        }
75
76        Some(c)
77    }
78}
79
80/// Represents an error that occurred during the parsing of a string input.
81#[derive(Debug)]
82pub struct ParseError {
83    pub lineno: usize,
84    pub colno: usize,
85}
86
87impl ParseError {
88    /// Crates an error containing the given index's line/column information.
89    pub fn from_index(idx: Index<'_>) -> Self {
90        Self {
91            lineno: idx.lineno,
92            colno: idx.colno,
93        }
94    }
95}
96
97pub trait Parse: Sized {
98    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError>;
99
100    #[inline]
101    fn parse_raw_at(index: Index<'_>) -> Result<(&str, Index<'_>), ParseError> {
102        let (_, new_index) = Self::parse_at(index)?;
103
104        let consumed_len = index.remaining.len() - new_index.remaining.len();
105        let consumed = index
106            .remaining
107            .get(..consumed_len)
108            .ok_or(ParseError::from_index(index))?;
109
110        Ok((consumed, new_index))
111    }
112
113    #[inline]
114    fn parse(input: &str) -> Result<Self, ParseError> {
115        let idx = Index {
116            remaining: input,
117            lineno: 0,
118            colno: 0,
119        };
120
121        let (ret, remaining) = Self::parse_at(idx)?;
122        if remaining.remaining.is_empty() {
123            Ok(ret)
124        } else {
125            Err(ParseError::from_index(idx))
126        }
127    }
128}
129
130impl<T: Parse> Parse for Box<T> {
131    #[inline]
132    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
133        T::parse_at(index).map(|(val, idx)| (Box::new(val), idx))
134    }
135}
136
137impl Parse for bool {
138    #[inline]
139    fn parse_at(mut index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
140        match index.next_multiple() {
141            Some(['t', 'r', 'u', 'e']) => Ok((true, index)),
142            Some(['f', 'a', 'l', 's']) if index.next() == Some('e') => Ok((true, index)),
143            _ => Err(ParseError::from_index(index)),
144        }
145    }
146}
147
148impl Parse for u8 {
149    #[inline]
150    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
151        parse_unsigned(index)
152    }
153}
154
155impl Parse for u16 {
156    #[inline]
157    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
158        parse_unsigned(index)
159    }
160}
161
162impl Parse for u32 {
163    #[inline]
164    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
165        parse_unsigned(index)
166    }
167}
168
169impl Parse for u64 {
170    #[inline]
171    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
172        parse_unsigned(index)
173    }
174}
175
176impl Parse for u128 {
177    #[inline]
178    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
179        parse_unsigned(index)
180    }
181}
182
183impl Parse for usize {
184    #[inline]
185    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
186        parse_unsigned(index)
187    }
188}
189
190impl Parse for i8 {
191    #[inline]
192    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
193        parse_signed(index)
194    }
195}
196
197impl Parse for i16 {
198    #[inline]
199    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
200        parse_signed(index)
201    }
202}
203
204impl Parse for i32 {
205    #[inline]
206    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
207        parse_signed(index)
208    }
209}
210
211impl Parse for i64 {
212    #[inline]
213    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
214        parse_signed(index)
215    }
216}
217
218impl Parse for i128 {
219    #[inline]
220    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
221        parse_signed(index)
222    }
223}
224
225impl Parse for isize {
226    #[inline]
227    fn parse_at(index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
228        parse_signed(index)
229    }
230}
231
232impl Parse for String {
233    #[inline]
234    fn parse_at(mut index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
235        let s = index.remaining.to_string();
236        index.advance_to_end();
237        Ok((s, index))
238    }
239}
240
241impl Parse for char {
242    #[inline]
243    fn parse_at(mut index: Index<'_>) -> Result<(Self, Index<'_>), ParseError> {
244        index
245            .next()
246            .map(|c| (c, index))
247            .ok_or(ParseError::from_index(index))
248    }
249}
250
251fn parse_unsigned<I: num::Unsigned + num::CheckedAdd + num::CheckedMul + From<u8>>(
252    mut index: Index<'_>,
253) -> Result<(I, Index<'_>), ParseError> {
254    let mut value = None;
255
256    // Edge case: 0 (no leading zeros are allowed)
257    if let Some('0') = index.peek() {
258        index.next();
259        return match index.peek() {
260            Some('0'..='9') => Err(ParseError::from_index(index)),
261            _ => Ok((I::zero(), index)),
262        };
263    }
264
265    // Match characters 0-9, checking for overflow
266    while let Some(c @ '0'..='9') = index.peek() {
267        let c_val = I::from((c as u8) - b'0');
268        value = value
269            .unwrap_or(I::zero())
270            .checked_mul(&10u8.into())
271            .and_then(|i| i.checked_add(&c_val));
272        if value.is_none() {
273            // Next digit would lead to overflow
274            return Err(ParseError::from_index(index));
275        }
276
277        index.next();
278    }
279
280    // If value == None then no digits were detected--return an error
281    value
282        .ok_or(ParseError::from_index(index))
283        .map(|i| (i, index))
284}
285
286fn parse_signed<I: num::Signed + num::CheckedAdd + num::CheckedSub + num::CheckedMul + From<i8>>(
287    mut index: Index<'_>,
288) -> Result<(I, Index<'_>), ParseError> {
289    let mut value = None;
290
291    let mut is_negative = false;
292
293    if let Some('-') = index.peek() {
294        is_negative = true;
295        index.next();
296    }
297
298    // Edge case: 0 (no leading zeros are allowed)
299    if let Some('0') = index.peek() {
300        index.next();
301        return match index.peek() {
302            Some('0'..='9') => Err(ParseError::from_index(index)),
303            _ => Ok((I::zero(), index)),
304        };
305    }
306
307    // Match characters 0-9, checking for overflow
308    while let Some(c @ '0'..='9') = index.peek() {
309        let c_val = I::from((c as i8) - ('0' as i8));
310        value = value.unwrap_or(I::zero()).checked_mul(&10i8.into());
311
312        value = if is_negative {
313            value.and_then(|i| i.checked_sub(&c_val))
314        } else {
315            value.and_then(|i| i.checked_add(&c_val))
316        };
317
318        if value.is_none() {
319            // Next digit would lead to overflow
320            return Err(ParseError::from_index(index));
321        }
322
323        index.next();
324    }
325
326    // If value == None then no digits were detected--return an error
327    value
328        .ok_or(ParseError::from_index(index))
329        .map(|i| (i, index))
330}