Nom 7中`alt`分支解析失败时未回溯的问题排查
Nom 7中
alt分支未按预期回溯的原因分析与修复 问题本质
你的第一个实现里,alt的作用范围搞错了——它只负责处理大括号内部的内容,而不是整个{...}结构。当用例6({a?})执行时,第一个alt分支其实是成功的,后续的错误来自外层的}匹配失败,和alt无关,自然不会触发第二个分支。
逐步骤拆解(以{a?}为例)
第一个实现brace_expr的执行流程:
- 外层
delimited先匹配{,剩余输入变为a?} - 进入
alt的第一个分支:delimited(multispace0, map(ident, Res::Good), multispace0)multispace0匹配空(没有空白),输入保持a?}ident成功匹配a,剩余输入变为?}multispace0再次匹配空(?不是空白,0个匹配也算成功),输入还是?}- 第一个分支成功返回
Good("a"),alt直接采用这个结果,不会尝试第二个分支
- 外层
delimited接下来要匹配},但当前输入的第一个字符是?,匹配失败,整个函数返回错误
第二个实现brace_expr2的执行流程:
alt的第一个分支是完整的delimited(char('{'), ..., char('}'))- 匹配
{后,剩余输入变为a?} - 内部的
delimited(multispace0, ident, multispace0)成功解析a,剩余输入变为?} - 尝试匹配
}失败,第一个分支整体失败
- 匹配
alt触发第二个分支,重新从原输入{a?}开始解析:- 匹配
{,剩余输入变为a?} - 第二个分支用
is_not("}")匹配所有内容直到},也就是a?,剩余输入变为} - 匹配
}成功,最终返回Bad("a?")
- 匹配
修复思路
如果想保留外层delimited的结构,需要让第一个分支明确:大括号内的内容只能是标识符+前后空白,不能有其他字符。最简单的方式是先提取大括号内的所有内容,再判断是否符合标识符规则,这样完全避开alt的回溯问题:
fn brace_expr_fixed(input: &str) -> IResult<&str, Res<'_>> { // 先提取大括号内的所有内容 let (remaining, content) = delimited(char('{'), is_not("}"), char('}'))(input)?; // 修剪空白后判断是否为合法标识符 let trimmed = content.trim(); match ident(trimmed) { Ok(_) => Ok((remaining, Res::Good(trimmed))), Err(_) => Ok((remaining, Res::Bad(content))), } }
这个实现逻辑更清晰,也不会出现回溯相关的问题,测试所有用例都会得到预期结果。
内容的提问来源于stack exchange,提问作者BallpointBen
相关产品推荐
相关产品推荐

