idf_parser/
primitives.rs

1use nom::bytes::complete::{is_not, tag};
2use nom::{Parser, character::complete::multispace0, sequence::delimited};
3
4/// A combinator that takes a parser `inner` and produces a parser that also consumes both leading and
5/// trailing whitespace, returning the output of `inner`.
6pub fn ws<'a, Output, Function>(
7    inner: Function,
8) -> impl Parser<&'a str, Output = Output, Error = nom::error::Error<&'a str>>
9where
10    Function: Parser<&'a str, Output = Output, Error = nom::error::Error<&'a str>>,
11{
12    delimited(multispace0, inner, multispace0)
13}
14
15/// Takes in a tuple of parsers with different return types
16/// and returns a tuple of parsers each wrapped with `ws`.
17///
18/// # Example
19/// ```
20/// use nom::character::complete::u32;
21/// use nom::number::complete::float;
22/// use nom::Parser;
23/// use idf_parser::ws_separated;
24/// use idf_parser::primitives::ws;
25///
26/// let input = "0 100.0 200.0 45.0";
27///
28/// let (remaining, (label, x, y, angle)) = ws_separated!((u32, float, float, float)).parse(input).unwrap();
29/// ```
30#[macro_export]
31macro_rules! ws_separated {
32    (($($parser:expr),+)) => {
33        ($(ws($parser)),+)
34    };
35}
36
37/// Section parser
38///
39/// Takes a section delimited by `.section` and `.end_section` and applies the given parser to the
40/// content of the section.
41///
42/// # Example
43///
44/// ```
45/// use idf_parser::primitives::ws;
46/// use idf_parser::parse_section;
47/// use nom::Parser;
48/// use nom::sequence::delimited;
49/// use nom::bytes::complete::tag;
50///
51/// let input = ".SECTION
52/// howdy!
53/// .END_SECTION";
54///
55/// let (remaining, point) = parse_section!("SECTION", tag("howdy!")).parse(input).unwrap();
56/// ```
57#[macro_export]
58macro_rules! parse_section {
59    ($section:expr, $parser:expr) => {
60        delimited(
61            ws(tag(format!(".{}", $section).as_str())),
62            $parser,
63            ws(tag(format!(".END_{}", $section).as_str())),
64        )
65    };
66}
67
68/// Parses a quoted string from the input string.
69///
70/// # Example
71///
72/// ```
73/// use idf_parser::primitives::quote_string;
74///
75/// let input = "\"Hello, World!\"";
76///
77/// let (remaining, quoted_str) = quote_string(input).unwrap();
78///  assert_eq!(quoted_str, "Hello, World!");
79/// ```
80pub fn quote_string(input: &str) -> nom::IResult<&str, &str> {
81    delimited(tag("\""), is_not("\""), tag("\"")).parse(input)
82}
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use crate::primitives::ws;
87    use nom::Parser;
88    use nom::bytes::complete::tag;
89    use nom::character::complete::u32;
90    use nom::number::complete::float;
91
92    #[test]
93    fn test_ws() {
94        let input = "\r0 \n\n\n100.0   200.0 \n45.0  ";
95        let (remaining, (label, x, y, angle)) = (ws(u32), ws(float), ws(float), ws(float))
96            .parse(input)
97            .unwrap();
98        assert_eq!(remaining, "");
99        assert_eq!(label, 0);
100        assert_eq!(x, 100.0);
101        assert_eq!(y, 200.0);
102        assert_eq!(angle, 45.0);
103    }
104
105    #[test]
106    fn test_ws_separated() {
107        let input = "0 100.0 200.0 45.0";
108        let (remaining, (label, x, y, angle)) = ws_separated!((u32, float, float, float))
109            .parse(input)
110            .unwrap();
111        assert_eq!(remaining, "");
112        assert_eq!(label, 0);
113        assert_eq!(x, 100.0);
114        assert_eq!(y, 200.0);
115        assert_eq!(angle, 45.0);
116    }
117
118    #[test]
119    fn test_section() {
120        let input = ".SECTION\n123 456\n.END_SECTION";
121        let (remaining, ints) = parse_section!("SECTION", (ws(u32), ws(u32)))
122            .parse(input)
123            .unwrap();
124        assert_eq!(remaining, "");
125        assert_eq!(ints, (123, 456));
126    }
127
128    #[test]
129    fn test_quote_string() {
130        let input = "\"Hello, World!\"";
131        let (remaining, quoted_str) = quote_string(input).unwrap();
132        assert_eq!(remaining, "");
133        assert_eq!(quoted_str, "Hello, World!");
134    }
135}