You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Nom的many0中传播失败上下文信息?

问题分析

你的Nom解析器遇到关键词时错误信息丢失,核心原因是many0的行为特性:当子解析器返回**可恢复的Err::Error时,它会停止解析并返回已成功解析的结果;当子解析器返回不可恢复的Err::Failure**时,本应直接传递该错误,但在你的代码中,后续的eof检查失败覆盖了原始错误,导致上下文信息丢失。

解决方案

以下是几种简洁有效的修复方式:

方案1:用all_consuming替代手动eof检查

all_consuming组合子会强制整个输入被完全解析,同时优先保留子解析器产生的错误,不会被末尾的eof检查错误覆盖。

修改后的parse函数:

use nom::combinator::all_consuming;

fn parse(s: &str) -> ParseResult<Vec<&str>> {
    all_consuming(many0(label))(s)
}

方案2:让label返回Error类型错误,结合many_till

如果需要保留手动eof检查逻辑,可以将label中的fail()替换为返回Error类型错误,再用many_till组合子绑定解析器与eof检查,确保捕获子解析器的错误。

修改后的代码:

use nom::error::make_error;
use nom::multi::many_till;

fn label(s1: &str) -> ParseResult<&str> {
    let (s2, a) = take_while1(|c: char| c.is_alphabetic())(s1)?;
    let (s3, _) = take_while(|c: char| c.is_whitespace())(s2)?;
    if a == "derp" {
        return Err(nom::Err::Error(make_error(
            s1,
            nom::error::ErrorKind::Custom(0),
        )));
    }
    Ok((s3, a))
}

fn parse(s: &str) -> ParseResult<Vec<&str>> {
    let (s, (labels, _)) = many_till(label, eof)(s)?;
    Ok((s, labels))
}

方案3:手动确保Failure错误被正确传递

如果坚持使用fail()和context,可以手动构建VerboseError并添加上下文,确保Failure错误不被many0忽略:

fn label(s1: &str) -> ParseResult<&str> {
    let (s2, a) = take_while1(|c: char| c.is_alphabetic())(s1)?;
    let (s3, _) = take_while(|c: char| c.is_whitespace())(s2)?;
    if a == "derp" {
        return Err(nom::Err::Failure(
            VerboseError::from_error_kind(s1, nom::error::ErrorKind::Fail)
                .add_context(s1, "this message is lost"),
        ));
    }
    Ok((s3, a))
}
效果验证

使用方案1修改后,解析"foo bar herp derp"会返回包含上下文的原始错误:

Err(Failure(VerboseError { errors: [("derp", Context("this message is lost")), ("derp", Nom(Fail))] }))

内容的提问来源于stack exchange,提问作者sshine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 12:15:42