使用Rust Nom实现支持可选空格的带括号分隔列表解析
解决Rust Nom解析器的令牌分隔列表问题
针对你遇到的问题,我们需要调整分隔符的解析逻辑,同时确保非法场景被正确拒绝。以下是具体的解决方案和修改后的代码:
核心思路
要同时支持带空格和无空格的分隔符,需要根据前后令牌的类型动态调整分隔符的匹配规则:
- 当分隔符前后是括号包裹的令牌时,允许
and前后无空格(如)and(); - 当分隔符前后是普通字母令牌时,
and必须被空格包围(如and); - 禁止普通令牌包含
and子串(避免aandb被误判为合法单个令牌)。
修改后的完整代码
use nom::branch::alt; use nom::bytes::complete::tag; use nom::character::complete::{alpha1, char, multispace0, multispace1}; use nom::combinator::{all_consuming, complete, look_ahead, map, verify}; use nom::error::Error; use nom::multi::separated_list1; use nom::sequence::delimited; use nom::{Finish, IResult}; fn parse_token(i: &str) -> IResult<&str, &str> { // 确保普通令牌不包含"and"子串,避免非法单个令牌通过 verify(alpha1, |s: &&str| !s.contains("and"))(i) } fn parse_parens(i: &str) -> IResult<&str, &str> { delimited(char('('), parse_token, char(')'))(i) } fn parse_separator(i: &str) -> IResult<&str, ()> { // 匹配分隔符前的可选空白(括号令牌后可无空白) let (i, _) = multispace0(i)?; // 匹配"and"关键字 let (i, _) = tag("and")(i)?; // 预判下一个令牌类型,决定后续空白规则 let next_is_paren = look_ahead(alt(( map(parse_parens, |_| true), map(parse_token, |_| false), )))(i)? .1; if next_is_paren { // 下一个是括号令牌,允许0个或多个空白 let (i, _) = multispace0(i)?; Ok((i, ())) } else { // 下一个是普通令牌,必须至少有一个空白 let (i, _) = multispace1(i)?; Ok((i, ())) } } fn parse_and(i: &str) -> IResult<&str, Vec<&str>> { separated_list1(parse_separator, alt((parse_parens, parse_token)))(i) } fn parse(i: &str) -> Result<Vec<&str>, Error<&str>> { let result = all_consuming(complete(parse_and))(i); result.finish().map(|(_, o)| o) } #[cfg(test)] mod tests { use super::*; #[test] fn no_parens() { assert!(parse("a and b").is_ok()) } #[test] fn parens() { assert!(parse("(a) and (b)").is_ok()) } #[test] fn mixed() { assert!(parse("(a) and b").is_ok()) } #[test] fn parens_no_space() { assert!(parse("(a)and b").is_ok()) } #[test] fn parens_no_space_both() { assert!(parse("(a)and(b)").is_ok()) } #[test] fn no_parens_no_space() { assert!(parse("(a)andb").is_err()) } #[test] fn invalid_single_token() { assert!(parse("aandb").is_err()) } #[test] fn invalid_and_after_plain() { assert!(parse("aand(b)").is_err()) } }
关键修改说明
- 普通令牌校验:在
parse_token中添加verify逻辑,确保普通令牌不包含and,直接拒绝aandb这类非法单个令牌。 - 动态分隔符解析:
parse_separator函数通过look_ahead预判下一个令牌类型:- 如果下一个是括号令牌,允许
and后无空格; - 如果下一个是普通令牌,强制要求
and后至少有一个空格,避免)andb这种非法情况。
- 如果下一个是括号令牌,允许
- 灵活空白匹配:使用
multispace0(允许0或多个空白)和multispace1(至少一个空白)适配不同场景的空格需求。
内容的提问来源于stack exchange,提问作者ojii
相关产品推荐
相关产品推荐

