1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
pub mod extra;
pub mod error;
pub mod lex;

use crate::error::CompilationError;
use crate::lex::Token;

pub use lalrpop_util;
use logos::Logos;

pub mod parser {
    #![allow(clippy::all)]
    use lalrpop_util::lalrpop_mod;
    lalrpop_mod!(pub json);
    use super::*;
    pub use json::*;

    pub type ParseError<'a> = lalrpop_util::ParseError<usize, Token<'a>, CompilationError>;
    pub type ParseResult<'a> = Result<value::Value<'a>, ParseError<'a>>;
    #[derive(Debug)]
    pub struct Parsed<'a>(pub ParseResult<'a>);
}

pub mod value {
    use lexical;
    use std::fmt;

    #[derive(Debug, Clone, PartialEq)]
    pub enum Value<'a> {
        Number(f64),
        String(&'a str),
        Object(Vec<(&'a str, Value<'a>)>),
        Bool(bool),
        Null,
        Array(Vec<Value<'a>>),
    }

    impl<'a> fmt::Display for Value<'a> {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            match self {
                Value::Number(float) => write!(f, "{}", lexical::to_string(*float)),
                Value::String(string) => write!(f, "\"{}\"", string),
                Value::Object(obj) => {
                    write!(f, "{{")?;
                    if let Some(((key, value), rest)) = obj.split_first() {
                        write!(f, "\"{}\": {}", key, value)?;
                        for (key, value) in rest.iter() {
                            write!(f, ", \"{}\": {}", key, value)?
                        }
                    }
                    write!(f, "}}")
                }
                Value::Bool(flag) => write!(f, "{}", flag),
                Value::Null => write!(f, "null"),
                Value::Array(array) => {
                    write!(f, "[")?;
                    if let Some((value, rest)) = array.split_first() {
                        write!(f, "{}", value)?;
                        for value in rest.iter() {
                            write!(f, ", {}", value)?
                        }
                    }
                    write!(f, "]")
                }
            }
        }
    }
}

pub fn parse_str<'a>(
    bytes: &'a str,
) -> std::result::Result<
    value::Value<'a>,
    lalrpop_util::ParseError<usize, Token<'a>, CompilationError>,
> {
    let lexer = Token::lexer(bytes).spanned().map(Token::to_lalr_triple);
    parser::jsonParser::new().parse(lexer)
}

pub fn stringify<'a, W: std::io::Write>(w: &mut W, v: &'a value::Value<'a>) -> std::io::Result<()> {
    write!(w, "{}", *v)
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::extra::source;
    use source::ErrorHandling as _;
    use source::Parsable as _;
    use crate::extra::test_utils::Test;

    #[test]
    fn test_invalid() -> Result<(), error::TopLevelError> {
        let sources = ["�", r#""string with missing end quote"#]
            .iter()
            .map(|src| Test::TestInvalid(src.into()));
        Ok(for test in sources {
            assert_eq!(
                test.handle_errors(test.parse())
                    .map_err(|e| error::TopLevelError::from(e))?,
                crate::value::Value::Null
            );
        })
    }

    #[test]
    fn test_valid() -> Result<(), error::TopLevelError> {
        // The lifetimes here are kind of annoying in that we need to
        // let bind these rather than just place them right in the array...
        let empty_array = crate::value::Value::Array([].to_vec());
        let string_value = crate::value::Value::String("foo bar");
        let empty_string = crate::value::Value::String("");
        let sources = [
            ("[]", empty_array),
            (r#""foo bar""#, string_value),
            (r#""""#, empty_string),
        ];
        let tests = sources
            .iter()
            .map(|(src, result)| (Test::TestValid(src.into()), result));
        Ok(for (test, result) in tests {
            assert_eq!(
                test.handle_errors(test.parse())
                    .map_err(|e| error::TopLevelError::from(e))?,
                *result
            );
        })
    }
}