自定义名称解析器错误,没有自定义ErrorKind



我有一个小解析器,如果它解析的数字超出边界,则会失败,

use nom::IResult;
use nom::bytes::complete::tag;
use nom::character::complete::digit1;
use nom::combinator::fail;
fn dup(s: &str) -> IResult<&str, u64> {
let (s, _) = tag("dup")(s)?;
let (s, n) = digit1(s)?;
let n = match n {
"0" => 0,
"1" => 1,
"2" => 2,
_ => return fail(s), // FIXME: Annotate.
};
Ok((s, n))
}

(参见操场)

但我希望错误更有意义。

我如何在n越界的上下文中注释此错误?

。代替fail(),使用提供错误消息的东西。

或者以某种方式将解析器的一部分包装在提供此上下文的内容中。

我知道你可以创建自己的自定义ErrorKind,但它可以做到没有吗?(当你有一个ErrorKind,error_position!()error_node_position!()宏将工作)

感谢masklin的回答(我已经标记为接受),添加自定义错误消息而不添加自定义错误类型的一种方法是使用VerboseErrorconvert_error()而不是默认的Error,因为这能够嵌入上下文。下面是一个修改后的例子(也是在操场上):

use nom::IResult;
use nom::bytes::complete::tag;
use nom::character::complete::digit1;
use nom::combinator::fail;
use nom::error::{context, VerboseError};
use nom::error::convert_error;
use nom::Finish;
fn dup(s: &str) -> IResult<&str, u64, VerboseError<&str>> {
let (s, _) = tag("dup")(s)?;
let (sd, n) = digit1(s)?;
let n = match n {
"0" => 0,
"1" => 1,
"2" => 2,
_ => return fail(s), // FIXME: Annotate.
};
Ok((sd, n))
}
fn main() {
let input = "dup3";
let result = context("dup", dup)(input).finish().err().unwrap();
println!("{}", convert_error(input, result));
}

添加context("dup", dup)为错误消息提供了一个非常漂亮和可读的上下文:

0: at line 1, in Fail:
dup3
^
1: at line 1, in dup:
dup3
^

,但它不能增加最内层的清晰度。如果我在fail行添加上下文:

let n = match n {
"0" => 0,
"1" => 1,
"2" => 2,
_ => return context("using an out-of-bounds dup", fail)(s),
};

则消息变为

0: at line 1, in Fail:
dup3
^
1: at line 1, in using an out-of-bounds dup:
dup3
^
2: at line 1, in dup:
dup3
^

就是我想要的!但是我真的只想替换掉失败"在使用超出范围的"时,不要添加"。这里值得一提的是错误管理文档对convert_error()的描述:

注意VerboseErrorconvert_error是作为语言错误的起点,但是它们不能涵盖所有用例。所以应该编写一个自定义的convert_error函数。

所以我发现添加自定义注释/上下文到错误消息的最不复杂的方式,是使用VerboseError,但也取代convert_error与一个弹出ErrorKind::FailErrorKind::Eof,如果他们后面跟着一个上下文,在这种情况下,我希望上下文驻留在同一位置,导致重复的条目:

fn pretty_print_error(s: &str, mut e: VerboseError<&str>) -> String {
let (_root_s, root_error) = e.errors[0].clone();
if matches!(root_error, VerboseErrorKind::Nom(ErrorKind::Fail))
|| matches!(root_error, VerboseErrorKind::Nom(ErrorKind::Eof))
{
e.errors.remove(0);
}
convert_error(s, e)
}

欢迎更简单的解决方案。

您可能需要阅读错误管理。

一般来说,nom::error::Error是解析器错误的低开销类型,这就是为什么它只有解析器错误

如果你想附加更多的上下文,nom提供了一个nom::error::VerboseError类型,也有辅助的crate提供进一步的错误包装。

最后,如果仍然不够,nom的错误处理是基于ParseError特性的,所以你可以有一个完全自定义的错误类型和实现。后一个选项显然开销最大,但灵活性也最高。

相关内容

  • 没有找到相关文章

最新更新