1use std::collections::HashMap;
2
3use nom::{
4 branch::alt,
5 bytes::complete::{is_not, tag, take_till, take_till1, take_until},
6 character::complete::multispace1,
7 combinator::{map, value},
8 multi::many0,
9 number::complete::double,
10 sequence::{delimited, pair, separated_pair, tuple},
11 IResult,
12};
13
14#[derive(thiserror::Error, Debug)]
15pub enum Error {
16 #[error("error ocurred while parsing")]
17 ParseError,
18}
19
20#[derive(Debug, PartialEq, Clone)]
21pub enum TotValue {
22 Unit,
23 Boolean(bool),
24 String(String),
25 Number(f64),
26 List(Vec<TotValue>),
27 Dict(HashMap<String, TotValue>),
28}
29
30pub type PResult<'a, T> = IResult<&'a str, T>;
31
32fn token(i: &str) -> PResult<&str> {
33 take_till1(|c: char| c.is_whitespace())(i)
34}
35
36pub(crate) fn unit(i: &str) -> PResult<()> {
37 value((), tag("null"))(i)
38}
39
40pub(crate) fn boolean(i: &str) -> PResult<bool> {
41 alt((value(true, tag("true")), value(false, tag("false"))))(i)
42}
43
44pub(crate) fn number(i: &str) -> PResult<f64> {
45 double(i)
46}
47
48pub(crate) fn string(i: &str) -> PResult<String> {
49 map(
50 delimited(tag("\""), take_till(|c: char| c == '"'), tag("\"")),
51 String::from,
52 )(i)
53}
54
55fn whitespace(i: &str) -> PResult<()> {
56 map(multispace1, |_| ())(i)
57}
58
59fn comma(i: &str) -> PResult<()> {
60 value((), tag(","))(i)
61}
62
63fn line_comment(i: &str) -> PResult<()> {
64 value((), pair(tag("//"), is_not("\r\n")))(i)
65}
66
67fn block_comment(i: &str) -> PResult<()> {
68 value((), tuple((tag("/*"), take_until("*/"), tag("*/"))))(i)
69}
70
71pub(crate) fn all_ignored(i: &str) -> PResult<()> {
72 map(
73 many0(alt((line_comment, block_comment, comma, whitespace))),
74 |_| (),
75 )(i)
76}
77
78fn list(i: &str) -> PResult<TotValue> {
79 delimited(tag("["), list_contents, tag("]"))(i)
80}
81
82fn list_contents(i: &str) -> PResult<TotValue> {
83 map(many0(delimited(all_ignored, scalar, all_ignored)), |v| {
84 TotValue::List(v)
85 })(i)
86}
87
88fn dict(i: &str) -> PResult<TotValue> {
89 delimited(tag("{"), dict_contents, tag("}"))(i)
90}
91
92fn dict_contents(i: &str) -> PResult<TotValue> {
93 map(many0(key_value), |v| TotValue::Dict(HashMap::from_iter(v)))(i)
94}
95
96pub(crate) fn key(i: &str) -> PResult<String> {
97 alt((map(string, String::from), map(token, String::from)))(i)
98}
99
100pub(crate) fn expression(i: &str) -> PResult<TotValue> {
101 todo!()
102}
103
104fn scalar(i: &str) -> PResult<TotValue> {
106 alt((
107 map(unit, |_| TotValue::Unit),
108 map(boolean, |v| TotValue::Boolean(v)),
109 map(number, |v| TotValue::Number(v)),
110 map(string, |v| TotValue::String(v)),
111 list,
112 dict,
113 ))(i)
114}
115
116fn key_value(i: &str) -> PResult<(String, TotValue)> {
117 delimited(
118 all_ignored,
119 separated_pair(key, all_ignored, scalar),
120 all_ignored,
121 )(i)
122}
123
124pub fn parse(i: &str) -> Result<TotValue, Error> {
125 if let Ok((rem, v)) = dict_contents(i) {
126 if rem.is_empty() {
127 return Ok(v);
128 }
129 }
130
131 Err(Error::ParseError)
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use std::collections::HashMap;
138
139 #[test]
140 fn test_parse() {
141 if let TotValue::Dict(v) = parse("test 1").unwrap() {
142 assert_eq!(
143 v.get_key_value("test").unwrap(),
144 (&"test".to_string(), &TotValue::Number(1.0))
145 );
146 } else {
147 assert!(false);
148 }
149
150 if let TotValue::Dict(v) = parse("test 1 blah true").unwrap() {
151 assert_eq!(v.get("test").unwrap(), &TotValue::Number(1.0));
152 assert_eq!(v.get("blah").unwrap(), &TotValue::Boolean(true));
153 } else {
154 assert!(false);
155 }
156
157 if let TotValue::Dict(v) = parse(
158 "\
159test 1
160blah true
161dict {
162 hello \"world\"
163}
164",
165 )
166 .unwrap()
167 {
168 assert_eq!(v.get("test").unwrap(), &TotValue::Number(1.0));
169 assert_eq!(v.get("blah").unwrap(), &TotValue::Boolean(true));
170 assert_eq!(
171 v.get("dict").unwrap(),
172 &TotValue::Dict({
173 let mut m = HashMap::new();
174 m.insert("hello".to_string(), TotValue::String("world".to_string()));
175
176 m
177 })
178 );
179 } else {
180 assert!(false);
181 }
182 }
183
184 #[test]
185 fn test_unit() {
186 let (rem, _) = unit("null// hello").unwrap();
187 assert_eq!(rem, "// hello");
188 }
189
190 #[test]
191 fn test_token() {
192 let (rem, par) = token("my-key 2").unwrap();
193 assert_eq!(rem, " 2");
194 assert_eq!(par, "my-key");
195
196 assert!(token("").is_err());
197 }
198
199 #[test]
200 fn test_boolean() {
201 let (_, par) = boolean("true").unwrap();
202 assert_eq!(par, true);
203
204 let (_, par) = boolean("false").unwrap();
205 assert_eq!(par, false);
206
207 assert!(boolean("True").is_err());
208 assert!(boolean("False").is_err());
209 assert!(boolean("").is_err());
210 }
211
212 #[test]
213 fn test_number() {
214 let (_, par) = number("10").unwrap();
215 assert_eq!(par, f64::from(10));
216
217 let (_, par) = number("10.").unwrap();
218 assert_eq!(par, f64::from(10));
219
220 let (_, par) = number("10.0").unwrap();
221 assert_eq!(par, f64::from(10));
222
223 let (_, par) = number("0").unwrap();
224 assert_eq!(par, f64::from(0));
225
226 let (_, par) = number("0.1").unwrap();
227 assert_eq!(par, f64::from(0.1));
228
229 let (_, par) = number(".1").unwrap();
230 assert_eq!(par, f64::from(0.1));
231
232 let (_, par) = number("10]").unwrap();
233 assert_eq!(par, f64::from(10));
234
235 assert!(number("one").is_err());
236 assert!(number("").is_err());
237 }
238
239 #[test]
240 fn test_string() {
241 let (rem, par) = string("\"hello world\"foo").unwrap();
242 assert_eq!(rem, "foo");
243 assert_eq!(par, "hello world");
244
245 assert!(string("hello world").is_err());
246 assert!(string("1").is_err());
247 }
248
249 #[test]
250 fn test_whitespace() {
251 let (rem, _) = whitespace(" hello").unwrap();
252 assert_eq!(rem, "hello");
253
254 let (rem, _) = whitespace(" ").unwrap();
255 assert_eq!(rem, "");
256
257 assert!(whitespace("hello").is_err());
258 }
259
260 #[test]
261 fn test_line_comment() {
262 let (rem, _) = line_comment("// blah").unwrap();
263 assert_eq!(rem, "");
264
265 let (rem, _) = line_comment("// this is a comment\ntext").unwrap();
266 assert_eq!(rem, "\ntext");
267 }
268
269 #[test]
270 fn test_block_comment() {
271 let (rem, _) = block_comment("/* moo */\nhello world").unwrap();
272 assert_eq!(rem, "\nhello world");
273
274 let (rem, _) = block_comment("/* moo\n\n\t\r */\nhello world").unwrap();
275 assert_eq!(rem, "\nhello world");
276 }
277
278 #[test]
279 fn test_all_ignored() {
280 let (rem, _) = all_ignored("/* hello world *///hello").unwrap();
281 assert_eq!(rem, "");
282
283 let (rem, _) = all_ignored("/* hello world */ //hello").unwrap();
284 assert_eq!(rem, "");
285
286 let (rem, _) = all_ignored("//hello /* hello world */").unwrap();
287 assert_eq!(rem, "");
288
289 let (rem, _) = all_ignored("/* hello world */ woot").unwrap();
290 assert_eq!(rem, "woot");
291 }
292
293 #[test]
294 fn test_list() {
295 let (rem, par) = list("[]").unwrap();
296 assert_eq!(rem, "");
297 assert_eq!(par, TotValue::List(vec![]));
298
299 let (rem, par) = list("[1]").unwrap();
300 assert_eq!(rem, "");
301 assert_eq!(par, TotValue::List(vec![TotValue::Number(1.0)]));
302
303 let (rem, par) = list("[] blah []").unwrap();
304 assert_eq!(rem, " blah []");
305 assert_eq!(par, TotValue::List(vec![]));
306
307 let (rem, par) = list("[1] blah []").unwrap();
308 assert_eq!(rem, " blah []");
309 assert_eq!(par, TotValue::List(vec![TotValue::Number(1.0)]));
310
311 let (rem, par) = list("[1, 2\n , /* inner comment */ 3.1 4] blah []").unwrap();
312 assert_eq!(rem, " blah []");
313 assert_eq!(
314 par,
315 TotValue::List(vec![
316 TotValue::Number(1.0),
317 TotValue::Number(2.0),
318 TotValue::Number(3.1),
319 TotValue::Number(4.0)
320 ])
321 );
322
323 assert!(list("").is_err());
324 assert!(list("hello").is_err());
326 assert!(list("[hello]").is_err());
328 assert!(list("[").is_err());
330 assert!(list("[ 1 ").is_err());
331 }
332
333 #[test]
334 fn test_dict() {
335 let (rem, par) = dict("{}").unwrap();
336 assert_eq!(rem, "");
337 assert_eq!(par, TotValue::Dict(HashMap::default()));
338
339 let (_, par) = dict("{hello \"world\"}").unwrap();
340 assert_eq!(
341 par,
342 TotValue::Dict({
343 let mut map = HashMap::new();
344 map.insert("hello".to_string(), TotValue::String("world".to_string()));
345
346 map
347 })
348 );
349
350 let (_, par) = dict("{hello \"world\" inner-list [true 10]}").unwrap();
351 assert_eq!(
352 par,
353 TotValue::Dict({
354 let mut map = HashMap::new();
355 map.insert("hello".to_string(), TotValue::String("world".to_string()));
356 map.insert(
357 "inner-list".to_string(),
358 TotValue::List(vec![TotValue::Boolean(true), TotValue::Number(10.0)]),
359 );
360
361 map
362 })
363 );
364 }
365
366 #[test]
367 fn test_key() {
368 let (rem, par) = key("my-key").unwrap();
369 assert_eq!(rem, "");
370 assert_eq!(par, "my-key");
371
372 let (rem, par) = key("my-key 2").unwrap();
373 assert_eq!(rem, " 2");
374 assert_eq!(par, "my-key");
375
376 let (rem, par) = key("\"my-key\" 2").unwrap();
377 assert_eq!(rem, " 2");
378 assert_eq!(par, "my-key");
379
380 let (rem, par) = key("\"my key\" 2").unwrap();
381 assert_eq!(rem, " 2");
382 assert_eq!(par, "my key");
383 }
384
385 #[test]
386 fn test_scalar() {
387 let (_, par) = scalar("true").unwrap();
388 assert_eq!(par, TotValue::Boolean(true));
389
390 let (_, par) = scalar("1").unwrap();
391 assert_eq!(par, TotValue::Number(1.0));
392
393 let (_, par) = scalar("\"hello\"").unwrap();
394 assert_eq!(par, TotValue::String("hello".to_string()));
395
396 let (_, par) = scalar("[false]").unwrap();
397 assert_eq!(par, TotValue::List(vec![TotValue::Boolean(false)]));
398 }
399
400 #[test]
401 fn test_key_value() {
402 let (_, par) = key_value("hello true").unwrap();
403 assert_eq!(par.0, "hello");
404 assert_eq!(par.1, TotValue::Boolean(true));
405
406 let (_, par) = key_value("\"hello world\" false").unwrap();
407 assert_eq!(par.0, "hello world");
408 assert_eq!(par.1, TotValue::Boolean(false));
409
410 let (_, par) = key_value("hello 10").unwrap();
411 assert_eq!(par.0, "hello");
412 assert_eq!(par.1, TotValue::Number(10.0));
413
414 let (_, par) = key_value("hello \"world\"").unwrap();
415 assert_eq!(par.0, "hello");
416 assert_eq!(par.1, TotValue::String("world".to_string()));
417
418 let (_, par) = key_value("hello [0 true [\"hello\"]]").unwrap();
419 assert_eq!(par.0, "hello");
420 assert_eq!(
421 par.1,
422 TotValue::List(vec![
423 TotValue::Number(0.0),
424 TotValue::Boolean(true),
425 TotValue::List(vec![TotValue::String("hello".to_string())])
426 ])
427 );
428 }
429}