1use nom::combinator;
2use nom::{
3 branch::alt,
4 bytes::complete::{escaped_transform, is_not, tag, take_while, take_while1},
5 character::complete::{char, one_of},
6 combinator::{eof, map, opt, recognize, value},
7 multi::{many0, many1, separated_list0},
8 sequence::{delimited, pair, terminated, tuple},
9 IResult,
10};
11
12use crate::json::JSON;
13pub fn parse_fson(input: &str) -> Option<JSON> {
14 let n = input.len();
15 let mut input = input.chars();
16 for _ in 0..n {
17 if let Ok((_, data)) = parse_data(input.as_str()) {
18 return Some(data);
19 }
20 input.next();
21 }
22 None
23}
24
25pub fn parse_data(input: &str) -> IResult<&str, JSON> {
26 alt((
27 parse_dict,
28 parse_array,
29 parse_null,
30 parse_str,
31 parse_float,
32 parse_int,
33 ))(input)
34}
35
36pub fn spaces(input: &str) -> IResult<&str, &str> {
37 take_while(|c: char| c.is_whitespace())(input)
38}
39
40fn comment(input: &str) -> IResult<&str, &str> {
41 let (input, _) = alt((tag("//"), tag("#"), tag(";"), tag("--")))(input)?;
42 let (input, _) = opt(is_not("\n\r"))(input)?;
43 alt((eof, spaces))(input)
44}
45
46pub fn commentable_spaces(input: &str) -> IResult<&str, ()> {
47 let (input, _) = spaces(input)?;
48 let (input, _) = many0(tuple((comment, spaces)))(input)?;
49 Ok((input, ()))
50}
51
52pub fn identifier(input: &str) -> IResult<&str, String> {
53 fn head(c: char) -> bool {
54 c.is_alphabetic() || c == '_' || c == '#' || c == '@'
55 }
56 fn tail(c: char) -> bool {
57 c.is_alphanumeric() || head(c)
58 }
59 let (input, s) = take_while1(head)(input)?;
60 let (input, t) = take_while(tail)(input)?;
61 let mut name = String::new();
62 name.push_str(s);
63 name.push_str(t);
64 Ok((input, name))
65}
66
67pub fn parse_null(input: &str) -> IResult<&str, JSON> {
68 value(
69 JSON::Null,
70 alt((
71 tag("null"),
72 tag("Null"),
73 tag("NULL"),
74 tag("NUL"),
75 tag("Nil"),
76 tag("nil"),
77 tag("None"),
78 tag("Nothing"),
79 )),
80 )(input)
81}
82
83pub fn parse_int(input: &str) -> IResult<&str, JSON> {
84 fn decimal(input: &str) -> IResult<&str, &str> {
85 recognize(many1(terminated(one_of("0123456789"), many0(char('_')))))(input)
86 }
87 let mut num_value = map(pair(opt(tag("-")), decimal), |(sign, num): (_, &str)| {
88 let num: String = num.chars().filter(|&c| c != '_').collect();
89 let val = num.parse::<i128>().unwrap();
90 match sign {
91 None => JSON::Int(val),
92 _ => JSON::Int(-val),
93 }
94 });
95 num_value(input)
96}
97
98pub fn parse_float(input: &str) -> IResult<&str, JSON> {
99 fn decimal(input: &str) -> IResult<&str, &str> {
100 recognize(many1(terminated(one_of("0123456789"), many0(char('_')))))(input)
101 }
102 let mut value = map(
103 alt((
104 recognize(tuple((opt(char('-')), char('.'), decimal))),
105 recognize(tuple((opt(char('-')), decimal, char('.'), decimal))),
106 )),
107 |num_str: &str| {
108 let num: String = num_str.chars().filter(|&c| c != '_').collect();
109 let x: f64 = num.parse().unwrap();
110 JSON::Float(x)
111 },
112 );
113 value(input)
114}
115
116pub fn parse_str(input: &str) -> IResult<&str, JSON> {
117 let mut value = alt((
118 combinator::value(JSON::Str(String::new()), tag("\"\"")),
119 map(
120 delimited(
121 tag("\""),
122 escaped_transform(
123 is_not("\"\\"),
124 '\\',
125 alt((
126 combinator::value("\\", tag("\\")),
127 combinator::value("\"", tag("\"")),
128 combinator::value("\'", tag("\'")),
129 combinator::value("\n", tag("n")),
130 combinator::value("\r", tag("r")),
131 combinator::value("\t", tag("t")),
132 )),
133 ),
134 tag("\""),
135 ),
136 JSON::Str,
137 ),
138 combinator::value(JSON::Str(String::new()), tag("\'\'")),
139 map(
140 delimited(
141 tag("\'"),
142 escaped_transform(
143 is_not("\'\\"),
144 '\\',
145 alt((
146 combinator::value("\\", tag("\\")),
147 combinator::value("\"", tag("\"")),
148 combinator::value("\'", tag("\'")),
149 combinator::value("\n", tag("n")),
150 combinator::value("\r", tag("r")),
151 combinator::value("\t", tag("t")),
152 )),
153 ),
154 tag("\'"),
155 ),
156 JSON::Str,
157 ),
158 ));
159 value(input)
160}
161
162pub fn parse_array(input: &str) -> IResult<&str, JSON> {
163 let mut value = map(
164 tuple((
165 tag("["),
166 commentable_spaces,
167 separated_list0(
168 tuple((tag(","), commentable_spaces)),
169 terminated(parse_data, commentable_spaces),
170 ),
171 opt(tuple((tag(","), commentable_spaces))),
172 tag("]"),
173 )),
174 |(_, _, elems, _, _)| JSON::Array(elems),
175 );
176 value(input)
177}
178
179pub fn parse_dict(input: &str) -> IResult<&str, JSON> {
180 let mut value = map(
181 tuple((
182 tag("{"),
183 commentable_spaces,
184 separated_list0(
185 tuple((tag(","), commentable_spaces)),
186 map(
187 tuple((
188 alt((
189 map(parse_str, |data| match data {
190 JSON::Str(s) => s,
191 _ => panic!(),
192 }),
193 identifier,
194 )),
195 commentable_spaces,
196 tag(":"),
197 commentable_spaces,
198 parse_data,
199 commentable_spaces,
200 )),
201 |(name, _, _, _, data, _)| (name, data),
202 ),
203 ),
204 opt(tuple((tag(","), commentable_spaces))),
205 tag("}"),
206 )),
207 |(_, _, items, _, _)| JSON::Dict(items),
208 );
209 value(input)
210}
211
212#[cfg(test)]
213mod test_parser {
214
215 use crate::json::JSON;
216 use crate::parser::parse_fson;
217
218 macro_rules! assert_value {
219 ($code: expr, $expected: expr) => {
220 assert_eq!(
221 parse_fson($code),
222 Some($expected),
223 "{} != {:?}",
224 $code,
225 $expected
226 );
227 };
228 }
229
230 #[test]
231 fn test_simple() {
232 assert_value!("null", JSON::Null);
233 assert_value!("None", JSON::Null);
234 assert_value!("NUL.", JSON::Null);
235 assert_value!("This is a NULL.", JSON::Null);
236 assert_value!("PI is 3.141", JSON::Float(3.141));
237 assert_value!("PI is 3.", JSON::Int(3));
238 assert_value!("\"\"", JSON::Str(String::from("")));
239 assert_value!("''", JSON::Str(String::from("")));
240 assert_value!("\"dog\"", JSON::Str(String::from("dog")));
241 assert_value!("\"'\"", JSON::Str(String::from("'")));
242 assert_value!("'\"'", JSON::Str(String::from("\"")));
243 }
244
245 #[test]
246 fn test_array() {
247 assert_value!("[]", JSON::Array(vec![]));
248 assert_value!("[null]", JSON::Array(vec![JSON::Null]));
249 assert_value!(
250 "[null, 1, []]",
251 JSON::Array(vec![JSON::Null, JSON::Int(1), JSON::Array(vec![]),])
252 );
253 }
254
255 #[test]
256 fn test_dict() {
257 assert_value!("{}", JSON::Dict(vec![]));
258 assert_value!(
259 "{x: 42, \"x 2\": {}}",
260 JSON::Dict(vec![
261 (String::from("x"), JSON::Int(42)),
262 (String::from("x 2"), JSON::Dict(vec![])),
263 ])
264 );
265 }
266
267 #[test]
268 fn test_complex() {
269 assert_value!(
270 r#"
271{
272 "x": 42,
273 'y': {},
274 z: [ nil ], // trailing comma!
275}.
276 "#,
277 JSON::Dict(vec![
278 (String::from("x"), JSON::Int(42)),
279 (String::from("y"), JSON::Dict(vec![])),
280 (String::from("z"), JSON::Array(vec![JSON::Null])),
281 ])
282 );
283 }
284}