如何编写基于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会被第二个变体匹配,提取出worldplain_text因为既没括号也没@前缀,会匹配失败
再给你提几个需要注意的点:
- 类型兼容:确保你用的现有组合子和
inner的输入/输出类型匹配,比如如果inner处理的是&str,那包装它的组合子也要能处理&str输入。 - 顺序影响:
alt是按传入的顺序尝试解析器的,如果两个变体有重叠的匹配可能(比如一个变体是另一个的子集),一定要把更严格的放在前面,避免被宽松的变体提前匹配。 - 扩展灵活:如果需要更多变体,直接在
alt的元组里添加更多用现有组合子包装后的解析器就行,完全不用改核心逻辑。
备注:内容来源于stack exchange,提问作者gspr
相关产品推荐
相关产品推荐

