okcodes_config/path/
parser.rs

1use std::str::FromStr;
2
3use nom::{
4    branch::alt,
5    bytes::complete::{is_a, tag},
6    character::complete::{char, digit1, space0},
7    combinator::{map, map_res, opt, recognize},
8    error::ErrorKind,
9    sequence::{delimited, pair, preceded},
10    Err, IResult,
11};
12
13use crate::path::Expression;
14
15fn raw_ident(i: &str) -> IResult<&str, String> {
16    map(
17        is_a(
18            "abcdefghijklmnopqrstuvwxyz \
19         ABCDEFGHIJKLMNOPQRSTUVWXYZ \
20         0123456789 \
21         _-",
22        ),
23        ToString::to_string,
24    )(i)
25}
26
27fn integer(i: &str) -> IResult<&str, isize> {
28    map_res(
29        delimited(space0, recognize(pair(opt(tag("-")), digit1)), space0),
30        FromStr::from_str,
31    )(i)
32}
33
34fn ident(i: &str) -> IResult<&str, Expression> {
35    map(raw_ident, Expression::Identifier)(i)
36}
37
38fn postfix<'a>(expr: Expression) -> impl FnMut(&'a str) -> IResult<&'a str, Expression> {
39    let e2 = expr.clone();
40    let child = map(preceded(tag("."), raw_ident), move |id| {
41        Expression::Child(Box::new(expr.clone()), id)
42    });
43
44    let subscript = map(delimited(char('['), integer, char(']')), move |num| {
45        Expression::Subscript(Box::new(e2.clone()), num)
46    });
47
48    alt((child, subscript))
49}
50
51pub fn from_str(input: &str) -> Result<Expression, ErrorKind> {
52    match ident(input) {
53        Ok((mut rem, mut expr)) => {
54            while !rem.is_empty() {
55                match postfix(expr)(rem) {
56                    Ok((rem_, expr_)) => {
57                        rem = rem_;
58                        expr = expr_;
59                    }
60
61                    // Forward Incomplete and Error
62                    result => {
63                        return result.map(|(_, o)| o).map_err(to_error_kind);
64                    }
65                }
66            }
67
68            Ok(expr)
69        }
70
71        // Forward Incomplete and Error
72        result => result.map(|(_, o)| o).map_err(to_error_kind),
73    }
74}
75
76pub fn to_error_kind(e: Err<nom::error::Error<&str>>) -> ErrorKind {
77    match e {
78        Err::Incomplete(_) => ErrorKind::Complete,
79        Err::Failure(e) | Err::Error(e) => e.code,
80    }
81}
82
83#[cfg(test)]
84mod test {
85    use super::Expression::*;
86    use super::*;
87
88    #[test]
89    fn test_id() {
90        let parsed: Expression = from_str("abcd").unwrap();
91        assert_eq!(parsed, Identifier("abcd".into()));
92    }
93
94    #[test]
95    fn test_id_dash() {
96        let parsed: Expression = from_str("abcd-efgh").unwrap();
97        assert_eq!(parsed, Identifier("abcd-efgh".into()));
98    }
99
100    #[test]
101    fn test_child() {
102        let parsed: Expression = from_str("abcd.efgh").unwrap();
103        let expected = Child(Box::new(Identifier("abcd".into())), "efgh".into());
104
105        assert_eq!(parsed, expected);
106
107        let parsed: Expression = from_str("abcd.efgh.ijkl").unwrap();
108        let expected = Child(
109            Box::new(Child(Box::new(Identifier("abcd".into())), "efgh".into())),
110            "ijkl".into(),
111        );
112
113        assert_eq!(parsed, expected);
114    }
115
116    #[test]
117    fn test_subscript() {
118        let parsed: Expression = from_str("abcd[12]").unwrap();
119        let expected = Subscript(Box::new(Identifier("abcd".into())), 12);
120
121        assert_eq!(parsed, expected);
122    }
123
124    #[test]
125    fn test_subscript_neg() {
126        let parsed: Expression = from_str("abcd[-1]").unwrap();
127        let expected = Subscript(Box::new(Identifier("abcd".into())), -1);
128
129        assert_eq!(parsed, expected);
130    }
131}