2

我是 Rust 和 Nom 的新手,我正在尝试解析可能包含转义引号的(单)引号字符串,例如'foo\' bar'or 'λx → x', ''or ' '

我找到了escaped!宏,它的文档说:

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

由于我想在匹配器中为“普通字符”匹配除反斜杠之外的任何内容,我尝试使用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 中。解决此问题的正确方法是什么?

4

1 回答 1

1

在所谓的流模式下运行时,nom可能会返回Incomplete表明它无法决定并需要更多数据。4nom介绍CompleteStr。除了 之外CompleteByteSlice,它们是 和 的完整输入对应&str&[u8]。解析器将它们作为完整模式下的输入工作。

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

对于所有这些血淋淋的细节,您可能需要查看这篇博文,尤其是Streaming VS complete parser部分。

此外,函数组合器比nom5 中的宏组合器更受欢迎。这是一种方法:

//# 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)
}
于 2019-11-18T03:05:30.380 回答