From 17b42b1a6c721fb2e369c2a300867c8db2beb959 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 11 Oct 2022 14:22:49 +0300 Subject: [PATCH] melib/parsec: add json deserialization tests --- melib/src/parsec.rs | 156 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/melib/src/parsec.rs b/melib/src/parsec.rs index 53d4d331f..ae9dc9a6c 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> +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), + JsonArray(Vec), + } + + 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> { + 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> { + 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 ',' + } +}