使用最新的(v7)标称板条箱。
尝试构建一个能够从markdown中提取代码块的解析器。在我需要支持的markdown风格中,代码块只有在一行中有三个抑音符/反勾号字符时才结束,除非后面可能有空格。
下面是一个例子,我用单引号(')替换反勾号,使StackOverflow减价编辑变得合理:
'''python
print("""
'''")
// this is all still a code block
'''
显而易见的解决方案是只使用take_until("'''")
,然而,这将提前结束take,因为这只是搜索第一次出现的'''
,这是不准确的。我需要终止条件为tuple((tag(code_end), space0, newline))
。
下一个明显的解决方案是使用正则表达式作为take_until
中的模式......但我更愿意避免这样做。是否有任何预构建的解析器(或在另一个机箱中可用)可以接受所有内容,直到解析器返回Ok
?
use nom::IResult;
use nom::combinator::opt;
use nom::sequence::{terminated, tuple};
use nom::bytes::complete::{tag, take_until};
use nom::character::complete::{newline, space0, alpha1};
fn code(i: &[u8]) -> IResult<&[u8], &[u8]> {
let (input, _) = tuple((tag("'''"), opt(alpha1), tag("\n")))(i)?;
let terminator = tuple((tag("'''"), space0, newline));
let (input, contents) = terminated(take_until("'''"), terminator)(input)?;
Ok((input, contents))
}
fn main() {
let test = &b"'''python
print(\"\"\"
'''\"\"\"
// this is all still a code block
'''
";
assert!(code(&test[..]).is_ok());
}
上面的Assert将失败。但是,如果删除带有三个(''')单引号的行,它将通过。这是因为terminator
和take_all("'''")
之间的差异。解决此问题的最佳模式是什么?
谢谢你的帮助。我有一种感觉,我错过了一些明显的东西,或者只是做错了什么。如果有什么不清楚的地方,请告诉我。
为方便起见,下面是Rust Playground中上述示例的链接:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d5459edded1e4258ba3e034658ea4acf
1条答案
按热度按时间gblwokeq1#
我认为正确的组合符应该是
many_till
:应用解析器f,直到解析器g产生结果。
它与
anychar
结合将为代码块返回Vec<char>
。我认为nom中没有
anybyte
,但是如果你更喜欢得到Vec<u8>
,你可以很容易地自己写。或者,如果你想避免分配,并且想要一个引用原始切片的切片,并且不介意有一点不安全,你可以忽略使用的字符,并从开始和结束指针(playground)开始计算切片: