使用Rust Nom v7解析Markdown代码块:终止条件匹配问题
Rust Nom v7 解析Markdown代码块的解决方案
核心思路
Nom v7 没有直接提供“读取直到指定解析器匹配”的预构建组合子,但可以通过many_till组合子实现——它会反复执行第一个解析器收集内容,直到第二个解析器成功匹配终止条件,最终返回收集到的内容和终止匹配结果。
具体实现
- 定义终止条件解析器:实现匹配“单独一行的三个反引号(可后跟任意空白)”的逻辑,同时兼容代码块在文档末尾结束(无换行)的场景:
use nom::{ branch::alt, bytes::complete::tag, character::complete::{newline, space0}, combinator::eof, sequence::tuple, IResult, }; fn code_block_end(input: &str) -> IResult<&str, ()> { let (input, _) = tuple((tag("```"), space0, alt((newline, eof))))(input)?; Ok((input, ())) }
- 收集代码块内容:用
many_till结合逐字符读取的解析器,直到code_block_end匹配成功:
use nom::{multi::many_till, bytes::complete::take_while_m_n}; fn code_block_content(input: &str) -> IResult<&str, String> { let (input, (content_chunks, _)) = many_till( // 逐字符读取,确保不会跳过任何内容 take_while_m_n(1, 1, |_| true), code_block_end )(input)?; Ok((input, content_chunks.into_iter().collect())) }
- 整合完整代码块解析器:加上代码块开头的三个反引号匹配逻辑:
fn markdown_code_block(input: &str) -> IResult<&str, String> { let (input, _) = tuple((tag("```"), space0, newline))(input)?; let (input, content) = code_block_content(input)?; Ok((input, content)) }
测试验证
对于包含内部```的代码块(示例用单引号替代反引号展示):
这里是代码内容
'''
继续写代码
实际测试替换回反引号后,解析器会正确跳过内部的反引号序列,仅当遇到单独一行的终止标记时才停止,原断言失败的问题会解决。
关键说明
- 放弃
take_until是正确选择,因为它仅基于字面量贪婪匹配,无法识别“单独一行”的上下文要求。 many_till是Nom处理“直到特定解析器匹配”场景的标准方案,完全无需正则,符合你的需求。
内容的提问来源于stack exchange,提问作者Keozon
相关产品推荐
相关产品推荐

