Skip to main content

ncl/
lib.rs

1#[macro_use] extern crate nom;
2
3pub use value::Value;
4pub use object::Object;
5
6use std::str::{self, FromStr};
7
8use nom::{
9    digit,
10    eof,
11    space,
12    not_line_ending,
13    line_ending,
14    IResult,
15    is_alphabetic,
16    is_alphanumeric,
17};
18
19pub mod value;
20pub mod object;
21
22pub type Key = String;
23type Entry = (Key, Value);
24
25named!(comment,
26       preceded!(tag!("#"), not_line_ending));
27
28named!(blank,
29       chain!(many0!(terminated!(
30               many0!(alt!(comment | space)),
31               line_ending)),
32               || { &b""[..] }));
33
34named!(boolean<Value>,
35       map!(
36           map_res!(
37               map_res!(
38                   alt!(tag!("true") | tag!("false")),
39                   str::from_utf8),
40                   <bool as FromStr>::from_str),
41                   From::from));
42
43named!(number<Value>,
44       map!(
45           map_res!(
46               map_res!(
47                   digit,
48                   str::from_utf8),
49                   <i64 as FromStr>::from_str),
50                   From::from));
51
52named!(string<Value>,
53       map!(
54           map_res!(
55               delimited!(tag!("\""), take_until!("\""), tag!("\"")),
56               str::from_utf8),
57               From::from));
58
59named!(entries<Object>,
60       map!(many0!(entry), From::from));
61
62named!(object_begin,
63       chain!(space? ~ tag!("{") ~ alt!(blank | space)?, || { &b""[..] }));
64
65named!(object_end,
66       chain!(space? ~ tag!("}"), || { &b""[..] }));
67
68named!(object<Value>,
69       map!(delimited!(object_begin, entries, object_end),
70       From::from));
71
72named!(value<Value>,
73       delimited!(opt!(space), alt!(number | boolean | string), opt!(space)));
74
75fn keyable<'a>(input: &'a [u8]) -> IResult<'a, &'a [u8], &[u8]> {
76    if input.len() > 0 && !is_alphabetic(input[0]) {
77        return IResult::Error(nom::Err::Position(666, input));
78    }
79    for idx in 1..input.len() {
80        if !is_alphanumeric(input[idx]) && input[idx] != b'_' && input[idx] != b'-' {
81            return IResult::Done(&input[idx..], &input[0..idx]);
82        }
83    }
84    IResult::Done(b"", input)
85}
86
87named!(key<Key>,
88       map_res!(
89           chain!(key: keyable ~
90                  space?,
91                  || { str::from_utf8(key).unwrap() }),
92                  FromStr::from_str));
93
94named!(entry<Entry>,
95       alt!(
96           chain!(space? ~
97                  key: key ~
98                  space? ~
99                  value: object ~
100                  blank?,
101                  || { (key, value) }) |
102           chain!(space? ~
103                  key: key ~
104                  tag!("=") ~
105                  value: value ~
106                  blank?,
107                  || { (key, value) })));
108
109named!(root<Object>,
110       terminated!(delimited!(opt!(blank), entries, many0!(alt!(space | line_ending | comment))), eof));
111
112#[derive(Debug)]
113pub enum Error {
114    ParserFailed(String),
115    Incomplete
116}
117
118pub fn parse<T: AsRef<[u8]>>(input: T) -> Result<Object, Error> {
119    match root(input.as_ref()) {
120        IResult::Done(_, object) => Ok(object),
121        IResult::Error(err) => Err(Error::ParserFailed(format!("{:?}", err))),
122        IResult::Incomplete(_) => Err(Error::Incomplete)
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn it_works() {
132        let data = parse("str = \"test\"\nnum = 42\nbool = true\nobj { num = 666 }").unwrap();
133
134        assert_eq!(data.get("str"),   Some(&Value::Str("test".to_string())));
135        assert_eq!(data.get("num"),   Some(&Value::Num(42)));
136        assert_eq!(data.get("bool"),  Some(&Value::Bool(true)));
137
138        let obj = match data.get("obj").unwrap() {
139            &Value::Object(ref obj) => obj,
140            _ => { assert!(false); unreachable!() }
141        };
142
143        assert_eq!(obj.get("num"), Some(&Value::Num(666)));
144    }
145
146    #[test]
147    fn it_fails() {
148        let data = parse("str = \"test\"\nnum = 42\nbool = true\nobj = { num = 666");
149
150        assert!(data.is_err(), "data is: {:?}", data);
151    }
152}