1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(feature = "std")]
4extern crate std as core;
5
6use core::fmt;
7
8#[derive(Debug, PartialEq)]
10pub enum Number {
11 Float(f64),
12 Integer(i64),
13}
14
15#[derive(Debug, PartialEq, Eq)]
17pub enum Parsed<'a> {
18 Token(&'a str),
20 Str(&'a str),
22 Number(Number),
24}
25
26#[derive(Debug)]
28pub enum ParseError {
29 UnexpectedEof,
31 InvalidString(usize),
33 InvalidNumber(usize),
35 ExpectedWhitespace(usize),
37}
38
39pub type ParseResult<'a, T> = Result<(usize, T, &'a str), ParseError>;
42
43pub fn parse_token(src: &str) -> ParseResult<&str> {
45 let mut t_start = None;
46 let mut t_end = None;
47 let mut end = None;
48 for (i, c) in src.chars().enumerate() {
49 if t_start.is_none() {
50 if !c.is_whitespace() {
51 t_start = Some(i);
52 }
53 continue;
54 }
55 if t_end.is_none() {
56 if c.is_whitespace() {
57 t_end = Some(i);
58 }
59 continue;
60 }
61 if !c.is_whitespace() {
62 end = Some(i);
63 break;
64 }
65 }
66 if t_start.is_none() && t_end.is_none() {
67 return Err(ParseError::UnexpectedEof);
68 }
69
70 let t_start = t_start.unwrap();
71 if t_end.is_none() {
72 Ok((t_start, &src[t_start..], ""))
73 } else if end.is_none() {
74 Ok((t_start, &src[t_start..t_end.unwrap()], ""))
75 } else {
76 Ok((t_start, &src[t_start..t_end.unwrap()], &src[end.unwrap()..]))
77 }
78}
79
80fn parse_delimited(
82 src: &str,
83 start_char: char,
84 end_char: char,
85 escape_char: char,
86) -> ParseResult<&str> {
87 let mut s_start = None;
88 let mut s_end = None;
89 let mut end = None;
90 let mut was_start_char = false;
91 let mut was_end_char = false;
92 let mut was_escape_char = false;
93 for (i, c) in src.chars().enumerate() {
94 if s_start.is_none() {
95 if was_start_char {
96 s_start = Some(i);
97 was_start_char = false;
98 } else if c == start_char {
99 was_start_char = true;
100 continue;
101 } else if c.is_whitespace() {
102 continue;
103 } else {
104 return Err(ParseError::InvalidString(i));
105 }
106 }
107 if s_end.is_none() {
108 if !was_escape_char && c == end_char {
109 s_end = Some(i);
110 was_end_char = true;
111 } else if c == escape_char {
112 was_escape_char = true;
113 continue;
114 }
115 was_escape_char = false;
116 continue;
117 }
118 if !c.is_whitespace() {
119 if was_end_char {
120 return Err(ParseError::ExpectedWhitespace(i));
121 }
122 end = Some(i);
123 break;
124 }
125 was_end_char = false;
126 }
127
128 if s_start.is_none() || s_end.is_none() {
129 return Err(ParseError::UnexpectedEof);
130 }
131
132 let s_start = s_start.unwrap();
133 let s_end = s_end.unwrap();
134 if end.is_none() {
135 Ok((s_start, &src[s_start..s_end], ""))
136 } else {
137 Ok((s_start, &src[s_start..s_end], &src[end.unwrap()..]))
138 }
139}
140
141#[inline]
143pub fn parse_string(src: &str) -> ParseResult<&str> {
144 parse_delimited(src, '`', '`', '\\')
145}
146
147pub fn parse_number(src: &str) -> ParseResult<Number> {
149 let (index, token, rest) = parse_token(src)?;
150 if let Ok(num) = token.parse() {
151 Ok((index, Number::Integer(num), rest))
152 } else if let Ok(num) = token.parse() {
153 Ok((index, Number::Float(num), rest))
154 } else {
155 return Err(ParseError::InvalidNumber(index));
156 }
157}
158
159pub fn parse_next(src: &str) -> ParseResult<Parsed> {
161 if let Ok((index, string, rest)) = parse_string(src) {
162 Ok((index, Parsed::Str(string), rest))
163 } else if let Ok((index, num, rest)) = parse_number(src) {
164 Ok((index, Parsed::Number(num), rest))
165 } else {
166 parse_token(src).map(|(index, token, rest)| (index, Parsed::Token(token), rest))
167 }
168}
169
170impl fmt::Display for ParseError {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 match self {
173 ParseError::UnexpectedEof => f.write_str("unexpected end of file"),
174 ParseError::InvalidString(i) => {
175 f.write_fmt(format_args!("invalid string at character {}", i + 1))
176 }
177 ParseError::InvalidNumber(i) => {
178 f.write_fmt(format_args!("invalid number at character {}", i + 1))
179 }
180 ParseError::ExpectedWhitespace(i) => {
181 f.write_fmt(format_args!("expected whitespace at character {}", i + 1))
182 }
183 }
184 }
185}
186
187#[cfg(feature = "std")]
188impl std::error::Error for ParseError {}
189
190impl Eq for Number {}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn parse_tokens() -> Result<(), ParseError> {
198 assert_eq!((0, "a", ""), parse_token("a")?);
199 assert_eq!((0, "ab", ""), parse_token("ab")?);
200 assert_eq!((0, "the", ""), parse_token("the")?);
201 assert_eq!((1, "the", ""), parse_token(" the")?);
202 assert_eq!((0, "the", ""), parse_token("the ")?);
203 assert_eq!((1, "the", ""), parse_token(" the ")?);
204 assert_eq!((3, "the", ""), parse_token(" the")?);
205 assert_eq!((0, "the", ""), parse_token("the ")?);
206 assert_eq!((3, "the", ""), parse_token(" the ")?);
207 assert_eq!((0, "the", "list"), parse_token("the list")?);
208 assert_eq!((1, "the", "list"), parse_token(" the list")?);
209 assert_eq!((0, "the", "list"), parse_token("the list")?);
210 assert_eq!((3, "the", "list"), parse_token(" the list")?);
211 assert_eq!((0, "the", "list "), parse_token("the list ")?);
212 assert_eq!((1, "the", "list "), parse_token(" the list ")?);
213 assert_eq!((0, "the", "list "), parse_token("the list ")?);
214 assert_eq!((3, "the", "list "), parse_token(" the list ")?);
215 assert!(matches!(parse_token(""), Err(ParseError::UnexpectedEof)));
216 assert!(matches!(parse_token(" "), Err(ParseError::UnexpectedEof)));
217 assert!(matches!(parse_token(" "), Err(ParseError::UnexpectedEof)));
218 Ok(())
219 }
220
221 #[test]
222 fn parse_strings() -> Result<(), ParseError> {
223 assert_eq!((1, "", ""), parse_string("``")?);
224 assert_eq!((1, "a", ""), parse_string("`a`")?);
225 assert_eq!((1, " ", ""), parse_string("` `")?);
226 assert_eq!((1, "hello, world", ""), parse_string("`hello, world`")?);
227 assert_eq!((2, "hello, world", ""), parse_string(" `hello, world`")?);
228 assert_eq!((1, "hello, world", ""), parse_string("`hello, world` ")?);
229 assert_eq!((2, "hello, world", ""), parse_string(" `hello, world` ")?);
230 assert_eq!((4, "hello, world", ""), parse_string(" `hello, world`")?);
231 assert_eq!((1, "hello, world", ""), parse_string("`hello, world` ")?);
232 assert_eq!(
233 (4, "hello, world", ""),
234 parse_string(" `hello, world` ")?
235 );
236
237 assert_eq!(
238 (1, "hello, world", "token"),
239 parse_string("`hello, world` token")?
240 );
241 assert_eq!(
242 (2, "hello, world", "token"),
243 parse_string(" `hello, world` token")?
244 );
245 assert_eq!(
246 (1, "hello, world", "token"),
247 parse_string("`hello, world` token")?
248 );
249 assert_eq!(
250 (4, "hello, world", "token"),
251 parse_string(" `hello, world` token")?
252 );
253
254 assert_eq!(
255 (1, "hello, world", "token "),
256 parse_string("`hello, world` token ")?
257 );
258 assert_eq!(
259 (2, "hello, world", "token "),
260 parse_string(" `hello, world` token ")?
261 );
262 assert_eq!(
263 (1, "hello, world", "token "),
264 parse_string("`hello, world` token ")?
265 );
266 assert_eq!(
267 (4, "hello, world", "token "),
268 parse_string(" `hello, world` token ")?
269 );
270
271 assert_eq!((1, "hello", "`world`"), parse_string("`hello` `world`")?);
272
273 assert!(matches!(parse_string(""), Err(ParseError::UnexpectedEof)));
274 assert!(matches!(parse_string(" "), Err(ParseError::UnexpectedEof)));
275 assert!(matches!(
276 parse_string(" "),
277 Err(ParseError::UnexpectedEof)
278 ));
279
280 assert!(matches!(parse_string("`"), Err(ParseError::UnexpectedEof)));
281 assert!(matches!(parse_string("` "), Err(ParseError::UnexpectedEof)));
282 assert!(matches!(parse_string(" `"), Err(ParseError::UnexpectedEof)));
283 assert!(matches!(
284 parse_string(" ` "),
285 Err(ParseError::UnexpectedEof)
286 ));
287
288 assert!(matches!(
289 parse_string("a"),
290 Err(ParseError::InvalidString(0))
291 ));
292 assert!(matches!(
293 parse_string("a "),
294 Err(ParseError::InvalidString(0))
295 ));
296 assert!(matches!(
297 parse_string(" a"),
298 Err(ParseError::InvalidString(1))
299 ));
300 assert!(matches!(
301 parse_string(" a "),
302 Err(ParseError::InvalidString(1))
303 ));
304
305 assert_eq!((1, r#"\`"#, ""), parse_string(r#"`\``"#)?);
306 assert_eq!((1, r#"escaped\`"#, ""), parse_string(r#"`escaped\``"#)?);
307 assert_eq!(
308 (1, r#"escaped\`text"#, ""),
309 parse_string(r#"`escaped\`text`"#)?
310 );
311 assert_eq!((1, r#" \`"#, ""), parse_string(r#"` \``"#)?);
312 assert_eq!((1, r#"\` "#, ""), parse_string(r#"`\` `"#)?);
313 assert_eq!((2, r#"\`"#, ""), parse_string(r#" `\``"#)?);
314 assert_eq!((1, r#"\`"#, ""), parse_string(r#"`\`` "#)?);
315 assert_eq!((2, r#"\`"#, ""), parse_string(r#" `\`` "#)?);
316
317 assert!(matches!(
318 parse_string("``a"),
319 Err(ParseError::ExpectedWhitespace(2))
320 ));
321 assert!(matches!(
322 parse_string("`hello`world"),
323 Err(ParseError::ExpectedWhitespace(7))
324 ));
325 assert!(matches!(
326 parse_string("`hello`world`"),
327 Err(ParseError::ExpectedWhitespace(7))
328 ));
329
330 Ok(())
331 }
332
333 #[test]
334 fn parse_numbers() -> Result<(), ParseError> {
335 assert_eq!((0, Number::Integer(0), ""), parse_number("0")?);
336 assert_eq!((0, Number::Integer(1), ""), parse_number("1")?);
337 assert_eq!((0, Number::Integer(-1), ""), parse_number("-1")?);
338 assert_eq!((0, Number::Float(0.), ""), parse_number("0.0")?);
339 assert_eq!((0, Number::Float(1.), ""), parse_number("1.0")?);
340 assert_eq!((0, Number::Float(-1.), ""), parse_number("-1.0")?);
341 assert_eq!((0, Number::Integer(123), ""), parse_number("123")?);
342 assert_eq!((0, Number::Integer(-123), ""), parse_number("-123")?);
343 assert_eq!((0, Number::Float(123.123), ""), parse_number("123.123")?);
344 assert_eq!((0, Number::Float(-123.123), ""), parse_number("-123.123")?);
345
346 assert_eq!((1, Number::Integer(1), ""), parse_number(" 1")?);
347 assert_eq!((0, Number::Integer(1), ""), parse_number("1 ")?);
348 assert_eq!((1, Number::Integer(1), ""), parse_number(" 1 ")?);
349 assert_eq!((1, Number::Float(1.), ""), parse_number(" 1.0")?);
350 assert_eq!((0, Number::Float(1.), ""), parse_number("1.0 ")?);
351 assert_eq!((1, Number::Float(1.), ""), parse_number(" 1.0 ")?);
352
353 assert_eq!((0, Number::Integer(1), "token"), parse_number("1 token")?);
354 assert_eq!((0, Number::Float(1.), "token"), parse_number("1.0 token")?);
355
356 assert!(matches!(
357 parse_number("a"),
358 Err(ParseError::InvalidNumber(0))
359 ));
360 assert!(matches!(
361 parse_number("a "),
362 Err(ParseError::InvalidNumber(0))
363 ));
364 assert!(matches!(
365 parse_number(" a"),
366 Err(ParseError::InvalidNumber(1))
367 ));
368 assert!(matches!(
369 parse_number(" a "),
370 Err(ParseError::InvalidNumber(1))
371 ));
372
373 Ok(())
374 }
375
376 #[test]
377 fn parse_nexts() -> Result<(), ParseError> {
378 assert_eq!(
379 (0, Parsed::Number(Number::Integer(0)), ""),
380 parse_next("0")?
381 );
382 assert_eq!(
383 (0, Parsed::Number(Number::Float(1.)), ""),
384 parse_next("1.0")?
385 );
386 assert_eq!(
387 (1, Parsed::Str("hello, world"), ""),
388 parse_next("`hello, world`")?
389 );
390 assert_eq!((0, Parsed::Token("token"), ""), parse_next("token")?);
391
392 assert_eq!(
393 (0, Parsed::Number(Number::Integer(0)), "token"),
394 parse_next("0 token")?
395 );
396 assert_eq!(
397 (0, Parsed::Number(Number::Float(1.)), "token"),
398 parse_next("1.0 token")?
399 );
400 assert_eq!(
401 (1, Parsed::Str("hello, world"), "token"),
402 parse_next("`hello, world` token")?
403 );
404 assert_eq!(
405 (0, Parsed::Token("token"), "token2"),
406 parse_next("token token2")?
407 );
408
409 Ok(())
410 }
411}