melib/parsec: add json deserialization tests
parent
6d20abdde7
commit
17b42b1a6c
|
@ -217,6 +217,8 @@ where
|
||||||
right(space0(), left(parser, space0()))
|
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)>
|
pub fn pair<'a, P1, P2, R1, R2>(parser1: P1, parser2: P2) -> impl Parser<'a, (R1, R2)>
|
||||||
where
|
where
|
||||||
P1: Parser<'a, R1>,
|
P1: Parser<'a, R1>,
|
||||||
|
@ -345,3 +347,157 @@ where
|
||||||
Ok((&input[offset..], input))
|
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 ','
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue