diff options
Diffstat (limited to 'src/generator.rs')
-rw-r--r-- | src/generator.rs | 143 |
1 files changed, 118 insertions, 25 deletions
diff --git a/src/generator.rs b/src/generator.rs index 0dbcaa3..7a527c5 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -11,17 +11,17 @@ use std::path::PathBuf; use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; -use syn::{self, Generics, Ident}; +use syn::{self, Ident}; use pest::unicode::unicode_property_names; use pest_meta::ast::*; use pest_meta::optimizer::*; use crate::docs::DocComment; +use crate::ParsedDerive; pub(crate) fn generate( - name: Ident, - generics: &Generics, + parsed_derive: ParsedDerive, paths: Vec<PathBuf>, rules: Vec<OptimizedRule>, defaults: Vec<&str>, @@ -29,14 +29,14 @@ pub(crate) fn generate( include_grammar: bool, ) -> TokenStream { let uses_eoi = defaults.iter().any(|name| *name == "EOI"); - + let name = parsed_derive.name; let builtins = generate_builtin_rules(); let include_fix = if include_grammar { generate_include(&name, paths) } else { quote!() }; - let rule_enum = generate_enum(&rules, doc_comment, uses_eoi); + let rule_enum = generate_enum(&rules, doc_comment, uses_eoi, parsed_derive.non_exhaustive); let patterns = generate_patterns(&rules, uses_eoi); let skip = generate_skip(&rules); @@ -49,7 +49,7 @@ pub(crate) fn generate( } })); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let (impl_generics, ty_generics, where_clause) = parsed_derive.generics.split_for_impl(); let result = result_type(); @@ -197,8 +197,13 @@ fn generate_include(name: &Ident, paths: Vec<PathBuf>) -> TokenStream { } } -fn generate_enum(rules: &[OptimizedRule], doc_comment: &DocComment, uses_eoi: bool) -> TokenStream { - let rules = rules.iter().map(|rule| { +fn generate_enum( + rules: &[OptimizedRule], + doc_comment: &DocComment, + uses_eoi: bool, + non_exhaustive: bool, +) -> TokenStream { + let rule_variants = rules.iter().map(|rule| { let rule_name = format_ident!("r#{}", rule.name); match doc_comment.line_docs.get(&rule.name) { @@ -213,26 +218,49 @@ fn generate_enum(rules: &[OptimizedRule], doc_comment: &DocComment, uses_eoi: bo }); 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)] + }; + if non_exhaustive { + result.append_all(quote! { + #[non_exhaustive] + }); + } + result.append_all(quote! { + pub enum Rule + }); if uses_eoi { - quote! { - #[doc = #grammar_doc] - #[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)] - #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub enum Rule { + result.append_all(quote! { + { + #[doc = "End-of-input"] EOI, - #( #rules ),* + #( #rule_variants ),* } - } + }); } 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)] - pub enum Rule { - #( #rules ),* + result.append_all(quote! { + { + #( #rule_variants ),* + } + }) + }; + + let rules = rules.iter().map(|rule| { + let rule_name = format_ident!("r#{}", rule.name); + quote! { #rule_name } + }); + + result.append_all(quote! { + impl Rule { + pub fn all_rules() -> &'static[Rule] { + &[ #(Rule::#rules), * ] } } - } + }); + + result } fn generate_patterns(rules: &[OptimizedRule], uses_eoi: bool) -> TokenStream { @@ -496,6 +524,26 @@ fn generate_expr(expr: OptimizedExpr) -> TokenStream { }) } } + #[cfg(feature = "grammar-extras")] + OptimizedExpr::RepOnce(expr) => { + let expr = generate_expr(*expr); + + quote! { + state.sequence(|state| { + #expr.and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + super::hidden::skip( + state + ).and_then(|state| { + #expr + }) + }) + }) + }) + }) + } + } OptimizedExpr::Skip(strings) => { quote! { let strings = [#(#strings),*]; @@ -517,6 +565,13 @@ fn generate_expr(expr: OptimizedExpr) -> TokenStream { state.restore_on_err(|state| #expr) } } + #[cfg(feature = "grammar-extras")] + OptimizedExpr::NodeTag(expr, tag) => { + let expr = generate_expr(*expr); + quote! { + #expr.and_then(|state| state.tag_node(#tag)) + } + } } } @@ -628,6 +683,22 @@ fn generate_expr_atomic(expr: OptimizedExpr) -> TokenStream { }) } } + #[cfg(feature = "grammar-extras")] + OptimizedExpr::RepOnce(expr) => { + let expr = generate_expr_atomic(*expr); + + quote! { + state.sequence(|state| { + #expr.and_then(|state| { + state.repeat(|state| { + state.sequence(|state| { + #expr + }) + }) + }) + }) + } + } OptimizedExpr::Skip(strings) => { quote! { let strings = [#(#strings),*]; @@ -649,6 +720,13 @@ fn generate_expr_atomic(expr: OptimizedExpr) -> TokenStream { state.restore_on_err(|state| #expr) } } + #[cfg(feature = "grammar-extras")] + OptimizedExpr::NodeTag(expr, tag) => { + let expr = generate_expr_atomic(*expr); + quote! { + #expr.and_then(|state| state.tag_node(#tag)) + } + } } } @@ -694,6 +772,7 @@ mod tests { use proc_macro2::Span; use std::collections::HashMap; + use syn::Generics; #[test] fn rule_enum_simple() { @@ -712,7 +791,7 @@ mod tests { }; assert_eq!( - generate_enum(&rules, doc_comment, false).to_string(), + generate_enum(&rules, doc_comment, false, false).to_string(), quote! { #[doc = "Rule doc\nhello"] #[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)] @@ -721,6 +800,11 @@ mod tests { #[doc = "This is rule comment"] r#f } + impl Rule { + pub fn all_rules() -> &'static [Rule] { + &[Rule::r#f] + } + } } .to_string() ); @@ -1033,9 +1117,13 @@ mod tests { let base_path = current_dir.join("base.pest").to_str().unwrap().to_string(); let test_path = current_dir.join("test.pest").to_str().unwrap().to_string(); - + let parsed_derive = ParsedDerive { + name, + generics, + non_exhaustive: false, + }; assert_eq!( - generate(name, &generics, vec![PathBuf::from("base.pest"), PathBuf::from("test.pest")], rules, defaults, doc_comment, true).to_string(), + generate(parsed_derive, vec![PathBuf::from("base.pest"), PathBuf::from("test.pest")], rules, defaults, doc_comment, true).to_string(), quote! { #[allow(non_upper_case_globals)] const _PEST_GRAMMAR_MyParser: [&'static str; 2usize] = [include_str!(#base_path), include_str!(#test_path)]; @@ -1048,6 +1136,11 @@ mod tests { #[doc = "If statement"] r#if } + impl Rule { + pub fn all_rules() -> &'static [Rule] { + &[Rule::r#a, Rule::r#if] + } + } #[allow(clippy::all)] impl ::pest::Parser<Rule> for MyParser { |