Snap for 10453563 from c8408ae39045367684a36cf6536046ac8ce6e7cd to mainline-art-release

Change-Id: Ice48ca065060fa1f98e2130cb679ded66ebf244f
diff --git a/Android.bp b/Android.bp
index dbc02e9..d8c1e56 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,8 +1,6 @@
 // This file is generated by cargo2android.py --config cargo2android.json.
 // Do not modify this file as changes will be overridden on upgrade.
 
-
-
 package {
     default_applicable_licenses: ["external_rust_crates_pest_meta_license"],
 }
@@ -41,14 +39,16 @@
 
 rust_library_host {
     name: "libpest_meta",
-    // has rustc warnings
     crate_name: "pest_meta",
     cargo_env_compat: true,
-    cargo_pkg_version: "2.1.3",
+    cargo_pkg_version: "2.5.5",
     srcs: ["src/lib.rs"],
-    edition: "2015",
+    edition: "2021",
     rustlibs: [
+        "libonce_cell",
         "libpest",
     ],
     compile_multilib: "first",
+    product_available: true,
+    vendor_available: true,
 }
diff --git a/Cargo.toml b/Cargo.toml
index 74f8377..f49a725 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,40 +3,46 @@
 # When uploading crates to the registry Cargo will automatically
 # "normalize" Cargo.toml files for maximal compatibility
 # with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies
+# to registry (e.g., crates.io) dependencies.
 #
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
 
 [package]
+edition = "2021"
+rust-version = "1.56"
 name = "pest_meta"
-version = "2.1.3"
+version = "2.5.5"
 authors = ["DragoČ™ Tiselice <dragostiselice@gmail.com>"]
 exclude = ["src/grammar.pest"]
-include = ["Cargo.toml", "src/**/*", "src/grammar.rs", "_README.md", "LICENSE-*"]
+include = [
+    "Cargo.toml",
+    "src/**/*",
+    "src/grammar.rs",
+    "_README.md",
+    "LICENSE-*",
+]
 description = "pest meta language parser and validator"
-homepage = "https://pest-parser.github.io/"
+homepage = "https://pest.rs/"
 documentation = "https://docs.rs/pest"
 readme = "_README.md"
-keywords = ["pest", "parser", "meta", "optimizer"]
+keywords = [
+    "pest",
+    "parser",
+    "meta",
+    "optimizer",
+]
 categories = ["parsing"]
 license = "MIT/Apache-2.0"
 repository = "https://github.com/pest-parser/pest"
-[dependencies.maplit]
-version = "1.0"
+
+[dependencies.once_cell]
+version = "1.8.0"
 
 [dependencies.pest]
-version = "2.1.0"
-[build-dependencies.sha-1]
-version = "0.8"
+version = "2.5.5"
+
+[build-dependencies.sha2]
+version = "0.10"
 default-features = false
-[badges.codecov]
-repository = "pest-parser/pest"
-
-[badges.maintenance]
-status = "actively-developed"
-
-[badges.travis-ci]
-repository = "pest-parser/pest"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 40c7570..accc303 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,9 +1,10 @@
 [package]
 name = "pest_meta"
 description = "pest meta language parser and validator"
-version = "2.1.3"
+version = "2.5.5"
+edition = "2021"
 authors = ["DragoČ™ Tiselice <dragostiselice@gmail.com>"]
-homepage = "https://pest-parser.github.io/"
+homepage = "https://pest.rs/"
 repository = "https://github.com/pest-parser/pest"
 documentation = "https://docs.rs/pest"
 keywords = ["pest", "parser", "meta", "optimizer"]
@@ -12,15 +13,11 @@
 readme = "_README.md"
 exclude = ["src/grammar.pest"]
 include = ["Cargo.toml", "src/**/*", "src/grammar.rs", "_README.md", "LICENSE-*"]
+rust-version = "1.56"
 
 [dependencies]
-maplit = "1.0"
-pest = { path = "../pest", version = "2.1.0" }
-
-[badges]
-codecov = { repository = "pest-parser/pest" }
-maintenance = { status = "actively-developed" }
-travis-ci = { repository = "pest-parser/pest" }
+pest = { path = "../pest", version = "2.5.5" }
+once_cell = "1.8.0"
 
 [build-dependencies]
-sha-1 = { version = "0.8", default-features = false }
+sha2 = { version = "0.10", default-features = false }
diff --git a/METADATA b/METADATA
index 7a2a522..325bf32 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/pest_meta
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
 name: "pest_meta"
 description: "pest meta language parser and validator"
 third_party {
@@ -7,14 +11,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/pest_meta/pest_meta-2.1.3.crate"
+    value: "https://static.crates.io/crates/pest_meta/pest_meta-2.5.5.crate"
   }
-  version: "2.1.3"
-  # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
+  version: "2.5.5"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2022
-    month: 1
-    day: 27
+    year: 2023
+    month: 2
+    day: 16
   }
 }
diff --git a/_README.md b/_README.md
index 8e55f29..da30ab7 100644
--- a/_README.md
+++ b/_README.md
@@ -1,16 +1,18 @@
+
 <p align="center">
   <img src="https://raw.github.com/pest-parser/pest/master/pest-logo.svg?sanitize=true" width="80%"/>
 </p>
 
 # pest. The Elegant Parser
 
