flams_utils/parsing/
mod.rs1mod read;
2mod string;
3
4pub use read::ReadParser;
5pub use string::StrParser;
6
7use crate::sourcerefs::StringPosition;
8
9pub 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 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 const IS_STR: bool;
85 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}