Mercurial > lbo > hg > rcombinators
changeset 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 | 2788bffa8d0d |
children | 6b717ea3438b |
files | examples/json/Cargo.toml examples/json/src/lib.rs |
diffstat | 2 files changed, 182 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/json/Cargo.toml Wed Jun 05 22:32:39 2019 +0200 @@ -0,0 +1,9 @@ +[package] +name = "rcombinators_example-json" +version = "0.1.0" +authors = ["Lewin Bormann <lewin@lewin-bormann.info>"] +edition = "2018" + +[dependencies] +rcombinators = { path = "../../" } +lazy_static = "1.3"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/json/src/lib.rs Wed Jun 05 22:32:39 2019 +0200 @@ -0,0 +1,173 @@ +//! 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)); + } +}