使用Nom 5解析带引号的单引号字符串

时间:2019-11-17 19:56:38

标签: rust nom

我是Rust和Nom的新手,我想解析一个(单个)带引号的字符串,其中可能包含转义的引号,例如'foo\' ? bar''λx → x'''' '

我发现了escaped!宏,其documentation表示:

  

第一个参数匹配普通字符(不能接受控制字符),第二个参数是控制字符(如大多数语言中的\),第三个参数匹配转义字符

由于我想在匹配器中匹配除“正常字符”外的任何反斜杠,因此我尝试使用take_till!

    named!(till_backslash<&str, &str>, take_till!(|ch| ch == '\\'));
    named!(esc<&str, &str>, escaped!(call!(till_backslash), '\\', one_of!("'n\\")));

    let (input, _) = nom::character::complete::char('\'')(input)?;
    let (input, value) = esc(input)?;
    let (input, _) = nom::character::complete::char('\'')(input)?;

    // … use `value`

但是,当尝试解析'x'时,它将返回Err(Incomplete(Size(1)))。搜索时,人们通常建议使用CompleteStr,但这不是Nom 5中的方法。解决此问题的正确方法是什么?

1 个答案:

答案 0 :(得分:1)

在所谓的流模式下运行时,nom可能返回Incomplete来指示它无法决定并需要更多数据。 nom 4引入了CompleteStr。与CompleteByteSlice一起,它们是&str&[u8]的完整输入副本。解析器以完整模式将它们作为输入工作。

它们在nom中消失了。在nom 5中,如您所见,基于宏的解析器始终以流模式工作。对于在流模式和完整模式下工作方式不同的解析器组合器,它们在单独的子模块中有不同的版本,例如nom::bytes::streamingnom::bytes::complete

对于所有这些细节,您可能需要查看this blog post,尤其是流VS完整解析器部分。

此外,函数组合器比nom 5中的宏组合器更受青睐。这是一种实现方法:

//# nom = "5.0.1"
use nom::{
    branch::alt,
    bytes::complete::{escaped, tag},
    character::complete::none_of,
    sequence::delimited,
    IResult,
};

fn main() {
    let (_, res) = parse_quoted(r#"'foo\' ? bar'"#).unwrap();
    assert_eq!(res, r#"foo\' ? bar"#);
    let (_, res) = parse_quoted("'λx → x'").unwrap();
    assert_eq!(res, "λx → x");
    let (_, res) = parse_quoted("'  '").unwrap();
    assert_eq!(res, "  ");
    let (_, res) = parse_quoted("''").unwrap();
    assert_eq!(res, "");
}

fn parse_quoted(input: &str) -> IResult<&str, &str> {
    let esc = escaped(none_of("\\\'"), '\\', tag("'"));
    let esc_or_empty = alt((esc, tag("")));
    let res = delimited(tag("'"), esc_or_empty, tag("'"))(input)?;

    Ok(res)
}