如何在Rust中为自己的解析器编写组合器? [英] How do I write combinators for my own parsers in Rust?
问题描述
受这部影片的启发,我认为一个解析器组合器库将是一个到目前为止,学习字符串,借用和在Rust中打字的好方法。
Inspired by this video, I thought a little parser combinator library would be a good way to learn about strings, borrowing and typing in Rust—and it was so far.
我设法让一个字符解析器和一个数字解析器可以工作:
I managed to get a char parser and a digit parser to work:
pub enum Parsed<'a, T> {
Some(T, &'a str),
None(&'a str),
}
impl<T> Parsed<'_, T> {
// I was neither sure with the third & before the T...
pub fn unwrap(&self) -> (&T, &str) {
match self {
// ... nor with the first one here.
Parsed::Some(head, tail) => (&head, &tail),
_ => panic!("Called unwrap on nothing."),
}
// But this was the only way that I came up with that compiled.
}
pub fn is_none(&self) -> bool {
match self {
Parsed::None(_) => true,
_ => false,
}
}
}
pub fn parse<T>(what: fn(&str) -> Parsed<T>, input: &str) -> Parsed<T> {
what(input)
}
pub fn char(input: &str) -> Parsed<char> {
match input.chars().next() {
Some(c) => Parsed::Some(c, &input[1..]),
None => Parsed::None(input),
}
}
pub fn digit(input: &str) -> Parsed<u8> {
match input.chars().next() {
Some(d @ '0'..='9') => Parsed::Some(d as u8 - ('0' as u8), &input[1..]),
_ => Parsed::None(input),
}
}
然后我想要转到组合器,这里 some
可以获取给定解析器的任意数量的匹配项。那个人让我很受打击。这是我刚开始可以完成一些单元测试的版本:
Then I wanted to turn to combinators, here some
to get an arbitrary number of matches for a given parser. That one hit me hard. This is the version I had in the beginning that was able to fulfill some unit tests:
pub fn some<T>(input: &str, parser: fn(&str) -> Parsed<T>) -> Parsed<Vec<T>> {
let mut re = Vec::new();
let mut pos = input;
loop {
match parser(pos) {
Parsed::Some(head, tail) => {
re.push(head);
pos = tail;
}
Parsed::None(_) => break,
}
}
Parsed::Some(re, pos)
}
但是要能够与 parse :: parse
一起使用,它只需要一个解析器函数并返回一个。我尝试了很多变体:
But to be able to use it with parse::parse
it has to take only a parser function and return one. I tried so many variants:
-
fn(& str)->将< T>
解析为返回类型 -
impl Fn(& str)->将< T>
解析为返回类型 -
impl FnOnce(& str)->将< T>
解析为返回类型 - 多个
c 编译器吐出来的东西,我什至都不明白
- 将代码打包到一个闭包中,并在有和没有
的情况下返回移动
fn(&str) -> Parsed<T>
as return typeimpl Fn(&str) -> Parsed<T>
as return typeimpl FnOnce(&str) -> Parsed<T>
as return type- several
for<'r> something
which the compiler spat out and I don't even understand - packing the code into a closure and returning that, with and without
move
Rust总是至少有一行不满意。现在我不知道该怎么办了。测试代码如下:
There was always at least one line that Rust wasn't happy with. Now I don't know what to try anymore. The testing code looks like this:
#[test]
fn test() {
assert_eq!(char("foo").unwrap(), (&'f', "oo"));
assert!(parse(digit, "foo").is_none());
assert_eq!(parse(digit, "9foo").unwrap(), (&9, "foo"));
assert_eq!(
parse(some(digit), "12space").unwrap(),
(&vec![1, 2], "space")
);
}
这是游乐场。
推荐答案
通过返回闭包,返回实现 Fn *
特征之一的匿名类型:
Return an anonymous type that implements one of the Fn*
traits by returning a closure:
fn some<T>(parser: impl Fn(&str) -> Parsed<T>) -> impl FnOnce(&str) -> Parsed<Vec<T>> {
move |input| {
let mut re = Vec::new();
let mut pos = input;
loop {
match parser(pos) {
Parsed::Some(head, tail) => {
re.push(head);
pos = tail;
}
Parsed::None(_) => break,
}
}
Parsed::Some(re, pos)
}
}
请注意,我已将函数指针从函数指针切换为通用类型:
Note that I've switched from function pointers to generic types for the arguments:
fn some<T>(parser: fn(&str) -> Parsed<T>) // before
fn some<T>(parser: impl Fn(&str) -> Parsed<T>) // after
I之后提倡对所有功能执行此操作,以使您拥有一致且可连接的API。
这是很多解析库采用的模式,包括我自己的 peresil 。
I advocate doing this for all of your functions so that you have a consistent and connectable API. This is the pattern taken by many parsing libraries, including my own peresil.
另请参见:
- How do you pass a Rust function as a parameter?
- Why does passing a closure to function which accepts a function pointer not work?
这篇关于如何在Rust中为自己的解析器编写组合器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!