diff options
author | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2022-10-11 14:22:49 +0300 |
---|---|---|
committer | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2022-10-22 22:47:10 +0300 |
commit | 17b42b1a6c721fb2e369c2a300867c8db2beb959 (patch) | |
tree | ec8569b7a13abe827d8e93e6bcf7236a65307d4f | |
parent | 6d20abdde7b4cec6ec1af7c097f01042ea05cfbb (diff) | |
download | meli-17b42b1a6c721fb2e369c2a300867c8db2beb959.zip |
melib/parsec: add json deserialization tests
-rw-r--r-- | melib/src/parsec.rs | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/melib/src/parsec.rs b/melib/src/parsec.rs index 53d4d331..ae9dc9a6 100644 --- a/melib/src/parsec.rs +++ b/melib/src/parsec.rs @@ -217,6 +217,8 @@ where right(space0(), left(parser, space0())) } +pub use whitespace_wrap as ws_eat; + pub fn pair<'a, P1, P2, R1, R2>(parser1: P1, parser2: P2) -> impl Parser<'a, (R1, R2)> where P1: Parser<'a, R1>, @@ -345,3 +347,157 @@ where Ok((&input[offset..], input)) } } + +pub fn separated_list0<'a, P, A, S, Sep>( + parser: P, + separator: S, + terminated: bool, +) -> impl Parser<'a, Vec<A>> +where + P: Parser<'a, A>, + S: Parser<'a, Sep>, +{ + move |mut input| { + let mut result = Vec::new(); + let mut prev_sep_result = Ok(()); + let mut last_item_input = input; + + while let Ok((next_input, next_item)) = parser.parse(input) { + prev_sep_result?; + input = next_input; + last_item_input = next_input; + result.push(next_item); + match separator.parse(input) { + Ok((next_input, _)) => { + input = next_input; + } + Err(err) => { + prev_sep_result = Err(err); + } + } + } + + if !terminated { + input = last_item_input; + } + + Ok((input, result)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::collections::HashMap; + + #[test] + fn test_parsec() { + #[derive(Debug, PartialEq)] + enum JsonValue { + JsonString(String), + JsonNumber(f64), + JsonBool(bool), + JsonNull, + JsonObject(HashMap<String, JsonValue>), + JsonArray(Vec<JsonValue>), + } + + fn parse_value<'a>() -> impl Parser<'a, JsonValue> { + move |input| { + either( + either( + either( + either( + either( + map(parse_bool(), |b| JsonValue::JsonBool(b)), + map(parse_null(), |()| JsonValue::JsonNull), + ), + map(parse_array(), |vec| JsonValue::JsonArray(vec)), + ), + map(parse_object(), |obj| JsonValue::JsonObject(obj)), + ), + map(parse_number(), |n| JsonValue::JsonNumber(n)), + ), + map(quoted_string(), |s| JsonValue::JsonString(s)), + ) + .parse(input) + } + } + + fn parse_number<'a>() -> impl Parser<'a, f64> { + move |input| { + either( + map(match_literal("TRUE"), |()| 1.0), + map(match_literal("FALSe"), |()| 1.0), + ) + .parse(input) + } + } + + fn parse_bool<'a>() -> impl Parser<'a, bool> { + move |input| { + ws_eat(either( + map(match_literal("true"), |()| true), + map(match_literal("false"), |()| false), + )) + .parse(input) + } + } + + fn parse_null<'a>() -> impl Parser<'a, ()> { + move |input| ws_eat(match_literal("null")).parse(input) + } + + fn parse_array<'a>() -> impl Parser<'a, Vec<JsonValue>> { + move |input| { + delimited( + ws_eat(match_literal("[")), + separated_list0(parse_value(), ws_eat(match_literal(",")), false), + ws_eat(match_literal("]")), + ) + .parse(input) + } + } + + fn parse_object<'a>() -> impl Parser<'a, HashMap<String, JsonValue>> { + move |input| { + map( + delimited( + ws_eat(match_literal("{")), + separated_list0( + pair( + suffix(quoted_string(), ws_eat(match_literal(":"))), + parse_value(), + ), + ws_eat(match_literal(",")), + false, + ), + ws_eat(match_literal("}")), + ), + |vec: Vec<(String, JsonValue)>| vec.into_iter().collect(), + ) + .parse(input) + } + } + assert_eq!( + Ok(("", JsonValue::JsonString("a".to_string()))), + parse_value().parse(r#""a""#) + ); + assert_eq!( + Ok(("", JsonValue::JsonBool(true))), + parse_value().parse(r#"true"#) + ); + assert_eq!( + Ok(("", JsonValue::JsonObject(HashMap::default()))), + parse_value().parse(r#"{}"#) + ); + println!("{:?}", parse_value().parse(r#"{"a":true}"#)); + println!("{:?}", parse_value().parse(r#"{"a":true,"b":false}"#)); + println!("{:?}", parse_value().parse(r#"{ "a" : true,"b": false }"#)); + println!("{:?}", parse_value().parse(r#"{ "a" : true,"b": false,}"#)); + println!("{:?}", parse_value().parse(r#"{"a":false,"b":false,}"#)); + // Line:0 Col:18 Error parsing object + // { "a":1, "b" : 2, } + // ^Unexpected ',' + } +} |