Skip to content

Instantly share code, notes, and snippets.

@south37
Last active June 25, 2022 15:18
Show Gist options
  • Select an option

  • Save south37/509ba25f32340184ad2981062e41a1e8 to your computer and use it in GitHub Desktop.

Select an option

Save south37/509ba25f32340184ad2981062e41a1e8 to your computer and use it in GitHub Desktop.

Revisions

  1. south37 revised this gist Jul 21, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion json_parser.rs
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    // Rewrite code with `parser!` macro.
    // Target: https://github.com/Marwes/combine/blob/master/benches/json.rs
    // Target: https://github.com/Marwes/combine/blob/f32fe7c135b8c3104843939b7b505f8b0ea4862e/benches/json.rs

    #[macro_use]
    extern crate combine;
  2. south37 created this gist Jul 21, 2018.
    214 changes: 214 additions & 0 deletions json_parser.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,214 @@
    // Rewrite code with `parser!` macro.
    // Target: https://github.com/Marwes/combine/blob/master/benches/json.rs

    #[macro_use]
    extern crate combine;

    use std::collections::hash_map::HashMap;

    use combine::parser::char::{char, string, digit, spaces};
    use combine::parser::choice::{choice, optional};
    use combine::parser::function::parser;
    use combine::parser::item::{any, satisfy, satisfy_map};
    use combine::parser::repeat::{many, many1, sep_by};
    use combine::parser::sequence::between;
    use combine::{Parser, Stream};
    use combine::error::{Consumed, ParseError};

    #[derive(PartialEq, Debug)]
    enum Value {
    Number(f64),
    String(String),
    Bool(bool),
    Null,
    Object(HashMap<String, Value>),
    Array(Vec<Value>),
    }

    parser! {
    fn lex[P](p: P)(P::Input) -> P::Output
    where [
    P: Parser,
    P::Input: Stream<Item = char>
    ]
    {
    p.skip(spaces())
    }
    }

    parser! {
    fn integer[I]()(I) -> i64
    where [
    I: Stream<Item = char>
    ]
    {
    lex(many1(digit()))
    .map(|s: String| {
    let mut n = 0;
    for c in s.chars() {
    n = n * 10 + (c as i64 - '0' as i64);
    }
    n
    })
    .expected("integer")
    }
    }

    parser! {
    fn number[I]()(I) -> f64
    where [
    I: Stream<Item = char>
    ]
    {
    let s = char('-').or(char('+'));
    let i = integer().map(|x| x as f64);
    let fractional = many(digit()).map(|digits: String| {
    let mut magnitude = 1.0;
    digits.chars().fold(0.0, |acc, d| {
    magnitude /= 10.0;
    match d.to_digit(10) {
    Some(d) => acc + (d as f64) * magnitude,
    None => panic!("Not a digit"),
    }
    })
    });
    let s2 = char('-').or(char('+'));
    let exp = satisfy(|c| c == 'e' || c == 'E').with(optional(s2).and(integer()));

    lex(optional(s)
    .and(i)
    .map(|(sign, n)| match sign {
    Some('-') => { -n }
    _ => { n }
    })
    .and(optional(char('.')).with(fractional))
    .map(|(x, y)| if x >= 0.0 { x + y } else { x - y })
    .and(optional(exp))
    .map(|(n, exp_option)| match exp_option {
    Some((sign, e)) => {
    let e = match sign {
    Some('-') => { -e }
    _ => { e }
    };
    n * 10.0f64.powi(e as i32)
    }
    None => n,
    })).expected("number")
    }
    }

    parser! {
    fn json_char[I]()(I) -> char
    where [
    I: Stream<Item = char>
    ]
    {
    parser(|input: &mut I| {
    let (c, consumed) = try!(any().parse_lazy(input).into());
    let mut back_slash_char = satisfy_map(|c| {
    Some(match c {
    '"' => '"',
    '\\' => '\\',
    '/' => '/',
    'b' => '\u{0008}',
    'f' => '\u{000c}',
    'n' => '\n',
    'r' => '\r',
    't' => '\t',
    _ => return None,
    })
    });
    match c {
    '\\' => consumed.combine(|_| back_slash_char.parse_stream(input)),
    '"' => Err(Consumed::Empty(I::Error::empty(input.position()).into())),
    _ => Ok((c, consumed)),
    }
    })
    }
    }

    parser! {
    fn json_string[I]()(I) -> String
    where [
    I: Stream<Item = char>
    ]
    {
    between(char('"'), lex(char('"')), many(json_char())).expected("string")
    }
    }

    parser! {
    fn object[I]()(I) -> Value
    where [
    I: Stream<Item = char>
    ]
    {
    let field = (json_string(), lex(char(':')), json_value()).map(|t| (t.0, t.2));
    let fields = sep_by(field, lex(char(',')));
    between(lex(char('{')), lex(char('}')), fields)
    .map(Value::Object)
    .expected("object")
    }
    }

    parser! {
    fn array[I]()(I) -> Value
    where [
    I: Stream<Item = char>
    ]
    {
    let values = sep_by(json_value(), lex(char(',')));
    between(lex(char('[')), lex(char(']')), values)
    .map(Value::Array)
    .expected("array")
    }
    }

    parser! {
    #[inline(always)]
    fn json_value[I]()(I) -> Value
    where [
    I: Stream<Item = char>
    ]
    {
    json_value_()
    }
    }

    // We need to use `parser!` to break the recursive use of `value` to prevent the returned parser
    // from containing itself
    parser!{
    #[inline(always)]
    fn json_value_[I]()(I) -> Value
    where [
    I: Stream<Item = char>
    ]
    {
    choice((
    json_string().map(Value::String),
    object(),
    array(),
    number().map(Value::Number),
    lex(string("false").map(|_| Value::Bool(false))),
    lex(string("true").map(|_| Value::Bool(true))),
    lex(string("null").map(|_| Value::Null)),
    ))
    }
    }

    fn main() {
    let mut p = number();
    let result = p.parse("-34.56e3");
    println!("{:?}", result);
    println!("{}", result.unwrap().0);

    let mut p2 = json_string();
    let result2 = p2.parse("\"this is json string!\\nGood!\"");
    println!("{:?}", result2);
    println!("{}", result2.unwrap().0);

    let mut p3 = json_value();
    let result3 = p3.parse("{ \"key\": [1, 2, 3] }");
    println!("{:?}", result3);
    println!("{:?}", result3.unwrap().0);
    }