storm_config/path/
parser.rs

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