nand2tetris_hdl_parser/
lib.rs

1//! parser for nand2tetris HDL
2
3#![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/// A type that represents a pin
29///
30/**
31```rust
32pub struct Pin {
33    pub name: String,
34    pub start: u32,
35    pub end: u32,
36}
37```
38*/
39#[derive(Eq, PartialEq, Hash, Clone)]
40pub struct Pin {
41    /// Holds the name of the pin
42    pub name: String,
43    /// Holds the start of the slice range
44    pub start: u32,
45    /// Holds the end of the slice range
46    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/// A type that represents a chip
67///
68/**
69```rust
70use nand2tetris_hdl_parser::{Part, Pin};
71pub struct Chip {
72    pub name: String,
73    pub inputs: Vec<Pin>,
74    pub outputs: Vec<Pin>,
75    pub parts: Vec<Part>,
76}
77```
78*/
79#[derive(Debug, Eq, PartialEq, Hash, Clone)]
80pub struct Chip {
81    /// Holds the name of the chip
82    pub name: String,
83    /// Holds a list of input pins
84    pub inputs: Vec<Pin>,
85    /// Holds a list of output pins
86    pub outputs: Vec<Pin>,
87    /// Holds a list of parts
88    pub parts: Vec<Part>,
89}
90
91/// A type that represents a part
92/// Internal pins are pins that match up to an input/output of the part - the first pin in a {}={} pair
93/// Internal pins are pins that match up to another part of the chip - the second pin in a {}={} pair
94///
95/**
96```rust
97use nand2tetris_hdl_parser::Pin;
98pub struct Part {
99    pub name: String,
100    pub internal: Vec<Pin>,
101    pub external: Vec<Pin>,
102}
103```
104*/
105#[derive(Debug, Eq, PartialEq, Hash, Clone)]
106pub struct Part {
107    /// Holds the name of the part
108    pub name: String,
109    /// Holds internal connections (the pins which match up to the input pins of the part)
110    pub internal: Vec<Pin>,
111    /// Holds external connections
112    pub external: Vec<Pin>,
113}
114
115/// Error returned when HDL cannot be parsed
116#[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
141/// Try to consume whitespace, line comments, and multiline comments until all three fail on the same text
142/// Matches 0 or more
143fn 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
168/// Parses a pin descriptor into a [Pin]
169///
170/// `a[0..3]` will become Pin { name: "a", start: 0, end: 3 }
171fn pin(text: &str) -> IResult<&str, Pin, VerboseError<&str>> {
172    /// parses a pin range descriptor into `(u32, u32)`.  Both u32 will be the same if the range is a single number.
173    ///
174    /// `[0..3]` will parse into (0, 3) and `[0]` will parse into (0,0)
175    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
235/// Parses a part descriptor into a [Part]
236///
237/// `Test(a[0..3]=a[0..3],b=b,out=out);` will become a part with the name `Test` and the pins parsed with [part_pin]
238fn 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
267/// parse input/output pin line with arbitrary label
268///
269/// `IN a, b;` would parse into a `Vec<Pin>` with two pins - a and b
270fn 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
296/// parse_hdl will consume text and return `Result<Chip, Error>` depending on if it can successfully be parsed
297pub 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}