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

使用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())
    }
}

关键修改说明

  1. 普通令牌校验:在parse_token中添加verify逻辑,确保普通令牌不包含and,直接拒绝aandb这类非法单个令牌。
  2. 动态分隔符解析:parse_separator函数通过look_ahead预判下一个令牌类型:
    • 如果下一个是括号令牌,允许and后无空格;
    • 如果下一个是普通令牌,强制要求and后至少有一个空格,避免)andb这种非法情况。
  3. 灵活空白匹配:使用multispace0(允许0或多个空白)和multispace1(至少一个空白)适配不同场景的空格需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 16:57:08