73 个版本 (36 个稳定版)

2.7.11 2024年7月2日
2.7.9 2024年4月2日
2.7.8 2024年3月2日
2.7.5 2023年10月24日
0.3.2 2016年6月28日

#6 in 解析器工具

Download history 741510/week @ 2024-05-04 797978/week @ 2024-05-11 787482/week @ 2024-05-18 767970/week @ 2024-05-25 834525/week @ 2024-06-01 806878/week @ 2024-06-08 786237/week @ 2024-06-15 790345/week @ 2024-06-22 722853/week @ 2024-06-29 865276/week @ 2024-07-06 804534/week @ 2024-07-13 854625/week @ 2024-07-20 846549/week @ 2024-07-27 820257/week @ 2024-08-03 900898/week @ 2024-08-10 751556/week @ 2024-08-17

3,466,431 每月下载量
用于 4,887 个 crate (703 直接使用)

MIT/Apache

1.5MB
22K SLoC

pest. 优雅的解析器

Join the chat at https://gitter.im/pest-parser/pest Book Docs

pest Continuous Integration codecov Rustc Version 1.61.0+

Crates.io Crates.io

pest 是一个以可访问性、正确性和性能为重点的通用解析器,用 Rust 编写。它使用解析表达式文法(或 PEG)作为输入,与正则表达式类似,但提供了解析复杂语言所需的表达性。

入门指南

使用 pest 开始解析的推荐方法是阅读官方的 书籍

其他有用的资源

  • docs.rs 上的 API 参考文档
  • 在我们的 fiddle 上试用语法并分享它们
  • GitHub 讨论区 中找到已回答的常见问题或提出问题
  • GitterDiscord 上留下反馈、提问或打招呼

示例

以下是一个列表数字标识符语法的示例,其中所有标识符都不以数字开头

alpha = { 'a'..'z' | 'A'..'Z' }
digit = { '0'..'9' }

ident = { !digit ~ (alpha | digit)+ }

ident_list = _{ ident ~ (" " ~ ident)* }
          // ^
          // ident_list rule is silent which means it produces no tokens

语法保存在单独的 .pest 文件中,永远不会与过程代码混合。这导致了一种始终是最新的、易于阅读和维护的语言规范。

有意义的错误报告

基于语法定义,解析器还包括自动错误报告。对于上面的例子,输入 "123" 将导致

thread 'main' panicked at ' --> 1:1
  |
1 | 123
  | ^---
  |
  = unexpected digit', src/main.rs:12

"ab *" 将导致

thread 'main' panicked at ' --> 1:1
  |
1 | ab *
  |    ^---
  |
  = expected ident', src/main.rs:12

这些错误消息可以从它们的默认 Display 实现中获得,例如 panic!("{}", parser_result.unwrap_err())println!("{}", e)

API 对

可以使用语法自动推导出 Parser 实现。解析返回嵌套标记对的迭代器

use pest_derive::Parser;
use pest::Parser;

#[derive(Parser)]
#[grammar = "ident.pest"]
struct IdentParser;

fn main() {
    let pairs = IdentParser::parse(Rule::ident_list, "a1 b2").unwrap_or_else(|e| panic!("{}", e));

    // Because ident_list is silent, the iterator will contain idents
    for pair in pairs {
        // A pair is a combination of the rule which matched and a span of input
        println!("Rule:    {:?}", pair.as_rule());
        println!("Span:    {:?}", pair.as_span());
        println!("Text:    {}", pair.as_str());

        // A pair can be converted to an iterator of the tokens which make it up:
        for inner_pair in pair.into_inner() {
            match inner_pair.as_rule() {
                Rule::alpha => println!("Letter:  {}", inner_pair.as_str()),
                Rule::digit => println!("Digit:   {}", inner_pair.as_str()),
                _ => unreachable!()
            };
        }
    }
}

这会产生以下输出

Rule:    ident
Span:    Span { start: 0, end: 2 }
Text:    a1
Letter:  a
Digit:   1
Rule:    ident
Span:    Span { start: 3, end: 5 }
Text:    b2
Letter:  b
Digit:   2

在单个文件中定义多个解析器

当前的自动 Parser 推导将产生 Rule 枚举,如果在单个文件中定义多个此类结构体,将会出现名称冲突。一种可能的解决方案是将每个解析器结构体放在单独的命名空间中

mod a {
    #[derive(Parser)]
    #[grammar = "a.pest"]
    pub struct ParserA;
}
mod b {
    #[derive(Parser)]
    #[grammar = "b.pest"]
    pub struct ParserB;
}

其他功能

  • 优先级爬升
  • 输入处理
  • 自定义错误
  • 在稳定的 Rust 上运行

使用 pest 的项目

你可以在 awesome-pest 仓库中找到更多项目和生态系统工具。

最低支持的 Rust 版本(MSRV)

此库应始终在 Rust 1.61.0 上使用默认功能编译。

no_std 支持

pestpest_derive crate 可以在没有 Rust 标准库的情况下构建,并针对嵌入式环境。要做到这一点,您需要禁用它们的默认功能。在您的 Cargo.toml 中,您可以指定如下

[dependencies]
# ...
pest = { version = "2", default-features = false }
pest_derive = { version = "2", default-features = false }

如果您想在 pest 仓库的工作区中构建这些 crate,您可以将 --no-default-features 标志传递给 cargo 并使用 --package--p)标志指定这些 crate。例如

$ cargo build --target thumbv7em-none-eabihf --no-default-features -p pest
$ cargo bootstrap
$ cargo build --target thumbv7em-none-eabihf --no-default-features -p pest_derive

特别感谢

特别感谢马里乌斯·米内亚教授的指导以及所有 pest 贡献者,其中一些是我的朋友。

依赖项

~245–660KB
~16K SLoC