1use std::num::ParseIntError;
18
19use thiserror::Error;
20
21use super::{Section, Vmem};
22
23pub type ParseResult<T> = Result<T, ParseError>;
24
25#[derive(Clone, Debug, Error, PartialEq, Eq)]
27pub enum ParseError {
28 #[error("failed to parse as hexadecimal integer")]
30 ParseInt(#[from] ParseIntError),
31
32 #[error("unclosed comment")]
34 UnclosedComment,
35
36 #[error("address is missing a value")]
38 AddrMissingValue,
39
40 #[error("unknown character '{0}'")]
42 UnknownChar(char),
43}
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46enum Token {
47 Eof,
49 Addr(u32),
51 Value(u32),
53 Comment,
55 Whitespace,
57}
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
61struct Span {
62 token: Token,
63 len: usize,
64}
65
66pub struct VmemParser;
68
69impl VmemParser {
70 pub fn parse(mut s: &str) -> ParseResult<Vmem> {
72 let mut vmem = Vmem::default();
74 vmem.sections.push(Section::default());
75
76 loop {
77 let Span { len, token } = Self::token(s)?;
79 s = &s[len..];
80
81 match token {
82 Token::Eof => break,
83 Token::Addr(addr) => {
84 vmem.sections.push(Section {
87 addr: addr * 4,
88 data: Vec::new(),
89 });
90 }
91 Token::Value(value) => {
92 let section = vmem.sections.last_mut().unwrap();
94 section.data.push(value)
95 }
96 Token::Whitespace => continue,
98 Token::Comment => continue,
99 }
100 }
101
102 Ok(vmem)
103 }
104
105 fn token(s: &str) -> ParseResult<Span> {
107 let parsers = [
108 Self::parse_eof,
109 Self::parse_addr,
110 Self::parse_value,
111 Self::parse_comment,
112 Self::parse_whitespace,
113 ];
114
115 let span = parsers.iter().find_map(|p| p(s).transpose());
117
118 match span {
120 Some(span) => span,
121 None => Err(ParseError::UnknownChar(s.chars().next().unwrap())),
122 }
123 }
124
125 fn parse_eof(s: &str) -> ParseResult<Option<Span>> {
127 match s.is_empty() {
129 true => Ok(Some(Span {
130 len: 0,
131 token: Token::Eof,
132 })),
133 false => Ok(None),
134 }
135 }
136
137 fn parse_addr(s: &str) -> ParseResult<Option<Span>> {
139 let Some(addr) = s.strip_prefix('@') else {
141 return Ok(None);
142 };
143
144 let addr_len = match addr.find(|c: char| !c.is_ascii_hexdigit()) {
146 Some(0) => return Err(ParseError::AddrMissingValue),
147 Some(len) => len,
148 None => addr.len(),
149 };
150 let len = '@'.len_utf8() + addr_len;
152
153 let val = u32::from_str_radix(&addr[..addr_len], 16)?;
155 let token = Token::Addr(val);
156 let span = Span { token, len };
157
158 Ok(Some(span))
159 }
160
161 fn parse_value(s: &str) -> ParseResult<Option<Span>> {
163 let len = match s.find(|c: char| !c.is_ascii_hexdigit()) {
165 Some(0) => return Ok(None),
166 Some(len) => len,
167 None => s.len(),
168 };
169
170 let val = u32::from_str_radix(&s[..len], 16)?;
171 let token = Token::Value(val);
172 let span = Span { token, len };
173
174 Ok(Some(span))
175 }
176
177 fn parse_comment(s: &str) -> ParseResult<Option<Span>> {
179 let len = match s {
181 s if s.starts_with("//") => s.find('\n').unwrap_or(s.len()),
182 s if s.starts_with("/*") => {
183 s.find("*/").ok_or(ParseError::UnclosedComment)? + "*/".len()
185 }
186 _ => return Ok(None),
187 };
188
189 let token = Token::Comment;
190 let span = Span { token, len };
191
192 Ok(Some(span))
193 }
194
195 fn parse_whitespace(s: &str) -> ParseResult<Option<Span>> {
197 let len = match s.find(|c: char| !c.is_whitespace()) {
199 Some(0) => return Ok(None),
200 Some(len) => len,
201 None => s.len(),
202 };
203
204 let token = Token::Whitespace;
205 let span = Span { len, token };
206
207 Ok(Some(span))
208 }
209}
210
211#[cfg(test)]
212mod test {
213 use super::*;
214
215 #[test]
216 fn parse() {
217 let input = r#"
218 AB
219 // comment
220 CD EF
221 @42
222 12 /* comment */ 34
223 "#;
224 let expected = Vmem {
225 sections: vec![
226 Section {
227 addr: 0x00,
228 data: vec![0xAB, 0xCD, 0xEF],
229 },
230 Section {
231 addr: 0x108,
232 data: vec![0x12, 0x34],
233 },
234 ],
235 };
236
237 assert_eq!(VmemParser::parse(input).unwrap(), expected);
238 }
239
240 #[test]
241 fn token() {
242 let expected = [
244 ("", Token::Eof, 0),
245 ("@ff", Token::Addr(0xff), 3),
246 ("ff", Token::Value(0xff), 2),
247 ("// X", Token::Comment, 4),
248 ("/* X */", Token::Comment, 7),
249 (" ", Token::Whitespace, 2),
250 ];
251
252 for (s, token, len) in expected {
253 let span = Span { token, len };
254 assert_eq!(VmemParser::token(s), Ok(span));
255 }
256
257 assert_eq!(VmemParser::token("X"), Err(ParseError::UnknownChar('X')));
259 }
260
261 #[test]
262 fn eof() {
263 assert_eq!(VmemParser::parse_eof(" ").unwrap(), None);
265
266 let expected = Some(Span {
268 len: 0,
269 token: Token::Eof,
270 });
271 assert_eq!(VmemParser::parse_eof("").unwrap(), expected);
272 }
273
274 #[test]
275 fn addr() {
276 assert_eq!(VmemParser::parse_addr("/* X */").unwrap(), None);
278
279 let expected = Some(Span {
280 len: 9,
281 token: Token::Addr(0x0123abcd),
282 });
283 assert_eq!(VmemParser::parse_addr("@0123ABCD FF").unwrap(), expected);
285 assert_eq!(VmemParser::parse_addr("@0123ABCD").unwrap(), expected);
287 assert_eq!(VmemParser::parse_addr("@0123abcd").unwrap(), expected);
289
290 assert!(VmemParser::parse_addr("@123456789").is_err());
292 assert!(VmemParser::parse_addr("@").is_err());
294 assert!(VmemParser::parse_addr("@ FF").is_err());
295 }
296
297 #[test]
298 fn value() {
299 assert_eq!(VmemParser::parse_value("/* X */").unwrap(), None);
301
302 let expected = Some(Span {
303 len: 8,
304 token: Token::Value(0x0123abcd),
305 });
306 assert_eq!(VmemParser::parse_value("0123ABCD FF").unwrap(), expected);
308 assert_eq!(VmemParser::parse_value("0123ABCD").unwrap(), expected);
310 assert_eq!(VmemParser::parse_value("0123abcd").unwrap(), expected);
312
313 assert!(VmemParser::parse_value("123456789").is_err());
315 }
316
317 #[test]
318 fn comment() {
319 assert_eq!(VmemParser::parse_comment("FF").unwrap(), None);
321
322 let expected = Some(Span {
323 len: 7,
324 token: Token::Comment,
325 });
326
327 assert_eq!(VmemParser::parse_comment("/* X */ FF").unwrap(), expected);
329 assert_eq!(VmemParser::parse_comment("/* X */").unwrap(), expected);
331 assert!(VmemParser::parse_comment("/* X").is_err());
333
334 assert_eq!(
336 VmemParser::parse_comment(concat!("// XXXX", '\n', "FF")).unwrap(),
337 expected
338 );
339 assert_eq!(VmemParser::parse_comment("// XXXX").unwrap(), expected);
341 }
342
343 #[test]
344 fn whitespace() {
345 assert_eq!(VmemParser::parse_whitespace("FF").unwrap(), None);
347
348 let expected = Some(Span {
349 len: 2,
350 token: Token::Whitespace,
351 });
352 assert_eq!(VmemParser::parse_whitespace(" FF").unwrap(), expected);
354 assert_eq!(VmemParser::parse_whitespace(" ").unwrap(), expected);
356 }
357}