如何在Rust中为自己的解析器编写组合器?

问题描述 投票:2回答:1

this video的启发,我认为一个小的解析器组合器库将是学习字符串,在Rust中借用和键入的一种好方法,到目前为止,这是个好方法。

我设法让一个字符解析器和一个数字解析器工作:

mod parse {
    pub enum Parsed<'a, T> {
        Some(T, &'a str),
        None(&'a str),
    }

    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以获取给定解析器的任意数量的匹配项。那一个让我难受。这是我刚开始可以完成一些单元测试的版本:

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一起使用,它只需要一个解析器函数并返回一个。我尝试了很多变体:

  • fn(&str) -> Parsed<T>作为返回类型
  • impl Fn(&str) -> Parsed<T>作为返回类型
  • impl FnOnce(&str) -> Parsed<T>作为返回类型
  • 几个for<'r> something,编译器吐了出来,我什至都不明白
  • 将代码打包到一个闭包中,并在有或没有move的情况下将其返回

Rust至少有一行不满意。现在我不知道该怎么办了。顺便说一下,测试代码如下所示:

// unwrap code not provided, but straightforward.
assert_eq!(parse::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")
          );
parsing types rust composition
1个回答
0
投票

通过返回闭包来返回实现Fn*特征之一的匿名类型:

pub fn some<T>(parser: 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)
    }
}

Playground

[这是许多解析库采用的模式,包括我自己的peresil

© www.soinside.com 2019 - 2024. All rights reserved.