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}