summaryrefslogtreecommitdiffstats
path: root/src/path/parser.rs
blob: eea4343d50edb52858cf7a19ec387d81e072c5cf (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
use nom::*;
use std::str::{FromStr, from_utf8};
use super::Expression;

named!(ident_<String>,
    map!(
        map_res!(is_a!(
            "abcdefghijklmnopqrstuvwxyz \
             ABCDEFGHIJKLMNOPQRSTUVWXYZ \
             0123456789 \
             _-"
        ), from_utf8),
        |s: &str| {
            s.to_string()
        }
    )
);

named!(integer <i32>,
    map_res!(
        map_res!(
            ws!(digit),
            from_utf8
        ),
        FromStr::from_str
    )
);

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

fn postfix(expr: Expression) -> Box<Fn(&[u8]) -> IResult<&[u8], Expression>> {
    return Box::new(move |i: &[u8]| {
        alt!(i,
            do_parse!(
                tag!(".") >>
                id: 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(input.as_bytes()) {
        IResult::Done(mut rem, mut expr) => {
            while rem.len() > 0 {
                match postfix(expr)(rem) {
                    IResult::Done(rem_, expr_) => {
                        rem = rem_;
                        expr = expr_;
                    }

                    // Forward Incomplete and Error
                    result @ _ => {
                        return result.to_result();
                    }
                }
            }

            Ok(expr)
        }

        // Forward Incomplete and Error
        result @ _ => result.to_result(),
    }
}

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

    #[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);
    }

    #[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);
    }
}