diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 05:06:25 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 05:06:25 +0000 |
commit | 731e13bfb28c0f264b43b85ec57df665f6b06740 (patch) | |
tree | 321a8f5a477532972161b16045110b5bfb16802a | |
parent | bdac48d64a2741c9dbdfcbb42fa941fccb3cd4b1 (diff) | |
parent | c8408ae39045367684a36cf6536046ac8ce6e7cd (diff) | |
download | pest_meta-android14-mainline-os-statsd-release.tar.gz |
Snap for 10453563 from c8408ae39045367684a36cf6536046ac8ce6e7cd to mainline-os-statsd-releaseaml_sta_341710000aml_sta_341615000aml_sta_341511040aml_sta_341410000aml_sta_341311010aml_sta_341114000aml_sta_341111000aml_sta_341010020aml_sta_340912000aml_sta_340911000aml_net_341111030android14-mainline-os-statsd-release
Change-Id: Id68516c957023383aa8cd533f208b3172abe7fef
-rw-r--r-- | Android.bp | 10 | ||||
-rw-r--r-- | Cargo.toml | 50 | ||||
-rw-r--r-- | Cargo.toml.orig | 17 | ||||
-rw-r--r-- | METADATA | 15 | ||||
-rw-r--r-- | _README.md | 66 | ||||
-rw-r--r-- | cargo2android.json | 1 | ||||
-rw-r--r-- | patches/0001-Remove-unused-extern-crate-maplit.patch | 25 | ||||
-rw-r--r-- | patches/Android.bp.diff | 12 | ||||
-rw-r--r-- | src/ast.rs | 40 | ||||
-rw-r--r-- | src/grammar.pest | 96 | ||||
-rw-r--r-- | src/grammar.rs | 2 | ||||
-rw-r--r-- | src/lib.rs | 143 | ||||
-rw-r--r-- | src/optimizer/concatenator.rs | 39 | ||||
-rw-r--r-- | src/optimizer/factorizer.rs | 65 | ||||
-rw-r--r-- | src/optimizer/lister.rs | 42 | ||||
-rw-r--r-- | src/optimizer/mod.rs | 138 | ||||
-rw-r--r-- | src/optimizer/restorer.rs | 14 | ||||
-rw-r--r-- | src/optimizer/rotater.rs | 13 | ||||
-rw-r--r-- | src/optimizer/skipper.rs | 39 | ||||
-rw-r--r-- | src/optimizer/unroller.rs | 107 | ||||
-rw-r--r-- | src/parser.rs | 258 | ||||
-rw-r--r-- | src/validator.rs | 273 |
22 files changed, 918 insertions, 547 deletions
@@ -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 @@ license { 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, } @@ -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.pest] -version = "2.1.0" -[build-dependencies.sha-1] -version = "0.8" -default-features = false -[badges.codecov] -repository = "pest-parser/pest" +[dependencies.once_cell] +version = "1.8.0" -[badges.maintenance] -status = "actively-developed" +[dependencies.pest] +version = "2.5.5" -[badges.travis-ci] -repository = "pest-parser/pest" +[build-dependencies.sha2] +version = "0.10" +default-features = false 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 @@ license = "MIT/Apache-2.0" 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 } @@ -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 @@ third_party { } 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 } } @@ -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 @@ Other helpful resources: * 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 @@ thread 'main' panicked at ' --> 1:1 = 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 @@ Letter: b 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 @@ Digit: 2 * [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 @@ Digit: 2 * [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", @@ -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 @@ pub enum Expr { } 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 @@ impl Expr { 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 @@ impl Expr { } } +/// The top down iterator for an expression. pub struct ExprTopDownIterator { current: Option<Expr>, next: Option<Expr>, @@ -214,6 +251,7 @@ pub struct ExprTopDownIterator { } 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 @@ mod tests { 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. - -grammar_rules = _{ SOI ~ grammar_rule+ ~ EOI } - +//! 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). + +/// 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 @@ modifier = _{ 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 @@ postfix_operator = _{ 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) } }) } } @@ -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 @@ where { 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 @@ macro_rules! box_tree { 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 @@ pub fn optimize(rules: Vec<Rule>) -> Vec<OptimizedRule> { .map(unroller::unroll) .map(concatenator::concatenate) .map(factorizer::factor) + .map(lister::list) .map(rule_to_optimized_rule) .collect(); @@ -85,36 +90,64 @@ fn to_hash_map(rules: &[OptimizedRule]) -> HashMap<String, OptimizedExpr> { .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 @@ impl OptimizedExpr { 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 @@ impl OptimizedExpr { } } +/// A top-down iterator over an `OptimizedExpr`. pub struct OptimizedExprTopDownIterator { current: Option<OptimizedExpr>, next: Option<OptimizedExpr>, @@ -221,6 +256,7 @@ pub struct OptimizedExprTopDownIterator { } 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 @@ mod tests { #[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 @@ mod tests { }] }; let rotated = { - use optimizer::OptimizedExpr::*; + use crate::optimizer::OptimizedExpr::*; vec![OptimizedRule { name: "rule".to_owned(), ty: RuleType::Normal, @@ -313,7 +349,7 @@ mod tests { #[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 @@ mod tests { #[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 @@ mod tests { 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 @@ mod tests { 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 @@ mod tests { 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 @@ mod tests { 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 @@ mod tests { #[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 @@ mod tests { #[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 @@ mod tests { }] }; let optimized = { - use optimizer::OptimizedExpr::*; + use crate::optimizer::OptimizedExpr::*; vec![OptimizedRule { name: "rule".to_owned(), ty: RuleType::Silent, @@ -507,4 +543,82 @@ mod tests { 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 @@ fn child_modifies_state( #[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 @@ pub fn rotate(rule: Rule) -> Rule { } } - 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 @@ pub fn skip(rule: Rule) -> Rule { } } - 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; + +/// 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"); +} -include!("grammar.rs"); +pub use self::grammar::*; -pub fn parse(rule: Rule, data: &str) -> Result<Pairs<Rule>, Error<Rule>> { +/// 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 @@ impl<'i> ParserNode<'i> { } } +/// 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 @@ fn convert_node(node: ParserNode) -> Expr { } } -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 @@ pub fn consume_rules(pairs: Pairs<Rule>) -> Result<Vec<AstRule>, Vec<Error<Rule> } } -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 @@ fn consume_rules_with_spans<'i>( 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_rules_with_spans<'i>( 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 @@ fn consume_expr<'i>( } } 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 @@ fn consume_expr<'i>( } } 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 @@ fn consume_expr<'i>( } 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 @@ fn consume_expr<'i>( 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 @@ fn consume_expr<'i>( _ => 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 @@ fn unescape(string: &str) -> Option<String> { #[cfg(test)] mod tests { + use std::convert::TryInto; + use super::super::unwrap_or_report; use super::*; @@ -1014,12 +1103,47 @@ mod tests { } #[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 @@ mod tests { parser: PestParser, input: "a = {}", rule: Rule::grammar_rules, - positives: vec![Rule::term], + positives: vec![Rule::expression], negatives: vec![], pos: 5 }; @@ -1092,6 +1216,18 @@ mod tests { } #[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 @@ mod tests { #[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 @@ mod tests { 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 @@ mod tests { expr: Expr::Seq( Box::new(Expr::PeekSlice(-4, None)), Box::new(Expr::PeekSlice(0, Some(3))), - ) + ), }], ); } @@ -1485,4 +1624,45 @@ mod tests { 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; - -#[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"); - - 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"); - - 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); - +use crate::parser::{ParserExpr, ParserNode, ParserRule, Rule}; + +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() +}); + +static PEST_KEYWORDS: Lazy<HashSet<&'static str>> = Lazy::new(|| { + [ + "_", "ANY", "DROP", "EOI", "PEEK", "PEEK_ALL", "POP", "POP_ALL", "PUSH", "SOI", + ] + .iter() + .cloned() + .collect() +}); + +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 @@ pub fn validate_pairs<'i>(pairs: Pairs<'i, Rule>) -> Result<Vec<&'i str>, Vec<Er 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 @@ pub fn validate_pairs<'i>(pairs: Pairs<'i, Rule>) -> Result<Vec<&'i str>, Vec<Er 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 @@ pub fn validate_rust_keywords<'i>( 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 @@ pub fn validate_pest_keywords<'i>( 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 @@ pub fn validate_already_defined<'i>(definitions: &Vec<Span<'i>>) -> Vec<Error<Ru ErrorVariant::CustomError { message: format!("rule {} already defined", name), }, - definition.clone(), + *definition, )) } else { defined.insert(name); @@ -211,11 +183,11 @@ pub fn validate_already_defined<'i>(definitions: &Vec<Span<'i>>) -> Vec<Error<Ru 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 @@ pub fn validate_undefined<'i>( 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 @@ pub fn validate_undefined<'i>( 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 @@ fn is_non_progressing<'i>( 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 @@ fn is_non_failing<'i>( 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 @@ fn validate_repetition<'a, 'i: 'a>(rules: &'a [ParserRule<'i>]) -> Vec<Error<Rul 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 @@ fn validate_repetition<'a, 'i: 'a>(rules: &'a [ParserRule<'i>]) -> Vec<Error<Rul infinitely" .to_owned(), }, - node.span.clone() + node.span )) } else { None @@ -389,7 +365,7 @@ fn validate_choices<'a, 'i: 'a>(rules: &'a [ParserRule<'i>]) -> Vec<Error<Rule>> "expression cannot fail; following choices cannot be reached" .to_owned(), }, - node.span.clone(), + node.span, )) } else { None @@ -419,7 +395,7 @@ fn validate_whitespace_comment<'a, 'i: 'a>(rules: &'a [ParserRule<'i>]) -> Vec<E &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 @@ fn validate_whitespace_comment<'a, 'i: 'a>(rules: &'a [ParserRule<'i>]) -> Vec<E &rule.name ), }, - rule.node.span.clone(), + rule.node.span, )) } else { None @@ -449,7 +425,6 @@ fn to_hash_map<'a, 'i: 'a>(rules: &'a [ParserRule<'i>]) -> HashMap<String, &'a P 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 @@ fn left_recursion<'a, 'i: 'a>(rules: HashMap<String, &'a ParserNode<'i>>) -> Vec 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 @@ fn left_recursion<'a, 'i: 'a>(rules: HashMap<String, &'a ParserNode<'i>>) -> Vec } } 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 @@ mod tests { --> 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 @@ mod tests { 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 @@ mod tests { 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 @@ mod tests { 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 @@ mod tests { 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( |