From cc4b96af0e7be992a534c54d03cb1e1f9bbde78e Mon Sep 17 00:00:00 2001 From: Devon Sawatsky Date: Tue, 4 Aug 2026 17:26:22 +0000 Subject: [PATCH] strings! --- src/lib.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1035aa9..e03a760 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,5 @@ use nom::{ - IResult, Parser, - branch::alt, - bytes::complete::tag, - combinator::{map_res, value}, - sequence::delimited, + IResult, Parser, branch::alt, bytes::complete::{tag, take_till, take_till1}, combinator::{map_res, value}, sequence::delimited, }; use std::collections::HashMap; @@ -27,10 +23,40 @@ fn parse_bool(input: &str) -> IResult<&str, JsonValue> { .parse(input) } fn parse_string(input: &str) -> IResult<&str, JsonValue> { - delimited(tag("\""), parse_string_inner, tag("\"")).parse(input) + delimited(tag("\""), parse_string_inner, tag("\"")) + .map(JsonValue::String) + .parse(input) +} +fn parse_string_inner(input: &str) -> IResult<&str, String> { + let mut string_chunks = vec![]; + let mut remaining = input; + while let Ok((rem, parsed)) = alt(( + parse_string_inner_naive.map(|s| s.to_string()), + parse_escape_character.map(|s| s.to_string()), + )) + .parse(remaining) + { + remaining = rem; + string_chunks.push(parsed) + } + Ok((remaining, string_chunks.concat())) } -fn parse_string_inner(input: &str) -> IResult<&str, JsonValue> { - todo!() +fn parse_string_inner_naive(input: &str) -> IResult<&str, &str> { + take_till1(|c: char| c == '\"' || c == '\\' || c.is_control())(input) +} +fn parse_escape_character(input: &str) -> IResult<&str, char> { + alt(( + value('\"', tag("\\\"")), + // value('\f', tag("\\f")), + value('\\', tag("\\\\")), + value('\n', tag("\\n")), + value('/', tag("\\/")), + value('\r', tag("\\r")), + // value('\b', tag("\\b")), + value('\t', tag("\\t")), + //todo: unicode + )) + .parse(input) } pub fn deserialize_json(json: &str) -> IResult<&str, JsonValue> { @@ -52,6 +78,22 @@ mod tests { let f = deserialize_json("false").unwrap(); assert_eq!(t.1, JsonValue::Boolean(true)); assert_eq!(f.1, JsonValue::Boolean(false)); + } + #[test] + fn deserializes_strings() { + let t = deserialize_json(r#""true""#).unwrap(); + assert_eq!(t.1, JsonValue::String("true".to_string())); + let f = deserialize_json(r#""false""#).unwrap(); + assert_eq!(f.1, JsonValue::String("false".to_string())); + } + #[test] + fn deserializes_escaped_strings() { + let escaped = deserialize_json(r#"" \" \\ \n \/ \r \t ""#).unwrap(); + assert_eq!(escaped.1, JsonValue::String(" \" \\ \n / \r \t ".to_string())); + } + #[test] + fn invalid_escaped_strings() { + deserialize_json(r#"" \g ""#).expect_err("should be an error"); } #[test] -- 2.51.2