view examples/json/src/lib.rs @ 25:3a4fbcfc9245

Add JSON example parser prototype Still need to figure out how to fix the recursive problem.
author Lewin Bormann <lewin@lewin-bormann.info>
date Wed, 05 Jun 2019 22:32:39 +0200
parents
children 09d274a46fa4
line wrap: on
line source

//! A simplistic JSON parser library based on the `rcombinators` crate.
//!

#[macro_use]
extern crate lazy_static;

use std::collections::HashMap;
use std::iter::FromIterator;

use rcombinators::combinators;
use rcombinators::primitives;
use rcombinators::{ParseResult, ParseState, Parser};

#[derive(Debug, PartialEq)]
pub enum Value {
    Number(f64),
    String(String),
    Dict(HashMap<String, Value>),
    List(Vec<Value>),
    None,
}

impl Default for Value {
    fn default() -> Value {
        Value::None
    }
}

struct PWrapper<P>(P);

impl<P: Parser<Result=Value>> Parser for PWrapper<P> {
    type Result = Value;
    fn parse(&mut self, st: &mut ParseState<impl Iterator<Item=char>>) -> ParseResult<Self::Result> {
        self.0.parse(st)
    }
}

#[derive(Default)]
struct ValueParser<P>(Option<P>);

impl<P: Parser<Result=Value>> Parser for ValueParser<P> {
    type Result = Value;
    fn parse(&mut self, st: &mut ParseState<impl Iterator<Item=char>>) -> ParseResult<Self::Result> {
        combinators::Alternative::new((dict(), list(), string(), number())).parse(st)
    }
}

fn number() -> impl Parser<Result = Value> {
    primitives::float().apply(|n| Ok(Value::Number(n)))
}

fn string() -> impl Parser<Result = Value> {
    let quote = primitives::StringParser::new("\"");
    let middle = primitives::string_none_of("\"", combinators::RepeatSpec::Any);
    let string_with_quotes = combinators::Sequence::new((quote.clone(), middle, quote));
    let string = string_with_quotes.apply(|(_, s, _)| Ok(Value::String(s)));
    string
}

fn list<P: Parser<Result=Value>>(val: Option<PWrapper<P>>) -> impl Parser<Result = Value> {
    let (open, close) = (
        primitives::StringParser::new("["),
        primitives::StringParser::new("]"),
    );
    let val = val;
    let comma = primitives::StringParser::new(",");
    let separated_element = combinators::Sequence::new((
        primitives::whitespace(),
        val,
        primitives::whitespace(),
        combinators::Maybe::new(comma),
    ));
    let separated_element = separated_element.apply(|(_, v, _, _)| Ok(v));
    let separated_elements =
        combinators::Repeat::new(separated_element, combinators::RepeatSpec::Any);
    let list = combinators::Sequence::new((open, separated_elements, close))
        .apply(|(_, es, _)| Ok(Value::List(es)));
    list
}

fn dict() -> impl Parser<Result = Value> {
    let (open, close) = (
        primitives::StringParser::new("{"),
        primitives::StringParser::new("}"),
    );
    let comma = primitives::StringParser::new(",");
    let sep = primitives::StringParser::new(":");
    let key = string().apply(|v| match v {
        Value::String(s) => Ok(s),
        _ => panic!("unexpected value type in string position"),
    });
    let value = ValueParser(None);
    let separated_element = combinators::Sequence::new((
        primitives::whitespace(),
        key,
        primitives::whitespace(),
        sep,
        primitives::whitespace(),
        value,
        primitives::whitespace(),
        combinators::Maybe::new(comma),
    ));
    let separated_element =
        separated_element.apply(|(_ws1, k, _ws2, _sep, _ws3, v, _ws4, _comma)| Ok((k, v)));
    let separated_elements =
        combinators::Repeat::new(separated_element, combinators::RepeatSpec::Any);
    let dict = combinators::Sequence::new((open, separated_elements, close))
        .apply(|(_, es, _)| Ok(Value::Dict(HashMap::from_iter(es.into_iter()))));
    dict
}

#[cfg(test)]
mod tests {
    use crate::*;

    #[test]
    fn test_number() {
        let mut ps = ParseState::new("-1.2e0");
        assert_eq!(Ok(Value::Number(-1.2)), number().parse(&mut ps));
    }

    #[test]
    fn test_string() {
        let mut ps = ParseState::new("\"Hello, World\n\"");
        assert_eq!(
            Ok(Value::String("Hello, World\n".to_string())),
            string().parse(&mut ps)
        );
    }

    #[test]
    fn test_list() {
        let mut ps = ParseState::new(r#"[1, 2, "Hello",]"#);
        let want = Value::List(vec![
            Value::Number(1.),
            Value::Number(2.),
            Value::String("Hello".to_string()),
        ]);
        assert_eq!(Ok(want), list().parse(&mut ps));
    }

    #[test]
    fn test_dict() {
        let mut ps = ParseState::new(r#"{"hello": ["world", []], "x": 4}"#);
        let want = Value::Dict(HashMap::from_iter(vec![
            (
                "hello".to_string(),
                Value::List(vec![
                    Value::String("world".to_string()),
                    Value::List(vec![]),
                ]),
            ),
            ("x".to_string(), Value::Number(4.)),
        ]));
        assert_eq!(Ok(want), dict().parse(&mut ps));
    }

    #[test]
    fn test_value() {
        let mut ps = ParseState::new(r#"{"hello": ["world", []], "x": 4}"#);
        let want = Value::Dict(HashMap::from_iter(vec![
            (
                "hello".to_string(),
                Value::List(vec![
                    Value::String("world".to_string()),
                    Value::List(vec![]),
                ]),
            ),
            ("x".to_string(), Value::Number(4.)),
        ]));
        assert_eq!(Ok(want), ValueParser.parse(&mut ps));
    }
}