summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2022-10-11 14:22:49 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2022-10-22 22:47:10 +0300
commit17b42b1a6c721fb2e369c2a300867c8db2beb959 (patch)
treeec8569b7a13abe827d8e93e6bcf7236a65307d4f
parent6d20abdde7b4cec6ec1af7c097f01042ea05cfbb (diff)
downloadmeli-17b42b1a6c721fb2e369c2a300867c8db2beb959.zip
melib/parsec: add json deserialization tests
-rw-r--r--melib/src/parsec.rs156
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 ','
+ }
+}