1 use std::str::FromStr;
2 
3 use 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 
13 use crate::path::Expression;
14 
raw_ident(i: &str) -> IResult<&str, String>15 fn 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 
integer(i: &str) -> IResult<&str, isize>27 fn 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 
ident(i: &str) -> IResult<&str, Expression>34 fn ident(i: &str) -> IResult<&str, Expression> {
35     map(raw_ident, Expression::Identifier)(i)
36 }
37 
postfix<'a>(expr: Expression) -> impl FnMut(&'a str) -> IResult<&'a str, Expression>38 fn 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 
from_str(input: &str) -> Result<Expression, ErrorKind>51 pub 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 
to_error_kind(e: Err<nom::error::Error<&str>>) -> ErrorKind76 pub 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)]
84 mod test {
85     use super::Expression::*;
86     use super::*;
87 
88     #[test]
test_id()89     fn test_id() {
90         let parsed: Expression = from_str("abcd").unwrap();
91         assert_eq!(parsed, Identifier("abcd".into()));
92     }
93 
94     #[test]
test_id_dash()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]
test_child()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]
test_subscript()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]
test_subscript_neg()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 }
132