5 个版本

0.4.3 2020年3月13日
0.4.2 2020年3月8日
0.4.1 2020年3月7日
0.4.0 2020年3月7日
0.2.0 2020年2月21日

#101 in 解析工具

每月 23 次下载

ISC 许可证

125KB
1.5K SLoC

Rust crates.io docs.rs license

秋季递归下降解析器组合库

秋季是一个用于使用组合器构建递归下降解析器的库。它提供了一系列基本解析器,用于解析单个字符和常见的字符组合,收集解析值的原语,以及组合和修改解析器的组合器。

入门

解析器是一个实现了 Parser 特性的项目。实现该特性的最简单方法是使用以下签名的函数

fn parser(source: &str, location: Span) -> ParseResult<String> {
    /* ... */
}

这是一个函数,它接受一个输入字符串切片及其在某个原始源文本中的位置,并从该位置解析一个 String 值。

一个简单的标识符解析器

以下是一个实现 C 类似标识符解析器的示例。这些标识符以 ASCII 字母或下划线开头,后面跟任意数量的 ASCII 数字、字母或下划线。

/// Parse a single alphabetic character or underscore
fn identifier_prefix(source: &str, location: Span) -> ParseResult<Span> {
    alphabetic.or("_").parse(source, location)
}

/// Parse a zero or more letters, digits, and underscores
fn identifier_body(source: &str, location: Span) -> ParseResult<Span> {
    identifier_prefix.or(digit).multiple().maybe().parse(source, location)
}

/// Accumulate the characters for a single identifier into a string
fn identifier(source: &str, location: Span) -> ParseResult<String> {
    identifier_prefix
        .and(identifier_body)
        .copy_string()
        .parse(source, location)
}

由于上述每个函数都实现了 Parser 特性,因此可以直接在函数上使用 组合器

示例中展示了大量最常用的组合器。

  • or 将接受一个解析器并尝试在相同位置使用另一个替代解析器。如果两者都成功,这可以产生来自两个解析器的结果。

  • and 将接受一个产生 List 的解析器,并将另一个产生相同类型列表的解析器的结果附加到其上。

  • multiple 将接受一个产生 List 的解析器,并尝试连续多次应用该解析器。

  • maybe 接收一个生成 List 的解析器,并尝试零次或一次应用该解析器。当同时使用 multiplemaybe 来实现零次或多次重复时,必须使用 multiple().maybe()maybe().multiple() 可以找到无限种方式将任何解析器应用于甚至空字符串。

  • map 可以用于转换特定解析器生成的类型。

  • end 仅在解析器之后没有剩余输入时才会产生成功的解析。

上述也使用了提供的一些 解析器

调用解析器时,必须以字符串切片的形式提供源,并以 Span 的形式提供当前位置。可以使用 new_location 提供任何字符串开头的初始 span,但应使用 path_location 指定与文件路径关联的 span。

解析器产生的 ParseResult 包含输入字符串的所有有效解析。

如果构建了一个有歧义的解析器,则将产生该解析器可以用来处理输入的所有独特方式;可以使用 single_parse 来检查是否只产生了一个有效解析。

每次成功的解析都会封装在一个与源代码中包含解析结果的字符范围相关联的Meta结构中,该结构体与一个Span相关联,反映了包含解析结果的源代码中的范围。可以使用meta组合子来获取解析过程中与值相关联的位置。

错误

解析器在解析过程中也可能需要生成错误。解析器会丢弃与有效解析无关的错误,因此必须将某些值与错误相关联,并且错误将与解析中的特定位置相关联。

如果与ParseResult相关联存在错误,其类型必须作为类型构造函数的第三个类型参数传递。

如果源代码的任何有效解析产生了错误,则is_success将返回false

以下示例将解析字母表的前五个小写字母,或者将产生与同一位置开始的字母字符相关联的错误。

/// Parses the first 5 letters of the alphabet in order
fn alphabet_parse() -> impl Parser<Span, &'static str> {
    "abcde"
        .on_none(
            alphabetic
                .multiple()
                .maybe()
                .and_then(|text| error(text, "Not in alphabetical order"))
        )
}

可以使用on_none组合子提供一个替代解析器,该解析器仅在原始解析器无法找到任何有效解析(包括产生错误的解析器)时使用。

可以使用on_failure组合子提供一个替代解析器,该解析器在原始解析器产生错误、未找到有效解析或找到与错误相关联的有效解析时使用。

error解析器将生成包含提供的值的有效解析,但也会生成提供的错误。这允许解析继续并找到更多的错误。

解析器生成的ParseResult包含所有有效解析输入产生的所有错误。每个error将与生成错误的位置相关联。

异常

异常可以用来将错误与输入的一个确切范围相关联,而不仅仅是单个位置。可以使用throw解析器,就像使用error解析器一样,除了它在同一位置创建一个异常而不是错误之外。然后可以使用catch组合子将异常转换为错误,并将错误的范围起始点扩展回原始解析的开始。

以下示例显示了如何将上述alphabet_parse函数产生的错误与生成错误的源代码范围相关联。

/// Parses the first 5 letters of the alphabet in order
fn alphabet_parse() -> impl Parser<Span, &'static str> {
    "abcde"
        .on_none(
            alphabetic
                .multiple()
                .maybe()
                .and_then(|text| throw(text, "Not in alphabetical order"))
        )
        .catch()
}

当前版本:0.4.3

许可证:ISC

无运行时依赖