-[![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)
-[![Book](https://img.shields.io/badge/book-WIP-4d76ae.svg)](https://pest-parser.github.io/book)
+[![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)
+[![Book](https://img.shields.io/badge/book-WIP-4d76ae.svg)](https://pest.rs/book)
 [![Docs](https://docs.rs/pest/badge.svg)](https://docs.rs/pest)
 
-[![Build Status](https://travis-ci.org/pest-parser/pest.svg?branch=master)](https://travis-ci.org/pest-parser/pest)
+[![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)
 [![codecov](https://codecov.io/gh/pest-parser/pest/branch/master/graph/badge.svg)](https://codecov.io/gh/pest-parser/pest)
-[![Fuzzit Status](https://app.fuzzit.dev/badge?org_id=pest-parser)](https://app.fuzzit.dev/orgs/pest-parser/dashboard)
+<a href="https://blog.rust-lang.org/2021/11/01/Rust-1.56.1.html"><img alt="Rustc Version 1.56.1+" src="https://img.shields.io/badge/rustc-1.56.1%2B-lightgrey.svg"/></a>
+
 [![Crates.io](https://img.shields.io/crates/d/pest.svg)](https://crates.io/crates/pest)
 [![Crates.io](https://img.shields.io/crates/v/pest.svg)](https://crates.io/crates/pest)
 
@@ -29,25 +31,28 @@
 
 * API reference on [docs.rs]
 * play with grammars and share them on our [fiddle]
-* leave feedback, ask questions, or greet us on [Gitter]
+* 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-parser.github.io/book
+[book]: https://pest.rs/book
 [docs.rs]: https://docs.rs/pest
-[fiddle]: https://pest-parser.github.io/#editor
-[Gitter]: https://gitter.im/dragostis/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
 
-The following is an example of a grammar for a list of alpha-numeric identifiers
-where the first identifier does not start with a digit:
+The following is an example of a grammar for a list of alphanumeric identifiers
+where all identifiers don't start with a digit:
 
 ```rust
 alpha = { 'a'..'z' | 'A'..'Z' }
 digit = { '0'..'9' }
 
-ident = { (alpha | digit)+ }
+ident = { !digit ~ (alpha | digit)+ }
 
-ident_list = _{ !digit ~ ident ~ (" " ~ ident)+ }
+ident_list = _{ ident ~ (" " ~ ident)* }
           // ^
           // ident_list rule is silent which means it produces no tokens
 ```
@@ -79,6 +84,9 @@
   = expected ident', src/main.rs:12
 ```
 
+These error messages can be obtained from their default `Display` implementation,
+e.g. `panic!("{}", parser_result.unwrap_err())` or `println!("{}", e)`.
+
 ## Pairs API
 
 The grammar can be used to derive a `Parser` implementation automatically.
@@ -131,6 +139,25 @@
 Digit:   2
 ```
 
+### 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:
+
+```rust
+mod a {
+    #[derive(Parser)]
+    #[grammar = "a.pest"]
+    pub struct ParserA;
+}
+mod b {
+    #[derive(Parser)]
+    #[grammar = "b.pest"]
+    pub struct ParserB;
+}
+```
+
 ## Other features
 
 * Precedence climbing
@@ -143,16 +170,18 @@
 * [pest_meta](https://github.com/pest-parser/pest/blob/master/meta/src/grammar.pest) (bootstrapped)
 * [AshPaper](https://github.com/shnewto/ashpaper)
 * [brain](https://github.com/brain-lang/brain)
-* [Chelone](https://github.com/Aaronepower/chelone)
+* [cicada](https://github.com/mitnk/cicada)
 * [comrak](https://github.com/kivikakk/comrak)
 * [elastic-rs](https://github.com/cch123/elastic-rs)
 * [graphql-parser](https://github.com/Keats/graphql-parser)
 * [handlebars-rust](https://github.com/sunng87/handlebars-rust)
 * [hexdino](https://github.com/Luz/hexdino)
 * [Huia](https://gitlab.com/jimsy/huia/)
+* [insta](https://github.com/mitsuhiko/insta)
 * [jql](https://github.com/yamafaktory/jql)
 * [json5-rs](https://github.com/callum-oakley/json5-rs)
 * [mt940](https://github.com/svenstaro/mt940-rs)
+* [Myoxine](https://github.com/d3bate/myoxine)
 * [py_literal](https://github.com/jturner314/py_literal)
 * [rouler](https://github.com/jarcane/rouler)
 * [RuSh](https://github.com/lwandrebeck/RuSh)
@@ -162,6 +191,17 @@
 * [ui_gen](https://github.com/emoon/ui_gen)
 * [ukhasnet-parser](https://github.com/adamgreig/ukhasnet-parser)
 * [ZoKrates](https://github.com/ZoKrates/ZoKrates)
+* [Vector](https://github.com/timberio/vector)
+* [AutoCorrect](https://github.com/huacnlee/autocorrect)
+* [yaml-peg](https://github.com/aofdev/yaml-peg)
+* [qubit](https://github.com/abhimanyu003/qubit)
+* [caith](https://github.com/Geobert/caith) (a dice roller crate)
+* [Melody](https://github.com/yoav-lavi/melody)
+
+## Minimum Supported Rust Version (MSRV)
+
+This library should always compile with default features on **Rust 1.56.1** 
+or **Rust 1.61** with `const_prec_climber`.
 
 ## Special thanks
 
diff --git a/cargo2android.json b/cargo2android.json
index 833343e..76b07a0 100644
--- a/cargo2android.json
+++ b/cargo2android.json
@@ -1,5 +1,4 @@
 {
-  "patch": "patches/Android.bp.diff",
   "run": true,
   "host-first-multilib": true
 }
diff --git a/patches/0001-Remove-unused-extern-crate-maplit.patch b/patches/0001-Remove-unused-extern-crate-maplit.patch
deleted file mode 100644
index abcf752..0000000
--- a/patches/0001-Remove-unused-extern-crate-maplit.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From cca209337abe86ad35dc705dd293b1ab264f5e46 Mon Sep 17 00:00:00 2001
-From: Henri Chataing <henrichataing@google.com>
-Date: Fri, 18 Feb 2022 11:49:59 +0100
-Subject: [PATCH] Remove unused extern crate maplit
-
-Change-Id: I4fdf3fb28528ba228d3c4869c7a4a17921f8293d
----
- src/lib.rs | 1 -
- 1 file changed, 1 deletion(-)
-
-diff --git a/src/lib.rs b/src/lib.rs
-index 1f9c5bc..28f33b0 100644
---- a/src/lib.rs
-+++ b/src/lib.rs
-@@ -9,7 +9,6 @@
- 
- #![allow(clippy::range_plus_one)]
- 
--extern crate maplit;
- #[cfg(test)]
- #[macro_use]
- extern crate pest;
--- 
-2.35.1.265.g69c8d7142f-goog
-
diff --git a/patches/Android.bp.diff b/patches/Android.bp.diff
deleted file mode 100644
index 14c6cb9..0000000
--- a/patches/Android.bp.diff
+++ /dev/null
@@ -1,12 +0,0 @@
-diff --git a/Android.bp b/Android.bp
-index 62e8f1f..691d848 100644
---- a/Android.bp
-+++ b/Android.bp
-@@ -12,7 +12,6 @@ rust_library_host {
-     srcs: ["src/lib.rs"],
-     edition: "2015",
-     rustlibs: [
--        "libmaplit",
-         "libpest",
-     ],
-     compile_multilib: "first",
diff --git a/src/ast.rs b/src/ast.rs
index b80c596..77c48e8 100644
--- a/src/ast.rs
+++ b/src/ast.rs
@@ -7,22 +7,55 @@
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
 
+//! Types for the pest's abstract syntax tree.
+
+/// A grammar rule
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Rule {
+    /// The name of the rule
     pub name: String,
+    /// The rule's type (silent, atomic, ...)
     pub ty: RuleType,
+    /// The rule's expression
     pub expr: Expr,
 }
 
+/// All possible rule types
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 pub enum RuleType {
+    /// The normal rule type
     Normal,
+    /// Silent rules are just like normal rules
+    /// — when run, they function the same way —
+    /// except they do not produce pairs or tokens.
+    /// If a rule is silent, it will never appear in a parse result.
+    /// (their syntax is `_{ ... }`)
     Silent,
+    /// atomic rule prevent implicit whitespace: inside an atomic rule,
+    /// the tilde ~ means "immediately followed by",
+    /// and repetition operators (asterisk * and plus sign +)
+    /// have no implicit separation. In addition, all other rules
+    /// called from an atomic rule are also treated as atomic.
+    /// In an atomic rule, interior matching rules are silent.
+    /// (their syntax is `@{ ... }`)
     Atomic,
+    /// Compound atomic rules are similar to atomic rules,
+    /// but they produce inner tokens as normal.
+    /// (their syntax is `${ ... }`)
     CompoundAtomic,
+    /// Non-atomic rules cancel the effect of atomic rules.
+    /// (their syntax is `!{ ... }`)
     NonAtomic,
 }
 
+/// All possible rule expressions
+///
+/// # Warning: Semantic Versioning
+/// There may be non-breaking changes to the meta-grammar
+/// between minor versions. Those non-breaking changes, however,
+/// may translate into semver-breaking changes due to the additional variants
+/// propaged from the `Rule` enum. This is a known issue and will be fixed in the
+/// future (e.g. by increasing MSRV and non_exhaustive annotations).
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum Expr {
     /// Matches an exact string, e.g. `"a"`
@@ -64,10 +97,12 @@
 }
 
 impl Expr {
+    /// Returns the iterator that steps the expression from top to bottom.
     pub fn iter_top_down(&self) -> ExprTopDownIterator {
         ExprTopDownIterator::new(self)
     }
 
+    /// Applies `f` to the expression and all its children (top to bottom).
     pub fn map_top_down<F>(self, mut f: F) -> Expr
     where
         F: FnMut(Expr) -> Expr,
@@ -137,6 +172,7 @@
         map_internal(self, &mut f)
     }
 
+    /// Applies `f` to the expression and all its children (bottom up).
     pub fn map_bottom_up<F>(self, mut f: F) -> Expr
     where
         F: FnMut(Expr) -> Expr,
@@ -207,6 +243,7 @@
     }
 }
 
+/// The top down iterator for an expression.
 pub struct ExprTopDownIterator {
     current: Option<Expr>,
     next: Option<Expr>,
@@ -214,6 +251,7 @@
 }
 
 impl ExprTopDownIterator {
+    /// Constructs a top-down iterator from the expression.
     pub fn new(expr: &Expr) -> Self {
         let mut iter = ExprTopDownIterator {
             current: None,
@@ -280,7 +318,7 @@
             Box::new(Expr::Str(String::from("a"))),
             Box::new(Expr::Str(String::from("b"))),
         );
-        let mut top_down = expr.clone().iter_top_down();
+        let mut top_down = expr.iter_top_down();
         assert_eq!(top_down.next(), Some(expr));
         assert_eq!(top_down.next(), Some(Expr::Str(String::from("a"))));
         assert_eq!(top_down.next(), Some(Expr::Str(String::from("b"))));
diff --git a/src/grammar.pest b/src/grammar.pest
index 0d03ba8..405ab39 100644
--- a/src/grammar.pest
+++ b/src/grammar.pest
@@ -6,22 +6,41 @@
 // license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
+//! Pest meta-grammar
+//!
+//! # Warning: Semantic Versioning
+//! There may be non-breaking changes to the meta-grammar
+//! between minor versions. Those non-breaking changes, however,
+//! may translate into semver-breaking changes due to the additional variants
+//! added to the `Rule` enum. This is a known issue and will be fixed in the
+//! future (e.g. by increasing MSRV and non_exhaustive annotations).
 
-grammar_rules = _{ SOI ~ grammar_rule+ ~ EOI }
+/// The top-level rule of a grammar.
+grammar_rules = _{ SOI ~ grammar_doc* ~ (grammar_rule)+ ~ EOI }
 
+/// A rule of a grammar.
 grammar_rule = {
     identifier ~ assignment_operator ~ modifier? ~
-    opening_brace ~ expression ~ closing_brace
+    opening_brace ~ expression ~ closing_brace |
+    line_doc
 }
 
+/// Assignment operator.
 assignment_operator = { "=" }
+/// Opening brace for a rule.
 opening_brace       = { "{" }
+/// Closing brace for a rule.
 closing_brace       = { "}" }
+/// Opening parenthesis for a branch, PUSH, etc.
 opening_paren       = { "(" }
+/// Closing parenthesis for a branch, PUSH, etc.
 closing_paren       = { ")" }
+/// Opening bracket for PEEK (slice inside).
 opening_brack       = { "[" }
+/// Closing bracket for PEEK (slice inside).
 closing_brack       = { "]" }
 
+/// A rule modifier.
 modifier = _{
     silent_modifier |
     atomic_modifier |
@@ -29,18 +48,29 @@
     non_atomic_modifier
 }
 
+/// Silent rule prefix.
 silent_modifier          = { "_" }
+/// Atomic rule prefix.
 atomic_modifier          = { "@" }
+/// Compound atomic rule prefix.
 compound_atomic_modifier = { "$" }
+/// Non-atomic rule prefix.
 non_atomic_modifier      = { "!" }
 
-expression =  { term ~ (infix_operator ~ term)* }
+/// A rule expression.
+expression =  { choice_operator? ~ term ~ (infix_operator ~ term)* }
+/// A rule term.
 term       =  { prefix_operator* ~ node ~ postfix_operator* }
+/// A rule node (inside terms).
 node       = _{ opening_paren ~ expression ~ closing_paren | terminal }
+/// A terminal expression.
 terminal   = _{ _push | peek_slice | identifier | string | insensitive_string | range }
 
+/// Possible predicates for a rule.
 prefix_operator  = _{ positive_predicate_operator | negative_predicate_operator }
+/// Branches or sequences.
 infix_operator   = _{ sequence_operator | choice_operator }
+/// Possible modifiers for a rule.
 postfix_operator = _{
     optional_operator |
     repeat_operator |
@@ -51,48 +81,96 @@
     repeat_min_max
 }
 
+/// A positive predicate.
 positive_predicate_operator = { "&" }
+/// A negative predicate.
 negative_predicate_operator = { "!" }
+/// A sequence operator.
 sequence_operator           = { "~" }
+/// A choice operator.
 choice_operator             = { "|" }
+/// An optional operator.
 optional_operator           = { "?" }
+/// A repeat operator.
 repeat_operator             = { "*" }
+/// A repeat at least once operator.
 repeat_once_operator        = { "+" }
 
+/// A repeat exact times.
 repeat_exact   = { opening_brace ~ number ~ closing_brace }
+/// A repeat at least times.
 repeat_min     = { opening_brace ~ number ~ comma ~ closing_brace }
+/// A repeat at most times.
 repeat_max     = { opening_brace ~ comma ~ number ~ closing_brace }
+/// A repeat in a range.
 repeat_min_max = { opening_brace ~ number ~ comma ~ number ~ closing_brace }
 
+/// A number.
 number = @{ '0'..'9'+ }
+/// An integer number (positive or negative).
 integer = @{ number | "-" ~ "0"* ~ '1'..'9' ~ number? }
 
+/// A comma terminal.
 comma = { "," }
 
+/// A PUSH expression.
 _push = { "PUSH" ~ opening_paren ~ expression ~ closing_paren }
+/// A PEEK expression.
 peek_slice = { "PEEK" ~ opening_brack ~ integer? ~ range_operator ~ integer? ~ closing_brack }
 
+/// An identifier.
 identifier = @{ !"PUSH" ~ ("_" | alpha) ~ ("_" | alpha_num)* }
+/// An alpha character.
 alpha      = _{ 'a'..'z' | 'A'..'Z' }
+/// An alphanumeric character.
 alpha_num  = _{ alpha | '0'..'9' }
 
+/// A string.
 string             = ${ quote ~ inner_str ~ quote }
+/// An insensitive string.
 insensitive_string =  { "^" ~ string }
+/// A character range.
 range              =  { character ~ range_operator ~ character }
+/// A single quoted character
 character          = ${ single_quote ~ inner_chr ~ single_quote }
 
+/// A quoted string.
 inner_str = @{ (!("\"" | "\\") ~ ANY)* ~ (escape ~ inner_str)? }
+/// An escaped or any character.
 inner_chr = @{ escape | ANY }
+/// An escape sequence.
 escape    = @{ "\\" ~ ("\"" | "\\" | "r" | "n" | "t" | "0" | "'" | code | unicode) }
+/// A hexadecimal code.
 code      = @{ "x" ~ hex_digit{2} }
+/// A unicode code.
 unicode   = @{ "u" ~ opening_brace ~ hex_digit{2, 6} ~ closing_brace }
+/// A hexadecimal digit.
 hex_digit = @{ '0'..'9' | 'a'..'f' | 'A'..'F' }
 
+/// A double quote.
 quote          = { "\"" }
+/// A single quote.
 single_quote   = { "'" }
+/// A range operator.
 range_operator = { ".." }
 
-newline    = _{ "\n" | "\r\n" }
-WHITESPACE = _{ " " | "\t" | newline }
-block_comment = _{ "/*" ~ (block_comment | !"*/" ~ ANY)* ~ "*/" }
-COMMENT    = _{ block_comment | ("//" ~ (!newline ~ ANY)*) }
+/// A newline character.
+newline        = _{ "\n" | "\r\n" }
+/// A whitespace character.
+WHITESPACE     = _{ " " | "\t" | newline }
+/// A single line comment.
+line_comment   = _{ ("//" ~ !("/" | "!") ~ (!newline ~ ANY)*) }
+/// A multi-line comment.
+block_comment  = _{ "/*" ~ (block_comment | !"*/" ~ ANY)* ~ "*/" }
+/// A grammar comment.
+COMMENT        = _{ block_comment | line_comment }
+
+// ref: https://doc.rust-lang.org/reference/comments.html
+/// A space character.
+space       = _{ " " | "\t" }
+/// A top-level comment.
+grammar_doc = ${ "//!" ~ space? ~ inner_doc }
+/// A rule comment.
+line_doc    = ${ "///" ~ space? ~ !"/" ~ inner_doc }
+/// A comment content.
+inner_doc   = @{ (!newline ~ ANY)* }
diff --git a/src/grammar.rs b/src/grammar.rs
index bf94a1e..b612fb1 100644
--- a/src/grammar.rs
+++ b/src/grammar.rs
@@ -1,2 +1,2 @@
 pub struct PestParser;
-# [ allow ( dead_code , non_camel_case_types ) ] # [ derive ( Clone , Copy , Debug , Eq , Hash , Ord , PartialEq , PartialOrd ) ] pub enum Rule { EOI , grammar_rules , grammar_rule , assignment_operator , opening_brace , closing_brace , opening_paren , closing_paren , opening_brack , closing_brack , modifier , silent_modifier , atomic_modifier , compound_atomic_modifier , non_atomic_modifier , expression , term , node , terminal , prefix_operator , infix_operator , postfix_operator , positive_predicate_operator , negative_predicate_operator , sequence_operator , choice_operator , optional_operator , repeat_operator , repeat_once_operator , repeat_exact , repeat_min , repeat_max , repeat_min_max , number , integer , comma , _push , peek_slice , identifier , alpha , alpha_num , string , insensitive_string , range , character , inner_str , inner_chr , escape , code , unicode , hex_digit , quote , single_quote , range_operator , newline , WHITESPACE , block_comment , COMMENT } # [ allow ( clippy :: all ) ] impl :: pest :: Parser < Rule > for PestParser { fn parse < 'i > ( rule : Rule , input : & 'i str ) -> :: std :: result :: Result < :: pest :: iterators :: Pairs < 'i , Rule > , :: pest :: error :: Error < Rule > > { mod rules { pub mod hidden { use super :: super :: Rule ; # [ inline ] # [ allow ( dead_code , non_snake_case , unused_variables ) ] pub fn skip ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { if state . atomicity ( ) == :: pest :: Atomicity :: NonAtomic { state . sequence ( | state | { state . repeat ( | state | super :: visible :: WHITESPACE ( state ) ) . and_then ( | state | { state . repeat ( | state | { state . sequence ( | state | { super :: visible :: COMMENT ( state ) . and_then ( | state | { state . repeat ( | state | super :: visible :: WHITESPACE ( state ) ) } ) } ) } ) } ) } ) } else { Ok ( state ) } } } pub mod visible { use super :: super :: Rule ; # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn grammar_rules ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . sequence ( | state | { self :: SOI ( state ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { state . sequence ( | state | { self :: grammar_rule ( state ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { state . sequence ( | state | { state . optional ( | state | { self :: grammar_rule ( state ) . and_then ( | state | { state . repeat ( | state | { state . sequence ( | state | { super :: hidden :: skip ( state ) . and_then ( | state | { self :: grammar_rule ( state ) } ) } ) } ) } ) } ) } ) } ) } ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: EOI ( state ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn grammar_rule ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: grammar_rule , | state | { state . sequence ( | state | { self :: identifier ( state ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: assignment_operator ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { state . optional ( | state | { self :: modifier ( state ) } ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: opening_brace ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: expression ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: closing_brace ( state ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn assignment_operator ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: assignment_operator , | state | { state . match_string ( "=" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn opening_brace ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: opening_brace , | state | { state . match_string ( "{" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn closing_brace ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: closing_brace , | state | { state . match_string ( "}" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn opening_paren ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: opening_paren , | state | { state . match_string ( "(" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn closing_paren ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: closing_paren , | state | { state . match_string ( ")" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn opening_brack ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: opening_brack , | state | { state . match_string ( "[" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn closing_brack ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: closing_brack , | state | { state . match_string ( "]" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn modifier ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { self :: silent_modifier ( state ) . or_else ( | state | { self :: atomic_modifier ( state ) } ) . or_else ( | state | { self :: compound_atomic_modifier ( state ) } ) . or_else ( | state | { self :: non_atomic_modifier ( state ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn silent_modifier ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: silent_modifier , | state | { state . match_string ( "_" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn atomic_modifier ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: atomic_modifier , | state | { state . match_string ( "@" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn compound_atomic_modifier ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: compound_atomic_modifier , | state | { state . match_string ( "$" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn non_atomic_modifier ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: non_atomic_modifier , | state | { state . match_string ( "!" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn expression ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: expression , | state | { state . sequence ( | state | { self :: term ( state ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { state . sequence ( | state | { state . optional ( | state | { state . sequence ( | state | { self :: infix_operator ( state ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: term ( state ) } ) } ) . and_then ( | state | { state . repeat ( | state | { state . sequence ( | state | { super :: hidden :: skip ( state ) . and_then ( | state | { state . sequence ( | state | { self :: infix_operator ( state ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: term ( state ) } ) } ) } ) } ) } ) } ) } ) } ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn term ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: term , | state | { state . sequence ( | state | { state . sequence ( | state | { state . optional ( | state | { self :: prefix_operator ( state ) . and_then ( | state | { state . repeat ( | state | { state . sequence ( | state | { super :: hidden :: skip ( state ) . and_then ( | state | { self :: prefix_operator ( state ) } ) } ) } ) } ) } ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: node ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { state . sequence ( | state | { state . optional ( | state | { self :: postfix_operator ( state ) . and_then ( | state | { state . repeat ( | state | { state . sequence ( | state | { super :: hidden :: skip ( state ) . and_then ( | state | { self :: postfix_operator ( state ) } ) } ) } ) } ) } ) } ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn node ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . sequence ( | state | { self :: opening_paren ( state ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: expression ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: closing_paren ( state ) } ) } ) . or_else ( | state | { self :: terminal ( state ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn terminal ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { self :: _push ( state ) . or_else ( | state | { self :: peek_slice ( state ) } ) . or_else ( | state | { self :: identifier ( state ) } ) . or_else ( | state | { self :: string ( state ) } ) . or_else ( | state | { self :: insensitive_string ( state ) } ) . or_else ( | state | { self :: range ( state ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn prefix_operator ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { self :: positive_predicate_operator ( state ) . or_else ( | state | { self :: negative_predicate_operator ( state ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn infix_operator ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { self :: sequence_operator ( state ) . or_else ( | state | { self :: choice_operator ( state ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn postfix_operator ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { self :: optional_operator ( state ) . or_else ( | state | { self :: repeat_operator ( state ) } ) . or_else ( | state | { self :: repeat_once_operator ( state ) } ) . or_else ( | state | { self :: repeat_exact ( state ) } ) . or_else ( | state | { self :: repeat_min ( state ) } ) . or_else ( | state | { self :: repeat_max ( state ) } ) . or_else ( | state | { self :: repeat_min_max ( state ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn positive_predicate_operator ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: positive_predicate_operator , | state | { state . match_string ( "&" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn negative_predicate_operator ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: negative_predicate_operator , | state | { state . match_string ( "!" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn sequence_operator ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: sequence_operator , | state | { state . match_string ( "~" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn choice_operator ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: choice_operator , | state | { state . match_string ( "|" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn optional_operator ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: optional_operator , | state | { state . match_string ( "?" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn repeat_operator ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: repeat_operator , | state | { state . match_string ( "*" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn repeat_once_operator ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: repeat_once_operator , | state | { state . match_string ( "+" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn repeat_exact ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: repeat_exact , | state | { state . sequence ( | state | { self :: opening_brace ( state ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: number ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: closing_brace ( state ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn repeat_min ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: repeat_min , | state | { state . sequence ( | state | { self :: opening_brace ( state ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: number ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: comma ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: closing_brace ( state ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn repeat_max ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: repeat_max , | state | { state . sequence ( | state | { self :: opening_brace ( state ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: comma ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: number ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: closing_brace ( state ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn repeat_min_max ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: repeat_min_max , | state | { state . sequence ( | state | { self :: opening_brace ( state ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: number ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: comma ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: number ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: closing_brace ( state ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn number ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: number , | state | { state . atomic ( :: pest :: Atomicity :: Atomic , | state | { state . sequence ( | state | { state . match_range ( '0' .. '9' ) . and_then ( | state | { state . repeat ( | state | { state . match_range ( '0' .. '9' ) } ) } ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn integer ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: integer , | state | { state . atomic ( :: pest :: Atomicity :: Atomic , | state | { self :: number ( state ) . or_else ( | state | { state . sequence ( | state | { state . match_string ( "-" ) . and_then ( | state | { state . repeat ( | state | { state . match_string ( "0" ) } ) } ) . and_then ( | state | { state . match_range ( '1' .. '9' ) } ) . and_then ( | state | { state . optional ( | state | { self :: number ( state ) } ) } ) } ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn comma ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: comma , | state | { state . match_string ( "," ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn _push ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: _push , | state | { state . sequence ( | state | { state . match_string ( "PUSH" ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: opening_paren ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: expression ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: closing_paren ( state ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn peek_slice ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: peek_slice , | state | { state . sequence ( | state | { state . match_string ( "PEEK" ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: opening_brack ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { state . optional ( | state | { self :: integer ( state ) } ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: range_operator ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { state . optional ( | state | { self :: integer ( state ) } ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: closing_brack ( state ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn identifier ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: identifier , | state | { state . atomic ( :: pest :: Atomicity :: Atomic , | state | { state . sequence ( | state | { state . lookahead ( false , | state | { state . match_string ( "PUSH" ) } ) . and_then ( | state | { state . match_string ( "_" ) . or_else ( | state | { self :: alpha ( state ) } ) } ) . and_then ( | state | { state . repeat ( | state | { state . match_string ( "_" ) . or_else ( | state | { self :: alpha_num ( state ) } ) } ) } ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn alpha ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . match_range ( 'a' .. 'z' ) . or_else ( | state | { state . match_range ( 'A' .. 'Z' ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn alpha_num ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { self :: alpha ( state ) . or_else ( | state | { state . match_range ( '0' .. '9' ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn string ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . atomic ( :: pest :: Atomicity :: CompoundAtomic , | state | { state . rule ( Rule :: string , | state | { state . sequence ( | state | { self :: quote ( state ) . and_then ( | state | { self :: inner_str ( state ) } ) . and_then ( | state | { self :: quote ( state ) } ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn insensitive_string ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: insensitive_string , | state | { state . sequence ( | state | { state . match_string ( "^" ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: string ( state ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn range ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: range , | state | { state . sequence ( | state | { self :: character ( state ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: range_operator ( state ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: character ( state ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn character ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . atomic ( :: pest :: Atomicity :: CompoundAtomic , | state | { state . rule ( Rule :: character , | state | { state . sequence ( | state | { self :: single_quote ( state ) . and_then ( | state | { self :: inner_chr ( state ) } ) . and_then ( | state | { self :: single_quote ( state ) } ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn inner_str ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: inner_str , | state | { state . atomic ( :: pest :: Atomicity :: Atomic , | state | { state . sequence ( | state | { let strings = [ "\"" , "\\" ] ; state . skip_until ( & strings ) . and_then ( | state | { state . optional ( | state | { state . sequence ( | state | { self :: escape ( state ) . and_then ( | state | { self :: inner_str ( state ) } ) } ) } ) } ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn inner_chr ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: inner_chr , | state | { state . atomic ( :: pest :: Atomicity :: Atomic , | state | { self :: escape ( state ) . or_else ( | state | { self :: ANY ( state ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn escape ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: escape , | state | { state . atomic ( :: pest :: Atomicity :: Atomic , | state | { state . sequence ( | state | { state . match_string ( "\\" ) . and_then ( | state | { state . match_string ( "\"" ) . or_else ( | state | { state . match_string ( "\\" ) } ) . or_else ( | state | { state . match_string ( "r" ) } ) . or_else ( | state | { state . match_string ( "n" ) } ) . or_else ( | state | { state . match_string ( "t" ) } ) . or_else ( | state | { state . match_string ( "0" ) } ) . or_else ( | state | { state . match_string ( "'" ) } ) . or_else ( | state | { self :: code ( state ) } ) . or_else ( | state | { self :: unicode ( state ) } ) } ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn code ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: code , | state | { state . atomic ( :: pest :: Atomicity :: Atomic , | state | { state . sequence ( | state | { state . match_string ( "x" ) . and_then ( | state | { self :: hex_digit ( state ) } ) . and_then ( | state | { self :: hex_digit ( state ) } ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn unicode ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: unicode , | state | { state . atomic ( :: pest :: Atomicity :: Atomic , | state | { state . sequence ( | state | { state . match_string ( "u" ) . and_then ( | state | { self :: opening_brace ( state ) } ) . and_then ( | state | { state . sequence ( | state | { self :: hex_digit ( state ) . and_then ( | state | { self :: hex_digit ( state ) } ) . and_then ( | state | { state . optional ( | state | { self :: hex_digit ( state ) } ) } ) . and_then ( | state | { state . optional ( | state | { self :: hex_digit ( state ) } ) } ) . and_then ( | state | { state . optional ( | state | { self :: hex_digit ( state ) } ) } ) . and_then ( | state | { state . optional ( | state | { self :: hex_digit ( state ) } ) } ) } ) } ) . and_then ( | state | { self :: closing_brace ( state ) } ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn hex_digit ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: hex_digit , | state | { state . atomic ( :: pest :: Atomicity :: Atomic , | state | { state . match_range ( '0' .. '9' ) . or_else ( | state | { state . match_range ( 'a' .. 'f' ) } ) . or_else ( | state | { state . match_range ( 'A' .. 'F' ) } ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn quote ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: quote , | state | { state . match_string ( "\"" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn single_quote ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: single_quote , | state | { state . match_string ( "'" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn range_operator ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: range_operator , | state | { state . match_string ( ".." ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn newline ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . match_string ( "\n" ) . or_else ( | state | { state . match_string ( "\r\n" ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn WHITESPACE ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . atomic ( :: pest :: Atomicity :: Atomic , | state | { state . match_string ( " " ) . or_else ( | state | { state . match_string ( "\t" ) } ) . or_else ( | state | { self :: newline ( state ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn block_comment ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . sequence ( | state | { state . match_string ( "/*" ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { state . sequence ( | state | { state . optional ( | state | { self :: block_comment ( state ) . or_else ( | state | { state . sequence ( | state | { state . lookahead ( false , | state | { state . match_string ( "*/" ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: ANY ( state ) } ) } ) } ) . and_then ( | state | { state . repeat ( | state | { state . sequence ( | state | { super :: hidden :: skip ( state ) . and_then ( | state | { self :: block_comment ( state ) . or_else ( | state | { state . sequence ( | state | { state . lookahead ( false , | state | { state . match_string ( "*/" ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { self :: ANY ( state ) } ) } ) } ) } ) } ) } ) } ) } ) } ) } ) . and_then ( | state | { super :: hidden :: skip ( state ) } ) . and_then ( | state | { state . match_string ( "*/" ) } ) } ) } # [ inline ] # [ allow ( non_snake_case , unused_variables ) ] pub fn COMMENT ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . atomic ( :: pest :: Atomicity :: Atomic , | state | { self :: block_comment ( state ) . or_else ( | state | { state . sequence ( | state | { state . match_string ( "//" ) . and_then ( | state | { state . repeat ( | state | { state . sequence ( | state | { state . lookahead ( false , | state | { self :: newline ( state ) } ) . and_then ( | state | { self :: ANY ( state ) } ) } ) } ) } ) } ) } ) } ) } # [ inline ] # [ allow ( dead_code , non_snake_case , unused_variables ) ] pub fn SOI ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . start_of_input ( ) } # [ inline ] # [ allow ( dead_code , non_snake_case , unused_variables ) ] pub fn ANY ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . skip ( 1 ) } # [ inline ] # [ allow ( dead_code , non_snake_case , unused_variables ) ] pub fn EOI ( state : Box < :: pest :: ParserState < Rule >> ) -> :: pest :: ParseResult < Box < :: pest :: ParserState < Rule >> > { state . rule ( Rule :: EOI , | state | state . end_of_input ( ) ) } } pub use self :: visible :: * ; } :: pest :: state ( input , | state | { match rule { Rule :: grammar_rules => rules :: grammar_rules ( state ) , Rule :: grammar_rule => rules :: grammar_rule ( state ) , Rule :: assignment_operator => rules :: assignment_operator ( state ) , Rule :: opening_brace => rules :: opening_brace ( state ) , Rule :: closing_brace => rules :: closing_brace ( state ) , Rule :: opening_paren => rules :: opening_paren ( state ) , Rule :: closing_paren => rules :: closing_paren ( state ) , Rule :: opening_brack => rules :: opening_brack ( state ) , Rule :: closing_brack => rules :: closing_brack ( state ) , Rule :: modifier => rules :: modifier ( state ) , Rule :: silent_modifier => rules :: silent_modifier ( state ) , Rule :: atomic_modifier => rules :: atomic_modifier ( state ) , Rule :: compound_atomic_modifier => rules :: compound_atomic_modifier ( state ) , Rule :: non_atomic_modifier => rules :: non_atomic_modifier ( state ) , Rule :: expression => rules :: expression ( state ) , Rule :: term => rules :: term ( state ) , Rule :: node => rules :: node ( state ) , Rule :: terminal => rules :: terminal ( state ) , Rule :: prefix_operator => rules :: prefix_operator ( state ) , Rule :: infix_operator => rules :: infix_operator ( state ) , Rule :: postfix_operator => rules :: postfix_operator ( state ) , Rule :: positive_predicate_operator => rules :: positive_predicate_operator ( state ) , Rule :: negative_predicate_operator => rules :: negative_predicate_operator ( state ) , Rule :: sequence_operator => rules :: sequence_operator ( state ) , Rule :: choice_operator => rules :: choice_operator ( state ) , Rule :: optional_operator => rules :: optional_operator ( state ) , Rule :: repeat_operator => rules :: repeat_operator ( state ) , Rule :: repeat_once_operator => rules :: repeat_once_operator ( state ) , Rule :: repeat_exact => rules :: repeat_exact ( state ) , Rule :: repeat_min => rules :: repeat_min ( state ) , Rule :: repeat_max => rules :: repeat_max ( state ) , Rule :: repeat_min_max => rules :: repeat_min_max ( state ) , Rule :: number => rules :: number ( state ) , Rule :: integer => rules :: integer ( state ) , Rule :: comma => rules :: comma ( state ) , Rule :: _push => rules :: _push ( state ) , Rule :: peek_slice => rules :: peek_slice ( state ) , Rule :: identifier => rules :: identifier ( state ) , Rule :: alpha => rules :: alpha ( state ) , Rule :: alpha_num => rules :: alpha_num ( state ) , Rule :: string => rules :: string ( state ) , Rule :: insensitive_string => rules :: insensitive_string ( state ) , Rule :: range => rules :: range ( state ) , Rule :: character => rules :: character ( state ) , Rule :: inner_str => rules :: inner_str ( state ) , Rule :: inner_chr => rules :: inner_chr ( state ) , Rule :: escape => rules :: escape ( state ) , Rule :: code => rules :: code ( state ) , Rule :: unicode => rules :: unicode ( state ) , Rule :: hex_digit => rules :: hex_digit ( state ) , Rule :: quote => rules :: quote ( state ) , Rule :: single_quote => rules :: single_quote ( state ) , Rule :: range_operator => rules :: range_operator ( state ) , Rule :: newline => rules :: newline ( state ) , Rule :: WHITESPACE => rules :: WHITESPACE ( state ) , Rule :: block_comment => rules :: block_comment ( state ) , Rule :: COMMENT => rules :: COMMENT ( state ) , Rule :: EOI => rules :: EOI ( state ) } } ) } }
+# [doc = "Pest meta-grammar\n\n# Warning: Semantic Versioning\nThere may be non-breaking changes to the meta-grammar\nbetween minor versions. Those non-breaking changes, however,\nmay translate into semver-breaking changes due to the additional variants\nadded to the `Rule` enum. This is a known issue and will be fixed in the\nfuture (e.g. by increasing MSRV and non_exhaustive annotations)."] # [allow (dead_code , non_camel_case_types , clippy :: upper_case_acronyms)] # [derive (Clone , Copy , Debug , Eq , Hash , Ord , PartialEq , PartialOrd)] pub enum Rule { EOI , # [doc = "The top-level rule of a grammar."] r#grammar_rules , # [doc = "A rule of a grammar."] r#grammar_rule , # [doc = "Assignment operator."] r#assignment_operator , # [doc = "Opening brace for a rule."] r#opening_brace , # [doc = "Closing brace for a rule."] r#closing_brace , # [doc = "Opening parenthesis for a branch, PUSH, etc."] r#opening_paren , # [doc = "Closing parenthesis for a branch, PUSH, etc."] r#closing_paren , # [doc = "Opening bracket for PEEK (slice inside)."] r#opening_brack , # [doc = "Closing bracket for PEEK (slice inside)."] r#closing_brack , # [doc = "A rule modifier."] r#modifier , # [doc = "Silent rule prefix."] r#silent_modifier , # [doc = "Atomic rule prefix."] r#atomic_modifier , # [doc = "Compound atomic rule prefix."] r#compound_atomic_modifier , # [doc = "Non-atomic rule prefix."] r#non_atomic_modifier , # [doc = "A rule expression."] r#expression , # [doc = "A rule term."] r#term , # [doc = "A rule node (inside terms)."] r#node , # [doc = "A terminal expression."] r#terminal , # [doc = "Possible predicates for a rule."] r#prefix_operator , # [doc = "Branches or sequences."] r#infix_operator , # [doc = "Possible modifiers for a rule."] r#postfix_operator , # [doc = "A positive predicate."] r#positive_predicate_operator , # [doc = "A negative predicate."] r#negative_predicate_operator , # [doc = "A sequence operator."] r#sequence_operator , # [doc = "A choice operator."] r#choice_operator , # [doc = "An optional operator."] r#optional_operator , # [doc = "A repeat operator."] r#repeat_operator , # [doc = "A repeat at least once operator."] r#repeat_once_operator , # [doc = "A repeat exact times."] r#repeat_exact , # [doc = "A repeat at least times."] r#repeat_min , # [doc = "A repeat at most times."] r#repeat_max , # [doc = "A repeat in a range."] r#repeat_min_max , # [doc = "A number."] r#number , # [doc = "An integer number (positive or negative)."] r#integer , # [doc = "A comma terminal."] r#comma , # [doc = "A PUSH expression."] r#_push , # [doc = "A PEEK expression."] r#peek_slice , # [doc = "An identifier."] r#identifier , # [doc = "An alpha character."] r#alpha , # [doc = "An alphanumeric character."] r#alpha_num , # [doc = "A string."] r#string , # [doc = "An insensitive string."] r#insensitive_string , # [doc = "A character range."] r#range , # [doc = "A single quoted character"] r#character , # [doc = "A quoted string."] r#inner_str , # [doc = "An escaped or any character."] r#inner_chr , # [doc = "An escape sequence."] r#escape , # [doc = "A hexadecimal code."] r#code , # [doc = "A unicode code."] r#unicode , # [doc = "A hexadecimal digit."] r#hex_digit , # [doc = "A double quote."] r#quote , # [doc = "A single quote."] r#single_quote , # [doc = "A range operator."] r#range_operator , # [doc = "A newline character."] r#newline , # [doc = "A whitespace character."] r#WHITESPACE , # [doc = "A single line comment."] r#line_comment , # [doc = "A multi-line comment."] r#block_comment , # [doc = "A grammar comment."] r#COMMENT , # [doc = "A space character."] r#space , # [doc = "A top-level comment."] r#grammar_doc , # [doc = "A rule comment."] r#line_doc , # [doc = "A comment content."] r#inner_doc } # [allow (clippy :: all)] impl :: pest :: Parser < Rule > for PestParser { fn parse < 'i > (rule : Rule , input : & 'i str) -> :: std :: result :: Result < :: pest :: iterators :: Pairs < 'i , Rule > , :: pest :: error :: Error < Rule > > { mod rules { # ! [allow (clippy :: upper_case_acronyms)] pub mod hidden { use super :: super :: Rule ; # [inline] # [allow (dead_code , non_snake_case , unused_variables)] pub fn skip (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { if state . atomicity () == :: pest :: Atomicity :: NonAtomic { state . sequence (| state | { state . repeat (| state | super :: visible :: WHITESPACE (state)) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: visible :: COMMENT (state) . and_then (| state | { state . repeat (| state | super :: visible :: WHITESPACE (state)) }) }) }) }) }) } else { Ok (state) } } } pub mod visible { use super :: super :: Rule ; # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#grammar_rules (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . sequence (| state | { self :: r#SOI (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . sequence (| state | { state . optional (| state | { self :: r#grammar_doc (state) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: hidden :: skip (state) . and_then (| state | { self :: r#grammar_doc (state) }) }) }) }) }) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . sequence (| state | { self :: r#grammar_rule (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . sequence (| state | { state . optional (| state | { self :: r#grammar_rule (state) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: hidden :: skip (state) . and_then (| state | { self :: r#grammar_rule (state) }) }) }) }) }) }) }) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#EOI (state) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#grammar_rule (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#grammar_rule , | state | { state . sequence (| state | { self :: r#identifier (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#assignment_operator (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . optional (| state | { self :: r#modifier (state) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#opening_brace (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#expression (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#closing_brace (state) }) }) . or_else (| state | { self :: r#line_doc (state) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#assignment_operator (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#assignment_operator , | state | { state . match_string ("=") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#opening_brace (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#opening_brace , | state | { state . match_string ("{") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#closing_brace (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#closing_brace , | state | { state . match_string ("}") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#opening_paren (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#opening_paren , | state | { state . match_string ("(") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#closing_paren (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#closing_paren , | state | { state . match_string (")") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#opening_brack (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#opening_brack , | state | { state . match_string ("[") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#closing_brack (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#closing_brack , | state | { state . match_string ("]") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#modifier (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { self :: r#silent_modifier (state) . or_else (| state | { self :: r#atomic_modifier (state) }) . or_else (| state | { self :: r#compound_atomic_modifier (state) }) . or_else (| state | { self :: r#non_atomic_modifier (state) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#silent_modifier (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#silent_modifier , | state | { state . match_string ("_") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#atomic_modifier (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#atomic_modifier , | state | { state . match_string ("@") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#compound_atomic_modifier (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#compound_atomic_modifier , | state | { state . match_string ("$") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#non_atomic_modifier (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#non_atomic_modifier , | state | { state . match_string ("!") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#expression (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#expression , | state | { state . sequence (| state | { state . optional (| state | { self :: r#choice_operator (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#term (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . sequence (| state | { state . optional (| state | { state . sequence (| state | { self :: r#infix_operator (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#term (state) }) }) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: hidden :: skip (state) . and_then (| state | { state . sequence (| state | { self :: r#infix_operator (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#term (state) }) }) }) }) }) }) }) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#term (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#term , | state | { state . sequence (| state | { state . sequence (| state | { state . optional (| state | { self :: r#prefix_operator (state) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: hidden :: skip (state) . and_then (| state | { self :: r#prefix_operator (state) }) }) }) }) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#node (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . sequence (| state | { state . optional (| state | { self :: r#postfix_operator (state) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: hidden :: skip (state) . and_then (| state | { self :: r#postfix_operator (state) }) }) }) }) }) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#node (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . sequence (| state | { self :: r#opening_paren (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#expression (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#closing_paren (state) }) }) . or_else (| state | { self :: r#terminal (state) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#terminal (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { self :: r#_push (state) . or_else (| state | { self :: r#peek_slice (state) }) . or_else (| state | { self :: r#identifier (state) }) . or_else (| state | { self :: r#string (state) }) . or_else (| state | { self :: r#insensitive_string (state) }) . or_else (| state | { self :: r#range (state) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#prefix_operator (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { self :: r#positive_predicate_operator (state) . or_else (| state | { self :: r#negative_predicate_operator (state) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#infix_operator (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { self :: r#sequence_operator (state) . or_else (| state | { self :: r#choice_operator (state) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#postfix_operator (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { self :: r#optional_operator (state) . or_else (| state | { self :: r#repeat_operator (state) }) . or_else (| state | { self :: r#repeat_once_operator (state) }) . or_else (| state | { self :: r#repeat_exact (state) }) . or_else (| state | { self :: r#repeat_min (state) }) . or_else (| state | { self :: r#repeat_max (state) }) . or_else (| state | { self :: r#repeat_min_max (state) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#positive_predicate_operator (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#positive_predicate_operator , | state | { state . match_string ("&") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#negative_predicate_operator (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#negative_predicate_operator , | state | { state . match_string ("!") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#sequence_operator (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#sequence_operator , | state | { state . match_string ("~") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#choice_operator (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#choice_operator , | state | { state . match_string ("|") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#optional_operator (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#optional_operator , | state | { state . match_string ("?") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#repeat_operator (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#repeat_operator , | state | { state . match_string ("*") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#repeat_once_operator (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#repeat_once_operator , | state | { state . match_string ("+") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#repeat_exact (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#repeat_exact , | state | { state . sequence (| state | { self :: r#opening_brace (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#number (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#closing_brace (state) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#repeat_min (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#repeat_min , | state | { state . sequence (| state | { self :: r#opening_brace (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#number (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#comma (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#closing_brace (state) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#repeat_max (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#repeat_max , | state | { state . sequence (| state | { self :: r#opening_brace (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#comma (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#number (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#closing_brace (state) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#repeat_min_max (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#repeat_min_max , | state | { state . sequence (| state | { self :: r#opening_brace (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#number (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#comma (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#number (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#closing_brace (state) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#number (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#number , | state | { state . atomic (:: pest :: Atomicity :: Atomic , | state | { state . sequence (| state | { state . match_range ('0' .. '9') . and_then (| state | { state . repeat (| state | { state . match_range ('0' .. '9') }) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#integer (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#integer , | state | { state . atomic (:: pest :: Atomicity :: Atomic , | state | { self :: r#number (state) . or_else (| state | { state . sequence (| state | { state . match_string ("-") . and_then (| state | { state . repeat (| state | { state . match_string ("0") }) }) . and_then (| state | { state . match_range ('1' .. '9') }) . and_then (| state | { state . optional (| state | { self :: r#number (state) }) }) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#comma (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#comma , | state | { state . match_string (",") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#_push (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#_push , | state | { state . sequence (| state | { state . match_string ("PUSH") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#opening_paren (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#expression (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#closing_paren (state) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#peek_slice (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#peek_slice , | state | { state . sequence (| state | { state . match_string ("PEEK") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#opening_brack (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . optional (| state | { self :: r#integer (state) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#range_operator (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . optional (| state | { self :: r#integer (state) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#closing_brack (state) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#identifier (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#identifier , | state | { state . atomic (:: pest :: Atomicity :: Atomic , | state | { state . sequence (| state | { state . lookahead (false , | state | { state . match_string ("PUSH") }) . and_then (| state | { state . match_string ("_") . or_else (| state | { self :: r#alpha (state) }) }) . and_then (| state | { state . repeat (| state | { state . match_string ("_") . or_else (| state | { self :: r#alpha_num (state) }) }) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#alpha (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . match_range ('a' .. 'z') . or_else (| state | { state . match_range ('A' .. 'Z') }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#alpha_num (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { self :: r#alpha (state) . or_else (| state | { state . match_range ('0' .. '9') }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#string (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . atomic (:: pest :: Atomicity :: CompoundAtomic , | state | { state . rule (Rule :: r#string , | state | { state . sequence (| state | { self :: r#quote (state) . and_then (| state | { self :: r#inner_str (state) }) . and_then (| state | { self :: r#quote (state) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#insensitive_string (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#insensitive_string , | state | { state . sequence (| state | { state . match_string ("^") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#string (state) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#range (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#range , | state | { state . sequence (| state | { self :: r#character (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#range_operator (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#character (state) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#character (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . atomic (:: pest :: Atomicity :: CompoundAtomic , | state | { state . rule (Rule :: r#character , | state | { state . sequence (| state | { self :: r#single_quote (state) . and_then (| state | { self :: r#inner_chr (state) }) . and_then (| state | { self :: r#single_quote (state) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#inner_str (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#inner_str , | state | { state . atomic (:: pest :: Atomicity :: Atomic , | state | { state . sequence (| state | { let strings = ["\"" , "\\"] ; state . skip_until (& strings) . and_then (| state | { state . optional (| state | { state . sequence (| state | { self :: r#escape (state) . and_then (| state | { self :: r#inner_str (state) }) }) }) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#inner_chr (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#inner_chr , | state | { state . atomic (:: pest :: Atomicity :: Atomic , | state | { self :: r#escape (state) . or_else (| state | { self :: r#ANY (state) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#escape (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#escape , | state | { state . atomic (:: pest :: Atomicity :: Atomic , | state | { state . sequence (| state | { state . match_string ("\\") . and_then (| state | { state . match_string ("\"") . or_else (| state | { state . match_string ("\\") }) . or_else (| state | { state . match_string ("r") }) . or_else (| state | { state . match_string ("n") }) . or_else (| state | { state . match_string ("t") }) . or_else (| state | { state . match_string ("0") }) . or_else (| state | { state . match_string ("'") }) . or_else (| state | { self :: r#code (state) }) . or_else (| state | { self :: r#unicode (state) }) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#code (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#code , | state | { state . atomic (:: pest :: Atomicity :: Atomic , | state | { state . sequence (| state | { state . match_string ("x") . and_then (| state | { self :: r#hex_digit (state) }) . and_then (| state | { self :: r#hex_digit (state) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#unicode (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#unicode , | state | { state . atomic (:: pest :: Atomicity :: Atomic , | state | { state . sequence (| state | { state . match_string ("u") . and_then (| state | { self :: r#opening_brace (state) }) . and_then (| state | { state . sequence (| state | { self :: r#hex_digit (state) . and_then (| state | { self :: r#hex_digit (state) }) . and_then (| state | { state . optional (| state | { self :: r#hex_digit (state) }) }) . and_then (| state | { state . optional (| state | { self :: r#hex_digit (state) }) }) . and_then (| state | { state . optional (| state | { self :: r#hex_digit (state) }) }) . and_then (| state | { state . optional (| state | { self :: r#hex_digit (state) }) }) }) }) . and_then (| state | { self :: r#closing_brace (state) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#hex_digit (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#hex_digit , | state | { state . atomic (:: pest :: Atomicity :: Atomic , | state | { state . match_range ('0' .. '9') . or_else (| state | { state . match_range ('a' .. 'f') }) . or_else (| state | { state . match_range ('A' .. 'F') }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#quote (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#quote , | state | { state . match_string ("\"") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#single_quote (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#single_quote , | state | { state . match_string ("'") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#range_operator (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#range_operator , | state | { state . match_string ("..") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#newline (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . match_string ("\n") . or_else (| state | { state . match_string ("\r\n") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#WHITESPACE (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . atomic (:: pest :: Atomicity :: Atomic , | state | { state . match_string (" ") . or_else (| state | { state . match_string ("\t") }) . or_else (| state | { self :: r#newline (state) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#line_comment (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . sequence (| state | { state . match_string ("//") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . lookahead (false , | state | { state . match_string ("/") . or_else (| state | { state . match_string ("!") }) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . sequence (| state | { state . optional (| state | { state . sequence (| state | { state . lookahead (false , | state | { self :: r#newline (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#ANY (state) }) }) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: hidden :: skip (state) . and_then (| state | { state . sequence (| state | { state . lookahead (false , | state | { self :: r#newline (state) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#ANY (state) }) }) }) }) }) }) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#block_comment (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . sequence (| state | { state . match_string ("/*") . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . sequence (| state | { state . optional (| state | { self :: r#block_comment (state) . or_else (| state | { state . sequence (| state | { state . lookahead (false , | state | { state . match_string ("*/") }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#ANY (state) }) }) }) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: hidden :: skip (state) . and_then (| state | { self :: r#block_comment (state) . or_else (| state | { state . sequence (| state | { state . lookahead (false , | state | { state . match_string ("*/") }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: r#ANY (state) }) }) }) }) }) }) }) }) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . match_string ("*/") }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#COMMENT (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . atomic (:: pest :: Atomicity :: Atomic , | state | { self :: r#block_comment (state) . or_else (| state | { self :: r#line_comment (state) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#space (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . match_string (" ") . or_else (| state | { state . match_string ("\t") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#grammar_doc (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . atomic (:: pest :: Atomicity :: CompoundAtomic , | state | { state . rule (Rule :: r#grammar_doc , | state | { state . sequence (| state | { state . match_string ("//!") . and_then (| state | { state . optional (| state | { self :: r#space (state) }) }) . and_then (| state | { self :: r#inner_doc (state) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#line_doc (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . atomic (:: pest :: Atomicity :: CompoundAtomic , | state | { state . rule (Rule :: r#line_doc , | state | { state . sequence (| state | { state . match_string ("///") . and_then (| state | { state . optional (| state | { self :: r#space (state) }) }) . and_then (| state | { state . lookahead (false , | state | { state . match_string ("/") }) }) . and_then (| state | { self :: r#inner_doc (state) }) }) }) }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn r#inner_doc (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: r#inner_doc , | state | { state . atomic (:: pest :: Atomicity :: Atomic , | state | { state . repeat (| state | { state . sequence (| state | { state . lookahead (false , | state | { self :: r#newline (state) }) . and_then (| state | { self :: r#ANY (state) }) }) }) }) }) } # [inline] # [allow (dead_code , non_snake_case , unused_variables)] pub fn ANY (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . skip (1) } # [inline] # [allow (dead_code , non_snake_case , unused_variables)] pub fn EOI (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . rule (Rule :: EOI , | state | state . end_of_input ()) } # [inline] # [allow (dead_code , non_snake_case , unused_variables)] pub fn SOI (state : :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >>) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < '_ , Rule >> > { state . start_of_input () } } pub use self :: visible :: * ; } :: pest :: state (input , | state | { match rule { Rule :: r#grammar_rules => rules :: r#grammar_rules (state) , Rule :: r#grammar_rule => rules :: r#grammar_rule (state) , Rule :: r#assignment_operator => rules :: r#assignment_operator (state) , Rule :: r#opening_brace => rules :: r#opening_brace (state) , Rule :: r#closing_brace => rules :: r#closing_brace (state) , Rule :: r#opening_paren => rules :: r#opening_paren (state) , Rule :: r#closing_paren => rules :: r#closing_paren (state) , Rule :: r#opening_brack => rules :: r#opening_brack (state) , Rule :: r#closing_brack => rules :: r#closing_brack (state) , Rule :: r#modifier => rules :: r#modifier (state) , Rule :: r#silent_modifier => rules :: r#silent_modifier (state) , Rule :: r#atomic_modifier => rules :: r#atomic_modifier (state) , Rule :: r#compound_atomic_modifier => rules :: r#compound_atomic_modifier (state) , Rule :: r#non_atomic_modifier => rules :: r#non_atomic_modifier (state) , Rule :: r#expression => rules :: r#expression (state) , Rule :: r#term => rules :: r#term (state) , Rule :: r#node => rules :: r#node (state) , Rule :: r#terminal => rules :: r#terminal (state) , Rule :: r#prefix_operator => rules :: r#prefix_operator (state) , Rule :: r#infix_operator => rules :: r#infix_operator (state) , Rule :: r#postfix_operator => rules :: r#postfix_operator (state) , Rule :: r#positive_predicate_operator => rules :: r#positive_predicate_operator (state) , Rule :: r#negative_predicate_operator => rules :: r#negative_predicate_operator (state) , Rule :: r#sequence_operator => rules :: r#sequence_operator (state) , Rule :: r#choice_operator => rules :: r#choice_operator (state) , Rule :: r#optional_operator => rules :: r#optional_operator (state) , Rule :: r#repeat_operator => rules :: r#repeat_operator (state) , Rule :: r#repeat_once_operator => rules :: r#repeat_once_operator (state) , Rule :: r#repeat_exact => rules :: r#repeat_exact (state) , Rule :: r#repeat_min => rules :: r#repeat_min (state) , Rule :: r#repeat_max => rules :: r#repeat_max (state) , Rule :: r#repeat_min_max => rules :: r#repeat_min_max (state) , Rule :: r#number => rules :: r#number (state) , Rule :: r#integer => rules :: r#integer (state) , Rule :: r#comma => rules :: r#comma (state) , Rule :: r#_push => rules :: r#_push (state) , Rule :: r#peek_slice => rules :: r#peek_slice (state) , Rule :: r#identifier => rules :: r#identifier (state) , Rule :: r#alpha => rules :: r#alpha (state) , Rule :: r#alpha_num => rules :: r#alpha_num (state) , Rule :: r#string => rules :: r#string (state) , Rule :: r#insensitive_string => rules :: r#insensitive_string (state) , Rule :: r#range => rules :: r#range (state) , Rule :: r#character => rules :: r#character (state) , Rule :: r#inner_str => rules :: r#inner_str (state) , Rule :: r#inner_chr => rules :: r#inner_chr (state) , Rule :: r#escape => rules :: r#escape (state) , Rule :: r#code => rules :: r#code (state) , Rule :: r#unicode => rules :: r#unicode (state) , Rule :: r#hex_digit => rules :: r#hex_digit (state) , Rule :: r#quote => rules :: r#quote (state) , Rule :: r#single_quote => rules :: r#single_quote (state) , Rule :: r#range_operator => rules :: r#range_operator (state) , Rule :: r#newline => rules :: r#newline (state) , Rule :: r#WHITESPACE => rules :: r#WHITESPACE (state) , Rule :: r#line_comment => rules :: r#line_comment (state) , Rule :: r#block_comment => rules :: r#block_comment (state) , Rule :: r#COMMENT => rules :: r#COMMENT (state) , Rule :: r#space => rules :: r#space (state) , Rule :: r#grammar_doc => rules :: r#grammar_doc (state) , Rule :: r#line_doc => rules :: r#line_doc (state) , Rule :: r#inner_doc => rules :: r#inner_doc (state) , Rule :: EOI => rules :: EOI (state) } }) } }
diff --git a/src/lib.rs b/src/lib.rs
index 28f33b0..198001a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,22 +6,33 @@
 // license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
+//! # pest meta
+//!
+//! This crate parses, validates, optimizes, and converts pest's own grammars to ASTs.
 
-#![allow(clippy::range_plus_one)]
+#![doc(
+    html_logo_url = "https://raw.githubusercontent.com/pest-parser/pest/master/pest-logo.svg",
+    html_favicon_url = "https://raw.githubusercontent.com/pest-parser/pest/master/pest-logo.svg"
+)]
+#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
 
 #[cfg(test)]
 #[macro_use]
 extern crate pest;
-#[cfg(not(test))]
-extern crate pest;
 
+use once_cell::sync::Lazy;
 use std::fmt::Display;
 
+use pest::error::Error;
+use pest::unicode::unicode_property_names;
+
 pub mod ast;
 pub mod optimizer;
 pub mod parser;
 pub mod validator;
 
+/// A helper that will unwrap the result or panic
+/// with the nicely formatted error message.
 pub fn unwrap_or_report<T, E>(result: Result<T, E>) -> T
 where
     E: IntoIterator,
@@ -29,105 +40,37 @@
 {
     result.unwrap_or_else(|e| {
         panic!(
-            "grammar error\n\n".to_owned()
-                + &e.into_iter()
-                    .map(|error| format!("{}", error))
-                    .collect::<Vec<_>>()
-                    .join("\n\n")
+            "{}{}",
+            "grammar error\n\n".to_owned(),
+            &e.into_iter()
+                .map(|error| format!("{}", error))
+                .collect::<Vec<_>>()
+                .join("\n\n")
         )
     })
 }
 
+/// A tuple returned by the validation and processing of the parsed grammar.
+/// The first element is the vector of used builtin rule names,
+/// the second element is the vector of optimized rules.
+type UsedBuiltinAndOptimized<'i> = (Vec<&'i str>, Vec<optimizer::OptimizedRule>);
+
+/// Parses, validates, processes and optimizes the provided grammar.
+pub fn parse_and_optimize(
+    grammar: &str,
+) -> Result<UsedBuiltinAndOptimized<'_>, Vec<Error<parser::Rule>>> {
+    let pairs = match parser::parse(parser::Rule::grammar_rules, grammar) {
+        Ok(pairs) => Ok(pairs),
+        Err(error) => Err(vec![error]),
+    }?;
+
+    let defaults = validator::validate_pairs(pairs.clone())?;
+    let ast = parser::consume_rules(pairs)?;
+
+    Ok((defaults, optimizer::optimize(ast)))
+}
+
 #[doc(hidden)]
-pub static UNICODE_PROPERTY_NAMES: &[&str] = &[
-    /* BINARY */ "ALPHABETIC",
-    "BIDI_CONTROL",
-    "CASE_IGNORABLE",
-    "CASED",
-    "CHANGES_WHEN_CASEFOLDED",
-    "CHANGES_WHEN_CASEMAPPED",
-    "CHANGES_WHEN_LOWERCASED",
-    "CHANGES_WHEN_TITLECASED",
-    "CHANGES_WHEN_UPPERCASED",
-    "DASH",
-    "DEFAULT_IGNORABLE_CODE_POINT",
-    "DEPRECATED",
-    "DIACRITIC",
-    "EXTENDER",
-    "GRAPHEME_BASE",
-    "GRAPHEME_EXTEND",
-    "GRAPHEME_LINK",
-    "HEX_DIGIT",
-    "HYPHEN",
-    "IDS_BINARY_OPERATOR",
-    "IDS_TRINARY_OPERATOR",
-    "ID_CONTINUE",
-    "ID_START",
-    "IDEOGRAPHIC",
-    "JOIN_CONTROL",
-    "LOGICAL_ORDER_EXCEPTION",
-    "LOWERCASE",
-    "MATH",
-    "NONCHARACTER_CODE_POINT",
-    "OTHER_ALPHABETIC",
-    "OTHER_DEFAULT_IGNORABLE_CODE_POINT",
-    "OTHER_GRAPHEME_EXTEND",
-    "OTHER_ID_CONTINUE",
-    "OTHER_ID_START",
-    "OTHER_LOWERCASE",
-    "OTHER_MATH",
-    "OTHER_UPPERCASE",
-    "PATTERN_SYNTAX",
-    "PATTERN_WHITE_SPACE",
-    "PREPENDED_CONCATENATION_MARK",
-    "QUOTATION_MARK",
-    "RADICAL",
-    "REGIONAL_INDICATOR",
-    "SENTENCE_TERMINAL",
-    "SOFT_DOTTED",
-    "TERMINAL_PUNCTUATION",
-    "UNIFIED_IDEOGRAPH",
-    "UPPERCASE",
-    "VARIATION_SELECTOR",
-    "WHITE_SPACE",
-    "XID_CONTINUE",
-    "XID_START",
-    /* CATEGORY */ "CASED_LETTER",
-    "CLOSE_PUNCTUATION",
-    "CONNECTOR_PUNCTUATION",
-    "CONTROL",
-    "CURRENCY_SYMBOL",
-    "DASH_PUNCTUATION",
-    "DECIMAL_NUMBER",
-    "ENCLOSING_MARK",
-    "FINAL_PUNCTUATION",
-    "FORMAT",
-    "INITIAL_PUNCTUATION",
-    "LETTER",
-    "LETTER_NUMBER",
-    "LINE_SEPARATOR",
-    "LOWERCASE_LETTER",
-    "MARK",
-    "MATH_SYMBOL",
-    "MODIFIER_LETTER",
-    "MODIFIER_SYMBOL",
-    "NONSPACING_MARK",
-    "NUMBER",
-    "OPEN_PUNCTUATION",
-    "OTHER",
-    "OTHER_LETTER",
-    "OTHER_NUMBER",
-    "OTHER_PUNCTUATION",
-    "OTHER_SYMBOL",
-    "PARAGRAPH_SEPARATOR",
-    "PRIVATE_USE",
-    "PUNCTUATION",
-    "SEPARATOR",
-    "SPACE_SEPARATOR",
-    "SPACING_MARK",
-    "SURROGATE",
-    "SYMBOL",
-    "TITLECASE_LETTER",
-    "UNASSIGNED",
-    "UPPERCASE_LETTER",
-];
+#[deprecated(note = "use `pest::unicode::unicode_property_names` instead")]
+pub static UNICODE_PROPERTY_NAMES: Lazy<Vec<&str>> =
+    Lazy::new(|| unicode_property_names().collect::<Vec<_>>());
diff --git a/src/optimizer/concatenator.rs b/src/optimizer/concatenator.rs
index e0aab7b..31d3aa5 100644
--- a/src/optimizer/concatenator.rs
+++ b/src/optimizer/concatenator.rs
@@ -7,28 +7,27 @@
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
 
-use ast::*;
+use crate::ast::*;
 
 pub fn concatenate(rule: Rule) -> Rule {
-    match rule {
-        Rule { name, ty, expr } => Rule {
-            name,
-            ty,
-            expr: expr.map_bottom_up(|expr| {
-                if ty == RuleType::Atomic {
-                    // TODO: Use box syntax when it gets stabilized.
-                    match expr {
-                        Expr::Seq(lhs, rhs) => match (*lhs, *rhs) {
-                            (Expr::Str(lhs), Expr::Str(rhs)) => Expr::Str(lhs + &rhs),
-                            (Expr::Insens(lhs), Expr::Insens(rhs)) => Expr::Insens(lhs + &rhs),
-                            (lhs, rhs) => Expr::Seq(Box::new(lhs), Box::new(rhs)),
-                        },
-                        expr => expr,
-                    }
-                } else {
-                    expr
+    let Rule { name, ty, expr } = rule;
+    Rule {
+        name,
+        ty,
+        expr: expr.map_bottom_up(|expr| {
+            if ty == RuleType::Atomic {
+                // TODO: Use box syntax when it gets stabilized.
+                match expr {
+                    Expr::Seq(lhs, rhs) => match (*lhs, *rhs) {
+                        (Expr::Str(lhs), Expr::Str(rhs)) => Expr::Str(lhs + &rhs),
+                        (Expr::Insens(lhs), Expr::Insens(rhs)) => Expr::Insens(lhs + &rhs),
+                        (lhs, rhs) => Expr::Seq(Box::new(lhs), Box::new(rhs)),
+                    },
+                    expr => expr,
                 }
-            }),
-        },
+            } else {
+                expr
+            }
+        }),
     }
 }
diff --git a/src/optimizer/factorizer.rs b/src/optimizer/factorizer.rs
index 236289e..cff018b 100644
--- a/src/optimizer/factorizer.rs
+++ b/src/optimizer/factorizer.rs
@@ -7,32 +7,49 @@
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
 
-use ast::*;
+use crate::ast::*;
 
 pub fn factor(rule: Rule) -> Rule {
-    match rule {
-        Rule { name, ty, expr } => Rule {
-            name,
-            ty,
-            expr: expr.map_top_down(|expr| {
-                // TODO: Use box syntax when it gets stabilized.
-                match expr {
-                    Expr::Choice(lhs, rhs) => match (*lhs, *rhs) {
-                        (Expr::Seq(l1, r1), Expr::Seq(l2, r2)) => {
-                            if l1 == l2 {
-                                Expr::Seq(l1, Box::new(Expr::Choice(r1, r2)))
-                            } else {
-                                Expr::Choice(
-                                    Box::new(Expr::Seq(l1, r1)),
-                                    Box::new(Expr::Seq(l2, r2)),
-                                )
-                            }
+    let Rule { name, ty, expr } = rule;
+    Rule {
+        name,
+        ty,
+        expr: expr.map_top_down(|expr| {
+            // TODO: Use box syntax when it gets stabilized.
+            match expr {
+                Expr::Choice(lhs, rhs) => match (*lhs, *rhs) {
+                    (Expr::Seq(l1, r1), Expr::Seq(l2, r2)) => {
+                        if l1 == l2 {
+                            Expr::Seq(l1, Box::new(Expr::Choice(r1, r2)))
+                        } else {
+                            Expr::Choice(Box::new(Expr::Seq(l1, r1)), Box::new(Expr::Seq(l2, r2)))
                         }
-                        (lhs, rhs) => Expr::Choice(Box::new(lhs), Box::new(rhs)),
-                    },
-                    expr => expr,
-                }
-            }),
-        },
+                    }
+                    // Converts `(rule ~ rest) | rule` to `rule ~ rest?`, avoiding trying to match `rule` twice.
+                    // This is only done for atomic rules, because other rule types have implicit whitespaces.
+                    // FIXME: "desugar" implicit whitespace rules before applying any optimizations
+                    (Expr::Seq(l1, l2), r)
+                        if matches!(ty, RuleType::Atomic | RuleType::CompoundAtomic) =>
+                    {
+                        if *l1 == r {
+                            Expr::Seq(l1, Box::new(Expr::Opt(l2)))
+                        } else {
+                            Expr::Choice(Box::new(Expr::Seq(l1, l2)), Box::new(r))
+                        }
+                    }
+                    // Converts `rule | (rule ~ rest)` to `rule` since `(rule ~ rest)`
+                    // will never match if `rule` didn't.
+                    (l, Expr::Seq(r1, r2)) => {
+                        if l == *r1 {
+                            l
+                        } else {
+                            Expr::Choice(Box::new(l), Box::new(Expr::Seq(r1, r2)))
+                        }
+                    }
+                    (lhs, rhs) => Expr::Choice(Box::new(lhs), Box::new(rhs)),
+                },
+                expr => expr,
+            }
+        }),
     }
 }
diff --git a/src/optimizer/lister.rs b/src/optimizer/lister.rs
new file mode 100644
index 0000000..e198850
--- /dev/null
+++ b/src/optimizer/lister.rs
@@ -0,0 +1,42 @@
+// pest. The Elegant Parser
+// Copyright (c) 2018 DragoČ™ Tiselice
+//
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
+// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. All files in the project carrying such notice may not be copied,
+// modified, or distributed except according to those terms.
+
+use crate::ast::*;
+
+pub fn list(rule: Rule) -> Rule {
+    let Rule { name, ty, expr } = rule;
+    Rule {
+        name,
+        ty,
+        expr: expr.map_bottom_up(|expr| {
+            // TODO: Use box syntax when it gets stabilized.
+            match expr {
+                Expr::Seq(l, r) => match *l {
+                    Expr::Rep(l) => {
+                        let l = *l;
+                        match l {
+                            Expr::Seq(l1, l2) => {
+                                // Converts `(rule ~ rest)* ~ rule` to `rule ~ (rest ~ rule)*`,
+                                // avoiding matching the last `rule` twice.
+                                if l1 == r {
+                                    Expr::Seq(l1, Box::new(Expr::Rep(Box::new(Expr::Seq(l2, r)))))
+                                } else {
+                                    Expr::Seq(Box::new(Expr::Rep(Box::new(Expr::Seq(l1, l2)))), r)
+                                }
+                            }
+                            expr => Expr::Seq(Box::new(Expr::Rep(Box::new(expr))), r),
+                        }
+                    }
+                    expr => Expr::Seq(Box::new(expr), r),
+                },
+                expr => expr,
+            }
+        }),
+    }
+}
diff --git a/src/optimizer/mod.rs b/src/optimizer/mod.rs
index 7013c43..f9cde83 100644
--- a/src/optimizer/mod.rs
+++ b/src/optimizer/mod.rs
@@ -7,7 +7,9 @@
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
 
-use ast::*;
+//! Different optimizations for pest's ASTs.
+
+use crate::ast::*;
 use std::collections::HashMap;
 
 #[cfg(test)]
@@ -20,11 +22,13 @@
 
 mod concatenator;
 mod factorizer;
+mod lister;
 mod restorer;
 mod rotater;
 mod skipper;
 mod unroller;
 
+/// Takes pest's ASTs and optimizes them
 pub fn optimize(rules: Vec<Rule>) -> Vec<OptimizedRule> {
     let optimized: Vec<OptimizedRule> = rules
         .into_iter()
@@ -33,6 +37,7 @@
         .map(unroller::unroll)
         .map(concatenator::concatenate)
         .map(factorizer::factor)
+        .map(lister::list)
         .map(rule_to_optimized_rule)
         .collect();
 
@@ -85,36 +90,64 @@
         .collect()
 }
 
+/// The optimized version of the pest AST's `Rule`.
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct OptimizedRule {
+    /// The name of the rule.
     pub name: String,
+    /// The type of the rule.
     pub ty: RuleType,
+    /// The optimized expression of the rule.
     pub expr: OptimizedExpr,
 }
 
+/// The optimized version of the pest AST's `Expr`.
+///
+/// # Warning: Semantic Versioning
+/// There may be non-breaking changes to the meta-grammar
+/// between minor versions. Those non-breaking changes, however,
+/// may translate into semver-breaking changes due to the additional variants
+/// propaged from the `Rule` enum. This is a known issue and will be fixed in the
+/// future (e.g. by increasing MSRV and non_exhaustive annotations).
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum OptimizedExpr {
+    /// Matches an exact string, e.g. `"a"`
     Str(String),
+    /// Matches an exact string, case insensitively (ASCII only), e.g. `^"a"`
     Insens(String),
+    /// Matches one character in the range, e.g. `'a'..'z'`
     Range(String, String),
+    /// Matches the rule with the given name, e.g. `a`
     Ident(String),
+    /// Matches a custom part of the stack, e.g. `PEEK[..]`
     PeekSlice(i32, Option<i32>),
+    /// Positive lookahead; matches expression without making progress, e.g. `&e`
     PosPred(Box<OptimizedExpr>),
+    /// Negative lookahead; matches if expression doesn't match, without making progress, e.g. `!e`
     NegPred(Box<OptimizedExpr>),
+    /// Matches a sequence of two expressions, e.g. `e1 ~ e2`
     Seq(Box<OptimizedExpr>, Box<OptimizedExpr>),
+    /// Matches either of two expressions, e.g. `e1 | e2`
     Choice(Box<OptimizedExpr>, Box<OptimizedExpr>),
+    /// Optionally matches an expression, e.g. `e?`
     Opt(Box<OptimizedExpr>),
+    /// Matches an expression zero or more times, e.g. `e*`
     Rep(Box<OptimizedExpr>),
+    /// Continues to match expressions until one of the strings in the `Vec` is found
     Skip(Vec<String>),
+    /// Matches an expression and pushes it to the stack, e.g. `push(e)`
     Push(Box<OptimizedExpr>),
+    /// Restores an expression's checkpoint
     RestoreOnErr(Box<OptimizedExpr>),
 }
 
 impl OptimizedExpr {
+    /// Returns a top-down iterator over the `OptimizedExpr`.
     pub fn iter_top_down(&self) -> OptimizedExprTopDownIterator {
         OptimizedExprTopDownIterator::new(self)
     }
 
+    /// Applies `f` to the `OptimizedExpr` top-down.
     pub fn map_top_down<F>(self, mut f: F) -> OptimizedExpr
     where
         F: FnMut(OptimizedExpr) -> OptimizedExpr,
@@ -164,6 +197,7 @@
         map_internal(self, &mut f)
     }
 
+    /// Applies `f` to the `OptimizedExpr` bottom-up.
     pub fn map_bottom_up<F>(self, mut f: F) -> OptimizedExpr
     where
         F: FnMut(OptimizedExpr) -> OptimizedExpr,
@@ -214,6 +248,7 @@
     }
 }
 
+/// A top-down iterator over an `OptimizedExpr`.
 pub struct OptimizedExprTopDownIterator {
     current: Option<OptimizedExpr>,
     next: Option<OptimizedExpr>,
@@ -221,6 +256,7 @@
 }
 
 impl OptimizedExprTopDownIterator {
+    /// Creates a new top down iterator from an `OptimizedExpr`.
     pub fn new(expr: &OptimizedExpr) -> Self {
         let mut iter = OptimizedExprTopDownIterator {
             current: None,
@@ -279,7 +315,7 @@
     #[test]
     fn rotate() {
         let rules = {
-            use ast::Expr::*;
+            use crate::ast::Expr::*;
             vec![Rule {
                 name: "rule".to_owned(),
                 ty: RuleType::Normal,
@@ -293,7 +329,7 @@
             }]
         };
         let rotated = {
-            use optimizer::OptimizedExpr::*;
+            use crate::optimizer::OptimizedExpr::*;
             vec![OptimizedRule {
                 name: "rule".to_owned(),
                 ty: RuleType::Normal,
@@ -313,7 +349,7 @@
     #[test]
     fn skip() {
         let rules = {
-            use ast::Expr::*;
+            use crate::ast::Expr::*;
             vec![Rule {
                 name: "rule".to_owned(),
                 ty: RuleType::Atomic,
@@ -335,7 +371,7 @@
     #[test]
     fn concat_strings() {
         let rules = {
-            use ast::Expr::*;
+            use crate::ast::Expr::*;
             vec![Rule {
                 name: "rule".to_owned(),
                 ty: RuleType::Atomic,
@@ -362,7 +398,7 @@
             expr: Expr::RepExact(Box::new(Expr::Ident(String::from("a"))), 3),
         }];
         let unrolled = {
-            use optimizer::OptimizedExpr::*;
+            use crate::optimizer::OptimizedExpr::*;
             vec![OptimizedRule {
                 name: "rule".to_owned(),
                 ty: RuleType::Atomic,
@@ -384,7 +420,7 @@
             expr: Expr::RepMax(Box::new(Expr::Str("a".to_owned())), 3),
         }];
         let unrolled = {
-            use optimizer::OptimizedExpr::*;
+            use crate::optimizer::OptimizedExpr::*;
             vec![OptimizedRule {
                 name: "rule".to_owned(),
                 ty: RuleType::Atomic,
@@ -406,7 +442,7 @@
             expr: Expr::RepMin(Box::new(Expr::Str("a".to_owned())), 2),
         }];
         let unrolled = {
-            use optimizer::OptimizedExpr::*;
+            use crate::optimizer::OptimizedExpr::*;
             vec![OptimizedRule {
                 name: "rule".to_owned(),
                 ty: RuleType::Atomic,
@@ -428,7 +464,7 @@
             expr: Expr::RepMinMax(Box::new(Expr::Str("a".to_owned())), 2, 3),
         }];
         let unrolled = {
-            use optimizer::OptimizedExpr::*;
+            use crate::optimizer::OptimizedExpr::*;
             vec![OptimizedRule {
                 name: "rule".to_owned(),
                 ty: RuleType::Atomic,
@@ -452,7 +488,7 @@
     #[test]
     fn concat_insensitive_strings() {
         let rules = {
-            use ast::Expr::*;
+            use crate::ast::Expr::*;
             vec![Rule {
                 name: "rule".to_owned(),
                 ty: RuleType::Atomic,
@@ -474,7 +510,7 @@
     #[test]
     fn long_common_sequence() {
         let rules = {
-            use ast::Expr::*;
+            use crate::ast::Expr::*;
             vec![Rule {
                 name: "rule".to_owned(),
                 ty: RuleType::Silent,
@@ -491,7 +527,7 @@
             }]
         };
         let optimized = {
-            use optimizer::OptimizedExpr::*;
+            use crate::optimizer::OptimizedExpr::*;
             vec![OptimizedRule {
                 name: "rule".to_owned(),
                 ty: RuleType::Silent,
@@ -507,4 +543,82 @@
 
         assert_eq!(optimize(rules), optimized);
     }
+
+    #[test]
+    fn short_common_sequence() {
+        let rules = {
+            use crate::ast::Expr::*;
+            vec![Rule {
+                name: "rule".to_owned(),
+                ty: RuleType::Atomic,
+                expr: box_tree!(Choice(
+                    Seq(Ident(String::from("a")), Ident(String::from("b"))),
+                    Ident(String::from("a"))
+                )),
+            }]
+        };
+        let optimized = {
+            use crate::optimizer::OptimizedExpr::*;
+            vec![OptimizedRule {
+                name: "rule".to_owned(),
+                ty: RuleType::Atomic,
+                expr: box_tree!(Seq(Ident(String::from("a")), Opt(Ident(String::from("b"))))),
+            }]
+        };
+
+        assert_eq!(optimize(rules), optimized);
+    }
+
+    #[test]
+    fn impossible_common_sequence() {
+        let rules = {
+            use crate::ast::Expr::*;
+            vec![Rule {
+                name: "rule".to_owned(),
+                ty: RuleType::Silent,
+                expr: box_tree!(Choice(
+                    Ident(String::from("a")),
+                    Seq(Ident(String::from("a")), Ident(String::from("b")))
+                )),
+            }]
+        };
+        let optimized = {
+            use crate::optimizer::OptimizedExpr::*;
+            vec![OptimizedRule {
+                name: "rule".to_owned(),
+                ty: RuleType::Silent,
+                expr: box_tree!(Ident(String::from("a"))),
+            }]
+        };
+
+        assert_eq!(optimize(rules), optimized);
+    }
+
+    #[test]
+    fn lister() {
+        let rules = {
+            use crate::ast::Expr::*;
+            vec![Rule {
+                name: "rule".to_owned(),
+                ty: RuleType::Silent,
+                expr: box_tree!(Seq(
+                    Rep(Seq(Ident(String::from("a")), Ident(String::from("b")))),
+                    Ident(String::from("a"))
+                )),
+            }]
+        };
+        let optimized = {
+            use crate::optimizer::OptimizedExpr::*;
+            vec![OptimizedRule {
+                name: "rule".to_owned(),
+                ty: RuleType::Silent,
+                expr: box_tree!(Seq(
+                    Ident(String::from("a")),
+                    Rep(Seq(Ident(String::from("b")), Ident(String::from("a"))))
+                )),
+            }]
+        };
+
+        assert_eq!(optimize(rules), optimized);
+    }
 }
diff --git a/src/optimizer/restorer.rs b/src/optimizer/restorer.rs
index 34a710e..e128e03 100644
--- a/src/optimizer/restorer.rs
+++ b/src/optimizer/restorer.rs
@@ -8,19 +8,15 @@
 // modified, or distributed except according to those terms.
 use std::collections::HashMap;
 
-use optimizer::*;
+use crate::optimizer::*;
 
 pub fn restore_on_err(
     rule: OptimizedRule,
     rules: &HashMap<String, OptimizedExpr>,
 ) -> OptimizedRule {
-    match rule {
-        OptimizedRule { name, ty, expr } => {
-            let expr = expr.map_bottom_up(|expr| wrap_branching_exprs(expr, rules));
-
-            OptimizedRule { name, ty, expr }
-        }
-    }
+    let OptimizedRule { name, ty, expr } = rule;
+    let expr = expr.map_bottom_up(|expr| wrap_branching_exprs(expr, rules));
+    OptimizedRule { name, ty, expr }
 }
 
 fn wrap_branching_exprs(
@@ -96,7 +92,7 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use optimizer::OptimizedExpr::*;
+    use crate::optimizer::OptimizedExpr::*;
 
     #[test]
     fn restore_no_stack_children() {
diff --git a/src/optimizer/rotater.rs b/src/optimizer/rotater.rs
index 3588738..7a7d8fb 100644
--- a/src/optimizer/rotater.rs
+++ b/src/optimizer/rotater.rs
@@ -7,7 +7,7 @@
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
 
-use ast::*;
+use crate::ast::*;
 
 pub fn rotate(rule: Rule) -> Rule {
     fn rotate_internal(expr: Expr) -> Expr {
@@ -35,11 +35,10 @@
         }
     }
 
-    match rule {
-        Rule { name, ty, expr } => Rule {
-            name,
-            ty,
-            expr: expr.map_top_down(rotate_internal),
-        },
+    let Rule { name, ty, expr } = rule;
+    Rule {
+        name,
+        ty,
+        expr: expr.map_top_down(rotate_internal),
     }
 }
diff --git a/src/optimizer/skipper.rs b/src/optimizer/skipper.rs
index f55edc0..40bc5a1 100644
--- a/src/optimizer/skipper.rs
+++ b/src/optimizer/skipper.rs
@@ -7,7 +7,7 @@
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
 
-use ast::*;
+use crate::ast::*;
 
 pub fn skip(rule: Rule) -> Rule {
     fn populate_choices(expr: Expr, mut choices: Vec<String>) -> Option<Expr> {
@@ -28,30 +28,29 @@
         }
     }
 
-    match rule {
-        Rule { name, ty, expr } => Rule {
-            name,
-            ty,
-            expr: if ty == RuleType::Atomic {
-                expr.map_top_down(|expr| {
-                    // TODO: Use box syntax when it gets stabilized.
-                    if let Expr::Rep(expr) = expr.clone() {
-                        if let Expr::Seq(lhs, rhs) = *expr.clone() {
-                            if let (Expr::NegPred(expr), Expr::Ident(ident)) = (*lhs, *rhs) {
-                                if ident == "ANY" {
-                                    if let Some(expr) = populate_choices(*expr, vec![]) {
-                                        return expr;
-                                    }
+    let Rule { name, ty, expr } = rule;
+    Rule {
+        name,
+        ty,
+        expr: if ty == RuleType::Atomic {
+            expr.map_top_down(|expr| {
+                // TODO: Use box syntax when it gets stabilized.
+                if let Expr::Rep(expr) = expr.clone() {
+                    if let Expr::Seq(lhs, rhs) = *expr {
+                        if let (Expr::NegPred(expr), Expr::Ident(ident)) = (*lhs, *rhs) {
+                            if ident == "ANY" {
+                                if let Some(expr) = populate_choices(*expr, vec![]) {
+                                    return expr;
                                 }
                             }
                         }
-                    };
+                    }
+                };
 
-                    expr
-                })
-            } else {
                 expr
-            },
+            })
+        } else {
+            expr
         },
     }
 }
diff --git a/src/optimizer/unroller.rs b/src/optimizer/unroller.rs
index fff1733..e3c360d 100644
--- a/src/optimizer/unroller.rs
+++ b/src/optimizer/unroller.rs
@@ -7,61 +7,60 @@
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
 
-use ast::*;
+use crate::ast::*;
 
 pub fn unroll(rule: Rule) -> Rule {
-    match rule {
-        Rule { name, ty, expr } => Rule {
-            name,
-            ty,
-            expr: expr.map_bottom_up(|expr| match expr {
-                Expr::RepOnce(expr) => Expr::Seq(expr.clone(), Box::new(Expr::Rep(expr))),
-                Expr::RepExact(expr, num) => (1..num + 1)
-                    .map(|_| *expr.clone())
-                    .rev()
-                    .fold(None, |rep, expr| match rep {
-                        None => Some(expr),
-                        Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
-                    })
-                    .unwrap(),
-                Expr::RepMin(expr, min) => (1..min + 2)
-                    .map(|i| {
-                        if i <= min {
-                            *expr.clone()
-                        } else {
-                            Expr::Rep(expr.clone())
-                        }
-                    })
-                    .rev()
-                    .fold(None, |rep, expr| match rep {
-                        None => Some(expr),
-                        Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
-                    })
-                    .unwrap(),
-                Expr::RepMax(expr, max) => (1..max + 1)
-                    .map(|_| Expr::Opt(expr.clone()))
-                    .rev()
-                    .fold(None, |rep, expr| match rep {
-                        None => Some(expr),
-                        Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
-                    })
-                    .unwrap(),
-                Expr::RepMinMax(expr, min, max) => (1..max + 1)
-                    .map(|i| {
-                        if i <= min {
-                            *expr.clone()
-                        } else {
-                            Expr::Opt(expr.clone())
-                        }
-                    })
-                    .rev()
-                    .fold(None, |rep, expr| match rep {
-                        None => Some(expr),
-                        Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
-                    })
-                    .unwrap(),
-                expr => expr,
-            }),
-        },
+    let Rule { name, ty, expr } = rule;
+    Rule {
+        name,
+        ty,
+        expr: expr.map_bottom_up(|expr| match expr {
+            Expr::RepOnce(expr) => Expr::Seq(expr.clone(), Box::new(Expr::Rep(expr))),
+            Expr::RepExact(expr, num) => (1..num + 1)
+                .map(|_| *expr.clone())
+                .rev()
+                .fold(None, |rep, expr| match rep {
+                    None => Some(expr),
+                    Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
+                })
+                .unwrap(),
+            Expr::RepMin(expr, min) => (1..min + 2)
+                .map(|i| {
+                    if i <= min {
+                        *expr.clone()
+                    } else {
+                        Expr::Rep(expr.clone())
+                    }
+                })
+                .rev()
+                .fold(None, |rep, expr| match rep {
+                    None => Some(expr),
+                    Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
+                })
+                .unwrap(),
+            Expr::RepMax(expr, max) => (1..max + 1)
+                .map(|_| Expr::Opt(expr.clone()))
+                .rev()
+                .fold(None, |rep, expr| match rep {
+                    None => Some(expr),
+                    Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
+                })
+                .unwrap(),
+            Expr::RepMinMax(expr, min, max) => (1..max + 1)
+                .map(|i| {
+                    if i <= min {
+                        *expr.clone()
+                    } else {
+                        Expr::Opt(expr.clone())
+                    }
+                })
+                .rev()
+                .fold(None, |rep, expr| match rep {
+                    None => Some(expr),
+                    Some(rep) => Some(Expr::Seq(Box::new(expr), Box::new(rep))),
+                })
+                .unwrap(),
+            expr => expr,
+        }),
     }
 }
diff --git a/src/parser.rs b/src/parser.rs
index 5f6f3b3..eb957a1 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -7,38 +7,62 @@
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
 
+//! Types and helpers for the pest's own grammar parser.
+
 use std::char;
 use std::iter::Peekable;
 
 use pest::error::{Error, ErrorVariant};
 use pest::iterators::{Pair, Pairs};
-use pest::prec_climber::{Assoc, Operator, PrecClimber};
+use pest::pratt_parser::{Assoc, Op, PrattParser};
 use pest::{Parser, Span};
 
-use ast::{Expr, Rule as AstRule, RuleType};
-use validator;
+use crate::ast::{Expr, Rule as AstRule, RuleType};
+use crate::validator;
 
-include!("grammar.rs");
+/// TODO: fix the generator to at least add explicit lifetimes
+#[allow(
+    missing_docs,
+    unused_attributes,
+    elided_lifetimes_in_paths,
+    unused_qualifications
+)]
+mod grammar {
+    include!("grammar.rs");
+}
 
-pub fn parse(rule: Rule, data: &str) -> Result<Pairs<Rule>, Error<Rule>> {
+pub use self::grammar::*;
+
+/// A helper that will parse using the pest grammar
+#[allow(clippy::perf)]
+pub fn parse(rule: Rule, data: &str) -> Result<Pairs<'_, Rule>, Error<Rule>> {
     PestParser::parse(rule, data)
 }
 
+/// The pest grammar rule
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct ParserRule<'i> {
+    /// The rule's name
     pub name: String,
+    /// The rule's span
     pub span: Span<'i>,
+    /// The rule's type
     pub ty: RuleType,
+    /// The rule's parser node
     pub node: ParserNode<'i>,
 }
 
+/// The pest grammar node
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct ParserNode<'i> {
+    /// The node's expression
     pub expr: ParserExpr<'i>,
+    /// The node's span
     pub span: Span<'i>,
 }
 
 impl<'i> ParserNode<'i> {
+    /// will remove nodes that do not match `f`
     pub fn filter_map_top_down<F, T>(self, mut f: F) -> Vec<T>
     where
         F: FnMut(ParserNode<'i>) -> Option<T>,
@@ -103,38 +127,52 @@
     }
 }
 
+/// All possible parser expressions
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum ParserExpr<'i> {
+    /// Matches an exact string, e.g. `"a"`
     Str(String),
+    /// Matches an exact string, case insensitively (ASCII only), e.g. `^"a"`
     Insens(String),
+    /// Matches one character in the range, e.g. `'a'..'z'`
     Range(String, String),
+    /// Matches the rule with the given name, e.g. `a`
     Ident(String),
+    /// Matches a custom part of the stack, e.g. `PEEK[..]`
     PeekSlice(i32, Option<i32>),
+    /// Positive lookahead; matches expression without making progress, e.g. `&e`
     PosPred(Box<ParserNode<'i>>),
+    /// Negative lookahead; matches if expression doesn't match, without making progress, e.g. `!e`
     NegPred(Box<ParserNode<'i>>),
+    /// Matches a sequence of two expressions, e.g. `e1 ~ e2`
     Seq(Box<ParserNode<'i>>, Box<ParserNode<'i>>),
+    /// Matches either of two expressions, e.g. `e1 | e2`
     Choice(Box<ParserNode<'i>>, Box<ParserNode<'i>>),
+    /// Optionally matches an expression, e.g. `e?`
     Opt(Box<ParserNode<'i>>),
+    /// Matches an expression zero or more times, e.g. `e*`
     Rep(Box<ParserNode<'i>>),
+    /// Matches an expression one or more times, e.g. `e+`
     RepOnce(Box<ParserNode<'i>>),
+    /// Matches an expression an exact number of times, e.g. `e{n}`
     RepExact(Box<ParserNode<'i>>, u32),
+    /// Matches an expression at least a number of times, e.g. `e{n,}`
     RepMin(Box<ParserNode<'i>>, u32),
+    /// Matches an expression at most a number of times, e.g. `e{,n}`
     RepMax(Box<ParserNode<'i>>, u32),
+    /// Matches an expression a number of times within a range, e.g. `e{m, n}`
     RepMinMax(Box<ParserNode<'i>>, u32, u32),
+    /// Matches an expression and pushes it to the stack, e.g. `push(e)`
     Push(Box<ParserNode<'i>>),
 }
 
-fn convert_rule(rule: ParserRule) -> AstRule {
-    match rule {
-        ParserRule { name, ty, node, .. } => {
-            let expr = convert_node(node);
-
-            AstRule { name, ty, expr }
-        }
-    }
+fn convert_rule(rule: ParserRule<'_>) -> AstRule {
+    let ParserRule { name, ty, node, .. } = rule;
+    let expr = convert_node(node);
+    AstRule { name, ty, expr }
 }
 
-fn convert_node(node: ParserNode) -> Expr {
+fn convert_node(node: ParserNode<'_>) -> Expr {
     match node.expr {
         ParserExpr::Str(string) => Expr::Str(string),
         ParserExpr::Insens(string) => Expr::Insens(string),
@@ -164,7 +202,8 @@
     }
 }
 
-pub fn consume_rules(pairs: Pairs<Rule>) -> Result<Vec<AstRule>, Vec<Error<Rule>>> {
+/// Converts a parser's result (`Pairs`) to an AST
+pub fn consume_rules(pairs: Pairs<'_, Rule>) -> Result<Vec<AstRule>, Vec<Error<Rule>>> {
     let rules = consume_rules_with_spans(pairs)?;
     let errors = validator::validate_ast(&rules);
     if errors.is_empty() {
@@ -174,16 +213,58 @@
     }
 }
 
-fn consume_rules_with_spans<'i>(
-    pairs: Pairs<'i, Rule>,
-) -> Result<Vec<ParserRule<'i>>, Vec<Error<Rule>>> {
-    let climber = PrecClimber::new(vec![
-        Operator::new(Rule::choice_operator, Assoc::Left),
-        Operator::new(Rule::sequence_operator, Assoc::Left),
-    ]);
+/// A helper function to rename verbose rules
+/// for the sake of better error messages
+#[inline]
+pub fn rename_meta_rule(rule: &Rule) -> String {
+    match *rule {
+        Rule::grammar_rule => "rule".to_owned(),
+        Rule::_push => "PUSH".to_owned(),
+        Rule::assignment_operator => "`=`".to_owned(),
+        Rule::silent_modifier => "`_`".to_owned(),
+        Rule::atomic_modifier => "`@`".to_owned(),
+        Rule::compound_atomic_modifier => "`$`".to_owned(),
+        Rule::non_atomic_modifier => "`!`".to_owned(),
+        Rule::opening_brace => "`{`".to_owned(),
+        Rule::closing_brace => "`}`".to_owned(),
+        Rule::opening_brack => "`[`".to_owned(),
+        Rule::closing_brack => "`]`".to_owned(),
+        Rule::opening_paren => "`(`".to_owned(),
+        Rule::positive_predicate_operator => "`&`".to_owned(),
+        Rule::negative_predicate_operator => "`!`".to_owned(),
+        Rule::sequence_operator => "`&`".to_owned(),
+        Rule::choice_operator => "`|`".to_owned(),
+        Rule::optional_operator => "`?`".to_owned(),
+        Rule::repeat_operator => "`*`".to_owned(),
+        Rule::repeat_once_operator => "`+`".to_owned(),
+        Rule::comma => "`,`".to_owned(),
+        Rule::closing_paren => "`)`".to_owned(),
+        Rule::quote => "`\"`".to_owned(),
+        Rule::insensitive_string => "`^`".to_owned(),
+        Rule::range_operator => "`..`".to_owned(),
+        Rule::single_quote => "`'`".to_owned(),
+        Rule::grammar_doc => "//!".to_owned(),
+        Rule::line_doc => "///".to_owned(),
+        other_rule => format!("{:?}", other_rule),
+    }
+}
+
+fn consume_rules_with_spans(
+    pairs: Pairs<'_, Rule>,
+) -> Result<Vec<ParserRule<'_>>, Vec<Error<Rule>>> {
+    let pratt = PrattParser::new()
+        .op(Op::infix(Rule::choice_operator, Assoc::Left))
+        .op(Op::infix(Rule::sequence_operator, Assoc::Left));
 
     pairs
         .filter(|pair| pair.as_rule() == Rule::grammar_rule)
+        .filter(|pair| {
+            // To ignore `grammar_rule > line_doc` pairs
+            let mut pairs = pair.clone().into_inner();
+            let pair = pairs.next().unwrap();
+
+            pair.as_rule() != Rule::line_doc
+        })
         .map(|pair| {
             let mut pairs = pair.into_inner().peekable();
 
@@ -206,7 +287,13 @@
 
             pairs.next().unwrap(); // opening_brace
 
-            let node = consume_expr(pairs.next().unwrap().into_inner().peekable(), &climber)?;
+            // skip initial infix operators
+            let mut inner_nodes = pairs.next().unwrap().into_inner().peekable();
+            if inner_nodes.peek().unwrap().as_rule() == Rule::choice_operator {
+                inner_nodes.next().unwrap();
+            }
+
+            let node = consume_expr(inner_nodes, &pratt)?;
 
             Ok(ParserRule {
                 name,
@@ -220,17 +307,17 @@
 
 fn consume_expr<'i>(
     pairs: Peekable<Pairs<'i, Rule>>,
-    climber: &PrecClimber<Rule>,
+    pratt: &PrattParser<Rule>,
 ) -> Result<ParserNode<'i>, Vec<Error<Rule>>> {
     fn unaries<'i>(
         mut pairs: Peekable<Pairs<'i, Rule>>,
-        climber: &PrecClimber<Rule>,
+        pratt: &PrattParser<Rule>,
     ) -> Result<ParserNode<'i>, Vec<Error<Rule>>> {
         let pair = pairs.next().unwrap();
 
         let node = match pair.as_rule() {
             Rule::opening_paren => {
-                let node = unaries(pairs, climber)?;
+                let node = unaries(pairs, pratt)?;
                 let end = node.span.end_pos();
 
                 ParserNode {
@@ -239,7 +326,7 @@
                 }
             }
             Rule::positive_predicate_operator => {
-                let node = unaries(pairs, climber)?;
+                let node = unaries(pairs, pratt)?;
                 let end = node.span.end_pos();
 
                 ParserNode {
@@ -248,7 +335,7 @@
                 }
             }
             Rule::negative_predicate_operator => {
-                let node = unaries(pairs, climber)?;
+                let node = unaries(pairs, pratt)?;
                 let end = node.span.end_pos();
 
                 ParserNode {
@@ -258,14 +345,14 @@
             }
             other_rule => {
                 let node = match other_rule {
-                    Rule::expression => consume_expr(pair.into_inner().peekable(), climber)?,
+                    Rule::expression => consume_expr(pair.into_inner().peekable(), pratt)?,
                     Rule::_push => {
                         let start = pair.clone().as_span().start_pos();
                         let mut pairs = pair.into_inner();
                         pairs.next().unwrap(); // opening_paren
                         let pair = pairs.next().unwrap();
 
-                        let node = consume_expr(pair.into_inner().peekable(), climber)?;
+                        let node = consume_expr(pair.into_inner().peekable(), pratt)?;
                         let end = node.span.end_pos();
 
                         ParserNode {
@@ -525,7 +612,7 @@
         Ok(node)
     }
 
-    let term = |pair: Pair<'i, Rule>| unaries(pair.into_inner().peekable(), climber);
+    let term = |pair: Pair<'i, Rule>| unaries(pair.into_inner().peekable(), pratt);
     let infix = |lhs: Result<ParserNode<'i>, Vec<Error<Rule>>>,
                  op: Pair<'i, Rule>,
                  rhs: Result<ParserNode<'i>, Vec<Error<Rule>>>| match op.as_rule() {
@@ -556,7 +643,7 @@
         _ => unreachable!(),
     };
 
-    climber.climb(pairs, term, infix)
+    pratt.map_primary(term).map_infix(infix).parse(pairs)
 }
 
 fn unescape(string: &str) -> Option<String> {
@@ -617,6 +704,8 @@
 
 #[cfg(test)]
 mod tests {
+    use std::convert::TryInto;
+
     use super::super::unwrap_or_report;
     use super::*;
 
@@ -1014,12 +1103,47 @@
     }
 
     #[test]
+    fn grammar_doc_and_line_doc() {
+        let input = "//! hello\n/// world\na = { \"a\" }";
+        parses_to! {
+            parser: PestParser,
+            input: input,
+            rule: Rule::grammar_rules,
+            tokens: [
+                grammar_doc(0, 9, [
+                    inner_doc(4, 9),
+                ]),
+                grammar_rule(10, 19, [
+                    line_doc(10, 19, [
+                        inner_doc(14, 19),
+                    ]),
+                ]),
+                grammar_rule(20, 31, [
+                    identifier(20, 21),
+                    assignment_operator(22, 23),
+                    opening_brace(24, 25),
+                    expression(26, 30, [
+                        term(26, 30, [
+                            string(26, 29, [
+                                quote(26, 27),
+                                inner_str(27, 28),
+                                quote(28, 29)
+                            ])
+                        ])
+                    ]),
+                    closing_brace(30, 31),
+                ])
+            ]
+        };
+    }
+
+    #[test]
     fn wrong_identifier() {
         fails_with! {
             parser: PestParser,
             input: "0",
             rule: Rule::grammar_rules,
-            positives: vec![Rule::identifier],
+            positives: vec![Rule::grammar_rule, Rule::grammar_doc],
             negatives: vec![],
             pos: 0
         };
@@ -1073,7 +1197,7 @@
             parser: PestParser,
             input: "a = {}",
             rule: Rule::grammar_rules,
-            positives: vec![Rule::term],
+            positives: vec![Rule::expression],
             negatives: vec![],
             pos: 5
         };
@@ -1092,6 +1216,18 @@
     }
 
     #[test]
+    fn incorrect_prefix() {
+        fails_with! {
+            parser: PestParser,
+            input: "a = { ~ b}",
+            rule: Rule::grammar_rules,
+            positives: vec![Rule::expression],
+            negatives: vec![],
+            pos: 6
+        };
+    }
+
+    #[test]
     fn wrong_op() {
         fails_with! {
             parser: PestParser,
@@ -1223,12 +1359,15 @@
 
     #[test]
     fn ast() {
-        let input =
-            "rule = _{ a{1} ~ \"a\"{3,} ~ b{, 2} ~ \"b\"{1, 2} | !(^\"c\" | PUSH('d'..'e'))?* }";
+        let input = r##"
+        /// This is line comment
+        /// This is rule
+        rule = _{ a{1} ~ "a"{3,} ~ b{, 2} ~ "b"{1, 2} | !(^"c" | PUSH('d'..'e'))?* }
+        "##;
 
         let pairs = PestParser::parse(Rule::grammar_rules, input).unwrap();
         let ast = consume_rules_with_spans(pairs).unwrap();
-        let ast: Vec<_> = ast.into_iter().map(|rule| convert_rule(rule)).collect();
+        let ast: Vec<_> = ast.into_iter().map(convert_rule).collect();
 
         assert_eq!(
             ast,
@@ -1266,7 +1405,7 @@
 
         let pairs = PestParser::parse(Rule::grammar_rules, input).unwrap();
         let ast = consume_rules_with_spans(pairs).unwrap();
-        let ast: Vec<_> = ast.into_iter().map(|rule| convert_rule(rule)).collect();
+        let ast: Vec<_> = ast.into_iter().map(convert_rule).collect();
 
         assert_eq!(
             ast,
@@ -1276,7 +1415,7 @@
                 expr: Expr::Seq(
                     Box::new(Expr::PeekSlice(-4, None)),
                     Box::new(Expr::PeekSlice(0, Some(3))),
-                )
+                ),
             }],
         );
     }
@@ -1485,4 +1624,45 @@
 
         assert_eq!(unescape(string), None);
     }
+
+    #[test]
+    fn handles_deep_nesting() {
+        let sample1 = include_str!(concat!(
+            env!("CARGO_MANIFEST_DIR"),
+            "/resources/test/fuzzsample1.grammar"
+        ));
+        let sample2 = include_str!(concat!(
+            env!("CARGO_MANIFEST_DIR"),
+            "/resources/test/fuzzsample2.grammar"
+        ));
+        let sample3 = include_str!(concat!(
+            env!("CARGO_MANIFEST_DIR"),
+            "/resources/test/fuzzsample3.grammar"
+        ));
+        let sample4 = include_str!(concat!(
+            env!("CARGO_MANIFEST_DIR"),
+            "/resources/test/fuzzsample4.grammar"
+        ));
+        let sample5 = include_str!(concat!(
+            env!("CARGO_MANIFEST_DIR"),
+            "/resources/test/fuzzsample5.grammar"
+        ));
+        const ERROR: &str = "call limit reached";
+        pest::set_call_limit(Some(5_000usize.try_into().unwrap()));
+        let s1 = parse(Rule::grammar_rules, sample1);
+        assert!(s1.is_err());
+        assert_eq!(s1.unwrap_err().variant.message(), ERROR);
+        let s2 = parse(Rule::grammar_rules, sample2);
+        assert!(s2.is_err());
+        assert_eq!(s2.unwrap_err().variant.message(), ERROR);
+        let s3 = parse(Rule::grammar_rules, sample3);
+        assert!(s3.is_err());
+        assert_eq!(s3.unwrap_err().variant.message(), ERROR);
+        let s4 = parse(Rule::grammar_rules, sample4);
+        assert!(s4.is_err());
+        assert_eq!(s4.unwrap_err().variant.message(), ERROR);
+        let s5 = parse(Rule::grammar_rules, sample5);
+        assert!(s5.is_err());
+        assert_eq!(s5.unwrap_err().variant.message(), ERROR);
+    }
 }
diff --git a/src/validator.rs b/src/validator.rs
index a358dbe..d9c2ed3 100644
--- a/src/validator.rs
+++ b/src/validator.rs
@@ -7,110 +7,85 @@
 // option. All files in the project carrying such notice may not be copied,
 // modified, or distributed except according to those terms.
 
+//! Helpers for validating pest grammars that could help with debugging
+//! and provide a more user-friendly error message.
+
+use once_cell::sync::Lazy;
 use std::collections::{HashMap, HashSet};
 
 use pest::error::{Error, ErrorVariant, InputLocation};
 use pest::iterators::Pairs;
+use pest::unicode::unicode_property_names;
 use pest::Span;
 
-use parser::{ParserExpr, ParserNode, ParserRule, Rule};
-use UNICODE_PROPERTY_NAMES;
+use crate::parser::{ParserExpr, ParserNode, ParserRule, Rule};
 
-#[allow(clippy::needless_pass_by_value)]
-pub fn validate_pairs<'i>(pairs: Pairs<'i, Rule>) -> Result<Vec<&'i str>, Vec<Error<Rule>>> {
-    let mut rust_keywords = HashSet::new();
-    rust_keywords.insert("abstract");
-    rust_keywords.insert("alignof");
-    rust_keywords.insert("as");
-    rust_keywords.insert("become");
-    rust_keywords.insert("box");
-    rust_keywords.insert("break");
-    rust_keywords.insert("const");
-    rust_keywords.insert("continue");
-    rust_keywords.insert("crate");
-    rust_keywords.insert("do");
-    rust_keywords.insert("else");
-    rust_keywords.insert("enum");
-    rust_keywords.insert("extern");
-    rust_keywords.insert("false");
-    rust_keywords.insert("final");
-    rust_keywords.insert("fn");
-    rust_keywords.insert("for");
-    rust_keywords.insert("if");
-    rust_keywords.insert("impl");
-    rust_keywords.insert("in");
-    rust_keywords.insert("let");
-    rust_keywords.insert("loop");
-    rust_keywords.insert("macro");
-    rust_keywords.insert("match");
-    rust_keywords.insert("mod");
-    rust_keywords.insert("move");
-    rust_keywords.insert("mut");
-    rust_keywords.insert("offsetof");
-    rust_keywords.insert("override");
-    rust_keywords.insert("priv");
-    rust_keywords.insert("proc");
-    rust_keywords.insert("pure");
-    rust_keywords.insert("pub");
-    rust_keywords.insert("ref");
-    rust_keywords.insert("return");
-    rust_keywords.insert("Self");
-    rust_keywords.insert("self");
-    rust_keywords.insert("sizeof");
-    rust_keywords.insert("static");
-    rust_keywords.insert("struct");
-    rust_keywords.insert("super");
-    rust_keywords.insert("trait");
-    rust_keywords.insert("true");
-    rust_keywords.insert("type");
-    rust_keywords.insert("typeof");
-    rust_keywords.insert("unsafe");
-    rust_keywords.insert("unsized");
-    rust_keywords.insert("use");
-    rust_keywords.insert("virtual");
-    rust_keywords.insert("where");
-    rust_keywords.insert("while");
-    rust_keywords.insert("yield");
+static RUST_KEYWORDS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
+    [
+        "abstract", "alignof", "as", "become", "box", "break", "const", "continue", "crate", "do",
+        "else", "enum", "extern", "false", "final", "fn", "for", "if", "impl", "in", "let", "loop",
+        "macro", "match", "mod", "move", "mut", "offsetof", "override", "priv", "proc", "pure",
+        "pub", "ref", "return", "Self", "self", "sizeof", "static", "struct", "super", "trait",
+        "true", "type", "typeof", "unsafe", "unsized", "use", "virtual", "where", "while", "yield",
+    ]
+    .iter()
+    .cloned()
+    .collect()
+});
 
-    let mut pest_keywords = HashSet::new();
-    pest_keywords.insert("_");
-    pest_keywords.insert("ANY");
-    pest_keywords.insert("DROP");
-    pest_keywords.insert("EOI");
-    pest_keywords.insert("PEEK");
-    pest_keywords.insert("PEEK_ALL");
-    pest_keywords.insert("POP");
-    pest_keywords.insert("POP_ALL");
-    pest_keywords.insert("PUSH");
-    pest_keywords.insert("SOI");
+static PEST_KEYWORDS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
+    [
+        "_", "ANY", "DROP", "EOI", "PEEK", "PEEK_ALL", "POP", "POP_ALL", "PUSH", "SOI",
+    ]
+    .iter()
+    .cloned()
+    .collect()
+});
 
-    let mut builtins = HashSet::new();
-    builtins.insert("ANY");
-    builtins.insert("DROP");
-    builtins.insert("EOI");
-    builtins.insert("PEEK");
-    builtins.insert("PEEK_ALL");
-    builtins.insert("POP");
-    builtins.insert("POP_ALL");
-    builtins.insert("SOI");
-    builtins.insert("ASCII_DIGIT");
-    builtins.insert("ASCII_NONZERO_DIGIT");
-    builtins.insert("ASCII_BIN_DIGIT");
-    builtins.insert("ASCII_OCT_DIGIT");
-    builtins.insert("ASCII_HEX_DIGIT");
-    builtins.insert("ASCII_ALPHA_LOWER");
-    builtins.insert("ASCII_ALPHA_UPPER");
-    builtins.insert("ASCII_ALPHA");
-    builtins.insert("ASCII_ALPHANUMERIC");
-    builtins.insert("ASCII");
-    builtins.insert("NEWLINE");
-    builtins.extend(UNICODE_PROPERTY_NAMES);
+static BUILTINS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
+    [
+        "ANY",
+        "DROP",
+        "EOI",
+        "PEEK",
+        "PEEK_ALL",
+        "POP",
+        "POP_ALL",
+        "SOI",
+        "ASCII_DIGIT",
+        "ASCII_NONZERO_DIGIT",
+        "ASCII_BIN_DIGIT",
+        "ASCII_OCT_DIGIT",
+        "ASCII_HEX_DIGIT",
+        "ASCII_ALPHA_LOWER",
+        "ASCII_ALPHA_UPPER",
+        "ASCII_ALPHA",
+        "ASCII_ALPHANUMERIC",
+        "ASCII",
+        "NEWLINE",
+    ]
+    .iter()
+    .cloned()
+    .chain(unicode_property_names())
+    .collect::<HashSet<&str>>()
+});
 
+/// It checks the parsed grammar for common mistakes:
+/// - using Pest keywords
+/// - duplicate rules
+/// - undefined rules
+///
+/// It returns a `Result` with a `Vec` of `Error`s if any of the above is found.
+/// If no errors are found, it returns the vector of names of used builtin rules.
+pub fn validate_pairs(pairs: Pairs<'_, Rule>) -> Result<Vec<&str>, Vec<Error<Rule>>> {
     let definitions: Vec<_> = pairs
         .clone()
         .filter(|pair| pair.as_rule() == Rule::grammar_rule)
-        .map(|pair| pair.into_inner().next().unwrap().as_span())
+        .map(|pair| pair.into_inner().next().unwrap())
+        .filter(|pair| pair.as_rule() != Rule::line_doc)
+        .map(|pair| pair.as_span())
         .collect();
+
     let called_rules: Vec<_> = pairs
         .clone()
         .filter(|pair| pair.as_rule() == Rule::grammar_rule)
@@ -125,10 +100,9 @@
 
     let mut errors = vec![];
 
-    errors.extend(validate_rust_keywords(&definitions, &rust_keywords));
-    errors.extend(validate_pest_keywords(&definitions, &pest_keywords));
+    errors.extend(validate_pest_keywords(&definitions));
     errors.extend(validate_already_defined(&definitions));
-    errors.extend(validate_undefined(&definitions, &called_rules, &builtins));
+    errors.extend(validate_undefined(&definitions, &called_rules));
 
     if !errors.is_empty() {
         return Err(errors);
@@ -142,22 +116,21 @@
     Ok(defaults.cloned().collect())
 }
 
-#[allow(clippy::implicit_hasher, clippy::ptr_arg)]
-pub fn validate_rust_keywords<'i>(
-    definitions: &Vec<Span<'i>>,
-    rust_keywords: &HashSet<&str>,
-) -> Vec<Error<Rule>> {
+/// Validates that the given `definitions` do not contain any Rust keywords.
+#[allow(clippy::ptr_arg)]
+#[deprecated = "Rust keywords are no longer restricted from the pest grammar"]
+pub fn validate_rust_keywords(definitions: &Vec<Span<'_>>) -> Vec<Error<Rule>> {
     let mut errors = vec![];
 
     for definition in definitions {
         let name = definition.as_str();
 
-        if rust_keywords.contains(name) {
+        if RUST_KEYWORDS.contains(name) {
             errors.push(Error::new_from_span(
                 ErrorVariant::CustomError {
                     message: format!("{} is a rust keyword", name),
                 },
-                definition.clone(),
+                *definition,
             ))
         }
     }
@@ -165,22 +138,20 @@
     errors
 }
 
-#[allow(clippy::implicit_hasher, clippy::ptr_arg)]
-pub fn validate_pest_keywords<'i>(
-    definitions: &Vec<Span<'i>>,
-    pest_keywords: &HashSet<&str>,
-) -> Vec<Error<Rule>> {
+/// Validates that the given `definitions` do not contain any Pest keywords.
+#[allow(clippy::ptr_arg)]
+pub fn validate_pest_keywords(definitions: &Vec<Span<'_>>) -> Vec<Error<Rule>> {
     let mut errors = vec![];
 
     for definition in definitions {
         let name = definition.as_str();
 
-        if pest_keywords.contains(name) {
+        if PEST_KEYWORDS.contains(name) {
             errors.push(Error::new_from_span(
                 ErrorVariant::CustomError {
                     message: format!("{} is a pest keyword", name),
                 },
-                definition.clone(),
+                *definition,
             ))
         }
     }
@@ -188,8 +159,9 @@
     errors
 }
 
+/// Validates that the given `definitions` do not contain any duplicate rules.
 #[allow(clippy::ptr_arg)]
-pub fn validate_already_defined<'i>(definitions: &Vec<Span<'i>>) -> Vec<Error<Rule>> {
+pub fn validate_already_defined(definitions: &Vec<Span<'_>>) -> Vec<Error<Rule>> {
     let mut errors = vec![];
     let mut defined = HashSet::new();
 
@@ -201,7 +173,7 @@
                 ErrorVariant::CustomError {
                     message: format!("rule {} already defined", name),
                 },
-                definition.clone(),
+                *definition,
             ))
         } else {
             defined.insert(name);
@@ -211,11 +183,11 @@
     errors
 }
 
-#[allow(clippy::implicit_hasher, clippy::ptr_arg)]
+/// Validates that the given `definitions` do not contain any undefined rules.
+#[allow(clippy::ptr_arg)]
 pub fn validate_undefined<'i>(
     definitions: &Vec<Span<'i>>,
     called_rules: &Vec<Span<'i>>,
-    builtins: &HashSet<&str>,
 ) -> Vec<Error<Rule>> {
     let mut errors = vec![];
     let definitions: HashSet<_> = definitions.iter().map(|span| span.as_str()).collect();
@@ -223,12 +195,12 @@
     for rule in called_rules {
         let name = rule.as_str();
 
-        if !definitions.contains(name) && !builtins.contains(name) {
+        if !definitions.contains(name) && !BUILTINS.contains(name) {
             errors.push(Error::new_from_span(
                 ErrorVariant::CustomError {
                     message: format!("rule {} is undefined", name),
                 },
-                rule.clone(),
+                *rule,
             ))
         }
     }
@@ -236,6 +208,10 @@
     errors
 }
 
+/// Validates the abstract syntax tree for common mistakes:
+/// - infinite repetitions
+/// - choices that cannot be reached
+/// - left recursion
 #[allow(clippy::ptr_arg)]
 pub fn validate_ast<'a, 'i: 'a>(rules: &'a Vec<ParserRule<'i>>) -> Vec<Error<Rule>> {
     let mut errors = vec![];
@@ -259,7 +235,7 @@
     trace: &mut Vec<String>,
 ) -> bool {
     match *expr {
-        ParserExpr::Str(ref string) => string == "",
+        ParserExpr::Str(ref string) => string.is_empty(),
         ParserExpr::Ident(ref ident) => {
             if ident == "soi" || ident == "eoi" {
                 return true;
@@ -297,7 +273,7 @@
     trace: &mut Vec<String>,
 ) -> bool {
     match *expr {
-        ParserExpr::Str(ref string) => string == "",
+        ParserExpr::Str(ref string) => string.is_empty(),
         ParserExpr::Ident(ref ident) => {
             if !trace.contains(ident) {
                 if let Some(node) = rules.get(ident) {
@@ -342,7 +318,7 @@
                                      infinitely"
                                         .to_owned()
                             },
-                            node.span.clone()
+                            node.span
                         ))
                     } else if is_non_progressing(&other.expr, &map, &mut vec![]) {
                         Some(Error::new_from_span(
@@ -352,7 +328,7 @@
                                      infinitely"
                                         .to_owned(),
                             },
-                            node.span.clone()
+                            node.span
                         ))
                     } else {
                         None
@@ -389,7 +365,7 @@
                                     "expression cannot fail; following choices cannot be reached"
                                         .to_owned(),
                             },
-                            node.span.clone(),
+                            node.span,
                         ))
                     } else {
                         None
@@ -419,7 +395,7 @@
                                 &rule.name
                             ),
                         },
-                        rule.node.span.clone(),
+                        rule.node.span,
                     ))
                 } else if is_non_progressing(&rule.node.expr, &map, &mut vec![]) {
                     Some(Error::new_from_span(
@@ -429,7 +405,7 @@
                                 &rule.name
                             ),
                         },
-                        rule.node.span.clone(),
+                        rule.node.span,
                     ))
                 } else {
                     None
@@ -449,7 +425,6 @@
     rules.iter().map(|r| (r.name.clone(), &r.node)).collect()
 }
 
-#[allow(clippy::needless_pass_by_value)]
 fn left_recursion<'a, 'i: 'a>(rules: HashMap<String, &'a ParserNode<'i>>) -> Vec<Error<Rule>> {
     fn check_expr<'a, 'i: 'a>(
         node: &'a ParserNode<'i>,
@@ -469,13 +444,13 @@
                     return Some(Error::new_from_span(
                         ErrorVariant::CustomError {
                             message: format!(
-                                "rule {} is left-recursive ({}); pest::prec_climber might be useful \
+                                "rule {} is left-recursive ({}); pest::pratt_parser might be useful \
                                  in this case",
                                 node.span.as_str(),
                                 chain
                             )
                         },
-                        node.span.clone()
+                        node.span
                     ));
                 }
 
@@ -499,22 +474,22 @@
                 }
             }
             ParserExpr::Choice(ref lhs, ref rhs) => {
-                check_expr(&lhs, rules, trace).or_else(|| check_expr(&rhs, rules, trace))
+                check_expr(lhs, rules, trace).or_else(|| check_expr(rhs, rules, trace))
             }
-            ParserExpr::Rep(ref node) => check_expr(&node, rules, trace),
-            ParserExpr::RepOnce(ref node) => check_expr(&node, rules, trace),
-            ParserExpr::Opt(ref node) => check_expr(&node, rules, trace),
-            ParserExpr::PosPred(ref node) => check_expr(&node, rules, trace),
-            ParserExpr::NegPred(ref node) => check_expr(&node, rules, trace),
-            ParserExpr::Push(ref node) => check_expr(&node, rules, trace),
+            ParserExpr::Rep(ref node) => check_expr(node, rules, trace),
+            ParserExpr::RepOnce(ref node) => check_expr(node, rules, trace),
+            ParserExpr::Opt(ref node) => check_expr(node, rules, trace),
+            ParserExpr::PosPred(ref node) => check_expr(node, rules, trace),
+            ParserExpr::NegPred(ref node) => check_expr(node, rules, trace),
+            ParserExpr::Push(ref node) => check_expr(node, rules, trace),
             _ => None,
         }
     }
 
     let mut errors = vec![];
 
-    for (ref name, ref node) in &rules {
-        let name = (*name).clone();
+    for (name, node) in &rules {
+        let name = name.clone();
 
         if let Some(error) = check_expr(node, &rules, &mut vec![name]) {
             errors.push(error);
@@ -536,22 +511,6 @@
 
  --> 1:1
   |
-1 | let = { \"a\" }
-  | ^-^
-  |
-  = let is a rust keyword")]
-    fn rust_keyword() {
-        let input = "let = { \"a\" }";
-        unwrap_or_report(validate_pairs(
-            PestParser::parse(Rule::grammar_rules, input).unwrap(),
-        ));
-    }
-
-    #[test]
-    #[should_panic(expected = "grammar error
-
- --> 1:1
-  |
 1 | ANY = { \"a\" }
   | ^-^
   |
@@ -723,7 +682,7 @@
 1 | a = { a }
   |       ^
   |
-  = rule a is left-recursive (a -> a); pest::prec_climber might be useful in this case")]
+  = rule a is left-recursive (a -> a); pest::pratt_parser might be useful in this case")]
     fn simple_left_recursion() {
         let input = "a = { a }";
         unwrap_or_report(consume_rules(
@@ -739,14 +698,14 @@
 1 | a = { b } b = { a }
   |       ^
   |
-  = rule b is left-recursive (b -> a -> b); pest::prec_climber might be useful in this case
+  = rule b is left-recursive (b -> a -> b); pest::pratt_parser might be useful in this case
 
  --> 1:17
   |
 1 | a = { b } b = { a }
   |                 ^
   |
-  = rule a is left-recursive (a -> b -> a); pest::prec_climber might be useful in this case")]
+  = rule a is left-recursive (a -> b -> a); pest::pratt_parser might be useful in this case")]
     fn indirect_left_recursion() {
         let input = "a = { b } b = { a }";
         unwrap_or_report(consume_rules(
@@ -762,7 +721,7 @@
 1 | a = { \"\" ~ \"a\"? ~ \"a\"* ~ (\"a\" | \"\") ~ a }
   |                                       ^
   |
-  = rule a is left-recursive (a -> a); pest::prec_climber might be useful in this case")]
+  = rule a is left-recursive (a -> a); pest::pratt_parser might be useful in this case")]
     fn non_failing_left_recursion() {
         let input = "a = { \"\" ~ \"a\"? ~ \"a\"* ~ (\"a\" | \"\") ~ a }";
         unwrap_or_report(consume_rules(
@@ -778,7 +737,7 @@
 1 | a = { \"a\" | a }
   |             ^
   |
-  = rule a is left-recursive (a -> a); pest::prec_climber might be useful in this case")]
+  = rule a is left-recursive (a -> a); pest::pratt_parser might be useful in this case")]
     fn non_primary_choice_left_recursion() {
         let input = "a = { \"a\" | a }";
         unwrap_or_report(consume_rules(