1#![forbid(unsafe_code)]
4#![deny(
5 missing_debug_implementations,
6 missing_docs,
7 trivial_casts,
8 trivial_numeric_casts,
9 unused_extern_crates,
10 unused_import_braces,
11 unused_qualifications,
12 unused_results,
13 warnings
14)]
15
16use core::fmt;
17use nom::branch::alt;
18use nom::bytes::complete::{tag, take_till, take_until};
19use nom::character::complete::{line_ending, multispace1};
20use nom::character::streaming::digit1;
21use nom::character::{is_alphabetic, is_alphanumeric};
22use nom::combinator::{not, opt};
23use nom::error::{convert_error, VerboseError};
24use nom::multi::{many0, many1};
25use nom::{Err, IResult};
26use std::error::Error;
27
28#[derive(Eq, PartialEq, Hash, Clone)]
40pub struct Pin {
41 pub name: String,
43 pub start: u32,
45 pub end: u32,
47}
48
49impl fmt::Debug for Pin {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 if self.start == self.end {
52 f.debug_struct("Pin")
53 .field("name", &self.name)
54 .field("index", &self.start)
55 .finish()
56 } else {
57 f.debug_struct("Pin")
58 .field("name", &self.name)
59 .field("start", &self.start)
60 .field("end", &self.end)
61 .finish()
62 }
63 }
64}
65
66#[derive(Debug, Eq, PartialEq, Hash, Clone)]
80pub struct Chip {
81 pub name: String,
83 pub inputs: Vec<Pin>,
85 pub outputs: Vec<Pin>,
87 pub parts: Vec<Part>,
89}
90
91#[derive(Debug, Eq, PartialEq, Hash, Clone)]
106pub struct Part {
107 pub name: String,
109 pub internal: Vec<Pin>,
111 pub external: Vec<Pin>,
113}
114
115#[derive(Debug)]
117pub struct HDLParseError {
118 details: String,
119}
120
121impl HDLParseError {
122 fn new(msg: &str) -> HDLParseError {
123 HDLParseError {
124 details: msg.to_string(),
125 }
126 }
127}
128
129impl fmt::Display for HDLParseError {
130 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131 write!(f, "{}", self.details)
132 }
133}
134
135impl Error for HDLParseError {
136 fn description(&self) -> &str {
137 &self.details
138 }
139}
140
141fn separator(text: &str) -> IResult<&str, (), VerboseError<&str>> {
144 fn comment_line(text: &str) -> IResult<&str, (), VerboseError<&str>> {
145 let (text, _) = tag("//")(text)?;
146 let (text, _) = take_until("\n")(text)?;
147 let (text, _) = line_ending(text)?;
148 Ok((text, ()))
149 }
150 fn comment_multiline(text: &str) -> IResult<&str, (), VerboseError<&str>> {
151 let (text, _) = tag("/**")(text)?;
152 let (text, _) = take_until("*/")(text)?;
153 let (text, _) = tag("*/")(text)?;
154
155 Ok((text, ()))
156 }
157 Ok((
158 many0(alt((
159 |x| Ok((multispace1(x)?.0, ())),
160 comment_line,
161 comment_multiline,
162 )))(text)?
163 .0,
164 (),
165 ))
166}
167
168fn pin(text: &str) -> IResult<&str, Pin, VerboseError<&str>> {
172 fn pin_index(text: &str) -> IResult<&str, (u32, u32), VerboseError<&str>> {
176 fn internal_pin_single(text: &str) -> IResult<&str, (u32, u32), VerboseError<&str>> {
177 let (text, _) = tag("[")(text)?;
178 let (text, index) = digit1(text)?;
179 let (text, _) = tag("]")(text)?;
180
181 Ok((
182 text,
183 (
184 index.parse::<u32>().unwrap_or(0),
185 index.parse::<u32>().unwrap_or(0),
186 ),
187 ))
188 }
189 fn internal_pin_range(text: &str) -> IResult<&str, (u32, u32), VerboseError<&str>> {
190 let (text, _) = tag("[")(text)?;
191 let (text, start) = digit1(text)?;
192 let (text, _) = tag("..")(text)?;
193 let (text, end) = digit1(text)?;
194 let (text, _) = tag("]")(text)?;
195
196 Ok((
197 text,
198 (
199 start.parse::<u32>().unwrap_or(0),
200 end.parse::<u32>().unwrap_or(0),
201 ),
202 ))
203 }
204 alt((internal_pin_single, internal_pin_range))(text)
205 }
206
207 let (text, _) = take_till(|x| is_alphabetic(x as u8))(text)?;
208 let (text, name) = take_till(|x| match x {
209 ',' | ')' | ';' | '=' | '[' | ' ' => true,
210 _ => false,
211 })(text)?;
212 let (text, _) = separator(text)?;
213 match pin_index(text) {
214 Ok((text, (start, end))) => Ok((
215 text,
216 Pin {
217 name: name.to_string(),
218 start,
219 end,
220 },
221 )),
222 Err(_) => {
223 Ok((
224 text,
225 Pin {
226 name: name.to_string(),
227 start: 0,
228 end: 0,
229 },
230 ))
231 }
232 }
233}
234
235fn part(text: &str) -> IResult<&str, Part, VerboseError<&str>> {
239 fn internal_part(text: &str) -> IResult<&str, (Pin, Pin), VerboseError<&str>> {
240 let (text, _) = not(tag(")"))(text)?;
241 let (text, pin1) = pin(text)?;
242 let (text, _) = tag("=")(text)?;
243 let (text, pin2) = pin(text)?;
244 Ok((text, (pin1, pin2)))
245 }
246 let (text, _) = take_till(|x| is_alphabetic(x as u8))(text)?;
247 let (text, name) = take_until("(")(text)?;
248 let (text, _) = tag("(")(text)?;
249 let (text, pins) = many0(internal_part)(text)?;
250 let pins = pins
251 .iter()
252 .map(|&(ref a, ref b)| (a.clone(), b.clone()))
253 .unzip();
254
255 let (text, _) = tag(");")(text)?;
256 let (text, _) = separator(text)?;
257 Ok((
258 text,
259 Part {
260 name: name.to_string(),
261 internal: pins.0,
262 external: pins.1,
263 },
264 ))
265}
266
267fn parse_io_pins<'a>(
271 text: &'a str,
272 label: &str,
273) -> IResult<&'a str, Vec<Pin>, VerboseError<&'a str>> {
274 fn interface_pin(text: &str) -> IResult<&str, Pin, VerboseError<&str>> {
275 let (text, _) = not(tag(";"))(text)?;
276 let (text, pin) = pin(text)?;
277
278 let (text, _) = opt(tag(","))(text)?;
279 let (text, _) = separator(text)?;
280 Ok((text, pin))
281 }
282
283 let (text, _) = separator(text)?;
284 let (text, _) = take_until(label)(text)?;
285
286 let (text, _) = separator(text)?;
287 let (text, _) = tag(label)(text)?;
288 let (text, _) = separator(text)?;
289
290 let (text, inputs) = many1(interface_pin)(text)?;
291
292 let (text, _) = separator(text)?;
293 Ok((text, inputs))
294}
295
296pub fn parse_hdl(text: &str) -> Result<Chip, HDLParseError> {
298 fn parse_hdl_internal(text: &str) -> IResult<&str, Chip, VerboseError<&str>> {
299 let (text, _) = separator(text)?;
300 let (text, _) = tag("CHIP")(text)?;
301
302 let (text, _) = separator(text)?;
303 let (text, chip_name) = take_till(|x| !is_alphanumeric(x as u8))(text)?;
304
305 let (text, inputs) = parse_io_pins(text, "IN")?;
306 let (text, outputs) = parse_io_pins(text, "OUT")?;
307
308 let (text, _) = take_until("PARTS:")(text)?;
309 let (text, _) = tag("PARTS:")(text)?;
310 let (text, _) = separator(text)?;
311 let (text, parts) = many0(part)(text)?;
312
313 Ok((
314 text,
315 Chip {
316 name: chip_name.to_string(),
317 inputs,
318 outputs,
319 parts,
320 },
321 ))
322 }
323
324 match parse_hdl_internal(text) {
325 Ok((_, chip)) => Ok(chip),
326 Err(Err::Error(e)) | Err(Err::Failure(e)) => {
327 Err(HDLParseError::new(&convert_error(text, e)))
328 }
329 _ => Err(HDLParseError::new(
330 "Should never happen, report it if it does",
331 )),
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use crate::{parse_hdl, parse_io_pins, Chip, Part, Pin};
338 use std::fs;
339 use std::io::Error;
340
341 #[test]
342 fn fails_parse() -> Result<(), Error> {
343 assert_eq!(
344 format!("{}", parse_hdl("aaaa ").err().unwrap()),
345 "0: at line 1, in Tag:
346aaaa
347^
348
349"
350 );
351 Ok(())
352 }
353
354 #[test]
355 fn example_hdl() -> Result<(), Error> {
356 let example_hdl_text = fs::read_to_string("test_cases/example.hdl")?;
357 let example_hdl_chip = Chip {
358 name: "Example".to_string(),
359 inputs: vec![
360 Pin {
361 name: "a".to_string(),
362 start: 0,
363 end: 0,
364 },
365 Pin {
366 name: "b".to_string(),
367 start: 0,
368 end: 0,
369 },
370 ],
371 outputs: vec![Pin {
372 name: "out".to_string(),
373 start: 0,
374 end: 0,
375 }],
376 parts: vec![
377 Part {
378 name: "Test".to_string(),
379 internal: vec![
380 Pin {
381 name: "a".to_string(),
382 start: 0,
383 end: 3,
384 },
385 Pin {
386 name: "b".to_string(),
387 start: 0,
388 end: 0,
389 },
390 Pin {
391 name: "out".to_string(),
392 start: 0,
393 end: 0,
394 },
395 ],
396 external: vec![
397 Pin {
398 name: "a".to_string(),
399 start: 0,
400 end: 3,
401 },
402 Pin {
403 name: "b".to_string(),
404 start: 0,
405 end: 0,
406 },
407 Pin {
408 name: "out1".to_string(),
409 start: 0,
410 end: 0,
411 },
412 ],
413 },
414 Part {
415 name: "Test".to_string(),
416 internal: vec![
417 Pin {
418 name: "a".to_string(),
419 start: 0,
420 end: 0,
421 },
422 Pin {
423 name: "b".to_string(),
424 start: 0,
425 end: 0,
426 },
427 Pin {
428 name: "out".to_string(),
429 start: 0,
430 end: 0,
431 },
432 ],
433 external: vec![
434 Pin {
435 name: "a".to_string(),
436 start: 0,
437 end: 0,
438 },
439 Pin {
440 name: "b".to_string(),
441 start: 0,
442 end: 0,
443 },
444 Pin {
445 name: "out".to_string(),
446 start: 0,
447 end: 0,
448 },
449 ],
450 },
451 Part {
452 name: "Test".to_string(),
453 internal: vec![
454 Pin {
455 name: "a".to_string(),
456 start: 0,
457 end: 0,
458 },
459 Pin {
460 name: "b".to_string(),
461 start: 0,
462 end: 0,
463 },
464 Pin {
465 name: "out".to_string(),
466 start: 0,
467 end: 0,
468 },
469 ],
470 external: vec![
471 Pin {
472 name: "a".to_string(),
473 start: 0,
474 end: 0,
475 },
476 Pin {
477 name: "b".to_string(),
478 start: 0,
479 end: 0,
480 },
481 Pin {
482 name: "out".to_string(),
483 start: 0,
484 end: 0,
485 },
486 ],
487 },
488 ],
489 };
490 assert_eq!(example_hdl_chip, parse_hdl(&example_hdl_text).unwrap());
491 Ok(())
492 }
493
494 #[test]
495 fn test_parse_io_pins() -> Result<(), Error> {
496 let text = " IN a, b;
497";
498 let (_, pins) = parse_io_pins(text, "IN").unwrap_or(("", vec![]));
499 assert_eq!(
500 pins,
501 vec![
502 Pin {
503 name: "a".to_string(),
504 start: 0,
505 end: 0,
506 },
507 Pin {
508 name: "b".to_string(),
509 start: 0,
510 end: 0,
511 }
512 ]
513 );
514 Ok(())
515 }
516
517 #[test]
518 fn test_pin_debug_display() -> Result<(), Error> {
519 let index_same_formatted: String = format!(
520 "{:?}",
521 Pin {
522 name: "placeholder".to_string(),
523 start: 0,
524 end: 0,
525 }
526 )
527 .chars()
528 .filter(|c| !c.is_whitespace())
529 .collect();
530 let index_same_cmp: String = "Pin { name: \"placeholder\", index: 0 }"
531 .chars()
532 .filter(|c| !c.is_whitespace())
533 .collect();
534 assert_eq!(index_same_formatted, index_same_cmp);
535
536 let index_different_formatted: String = format!(
537 "{:?}",
538 Pin {
539 name: "placeholder".to_string(),
540 start: 3,
541 end: 4,
542 }
543 )
544 .chars()
545 .filter(|c| !c.is_whitespace())
546 .collect();
547 let index_different_cmp: String = "Pin { name: \"placeholder\", start: 3, end: 4 }"
548 .chars()
549 .filter(|c| !c.is_whitespace())
550 .collect();
551 assert_eq!(index_different_formatted, index_different_cmp);
552
553 Ok(())
554 }
555}