use nom::{branch::alt, bytes::complete::{escaped_transform, tag, take_until}, character::complete::{char, none_of}, combinator::{map, opt, value}, sequence::delimited, IResult, Parser};
use crate::{parser::{doc::StofParseError, whitespace::whitespace}, runtime::Val};
pub fn string(input: &str) -> IResult<&str, Val, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, out) = alt((
raw_double_string,
double_string,
single_string
)).parse(input)?;
return Ok((input, Val::Str(out.into())))
}
pub(self) fn raw_double_string(input: &str) -> IResult<&str, String, StofParseError> {
let (input, res) = delimited(tag("r#\""), take_until("\"#"), tag("\"#")).parse(input)?;
Ok((input, res.into()))
}
pub fn double_string(input: &str) -> IResult<&str, String, StofParseError> {
let normal = none_of("\"\\"); let inner = escaped_transform(normal, '\\', alt((
value("\\", tag("\\")),
value("\"", tag("\"")),
value("\n", tag("n")),
value("\r", tag("r")),
value("\t", tag("t")),
)));
delimited(char('"'), map(opt(inner), |opt| opt.unwrap_or_default()), char('"')).parse(input)
}
pub fn single_string(input: &str) -> IResult<&str, String, StofParseError> {
let normal = none_of("'\\"); let inner = escaped_transform(normal, '\\', alt((
value("\\", tag("\\")),
value("'", tag("'")),
value("\n", tag("n")),
value("\r", tag("r")),
value("\t", tag("t")),
)));
delimited(char('\''), map(opt(inner), |opt| opt.unwrap_or_default()), char('\'')).parse(input)
}
#[cfg(test)]
mod tests {
use crate::parser::string::{double_string, raw_double_string, single_string, string};
#[test]
fn raw() {
let res = raw_double_string("r#\"Hello, world!\"#").unwrap();
assert_eq!(res.1, "Hello, world!");
assert_eq!(res.0, "");
}
#[test]
fn raw_double_esc_quote() {
let res = raw_double_string("r#\"Hello, \"world\"!\"#").unwrap();
assert_eq!(res.1, r#"Hello, "world"!"#);
assert_eq!(res.0, "");
}
#[test]
fn double() {
let res = double_string("\"Hello, world!\"").unwrap();
assert_eq!(res.1, "Hello, world!");
assert_eq!(res.0, "");
}
#[test]
fn double_esc() {
let res = double_string("\"Hello,\\t \\\"world\\\"!\"").unwrap();
assert_eq!(res.1, "Hello,\t \"world\"!");
assert_eq!(res.0, "");
}
#[test]
fn single() {
let res = single_string("'Hello, world!'").unwrap();
assert_eq!(res.1, "Hello, world!");
assert_eq!(res.0, "");
}
#[test]
fn single_esc() {
let res = single_string("'Hello,\\n \\'world\\'!'").unwrap();
assert_eq!(res.1, "Hello,\n 'world'!");
assert_eq!(res.0, "");
}
#[test]
fn value() {
assert_eq!(string(r#""Hello, \"CJ\"!""#).unwrap().1, "Hello, \"CJ\"!".into());
assert_eq!(string(r#"'Hello, \'CJ\'!'"#).unwrap().1, "Hello, 'CJ'!".into());
assert_eq!(string("r#\"Hello, \"CJ\"!\"#").unwrap().1, "Hello, \"CJ\"!".into());
}
}