1use std::any::type_name;
10use std::num::ParseIntError;
11use std::str::FromStr;
12
13#[derive(Debug, thiserror::Error)]
15pub enum ParserError {
16 #[error("parse error: expected '{expected}' but got end of input")]
17 UnexpectedEndOfInput { expected: &'static str },
18
19 #[error("parse error: expected '{expected}' but got '{actual}'")]
20 WrongToken {
21 expected: &'static str,
22 actual: String,
23 },
24
25 #[error("parse error: token '{actual}' is not a '{expected_type}'")]
26 WrongTokenType {
27 expected_type: &'static str,
28 actual: String,
29 },
30
31 #[error("parse error: {error}: expected integer but got '{token}'")]
32 NotAnInteger {
33 token: String,
34 #[source]
35 error: ParseIntError,
36 },
37}
38
39pub struct Parser<'a> {
45 str: &'a str,
46 delims: &'a [&'a str],
47}
48
49impl<'a> Parser<'a> {
50 pub fn new(str: &'a str, delims: &'a [&'a str]) -> Self {
53 Self { str, delims }
54 }
55
56 pub fn peek(&self) -> Option<&'a str> {
59 self.split().map(|(token, _)| token)
60 }
61
62 pub fn peek_char(&self) -> Option<char> {
65 self.str.chars().next()
66 }
67
68 pub fn peek_or_err(&self, expected: &'static str) -> Result<&'a str, ParserError> {
71 self.split()
72 .map(|(token, _)| token)
73 .ok_or(ParserError::UnexpectedEndOfInput { expected })
74 }
75
76 pub fn expect(&mut self, expected: &'static str) -> Result<(), ParserError> {
79 let token = self.peek_or_err(expected)?;
80 if token != expected {
81 Err(ParserError::WrongToken {
82 expected,
83 actual: token.to_string(),
84 })
85 } else {
86 let _ = self.next();
87 Ok(())
88 }
89 }
90
91 pub fn next_or_err(&mut self, expected: &'static str) -> Result<&'a str, ParserError> {
94 self.next()
95 .ok_or(ParserError::UnexpectedEndOfInput { expected })
96 }
97
98 pub fn try_parse<T: FromStr>(&mut self) -> Result<T, ParserError> {
100 let token = self.peek_or_err("a token")?;
101 let result = token.parse().map_err(|_e| ParserError::WrongTokenType {
102 expected_type: type_name::<T>(),
103 actual: token.to_string(),
104 });
105 if result.is_ok() {
106 let _ = self.next();
107 }
108 result
109 }
110
111 pub fn is_empty(&self) -> bool {
113 self.str.trim().is_empty()
114 }
115
116 fn split(&self) -> Option<(&'a str, &'a str)> {
117 if self.str.is_empty() {
118 return None;
119 }
120
121 match self
122 .delims
123 .iter()
124 .enumerate()
125 .flat_map(|(index, d)| self.str.find(d).map(|pos| (index, pos)))
126 .min_by_key(|&(_, v)| v)
127 {
128 Some((index, 0)) => Some((self.delims[index], &self.str[self.delims[index].len()..])),
129 Some((_, pos)) => Some((self.str[..pos].trim(), &self.str[pos..])),
130 None => Some((self.str.trim(), "")),
131 }
132 }
133
134 pub fn parse_string_literal(&mut self) -> Result<String, ParserError> {
138 let mut s = self.str;
139
140 if !s.starts_with('"') {
141 let tok = self.peek_or_err("\"")?;
142 return Err(ParserError::WrongToken {
143 expected: "\"",
144 actual: tok.to_string(),
145 });
146 }
147
148 s = &s[1..];
150 let mut out = String::new();
151 let mut consumed = 1; let mut chars = s.chars();
154 while let Some(c) = chars.next() {
155 consumed += c.len_utf8();
156 match c {
157 '\\' => {
158 if let Some(e) = chars.next() {
160 consumed += e.len_utf8();
161 match e {
162 '\\' => out.push('\\'),
163 '"' => out.push('"'),
164 'n' => out.push('\n'),
165 'r' => out.push('\r'),
166 't' => out.push('\t'),
167 other => {
168 out.push('\\');
171 out.push(other);
172 }
173 }
174 } else {
175 return Err(ParserError::UnexpectedEndOfInput {
176 expected: "escape sequence",
177 });
178 }
179 }
180 '"' => {
181 self.str = &self.str[consumed..];
183 return Ok(out);
184 }
185 _ => out.push(c),
186 }
187 }
188
189 Err(ParserError::UnexpectedEndOfInput {
190 expected: "closing quote",
191 })
192 }
193}
194
195impl<'a> Iterator for Parser<'a> {
196 type Item = &'a str;
197
198 fn next(&mut self) -> Option<Self::Item> {
199 self.split().map(|(token, rest)| {
200 self.str = rest;
201 token
202 })
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_basic() {
212 let mut p = Parser::new("foo,bar", &[","]);
213 assert_eq!(p.next(), Some("foo"));
214 assert_eq!(p.next(), Some(","));
215 assert_eq!(p.peek(), Some("bar"));
216 assert_eq!(p.next(), Some("bar"));
217 assert_eq!(p.next(), None);
218 assert_eq!(p.peek(), None);
219 }
220}