2 <img src="https://raw.github.com/pest-parser/pest/master/pest-logo.svg?sanitize=true" width="80%"/>
5 # pest. The Elegant Parser
7 [![Join the chat at https://gitter.im/dragostis/pest](https://badges.gitter.im/dragostis/pest.svg)](https://gitter.im/dragostis/pest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
8 [![Book](https://img.shields.io/badge/book-WIP-4d76ae.svg)](https://pest-parser.github.io/book)
9 [![Docs](https://docs.rs/pest/badge.svg)](https://docs.rs/pest)
10 (docs are currently [broken on docs.rs](https://github.com/onur/docs.rs/issues/23#issuecomment-359179441); build them locally with `cargo doc`)
12 [![Build Status](https://travis-ci.org/pest-parser/pest.svg?branch=master)](https://travis-ci.org/pest-parser/pest)
13 [![codecov](https://codecov.io/gh/pest-parser/pest/branch/master/graph/badge.svg)](https://codecov.io/gh/pest-parser/pest)
14 [![Crates.io](https://img.shields.io/crates/d/pest.svg)](https://crates.io/crates/pest)
15 [![Crates.io](https://img.shields.io/crates/v/pest.svg)](https://crates.io/crates/pest)
17 pest is a [PEG](https://en.wikipedia.org/wiki/Parsing_expression_grammar) parser with [simplicity][1] and [speed][2] in mind.
19 [1]: https://github.com/pest-parser/pest#elegant-grammar
20 [2]: https://github.com/pest-parser/pest#sheer-performance
24 Defining a grammar for a list of alpha-numeric identifiers where the first identifier does not start with a digit is as
28 alpha = { 'a'..'z' | 'A'..'Z' }
31 ident = { (alpha | digit)+ }
33 ident_list = _{ !digit ~ ident ~ (" " ~ ident)+ }
35 // ident_list rule is silent which means it produces no tokens
38 This is then saved in a `.pest` grammar file and is never mixed up with Rust code which results in an always up-to-date
39 formal definition of the grammar which is very easy to maintain.
43 The grammar can be used to derive a `Parser` implementation automatically. Parsing returns an iterator of nested token
49 extern crate pest_derive;
54 #[grammar = "ident.pest"]
58 let pairs = IdentParser::parse(Rule::ident_list, "a1 b2").unwrap_or_else(|e| panic!("{}", e));
60 // Because ident_list is silent, the iterator will contain idents
62 // A pair is a combination of the rule which matched and a span of input
63 println!("Rule: {:?}", pair.as_rule());
64 println!("Span: {:?}", pair.clone().into_span());
65 println!("Text: {}", pair.clone().into_span().as_str());
67 // A pair can be converted to an iterator of the tokens which make it up:
68 for inner_pair in pair.into_inner() {
69 match inner_pair.as_rule() {
70 Rule::alpha => println!("Letter: {}", inner_pair.into_span().as_str()),
71 Rule::digit => println!("Digit: {}", inner_pair.into_span().as_str()),
79 This produces the following output:
82 Span: Span { start: 0, end: 2 }
87 Span: Span { start: 3, end: 5 }
93 ## Meaningful error reporting
95 Parsing `"123"` instead of `"a1 b2"` in the code above will result in the following panic:
98 thread 'main' panicked at ' --> 1:1
103 = unexpected digit', src/main.rs:12
106 while parsing `"ab *"` will result in:
109 thread 'main' panicked at ' --> 1:4
114 = expected ident', src/main.rs:12
119 pest provides parsing performance in the same league as carefully written manual parsers.
120 The following JSON benchmark puts it somewhere in between one of the most optimized JSON parsers,
121 [ujson4c](https://github.com/esnme/ujson4c), and a static native-speed parser, [nom](https://github.com/Geal/nom).
123 The first entry of pest scores 36ms, while the second scores 96ms since it's mapping `Pair`s
124 to a custom JSON AST. While the first entry forms a perfectly usable tree, it does not process
125 the file to a fully-processed JSON object. The second one does, but since it has an extra
126 intermediate representation of the object, it repeats some work.
129 <img src="https://raw.github.com/pest-parser/pest/master/results.svg?sanitize=true"/>
132 The [benchmark](https://github.com/Geal/pestvsnom) uses
133 [a large 2MB JSON file](https://github.com/miloyip/nativejson-benchmark/blob/master/data/canada.json).
134 Tested on a 2.6GHz Intel® Core™ i5 running macOS.
138 * precedence climbing
141 * runs on stable Rust
144 pest requires [Cargo and Rust 1.23](https://www.rust-lang.org/en-US/downloads.html).
146 Add the following to `Cargo.toml`:
153 and in your Rust `lib.rs` or `main.rs`:
158 extern crate pest_derive;
161 ## Projects using pest
163 * [brain](https://github.com/brain-lang/brain)
164 * [comrak](https://github.com/kivikakk/comrak)
165 * [graphql-parser](https://github.com/Keats/graphql-parser)
166 * [handlebars-rust](https://github.com/sunng87/handlebars-rust)
167 * [pest](https://github.com/pest-parser/pest/blob/master/pest_derive/src/parser.rs) (bootstrapped)
168 * [Huia](https://gitlab.com/huia-lang/huia-parser)
169 * [rouler](https://github.com/jarcane/rouler)
170 * [RuSh](https://github.com/lwandrebeck/RuSh)
171 * [rs_pbrt](https://github.com/wahn/rs_pbrt)
172 * [stache](https://github.com/dgraham/stache)
173 * [tera](https://github.com/Keats/tera)
174 * [ui_gen](https://github.com/emoon/ui_gen)
175 * [ukhasnet-parser](https://github.com/adamgreig/ukhasnet-parser)
179 A special round of applause goes to prof. Marius Minea for his guidance and all pest contribuitors,
180 some of which being none other than my friends.