Add some tests

This commit is contained in:
Timothy Warren 2020-02-07 20:01:10 -05:00
parent 5e4e8c4356
commit 7588f29cc9

View File

@ -1,6 +1,6 @@
//! # Naive JSON Parser
use std::collections::HashMap; use std::collections::HashMap;
use std::iter::FromIterator; use std::iter::FromIterator;
use crate::ParseError::UnexpectedEndOfInput;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum JSONValue { pub enum JSONValue {
@ -13,7 +13,7 @@ pub enum JSONValue {
Null, Null,
} }
#[derive(Debug)] #[derive(Debug, PartialEq)]
pub enum ParseError { pub enum ParseError {
UnexpectedEndOfInput(String), UnexpectedEndOfInput(String),
ExpectedEndOfInput(String), ExpectedEndOfInput(String),
@ -25,7 +25,7 @@ pub enum ParseError {
ExpectedUnicodeEscape(String), ExpectedUnicodeEscape(String),
} }
#[derive(Debug)] #[derive(Debug, PartialEq)]
pub struct JSON { pub struct JSON {
chars: Vec<char>, chars: Vec<char>,
i: usize i: usize
@ -55,7 +55,7 @@ impl JSON {
match types.next() { match types.next() {
Some(val) => Ok(val), Some(val) => Ok(val),
None => Err(UnexpectedEndOfInput(String::new())) None => Err(ParseError::UnexpectedEndOfInput(String::new()))
} }
} }
@ -76,7 +76,7 @@ impl JSON {
// we take the path of string -> whitespace -> ':' -> value -> ... // we take the path of string -> whitespace -> ':' -> value -> ...
while self.chars[self.i] != '}' { while self.chars[self.i] != '}' {
if initial == false { if initial == false {
self.eat_char(',')?; self.eat(',')?;
self.skip_whitespace(); self.skip_whitespace();
} }
@ -89,7 +89,7 @@ impl JSON {
}; };
self.skip_whitespace(); self.skip_whitespace();
self.eat_char(':')?; self.eat(':')?;
let value = self.parse_value()?; let value = self.parse_value()?;
result.insert(key, value); result.insert(key, value);
@ -116,7 +116,7 @@ impl JSON {
while self.chars[self.i] != ']' { while self.chars[self.i] != ']' {
if initial == false { if initial == false {
self.eat_char(',')?; self.eat(',')?;
} }
let value = self.parse_value()?; let value = self.parse_value()?;
result.push(value); result.push(value);
@ -130,15 +130,22 @@ impl JSON {
} }
fn parse_string(&mut self) -> PartialResult { fn parse_string(&mut self) -> PartialResult {
todo!(); Ok(None)
} }
fn parse_number(&mut self) -> PartialResult { fn parse_number(&mut self) -> PartialResult {
todo!(); Ok(None)
} }
fn parse_keyword(&mut self, search: &str, value: JSONValue) -> PartialResult { fn parse_keyword(&mut self, search: &str, value: JSONValue) -> PartialResult {
let slice = &String::from_iter(&self.chars[self.i..self.i+search.len()]); let start = self.i;
let end = if self.i + search.len() > self.chars.len() {
self.chars.len()
} else {
self.i + search.len()
};
let slice = &String::from_iter(&self.chars[start..end]);
if slice == search { if slice == search {
self.i += search.len(); self.i += search.len();
@ -154,7 +161,7 @@ impl JSON {
} }
} }
fn eat_char(&mut self, ch: char) -> Result<(), ParseError> { fn eat(&mut self, ch: char) -> Result<(), ParseError> {
if self.chars[self.i] != ch { if self.chars[self.i] != ch {
let msg = format!(r#"Expected "{}"."#, ch); let msg = format!(r#"Expected "{}"."#, ch);
return Err(ParseError::ExpectedToken(msg)); return Err(ParseError::ExpectedToken(msg));
@ -165,6 +172,7 @@ impl JSON {
Ok(()) Ok(())
} }
/// Convert a `&str` containing JSON into a `Result<JSONValue, ParseError>`
pub fn parse(json: &str) -> JSONResult { pub fn parse(json: &str) -> JSONResult {
JSON::new(json).parse_value() JSON::new(json).parse_value()
} }
@ -177,7 +185,34 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn it_works() { fn parse_keyword() {
assert_eq!(2 + 2, 4); let mut parser = JSON::new(r#""foobarbaz""#);
let res = JSON::parse_keyword(&mut parser, "true", JSONValue::True);
assert_eq!(res, Ok(None));
let mut parser = JSON::new("true");
let res = JSON::parse_keyword(&mut parser, "true", JSONValue::True);
assert_eq!(res, Ok(Some(JSONValue::True)));
}
#[test]
fn skip_whitespace() {
let mut parser = JSON::new(" \t\r\nx");
parser.skip_whitespace();
assert_eq!('x', parser.chars[parser.i]);
}
#[test]
fn parse_empty_array() {
let mut parser = JSON::new("[]");
let res = JSON::parse_value(&mut parser);
assert_eq!(res, Ok(JSONValue::Array(vec![])));
}
#[test]
fn can_parse_array_of_keywords() {
let result = JSON::parse("[true,false,null]");
assert_eq!(result, Ok(JSONValue::Array(vec![JSONValue::True, JSONValue::False, JSONValue::Null])));
} }
} }