3 <img src="https://raw.github.com/pest-parser/pest/master/pest-logo.svg?sanitize=true" width="80%"/>
6 # pest. The Elegant Parser
8 [![Join the chat at https://gitter.im/pest-parser/pest](https://badges.gitter.im/dragostis/pest.svg)](https://gitter.im/pest-parser/pest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
9 [![Book](https://img.shields.io/badge/book-WIP-4d76ae.svg)](https://pest.rs/book)
10 [![Docs](https://docs.rs/pest/badge.svg)](https://docs.rs/pest)
12 [![pest Continuous Integration](https://github.com/pest-parser/pest/actions/workflows/ci.yml/badge.svg)](https://github.com/pest-parser/pest/actions/workflows/ci.yml)
13 [![codecov](https://codecov.io/gh/pest-parser/pest/branch/master/graph/badge.svg)](https://codecov.io/gh/pest-parser/pest)
14 <a href="https://blog.rust-lang.org/2021/11/01/Rust-1.60.0.html"><img alt="Rustc Version 1.60.0+" src="https://img.shields.io/badge/rustc-1.60.0%2B-lightgrey.svg"/></a>
16 [![Crates.io](https://img.shields.io/crates/d/pest.svg)](https://crates.io/crates/pest)
17 [![Crates.io](https://img.shields.io/crates/v/pest.svg)](https://crates.io/crates/pest)
19 pest is a general purpose parser written in Rust with a focus on accessibility,
20 correctness, and performance. It uses parsing expression grammars
21 (or [PEG]) as input, which are similar in spirit to regular expressions, but
22 which offer the enhanced expressivity needed to parse complex languages.
24 [PEG]: https://en.wikipedia.org/wiki/Parsing_expression_grammar
28 The recommended way to start parsing with pest is to read the official [book].
30 Other helpful resources:
32 * API reference on [docs.rs]
33 * play with grammars and share them on our [fiddle]
34 * find previous common questions answered or ask questions on [GitHub Discussions]
35 * leave feedback, ask questions, or greet us on [Gitter] or [Discord]
37 [book]: https://pest.rs/book
38 [docs.rs]: https://docs.rs/pest
39 [fiddle]: https://pest.rs/#editor
40 [Gitter]: https://gitter.im/pest-parser/pest
41 [Discord]: https://discord.gg/XEGACtWpT2
42 [GitHub Discussions]: https://github.com/pest-parser/pest/discussions
46 The following is an example of a grammar for a list of alphanumeric identifiers
47 where all identifiers don't start with a digit:
50 alpha = { 'a'..'z' | 'A'..'Z' }
53 ident = { !digit ~ (alpha | digit)+ }
55 ident_list = _{ ident ~ (" " ~ ident)* }
57 // ident_list rule is silent which means it produces no tokens
60 Grammars are saved in separate .pest files which are never mixed with procedural
61 code. This results in an always up-to-date formalization of a language that is
62 easy to read and maintain.
64 ## Meaningful error reporting
66 Based on the grammar definition, the parser also includes automatic error
67 reporting. For the example above, the input `"123"` will result in:
70 thread 'main' panicked at ' --> 1:1
75 = unexpected digit', src/main.rs:12
77 while `"ab *"` will result in:
79 thread 'main' panicked at ' --> 1:1
84 = expected ident', src/main.rs:12
87 These error messages can be obtained from their default `Display` implementation,
88 e.g. `panic!("{}", parser_result.unwrap_err())` or `println!("{}", e)`.
92 The grammar can be used to derive a `Parser` implementation automatically.
93 Parsing returns an iterator of nested token pairs:
98 extern crate pest_derive;
103 #[grammar = "ident.pest"]
107 let pairs = IdentParser::parse(Rule::ident_list, "a1 b2").unwrap_or_else(|e| panic!("{}", e));
109 // Because ident_list is silent, the iterator will contain idents
111 // A pair is a combination of the rule which matched and a span of input
112 println!("Rule: {:?}", pair.as_rule());
113 println!("Span: {:?}", pair.as_span());
114 println!("Text: {}", pair.as_str());
116 // A pair can be converted to an iterator of the tokens which make it up:
117 for inner_pair in pair.into_inner() {
118 match inner_pair.as_rule() {
119 Rule::alpha => println!("Letter: {}", inner_pair.as_str()),
120 Rule::digit => println!("Digit: {}", inner_pair.as_str()),
128 This produces the following output:
131 Span: Span { start: 0, end: 2 }
136 Span: Span { start: 3, end: 5 }
142 ### Defining multiple parsers in a single file
143 The current automatic `Parser` derivation will produce the `Rule` enum
144 which would have name conflicts if one tried to define multiple such structs
145 that automatically derive `Parser`. One possible way around it is to put each
146 parser struct in a separate namespace:
151 #[grammar = "a.pest"]
156 #[grammar = "b.pest"]
163 * Precedence climbing
166 * Runs on stable Rust
168 ## Projects using pest
170 You can find more projects and ecosystem tools in the [awesome-pest](https://github.com/pest-parser/awesome-pest) repo.
172 * [pest_meta](https://github.com/pest-parser/pest/blob/master/meta/src/grammar.pest) (bootstrapped)
173 * [AshPaper](https://github.com/shnewto/ashpaper)
174 * [brain](https://github.com/brain-lang/brain)
175 * [cicada](https://github.com/mitnk/cicada)
176 * [comrak](https://github.com/kivikakk/comrak)
177 * [elastic-rs](https://github.com/cch123/elastic-rs)
178 * [graphql-parser](https://github.com/Keats/graphql-parser)
179 * [handlebars-rust](https://github.com/sunng87/handlebars-rust)
180 * [hexdino](https://github.com/Luz/hexdino)
181 * [Huia](https://gitlab.com/jimsy/huia/)
182 * [insta](https://github.com/mitsuhiko/insta)
183 * [jql](https://github.com/yamafaktory/jql)
184 * [json5-rs](https://github.com/callum-oakley/json5-rs)
185 * [mt940](https://github.com/svenstaro/mt940-rs)
186 * [Myoxine](https://github.com/d3bate/myoxine)
187 * [py_literal](https://github.com/jturner314/py_literal)
188 * [rouler](https://github.com/jarcane/rouler)
189 * [RuSh](https://github.com/lwandrebeck/RuSh)
190 * [rs_pbrt](https://github.com/wahn/rs_pbrt)
191 * [stache](https://github.com/dgraham/stache)
192 * [tera](https://github.com/Keats/tera)
193 * [ui_gen](https://github.com/emoon/ui_gen)
194 * [ukhasnet-parser](https://github.com/adamgreig/ukhasnet-parser)
195 * [ZoKrates](https://github.com/ZoKrates/ZoKrates)
196 * [Vector](https://github.com/timberio/vector)
197 * [AutoCorrect](https://github.com/huacnlee/autocorrect)
198 * [yaml-peg](https://github.com/aofdev/yaml-peg)
199 * [qubit](https://github.com/abhimanyu003/qubit)
200 * [caith](https://github.com/Geobert/caith) (a dice roller crate)
201 * [Melody](https://github.com/yoav-lavi/melody)
202 * [json5-nodes](https://github.com/jlyonsmith/json5-nodes)
204 ## Minimum Supported Rust Version (MSRV)
206 This library should always compile with default features on **Rust 1.60.0**
207 or **Rust 1.61** with `const_prec_climber`.
211 The `pest` and `pest_derive` crates can be built without the Rust standard
212 library and target embedded environments. To do so, you need to disable
213 their default features. In your `Cargo.toml`, you can specify it as follows:
218 pest = { version = "2", default-features = false }
219 pest_derive = { version = "2", default-features = false }
222 If you want to build these crates in the pest repository's workspace, you can
223 pass the `--no-default-features` flag to `cargo` and specify these crates using
224 the `--package` (`-p`) flag. For example:
227 $ cargo build --target thumbv7em-none-eabihf --no-default-features -p pest
229 $ cargo build --target thumbv7em-none-eabihf --no-default-features -p pest_derive
234 A special round of applause goes to prof. Marius Minea for his guidance and all
235 pest contributors, some of which being none other than my friends.