pest-parser/pest
{ "createdAt": "2016-04-24T09:50:43Z", "defaultBranch": "master", "description": "The Elegant Parser", "fullName": "pest-parser/pest", "homepage": "https://pest.rs", "language": "Rust", "name": "pest", "pushedAt": "2025-11-21T11:07:13Z", "stargazersCount": 5194, "topics": [ "parsing", "peg", "rust" ], "updatedAt": "2025-11-26T00:56:22Z", "url": "https://github.com/pest-parser/pest"}
pest. The Elegant Parser
Section titled “pest. The Elegant Parser”pest is a general purpose parser written in Rust with a focus on accessibility, correctness, and performance. It uses parsing expression grammars (or [PEG]) as input, which are similar in spirit to regular expressions, but which offer the enhanced expressivity needed to parse complex languages.
[PEG] !: https://en.wikipedia.org/wiki/Parsing_expression_grammar
Getting started
Section titled “Getting started”The recommended way to start parsing with pest is to read the official [book].
Other helpful resources:
- API reference on [docs.rs]
- play with grammars and share them on our [fiddle]
- find previous common questions answered or ask questions on [GitHub Discussions]
- leave feedback, ask questions, or greet us on [Gitter] or [Discord]
[book] !: https://pest.rs/book [docs.rs] !: https://docs.rs/pest [fiddle] !: https://pest.rs/#editor [Gitter] !: https://gitter.im/pest-parser/pest [Discord] !: https://discord.gg/XEGACtWpT2 [GitHub Discussions] !: https://github.com/pest-parser/pest/discussions
Example
Section titled “Example”The following is an example of a grammar for a list of alphanumeric identifiers where all identifiers don’t start with a digit:
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 tokensGrammars are saved in separate .pest files which are never mixed with procedural code. This results in an always up-to-date formalization of a language that is easy to read and maintain.
Meaningful error reporting
Section titled “Meaningful error reporting”Based on the grammar definition, the parser also includes automatic error
reporting. For the example above, the input "123" will result in:
thread 'main' panicked at ' --> 1:1 |1 | 123 | ^--- | = unexpected digit', src/main.rs:12while "ab *" will result in:
thread 'main' panicked at ' --> 1:1 |1 | ab * | ^--- | = expected ident', src/main.rs:12These error messages can be obtained from their default Display implementation,
e.g. panic!("{}", parser_result.unwrap_err()) or println!("{}", e).
Pairs API
Section titled “Pairs API”The grammar can be used to derive a Parser implementation automatically.
Parsing returns an iterator of nested token pairs:
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!() }; } }}This produces the following output:
Rule: identSpan: Span { start: 0, end: 2 }Text: a1Letter: aDigit: 1Rule: identSpan: Span { start: 3, end: 5 }Text: b2Letter: bDigit: 2Defining multiple parsers in a single file
Section titled “Defining multiple parsers in a single file”The current automatic Parser derivation will produce the Rule enum
which would have name conflicts if one tried to define multiple such structs
that automatically derive Parser. One possible way around it is to put each
parser struct in a separate namespace:
mod a { #[derive(Parser)] #[grammar = "a.pest"] pub struct ParserA;}mod b { #[derive(Parser)] #[grammar = "b.pest"] pub struct ParserB;}Other features
Section titled “Other features”- Precedence climbing
- Input handling
- Custom errors
- Runs on stable Rust
Projects using pest
Section titled “Projects using pest”You can find more projects and ecosystem tools in the awesome-pest repo.
- pest_meta (bootstrapped)
- AshPaper
- brain
- cicada
- comrak
- elastic-rs
- graphql-parser
- handlebars-rust
- hexdino
- Huia
- insta
- jql
- json5-rs
- mt940
- Myoxine
- py_literal
- rouler
- RuSh
- rs_pbrt
- stache
- tera
- ui_gen
- ukhasnet-parser
- ZoKrates
- Vector
- AutoCorrect
- yaml-peg
- qubit
- caith (a dice roller crate)
- Melody
- json5-nodes
- prisma
- ws2markdown (a WordStar to Markdown converter)
Minimum Supported Rust Version (MSRV)
Section titled “Minimum Supported Rust Version (MSRV)”This library should always compile with default features on Rust 1.83.0.
no_std support
Section titled “no_std support”The pest and pest_derive crates can be built without the Rust standard
library and target embedded environments. To do so, you need to disable
their default features. In your Cargo.toml, you can specify it as follows:
[dependencies]# ...pest = { version = "2", default-features = false }pest_derive = { version = "2", default-features = false }If you want to build these crates in the pest repository’s workspace, you can
pass the --no-default-features flag to cargo and specify these crates using
the --package (-p) flag. For example:
$ cargo build --target thumbv7em-none-eabihf --no-default-features -p pest$ cargo bootstrap$ cargo build --target thumbv7em-none-eabihf --no-default-features -p pest_deriveSpecial thanks
Section titled “Special thanks”A special round of applause goes to prof. Marius Minea for his guidance and all pest contributors, some of which being none other than my friends.