Update pest_generator to 2.7.15
Test: m
Change-Id: Ic2a86206b422690157987fa8102639e2abdf4d04
diff --git a/crates/pest_generator/.android-checksum.json b/crates/pest_generator/.android-checksum.json
index a31cbc9..9258c0b 100644
--- a/crates/pest_generator/.android-checksum.json
+++ b/crates/pest_generator/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"8711726461f3c9437e9eee9d36ed82d3974bc87116897dfed6a0cd2e83355291","Android.bp":"f7b74d3f7faa345bf489d5c4f04c0fe9f007794aa65863aa9c46b6506d86d8b9","Cargo.toml":"82a4dbe24b3fa489496018714d24c061558470c1a3caf5ac62d2b46c039c2cd1","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-MIT":"38620a3cfaeec97a9197e8c39e436ea7f0bc86699b1f1c35f1aa41785b6d4eac","METADATA":"d1f5f3b6e746328d97d9b50ac450a8cd127f547a0719e79efd26a5998431fa93","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","_README.md":"6b973bfc5a49b890c6c47d4705f7f6ae59ec30552d0c3fd0fccf77bb1c694157","cargo_embargo.json":"3ca1544e82177080d7388ff17069a5f1766ba92138a9e2c502c156a296f418d8","src/docs.rs":"fb624d5b414372276e70538ff7b9dfa79bdede5bbd9b4609a2b2456c428ae3e6","src/generator.rs":"f3f7e4a1a758d50d4d92062c754de222f3f16f7af0d49acc6e5847a25890004d","src/lib.rs":"8975d84fea527807a5ec8e9f88ce0790df86b1903175fb7cb51d1760d73a4727","src/macros.rs":"7bb24e482baae1b7f5a7845da000c2fb7a9417a802cdc27eb0de393efc73612c","tests/base.pest":"842b80623c38d4c5221105126135fc42fa7c8a9b631677d6f3013068370dc149","tests/test.pest":"84526a2b662665a3ed83baf588dbeaf8e92e1898b904d4c69f7c16d7b1c399ac"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"3ae08460988dbccb11757257a8b143f85c92286fdeda0c4d056fadeae4044337","Android.bp":"6649afc7911a5c155d85ee67c65c0a04cf71f59f87ca474a9c733d3d261b3656","Cargo.toml":"2cda83978ec6ff9cca0e36dd6b9ee88dfbf69589f73e219496e62fb585045f29","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-MIT":"38620a3cfaeec97a9197e8c39e436ea7f0bc86699b1f1c35f1aa41785b6d4eac","METADATA":"aa4e506347229c5caedc047f7b8382fa4aa66a05847478973ac57187985e082d","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","_README.md":"6b973bfc5a49b890c6c47d4705f7f6ae59ec30552d0c3fd0fccf77bb1c694157","cargo_embargo.json":"3ca1544e82177080d7388ff17069a5f1766ba92138a9e2c502c156a296f418d8","src/docs.rs":"bf0ca813afd16b6f3f4ae9bc6818f389bad0773249081895225b85bef2fea208","src/generator.rs":"c3df900c8448042d2011c08cf852480e8ff8a31c423c656f3df7500145be6d17","src/lib.rs":"7ff0e9dfe50cacb698a6b1c5e8e83d1b9aea42e9c367e4a515e1255a398245e1","src/macros.rs":"7bb24e482baae1b7f5a7845da000c2fb7a9417a802cdc27eb0de393efc73612c","src/parse_derive.rs":"b49de93f401c13928c662b0436ae7f4d08eb9af11139a6ee7b23fd400215c7eb","tests/base.pest":"842b80623c38d4c5221105126135fc42fa7c8a9b631677d6f3013068370dc149","tests/test.pest":"84526a2b662665a3ed83baf588dbeaf8e92e1898b904d4c69f7c16d7b1c399ac"}}
\ No newline at end of file
diff --git a/crates/pest_generator/.cargo-checksum.json b/crates/pest_generator/.cargo-checksum.json
index 141f902..37ccbe3 100644
--- a/crates/pest_generator/.cargo-checksum.json
+++ b/crates/pest_generator/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"2936f4743aacfebd9824fb7fd69f6b03204312bd41153c2dcdfafa42ad0b05c1","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","_README.md":"bde746653575153be4ae26ce950963ced5034449e352e60cfd8825260c666c16","src/docs.rs":"041b2c24377955dfdb6c29991b1f1dd7d7191431d8e5eaa245325253b250f702","src/generator.rs":"21dea1774cbca7c03bc7283157c449f1acad6ac387078bbaa3fd0d5134a5e4ab","src/lib.rs":"034624c6d8ad89b365f81ab04ad96a3d67909ba3485008355f21429a42b5e02c","src/macros.rs":"897d9004449b1c219f17c079630a790f3de1a27f61bc6a03cd777a163a6a1fba","tests/base.pest":"30f6965031bc52937114f60233a327e41ccc43429ae41a8e40c7b7c8006c466f","tests/test.pest":"f3fea8154a9a26c773ab8392685039d0d84bd845587bb2d42b970946f7967ee8"},"package":"2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275"}
\ No newline at end of file
+{"files":{"Cargo.toml":"7947cbefca45a6924315a1a3495beb7cb7bd6ff7602c7f7ebf7ce110656c3028","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","_README.md":"bde746653575153be4ae26ce950963ced5034449e352e60cfd8825260c666c16","src/docs.rs":"47a5eaeeaa17efbf0537bd931eb388caa7a30cfd8504af0a90af8facee32e264","src/generator.rs":"bba65552cdf4816b7315cea607ea534d795e1ec239f1ab378cd85ef97a3ca335","src/lib.rs":"c515d9b83cd11ca3b7d9aecc3493adeb0e4eb370c0b274c4bcd1be20a48ff482","src/macros.rs":"897d9004449b1c219f17c079630a790f3de1a27f61bc6a03cd777a163a6a1fba","src/parse_derive.rs":"44fd33409a368c740d98e4eafba49db2da584dff9cda33b3de640b532a974f12","tests/base.pest":"30f6965031bc52937114f60233a327e41ccc43429ae41a8e40c7b7c8006c466f","tests/test.pest":"f3fea8154a9a26c773ab8392685039d0d84bd845587bb2d42b970946f7967ee8"},"package":"7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b"}
\ No newline at end of file
diff --git a/crates/pest_generator/Android.bp b/crates/pest_generator/Android.bp
index 5e5de3f..4d94340 100644
--- a/crates/pest_generator/Android.bp
+++ b/crates/pest_generator/Android.bp
@@ -18,7 +18,7 @@
host_cross_supported: false,
crate_name: "pest_generator",
cargo_env_compat: true,
- cargo_pkg_version: "2.7.6",
+ cargo_pkg_version: "2.7.15",
crate_root: "src/lib.rs",
edition: "2021",
features: [
diff --git a/crates/pest_generator/Cargo.toml b/crates/pest_generator/Cargo.toml
index 85dabac..2c3305f 100644
--- a/crates/pest_generator/Cargo.toml
+++ b/crates/pest_generator/Cargo.toml
@@ -13,7 +13,7 @@
edition = "2021"
rust-version = "1.61"
name = "pest_generator"
-version = "2.7.6"
+version = "2.7.15"
authors = ["DragoČ™ Tiselice <dragostiselice@gmail.com>"]
description = "pest code generator"
homepage = "https://pest.rs/"
@@ -28,11 +28,11 @@
repository = "https://github.com/pest-parser/pest"
[dependencies.pest]
-version = "2.7.6"
+version = "2.7.15"
default-features = false
[dependencies.pest_meta]
-version = "2.7.6"
+version = "2.7.15"
[dependencies.proc-macro2]
version = "1.0"
@@ -45,6 +45,7 @@
[features]
default = ["std"]
+export-internal = []
grammar-extras = ["pest_meta/grammar-extras"]
not-bootstrap-in-src = ["pest_meta/not-bootstrap-in-src"]
std = ["pest/std"]
diff --git a/crates/pest_generator/METADATA b/crates/pest_generator/METADATA
index 331a5f5..b64d063 100644
--- a/crates/pest_generator/METADATA
+++ b/crates/pest_generator/METADATA
@@ -1,17 +1,17 @@
name: "pest_generator"
description: "pest code generator"
third_party {
- version: "2.7.6"
+ version: "2.7.15"
license_type: NOTICE
last_upgrade_date {
- year: 2024
- month: 2
- day: 5
+ year: 2025
+ month: 1
+ day: 15
}
homepage: "https://crates.io/crates/pest_generator"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/pest_generator/pest_generator-2.7.6.crate"
- version: "2.7.6"
+ value: "https://static.crates.io/crates/pest_generator/pest_generator-2.7.15.crate"
+ version: "2.7.15"
}
}
diff --git a/crates/pest_generator/src/docs.rs b/crates/pest_generator/src/docs.rs
index ccc82e7..22f4f31 100644
--- a/crates/pest_generator/src/docs.rs
+++ b/crates/pest_generator/src/docs.rs
@@ -1,9 +1,13 @@
+//! Type and helper to collect the gramamr and rule documentation.
+
use pest::iterators::Pairs;
use pest_meta::parser::Rule;
use std::collections::HashMap;
+/// Abstraction for the grammer and rule doc.
#[derive(Debug)]
-pub(crate) struct DocComment {
+pub struct DocComment {
+ /// The grammar documentation is defined at the beginning of a file with //!.
pub grammar_doc: String,
/// HashMap for store all doc_comments for rules.
@@ -33,7 +37,7 @@
/// grammar_doc = "This is a grammar doc"
/// line_docs = { "foo": "line doc 1\nline doc 2", "bar": "line doc 3" }
/// ```
-pub(crate) fn consume(pairs: Pairs<'_, Rule>) -> DocComment {
+pub fn consume(pairs: Pairs<'_, Rule>) -> DocComment {
let mut grammar_doc = String::new();
let mut line_docs: HashMap<String, String> = HashMap::new();
diff --git a/crates/pest_generator/src/generator.rs b/crates/pest_generator/src/generator.rs
index 7a527c5..fd48ec9 100644
--- a/crates/pest_generator/src/generator.rs
+++ b/crates/pest_generator/src/generator.rs
@@ -7,6 +7,8 @@
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.
+//! Helpers to generate the code for the Parser `derive``.
+
use std::path::PathBuf;
use proc_macro2::TokenStream;
@@ -18,9 +20,12 @@
use pest_meta::optimizer::*;
use crate::docs::DocComment;
-use crate::ParsedDerive;
+use crate::parse_derive::ParsedDerive;
-pub(crate) fn generate(
+/// Generates the corresponding parser based based on the processed macro input. If `include_grammar`
+/// is set to true, it'll generate an explicit "include_str" statement (done in pest_derive, but
+/// turned off in the local bootstrap).
+pub fn generate(
parsed_derive: ParsedDerive,
paths: Vec<PathBuf>,
rules: Vec<OptimizedRule>,
@@ -218,10 +223,17 @@
});
let grammar_doc = &doc_comment.grammar_doc;
- let mut result = quote! {
- #[doc = #grammar_doc]
- #[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
- #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ let mut result = if grammar_doc.is_empty() {
+ quote! {
+ #[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
+ #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ }
+ } else {
+ quote! {
+ #[doc = #grammar_doc]
+ #[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
+ #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ }
};
if non_exhaustive {
result.append_all(quote! {
@@ -566,12 +578,42 @@
}
}
#[cfg(feature = "grammar-extras")]
- OptimizedExpr::NodeTag(expr, tag) => {
- let expr = generate_expr(*expr);
- quote! {
- #expr.and_then(|state| state.tag_node(#tag))
+ OptimizedExpr::NodeTag(expr, tag) => match *expr {
+ OptimizedExpr::Opt(expr) => {
+ let expr = generate_expr(*expr);
+ quote! {
+ state.optional(|state| {
+ #expr.and_then(|state| state.tag_node(#tag))
+ })
+ }
}
- }
+ OptimizedExpr::Rep(expr) => {
+ let expr = generate_expr(*expr);
+ quote! {
+ state.sequence(|state| {
+ state.optional(|state| {
+ #expr.and_then(|state| {
+ state.repeat(|state| {
+ state.sequence(|state| {
+ super::hidden::skip(
+ state
+ ).and_then(|state| {
+ #expr.and_then(|state| state.tag_node(#tag))
+ })
+ })
+ })
+ }).and_then(|state| state.tag_node(#tag))
+ })
+ })
+ }
+ }
+ expr => {
+ let expr = generate_expr(expr);
+ quote! {
+ #expr.and_then(|state| state.tag_node(#tag))
+ }
+ }
+ },
}
}
@@ -721,12 +763,32 @@
}
}
#[cfg(feature = "grammar-extras")]
- OptimizedExpr::NodeTag(expr, tag) => {
- let expr = generate_expr_atomic(*expr);
- quote! {
- #expr.and_then(|state| state.tag_node(#tag))
+ OptimizedExpr::NodeTag(expr, tag) => match *expr {
+ OptimizedExpr::Opt(expr) => {
+ let expr = generate_expr_atomic(*expr);
+
+ quote! {
+ state.optional(|state| {
+ #expr.and_then(|state| state.tag_node(#tag))
+ })
+ }
}
- }
+ OptimizedExpr::Rep(expr) => {
+ let expr = generate_expr_atomic(*expr);
+
+ quote! {
+ state.repeat(|state| {
+ #expr.and_then(|state| state.tag_node(#tag))
+ })
+ }
+ }
+ expr => {
+ let expr = generate_expr_atomic(expr);
+ quote! {
+ #expr.and_then(|state| state.tag_node(#tag))
+ }
+ }
+ },
}
}
@@ -811,6 +873,41 @@
}
#[test]
+ fn rule_empty_doc() {
+ let rules = vec![OptimizedRule {
+ name: "f".to_owned(),
+ ty: RuleType::Normal,
+ expr: OptimizedExpr::Ident("g".to_owned()),
+ }];
+
+ let mut line_docs = HashMap::new();
+ line_docs.insert("f".to_owned(), "This is rule comment".to_owned());
+
+ let doc_comment = &DocComment {
+ grammar_doc: "".to_owned(),
+ line_docs,
+ };
+
+ assert_eq!(
+ generate_enum(&rules, doc_comment, false, false).to_string(),
+ quote! {
+ #[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
+ #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ pub enum Rule {
+ #[doc = "This is rule comment"]
+ r#f
+ }
+ impl Rule {
+ pub fn all_rules() -> &'static [Rule] {
+ &[Rule::r#f]
+ }
+ }
+ }
+ .to_string()
+ );
+ }
+
+ #[test]
fn sequence() {
let expr = OptimizedExpr::Seq(
Box::new(OptimizedExpr::Str("a".to_owned())),
diff --git a/crates/pest_generator/src/lib.rs b/crates/pest_generator/src/lib.rs
index cbd13ea..7277c75 100644
--- a/crates/pest_generator/src/lib.rs
+++ b/crates/pest_generator/src/lib.rs
@@ -26,14 +26,29 @@
use std::io::{self, Read};
use std::path::Path;
+use generator::generate;
use proc_macro2::TokenStream;
-use syn::{Attribute, DeriveInput, Expr, ExprLit, Generics, Ident, Lit, Meta};
+use syn::DeriveInput;
#[macro_use]
mod macros;
+
+#[cfg(feature = "export-internal")]
+pub mod docs;
+#[cfg(not(feature = "export-internal"))]
mod docs;
+
+#[cfg(feature = "export-internal")]
+pub mod generator;
+#[cfg(not(feature = "export-internal"))]
mod generator;
+#[cfg(feature = "export-internal")]
+pub mod parse_derive;
+#[cfg(not(feature = "export-internal"))]
+mod parse_derive;
+
+use crate::parse_derive::{parse_derive, GrammarSource};
use pest_meta::parser::{self, rename_meta_rule, Rule};
use pest_meta::{optimizer, unwrap_or_report, validator};
@@ -44,6 +59,7 @@
let ast: DeriveInput = syn::parse2(input).unwrap();
let (parsed_derive, contents) = parse_derive(ast);
+ // Grammar presented in a view of a string.
let mut data = String::new();
let mut paths = vec![];
@@ -86,6 +102,7 @@
}
}
+ // `Rule::grammar_rules` is taken from meta/srd/parser.rs.
let pairs = match parser::parse(Rule::grammar_rules, &data) {
Ok(pairs) => pairs,
Err(error) => panic!("error parsing \n{}", error.renamed_rules(rename_meta_rule)),
@@ -96,7 +113,7 @@
let ast = unwrap_or_report(parser::consume_rules(pairs));
let optimized = optimizer::optimize(ast);
- generator::generate(
+ generate(
parsed_derive,
paths,
optimized,
@@ -113,160 +130,8 @@
Ok(string)
}
-#[derive(Debug, PartialEq)]
-enum GrammarSource {
- File(String),
- Inline(String),
-}
-
-struct ParsedDerive {
- pub(crate) name: Ident,
- pub(crate) generics: Generics,
- pub(crate) non_exhaustive: bool,
-}
-
-fn parse_derive(ast: DeriveInput) -> (ParsedDerive, Vec<GrammarSource>) {
- let name = ast.ident;
- let generics = ast.generics;
-
- let grammar: Vec<&Attribute> = ast
- .attrs
- .iter()
- .filter(|attr| {
- let path = attr.meta.path();
- path.is_ident("grammar") || path.is_ident("grammar_inline")
- })
- .collect();
-
- if grammar.is_empty() {
- panic!("a grammar file needs to be provided with the #[grammar = \"PATH\"] or #[grammar_inline = \"GRAMMAR CONTENTS\"] attribute");
- }
-
- let mut grammar_sources = Vec::with_capacity(grammar.len());
- for attr in grammar {
- grammar_sources.push(get_attribute(attr))
- }
-
- let non_exhaustive = ast
- .attrs
- .iter()
- .any(|attr| attr.meta.path().is_ident("non_exhaustive"));
-
- (
- ParsedDerive {
- name,
- generics,
- non_exhaustive,
- },
- grammar_sources,
- )
-}
-
-fn get_attribute(attr: &Attribute) -> GrammarSource {
- match &attr.meta {
- Meta::NameValue(name_value) => match &name_value.value {
- Expr::Lit(ExprLit {
- lit: Lit::Str(string),
- ..
- }) => {
- if name_value.path.is_ident("grammar") {
- GrammarSource::File(string.value())
- } else {
- GrammarSource::Inline(string.value())
- }
- }
- _ => panic!("grammar attribute must be a string"),
- },
- _ => panic!("grammar attribute must be of the form `grammar = \"...\"`"),
- }
-}
-
#[cfg(test)]
mod tests {
- use super::parse_derive;
- use super::GrammarSource;
-
- #[test]
- fn derive_inline_file() {
- let definition = "
- #[other_attr]
- #[grammar_inline = \"GRAMMAR\"]
- pub struct MyParser<'a, T>;
- ";
- let ast = syn::parse_str(definition).unwrap();
- let (_, filenames) = parse_derive(ast);
- assert_eq!(filenames, [GrammarSource::Inline("GRAMMAR".to_string())]);
- }
-
- #[test]
- fn derive_ok() {
- let definition = "
- #[other_attr]
- #[grammar = \"myfile.pest\"]
- pub struct MyParser<'a, T>;
- ";
- let ast = syn::parse_str(definition).unwrap();
- let (parsed_derive, filenames) = parse_derive(ast);
- assert_eq!(filenames, [GrammarSource::File("myfile.pest".to_string())]);
- assert!(!parsed_derive.non_exhaustive);
- }
-
- #[test]
- fn derive_multiple_grammars() {
- let definition = "
- #[other_attr]
- #[grammar = \"myfile1.pest\"]
- #[grammar = \"myfile2.pest\"]
- pub struct MyParser<'a, T>;
- ";
- let ast = syn::parse_str(definition).unwrap();
- let (_, filenames) = parse_derive(ast);
- assert_eq!(
- filenames,
- [
- GrammarSource::File("myfile1.pest".to_string()),
- GrammarSource::File("myfile2.pest".to_string())
- ]
- );
- }
-
- #[test]
- fn derive_nonexhaustive() {
- let definition = "
- #[non_exhaustive]
- #[grammar = \"myfile.pest\"]
- pub struct MyParser<'a, T>;
- ";
- let ast = syn::parse_str(definition).unwrap();
- let (parsed_derive, filenames) = parse_derive(ast);
- assert_eq!(filenames, [GrammarSource::File("myfile.pest".to_string())]);
- assert!(parsed_derive.non_exhaustive);
- }
-
- #[test]
- #[should_panic(expected = "grammar attribute must be a string")]
- fn derive_wrong_arg() {
- let definition = "
- #[other_attr]
- #[grammar = 1]
- pub struct MyParser<'a, T>;
- ";
- let ast = syn::parse_str(definition).unwrap();
- parse_derive(ast);
- }
-
- #[test]
- #[should_panic(
- expected = "a grammar file needs to be provided with the #[grammar = \"PATH\"] or #[grammar_inline = \"GRAMMAR CONTENTS\"] attribute"
- )]
- fn derive_no_grammar() {
- let definition = "
- #[other_attr]
- pub struct MyParser<'a, T>;
- ";
- let ast = syn::parse_str(definition).unwrap();
- parse_derive(ast);
- }
#[doc = "Matches dar\n\nMatch dar description\n"]
#[test]
diff --git a/crates/pest_generator/src/parse_derive.rs b/crates/pest_generator/src/parse_derive.rs
new file mode 100644
index 0000000..52374c2
--- /dev/null
+++ b/crates/pest_generator/src/parse_derive.rs
@@ -0,0 +1,172 @@
+// 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.
+
+//! Types and helpers to parse the input of the derive macro.
+
+use syn::{Attribute, DeriveInput, Expr, ExprLit, Generics, Ident, Lit, Meta};
+
+#[derive(Debug, PartialEq)]
+pub(crate) enum GrammarSource {
+ File(String),
+ Inline(String),
+}
+
+/// Parsed information of the derive and the attributes.
+pub struct ParsedDerive {
+ /// The identifier of the deriving struct, union, or enum.
+ pub name: Ident,
+ /// The generics of the deriving struct, union, or enum.
+ pub generics: Generics,
+ /// Indicates whether the 'non_exhaustive' attribute is added to the 'Rule' enum.
+ pub non_exhaustive: bool,
+}
+
+pub(crate) fn parse_derive(ast: DeriveInput) -> (ParsedDerive, Vec<GrammarSource>) {
+ let name = ast.ident;
+ let generics = ast.generics;
+
+ let grammar: Vec<&Attribute> = ast
+ .attrs
+ .iter()
+ .filter(|attr| {
+ let path = attr.meta.path();
+ path.is_ident("grammar") || path.is_ident("grammar_inline")
+ })
+ .collect();
+
+ if grammar.is_empty() {
+ panic!("a grammar file needs to be provided with the #[grammar = \"PATH\"] or #[grammar_inline = \"GRAMMAR CONTENTS\"] attribute");
+ }
+
+ let mut grammar_sources = Vec::with_capacity(grammar.len());
+ for attr in grammar {
+ grammar_sources.push(get_attribute(attr))
+ }
+
+ let non_exhaustive = ast
+ .attrs
+ .iter()
+ .any(|attr| attr.meta.path().is_ident("non_exhaustive"));
+
+ (
+ ParsedDerive {
+ name,
+ generics,
+ non_exhaustive,
+ },
+ grammar_sources,
+ )
+}
+
+fn get_attribute(attr: &Attribute) -> GrammarSource {
+ match &attr.meta {
+ Meta::NameValue(name_value) => match &name_value.value {
+ Expr::Lit(ExprLit {
+ lit: Lit::Str(string),
+ ..
+ }) => {
+ if name_value.path.is_ident("grammar") {
+ GrammarSource::File(string.value())
+ } else {
+ GrammarSource::Inline(string.value())
+ }
+ }
+ _ => panic!("grammar attribute must be a string"),
+ },
+ _ => panic!("grammar attribute must be of the form `grammar = \"...\"`"),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::parse_derive;
+ use super::GrammarSource;
+
+ #[test]
+ fn derive_inline_file() {
+ let definition = "
+ #[other_attr]
+ #[grammar_inline = \"GRAMMAR\"]
+ pub struct MyParser<'a, T>;
+ ";
+ let ast = syn::parse_str(definition).unwrap();
+ let (_, filenames) = parse_derive(ast);
+ assert_eq!(filenames, [GrammarSource::Inline("GRAMMAR".to_string())]);
+ }
+
+ #[test]
+ fn derive_ok() {
+ let definition = "
+ #[other_attr]
+ #[grammar = \"myfile.pest\"]
+ pub struct MyParser<'a, T>;
+ ";
+ let ast = syn::parse_str(definition).unwrap();
+ let (parsed_derive, filenames) = parse_derive(ast);
+ assert_eq!(filenames, [GrammarSource::File("myfile.pest".to_string())]);
+ assert!(!parsed_derive.non_exhaustive);
+ }
+
+ #[test]
+ fn derive_multiple_grammars() {
+ let definition = "
+ #[other_attr]
+ #[grammar = \"myfile1.pest\"]
+ #[grammar = \"myfile2.pest\"]
+ pub struct MyParser<'a, T>;
+ ";
+ let ast = syn::parse_str(definition).unwrap();
+ let (_, filenames) = parse_derive(ast);
+ assert_eq!(
+ filenames,
+ [
+ GrammarSource::File("myfile1.pest".to_string()),
+ GrammarSource::File("myfile2.pest".to_string())
+ ]
+ );
+ }
+
+ #[test]
+ fn derive_nonexhaustive() {
+ let definition = "
+ #[non_exhaustive]
+ #[grammar = \"myfile.pest\"]
+ pub struct MyParser<'a, T>;
+ ";
+ let ast = syn::parse_str(definition).unwrap();
+ let (parsed_derive, filenames) = parse_derive(ast);
+ assert_eq!(filenames, [GrammarSource::File("myfile.pest".to_string())]);
+ assert!(parsed_derive.non_exhaustive);
+ }
+
+ #[test]
+ #[should_panic(expected = "grammar attribute must be a string")]
+ fn derive_wrong_arg() {
+ let definition = "
+ #[other_attr]
+ #[grammar = 1]
+ pub struct MyParser<'a, T>;
+ ";
+ let ast = syn::parse_str(definition).unwrap();
+ parse_derive(ast);
+ }
+
+ #[test]
+ #[should_panic(
+ expected = "a grammar file needs to be provided with the #[grammar = \"PATH\"] or #[grammar_inline = \"GRAMMAR CONTENTS\"] attribute"
+ )]
+ fn derive_no_grammar() {
+ let definition = "
+ #[other_attr]
+ pub struct MyParser<'a, T>;
+ ";
+ let ast = syn::parse_str(definition).unwrap();
+ parse_derive(ast);
+ }
+}
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index 984e54a..6abba92 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -3001,20 +3001,11 @@
[[package]]
name = "litrs"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b487d13a3f4b465df87895a37b24e364907019afa12d943528df5b7abe0836f1"
-dependencies = [
- "proc-macro2 1.0.93",
-]
-
-[[package]]
-name = "litrs"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
dependencies = [
- "proc-macro2 1.0.92",
+ "proc-macro2 1.0.93",
]
[[package]]
@@ -3338,18 +3329,6 @@
[[package]]
name = "mockall_derive"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2"
-dependencies = [
- "cfg-if",
- "proc-macro2 1.0.93",
- "quote 1.0.38",
- "syn 2.0.96",
-]
-
-[[package]]
-name = "mockall_derive"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898"
@@ -3634,7 +3613,7 @@
dependencies = [
"proc-macro2 1.0.93",
"quote 1.0.38",
- "syn 2.0.90",
+ "syn 2.0.96",
]
[[package]]
@@ -3852,9 +3831,9 @@
[[package]]
name = "pest_generator"
-version = "2.7.6"
+version = "2.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275"
+checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b"
dependencies = [
"pest",
"pest_meta",
@@ -4371,7 +4350,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
]
[[package]]
@@ -5229,9 +5208,9 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c"
dependencies = [
- "proc-macro2 1.0.92",
+ "proc-macro2 1.0.93",
"quote 1.0.38",
- "syn 2.0.90",
+ "syn 2.0.96",
]
[[package]]
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index e24e8f0..7c4b956 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -236,7 +236,7 @@
percore = "=0.1.0"
pest = "=2.7.15"
pest_derive = "=2.7.6"
-pest_generator = "=2.7.6"
+pest_generator = "=2.7.15"
pest_meta = "=2.7.15"
petgraph = "=0.6.5"
pin-project = "=1.1.3"