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

如何编写基于nom的解析器组合子:利用现有组合子生成解析器变体并通过alt尝试匹配

如何编写基于nom的解析器组合子:利用现有组合子生成解析器变体并通过alt尝试匹配

嘿,刚好我对nom的组合子玩法熟得很!你想要的是把一个通用的inner解析器,用nom自带的组合子改出两个不同版本,再用alt挨个尝试匹配对吧?这其实是组合子模式的典型用法,我给你捋清楚怎么实现。

核心思路很简单:先拿你的通用inner解析器,用nom里已有的组合子(比如delimited、preceded、terminated这类)包装出不同的解析逻辑变体,再把这些变体传给alt组合子,让它按顺序尝试匹配——哪个先成功就用哪个的结果。

我给你写个具体的示例代码,一看就懂:

use nom::{branch::alt, combinator::{delimited, preceded}, character::complete::{char, alpha1}, Parser};

// 自定义的组合子:接收通用inner解析器,返回组合后的新解析器
fn my_custom_combinator<I, O, E: nom::error::ParseError<I>>(
    inner: impl Parser<I, O, E>,
) -> impl Parser<I, O, E> {
    // 第一个变体:把inner包裹在圆括号里(用delimited组合子)
    let wrapped_in_parens = delimited(char('('), inner, char(')'));
    // 第二个变体:给inner加一个@前缀(用preceded组合子)
    let prefixed_with_at = preceded(char('@'), inner);
    
    // 用alt依次尝试两个变体
    alt((wrapped_in_parens, prefixed_with_at))
}

// 测试一下这个组合子的效果
fn main() {
    // 用解析字母的alpha1作为inner解析器
    let test_inputs = ["(hello)", "@world", "plain_text"];
    
    for input in test_inputs {
        match my_custom_combinator(alpha1).parse(input) {
            Ok((remaining, output)) => println!("✅ 匹配成功:剩余输入「{}」,结果「{}」", remaining, output),
            Err(e) => println!("❌ 匹配失败:{}", e),
        }
    }
}

运行这段代码,你会看到:

  • (hello)会被第一个变体匹配,提取出hello
  • @world会被第二个变体匹配,提取出world
  • plain_text因为既没括号也没@前缀,会匹配失败

再给你提几个需要注意的点:

  • 类型兼容:确保你用的现有组合子和inner的输入/输出类型匹配,比如如果inner处理的是&str,那包装它的组合子也要能处理&str输入。
  • 顺序影响:alt是按传入的顺序尝试解析器的,如果两个变体有重叠的匹配可能(比如一个变体是另一个的子集),一定要把更严格的放在前面,避免被宽松的变体提前匹配。
  • 扩展灵活:如果需要更多变体,直接在alt的元组里添加更多用现有组合子包装后的解析器就行,完全不用改核心逻辑。

备注:内容来源于stack exchange,提问作者gspr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 16:24:34