Less clever, lazy evaluation of parser method chain

This commit is contained in:
Timothy Warren 2020-02-11 10:57:48 -05:00
parent a2ecaef5ee
commit e4070abd40
1 changed files with 59 additions and 43 deletions

View File

@ -79,38 +79,37 @@ impl JSON {
} }
/// Parse a `JSONValue` from the current JSON string /// Parse a `JSONValue` from the current JSON string
///
/// This is probably an abuse of iterators...but it's still much better than the alternative
/// of nested matches.
///
/// In order to determine the type of JSON value, each parse method is tried, until one
/// matches, or a parse error happens.
///
/// * `Option`s implement IntoIterator, which returns an iterator of 0 or 1 items: the
/// transferred (not borrowed) Some() value.
/// * The `chain` method of iterators allows you to link iterators together,
/// to act as one iterator
/// * The first result from the iterator is the first parse method with a non-empty value,
/// and should be the value wanted
fn parse_value(&mut self) -> Result<JSONValue, ParseError> { fn parse_value(&mut self) -> Result<JSONValue, ParseError> {
self.skip_whitespace(); self.skip_whitespace();
let mut value = self return if let Some(string) = self.parse_string()? {
.parse_string()? Ok(string)
.into_iter() } else if let Some(number) = self.parse_number()? {
.chain(self.parse_number()?.into_iter()) Ok(number)
.chain(self.parse_object()?.into_iter()) } else if let Some(object) = self.parse_object()? {
.chain(self.parse_array()?.into_iter()) Ok(object)
.chain(self.parse_keyword("true", JSONValue::True)?.into_iter()) } else if let Some(array) = self.parse_array()? {
.chain(self.parse_keyword("false", JSONValue::False)?.into_iter()) Ok(array)
.chain(self.parse_keyword("null", JSONValue::Null)?.into_iter()); } else if let Some(t) = self.parse_keyword("true", JSONValue::True)? {
Ok(t)
match value.next() { } else if let Some(f) = self.parse_keyword("false", JSONValue::False)? {
Some(val) => Ok(val), Ok(f)
None => Err(ParseError::UnexpectedEndOfInput(String::from( } else if let Some(n) = self.parse_keyword("null", JSONValue::Null)? {
Ok(n)
} else {
Err(ParseError::UnexpectedEndOfInput(String::from(
"Doesn't seem to be valid JSON", "Doesn't seem to be valid JSON",
))), )))
} };
// Eagerly evaluated simpler alternative to the original option iterator chain
// let value = self.parse_string()?
// .or(self.parse_number()?)
// .or(self.parse_object()?)
// .or(self.parse_array()?)
// .or(self.parse_keyword("true", JSONValue::True)?)
// .or(self.parse_keyword("false", JSONValue::False)?)
// .or(self.parse_keyword("null", JSONValue::Null)?);
} }
/// See if there's a `JSONValue::Object` next in the JSON /// See if there's a `JSONValue::Object` next in the JSON
@ -255,7 +254,7 @@ impl JSON {
let start = self.i; let start = self.i;
// If it doesn't start with 0-9 or a minus sign, it's probably not a number // If it doesn't start with 0-9 or a minus sign, it's probably not a number
if ! (self.chars[start].is_ascii_digit() || self.chars[start] == '-') { if !(self.chars[start].is_ascii_digit() || self.chars[start] == '-') {
return Ok(None); return Ok(None);
} }
@ -297,11 +296,7 @@ impl JSON {
// If there are numeric digits attempt to parse the digits as a number // If there are numeric digits attempt to parse the digits as a number
if n > start { if n > start {
let mut end = if n < self.chars.len() { let mut end = if n < self.chars.len() { n } else { max };
n
} else {
max
};
// Hack to remove non-number characters // Hack to remove non-number characters
if !self.chars[end].is_ascii_digit() { if !self.chars[end].is_ascii_digit() {
@ -313,8 +308,8 @@ impl JSON {
match str.parse::<f64>() { match str.parse::<f64>() {
Ok(number) => { Ok(number) => {
self.increment(str.len()); self.increment(str.len());
return Ok(Some(JSONValue::Number(number))) return Ok(Some(JSONValue::Number(number)));
}, }
Err(e) => Err(ParseError::ExpectedDigit(format!("'{}', {:#?}", str, e))), Err(e) => Err(ParseError::ExpectedDigit(format!("'{}', {:#?}", str, e))),
} }
} else { } else {
@ -488,19 +483,37 @@ mod tests {
// Number // Number
let res = JSON::parse("9.38083151965"); let res = JSON::parse("9.38083151965");
assert_eq!(res, Ok(JSONValue::Number(9.38083151965)), "Failed to parse number"); assert_eq!(
res,
Ok(JSONValue::Number(9.38083151965)),
"Failed to parse number"
);
// String // String
let res = JSON::parse(r#""/^$/""#); let res = JSON::parse(r#""/^$/""#);
assert_eq!(res, Ok(JSONValue::String(String::from("/^$/"))), "Failed to parse string"); assert_eq!(
res,
Ok(JSONValue::String(String::from("/^$/"))),
"Failed to parse string"
);
// Number array // Number array
let res = JSON::parse("[1, 2, 3]"); let res = JSON::parse("[1, 2, 3]");
assert_eq!(res, Ok(JSONValue::Array(vec![JSONValue::Number(1f64), JSONValue::Number(2f64), JSONValue::Number(3f64)]))); assert_eq!(
res,
Ok(JSONValue::Array(vec![
JSONValue::Number(1f64),
JSONValue::Number(2f64),
JSONValue::Number(3f64)
]))
);
// Object array // Object array
let result = JSON::parse("[{}]"); let result = JSON::parse("[{}]");
assert_eq!(result, Ok(JSONValue::Array(vec![JSONValue::Object(HashMap::new())]))); assert_eq!(
result,
Ok(JSONValue::Array(vec![JSONValue::Object(HashMap::new())]))
);
} }
#[test] #[test]
@ -512,8 +525,10 @@ mod tests {
#[test] #[test]
fn can_parse_arbitrary_json() { fn can_parse_arbitrary_json() {
let result = JSON::parse(r#"[{ "a": 9.38083151965, "b": 4e3 }]"#); let result = JSON::parse(r#"[{ "a": 9.38083151965, "b": 4e3 }]"#);
assert!(result.is_ok(), format!("Failed on just number values: {:#?}", result)); assert!(
result.is_ok(),
format!("Failed on just number values: {:#?}", result)
);
let result = JSON::parse( let result = JSON::parse(
r#"[{ r#"[{
@ -529,7 +544,8 @@ mod tests {
} }
}, },
"i": ["\"", "\\", "/", "\b", "\f", "\n", "\r", "\t", "\u0001", "\uface"] "i": ["\"", "\\", "/", "\b", "\f", "\n", "\r", "\t", "\u0001", "\uface"]
}]"#); }]"#,
);
assert!(result.is_ok(), format!("{:#?}", result)); assert!(result.is_ok(), format!("{:#?}", result));
} }
} }