Skip to content

Instantly share code, notes, and snippets.

@Lucretiel
Created December 20, 2019 05:43
Show Gist options
  • Save Lucretiel/f77b08242f57a54faaa097a3064bf213 to your computer and use it in GitHub Desktop.
Save Lucretiel/f77b08242f57a54faaa097a3064bf213 to your computer and use it in GitHub Desktop.
Parse an escaped string, using (kind of) the JSON rules
use std::convert::TryFrom;
use nom::branch::alt;
use nom::bytes::streaming::{is_not, take};
use nom::character::streaming::char;
use nom::combinator::{map, map_res, value, verify};
use nom::error::ParseError;
use nom::multi::fold_many0;
use nom::sequence::{delimited, preceded};
use nom::IResult;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum StringFragment<'a> {
Literal(&'a str),
Escaped(char),
}
/// Parse a string using (mostly) the JSON rules. Allows streaming input.
fn parse_string<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&'a str, String, E> {
delimited(
char('"'),
fold_many0(
alt((
// Match either a bunch of non-escaped characters or one escaped character
map(
verify(is_not("\"\\"), |s: &str| !s.is_empty()),
StringFragment::Literal,
),
map(
preceded(
char('\\'),
alt((
value('\\', char('\\')),
value('/', char('/')),
value('"', char('"')),
value('\u{08}', char('b')),
value('\u{0C}', char('f')),
value('\n', char('n')),
value('\r', char('r')),
value('\t', char('t')),
preceded(
char('u'),
map_res(
map_res(take(4usize), |s| u32::from_str_radix(s, 16)),
char::try_from,
),
),
)),
),
StringFragment::Escaped,
),
)),
String::new(),
|mut string, fragment| {
match fragment {
StringFragment::Literal(s) => string.push_str(s),
StringFragment::Escaped(c) => string.push(c),
};
string
},
),
char('"'),
)(input)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment