]>
Commit | Line | Data |
---|---|---|
9fa01778 XL |
1 | <p align="center"> |
2 | <img src="https://raw.github.com/pest-parser/pest/master/pest-logo.svg?sanitize=true" width="80%"/> | |
3 | </p> | |
4 | ||
5 | # pest. The Elegant Parser | |
6 | ||
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 | ||
11 | [![Build Status](https://travis-ci.org/pest-parser/pest.svg?branch=master)](https://travis-ci.org/pest-parser/pest) | |
12 | [![codecov](https://codecov.io/gh/pest-parser/pest/branch/master/graph/badge.svg)](https://codecov.io/gh/pest-parser/pest) | |
f9f354fc | 13 | [![Fuzzit Status](https://app.fuzzit.dev/badge?org_id=pest-parser)](https://app.fuzzit.dev/orgs/pest-parser/dashboard) |
9fa01778 XL |
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) | |
16 | ||
17 | pest is a general purpose parser written in Rust with a focus on accessibility, | |
18 | correctness, and performance. It uses parsing expression grammars | |
19 | (or [PEG]) as input, which are similar in spirit to regular expressions, but | |
20 | which offer the enhanced expressivity needed to parse complex languages. | |
21 | ||
22 | [PEG]: https://en.wikipedia.org/wiki/Parsing_expression_grammar | |
23 | ||
24 | ## Getting started | |
25 | ||
26 | The recommended way to start parsing with pest is to read the official [book]. | |
27 | ||
28 | Other helpful resources: | |
29 | ||
30 | * API reference on [docs.rs] | |
31 | * play with grammars and share them on our [fiddle] | |
32 | * leave feedback, ask questions, or greet us on [Gitter] | |
33 | ||
34 | [book]: https://pest-parser.github.io/book | |
35 | [docs.rs]: https://docs.rs/pest | |
36 | [fiddle]: https://pest-parser.github.io/#editor | |
37 | [Gitter]: https://gitter.im/dragostis/pest | |
38 | ||
39 | ## Example | |
40 | ||
41 | The following is an example of a grammar for a list of alpha-numeric identifiers | |
42 | where the first identifier does not start with a digit: | |
43 | ||
44 | ```rust | |
45 | alpha = { 'a'..'z' | 'A'..'Z' } | |
46 | digit = { '0'..'9' } | |
47 | ||
48 | ident = { (alpha | digit)+ } | |
49 | ||
50 | ident_list = _{ !digit ~ ident ~ (" " ~ ident)+ } | |
51 | // ^ | |
52 | // ident_list rule is silent which means it produces no tokens | |
53 | ``` | |
54 | ||
55 | Grammars are saved in separate .pest files which are never mixed with procedural | |
56 | code. This results in an always up-to-date formalization of a language that is | |
57 | easy to read and maintain. | |
58 | ||
59 | ## Meaningful error reporting | |
60 | ||
61 | Based on the grammar definition, the parser also includes automatic error | |
62 | reporting. For the example above, the input `"123"` will result in: | |
63 | ||
64 | ``` | |
65 | thread 'main' panicked at ' --> 1:1 | |
66 | | | |
67 | 1 | 123 | |
68 | | ^--- | |
69 | | | |
70 | = unexpected digit', src/main.rs:12 | |
71 | ``` | |
72 | while `"ab *"` will result in: | |
73 | ``` | |
74 | thread 'main' panicked at ' --> 1:1 | |
75 | | | |
76 | 1 | ab * | |
77 | | ^--- | |
78 | | | |
79 | = expected ident', src/main.rs:12 | |
80 | ``` | |
81 | ||
82 | ## Pairs API | |
83 | ||
84 | The grammar can be used to derive a `Parser` implementation automatically. | |
85 | Parsing returns an iterator of nested token pairs: | |
86 | ||
87 | ```rust | |
88 | extern crate pest; | |
89 | #[macro_use] | |
90 | extern crate pest_derive; | |
91 | ||
92 | use pest::Parser; | |
93 | ||
94 | #[derive(Parser)] | |
95 | #[grammar = "ident.pest"] | |
96 | struct IdentParser; | |
97 | ||
98 | fn main() { | |
99 | let pairs = IdentParser::parse(Rule::ident_list, "a1 b2").unwrap_or_else(|e| panic!("{}", e)); | |
100 | ||
101 | // Because ident_list is silent, the iterator will contain idents | |
102 | for pair in pairs { | |
9fa01778 XL |
103 | // A pair is a combination of the rule which matched and a span of input |
104 | println!("Rule: {:?}", pair.as_rule()); | |
f9f354fc XL |
105 | println!("Span: {:?}", pair.as_span()); |
106 | println!("Text: {}", pair.as_str()); | |
9fa01778 XL |
107 | |
108 | // A pair can be converted to an iterator of the tokens which make it up: | |
109 | for inner_pair in pair.into_inner() { | |
9fa01778 | 110 | match inner_pair.as_rule() { |
f9f354fc XL |
111 | Rule::alpha => println!("Letter: {}", inner_pair.as_str()), |
112 | Rule::digit => println!("Digit: {}", inner_pair.as_str()), | |
9fa01778 XL |
113 | _ => unreachable!() |
114 | }; | |
115 | } | |
116 | } | |
117 | } | |
118 | ``` | |
119 | ||
120 | This produces the following output: | |
121 | ``` | |
122 | Rule: ident | |
123 | Span: Span { start: 0, end: 2 } | |
124 | Text: a1 | |
125 | Letter: a | |
126 | Digit: 1 | |
127 | Rule: ident | |
128 | Span: Span { start: 3, end: 5 } | |
129 | Text: b2 | |
130 | Letter: b | |
131 | Digit: 2 | |
132 | ``` | |
133 | ||
134 | ## Other features | |
135 | ||
136 | * Precedence climbing | |
137 | * Input handling | |
138 | * Custom errors | |
139 | * Runs on stable Rust | |
140 | ||
141 | ## Projects using pest | |
142 | ||
143 | * [pest_meta](https://github.com/pest-parser/pest/blob/master/meta/src/grammar.pest) (bootstrapped) | |
f9f354fc | 144 | * [AshPaper](https://github.com/shnewto/ashpaper) |
9fa01778 XL |
145 | * [brain](https://github.com/brain-lang/brain) |
146 | * [Chelone](https://github.com/Aaronepower/chelone) | |
147 | * [comrak](https://github.com/kivikakk/comrak) | |
f9f354fc | 148 | * [elastic-rs](https://github.com/cch123/elastic-rs) |
9fa01778 XL |
149 | * [graphql-parser](https://github.com/Keats/graphql-parser) |
150 | * [handlebars-rust](https://github.com/sunng87/handlebars-rust) | |
151 | * [hexdino](https://github.com/Luz/hexdino) | |
152 | * [Huia](https://gitlab.com/jimsy/huia/) | |
f9f354fc | 153 | * [jql](https://github.com/yamafaktory/jql) |
9fa01778 XL |
154 | * [json5-rs](https://github.com/callum-oakley/json5-rs) |
155 | * [mt940](https://github.com/svenstaro/mt940-rs) | |
156 | * [py_literal](https://github.com/jturner314/py_literal) | |
157 | * [rouler](https://github.com/jarcane/rouler) | |
158 | * [RuSh](https://github.com/lwandrebeck/RuSh) | |
159 | * [rs_pbrt](https://github.com/wahn/rs_pbrt) | |
160 | * [stache](https://github.com/dgraham/stache) | |
161 | * [tera](https://github.com/Keats/tera) | |
162 | * [ui_gen](https://github.com/emoon/ui_gen) | |
163 | * [ukhasnet-parser](https://github.com/adamgreig/ukhasnet-parser) | |
f9f354fc | 164 | * [ZoKrates](https://github.com/ZoKrates/ZoKrates) |
9fa01778 XL |
165 | |
166 | ## Special thanks | |
167 | ||
168 | A special round of applause goes to prof. Marius Minea for his guidance and all | |
169 | pest contributors, some of which being none other than my friends. |