melib/parsec: add json deserialization tests

parsec-improve
Manos Pitsidianakis 2022-10-11 14:22:49 +03:00
parent 6d20abdde7
commit 17b42b1a6c
1 changed files with 156 additions and 0 deletions

View File

@ -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 ','
}
}