use super::prelude::*;
fn parse_esc(input: &str) -> IResult<&str, char> {
preceded(
char('\\'),
context(
"escape",
alt((
value('\\', char('\\')),
value('\n', char('n')),
value('\r', char('r')),
value('\t', char('t')),
value('\'', char('\'')),
value('`', char('`')),
value('"', char('"')),
)),
),
)(input)
}
fn literal_parser<'a>(quo: char) -> impl FnMut(&'a str) -> IResult<&'a str, &'a str> {
let not_quote_slash = take_till(move |c| c == '\\' || c == quo);
verify(not_quote_slash, |s: &str| !s.is_empty())
}
pub enum Fragment<'a> {
Literal(&'a str),
Char(char),
}
fn fragment_parser<'a>(quo: char) -> impl FnMut(&'a str) -> IResult<&'a str, Fragment<'a>> {
alt((
map(literal_parser(quo), Fragment::Literal),
map(parse_esc, Fragment::Char),
))
}
pub fn parse_string(input: &str) -> IResult<&str, String> {
context(
"string",
alt((string_parser('"'), string_parser('`'), string_parser('\''))),
)(input)
}
fn string_parser<'a>(quo: char) -> impl FnMut(&'a str) -> IResult<&'a str, String> {
let parse_fragment = fragment_parser(quo);
let build_string = fold_many0(parse_fragment, String::new, |mut buf, frag| {
match frag {
Fragment::Literal(s) => buf.push_str(s),
Fragment::Char(c) => buf.push(c),
};
buf
});
delimited(char(quo), build_string, char(quo))
}