Skip to main content

flams_utils/parsing/
mod.rs

1mod read;
2mod string;
3
4pub use read::ReadParser;
5pub use string::StrParser;
6
7use crate::sourcerefs::StringPosition;
8
9/// A wrapper around a [`memchr::memmem::Finder`] that is guaranteed by construction to
10/// be a valid utf-8 [`str`]
11pub struct Needle<'a>(memchr::memmem::Finder<'a>);
12
13#[allow(clippy::inline_always)]
14impl<'a> Needle<'a> {
15    #[inline(always)]
16    #[must_use]
17    pub fn new(needle: &'a str) -> Self {
18        Self(memchr::memmem::Finder::new(needle))
19    }
20    #[inline(always)]
21    #[must_use]
22    pub fn as_str(&self) -> &str {
23        // SAFETY: constructed from a str
24        unsafe { str::from_utf8_unchecked(self.0.needle()) }
25    }
26    #[inline(always)]
27    #[must_use]
28    pub fn find_in(&self, haystack: &str) -> Option<usize> {
29        self.0.find(haystack.as_bytes())
30    }
31    #[inline(always)]
32    #[must_use]
33    pub fn find_in_bytes(&self, haystack: &[u8]) -> Option<usize> {
34        self.0.find(haystack)
35    }
36}
37
38pub trait SourceParser<'a>: 'a {
39    type Pos: StringPosition;
40    type Str: StringOrStr<'a>;
41    type Source;
42    fn curr_pos(&self) -> Self::Pos;
43    fn next_char(&mut self) -> Option<char>;
44    fn read_until_line_end(&mut self) -> (Self::Str, Self::Pos);
45    fn trim_start(&mut self);
46    fn starts_with(&mut self, c: char) -> bool;
47    fn peek_head(&mut self) -> Option<char>;
48    fn read_n(&mut self, i: usize) -> Self::Str;
49    fn read_while(&mut self, pred: impl FnMut(char) -> bool) -> Self::Str;
50    #[inline]
51    fn read_until(&mut self, mut pred: impl FnMut(char) -> bool) -> Self::Str {
52        self.read_while(|c| !pred(c))
53    }
54    fn read_until_inclusive(&mut self, pred: impl FnMut(char) -> bool) -> Self::Str;
55    fn read_until_str(&mut self, s: &str) -> Self::Str;
56    fn read_until_needle(&mut self, s: &Needle) -> Self::Str;
57    fn read_until_byte(&mut self, b: u8) -> Self::Str;
58    fn read_until_char(&mut self, c: char) -> Self::Str;
59
60    fn starts_with_str(&mut self, s: &str) -> bool;
61    fn drop_prefix(&mut self, s: &str) -> bool;
62    fn read_until_with_brackets(
63        &mut self,
64        open: char,
65        close: char,
66        pred: impl FnMut(char) -> bool,
67    ) -> Self::Str;
68    fn read_until_byte_with_brackets(&mut self, b: u8, open: u8, close: u8) -> Self::Str;
69    fn skip(&mut self, i: usize);
70}
71
72pub trait StringOrStr<'a>:
73    AsRef<str>
74    + From<&'a str>
75    + std::fmt::Debug
76    + std::fmt::Display
77    + Eq
78    + std::hash::Hash
79    + Clone
80    + for<'b> PartialEq<&'b str>
81    + Into<std::borrow::Cow<'a, str>>
82{
83    /// Self == &str
84    const IS_STR: bool;
85    /// # Errors
86    ///
87    /// Will return `Err` if self does not start with prefix.
88    fn strip_prefix(self, s: &str) -> Result<Self, Self>;
89    #[must_use]
90    fn split_n(self, n: usize) -> (Self, Self);
91    fn trim_ws(&mut self);
92    fn split_noparens_bytes(
93        &'a self,
94        open: u8,
95        close: u8,
96        split_char: u8,
97    ) -> impl Iterator<Item = &'a str>;
98    fn split_noparens(
99        &'a self,
100        open: char,
101        close: char,
102        split_char: char,
103    ) -> impl Iterator<Item = &'a str>;
104    fn as_cow(&self) -> std::borrow::Cow<'a, str>;
105}
106
107impl<'a> StringOrStr<'a> for &'a str {
108    const IS_STR: bool = true;
109    #[inline]
110    fn strip_prefix(self, s: &str) -> Result<Self, Self> {
111        str::strip_prefix(self, s).map(str::trim_start).ok_or(self)
112    }
113    #[inline]
114    fn split_n(self, n: usize) -> (Self, Self) {
115        (&self[..n], &self[n..])
116    }
117    #[inline]
118    fn trim_ws(&mut self) {
119        *self = self.trim();
120    }
121    fn split_noparens_bytes(
122        &'a self,
123        open: u8,
124        close: u8,
125        split_char: u8,
126    ) -> impl Iterator<Item = &'a str> {
127        struct It<'a> {
128            open: u8,
129            close: u8,
130            split_char: u8,
131            in_brackets: u8,
132            s: &'a str,
133        }
134        impl<'a> Iterator for It<'a> {
135            type Item = &'a str;
136            fn next(&mut self) -> Option<Self::Item> {
137                if self.s.is_empty() {
138                    return None;
139                }
140                loop {
141                    if self.in_brackets == 0 {
142                        if let Some(i) =
143                            memchr::memchr2(self.split_char, self.open, self.s.as_bytes())
144                        {
145                            let prefix = &self.s[..i];
146                            let b = self.s.as_bytes()[i];
147                            self.s = if prefix.len() == self.s.len() {
148                                ""
149                            } else {
150                                &self.s[i + 1..]
151                            };
152                            if b == self.split_char {
153                                return Some(prefix);
154                            }
155                            self.in_brackets += 1;
156                        } else {
157                            let r = self.s;
158                            self.s = "";
159                            return Some(r);
160                        }
161                    } else if let Some(i) =
162                        memchr::memchr2(self.open, self.close, self.s.as_bytes())
163                    {
164                        let prefix = &self.s[..i];
165                        let b = self.s.as_bytes()[i];
166                        self.s = if prefix.len() == self.s.len() {
167                            ""
168                        } else {
169                            &self.s[i + 1..]
170                        };
171                        if b == self.open {
172                            self.in_brackets += 1;
173                        } else {
174                            self.in_brackets -= 1;
175                        }
176                    } else {
177                        let r = self.s;
178                        self.s = "";
179                        return Some(r);
180                    }
181                }
182            }
183        }
184
185        It {
186            open,
187            close,
188            split_char,
189            in_brackets: 0,
190            s: self,
191        }
192    }
193    fn split_noparens(
194        &'a self,
195        open: char,
196        close: char,
197        split_char: char,
198    ) -> impl Iterator<Item = &'a str> {
199        let mut depth = 0;
200        self.split(move |c: char| {
201            if c == open {
202                depth += 1;
203                false
204            } else if c == close && depth > 0 {
205                depth -= 1;
206                false
207            } else if depth > 0 {
208                false
209            } else {
210                c == split_char
211            }
212        })
213    }
214    #[inline]
215    fn as_cow(&self) -> std::borrow::Cow<'a, str> {
216        std::borrow::Cow::Borrowed(self)
217    }
218}
219
220impl<'a> StringOrStr<'a> for String {
221    const IS_STR: bool = false;
222
223    #[allow(clippy::option_if_let_else)]
224    fn strip_prefix(self, s: &str) -> Result<Self, Self> {
225        match str::strip_prefix(&self, s) {
226            Some(s) => Ok(s.trim_start().to_string()),
227            None => Err(self),
228        }
229    }
230    #[inline]
231    fn trim_ws(&mut self) {
232        if let Some(i) = self.find(|c: char| !c.is_ascii_whitespace())
233            && i > 0
234        {
235            *self = self.split_off(i);
236        }
237        if let Some(i) = self.rfind(|c: char| !c.is_ascii_whitespace())
238            && i < self.len()
239        {
240            let _ = self.split_off(i);
241        }
242    }
243    fn split_n(mut self, n: usize) -> (Self, Self) {
244        let r = self.split_off(n);
245        (self, r)
246    }
247    fn split_noparens_bytes(
248        &'a self,
249        open: u8,
250        close: u8,
251        split_char: u8,
252    ) -> impl Iterator<Item = &'a str> {
253        self.split_noparens(open as _, close as _, split_char as _)
254    }
255    fn split_noparens(
256        &'a self,
257        open: char,
258        close: char,
259        split_char: char,
260    ) -> impl Iterator<Item = &'a str> {
261        let mut depth = 0;
262        self.split(move |c: char| {
263            if c == open {
264                depth += 1;
265                false
266            } else if c == close && depth > 0 {
267                depth -= 1;
268                false
269            } else if depth > 0 {
270                false
271            } else {
272                c == split_char
273            }
274        })
275    }
276    #[inline]
277    fn as_cow(&self) -> std::borrow::Cow<'a, str> {
278        std::borrow::Cow::Owned(self.clone())
279    }
280}