summaryrefslogtreecommitdiffstats
path: root/src/path/parser.rs
blob: a1660ca1d0827c3f52900342c75db53ecfe3df0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use super::Expression;
use nom::types::CompleteStr;
use nom::{digit, ErrorKind, IResult};
use std::str::{from_utf8, FromStr};

named!(raw_ident<CompleteStr, String>,
    map!(is_a!(
        "abcdefghijklmnopqrstuvwxyz \
         ABCDEFGHIJKLMNOPQRSTUVWXYZ \
         0123456789 \
         _-"
    ), |s: CompleteStr| {
        s.to_string()
    })
);

named!(integer<CompleteStr, isize>,
    map_res!(
        ws!(digit),
        |s: CompleteStr| {
            s.parse()
        }
    )
);

named!(ident<CompleteStr, Expression>, map!(raw_ident, Expression::Identifier));

#[allow(cyclomatic_complexity)]
fn postfix(expr: Expression) -> Box<Fn(CompleteStr) -> IResult<CompleteStr, Expression>> {
    Box::new(move |i: CompleteStr| {
        alt!(
            i,
            do_parse!(tag!(".") >> id: raw_ident >> (Expression::Child(Box::new(expr.clone()), id)))
                | delimited!(
                    char!('['),
                    do_parse!(
                        negative: opt!(tag!("-")) >> num: integer
                            >> (Expression::Subscript(
                                Box::new(expr.clone()),
                                num * (if negative.is_none() { 1 } else { -1 }),
                            ))
                    ),
                    char!(']')
                )
        )
    })
}

pub fn from_str(input: &str) -> Result<Expression, ErrorKind> {
    match ident(CompleteStr(input)) {
        Ok((mut rem, mut expr)) => {
            while !rem.is_empty() {
                match postfix(expr)(rem) {
                    Ok((rem_, expr_)) => {
                        rem = rem_;
                        expr = expr_;
                    }

                    // Forward Incomplete and Error
                    result => {
                        return result.map(|(_, o)| o).map_err(|e| e.into_error_kind());
                    }
                }
            }

            Ok(expr)
        }

        // Forward Incomplete and Error
        result => result.map(|(_, o)| o).map_err(|e| e.into_error_kind()),
    }
}

#[cfg(test)]
mod test {
    use super::Expression::*;
    use super::*;

    #[test]
    fn test_id() {
        let parsed: Expression = from_str("abcd").unwrap();
        assert_eq!(parsed, Identifier("abcd".into()));
    }

    #[test]
    fn test_id_dash() {
        let parsed: Expression = from_str("abcd-efgh").unwrap();
        assert_eq!(parsed, Identifier("abcd-efgh".into()));
    }

    #[test]
    fn test_child() {
        let parsed: Expression = from_str("abcd.efgh").unwrap();
        let expected = Child(Box::new(Identifier("abcd".into())), "efgh".into());

        assert_eq!(parsed, expected);

        let parsed: Expression = from_str("abcd.efgh.ijkl").unwrap();
        let expected = Child(
            Box::new(Child(Box::new(Identifier("abcd".into())), "efgh".into())),
            "ijkl".into(),
        );

        assert_eq!(parsed, expected);
    }

    #[test]
    fn test_subscript() {
        let parsed: Expression = from_str("abcd[12]").unwrap();
        let expected = Subscript(Box::new(Identifier("abcd".into())), 12);

        assert_eq!(parsed, expected);
    }

    #[test]
    fn test_subscript_neg() {
        let parsed: Expression = from_str("abcd[-1]").unwrap();
        let expected = Subscript(Box::new(Identifier("abcd".into())), -1);

        assert_eq!(parsed, expected);
    }
}